home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 592b.lha / XprZmodem_v2.50 / Receive.c < prev    next >
C/C++ Source or Header  |  1991-11-19  |  19KB  |  626 lines

  1. /**********************************************************************
  2.  * Receive.c:  File reception routines for xprzmodem.library;
  3.  * Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Based closely on Chuck Forsberg's rz.c example ZModem code,
  5.  * but too pervasively modified to even think of detailing the changes.
  6.  * Released to the Public Domain; do as you like with this code.
  7.  *
  8.  * Version 2.50, 15 November 1991, CRC-32 additions by William M. Perkins.
  9.  **********************************************************************/
  10.  
  11. #include <proto/all.h>
  12. #include <exec/types.h>
  13. #include <ctype.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include "xproto.h"
  18. #include "zmodem.h"
  19. #include "xprzmodem.h"
  20.  
  21. #ifdef DEBUGLOG
  22. extern void *DebugLog;
  23. #endif
  24.  
  25. /**********************************************************
  26.  *    long XProtocolReceive(struct XPR_IO *xio)
  27.  *
  28.  * Main file reception routine; called by comm program
  29.  **********************************************************/
  30. long __saveds __asm XProtocolReceive(register __a0 struct XPR_IO *xio)
  31. {
  32.    struct SetupVars *sv;
  33.    struct Vars *v;
  34.    UBYTE err = FALSE;
  35.  
  36.    /* Perform common setup and initializations */
  37.    if (! (v = setup(xio)))
  38.       return XPRS_FAILURE;
  39.    v->Tryzhdrtype = ZRINIT;
  40.    v->Rxtimeout = 100;
  41.  
  42.    sv = (void *) v->io.xpr_data;
  43.    if (sv->bufpos)
  44.    {
  45.       v->Modemchar = v->Modembuf;
  46.       if (sv->buflen > sizeof(v->Modembuf))
  47.      sv->buflen = sizeof(v->Modembuf);
  48.       memcpy(v->Modembuf, sv->bufpos,sv->buflen);
  49.       v->Modemcount = sv->buflen;
  50.       }
  51.  
  52.    /* Transfer the files */  
  53.    if (rcvbatch(v) == ERROR)
  54.    {
  55.       upderr(v, "Download cancelled or timed out");
  56.       err = TRUE;
  57.       }
  58.    else
  59.       updmsg(v, "Done.");
  60.  
  61.    /* Clean up and return */
  62.    if (v->io.xpr_setserial && v->Oldstatus != -1)
  63.       (*v->io.xpr_setserial)(v->Oldstatus);
  64.    FreeMem(v->Filebuf, v->Filebufmax);
  65.    FreeMem(v, (long) sizeof(struct Vars));
  66.  
  67. #ifdef DEBUGLOG
  68.    if (DebugLog)
  69.    {
  70.       xpr_fclose(&v->io, DebugLog);
  71.       DebugLog = NULL;
  72.       }
  73. #endif
  74.  
  75.    return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
  76.    }    /* End of long XProtocolReceive() */
  77.  
  78. /**********************************************************
  79.  *    short rcvbatch(struct Vars *v)
  80.  *
  81.  * Start the batch transfer
  82.  **********************************************************/
  83. short rcvbatch(struct Vars *v)
  84. {
  85.    switch (tryz(v))
  86.    {
  87.    case ZCOMPL:
  88.       return OK;
  89.    case ZFILE:
  90.       if (rzfiles(v) == OK)
  91.      return OK;
  92.       }
  93.    canit(v);
  94.    return ERROR;
  95.    }    /* End of short rcvbatch() */
  96.  
  97. /**********************************************************
  98.  *    short tryz(struct Vars *v)
  99.  *
  100.  * Negotiate with sender to start a file transfer
  101.  **********************************************************/
  102. short tryz(struct Vars *v)
  103. {
  104.    short n, errors = 0;
  105.  
  106.    for (n = v->ErrorLimit; --n >= 0; )
  107.    {
  108.       /* Set max frame length and capability flags */
  109.       stohdr(v, (long) v->Tframlen);
  110.       v->Txhdr[ZF0] = CANFC32 | CANFDX | CANOVIO;
  111.       zshhdr(v, v->Tryzhdrtype);
  112.       sendbuf(v);
  113. again:
  114.       /* Check for abort from comm program */
  115.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
  116.          return ERROR;
  117.       switch (zgethdr(v))
  118.       {
  119.       case ZFILE:    /* File name and info packet */
  120.          v->Zconv  = v->Rxhdr[ZF0];  /* Suggested txt mode; ZCNL = text, */
  121.                      /* ZCBIN = binary, 0 = don't know. */
  122.          v->Zmanag = v->Rxhdr[ZF1];  /* Suggested file management mode. */
  123.          v->Ztrans = v->Rxhdr[ZF2];  /* Suggested file transport mode. */
  124.          v->Tryzhdrtype = ZRINIT;
  125.          if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW)
  126.         return ZFILE;
  127.          zshhdr(v, ZNAK);   /* Packet mangled, ask for retry */
  128.          sendbuf(v);
  129.          goto again;
  130.       case ZSINIT:   /* Special attention-grabbing string to use to */
  131.              /* interrupt sender */
  132.          if (zrdata(v, v->Attn, ZATTNLEN) == GOTCRCW)
  133.             zshhdr(v, ZACK);
  134.          else
  135.             zshhdr(v, ZNAK);
  136.          sendbuf(v);
  137.          goto again;
  138.       case ZFREECNT: /* Sender wants to know how much room we've got */
  139.          stohdr(v, getfree());
  140.          zshhdr(v, ZACK);
  141.          sendbuf(v);
  142.          goto again;
  143.       case ZCOMMAND: /* Sender wants us to do remote commands, */
  144.              /* but we don't do requests. */
  145.          if (zrdata(v, v->Pktbuf, KSIZE) == GOTCRCW)
  146.      {
  147.             mysprintf(v->Msgbuf, "Ignoring command: %s", v->Pktbuf);
  148.             upderr(v, v->Msgbuf); /* Ignore and report all uploaded commands */
  149.             stohdr(v, 0L);        /* whilst telling sender they worked; */
  150.             do
  151.         {
  152.                zshhdr(v, ZCOMPL); /* paranoia can be good for you... */
  153.                sendbuf(v);
  154.                }
  155.         while (++errors < v->ErrorLimit && zgethdr(v) != ZFIN);
  156.             ackbibi(v);
  157.             return ZCOMPL;
  158.             }
  159.      else
  160.             zshhdr(v, ZNAK);
  161.          sendbuf(v);
  162.          goto again;
  163.       case ZCOMPL:
  164.          goto again;
  165.       case ZFIN:    /* Sender has ended batch */
  166.          ackbibi(v);
  167.          return ZCOMPL;
  168.       case ZCAN:
  169.       case RCDO:
  170.          upderr(v, v->Msgbuf);
  171.          return ERROR;
  172.          }
  173.       }
  174.    return ERROR;
  175.    }    /* End of short tryz() */
  176.  
  177. /**********************************************************
  178.  *    short rzfiles(struct Vars *v)
  179.  *
  180.  * Receive a batch of files
  181.  **********************************************************/
  182. short rzfiles(struct Vars *v)
  183. {
  184.    struct SetupVars *sv;
  185.    short c;
  186.  
  187.    /* Keep receiving files until end of batch or error */
  188.    while (TRUE)
  189.    {
  190.       switch (c = rzfile(v))
  191.       {
  192.       case ZEOF:
  193.       case ZSKIP:
  194.          switch (tryz(v))
  195.      {
  196.          case ZCOMPL:
  197.             return OK;
  198.          default:
  199.             return ERROR;
  200.          case ZFILE:
  201.             break;
  202.             }
  203.          break;
  204.       default:
  205.          bfclose(v);
  206.          sv = (void *) v->io.xpr_data;
  207.          if (*sv->option_k == 'N' && v->io.xpr_extension >= 2
  208.         && v->io.xpr_unlink)
  209.      {
  210.             updmsg(v, "Deleting partially received file");
  211.             (*v->io.xpr_unlink)(v->Filename);
  212.             }
  213.      else
  214.         updmsg(v, "Keeping partially received file");
  215.          return c;
  216.          }
  217.       }
  218.    }    /* End of short rzfiles() */
  219.  
  220. /**********************************************************
  221.  *    short rzfile(struct Vars *v)
  222.  *
  223.  * Receive one file; file name packet already read into
  224.  * Pktbuf by tryz()
  225.  **********************************************************/
  226. short rzfile(struct Vars *v)
  227. {
  228.    short c, n;
  229.  
  230.    /*
  231.     * Process file name packet; either open file and prepare to receive,
  232.     * or tell us to skip this one.
  233.     */
  234.    if (procheader(v) == ERROR)
  235.       return v->Tryzhdrtype = ZSKIP;
  236.  
  237.    n = v->ErrorLimit;
  238.    v->Rxbytes = v->Strtpos;
  239.    v->Eofseen = FALSE;
  240.  
  241.    /* Receive ZDATA frames until finished */
  242.    while (TRUE)
  243.    {
  244.       stohdr(v, v->Rxbytes);      /* Tell sender where to start frame */
  245.       zshhdr(v, ZRPOS);
  246.       sendbuf(v);
  247. nxthdr:
  248.       /* Check for abort from comm program */
  249.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
  250.          return ERROR;
  251.       switch (c = zgethdr(v))    /* Wait for frame header */
  252.       {
  253.       default:
  254. #ifdef DEBUGLOG
  255.          mysprintf(v->Msgbuf, "rzfile: zgethdr returned %ld\n", (long) c);
  256.          dlog(v, v->Msgbuf);
  257. #endif
  258.          return ERROR;
  259.       case ZNAK:
  260.       case TIMEOUT:
  261.          if (--n < 0)
  262.         return ERROR;
  263. #ifdef DEBUGLOG
  264.          dlog(v, "rzfile: zgethdr NAK/Timeout\n");
  265. #endif
  266.          v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
  267.          mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
  268.         v->Rxbytes, (long) n);
  269.          v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  270.          ++v->xpru.xpru_timeouts;
  271.          (*v->io.xpr_update)(&v->xpru);
  272.          continue;
  273.       case ZFILE:     /* Sender didn't see our ZRPOS yet; try again */
  274.          zrdata(v, v->Pktbuf, KSIZE);   /* Read and discard redundant */
  275.      continue;            /* filename packet */
  276.       case ZEOF:      /* End of file data */
  277.          if (v->Rxpos != v->Rxbytes)    /* We aren't in sync; go back */
  278.      {
  279.             mysprintf(v->Msgbuf, "Bad EOF; here=%ld, there=%ld",
  280.            v->Rxbytes, v->Rxpos);
  281.             upderr(v, v->Msgbuf);
  282.             continue;
  283.             }
  284.          bfclose(v);  /* All done; close file */
  285. #ifdef DEBUGLOG
  286.          dlog(v, "rzfile: EOF\n");
  287. #endif
  288.          updmsg(v, "EOF received; checking for next file");
  289.          return c;
  290.       case ERROR:     /* Too much garbage while waiting for frame header */
  291.          if ( --n < 0)
  292.         return ERROR;
  293. #ifdef DEBUGLOG
  294.          dlog(v, "rzfile: zgethdr garbage overflow\n");
  295. #endif
  296.          v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  297.          mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
  298.         v->Rxbytes, (long) n);
  299.          v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  300.          ++v->xpru.xpru_errors;
  301.          (*v->io.xpr_update)(&v->xpru);
  302.          zmputs(v, v->Attn);
  303.          continue;
  304.       case ZDATA:     /* More file data packets forthcoming */
  305.          if (v->Rxpos != v->Rxbytes)     /* We aren't in sync; go back */
  306.      {
  307.             if ( --n < 0)
  308.            return ERROR;
  309.             v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  310.             mysprintf(v->Msgbuf, "Data at bad position; here=%ld, there=%ld",
  311.            v->Rxbytes, v->Rxpos);
  312.             v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  313.             ++v->xpru.xpru_errors;
  314.             (*v->io.xpr_update)(&v->xpru);
  315.             zmputs(v, v->Attn);
  316.             continue;
  317.             }
  318.          /* Receive file data packet(s) */
  319. moredata:
  320.          /* Give comm program its timeslice if it needs one */
  321.          if (v->io.xpr_chkmisc)
  322.         (*v->io.xpr_chkmisc)();
  323.          /* Check for abort from comm program */
  324.          if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
  325.         goto aborted;
  326.          switch (c = zrdata(v, v->Pktbuf,KSIZE))
  327.      {
  328.      case ZCAN:
  329.          case RCDO:
  330. aborted:
  331. #ifdef DEBUGLOG
  332.             dlog(v, "rzfile: zrdata returned CAN\n");
  333. #endif
  334.             upderr(v, "Transfer cancelled");
  335.             return ERROR;
  336.          case ERROR:     /* CRC error or packet too long */
  337.             if ( --n < 0)
  338.            return ERROR;
  339. #ifdef DEBUGLOG
  340.             dlog(v, "rzfile: zrdata returned error\n");
  341. #endif
  342.             v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_ERRORS;
  343.             mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
  344.            v->Rxbytes, (long) n);
  345.             v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  346.             ++v->xpru.xpru_errors;
  347.             (*v->io.xpr_update)(&v->xpru);
  348. #ifdef DEBUGLOG
  349.             dlog(v, v->Msgbuf);
  350.             dlog(v, "\n");
  351. #endif
  352.             zmputs(v, v->Attn);
  353.             continue;
  354.          case TIMEOUT:
  355.             if ( --n < 0)
  356.            return ERROR;
  357. #ifdef DEBUGLOG
  358.             dlog(v, "rzfile: zrdata returned timeout\n");
  359. #endif
  360.             v->xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_TIMEOUTS;
  361.             mysprintf(strchr(v->Msgbuf, '\0'), "@ %ld; %ld retries left",
  362.            v->Rxbytes, (long) n);
  363.             v->xpru.xpru_errormsg = (char *) v->Msgbuf;
  364.             ++v->xpru.xpru_timeouts;
  365.             (*v->io.xpr_update)(&v->xpru);
  366. #ifdef DEBUGLOG
  367.             dlog(v, v->Msgbuf);
  368.             dlog(v, "\n");
  369. #endif
  370.             continue;
  371.          case GOTCRCW:   /* Sender says it's waiting for an ACK */
  372.             n = v->ErrorLimit;
  373.             if (putsec(v) == ERROR)
  374.            return ERROR;
  375.             stohdr(v, v->Rxbytes);
  376.             zshhdr(v, ZACK);
  377.             sendbuf(v);
  378.             goto nxthdr;
  379.          case GOTCRCQ:   /* Sender says it's not waiting, */
  380.              /* but ACK anyway (rarely used) */
  381.             n = v->ErrorLimit;
  382.             if (putsec(v) == ERROR)
  383.            return ERROR;
  384.             stohdr(v, v->Rxbytes);
  385.             zshhdr(v, ZACK);
  386.             sendbuf(v);
  387.             goto moredata;
  388.          case GOTCRCG:   /* Sender says keep receiving, there's more coming */
  389.             n = v->ErrorLimit;
  390.             if (putsec(v) == ERROR)
  391.            return ERROR;
  392.             goto moredata;
  393.          case GOTCRCE:   /* Sender says this is the last packet */
  394.             n = v->ErrorLimit;
  395.             if (putsec(v) == ERROR)
  396.            return ERROR;
  397.             goto nxthdr;
  398.             }
  399.          }
  400.       }
  401.    }    /* End of short rzfile() */
  402.  
  403. /**********************************************************
  404.  *    short procheader(struct Vars *v)
  405.  *
  406.  * Process file name & info packet; either open file and
  407.  * prepare to receive, or return ERROR if we should skip
  408.  * this one for some reason
  409.  **********************************************************/
  410. short procheader(struct Vars *v)
  411. {
  412.    struct SetupVars *sv;
  413.    UBYTE *p, *openmode, buff[PATHLEN];
  414.    long n;
  415.  
  416.    openmode = "w";
  417.    v->Strtpos = 0;
  418.  
  419.    /* Extract expected filesize from file info packet, if given */
  420.    v->Fsize = -1;
  421.    p = strchr(v->Pktbuf, '\0') + 1;
  422.    if (*p)
  423.       v->Fsize = atol(p);
  424.    /*
  425.     * Make sure we have room for file; skip it if not.
  426.     * Commented out for now, since getfree() isn't implemented yet.
  427.    if (v->Fsize > getfree())
  428.    {
  429.       mysprintf(v->Msgbuf, "Insufficient disk space; need %ld bytes, have %ld",
  430.      v->Fsize, getfree());
  431.       upderr(v, v->Msgbuf);
  432.       v->Noroom = TRUE;
  433.       return ERROR;
  434.       }
  435.    */
  436.  
  437.    /* If option RY set, use full received file path */
  438.    sv = (void *) v->io.xpr_data;
  439.    if (*sv->option_r == 'Y')
  440.       strcpy(v->Filename, v->Pktbuf);
  441.    else
  442.    {
  443.       /* else use the default directory path specified in the setup options */
  444.       strcpy(v->Filename, sv->option_p);
  445.       p = v->Filename + strlen(v->Filename) - 1;
  446.       if (p >= v->Filename && *p != '/' && *p != ':')
  447.      *++p = '/';
  448.       *++p = '\0';
  449.       /*
  450.        * Append the filename from the file info packet; ignore anything before
  451.        * last /, \, or : in filename (received directory path is ignored)
  452.        */
  453.       p = strchr(v->Pktbuf, '\0'); /* start at end and scan back */
  454.                    /* to start of name */
  455.       while (p >= v->Pktbuf && *p != '/' && *p != '\\' && *p != ':')
  456.      --p;
  457.       strcat(v->Filename, ++p);
  458.       }
  459.  
  460.    /* Display name of file being received for user */
  461.    v->xpru.xpru_updatemask = XPRU_FILENAME;
  462.    v->xpru.xpru_filename = (char *) v->Filename;
  463.    (*v->io.xpr_update)(&v->xpru);
  464.  
  465.    /*
  466.     * If a file with this name already exists, handle in
  467.     * accordance with O option
  468.     */
  469.    if (exist(v))
  470.    {
  471.       switch (*sv->option_o)
  472.       {
  473.       case 'N': /* Don't overwrite; change name to prevent collision */
  474.          strcpy(buff, v->Filename);
  475.          strcat(v->Filename, ".dup");
  476.          n = 2;
  477.          while (exist(v))
  478.      {
  479.             mysprintf(v->Filename, "%s.dup%ld", buff, n);
  480.             ++n;
  481.             }
  482.          /* Update filename display to show new name */
  483.          (*v->io.xpr_update)(&v->xpru);
  484.          break;
  485.       case 'R': /* Resume transfer from current end of file */
  486.          openmode = "a";
  487.          v->Strtpos = (*v->io.xpr_finfo)(v->Filename, 1L);
  488.          break;
  489.       case 'S': /* Skip it */
  490.          upderr(v, "File already exists; skipping");
  491.          return ERROR;
  492.          /* Else 'Y', go ahead and overwrite it (openmode = w) */
  493.          }
  494.       }
  495.  
  496.    /* Set text/binary mode according to options before opening file */
  497.    set_textmode(v);
  498.  
  499.    /*
  500.     * Figure out file translation mode to use; either binary (verbatim
  501.     * transfer) or ASCII (perform end-of-line conversions).  If user has
  502.     * specified a mode (TY or TN), that's what we use.  If user says use
  503.     * sender's suggestion (T?), set mode according to Zconv flag.  If neither
  504.     * side specifies, default to binary mode.
  505.     */
  506.    v->Thisbinary =  v->Rxbinary || !v->Rxascii;
  507.    if (! v->Rxbinary && v->Zconv == ZCNL)
  508.       v->Thisbinary = FALSE;
  509.    if (! v->Rxascii && v->Zconv == ZCBIN)
  510.       v->Thisbinary = TRUE;
  511.  
  512.    /* Open the file (finally) */
  513.    if (! (v->File = bfopen(v, openmode)))
  514.    {
  515.       ++v->Errcnt;
  516.       upderr(v, "Can't open file; skipping");
  517.       return ERROR;
  518.       }
  519.    getsystime(&v->Starttime);
  520.  
  521.    /* Initialize comm program transfer status display */
  522.    v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG 
  523.       | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
  524.       | XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME| XPRU_DATARATE;
  525.    v->xpru.xpru_protocol = "ZModem";
  526.    v->xpru.xpru_filesize = v->Fsize;
  527.    v->xpru.xpru_msg = (v->Thisbinary) ? "Receiving binary file..."
  528.       : "Receiving text file...";
  529.    v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
  530.    v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
  531.    v->xpru.xpru_bytes = v->Strtpos;
  532.    update_rate(v);
  533.    (*v->io.xpr_update)(&v->xpru);
  534.  
  535.    return OK;
  536.    }    /* End of short procheader() */
  537.  
  538. /**********************************************************
  539.  *    short putsec(struct Vars *v)
  540.  *
  541.  * Writes the received file data to the output file.
  542.  * If in ASCII mode, stops writing at first ^Z, and converts all
  543.  * \r\n pairs or solo \r's to \n's.
  544.  **********************************************************/
  545. short putsec(struct Vars *v)
  546. {
  547.    static char nl = '\n';
  548.    UBYTE *p;
  549.    short n;
  550.  
  551.    /* If in binary mode, write it out verbatim */
  552.    if (v->Thisbinary)
  553.    {
  554.       if (bfwrite(v, v->Pktbuf, (long) v->Rxcount) != v->Rxcount)
  555.      goto diskfull;
  556.       /* If in text mode, perform end-of-line cleanup */
  557.       }
  558.    else
  559.    {
  560.       if (v->Eofseen)
  561.      return OK;
  562.       for (p = v->Pktbuf, n = v->Rxcount; --n >= 0; ++p)
  563.       {
  564.      if (*p == CPMEOF)
  565.      {
  566.             v->Eofseen = TRUE;
  567.             return OK;
  568.         }
  569.      else if (*p != '\n' && v->Lastsent == '\r')
  570.      {
  571.             if (bfwrite(v, &nl, 1L) != 1)
  572.            goto diskfull;
  573.             }  
  574.          if (*p != '\r' && bfwrite(v, p, 1L) != 1)
  575.         goto diskfull;
  576.      v->Lastsent = *p;
  577.      }
  578.       }
  579.  
  580.    /* Update comm program status display */
  581.    v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
  582.       | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE | XPRU_BLOCKCHECK;
  583.    ++v->xpru.xpru_blocks;
  584.    v->xpru.xpru_blocksize = v->Rxcount;
  585.    v->xpru.xpru_bytes = v->Rxbytes += v->Rxcount;
  586.    v->xpru.xpru_blockcheck = v->Crc32 ? "CRC-32" : "CRC-16";
  587.    update_rate(v);
  588.    (*v->io.xpr_update)(&v->xpru);
  589.  
  590.    return OK;
  591.  
  592. diskfull:
  593.    upderr(v, "Error writing file; disk full?");
  594.    v->Noroom = TRUE;
  595.    return ERROR;
  596.    }    /* End of short putsec() */
  597.  
  598. /**********************************************************
  599.  *    void ackbibi(struct Vars *v)
  600.  *
  601.  * End of batch transmission; disengage cleanly from sender
  602.  **********************************************************/
  603. void ackbibi(struct Vars *v)
  604. {
  605.    short n;
  606.  
  607. #ifdef DEBUGLOG
  608.    dlog(v, "ackbibi:\n");
  609. #endif
  610.    stohdr(v, 0L);
  611.    for (n = 4; --n; )
  612.    {
  613.       zshhdr(v, ZFIN);
  614.       sendbuf(v);
  615.       switch (readock(v, 100))
  616.       {
  617.       case 'O':
  618.          readock(v, 1);    /* Discard 2nd 'O' */
  619.       case TIMEOUT:
  620.       case RCDO:
  621.          return;
  622.          }
  623.       }
  624.    }    /* End of void ackbibi() */
  625. /* End of Receive.c source */
  626.