home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / comm / term23_2.lha / Source_Code / XPRXModem / dlink.c < prev    next >
C/C++ Source or Header  |  1991-08-26  |  15KB  |  553 lines

  1. /* dlink - data link transfer routines for bmodem */
  2.  
  3. /*
  4.  * by David Betz, BYTE Magazine/BIX
  5.  * 
  6.  * statements involving bm_infoinit(), bm_info() added by Willy Langeveld for
  7.  * VLT
  8.  * 
  9.  * ANSIsized and made re-entrant by Marc Boucher for XPR implementation.
  10.  * 
  11.  * 91.08.13 [astp] Fixed pre-computed crc table, which I added 91.08.10 *sigh*
  12.  */
  13.  
  14. #include <string.h>
  15. #include <stdio.h>
  16.  
  17. #include "xprxmodem.h"
  18.  
  19. #include "proto-dlink.h"
  20. #include "proto-xprxmodem.h"
  21. #include "proto-callback.h"
  22.  
  23. #define WTYPE unsigned short
  24.  
  25. /* useful definitions */
  26. #define TRUE    1
  27. #define FALSE   0
  28.  
  29. /* protocol characters */
  30. #define SOH     0x01        /* start of a 128 byte block */
  31. #define STX     0x02        /* start of a 1024 byte block */
  32. #define EOT     0x04        /* end of a complete file */
  33. #define ACK     0x06        /* positive acknowledgement */
  34. #define NAK     0x15        /* negative acknowledgement */
  35. #define CAN     0x18        /* cancel transfer */
  36. #define CRC     'C'        /* CRC request (instead of NAK to start a
  37.                  * transfer) */
  38.  
  39. /* routine status codes */
  40. #define DT_EOF  -1
  41. #define DT_OK   1
  42.  
  43. /* number of times to retry */
  44. #define RETRY   10
  45.  
  46. /* timeout loop counters */
  47. #define STIME   4        /* timeout between characters of a packet */
  48. #define LTIME   20        /* timeout waiting for an ACK/NAK */
  49. #define XTIME   60        /* timeout waiting for initial NAK */
  50.  
  51. /* dlabort - wait for the line to clear */
  52. static void dlabort(struct XPR_IO * IO)
  53. {
  54.     int res;
  55.  
  56.     do
  57.         res = md_get(IO, STIME);
  58.     while ((res != DT_TIME) && (res != DT_ERR));
  59. }
  60.  
  61. /* chk_compute - compute the checksum */
  62. static int chk_compute(unsigned char *buf, int len)
  63. {
  64.     int chk, i;
  65.  
  66.     for (chk = i = 0; i < len; ++i)
  67.         chk += buf[i];
  68.     return (chk & 0xFF);
  69. }
  70.  
  71. #ifndef PRECOMPUTECRC
  72. /* crc_compute - compute the CRC value */
  73. static int crc_compute(unsigned char *buf, int len)
  74. {
  75.     int crc, shifter, highbit, i;
  76.  
  77.     buf[len++] = '\0';
  78.     buf[len++] = '\0';
  79.     for (crc = i = 0; i < len; ++i)
  80.         for (shifter = 0x80; shifter; shifter >>= 1) {
  81.             highbit = crc & 0x8000;
  82.             crc <<= 1;
  83.             crc |= (buf[i] & shifter ? 1 : 0);
  84.             if (highbit)
  85.                 crc ^= 0x1021;
  86.         }
  87.     return (crc & 0xFFFF);
  88. }
  89. #else
  90. /*
  91.  * Pre-computed CRC polynomials (crctab) by Mark G. Mendel, 7/86
  92.  * (Network Systems Corp.); Credit also due to Stephen Satchell
  93.  * (Satchell Evaluations) for the improved updcrc (crc_compute)
  94.  * function, and Chuck Forsberg (Omen Technology) who wrote RBSB.c
  95.  * which Stephen extracted the original CRC table & functions.
  96.  */
  97. static WTYPE crctab[256] = /* as calculated by initcrctab() */ {
  98.     0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
  99.     0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
  100.     0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
  101.     0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
  102.     0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
  103.     0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
  104.     0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
  105.     0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
  106.     0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
  107.     0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
  108.     0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
  109.     0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
  110.     0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
  111.     0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
  112.     0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
  113.     0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
  114.     0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
  115.     0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
  116.     0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
  117.     0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
  118.     0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
  119.     0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
  120.     0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
  121.     0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
  122.     0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
  123.     0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
  124.     0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
  125.     0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
  126.     0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
  127.     0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
  128.     0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
  129.     0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
  130. } ;
  131.  
  132. static WTYPE crc_compute(unsigned char *buf, int len)
  133. {
  134.      register WTYPE crc = 0;
  135.      register unsigned char *cp = buf;
  136.  
  137.     buf[len++] = '\0';
  138.     buf[len++] = '\0';
  139.  
  140.      while( len-- ) {
  141.           crc = crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ *cp++;
  142.      }
  143.  
  144.      return( crc );
  145. }
  146. #endif
  147.  
  148. static char *blkchk[] =
  149. {"Checksum", "CRC-16"};
  150. static char *timefmt = "%02ld:%02ld:%02ld";
  151.  
  152. static void bm_info(struct XPR_IO * IO, int error)
  153. {
  154.     long (*xupdate) (), (*xchkmisc) (void);
  155.     struct XPR_UPDATE xpru;
  156.     long secs, micros, elapsed;
  157.     char buff1[20], buff2[20];
  158.     int blksiz, Iblknum;
  159.  
  160.     blksiz=IO->xpr_data->msglength;
  161.     Iblknum=IO->xpr_data->blknum;
  162.  
  163.     xchkmisc = IO->xpr_chkmisc;
  164.  
  165.     if (xchkmisc)
  166.         xchkmisc();
  167.  
  168.     /*
  169.      * Calculate elapsed time in tenths of seconds
  170.      */
  171.     CurrentTime(&secs, µs);
  172.     secs -= IO->xpr_data->startsecs;
  173.     micros -= IO->xpr_data->startmics;
  174.     elapsed = (secs * 10L) + (micros / 100000L);
  175.  
  176.     /*
  177.      * Always update data rate and elapsed time
  178.      */
  179.     xpru.xpru_updatemask = XPRU_DATARATE | XPRU_ELAPSEDTIME;
  180.  
  181.     sprintf(buff1, timefmt, secs / 3600L, (secs / 60L) % 60L, secs % 60L);
  182.     xpru.xpru_elapsedtime = buff1;
  183.  
  184.     if (elapsed)
  185.         xpru.xpru_datarate = ((long) blksiz * Iblknum * 10L) / elapsed;
  186.     else
  187.         xpru.xpru_datarate = 0;
  188.  
  189.     /*
  190.      * If we know the file size and the data rate we can compute the
  191.      * estimated time.
  192.      */
  193.     if (IO->xpr_data->filsiz) {
  194.         if (xpru.xpru_datarate) {
  195.             xpru.xpru_updatemask |= XPRU_EXPECTTIME;
  196.             secs = IO->xpr_data->filsiz / xpru.xpru_datarate;
  197.             sprintf(buff2, timefmt, secs / 3600L, (secs / 60L) % 60L, secs % 60L);
  198.             xpru.xpru_expecttime = buff2;
  199.         }
  200.     }
  201.  
  202.     /*
  203.      * On error, timeout or otherwise, update different things
  204.      */
  205.     if (error == DT_ERR) {
  206.         IO->xpr_data->numerrs++;
  207.         xpru.xpru_updatemask |= XPRU_ERRORS;
  208.         xpru.xpru_errors = IO->xpr_data->numerrs;
  209.     } else if (error == DT_TIME) {
  210.         IO->xpr_data->numtime++;
  211.         xpru.xpru_updatemask |= XPRU_TIMEOUTS;
  212.         xpru.xpru_timeouts = IO->xpr_data->numtime;
  213.     } else {
  214.         xpru.xpru_updatemask |= XPRU_BLOCKS | XPRU_BLOCKSIZE |
  215.             XPRU_BLOCKCHECK | XPRU_BYTES;
  216.         xpru.xpru_blocks = (long) Iblknum;
  217.         xpru.xpru_blocksize = (long) blksiz;
  218.         xpru.xpru_bytes = (long) blksiz *(long) Iblknum;
  219.  
  220.         xpru.xpru_blockcheck = IO->xpr_data->crcmode ? blkchk[1] : blkchk[0];
  221.     }
  222.  
  223.     /*
  224.      * Do the actual update
  225.      */
  226.     if ((xupdate = IO->xpr_update) != NULL)
  227.         calla(xupdate, &xpru);
  228.  
  229.     return;
  230. }
  231.  
  232. /* mdsnd - send a buffer of data */
  233. static int mdsnd(struct XPR_IO * IO)
  234. {
  235.     int plength, retries, ch;
  236.  
  237.     /* setup the packet header */
  238.     IO->xpr_data->packet[0] = IO->xpr_data->msgstart;
  239.     IO->xpr_data->packet[1] = IO->xpr_data->blknum;
  240.     IO->xpr_data->packet[2] = ~IO->xpr_data->blknum;
  241.  
  242.     /* compute the block check code */
  243.     if (IO->xpr_data->crcmode) {    /* compute the CRC */
  244.         long dumb_index;/* workaround manx expression too complex bug */
  245.  
  246.         IO->xpr_data->chksum = (int)crc_compute(IO->xpr_data->buffer, IO->xpr_data->msglength);
  247.         IO->xpr_data->buffer[IO->xpr_data->msglength] = IO->xpr_data->chksum >> 8;
  248.         dumb_index = IO->xpr_data->msglength + 1;
  249.         IO->xpr_data->buffer[dumb_index] = IO->xpr_data->chksum;
  250.         plength = IO->xpr_data->msglength + 5;
  251.     } else {        /* compute the checksum */
  252.         long dumb_index;/* workaround manx expression too complex bug */
  253.  
  254.         IO->xpr_data->chksum = chk_compute(IO->xpr_data->buffer, IO->xpr_data->msglength);
  255.         dumb_index = IO->xpr_data->msglength;
  256.         IO->xpr_data->buffer[dumb_index] = IO->xpr_data->chksum;
  257.         plength = IO->xpr_data->msglength + 4;
  258.     }
  259.  
  260.     /* send data and wait for an ACK */
  261.     for (retries = 0; retries < RETRY; retries++) {
  262.  
  263.         /* send the data */
  264.         md_write(IO, (char *)IO->xpr_data->packet, plength);
  265.  
  266.         /* return on an ACK */
  267.         if ((ch = md_get(IO, LTIME)) == ACK)
  268.             return (DT_OK);
  269.  
  270.         /* abort transfer on two successive CAN's */
  271.         else if (ch == CAN) {
  272.             if ((ch = md_get(IO, STIME)) == ACK)
  273.                 return (DT_OK);
  274.             else if (ch == CAN)
  275.                 break;
  276.         } else if (ch == DT_ERR) {
  277.             dlabort(IO);
  278.             md_put(IO, CAN);
  279.             md_put(IO, CAN);
  280.             return (DT_ERR);
  281.         }
  282.         bm_info(IO, ch);
  283.     }
  284.  
  285.     /* return failure */
  286.     return (DT_ERR);
  287. }
  288.  
  289. /* msgput - put a message data character */
  290. static int msgput(struct XPR_IO * IO, int ch)
  291. {
  292.     int sts;
  293.  
  294.     IO->xpr_data->buffer[IO->xpr_data->bufptr++] = ch;
  295.     if (IO->xpr_data->bufptr == IO->xpr_data->msglength) {
  296.         bm_info(IO, 0);
  297.         if ((sts = mdsnd(IO)) != DT_OK)
  298.             return (sts);
  299.         ++IO->xpr_data->blknum;
  300.         IO->xpr_data->bufptr = 0;
  301.     }
  302.     return (ch);
  303. }
  304.  
  305. /* mdrcv - receive a buffer of data */
  306. static int mdrcv(struct XPR_IO * IO)
  307. {
  308.     int plength, i, ch, retries;
  309.  
  310.     /* receive data */
  311.     for (retries = 0; retries < RETRY + 1; retries++) {
  312.  
  313.         /* check for data packet or eot */
  314.         if ((ch = md_get(IO, LTIME)) == DT_TIME) {
  315.             bm_info(IO, ch);
  316.             md_put(IO, IO->xpr_data->nak);
  317.             continue;
  318.         } else if (ch == DT_ERR) {
  319.             bm_info(IO, ch);
  320.             dlabort(IO);
  321.             md_put(IO, CAN);
  322.             md_put(IO, CAN);
  323.             return (DT_ERR);
  324.         } else if (ch == EOT) {    /* end of transfer */
  325.             md_put(IO, ACK);
  326.             return (DT_EOF);
  327.         } else if (ch == SOH)    /* start of a short packet */
  328.             IO->xpr_data->msglength = SMSGLEN;
  329.         else if (ch == STX)    /* start of a long packet */
  330.             IO->xpr_data->msglength = LMSGLEN;
  331.         else {
  332.             bm_info(IO, DT_ERR);
  333.             dlabort(IO);
  334.             md_put(IO, IO->xpr_data->nak);
  335.             continue;
  336.         }
  337.  
  338.         /* reset the NAK character */
  339.         IO->xpr_data->nak = NAK;
  340.  
  341.         /* compute the packet length */
  342.         plength = IO->xpr_data->msglength + (IO->xpr_data->crcmode ? 5 : 4);
  343.  
  344.         /* receive the data */
  345.         for (i = 1; i < plength; IO->xpr_data->packet[i++] = ch) {
  346.             if ((ch = md_get(IO, STIME)) == DT_TIME)
  347.                 break;
  348.             else if (ch == DT_ERR)
  349.                 break;
  350.         }
  351.  
  352.         /* check for timeout */
  353.         if (ch == DT_TIME) {
  354.             bm_info(IO, ch);
  355.             md_put(IO, NAK);
  356.             continue;
  357.         }
  358.         if (ch == DT_ERR) {
  359.             bm_info(IO, ch);
  360.             dlabort(IO);
  361.             md_put(IO, CAN);
  362.             md_put(IO, CAN);
  363.             return (DT_ERR);
  364.         }
  365.         /* check the block number */
  366.         if (IO->xpr_data->packet[1] != (~IO->xpr_data->packet[2] & 0xFF)) {
  367.             bm_info(IO, DT_ERR);
  368.             md_put(IO, NAK);
  369.             continue;
  370.         }
  371.         /* check the block check code */
  372.         if (IO->xpr_data->crcmode) {    /* CRC */
  373.             IO->xpr_data->chksum = (IO->xpr_data->buffer[IO->xpr_data->msglength] << 8) | IO->xpr_data->buffer[IO->xpr_data->msglength + 1];
  374.             if (IO->xpr_data->chksum != (int)crc_compute(IO->xpr_data->buffer, IO->xpr_data->msglength)) {
  375.                 bm_info(IO, DT_ERR);
  376.                 md_put(IO, NAK);
  377.                 continue;
  378.             }
  379.         } else {    /* checksum */
  380.             IO->xpr_data->chksum = IO->xpr_data->buffer[IO->xpr_data->msglength];
  381.             if (IO->xpr_data->chksum != chk_compute(IO->xpr_data->buffer, IO->xpr_data->msglength)) {
  382.                 bm_info(IO, DT_ERR);
  383.                 md_put(IO, NAK);
  384.                 continue;
  385.             }
  386.         }
  387.  
  388.         /* check the block number */
  389.         if (IO->xpr_data->packet[1] == (IO->xpr_data->blknum & 0xFF)) {
  390.             md_put(IO, ACK);
  391.             return (DT_OK);
  392.         } else if (IO->xpr_data->packet[1] != ((IO->xpr_data->blknum - 1) & 0xFF)) {
  393.             bm_info(IO, DT_ERR);
  394.             dlabort(IO);
  395.             md_put(IO, CAN);
  396.             md_put(IO, CAN);
  397.             return (DT_ERR);
  398.         }
  399.         /* send a ack */
  400.         md_put(IO, ACK);
  401.     }
  402.  
  403.     /* return failure */
  404.     return (DT_ERR);
  405. }
  406.  
  407. /* msgget - get a message data character */
  408. static int msgget(struct XPR_IO * IO)
  409. {
  410.     int sts;
  411.  
  412.     if (IO->xpr_data->bufptr == IO->xpr_data->msglength) {
  413.         if ((sts = mdrcv(IO)) != DT_OK)
  414.             return (sts);
  415.         bm_info(IO, 0);
  416.         ++IO->xpr_data->blknum;
  417.         IO->xpr_data->bufptr = 0;
  418.     }
  419.     return (IO->xpr_data->buffer[IO->xpr_data->bufptr++]);
  420. }
  421.  
  422. /* dl_snd - send data across the link */
  423. int dl_snd(struct XPR_IO * IO, int (*getch) (struct XPR_IO * IO))
  424. {
  425.     int ch, retries;
  426.  
  427.     IO->xpr_data->abort = 0;
  428.  
  429.     /* setup message length */
  430.     if (IO->xpr_data->big) {
  431.         IO->xpr_data->msgstart = STX;
  432.         IO->xpr_data->msglength = LMSGLEN;
  433.     } else {
  434.         IO->xpr_data->msgstart = SOH;
  435.         IO->xpr_data->msglength = SMSGLEN;
  436.     }
  437.  
  438.     /* initialize */
  439.     IO->xpr_data->blknum = 1;    /* start a block number 1 */
  440.     IO->xpr_data->bufptr = 0;    /* start with an empty buffer */
  441.  
  442.     /* wait for the initial NAK (or CRC) */
  443.     for (retries = 0; retries < RETRY; retries++)
  444.         if ((ch = md_get(IO, XTIME)) == NAK) {    /* start of checksum
  445.                              * transfer */
  446.             IO->xpr_data->crcmode = FALSE;
  447.             break;
  448.         } else if (IO->xpr_data->crc_conf && ch == CRC) {    /* start of CRC transfer */
  449.             IO->xpr_data->crcmode = TRUE;
  450.             break;
  451.         } else if (ch == DT_ERR) {
  452.             bm_info(IO, DT_ERR);
  453.             dlabort(IO);
  454.             return (FALSE);
  455.         } else if (ch == DT_TIME) {
  456.             bm_info(IO, DT_TIME);
  457.             dlabort(IO);
  458.             return (FALSE);
  459.         } else
  460.             dlabort(IO);
  461.  
  462.     /* check for failure */
  463.     if (retries >= RETRY)
  464.         return (FALSE);
  465.  
  466.     /* send each byte */
  467.     while ((ch = (*getch) (IO)) != EOF)
  468.         if (msgput(IO, ch) == DT_ERR)
  469.             return (FALSE);
  470.  
  471.     /* flush partial buffer */
  472.     if (IO->xpr_data->bufptr > 0)
  473.         while (IO->xpr_data->bufptr > 0)
  474.             if (msgput(IO, 0) == DT_ERR)
  475.                 return (FALSE);
  476.  
  477.     /* send EOT and wait for ACK */
  478.     for (retries = 0; retries < RETRY; retries++) {
  479.         md_put(IO, EOT);
  480.         if (md_get(IO, LTIME) == ACK)
  481.             return (TRUE);
  482.     }
  483.  
  484.     /* return failure */
  485.     return (FALSE);
  486. }
  487.  
  488. /* dl_rcv - receive data across the link */
  489. int dl_rcv(struct XPR_IO * IO, void (*putch) (struct XPR_IO * IO, int ch))
  490. {
  491.     int ch;
  492.  
  493.     /* initialize */
  494.     IO->xpr_data->abort = 0;
  495.     IO->xpr_data->blknum = 1;
  496.     IO->xpr_data->bufptr = IO->xpr_data->msglength = SMSGLEN;
  497.  
  498.     /* send the initial NAK (or CRC) */
  499.     IO->xpr_data->nak = (IO->xpr_data->crc_conf ? CRC : NAK);
  500.     IO->xpr_data->crcmode = IO->xpr_data->crc_conf;
  501.     md_put(IO, IO->xpr_data->nak);
  502.  
  503.     /* receive each byte */
  504.     while ((ch = msgget(IO)) != DT_EOF && ch != DT_ERR)
  505.         (*putch) (IO, ch);
  506.  
  507.     /* return with status */
  508.     return (ch == DT_EOF);
  509. }
  510.  
  511. void bm_infoinit(struct XPR_IO * IO, int send, long size)
  512. {
  513.     long (*xupdate) ();
  514.     struct XPR_UPDATE xpru;
  515.     char buff[20];
  516.  
  517.     IO->xpr_data->numerrs = 0L;
  518.     IO->xpr_data->numtime = 0L;
  519.  
  520.     if (send)
  521.         strcpy(buff, "Send    ");
  522.     else
  523.         strcpy(buff, "Receive ");
  524.     if (IO->xpr_data->ascii)
  525.         strcat(buff, "Text  ");
  526.     else
  527.         strcat(buff, "Binary");
  528.  
  529.     xpru.xpru_updatemask = XPRU_MSG | XPRU_FILENAME |
  530.         XPRU_BLOCKSIZE | XPRU_BLOCKCHECK | XPRU_BYTES |
  531.         XPRU_BLOCKS | XPRU_TIMEOUTS | XPRU_ERRORS;
  532.     IO->xpr_data->filsiz = size;
  533.     if (size)
  534.         xpru.xpru_updatemask |= XPRU_FILESIZE;
  535.  
  536.     xpru.xpru_filename = IO->xpr_filename;
  537.     xpru.xpru_filesize = size;
  538.     xpru.xpru_msg = buff;
  539.     xpru.xpru_blocks = 0L;
  540.     xpru.xpru_errors = 0L;
  541.     xpru.xpru_timeouts = 0L;
  542.     xpru.xpru_blocksize = IO->xpr_data->big ? 1024L : 128L;
  543.     xpru.xpru_blockcheck = IO->xpr_data->crc_conf ? blkchk[1] : blkchk[0];
  544.     xpru.xpru_bytes = 0L;
  545.  
  546.     if ((xupdate = IO->xpr_update) != NULL)
  547.         calla(xupdate, &xpru);
  548.  
  549.     CurrentTime(&IO->xpr_data->startsecs, &IO->xpr_data->startmics);
  550.  
  551.     return;
  552. }
  553.