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