home *** CD-ROM | disk | FTP | other *** search
/ Amiga Elysian Archive / AmigaElysianArchive.iso / comm / xprzedza.lzh / Send.c < prev    next >
C/C++ Source or Header  |  1992-11-30  |  21KB  |  698 lines

  1. /**********************************************************************
  2.  * Send.c: File transmission routines for xprzmodem.library;
  3.  * Original Version 2.10, 12 February 1991, by Rick Huebner.
  4.  * Based closely on Chuck Forsberg's sz.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.  * Version 2.51 29, January 1992, RX_timout fix by John Tillema
  10.  * Version 2.52   6 March 1992, Very minor fix with compiled 020 library
  11.  *               by William M. Perkins.
  12.  *
  13.  **********************************************************************
  14.  * Modified to support ZedZap/ZedZip fidonet protocol by
  15.  * Yves Konighofer and Russel McOrmond.
  16.  *
  17.  * Adapted to xprzmodem Version 2.52 by Andrea Cisternino
  18.  **********************************************************************/
  19.  
  20. #include <proto/all.h>
  21. #include <exec/types.h>
  22. #include <ctype.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include "xproto.h"
  26. #include "zmodem.h"
  27. #include "xprzmodem.h"
  28.  
  29. #ifdef DEBUGLOG
  30. extern void *DebugLog;
  31. #endif
  32.  
  33. /**********************************************************
  34.  * long XProtocolSend(struct XPR_IO *xio)
  35.  *
  36.  * Main file transmission routine; called by comm program
  37.  **********************************************************/
  38. long __saveds __asm XProtocolSend(register __a0 struct XPR_IO *xio)
  39. {
  40.    struct Vars *v;
  41.    short err;
  42.  
  43.    /* Perform common setup and initializations */
  44.    if (! (v = setup(xio)) ) return XPRS_FAILURE;
  45.  
  46. /*  was 600, set to 300 to fix so it uploads correctly */
  47.    v->Rxtimeout = 300;
  48.    v->Wantfcs32 = TRUE;
  49.    v->Rxflags = 0;
  50.  
  51.    /* first flush the inbound buffer */
  52.    if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
  53.  
  54.    /* Transfer the files */  
  55.    zmputs(v, "rz\r");
  56.    stohdr(v, 0L);
  57.    zshhdr(v, ZRQINIT);
  58.    sendbuf(v);
  59.    if (getzrxinit(v) == ERROR) upderr(v, "Upload cancelled or timed out");
  60.    else  sendbatch(v);
  61.  
  62.    /* Clean up and return */
  63.    if (err = v->Errcnt) upderr(v, "One or more files skipped due to errors");
  64.    else  updmsg(v, "Done.");
  65.  
  66.    if (v->io.xpr_setserial && v->Oldstatus != -1) (*v->io.xpr_setserial)(v->Oldstatus);
  67.    FreeMem(v->Filebuf, v->Filebufmax);
  68.    FreeMem(v, (long) sizeof(struct Vars));
  69.   
  70. #ifdef DEBUGLOG
  71.    if (DebugLog)
  72.    {
  73.       xpr_fclose(&v->io, DebugLog);
  74.       DebugLog = NULL;
  75.    }
  76. #endif
  77.    return (err) ? XPRS_FAILURE : XPRS_SUCCESS;
  78. }  /* End of long XProtocolSend() */
  79.  
  80. /**********************************************************
  81.  * short getzrxinit(struct Vars *v)
  82.  *
  83.  * Negotiate with receiver to start a file transfer
  84.  **********************************************************/
  85. short getzrxinit(struct Vars *v)
  86. {
  87.    short n;
  88.  
  89.    for (n = v->ErrorLimit; --n >= 0; )
  90.    {
  91.       /* Check for abort from comm program */
  92.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) return ERROR;
  93.       switch (zgethdr(v))
  94.       {
  95.          case ZCHALLENGE:        /* Echo receiver's challenge number */
  96.             stohdr(v, v->Rxpos);
  97.             zshhdr(v, ZACK);
  98.             sendbuf(v);
  99.             continue;
  100.  
  101.          case ZCOMMAND:          /* They didn't see our ZRQINIT; try again */
  102.             stohdr(v, 0L);
  103.             zshhdr(v, ZRQINIT);
  104.             sendbuf(v);
  105.             continue;
  106.  
  107.          case ZRINIT:            /* Receiver ready; get transfer parameters */
  108.             v->Rxflags = 0xFF & v->Rxhdr[ZF0];
  109.             v->Txfcs32 = (v->Wantfcs32 && (v->Rxflags & CANFC32));
  110.             v->Rxbuflen = ((USHORT) v->Rxhdr[ZP1] << 8) | v->Rxhdr[ZP0];
  111. #ifdef DEBUGLOG
  112.             mysprintf(v->Msgbuf, "Txfcs32=%ld Rxbuflen=%ld Tframlen=%ld\n",
  113.                   (long) v->Txfcs32, (long) v->Rxbuflen, (long) v->Tframlen);
  114.             dlog(v, v->Msgbuf);
  115. #endif
  116.  
  117.             /* Use shortest of the two side's max frame lengths */
  118.             if (v->Tframlen && (! v->Rxbuflen || v->Tframlen < v->Rxbuflen)) v->Rxbuflen = v->Tframlen;
  119. #ifdef DEBUGLOG
  120.             mysprintf(v->Msgbuf, "Rxbuflen=%ld\n", (long) v->Rxbuflen);
  121.             dlog(v, v->Msgbuf);
  122. #endif
  123.             return OK;
  124.  
  125.          case ZCAN:
  126.          case RCDO:
  127.          case TIMEOUT:
  128.             upderr(v, v->Msgbuf);
  129.             return ERROR;
  130.  
  131.          case ZRQINIT:
  132.             if (v->Rxhdr[ZF0] == ZCOMMAND) continue;
  133.             /* fallthrough... */
  134.  
  135.          default:
  136.             zshhdr(v, ZNAK);
  137.             sendbuf(v);
  138.             continue;
  139.  
  140.       }  /* End of "switch (zgethdr(v))" */
  141.  
  142.    }  /* End of "for (n = v->ErrorLimit; --n >= 0; )" */
  143.  
  144.    return ERROR;
  145. }  /* End of short getzrxinit() */
  146.  
  147. /**********************************************************
  148.  * void sendbatch(struct Vars *v)
  149.  *
  150.  * Send a batch of files
  151.  **********************************************************/
  152. void sendbatch(struct Vars *v)
  153. {
  154.    UBYTE single, done = FALSE;
  155.    long fstate;
  156.  
  157.    /* Lets just clear this in case the host program fails to set this */
  158.    v->Filcnt = 0;    /* Up to now we have not sent any files */
  159.  
  160.    /* If template routines not provided, must be single filename */
  161.    if (!(v->io.xpr_ffirst) || !(v->io.xpr_fnext))
  162.    {
  163.       single = TRUE;
  164.       strcpy(v->Filename,(v->io.xpr_filename) ? (v->io.xpr_filename) : "");
  165.       if (!(v->Filename[0]))
  166.       {
  167.          if (USEZERO) updmsg(v,"30-Detected No Files...");
  168.          else  upderr(v,"No files Are Specified");
  169.          done = TRUE;
  170.       }
  171.       /* Else use the template routines to get the first filename */
  172.    }
  173.    else
  174.    {
  175.       single = FALSE;
  176.       fstate = (*v->io.xpr_ffirst)(v->Filename, v->io.xpr_filename);
  177.       if (!(fstate) || !(v->Filename[0]))  /* There are no files found */
  178.       {
  179.          if (USEZERO)   /* That's OK... we allow this kind of thing */
  180.          {
  181.             updmsg(v,"30-Detected No files");
  182.             done = TRUE;
  183.          }
  184.          else     /* In that case, I'm leaving */
  185.          {
  186.             if (!(v->io.xpr_filename) || !(v->io.xpr_filename[0])) upderr(v,"No Files Are Specified");
  187.             else  upderr(v,"No Files Match Template");
  188.             return;
  189.          }
  190.       }
  191.    }
  192.  
  193.    /* If using templates, keep getting names & sending until done */
  194.    while (! done)
  195.    {
  196.       long ret;
  197.  
  198.       if ((ret=sendone(v)) == ERROR)
  199.       {
  200.          if(strlen(v->Filename)) updstatus(v,v->Filename,XPRS_FAILURE);
  201.          updmsg(v,"40-Transfer Aborted Due To Error");
  202.          return;
  203.       }
  204.       if(strlen(v->Filename))   /* Either OK or ZSKIP */
  205.       {                         /* Skip does not delete the file! */
  206.          updstatus(v,v->Filename,(ret==OK) ? XPRS_SUCCESS : XPRS_FAILURE);
  207.       }
  208.       if (single) break;
  209.       fstate = (*v->io.xpr_fnext)(fstate, v->Filename, v->io.xpr_filename);
  210.       done = !fstate;
  211.    }
  212.  
  213.    /* End batch and return; if we never got started, just cancel receiver */
  214.    if ((v->Filcnt == NULL) && (USEZERO == TRUE))
  215.    {
  216.       updmsg(v,"31-Sent No Files..."); /* We may have wanted to send no files */
  217.       /* Anyway, we had that option enabled and I just assumed that because */
  218.       /* no files could be sent we were not going to be sending any... */
  219.    }
  220.    else
  221.    {
  222.       if (v->Filcnt == NULL) updmsg(v,"32-Could Not Send Any Files...");
  223.       /* We did NOT want to send no files!!! */
  224.       /* Remember, if we even so much as find the file then ++v->Filcnt ! */
  225.    }
  226.  
  227.    /* If we opened up a file correctly or if we are doing  ZedZap... */
  228.    if ((v->Filcnt) || (USEZERO == TRUE)) saybibi(v);
  229.  
  230.    /* Else... well... we do this... (but not during ZedZap/ZedZip) */
  231.    else  canit(v);
  232.  
  233. }  /* End of void sendbatch() */
  234.  
  235. /**********************************************************
  236.  * short sendone(struct Vars *v)
  237.  *
  238.  * Send the file named in v->Filename
  239.  **********************************************************/
  240. short sendone(struct Vars *v)
  241. {
  242.    struct SetupVars *sv;
  243.  
  244. #ifdef DEBUGLOG
  245.    mysprintf(v->Msgbuf, "*** Sending %s\n", v->Filename);
  246.    dlog(v, v->Msgbuf);
  247. #endif
  248.  
  249.    /* Display name of file being sent for user */
  250.    v->xpru.xpru_updatemask = XPRU_FILENAME;
  251.    v->xpru.xpru_filename = v->Filename;
  252.    (*v->io.xpr_update)(&v->xpru);
  253.  
  254.    /* Set text/binary mode according to options before opening file */
  255.    set_textmode(v);
  256.  
  257.    /* Open the file, if possible */
  258.    if (! bfopen(v, "r"))
  259.    {
  260.       ++(v->Errcnt);
  261.       upderr(v, "Can't open file; skipping");
  262.       return OK;      /* pass over it, there may be others */
  263.    }
  264.  
  265.    ++(v->Filcnt);
  266.    getsystime(&v->Starttime);
  267.  
  268.    /* Kick off the file transfer */
  269.    sv = (void *) v->io.xpr_data;
  270.    switch (sendname(v))
  271.    {
  272.       case ZSKIP:
  273.          bfclose(v);    /* make sure it's closed! */
  274.          return ZSKIP;
  275.  
  276.       case OK:
  277.          bfclose(v);    /* make sure it's closed! */
  278.          /* File sent; if option DY, delete file after sending */
  279.          if (*sv->option_d == 'Y' && v->io.xpr_extension >= 2 && v->io.xpr_unlink)
  280.          {
  281.             updmsg(v, "41-Deleting file ...");
  282.             (*v->io.xpr_unlink)(v->Filename);
  283.          }
  284.          break;
  285.  
  286.       case ERROR:
  287.       default:
  288.          bfclose(v); /* make sure it's closed! */
  289.          ++(v->Errcnt);
  290.          return ERROR;
  291.    }  /* End of "switch (sendname(v))" */
  292.  
  293.    return OK;
  294. }  /* End of short sendone() */
  295.  
  296. /**********************************************************
  297.  * short sendname(struct Vars *v)
  298.  *
  299.  * Build file info block consisting of file name, length,
  300.  * time, and mode
  301.  **********************************************************/
  302. short sendname(struct Vars *v)
  303. {
  304.    struct SetupVars *sv;
  305.    UBYTE *p, *q, buff [32];
  306.  
  307.    /* Initialize comm program transfer status display */
  308.    v->Fsize = (v->io.xpr_finfo) ? (*v->io.xpr_finfo)(v->Filename, 1L) : -1;
  309.    v->xpru.xpru_updatemask = XPRU_PROTOCOL | XPRU_FILESIZE | XPRU_MSG
  310.       | XPRU_BLOCKS | XPRU_ERRORS | XPRU_TIMEOUTS | XPRU_BLOCKCHECK
  311.       | XPRU_BYTES | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
  312.    v->xpru.xpru_protocol = "ZedZap";
  313.    v->xpru.xpru_filesize = v->Fsize;
  314.    v->xpru.xpru_msg = (v->Lzconv == ZCNL) ? "Sending text file..." :
  315.       ( (v->Lzconv == ZCBIN) ? "Sending binary file..." : "Sending file...");
  316.    v->xpru.xpru_blocks = v->xpru.xpru_errors = v->xpru.xpru_timeouts = 0;
  317.    v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  318.    v->xpru.xpru_bytes = v->Strtpos = 0;
  319.    update_rate(v);
  320.    (*v->io.xpr_update)(&v->xpru);
  321.  
  322.    sv = (void *) v->io.xpr_data;
  323.    if (*sv->option_s == 'Y')
  324.    {
  325.       /* If "SY" option selected, send full path */
  326.       strcpy(v->Pktbuf, v->Filename);
  327.       p = v->Pktbuf + strlen(v->Pktbuf) + 1;
  328.    }
  329.    else
  330.    {
  331.       /* else extract outgoing file name without directory path */
  332.       for (p = v->Filename, q = v->Pktbuf ; *p; ++p, ++q)
  333.       {
  334.          if ((*q = *p) == '/' || *q == ':') q = v->Pktbuf - 1;
  335.       }
  336.       *q = '\0';
  337.       p = ++q;
  338.    }
  339.  
  340.    /* Zero out remainder of file info packet */
  341.    memset(p, 0, sizeof(v->Pktbuf) - (p - v->Pktbuf));
  342.  
  343.    /* Store file size, timestamp, and mode in info packet */
  344.    /*
  345.     * XPR spec doesn't provide a way to get the file timestamp or file mode,
  346.     * so we'll just fake it with the current time and a dummy 0.
  347.     */
  348.    stcl_o(buff, getsystime(NULL) + UnixTimeOffset);
  349.  
  350.    /* amiga.lib mysprintf() can't do %lo format, so we do it the hard way */
  351.    /* Yes, octal; ZModem was originally done on Unix, and they like octal there */
  352.    mysprintf(p, "%ld %s 0", (v->Fsize < 0) ? 0L : v->Fsize,buff);
  353.  
  354.    /* Send filename packet */
  355.    return zsendfile(v, (short) (p - v->Pktbuf + strlen(p) + 1));
  356.  
  357. }  /* End of short sendname() */
  358.  
  359. /**********************************************************
  360.  * short zsendfile(struct Vars *v, short blen)
  361.  *
  362.  * Send the filename packet and see if receiver will accept
  363.  * file
  364.  **********************************************************/
  365. short zsendfile(struct Vars *v, short blen)
  366. {
  367.    short c;
  368.  
  369.    while (TRUE)
  370.    {
  371.       v->Txhdr[ZF0] = v->Lzconv; /* Text or Binary mode; from config string */
  372.       v->Txhdr[ZF1] = LZMANAG;   /* Default file management mode */
  373.       v->Txhdr[ZF2] = LZTRANS;   /* Default file transport mode */
  374.       v->Txhdr[ZF3] = 0;
  375.       zsbhdr(v, ZFILE);
  376.       zsdata(v, blen, ZCRCW);
  377.       sendbuf(v);
  378. again:
  379.       /* Check for abort from comm program */
  380.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)())
  381.       {
  382.          bfclose(v);
  383.          return ERROR;
  384.       }
  385.       switch (c = zgethdr(v))
  386.       {
  387.          case ZRINIT:
  388.             goto again;
  389.  
  390.          case ZCAN:
  391.          case ZCRC:
  392.          case RCDO:
  393.          case TIMEOUT:
  394.          case ZABORT:
  395.          case ZFIN:
  396.             upderr(v, v->Msgbuf);
  397.             return ERROR;
  398.  
  399.          case ZSKIP:       /* Receiver doesn't want this one */
  400.             upderr(v, "SKIP command received");
  401.             bfclose(v);
  402.             return c;
  403.  
  404.          case ZRPOS:       /* Receiver wants it; this is starting position */
  405.             bfseek(v, v->Rxpos);
  406.             v->Strtpos = v->Txpos = v->Rxpos;
  407.             if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
  408.             v->Modemcount = 0;
  409.             return zsendfdata(v);
  410.  
  411.       }  /* End of "switch (c = zgethdr(v))" */
  412.  
  413.    }  /* End of "while (TRUE)" */
  414.  
  415. }  /* End of short zsendfile() */
  416.  
  417. /**********************************************************
  418.  * short zsendfdata(struct Vars *v)
  419.  *
  420.  * Send the file data
  421.  **********************************************************/
  422. short zsendfdata(struct Vars *v)
  423. {
  424.    short c, e, blklen, goodbytes = 0;
  425.    USHORT framelen, maxblklen, goodneeded = 512;
  426.  
  427.    /* Figure out max data packet size to send */
  428.    maxblklen = KSIZE;
  429.    if (v->Rxbuflen && maxblklen > v->Rxbuflen) maxblklen = v->Rxbuflen;
  430.    blklen = (v->Baud < 1200) ? 256 : KSIZE;
  431.    if (blklen > maxblklen) blklen = maxblklen;
  432.  
  433. #ifdef DEBUGLOG
  434.    mysprintf(v->Msgbuf, "Rxbuflen=%ld blklen=%ld\n", (long) v->Rxbuflen, (long) blklen);
  435.    dlog(v, v->Msgbuf);
  436. #endif
  437.  
  438.    /* If an interruption happened, handle it; else keep sending data */
  439. somemore:
  440.    while (char_avail(v))
  441.    {
  442.       /* Check for another incoming packet while discarding line noise */
  443.       switch (readock(v, 1))
  444.       {
  445.          case CAN:
  446.          case RCDO:
  447.          case ZPAD:
  448.             break;
  449.  
  450.          default:
  451.             continue;
  452.       }
  453.  
  454. waitack:
  455. #ifdef DEBUGLOG
  456.       dlog(v, "--- At waitack\n");
  457. #endif
  458.       switch (c = getinsync(v))
  459.       {
  460.          default:
  461.             upderr(v, "Transfer cancelled");
  462.             bfclose(v);
  463.             return ERROR;
  464.  
  465.          case ZSKIP:  /* Receiver changed its mind and wants to skip the file */
  466.             return c;
  467.  
  468.          case ZACK:   /* ACK at end of frame; resume sending data */
  469.             break;
  470.  
  471.          case ZRPOS:  /* An error; resend data from last good point */
  472.             blklen >>= 2;
  473.             if (blklen < MINBLOCK) blklen = MINBLOCK;
  474.             if (goodneeded < MAXGOODNEEDED) goodneeded <<= 1;
  475.             v->xpru.xpru_updatemask = XPRU_ERRORS;
  476.             ++v->xpru.xpru_errors;
  477.             (*v->io.xpr_update)(&v->xpru);
  478.             break;
  479.  
  480.          case ZRINIT:
  481.             updmsg(v, "Done.");
  482.             return OK;
  483.  
  484.       }  /* End of "switch (c = getinsync(v))" */
  485.  
  486.    }  /* End of "while (char_avail(v))" */
  487.  
  488.    /* Transmit ZDATA frame header */
  489.    framelen = v->Rxbuflen;
  490.    stohdr(v, v->Txpos);
  491.    zsbhdr(v, ZDATA);
  492.  
  493.    /* Keep sending data packets until finished or interrupted */
  494.    do
  495.    {
  496.       /* Read next chunk of file data */
  497.       c = bfread(v, v->Pktbuf, (long) blklen);
  498.  
  499.       /* Figure out how to handle this data packet */
  500.       if (c < blklen)
  501.       {
  502.          e = ZCRCE;     /* If end of file, this is last data packet */
  503.       }
  504.       else if (v->Rxbuflen && (framelen -= c) <= 0)
  505.       {
  506.          e = ZCRCW;     /* If end of frame, ask for ACK */
  507.       }
  508.       else  e = ZCRCG;  /* Else tell receiver to expect more data packets */
  509.  
  510.       zsdata(v, c, e);  /* Send the packet */
  511.       sendbuf(v);
  512.  
  513.       /* Update comm program status display */
  514.       v->xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_BLOCKSIZE | XPRU_BYTES
  515.             | XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE
  516.             | XPRU_BLOCKCHECK;
  517.       ++v->xpru.xpru_blocks;
  518.       v->xpru.xpru_blocksize = c;
  519.       v->xpru.xpru_blockcheck = v->Crc32t ? "CRC-32" : "CRC-16";
  520.       v->xpru.xpru_bytes = v->Txpos += c;
  521.       update_rate(v);
  522.       (*v->io.xpr_update)(&v->xpru);
  523.  
  524.       /*
  525.        * If we've been sending smaller than normal packets, see if it's
  526.        * time to bump the packet size up a notch yet
  527.        */
  528.       if (blklen < maxblklen && (goodbytes += c) >= goodneeded)
  529.       {
  530.          blklen <<= 1;
  531.          if (blklen > maxblklen) blklen = maxblklen;
  532.          goodbytes = 0;
  533.  
  534. #ifdef DEBUGLOG
  535.          mysprintf(v->Msgbuf, "Bumping packet size to %ld at %ld\n", (long) blklen, v->Txpos);
  536.          dlog(v, v->Msgbuf);
  537. #endif
  538.       }
  539.  
  540.       /* Give comm program its timeslice if it needs one */
  541.       if (v->io.xpr_chkmisc) (*v->io.xpr_chkmisc)();
  542.  
  543.       /* Check for abort from comm program */
  544.       if (v->io.xpr_chkabort && (*v->io.xpr_chkabort)()) goto aborted;
  545.  
  546.       /* If this was last packet in frame, go wait for ACK from receiver */
  547.       if (e == ZCRCW) goto waitack;
  548.  
  549.       /*
  550.        * Check if receiver trying to interrupt us; look for incoming packet
  551.        * while discarding line noise
  552.        */
  553.       while (char_avail(v))
  554.       {
  555.          switch (readock(v, 1))
  556.          {
  557.             case CAN:
  558.             case RCDO:
  559.             case ZPAD:
  560.                /* Interruption detected; stop sending and process complaint */
  561. #ifdef DEBUGLOG
  562.                dlog(v, "--- Interrupted send\n");
  563. #endif
  564.                zsdata(v, 0, ZCRCE);
  565.                sendbuf(v);
  566.                goto waitack;
  567.          }
  568.       }
  569.  
  570.    } while (e == ZCRCG);  /* If no interruption, keep sending data packets */
  571.  
  572.    /* Done sending file data; send EOF and wait for receiver to acknowledge */
  573.    while (TRUE)
  574.    {
  575.       updmsg(v, "Sending EOF");
  576.       stohdr(v, v->Txpos);
  577.       zsbhdr(v, ZEOF);
  578.       sendbuf(v);
  579.       switch (c = getinsync(v))
  580.       {
  581.          case ZACK:
  582.             continue;
  583.  
  584.          case ZRPOS:
  585.             goto somemore;
  586.  
  587.          case ZRINIT:
  588.             updmsg(v, "EOF acknowledged");
  589.             ++v->Starttime.tv_secs;
  590.             update_rate(v);
  591.             v->xpru.xpru_updatemask = XPRU_EXPECTTIME | XPRU_ELAPSEDTIME | XPRU_DATARATE;
  592.             (*v->io.xpr_update)(&v->xpru);
  593.             return OK;
  594.  
  595.          case ZSKIP:
  596.             return c;
  597.  
  598.          default:
  599. aborted:
  600.             upderr(v, "Transfer cancelled");
  601.             bfclose(v);
  602.             return ERROR;
  603.  
  604.       }  /* End of "switch (c = getinsync(v))" */
  605.  
  606.    }  /* End of "while (TRUE)" */
  607.  
  608. }  /* End of short zsendfdata() */
  609.  
  610. /**********************************************************
  611.  *    short getinsync(struct Vars *v)
  612.  *
  613.  * Respond to receiver's complaint, get back in sync with
  614.  * receiver
  615.  **********************************************************/
  616. short getinsync(struct Vars *v)
  617. {
  618.    short c;
  619.  
  620.    while (TRUE)
  621.    {
  622.  
  623. #ifdef DEBUGLOG
  624.       dlog(v, "--- At getinsync\n");
  625. #endif
  626.  
  627.       c = zgethdr(v);
  628.       if (v->io.xpr_sflush) (*v->io.xpr_sflush)();
  629.       v->Modemcount = 0;
  630.       switch (c)
  631.       {
  632.          case ZCAN:
  633.          case ZABORT:
  634.          case ZFIN:
  635.          case RCDO:
  636.          case TIMEOUT:
  637.             upderr(v, v->Msgbuf);
  638.             return ERROR;
  639.  
  640.          case ZRPOS:
  641.             bfseek(v, v->Rxpos);
  642.             v->Txpos = v->Rxpos;
  643.             mysprintf(v->Msgbuf, "Resending from %ld", v->Txpos);
  644.             upderr(v, v->Msgbuf);
  645.             return c;
  646.  
  647.          case ZSKIP:
  648.             upderr(v, "SKIP command received");
  649.  
  650.             /* fallthrough... */
  651.          case ZRINIT:
  652.             bfclose(v);
  653.  
  654.             /* fallthrough... */
  655.          case ZACK:
  656.             return c;
  657.  
  658.          default:
  659.             zsbhdr(v, ZNAK);
  660.             sendbuf(v);
  661.             continue;
  662.       }
  663.    }
  664. }  /* End of short getinsync() */
  665.  
  666. /**********************************************************
  667.  * void saybibi(struct Vars *v)
  668.  *
  669.  * End of batch transmission; disengage cleanly from receiver
  670.  **********************************************************/
  671. void saybibi(struct Vars *v)
  672. {
  673.    long q;
  674.  
  675.    while (TRUE)
  676.    {
  677.       stohdr(v, 0L);
  678.       zsbhdr(v, ZFIN);
  679.       sendbuf(v);
  680.       q = zgethdr(v);
  681.       switch (q)
  682.       {
  683.          case ZFIN:
  684.             sendline(v, 'O');
  685.             sendline(v, 'O');
  686.             sendbuf(v);
  687.  
  688.          /* fallthrough... */
  689.          case ZCAN:
  690.          case RCDO:
  691.          case TIMEOUT:
  692.             return;
  693.       }
  694.    }
  695. }  /* End of void saybibi() */
  696.  
  697. /* End of Send.c source */
  698.