home *** CD-ROM | disk | FTP | other *** search
/ ftp.ncsa.uiuc.edu / ftp.ncsa.uiuc.edu.zip / ftp.ncsa.uiuc.edu / DataScope / misc / DS_serve.c next >
C/C++ Source or Header  |  2017-03-03  |  15KB  |  687 lines

  1. /**************************************************************************/
  2. /*  NCSA DataScope
  3. *   An experiment with real numbers.
  4. *   by Tim Krauskopf
  5. *   
  6. *   National Center for Supercomputing Applications
  7. *   University of Illinois at Urbana-Champaign
  8. *   605 E. Springfield Ave.
  9. *   Champaign, IL  61820
  10. *
  11. *   email:          softdev@ncsa.uiuc.edu
  12. *   bug reports:    bugs@ncsa.uiuc.edu
  13. *   server:         ftp.ncsa.uiuc.edu  (128.174.20.50)
  14. *
  15. *   NCSA DataScope is in the public domain.  See the manual for a complete
  16. *   permissions statement.  We ask that the following message be included
  17. *   in all derivative works:
  18. *   Portions developed at the National Center for Supercomputing Applications
  19. *   University of Illinois at Urbana-Champaign.
  20. *
  21. *   version                      comments
  22. *   -----------                -------------
  23. *   1.0 TKK  December 1988
  24. *   1.1 TKK  May 1989       -- new polar, computations, interpolation
  25. *   1.2 TKK  January 1990   -- networking additions
  26. */
  27. /*
  28. *  DS_serve
  29. *
  30. *  This server sits on a unix machine and waits 
  31. *  for a DataScope client to send it work to do.
  32. *
  33. *  It is run from rexecd, so it reads and writes to/from stdin
  34. *  and stdout.
  35. *
  36. *  Calls are made to C or FORTRAN subroutines to execute user-declared
  37. *  functions.  See dsfn.h and documentation for declaration of functions.
  38. *
  39. *
  40. *  September, 1989
  41. *  Tim Krauskopf
  42. */
  43.  
  44.  
  45. #include <stdio.h>
  46.  
  47.  
  48. #ifdef UNICOS
  49. #define INTOFF 4        /* distance from beginning of int to 32 bit half */
  50. #define FLOFF 4            /* offset for floating point length diff */
  51. #define FLEN 8            /* floating point number length, for readability */
  52.  
  53. #endif
  54.  
  55.  
  56. #ifdef SUN
  57. #define INTOFF 0
  58. #define FLOFF 0
  59. #define FLEN 4
  60.  
  61. #endif SUN
  62.  
  63.  
  64. /********************************************************************/
  65.  
  66. main(argc,argv)
  67.     int argc;
  68.     char *argv[];
  69.     {
  70.     int i,cd,ret;
  71.     char *malloc();
  72.     char dowhat[5];
  73.  
  74.     cd = 0;
  75. /*
  76. *  Send the first byte to indicate that we are ready to receive.
  77. */
  78.     fullmsg("\0");
  79.  
  80. /*
  81. *  Check the first four characters to make sure they aren't coming from some
  82. *  other client who doesn't know what is going on.
  83. */
  84.     if (4 == read(cd,dowhat,4)) {
  85.  
  86.         if (!strncmp("DSfn",dowhat,4))
  87.         {            
  88.             DSfn(cd);                    /* DataScope fn */
  89.         }
  90.         else
  91.             fullmsg("DS_serve: Unsupported function for this server");
  92.     }
  93.  
  94.     fullmsg("DS_serve: Cannot read startup code.");
  95. }
  96.  
  97.  
  98. #include "DScope.h"
  99. #include "dsfn.h"
  100. #define DS_ERROR -1
  101. #define DS_CONSTANT 0
  102. #define DS_ARRAY 1
  103.  
  104. DSfn(cd)
  105.     int cd;
  106. {
  107.     scope_array lft,rgt,answer;
  108.  
  109.     int i,j,cnt,atemp;
  110.     char *malloc(),fn[256];
  111.     float *f,*fr,*fc;
  112.  
  113.  
  114.     if (0 > readtonul(cd,fn))            /* get name of function */
  115.         return(-1);
  116.  
  117. /*
  118. *  Read parameter information, first left, then right.
  119. *  The 13 bytes are required, then an optional array, depending upon
  120. *  the setting of the "kind" subfield.
  121. */
  122.     read(cd,&lft.kind,1);
  123.     lft.cval = 0.0;
  124.     read(cd,(char *)&lft.cval,4);
  125.     flconvert(&lft.cval,1);
  126.     lft.nrows = 0;
  127.     read(cd,INTOFF + (char *)&lft.nrows,4);
  128.     lft.ncols = 0;
  129.     read(cd,INTOFF + (char *)&lft.ncols,4);
  130.  
  131.     if (lft.kind == DS_ARRAY) {        /* read array in */
  132.         lft.rows = (float *)malloc(FLEN*lft.nrows);
  133.         lft.cols = (float *)malloc(FLEN*lft.ncols);
  134.         lft.vals = (float *)malloc(FLEN*lft.ncols*lft.nrows);
  135.  
  136.         if (0 > fullread(cd,(char *)lft.rows,4*lft.nrows))
  137.             return(-1);
  138.         flconvert(lft.rows,lft.nrows);
  139.  
  140.         if (0 > fullread(cd,(char *)lft.cols,4*lft.ncols))
  141.             return(-1);
  142.         flconvert(lft.cols,lft.ncols);
  143.  
  144.         if (0 > fullread(cd,(char *)lft.vals,4*lft.nrows*lft.ncols))
  145.             return(-1);
  146.         flconvert(lft.vals,lft.ncols*lft.nrows);
  147.  
  148.     }
  149.  
  150.     read(cd,&rgt.kind,1);
  151.     rgt.cval = 0.0;
  152.     read(cd,(char *)&rgt.cval,4);
  153.     flconvert(&rgt.cval,1);
  154.     rgt.nrows = 0;
  155.     read(cd,INTOFF + (char *)&rgt.nrows,4);
  156.     rgt.ncols = 0;
  157.     read(cd,INTOFF + (char *)&rgt.ncols,4);
  158.  
  159.     if (rgt.kind == DS_ARRAY) {        /* read array in */
  160.         rgt.rows = (float *)malloc(FLEN*rgt.nrows);
  161.         rgt.cols = (float *)malloc(FLEN*rgt.ncols);
  162.         rgt.vals = (float *)malloc(FLEN*rgt.ncols*rgt.nrows);
  163.  
  164.         if (0 > fullread(cd,(char *)rgt.rows,4*rgt.nrows))
  165.             return(-1);
  166.         flconvert(rgt.rows,rgt.nrows);
  167.  
  168.         if (0 > fullread(cd,(char *)rgt.cols,4*rgt.ncols))
  169.             return(-1);
  170.         flconvert(rgt.cols,rgt.ncols);
  171.  
  172.         if (0 > fullread(cd,(char *)rgt.vals,4*rgt.nrows*rgt.ncols))
  173.             return(-1);
  174.         flconvert(rgt.vals,rgt.nrows*rgt.ncols);
  175.  
  176.     }
  177.  
  178.     answer.kind = DS_CONSTANT;
  179.     if (lft.kind == DS_ARRAY) {
  180.         answer.kind = DS_ARRAY;
  181.         answer.nrows = lft.nrows;
  182.         answer.ncols = lft.ncols;
  183.         fr = lft.rows; 
  184.         fc = lft.cols;
  185.     }
  186.     else if (rgt.kind == DS_ARRAY) {
  187.         answer.kind = DS_ARRAY;
  188.         answer.nrows = rgt.nrows;
  189.         answer.ncols = rgt.ncols;
  190.         fr = rgt.rows; 
  191.         fc = rgt.cols;
  192.     }
  193.     atemp = 0;
  194.     if (answer.kind == DS_ARRAY) {
  195.         atemp = 1;                        /* we malloced for answer */
  196.         answer.rows = (float *)malloc(FLEN*answer.nrows);
  197.         answer.cols = (float *)malloc(FLEN*answer.ncols);
  198.         answer.vals = (float *)malloc(FLEN*answer.ncols*answer.nrows);
  199.  
  200.         f = answer.rows;                 /* copy row and col labels */
  201.         for (i=0; i<answer.nrows; i++)
  202.             *f++ = *fr++;
  203.         f = answer.cols;
  204.         for (i=0; i<answer.ncols; i++)
  205.             *f++ = *fc++;
  206.     }
  207.  
  208. /*
  209. *  call processing routine
  210. */
  211.  
  212.     doDSfn(fn,&lft,&rgt,&answer);
  213. /*
  214. * write answer back 
  215. */
  216.     cd = 1;
  217.     write(cd,"DSfn",4);
  218.     write(cd,&answer.kind,1);
  219.     flbackconvert(&answer.cval,1);
  220.     write(cd,(char *)&answer.cval,4);
  221.  
  222.     write(cd,INTOFF + (char *)&answer.nrows,4);
  223.     write(cd,INTOFF + (char *)&answer.ncols,4);
  224.  
  225.     if (answer.kind == DS_ARRAY) {
  226.  
  227.         flbackconvert(answer.rows,answer.nrows);
  228.         if (0 > fullwrite(cd,(char *)answer.rows,4*answer.nrows))
  229.             return(-1);
  230.  
  231.         flbackconvert(answer.cols,answer.ncols);
  232.         if (0 > fullwrite(cd,(char *)answer.cols,4*answer.ncols))
  233.             return(-1);
  234.  
  235.         flbackconvert(answer.vals,answer.nrows*answer.ncols);
  236.         if (0 > fullwrite(cd,(char *)answer.vals,4*answer.nrows*answer.ncols))
  237.             return(-1);
  238.     }
  239.  
  240.     if (lft.kind == DS_ARRAY) {
  241.         free(lft.rows);
  242.         free(lft.cols);
  243.         free(lft.vals);
  244.     }
  245.     if (rgt.kind == DS_ARRAY) {
  246.         free(rgt.rows);
  247.         free(rgt.cols);
  248.         free(rgt.vals);
  249.     }
  250.     if (atemp) {
  251.         free(answer.rows);
  252.         free(answer.cols);
  253.         free(answer.vals);
  254.     }
  255.  
  256.     return(0);
  257. }
  258.  
  259. /**********************************************************************/
  260. /*  doDSfn
  261. *   perform the function we are here to do.
  262. */
  263. doDSfn(fn,lft,rgt,answer)
  264.     char *fn;
  265.     scope_array *lft,*rgt,*answer;
  266. {
  267.     int i,lim;
  268.     float *fl,*fr;
  269.     char msg[256];
  270.  
  271. /*
  272. *  First, check the C routines
  273. */
  274.     i = 0;
  275.     while ( dsc[i].namestring[0] ) {
  276.  
  277.     /*
  278.     *  If we find a match, make the function call and then return.
  279.     */
  280.         if (!strcmp(dsc[i].namestring,fn)) {
  281.             (*dsc[i].fncall)(lft,rgt,answer);
  282.             return;
  283.         }
  284.  
  285.         i++;
  286.     }
  287.  
  288. /*
  289. *  Now, the FORTRAN routines
  290. */
  291.     i = 0;
  292.     while ( dsf[i].namestring[0] ) {
  293.     
  294.     /*
  295.     *  If we find a match, make the function call and then return.
  296.     *  For FORTRAN, use the call:
  297.     *     subr(vals,rows,cols,nrows,ncols,maxr,maxc,p)
  298.     *     INTEGER maxr,maxc,p                    // array limits, uses extra space sometimes
  299.     *     REAL vals(maxc,maxr,0:p)               // the array values for answer, parms
  300.     *     REAL rows(maxr,0:p),cols(maxc,0:p)     // the scale values for answer, parms
  301.     *     REAL nrows(0:p),ncols(0:p)             // the number of rows,cols, use 1 for constants
  302.     *
  303.     */
  304.     
  305.         if (!strcmp(dsf[i].namestring,fn)) {
  306.         
  307.             register int a,b;
  308.             float *vals,*rows,*cols;
  309.             register float *f,*ft;
  310.             int nrows[3],ncols[3],maxr,maxc,p;            /* p will always be 2, even if one unused */
  311.  
  312. /*
  313. *  set up master arrays, first find largest row and column girth for multiple array
  314. */
  315.             maxr = 0;
  316.             maxc = 0;
  317.             p = 2;
  318.  
  319.             if (lft->kind == DS_CONSTANT) {
  320.                 if (maxr < 1) maxr = 1;
  321.                 if (maxc < 1) maxc = 1;
  322.                 nrows[1] = 1;
  323.                 ncols[1] = 1;
  324.             } else {
  325.                 if (maxr < lft->nrows) maxr = lft->nrows;
  326.                 if (maxc < lft->ncols) maxc = lft->ncols;    
  327.                 nrows[1] = lft->nrows;
  328.                 ncols[1] = lft->ncols;
  329.             }
  330.             if (rgt->kind == DS_CONSTANT) {
  331.                 if (maxr < 1) maxr = 1;
  332.                 if (maxc < 1) maxc = 1;
  333.                 nrows[2] = 1;
  334.                 ncols[2] = 1;
  335.             } else {
  336.                 if (maxr < rgt->nrows) maxr = rgt->nrows;
  337.                 if (maxc < rgt->ncols) maxc = rgt->ncols;    
  338.                 nrows[2] = rgt->nrows;
  339.                 ncols[2] = rgt->ncols;
  340.             }
  341.             if (answer->kind == DS_CONSTANT) {
  342.                 if (maxr < 1) maxr = 1;
  343.                 if (maxc < 1) maxc = 1;
  344.                 nrows[0] = 1;
  345.                 ncols[0] = 1;
  346.             } else {
  347.                 if (maxr < answer->nrows) maxr = answer->nrows;
  348.                 if (maxc < answer->ncols) maxc = answer->ncols;    
  349.                 nrows[0] = answer->nrows;
  350.                 ncols[0] = answer->ncols;
  351.             }
  352.             
  353.             if ((NULL == (vals = (float *)malloc(3*maxr*maxc*sizeof(float)))) ||
  354.                 (NULL == (rows = (float *)malloc(3*maxr*sizeof(float)))) ||
  355.                 (NULL == (cols = (float *)malloc(3*maxc*sizeof(float))))) {
  356.                 answer->kind = DS_ERROR;
  357.                 return;
  358.             }
  359. /*
  360. *  fill in those master arrays, first the data values
  361. */            
  362.             f = lft->vals;
  363.             if (lft->kind == DS_CONSTANT) f = &lft->cval;
  364.             ft = vals + maxr*maxc;                    /* skip to left parm array */
  365.             for (a=0; a < nrows[1]; a++) {
  366.                 for (b=0; b < ncols[1]; b++)
  367.                     *ft++ = *f++;
  368.  
  369.                 ft += maxc - ncols[1];                /* skip space */
  370.             }
  371.                 
  372.             f = rgt->vals;
  373.             if (rgt->kind == DS_CONSTANT) f = &rgt->cval;
  374.             ft = vals + 2*maxr*maxc;                    /* skip to right parm array */
  375.             for (a=0; a < nrows[2]; a++) {
  376.                 for (b=0; b < ncols[2]; b++)
  377.                     *ft++ = *f++;
  378.  
  379.                 ft += maxc - ncols[2];                /* skip space */
  380.             }
  381.  
  382. /*
  383. *  Now the scale values for each of the rows and columns
  384. */
  385.             if (answer->kind == DS_ARRAY) {            
  386.                 f = answer->rows;
  387.                 ft = rows;
  388.                 for (a=0; a < nrows[0]; a++)
  389.                     *ft++ = *f++;
  390.                 f = answer->cols;
  391.                 ft = cols;
  392.                 for (a=0; a < ncols[0]; a++)
  393.                     *ft++ = *f++;
  394.             }
  395.             if (lft->kind == DS_ARRAY) {            
  396.                 f = lft->rows;
  397.                 ft = rows + maxr;
  398.                 for (a=0; a < nrows[1]; a++)
  399.                     *ft++ = *f++;
  400.                 f = lft->cols;
  401.                 ft = cols + maxc;
  402.                 for (a=0; a < ncols[1]; a++)
  403.                     *ft++ = *f++;
  404.             }
  405.             if (rgt->kind == DS_ARRAY) {            
  406.                 f = rgt->rows;
  407.                 ft = rows + 2*maxr;
  408.                 for (a=0; a < nrows[2]; a++)
  409.                     *ft++ = *f++;
  410.                 f = rgt->cols;
  411.                 ft = cols + 2*maxc;
  412.                 for (a=0; a < ncols[2]; a++)
  413.                     *ft++ = *f++;
  414.             }
  415.             
  416. /*
  417. *  now finally make the call
  418. */
  419.             
  420.             (*dsf[i].fncall)(vals,rows,cols,nrows,ncols,
  421.                 &maxr,&maxc,&p);
  422.                 
  423. /*
  424. *  put the answer where it belongs and free the temp memory
  425. */
  426.             f = answer->vals;
  427.             if (nrows[0] <= 1 && ncols[0] <= 1) {
  428.                 answer->kind = DS_CONSTANT;
  429.                 f = &answer->cval;
  430.             }
  431.             ft = vals;
  432.             for (a=0; a < nrows[0]; a++) {            /* data values themselves */
  433.                 for (b=0; b < ncols[0]; b++)
  434.                     *f++ = *ft++;
  435.  
  436.                 ft += maxc - ncols[0];                /* skip space */
  437.             }
  438.  
  439.             if (answer->kind == DS_ARRAY) {            /* row and column scales */    
  440.                 f = answer->rows;
  441.                 ft = rows;
  442.                 for (a=0; a < nrows[0]; a++)
  443.                     *f++ = *ft++;
  444.                 f = answer->cols;
  445.                 ft = cols;
  446.                 for (a=0; a < ncols[0]; a++)
  447.                     *f++ = *ft++;
  448.             }
  449.  
  450.  
  451.             free(vals);
  452.             free(rows);
  453.             free(cols);
  454.             
  455.             return;
  456.         }
  457.  
  458.         i++;
  459.     }
  460.  
  461. /*
  462. *  We did not find the function name in either the FORTRAN or C lists.
  463. */
  464.     sprintf(msg,"DS_serve: Function not found \n> %s",fn);
  465.     fullmsg(msg);
  466.     answer->kind = DS_ERROR;
  467.     return;
  468.  
  469. }
  470.  
  471. /**********************************************************************/
  472. /*  fullread
  473. *   read a full segment from the network.
  474. *   returns 0 for successful read, -1 for error 
  475. */
  476. fullread(skt,whereread,toread)
  477.     int skt,toread;
  478.     char *whereread;
  479. {
  480.     int cnt;
  481.  
  482.     while (toread > 0) {            /* count of remaining bytes to read */
  483.  
  484.         cnt = read(skt,whereread,toread);        /* read a chunk */
  485.         if (cnt < 0)                /* connection broken */
  486.             return(-1);
  487.  
  488.         toread -= cnt;                /* adjust counters for what was read */
  489.         whereread += cnt;
  490.     }
  491.  
  492.     return(0);
  493. }
  494.  
  495.  
  496. /**********************************************************************/
  497. /*  fullmsg
  498. *  Send a message to the Macintosh, probably an error message.
  499. *  Mac messages go to stdout.
  500. *  Includes the terminating zero in the string that it sends.
  501. */
  502. fullmsg(s)
  503.     char *s;
  504. {
  505.  
  506.     return(fullwrite(1,s,strlen(s)+1));
  507.  
  508. }
  509.  
  510. /**********************************************************************/
  511. /*  fullwrite
  512. *   write a full segment to the network.
  513. *   returns 0 for successful write, -1 for error
  514. */
  515. fullwrite(skt,wherewrite,towrite)
  516.     int skt,towrite;
  517.     char *wherewrite;
  518. {
  519.     int cnt;
  520.  
  521.     while (towrite > 0) {            /* count of remaining bytes to read */
  522.  
  523.         cnt = write(skt,wherewrite,towrite);       /* write a chunk */
  524.         if (cnt < 0)                /* connection broken */
  525.             return(-1);
  526.  
  527.         towrite -= cnt;              /* adjust counters for what was write */
  528.         wherewrite += cnt;
  529.     }
  530.  
  531.     return(0);
  532. }
  533.  
  534.  
  535. /**********************************************************************/
  536. /* readtonul
  537. *  read from the stream until reaching a NUL
  538. */
  539. readtonul(skt,p)
  540.     int skt;
  541.     char *p;
  542.     {
  543.     char cc;
  544.     int ret;
  545.  
  546.     do {
  547.         if (0 >= (ret = read(skt,&cc,1)))
  548.             return(-1);
  549.         *p++ = cc;
  550.     } while (ret && cc);
  551.  
  552.     return(0);
  553. }
  554.  
  555. /**********************************************************************/
  556.  
  557.  
  558. #ifdef SUN
  559. flconvert()
  560.     {
  561.  
  562. }
  563.  
  564. flbackconvert()
  565.     {
  566.  
  567. }
  568. #endif
  569.  
  570.  
  571. #ifdef UNICOS
  572.  
  573.  
  574. /*
  575. *  Convert floats from 4 bytes IEEE-32 to/from Cray-64 8 bytes.
  576. *  Also responsible for unpacking and packing the 4 byte numbers.
  577. *
  578. *  These routines are not responsible for space allocation whatsoever.
  579. *  They are assured that the space given is 8-bytes per float for as
  580. *  many floats are given.  When extra space is generated in backconvert,
  581. *  it lets the calling routine still take care of it.
  582. */
  583.  
  584.  
  585. #define MINEXP    0x3f81000000000000  /* min valid Cray masked exponent */
  586. #define MAXEXP    0x407e000000000000  /* max valid Cray masked exponent */
  587.  
  588. #define C_FMASK   0x00007fffff000000  /* Cray fraction mask (1st 23 bits)*/
  589. #define C_EMASK   0x7fff000000000000  /* Cray exponent mask */
  590. #define C_SMASK   0x8000000000000000  /* Cray sign mask */
  591. #define C_IMPLICIT 0x0000800000000000 /* Cray implicit bit */
  592.  
  593. #define I_FMASK   0x007fffff          /* IEEE fraction mask */
  594. #define I_EMASK   0x7f800000          /* IEEE exponent mask */
  595. #define I_SMASK   0x80000000          /* IEEE sign mask     */
  596.  
  597. #define IEEE_BIAS 0177
  598. #define CRAY_BIAS 040000
  599.  
  600. static long C2I_diff;
  601. static long I2C_diff;
  602.  
  603.  
  604.  
  605. flbackconvert(farr,nf)
  606.     char *farr;
  607.     int nf;
  608.     {
  609.     int i;
  610.     long tmp,newnum;
  611.     char *to,*p;
  612.  
  613.     to = farr;            /* counts 4 byte IEEE numbers */
  614.  
  615.     for (i=0; i< nf; i++) {
  616.         bcopy(farr, &newnum, 8);
  617.         farr += 8;
  618.  
  619.         if (!newnum)
  620.             tmp = 0;
  621.         else {
  622.              
  623.             tmp = (C_EMASK & newnum);
  624.             if (tmp < MINEXP) {
  625.                 newnum = 1e-30;            /* should be -INF */
  626.                 tmp = (C_EMASK & newnum);
  627.             }
  628.             else if (tmp > MAXEXP) {
  629.                 newnum = 1e30;            /* should be +INF */
  630.                 tmp = (C_EMASK & newnum);
  631.             }
  632.  
  633.             C2I_diff = (IEEE_BIAS - CRAY_BIAS - 1) << 48;
  634.             tmp = (( tmp + C2I_diff ) << 7)
  635.                 | ( (newnum & C_FMASK) << 8 )
  636.                 | ( (newnum & C_SMASK));
  637.  
  638.         }
  639.  
  640.         bcopy(&tmp,to,4);
  641.         to += 4;
  642.  
  643.     }
  644.  
  645. }
  646.  
  647.  
  648.  
  649. /* Conversion from IEEE floating point format to Cray format */
  650.  
  651. flconvert(farr,nf)
  652.     char *farr;
  653.     int nf;
  654.     {
  655.     int i;
  656.     long tmp,targ;
  657.     char *from,*to;
  658.  
  659.     from = farr + 4*(nf-1);        /* end of IEEE array, work backwards */
  660.     to = farr + 8*(nf-1);        /* end of Cray array, work backwards */
  661.  
  662.     for (i=0; i<nf; i++) {        /* for each float */
  663.         tmp = 0;
  664.         bcopy(from, FLOFF+(char *)&tmp, 4); 
  665.         from -= 4;
  666.  
  667.         if (!(targ = (tmp & I_EMASK))) {
  668.             targ = 0;
  669.         }
  670.         else {
  671.             I2C_diff = (CRAY_BIAS - IEEE_BIAS + 1) << 23;
  672.             targ += I2C_diff;
  673.             targ = (targ<< 25)  | ( (tmp & I_FMASK) << 24)
  674.                | ( (tmp & I_SMASK) << 32) | C_IMPLICIT;
  675.  
  676.         }
  677.         bcopy(&targ, to, 8);
  678.         to -= 8;                /* room for next one */
  679.  
  680.     }
  681.  
  682. }
  683.  
  684.  
  685.  
  686. #endif
  687.