ZSEND.C

34.4 KB 8d677fe860e7189d…
/*--------------------------------------------------------------------------*/
/*                                                                          */
/*                                                                          */
/*      ------------         Bit-Bucket Software, Co.                       */
/*      \ 10001101 /         Writers and Distributors of                    */
/*       \ 011110 /          Freely Available<tm> Software.                 */
/*        \ 1011 /                                                          */
/*         ------                                                           */
/*                                                                          */
/*  (C) Copyright 1987-91, 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.250.    */
/*                                                                          */
/*    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:343/491             */
/* P.O. Box 460398                AlterNet 7:491/0                          */
/* Aurora, CO 80046               BBS-Net  86:2030/1                        */
/*                                Internet f491.n343.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 this file before any other includes or defines! */

#include "includes.h"

/*--------------------------------------------------------------------------*/
/* Private routines                                                         */
/*--------------------------------------------------------------------------*/

void ZS_SendBinaryHeader (unsigned short, byte *);
void ZS_32SendBinaryHeader (unsigned short, byte *);
void ZS_SendData (byte *, int, unsigned short);
void ZS_32SendData (byte *, int, unsigned short);
void ZS_SendByte (byte);
int ZS_GetReceiverInfo (void);
int ZS_SendFile (int, int);
int ZS_SendFileData (int);
int ZS_SyncWithReceiver (int);
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 (char *fname, char *alias, int fsent, int wazoo)
{
   register byte *p;
   register byte *q;
   struct stat f;
   int i;
   int rc = TRUE;
   char j[100];

#ifdef DEBUG
   show_debug_name ("send_Zmodem");
#endif

   IN_XON_ENABLE ();

   z_size = 0;
   Infile = NULL;

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

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

      case NOTHING_TO_DO:
         Rxtimeout = 200;
         if (ZS_GetReceiverInfo () == ERROR)
            {
            XON_DISABLE ();
            XON_ENABLE ();

            return FALSE;
            }
      }

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

   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 = share_fopen (Filename, read_binary, DENY_WRITE)) == NULL)
      {
      (void) got_error (MSG_TXT(M_OPEN_MSG), Filename);
      rc = OK;
      goto Done;
      }

   if (isatty (fileno (Infile)))
      {
      errno = 1;
      (void) got_error (MSG_TXT(M_DEVICE_MSG), Filename);
      rc = OK;
      goto Done;
      }


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

   (void) stat (Filename, &f);

   i = (int) (f.st_size * 10 / cur_baud.rate_value + 53) / 54;
   (void) 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 ( file_hWnd, 1, 2);
      FlLnModeSet( FILE_LN_2, 1 );
      sb_puts( GetDlgItem( file_hWnd, FILE_LN_1 ), j );
      elapse_time ();
      (void) sprintf (j, "%3d min", i);
      sb_move ( file_hWnd, 2, 69);
      FlLnModeSet( FILE_LN_2, 1 );
      sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_DTTM ), j );
      sb_show ();
      }
   else
      {
      (void) printf ("%s", j);
      set_xy (NULL);
      locate_x += 2;
      }


   /*--------------------------------------------------------------------*/
   /* Get outgoing file name; no directory path, lower case              */
   /*--------------------------------------------------------------------*/

#ifndef NEW_PATH_STUFF
   for (p = (byte *)((alias != NULL) ? alias : Filename), q = Txbuf; *p;)
      {
      if ((*p == '/') || (*p == '\\') || (*p == ':'))
         q = Txbuf;
      else *q++ = (char) tolower (*p);

      p++;
      }

   *q++ = '\0';
   p = q;
#else
   p = ZMdmFlNmCndtn( Txbuf,
                      ((alias != NULL) ? alias : Filename),
                      NULL,
                      0 );
   p += strlen( p );
   q = ++p;
#endif


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

   /*--------------------------------------------------------------------*/
   /* Store filesize, time last modified, and file mode in header packet */
   /*--------------------------------------------------------------------*/
   (void) sprintf ((char *)p, "%lu %lo %o", 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 ((char *)p) + (int) (p - Txbuf), wazoo))
      {
      case ERROR:
         /*--------------------------------------------------*/
         /* Something tragic happened                        */
         /*--------------------------------------------------*/
         goto Err_Out;

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

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

         update_files (1);
         goto Done;

      case ZSKIP:
         status_line (MSG_TXT(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  */
         /*--------------------------------------------------*/
         goto Done;
      }                                          /* switch */

Err_Out:
   rc = FALSE;

Done:
   if (Infile)
      (void) 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                               */
/*--------------------------------------------------------------------------*/
void ZS_SendBinaryHeader (unsigned short type, register byte *hdr)
{
   register unsigned short crc;
   int n;

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

   BUFFER_BYTE (ZPAD);
   BUFFER_BYTE (ZDLE);

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

      crc = Z_UpdateCRC (type, 0);

      for (n = 4; --n >= 0;)
         {
         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                               */
/*--------------------------------------------------------------------------*/
void ZS_32SendBinaryHeader (unsigned short type, register byte *hdr)
{
   unsigned long crc;
   int n;

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

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

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

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

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

   UNBUFFER_BYTES ();
}                                                /* ZS_SendBinaryHeader */

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

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

   if (Crc32t)
      ZS_32SendData (buf, length, frameend);
   else
      {
      crc = 0;
      for (; --length >= 0;)
         {
         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                 */
/*--------------------------------------------------------------------------*/
void ZS_32SendData (register byte *buf, int length, unsigned short frameend)
{
   unsigned long crc;

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

   crc = 0xFFFFFFFF;
   for (; --length >= 0; ++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 >= 0;)
      {
      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)             */
/*--------------------------------------------------------------------------*/
void ZS_SendByte (register byte c)
{
   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                                       */
/*--------------------------------------------------------------------------*/
int ZS_GetReceiverInfo ()
{
   int n;

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

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

         case ZCOMMAND:
            /*--------------------------------------*/
            /* They didn't see our ZRQINIT          */
            /*--------------------------------------*/
            Z_PutLongIntoHeader (0L);
            Z_SendHexHeader (ZRQINIT, (byte *)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, (byte *)Txhdr);
            continue;
         }                                       /* switch */
      }                                          /* for */

   return ERROR;
}                                                /* ZS_GetReceiverInfo */

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

#ifdef DEBUG
   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 (MSG_TXT(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, (byte *)Txhdr);
      ZS_SendData (Txbuf, blen, ZCRCW);

Again:
      switch (c = Z_GetHeader ((byte *)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...            */
            /*-----------------------------------------*/
            (void) fseek (Infile, Rxpos, SEEK_SET);
            if (Rxpos != 0L)
               {
               status_line (MSG_TXT(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                                                */
/*--------------------------------------------------------------------------*/
int ZS_SendFileData (int wazoo)
{
   register int c, e;
   int i;
   unsigned int rate;
   char j[100];
   word newcnt;
   word blklen;
   word maxblklen;
   word goodblks = 0;
   word goodneeded = 1;

   long t;

#ifdef DEBUG
   show_debug_name ("ZS_SendFileData");
#endif
   rate = cur_baud.rate_value;

   maxblklen = (rate < 300) ? 128 : rate / 300 * 256;

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

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

   blklen = (fstblklen != 0) ? fstblklen : maxblklen;
   goodneeded = (fstblklen != 0) ? 8 : 1;

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 (MSG_TXT(M_CAN_MSG));
            (void) 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, (byte *)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 (MSG_TXT(M_KBD_MSG));
         goto oops;
         }

      if (!CARRIER)
         goto oops;

      if ((unsigned)(c = fread (Txbuf, 1, blklen, Infile)) != z_size)
         {
         if (fullscreen && un_attended)
            {
            sb_move (file_hWnd, 2, 12);
            sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_SIZE ),
                     ultoa (((unsigned long) (z_size = c)), e_input, 10));
#ifndef MILQ
            sb_puts (file_hWnd, "    ");
#endif
            elapse_time ();
            sb_show ();
            }
         else
            {
            gotoxy (locate_x + 10, locate_y);
            (void) cputs (ultoa (((unsigned long) (z_size = c)), e_input, 10));
            (void) putch (' ');
            }
         }

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

      ZS_SendData (Txbuf, c, e);

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

      if (fullscreen && un_attended)
         {
         sb_move (file_hWnd, 2, 2);
         sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_TOTAL ),
                  ultoa (((unsigned long) Txpos), e_input, 10));
#ifndef MILQ
         sb_puts (file_hWnd, "  ");
#endif   
         sb_move (file_hWnd, 2, 69);
         sb_puts( GetDlgItem( file_hWnd, FILE_LN_2 + GD_DTTM ), j );
         elapse_time ();
         sb_show ();
         }
      else
         {
         gotoxy (locate_x, locate_y);
         (void) cputs (ultoa (((unsigned long) Txpos), e_input, 10));
         (void) putch (' ');
         (void) putch (' ');
         gotoxy (locate_x + 20, locate_y);
         (void) printf ("%s", j);
         (void) putch (' ');
         }

      Txpos += c;
      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 (MSG_TXT(M_TROUBLE));
               CLEAR_OUTBOUND ();
               ZS_SendData (Txbuf, 0, ZCRCE);
               goto WaitAck;
            }                                    /* switch */
         }                                       /* while */

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

   for (;;)
      {
      Z_PutLongIntoHeader (Txpos);
      ZS_SendBinaryHeader (ZEOF, (byte *)Txhdr);

      switch (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 (MSG_TXT(M_SKIP_MSG));
            CLEAR_IOERR ();
            (void) fclose (Infile);
            return c;

         default:
      oops:
            z_log (MSG_TXT(M_CAN_MSG));
            (void) fclose (Infile);
            return ERROR;
         }                                       /* switch */
      }                                          /* while */
}                                                /* ZS_SendFileData */

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

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

   for (;;)
      {
      c = Z_GetHeader ((byte *)Rxhdr);
      CLEAR_INBOUND ();
      switch (c)
         {
         case TIMEOUT:
            z_message (MSG_TXT(M_TIMEOUT));
            if ((num_errs--) >= 0)
               break;

         case ZCAN:
         case ZABORT:
         case ZFIN:
         case RCDO:
            z_log (MSG_TXT(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 */
            (void) fseek (Infile, Rxpos, SEEK_SET);
            Txpos = Rxpos;
            (void) sprintf (j, MSG_TXT(M_RESENDING_FROM),
                     ultoa (((unsigned long) (Txpos)), e_input, 10));
            z_message (j);
            return c;

         case ZSKIP:
            z_log (MSG_TXT(M_SKIP_MSG));

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

         case ZACK:
            z_message (NULL);
            return c;

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




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

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

   for (;;)
      {
      Z_PutLongIntoHeader (0L);
      ZS_SendBinaryHeader (ZFIN, (byte *)Txhdr);

      switch (Z_GetHeader ((byte *)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 */