/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*  (C) Copyright 1987-90, Bit Bucket Software Co., a Delaware Corporation. */
/*                                                                          */
/*                                                                          */
/*                    Zmodem file transmission module                       */
/*                                                                          */
/*                                                                          */
/*    For complete  details  of the licensing restrictions, please refer    */
/*    to the License  agreement,  which  is published in its entirety in    */
/*    the MAKEFILE and BT.C, and also contained in the file LICENSE.240.    */
/*                                                                          */
/*    USE  OF THIS FILE IS SUBJECT TO THE  RESTRICTIONS CONTAINED IN THE    */
/*    BINKLEYTERM  LICENSING  AGREEMENT.  IF YOU DO NOT FIND THE TEXT OF    */
/*    THIS  AGREEMENT IN ANY OF THE  AFOREMENTIONED FILES,  OR IF YOU DO    */
/*    NOT HAVE THESE FILES,  YOU  SHOULD  IMMEDIATELY CONTACT BIT BUCKET    */
/*    SOFTWARE CO.  AT ONE OF THE  ADDRESSES  LISTED BELOW.  IN NO EVENT    */
/*    SHOULD YOU  PROCEED TO USE THIS FILE  WITHOUT HAVING  ACCEPTED THE    */
/*    TERMS  OF  THE  BINKLEYTERM  LICENSING  AGREEMENT,  OR  SUCH OTHER    */
/*    AGREEMENT AS YOU ARE ABLE TO REACH WITH BIT BUCKET SOFTWARE, CO.      */
/*                                                                          */
/*                                                                          */
/* You can contact Bit Bucket Software Co. at any one of the following      */
/* addresses:                                                               */
/*                                                                          */
/* Bit Bucket Software Co.        FidoNet  1:104/501, 1:132/491, 1:141/491  */
/* P.O. Box 460398                AlterNet 7:491/0                          */
/* Aurora, CO 80046               BBS-Net  86:2030/1                        */
/*                                Internet f491.n132.z1.fidonet.org         */
/*                                                                          */
/* Please feel free to contact us at any time to share your comments about  */
/* our software and/or licensing policies.                                  */
/*                                                                          */
/*                                                                          */
/*  This module is based largely on a similar module in OPUS-CBCS V1.03b.   */
/*  The original work is (C) Copyright 1986, Wynn Wagner III. The original  */
/*  authors have graciously allowed us to use their code in this work.      */
/*                                                                          */
/*--------------------------------------------------------------------------*/


#include <stdio.h>
#include <ctype.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>

#ifdef __TOS__
#include <ext.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif

#ifdef unix
#include <errno.h>
#else
#include <errno.h>
#ifdef LATTICE
#define ultoa(v,s,r) (stcul_d(s,v),s)
#include <errno.h>
#else
#include <io.h>
#endif
#include <conio.h>
#endif

#include "bink.h"
#include "msgs.h"
#include "sbuf.h"
#include "zmodem.h"
#include "com.h"
#include "vfossil.h"
#include "session.h"
#include "wazoo.h"

/*--------------------------------------------------------------------------*/
/* Private routines                                                         */
/*--------------------------------------------------------------------------*/
static void ZS_SendBinaryHeader (unsigned short, byte *);
static void ZS_32SendBinaryHeader (unsigned short, byte *);
static void ZS_SendData (register byte *, size_t, unsigned short);
static void ZS_32SendData (byte *, size_t, unsigned short);
static void ZS_SendByte (byte);
static int ZS_GetReceiverInfo (void);
static int ZS_SendFile (size_t, int);
static int ZS_SendFileData (int);
static int ZS_SyncWithReceiver (int);
static void ZS_EndSend (void);

/*--------------------------------------------------------------------------*/
/* Private data                                                             */
/*--------------------------------------------------------------------------*/
static FILE *Infile;                             /* Handle of file being sent */
static long Strtpos;                             /* Starting byte position of */

 /* download                 */
static long LastZRpos;                           /* Last error location      */
static long ZRPosCount;                          /* ZRPOS repeat count       */
static long Txpos;                               /* Transmitted file position */
static int Rxbuflen;                             /* Receiver's max buffer    */

 /* length                   */
static int Rxflags;                              /* Receiver's flags         */

/*--------------------------------------------------------------------------*/
/* SEND ZMODEM (send a file)                                                */
/*   returns TRUE (1) for good xfer, FALSE (0) for bad                      */
/*   sends one file per call; 'fsent' flags start and end of batch          */
/*--------------------------------------------------------------------------*/
int Send_Zmodem (fname, alias, fsent, wazoo)
char *fname;
char *alias;
int fsent;
int wazoo;
{
   register byte *p;
   register byte *q;
   struct stat f;
   int i;
   int rc;
   char j[100];

#ifdef ZDEBUG
   show_debug_name ("send_Zmodem");
#endif
#ifdef DEBUG
   status_line(">send_Zmodem(%s,%s,%d,%d)",
   		fname ? fname : "NULL",
   		alias ? alias : "NULL",
   		fsent, wazoo);
#endif

   _BRK_DISABLE ();
   IN_XON_ENABLE ();

   z_size = 0;
   Infile = NULL;
#ifdef NEW  /* BUG,  22.08.1990   to avoid "may be used without having been initializes */
   rc = TRUE;
#endif

   if (fname && !(fullscreen && un_attended))
      set_xy ("");

   switch (fsent)
      {
      case 0:
         Z_PutString ("rz\r");
#ifdef NEW
 	  case EMPTY_BATCH:
#endif
         Z_PutLongIntoHeader (0L);
         Z_SendHexHeader (ZRQINIT, Txhdr);
         /* Fall through */

      case NOTHING_TO_DO:
         Rxtimeout = 200;
         if (ZS_GetReceiverInfo () == ERROR)
            {
            XON_DISABLE ();
            XON_ENABLE ();
#ifdef DEBUG
			status_line(">GetReceiver returned ERROR");
#endif
            return FALSE;
            }
#ifdef NEW
			CLEAR_OUTBOUND();
			CLEAR_INBOUND();
#endif
            
      }

   Rxtimeout = (int) (614400L / (long) cur_baud);

   if (Rxtimeout < 100)
      Rxtimeout = 100;

   if (fname == NULL)
      goto Done;

   /*--------------------------------------------------------------------*/
   /* Prepare the file for transmission.  Just ignore file open errors   */
   /* because there may be other files that can be sent.                 */
   /*--------------------------------------------------------------------*/
   Filename = fname;
   CLEAR_IOERR ();
   if ((Infile = fopen (Filename, read_binary)) == NULL)
      {
      (void) got_error (msgtxt[M_OPEN_MSG], Filename);
      XON_DISABLE ();
      XON_ENABLE ();

      return OK;
      }
   if (isatty (fileno (Infile)))
      {
      errno = 1;
      (void) got_error (msgtxt[M_DEVICE_MSG], Filename);
      fclose (Infile);
      XON_DISABLE ();
      XON_ENABLE ();

      return OK;
      }


   /*--------------------------------------------------------------------*/
   /* Send the file                                                      */
   /*--------------------------------------------------------------------*/
   rc = TRUE;

   /*--------------------------------------------------------------------*/
   /* Display outbound filename, size, and ETA for sysop                 */
   /*--------------------------------------------------------------------*/

#ifdef ATARIST
   stat(Filename, &f);
#else
   fstat (fileno (Infile), &f);
#endif   

   i = (int) (f.st_size * 10 / cur_baud + 53) / 54;
#ifdef NEW
	if(alias)
		sprintf (j, "Z-Send %s (%s), %ldb, %d min.", Filename, alias, f.st_size, i);
	else
#endif
   		sprintf (j, "Z-Send %s, %ldb, %d min.", Filename, f.st_size, i);
   file_length = f.st_size;

   if (un_attended && fullscreen)
      {
      clear_filetransfer ();
      sb_move (filewin, 1, 2);
      sb_puts (filewin, j);
      elapse_time ();
      sb_move (filewin, 2, 69);
      sprintf (j, "%3d min", i);
      sb_puts (filewin, j);
      sb_show ();
      }
   else
      {
      printf ("%s", j);
      set_xy (NULL);
      locate_x += 2;
      }


   /*--------------------------------------------------------------------*/
   /* Get outgoing file name; no directory path, lower case              */
   /*--------------------------------------------------------------------*/
   for (p = (alias != NULL) ? alias : Filename, q = Txbuf; *p;)
      {
      if ((*p == '/') || (*p == '\\') || (*p == ':'))
         q = Txbuf;
#ifdef unix
      else *q++ = *p;
#else
      else *q++ = tolower (*p);
#endif
      p++;
      }

   *q++ = '\0';
   p = q;

   /*--------------------------------------------------------------------*/
   /* Zero out remainder of file header packet                           */
   /*--------------------------------------------------------------------*/
   while (q < (Txbuf + KSIZE))
      *q++ = '\0';

   /*--------------------------------------------------------------------*/
   /* Store filesize, time last modified, and file mode in header packet */
   /*--------------------------------------------------------------------*/
   sprintf (p, "%lu %lo %ho", f.st_size, f.st_mtime, f.st_mode);

   /*--------------------------------------------------------------------*/
   /* Transmit the filename block and { the download                 */
   /*--------------------------------------------------------------------*/
   throughput (0, 0L);

   /*--------------------------------------------------------------------*/
   /* Check the results                                                  */
   /*--------------------------------------------------------------------*/
   switch (ZS_SendFile (1 + strlen (p) + (p - Txbuf), wazoo))
      {
      case ERROR:
         /*--------------------------------------------------*/
         /* Something tragic happened                        */
         /*--------------------------------------------------*/
#ifdef DEBUG
		status_line("#Unknown Zmodem error");
#endif
         goto Err_Out;

      case OK:
         /*--------------------------------------------------*/
         /* File was sent                                    */
         /*--------------------------------------------------*/
         CLEAR_IOERR ();
         fclose (Infile);
         Infile = NULL;

         status_line ("%s-Z%s %s", msgtxt[M_FILE_SENT], Crc32t ? "/32" : "", Filename);

         update_files (1);
         goto Done;

      case ZSKIP:
         status_line (msgtxt[M_REMOTE_REFUSED], Filename);
         rc = SPEC_COND;                         /* Success but don't
                                                  * truncate! */
         goto Done;

      default:
         /*--------------------------------------------------*/
         /* Ignore the problem, get next file, trust other   */
         /* error handling mechanisms to deal with problems  */
         /*--------------------------------------------------*/
#ifdef DEBUG
		status_line(">Default Zmodem error");
#endif
         goto Done;
      }                                          /* switch */

Err_Out:
   rc = FALSE;

Done:
   if (Infile)
      fclose (Infile);

   if (fsent < 0)
      ZS_EndSend ();

   XON_DISABLE ();
   XON_ENABLE ();

   return rc;
}                                                /* send_Zmodem */

/*--------------------------------------------------------------------------*/
/* ZS SEND BINARY HEADER                                                    */
/* Send ZMODEM binary header hdr of type type                               */
/*--------------------------------------------------------------------------*/
#ifdef unix
static void ZS_SendBinaryHeader (unsigned short type, byte *hdr)
#else
static void ZS_SendBinaryHeader (type, hdr)
unsigned short type;
register byte *hdr;
#endif
{
   register unsigned short crc;
   int n;

#ifdef ZDEBUG
   show_debug_name ("ZS_SendBinaryHeader");
#endif

   BUFFER_BYTE (ZPAD);
   BUFFER_BYTE (ZDLE);

   if (Crc32t = Txfcs32)
      ZS_32SendBinaryHeader (type, hdr);
   else
      {
      BUFFER_BYTE (ZBIN);
      ZS_SendByte ((byte) type);

      crc = Z_UpdateCRC (type, 0);

      for (n = 4; n-- ;)
         {
         ZS_SendByte (*hdr);
         crc = Z_UpdateCRC (((unsigned short) (*hdr++)), crc);
         }
      ZS_SendByte ((byte) (crc >> 8));
      ZS_SendByte ((byte) crc);

      UNBUFFER_BYTES ();
      }

   if (type != ZDATA)
      {
      while (CARRIER && !OUT_EMPTY ())
         time_release ();
      if (!CARRIER)
         CLEAR_OUTBOUND ();
      }
}                                                /* ZS_SendBinaryHeader */

/*--------------------------------------------------------------------------*/
/* ZS SEND BINARY HEADER                                                    */
/* Send ZMODEM binary header hdr of type type                               */
/*--------------------------------------------------------------------------*/
#ifdef unix
static void ZS_32SendBinaryHeader (unsigned short type,  byte *hdr)
#else
static void ZS_32SendBinaryHeader (type, hdr)
unsigned short type;
register byte *hdr;
#endif
{
   unsigned long crc;
   int n;

#ifdef ZDEBUG
   show_debug_name ("ZS_32SendBinaryHeader");
#endif

   BUFFER_BYTE (ZBIN32);
   ZS_SendByte ((byte) type);

   crc = 0xFFFFFFFF;
   crc = Z_32UpdateCRC (type, crc);

   for (n = 4; n--;)
      {
      ZS_SendByte (*hdr);
      crc = Z_32UpdateCRC (((unsigned short) (*hdr++)), crc);
      }

   crc = ~crc;
   for (n = 4; n--;)
      {
      ZS_SendByte ((byte) crc);
      crc >>= 8;
      }

   UNBUFFER_BYTES ();
}                                                /* ZS_SendBinaryHeader */

/*--------------------------------------------------------------------------*/
/* ZS SEND DATA                                                             */
/* Send binary array buf with ending ZDLE sequence frameend                 */
/*--------------------------------------------------------------------------*/
static void ZS_SendData (register byte *buf, size_t length, unsigned short frameend)
{
   register unsigned short crc;

#ifdef ZDEBUG
   show_debug_name ("ZS_SendData");
#endif

   if (Crc32t)
      ZS_32SendData (buf, length, frameend);
   else
      {
      crc = 0;
      for (; length--;)
         {
         ZS_SendByte (*buf);
         crc = Z_UpdateCRC (((unsigned short) (*buf++)), crc);
         }

      BUFFER_BYTE (ZDLE);
      BUFFER_BYTE ((unsigned char) frameend);
      crc = Z_UpdateCRC (frameend, crc);
      ZS_SendByte ((byte) (crc >> 8));
      ZS_SendByte ((byte) crc);

      UNBUFFER_BYTES ();

      }

   if (frameend == ZCRCW)
      {
      SENDBYTE (XON);
      while (CARRIER && !OUT_EMPTY ())
         time_release ();
      if (!CARRIER)
         CLEAR_OUTBOUND ();
      }
}                                                /* ZS_SendData */

/*--------------------------------------------------------------------------*/
/* ZS SEND DATA with 32 bit CRC                                             */
/* Send binary array buf with ending ZDLE sequence frameend                 */
/*--------------------------------------------------------------------------*/
static void ZS_32SendData (byte *buf, size_t length, unsigned short frameend)
{
   unsigned long crc;

#ifdef ZDEBUG
   show_debug_name ("ZS_32SendData");
#endif

   crc = 0xFFFFFFFF;
   for (; length--; ++buf)
      {
      ZS_SendByte (*buf);
      crc = Z_32UpdateCRC (((unsigned short) (*buf)), crc);
      }

   BUFFER_BYTE (ZDLE);
   BUFFER_BYTE ((unsigned char) frameend);
   crc = Z_32UpdateCRC (frameend, crc);

   crc = ~crc;

   for (length = 4; length--;)
      {
      ZS_SendByte ((byte) crc);
      crc >>= 8;
      }

   UNBUFFER_BYTES ();
}                                                /* ZS_SendData */

/*--------------------------------------------------------------------------*/
/* ZS SEND BYTE                                                             */
/* Send character c with ZMODEM escape sequence encoding.                   */
/* Escape XON, XOFF. Escape CR following @ (Telenet net escape)             */
/*--------------------------------------------------------------------------*/
#ifdef unix
static void ZS_SendByte (byte c)
#else
static void ZS_SendByte (byte c)
register byte c;
#endif
{
   static byte lastsent;

   switch (c)
      {
      case 015:
      case 0215:
         if ((lastsent & 0x7F) != '@')
            goto SendIt;
      case 020:
      case 021:
      case 023:
      case 0220:
      case 0221:
      case 0223:
      case ZDLE:
         /*--------------------------------------------------*/
         /* Quoted characters                                */
         /*--------------------------------------------------*/
         BUFFER_BYTE (ZDLE);
         c ^= 0x40;

      default:
         /*--------------------------------------------------*/
         /* Normal character output                          */
         /*--------------------------------------------------*/
   SendIt:
         BUFFER_BYTE (lastsent = c);

      }                                          /* switch */
}                                                /* ZS_SendByte */

/*--------------------------------------------------------------------------*/
/* ZS GET RECEIVER INFO                                                     */
/* Get the receiver's init parameters                                       */
/*--------------------------------------------------------------------------*/
static int ZS_GetReceiverInfo ()
{
   int n;

#ifdef ZDEBUG
   show_debug_name ("ZS_GetReceiverInfo");
#endif

   for (n = 10; n--;)
      {
      switch (Z_GetHeader (Rxhdr))
         {
         case ZCHALLENGE:
            /*--------------------------------------*/
            /* Echo receiver's challenge number     */
            /*--------------------------------------*/
            Z_PutLongIntoHeader (Rxpos);
            Z_SendHexHeader (ZACK, Txhdr);
            continue;

         case ZCOMMAND:
            /*--------------------------------------*/
            /* They didn't see our ZRQINIT          */
            /*--------------------------------------*/
            Z_PutLongIntoHeader (0L);
            Z_SendHexHeader (ZRQINIT, Txhdr);
            continue;

         case ZRINIT:
            /*--------------------------------------*/
            /* */
            /*--------------------------------------*/
            Rxflags = 0377 & Rxhdr[ZF0];
            Rxbuflen = ((word) Rxhdr[ZP1] << 8) | Rxhdr[ZP0];
            Txfcs32 = Rxflags & CANFC32;
            return OK;

         case ZCAN:
         case RCDO:
         case TIMEOUT:
            return ERROR;

         case ZRQINIT:
            if (Rxhdr[ZF0] == ZCOMMAND)
               continue;

         default:
            Z_SendHexHeader (ZNAK, Txhdr);
            continue;
         }                                       /* switch */
      }                                          /* for */

   return ERROR;
}                                                /* ZS_GetReceiverInfo */

/*--------------------------------------------------------------------------*/
/* ZS SEND FILE                                                             */
/* Send ZFILE frame and begin sending ZDATA frame                           */
/*--------------------------------------------------------------------------*/
static int ZS_SendFile (size_t blen, int wazoo)
{
   register int c;
   long t;

#ifdef ZDEBUG
   show_debug_name ("ZS_SendFile");
#endif

   for(;;)
      {
      if (got_ESC ())
         {
         CLEAR_OUTBOUND ();
         XON_DISABLE ();                         /* Make sure xmitter is
                                                  * unstuck */
         send_can ();                            /* transmit at least 10 cans    */
         t = timerset (200);                     /* wait no more than 2
                                                  * seconds  */
         while (!timeup (t) && !OUT_EMPTY () && CARRIER)
            time_release ();                     /* Give up slice while
                                                  * waiting  */
         XON_ENABLE ();                          /* Turn XON/XOFF back on...     */
         z_log (msgtxt[M_KBD_MSG]);
         return ERROR;
         }
      else if (!CARRIER)
         return ERROR;

      Txhdr[ZF0] = LZCONV;                       /* Default file conversion
                                                  * mode */
      Txhdr[ZF1] = LZMANAG;                      /* Default file management
                                                  * mode */
      Txhdr[ZF2] = LZTRANS;                      /* Default file transport
                                                  * mode */
      Txhdr[ZF3] = 0;
      ZS_SendBinaryHeader (ZFILE, Txhdr);
      ZS_SendData (Txbuf, blen, ZCRCW);

Again:
      switch (c = Z_GetHeader (Rxhdr))
         {
         case ZRINIT:
            while ((c = Z_GetByte (50)) > 0)
               if (c == ZPAD)
                  goto Again;

            /* Fall thru to */

         default:
            continue;

         case ZCAN:
         case RCDO:
         case TIMEOUT:
         case ZFIN:
         case ZABORT:
            return ERROR;

         case ZSKIP:
            /*-----------------------------------------*/
            /* Other system wants to skip this file    */
            /*-----------------------------------------*/
            return c;

         case ZRPOS:
            /*-----------------------------------------*/
            /* Resend from this position...            */
            /*-----------------------------------------*/
            fseek (Infile, Rxpos, SEEK_SET);
            if (Rxpos != 0L)
               {
               status_line (msgtxt[M_SYNCHRONIZING_OFFSET], Rxpos);
               CLEAR_OUTBOUND ();                /* Get rid of queued data */
               XON_DISABLE ();                   /* End XON/XOFF restraint */
               SENDBYTE (XON);                   /* Send XON to remote     */
               XON_ENABLE ();                    /* Start XON/XOFF again   */
               }
            LastZRpos = Strtpos = Txpos = Rxpos;
            ZRPosCount = 10;
            CLEAR_INBOUND ();
            return ZS_SendFileData (wazoo);
         }                                       /* switch */
      }                                          /* while */
}                                                /* ZS_SendFile */

/*--------------------------------------------------------------------------*/
/* ZS SEND FILE DATA                                                        */
/* Send the data in the file                                                */
/*--------------------------------------------------------------------------*/
static int ZS_SendFileData (wazoo)
int wazoo;
{
   register int c, e;
   size_t tsize;
   int i;
   char j[100];
   word goodblks, goodneeded = 1;
   size_t newcnt, maxblklen;	
#ifndef NEW
	size_t blklen;
#endif


   long t;

#ifdef ZDEBUG
   show_debug_name ("ZS_SendFileData");
#endif

   maxblklen = ((cur_baud >= 0) && (cur_baud < 300)) ? 128 : cur_baud / 300 * 256;

   if (maxblklen > WAZOOMAX)
      maxblklen = WAZOOMAX;
   if (!wazoo && maxblklen > KSIZE)
      maxblklen = KSIZE;
   if (Rxbuflen && maxblklen > Rxbuflen)
      maxblklen = Rxbuflen;

   if (wazoo && (remote_capabilities & ZED_ZIPPER))
      maxblklen = KSIZE;

#ifdef NEW /* BUG,  22.08.1990,  to avaid "possibly use before initialization" bug */
   goodblks = 0;
#endif
#ifdef NEW /* ZMODEMBLOCKLEN */
   if (!blklen)
      {
      if (small_window && (mdm_reliable[0]=='\0'))
         {
         blklen     = ((maxblklen >> 3) > 64) ? maxblklen >> 3 : 64;
         goodneeded = 6;
         }
      else
         {
         blklen = maxblklen;
         }
      }
#else
   blklen = maxblklen;
#endif

SomeMore:

   if (CHAR_AVAIL ())
      {
WaitAck:

      switch (c = ZS_SyncWithReceiver (1))
         {
         case ZSKIP:
            /*-----------------------------------------*/
            /* Skip this file                          */
            /*-----------------------------------------*/
            return c;

         case ZACK:
            break;

         case ZRPOS:
            /*-----------------------------------------*/
            /* Resume at this position                 */
            /*-----------------------------------------*/
            blklen = ((blklen >> 2) > 64) ? blklen >> 2 : 64;
            goodblks = 0;
            goodneeded = ((goodneeded << 1) > 16) ? 16 : goodneeded << 1;
            break;

         case ZRINIT:
            /*-----------------------------------------*/
            /* Receive init                            */
            /*-----------------------------------------*/
            if (locate_y && !(fullscreen && un_attended))
               gotoxy (2, (byte) locate_y - 1);
            throughput (1, Txpos - Strtpos);
            return OK;

         case TIMEOUT:
            /*-----------------------------------------*/
            /* Timed out on message from other side    */
            /*-----------------------------------------*/
            break;

         default:
            z_log (msgtxt[M_CAN_MSG]);
            fclose (Infile);
            return ERROR;
         }                                       /* switch */

      /*
       * Noise probably got us here. Odds of surviving are not good. But we
       * have to get unstuck in any event. 
       *
       */

      Z_UncorkTransmitter ();                    /* Get our side free if need
                                                  * be      */
      SENDBYTE (XON);                            /* Send an XON to release
                                                  * other side */

      while (CHAR_AVAIL ())
         {
         switch (MODEM_IN ())
            {
            case CAN:
            case RCDO:
            case ZPAD:
               goto WaitAck;
            }                                    /* switch */
         }                                       /* while */
      }                                          /* while */

   newcnt = Rxbuflen;
   Z_PutLongIntoHeader (Txpos);
   ZS_SendBinaryHeader (ZDATA, Txhdr);

   do
      {
      if (got_ESC ())
         {
         CLEAR_OUTBOUND ();
         XON_DISABLE ();                         /* Make sure xmitter is
                                                  * unstuck */
         send_can ();                            /* transmit at least 10 cans    */
         t = timerset (200);                     /* wait no more than 2
                                                  * seconds  */
         while (!timeup (t) && !OUT_EMPTY () && CARRIER)
            time_release ();                     /* Give up slice while
                                                  * waiting  */
         XON_ENABLE ();                          /* Turn XON/XOFF back on...     */
         z_log (msgtxt[M_KBD_MSG]);
         goto oops;
         }

      if (!CARRIER)
         goto oops;

      if ((tsize = fread (Txbuf, 1, (size_t) blklen, Infile)) != z_size)
         {
         if (fullscreen && un_attended)
            {
            sb_move (filewin, 2, 12);
            sb_puts (filewin, ultoa (((unsigned long) (z_size = tsize)), e_input, 10));
            sb_puts (filewin, "    ");
            elapse_time ();
            sb_show ();
            }
         else
            {
            gotoxy (locate_x + 10, locate_y);
            cputs (ultoa (((unsigned long) (z_size = tsize)), e_input, 10));
            putch (' ');
            }
         }

      if (tsize < blklen)
         e = ZCRCE;
      else if (Rxbuflen && (newcnt -= tsize) <= 0)
         e = ZCRCW;
      else e = ZCRCG;

      ZS_SendData (Txbuf, tsize, e);

      i = (int) ((file_length - Txpos) * 10 / cur_baud + 53) / 54;
      sprintf (j, "%3d min", i);

      if (fullscreen && un_attended)
         {
         sb_move (filewin, 2, 2);
         sb_puts (filewin, ultoa (((unsigned long) Txpos), e_input, 10));
         sb_puts (filewin, "  ");
         sb_move (filewin, 2, 69);
         sb_puts (filewin, j);
         elapse_time ();
         sb_show ();
         }
      else
         {
         gotoxy (locate_x, locate_y);
         cputs (ultoa (((unsigned long) Txpos), e_input, 10));
         putch (' ');
         putch (' ');
         gotoxy (locate_x + 20, locate_y);
         printf ("%s", j);
         putch (' ');
         }

      Txpos += tsize;
      if (blklen < maxblklen && ++goodblks > goodneeded)
         {
         blklen = ((blklen << 1) < maxblklen) ? blklen << 1 : maxblklen;
         goodblks = 0;
         }

      if (e == ZCRCW)
         goto WaitAck;

      while (CHAR_AVAIL ())
         {
         switch (MODEM_IN ())
            {
            case CAN:
            case RCDO:
            case ZPAD:
               /*--------------------------------------*/
               /* Interruption detected;               */
               /* stop sending and process complaint   */
               /*--------------------------------------*/
               z_message (msgtxt[M_TROUBLE]);
               CLEAR_OUTBOUND ();
               ZS_SendData (Txbuf, 0, ZCRCE);
               goto WaitAck;
            }                                    /* switch */
         }                                       /* while */

      }                                          /* do */
   while (e == ZCRCG);

   while (1)
      {
      Z_PutLongIntoHeader (Txpos);
      ZS_SendBinaryHeader (ZEOF, Txhdr);

      switch (c = ZS_SyncWithReceiver (7))
         {
         case ZACK:
            continue;

         case ZRPOS:
            /*-----------------------------------------*/
            /* Resume at this position...              */
            /*-----------------------------------------*/
            goto SomeMore;

         case ZRINIT:
            /*-----------------------------------------*/
            /* Receive init                            */
            /*-----------------------------------------*/
            if (locate_y && !(fullscreen && un_attended))
               gotoxy (2, (byte) locate_y - 1);
            throughput (1, Txpos - Strtpos);
            return OK;

         case ZSKIP:
            /*-----------------------------------------*/
            /* Request to skip the current file        */
            /*-----------------------------------------*/
            z_log (msgtxt[M_SKIP_MSG]);
            CLEAR_IOERR ();
            fclose (Infile);
            return c;

         default:
      oops:
            z_log (msgtxt[M_CAN_MSG]);
            fclose (Infile);
            return ERROR;
         }                                       /* switch */
      }                                          /* while */
}                                                /* ZS_SendFileData */

/*--------------------------------------------------------------------------*/
/* ZS SYNC WITH RECEIVER                                                    */
/* Respond to receiver's complaint, get back in sync with receiver          */
/*--------------------------------------------------------------------------*/
static int ZS_SyncWithReceiver (num_errs)
int num_errs;
{
   register int c;
   char j[50];

#ifdef ZDEBUG
   show_debug_name ("ZS_SyncWithReceiver");
#endif

   while (1)
      {
      c = Z_GetHeader (Rxhdr);
      CLEAR_INBOUND ();
      switch (c)
         {
         case TIMEOUT:
            z_message (msgtxt[M_TIMEOUT]);
            if ((num_errs--) >= 0)
               break;

         case ZCAN:
         case ZABORT:
         case ZFIN:
         case RCDO:
            z_log (msgtxt[M_ERROR]);
            return ERROR;

         case ZRPOS:
            if (Rxpos == LastZRpos)              /* Same as last time?    */
               {
               if (!(--ZRPosCount))              /* Yup, 10 times yet?    */
                  return ERROR;                  /* Too many, get out     */
               }
            else ZRPosCount = 10;                /* Reset repeat count    */
            LastZRpos = Rxpos;                   /* Keep track of this    */

            rewind (Infile);                     /* In case file EOF seen */
            fseek (Infile, Rxpos, SEEK_SET);
            Txpos = Rxpos;
            sprintf (j, msgtxt[M_RESENDING_FROM],
                     ultoa (((unsigned long) (Txpos)), e_input, 10));
            z_message (j);
            return c;

         case ZSKIP:
            z_log (msgtxt[M_SKIP_MSG]);

         case ZRINIT:
            CLEAR_IOERR ();
            fclose (Infile);
            return c;

         case ZACK:
            z_message (NULL);
            return c;

         default:
            z_message (IDUNNO_msg);
            ZS_SendBinaryHeader (ZNAK, Txhdr);
            continue;
         }                                       /* switch */
      }                                          /* while */
}                                                /* ZS_SyncWithReceiver */




/*--------------------------------------------------------------------------*/
/* ZS END SEND                                                              */
/* Say BIBI to the receiver, try to do it cleanly                           */
/*--------------------------------------------------------------------------*/
static void ZS_EndSend ()
{

#ifdef ZDEBUG
    show_debug_name ("ZS_EndSend");
#endif

#ifdef NEW
   CLEAR_OUTBOUND();
   CLEAR_INBOUND();
#endif

   while (1)
      {
      Z_PutLongIntoHeader (0L);
      ZS_SendBinaryHeader (ZFIN, Txhdr);

      switch (Z_GetHeader (Rxhdr))
         {
         case ZFIN:
            SENDBYTE ('O');
            SENDBYTE ('O');
            while (CARRIER && !OUT_EMPTY ())
               time_release ();
            if (!CARRIER)
               CLEAR_OUTBOUND ();
            /* fallthrough... */
         case ZCAN:
         case RCDO:
         case TIMEOUT:
            return;
         }                                       /* switch */
      }                                          /* while */
}                                                /* ZS_EndSend */
