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