home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 019.lha / Aterm / XmodemCRC.c < prev   
C/C++ Source or Header  |  1986-11-10  |  20KB  |  723 lines

  1. /* XmodemCRC.c - Checksum/CRC Xmodem */
  2. /*
  3.  * Adapted from Djj's Aterm1.3 version by Jeff Lydiatt, Vancouver, Canada
  4.  *
  5.  * Business first... This code is NOT to be used for commercial purposes.
  6.  * You are granted a limited license to distribute it on a not-for-profit
  7.  * basis by any means, including, but not limited to, paper, magnetic media,
  8.  * or telecommunications. We have no objection to its appearance on a 
  9.  * commercial communications network provided it may be obtained for no
  10.  * cost over and above the standard connect time charges.
  11.  *  In addition, we would appreciate it if anyone modifying this code
  12.  * would attempt to get the modifications back to us so that we can
  13.  * keep some semblance of continuity of versions.
  14.  *
  15.  * Jeff Lydiatt
  16.  * Larry Phillips, Compuserve, 76703,4322
  17.  *
  18.  * This version supports the following features:
  19.  *   . Both Cyclic Redundancy Check and Checksum Xmodem are supported.
  20.  *   . AutoChop logic swiped from Aterm Version 6.1
  21.  *   . Timeouts for receive are 5 seconds within a block, and
  22.  *     20 seconds outside a block.
  23.  *   . Timeouts use the timer, rather than polling the elapsed time
  24.  *     in the standard date function.
  25.  *   . In send mode, the last block is padded with the Amiga HUNKEND
  26.  *     symbol (0x0f2) if I think you're sending a loadable program.  This
  27.  *     is detected by all of the conditions, a) last block has 4 or more 
  28.  *     characters, and b) is a even multiple of four bytes, and c) it
  29.  *     already ends with a HUNKEND symbol.
  30.  *        If it's not a loadable module, the padding character is a
  31.  *     control-Z, unless it already ends with control-Z.  In this case
  32.  *     the send file is padded with nulls ('\0').
  33.  *   . Also for sends, the program gives a theoretical time to transmit the
  34.  *     file based on it's size and the baud rate.
  35.  *   . 17Aug86 - by Jal. Chop modified to allow for files like Arc which
  36.  *     end in hexadecimal 1A00.
  37.  *   . 17Aug86 - by Jal. Send routine padding character changed to standard
  38.  *     of '\0' instead of CPM_EOF.  
  39.  *   . 30Sep86 - by Jal. Fixed CRC/Checksum_Verify always returns OK.
  40.  *   . 22Oct86 - Xmodem works better with buffered I/O.
  41.  */
  42.  
  43. #include <exec/types.h>
  44. #include <exec/memory.h>
  45. #include <stdio.h>
  46. #include <fcntl.h>
  47. #include <libraries/dos.h>
  48. #include <functions.h>
  49. #include "ConsoleIO.h"
  50. #include "SerialIO.h"
  51. #include "Timer.h"
  52.  
  53. /* things for xmodem send and recieve */
  54. #define SECSIZ    128
  55. #define BLOCKTIME 20        /* max seconds to timeout for a new block */
  56. #define CHARTIME  5        /* max seconds to timeout within a block */
  57. #define BufSize   SECSIZ*2  /* Text buffer */
  58. #define ERRORMAX  10        /* Max errors before abort */
  59. #define RETRYMAX  10        /* Maximum retrys before abort */
  60. #define SOH       1         /* Start of sector char */
  61. #define EOT       4         /* end of transmission char */
  62. #define ACK       6         /* acknowledge sector transmission */
  63. #define NAK      21         /* error in transmission detected */
  64. #define HUNKEND  0x3F2L     /* Normal Load Module end marker. */
  65. #define CAN      24         /* Cancel character -- abort transmission */
  66. #define TIMEOUT  -1         /* timeout during receive character */
  67. #define ABORT    -2         /* user aborted XMODEM transfer with escape key */
  68. #define OK       TRUE       /* sector sent OK */
  69. #define ESC      '\x1b'        /* Use ESC to abort Xmodem transfer */
  70. #define BELL    ('G'& 0x1F) /* Bell */
  71. #define CPM_EOF   26
  72. #define FOREVER for(;;)
  73.  
  74. /* Note: the fillbuf() routine depends upon buffer being longword aligned.
  75.  *     Having bytes_xferred immediately preceding buffer will accomplish
  76.  *     this for us.
  77.  */
  78.  
  79. static long bytes_xferred;
  80. static char buffer[BufSize];
  81. static FILE *fd;
  82. static int
  83.     sector;
  84. static BOOL
  85.     cancel   = FALSE,
  86.     timeout  = FALSE;
  87.  
  88. static short crcflag;        /* TRUE if crc mode, FALSE for checksum */
  89.             /* can be changed in send mode by remote receiver */
  90. static unsigned short crc;    /* 16 bit crc value */
  91. static BOOL autoChop;
  92. static unsigned char checksum;
  93. static ULONG waitMask;
  94.  
  95. /*--------------------------------------------------------------*/
  96. /*    check_abort: return TRUE if ESC pressed.        */
  97. /*--------------------------------------------------------------*/
  98.  
  99. static BOOL check_abort()
  100. {
  101.   if ( CheckKey() != ESC )
  102.      cancel = FALSE;
  103.   else
  104.      {
  105.         PutString( "Xmodem Cancelled!\n");
  106.     PutChar( BELL );
  107.         cancel = TRUE;
  108.      }
  109.   return cancel;
  110. }
  111.  
  112.  
  113. /*--------------------------------------------------------------*/
  114. /*    sendchar: send a character to the modem.        */
  115. /*--------------------------------------------------------------*/
  116.  
  117. static void sendchar(ch)
  118. int ch;
  119. {
  120.    SerIOWrite( (UBYTE)(ch & 0xFF) );
  121. }
  122.  
  123.  
  124. /*--------------------------------------------------------------*/
  125. /*    readchar: timed wait for char to arrive.        */
  126. /*--------------------------------------------------------------*/
  127.  
  128. /*--------------------------------------
  129. readchar() waits for a character
  130.   timeout built in.
  131.   Timeout condition causes error return
  132.   'seconds' controls duration of wait.
  133.   'doabort' TRUE indicates that CAN recvd
  134.      will set cancel flag
  135. ----------------------------------------*/
  136.  
  137. static int readchar( seconds, doabort )
  138. int doabort;
  139. int seconds;
  140. {
  141.    UBYTE ch;
  142.    BOOL  timerOn = FALSE;
  143.  
  144.    timeout = FALSE;
  145.    do
  146.      {      
  147.     if ( CheckSerIO() )
  148.        {
  149.          ch = SerIORead();
  150.          if ( doabort && (ch == CAN) )
  151.         cancel = TRUE;
  152.          return (int)(ch);
  153.        }
  154.     else if ( (ch = CheckKey()) != '\0' )
  155.        if ( ch == ESC )
  156.          {
  157.             PutString( "Xmodem Cancelled!\n");
  158.         PutChar( BELL );
  159.         cancel = TRUE;
  160.         return ABORT; 
  161.          }
  162.         else;
  163.     if ( !timerOn )
  164.       {
  165.         StartTimer( (long)seconds, 0L );
  166.         timerOn = TRUE;
  167.       }
  168.     Wait( waitMask );
  169.  
  170.       } while( !TimerExpired() );
  171.  
  172.    /* if not the above, the timer must have timed out */
  173.    PutString( "  XMODEM Timeout.\n");
  174.    timeout = TRUE;
  175.    return TIMEOUT;
  176.  
  177. }
  178.  
  179.  
  180. /*--------------------------------------------------------------*/
  181. /*    purge: purge the serial port buffer until timeout.    */
  182. /*--------------------------------------------------------------*/
  183.  
  184. static void purge( purgetime )
  185. int purgetime;
  186. {
  187.    timeout = FALSE;
  188.    FlushSerIO();
  189.    while ( timeout == FALSE )
  190.       (void)readchar ( purgetime, FALSE );
  191. }
  192.  
  193. /*--------------------------------------------------------------*/
  194. /*    do_crc: computes CRC using the CCITT polynomial.    */
  195. /*--------------------------------------------------------------*/
  196.  
  197. static void do_crc( val )
  198. unsigned char val;
  199. {
  200.    register unsigned short shifter,flag;
  201.  
  202.    if(crcflag)
  203.      for( shifter = 0x80 ; shifter ; shifter >>= 1 )
  204.        {
  205.          flag = (crc & 0x8000);
  206.          crc <<= 1;
  207.          crc |= ((shifter & val) ? 1 : 0);
  208.          if( flag )
  209.             crc ^= 0x1021;
  210.        }
  211.    else
  212.      checksum += val;
  213. }
  214.  
  215. /*-----------------------------------------------------------*/
  216. /* SetWaitMask: for Console, Serial Read or timer expired.   */
  217. /*-----------------------------------------------------------*/
  218.  
  219. static void SetWaitMask()
  220. {
  221.    waitMask = ( 1L << GetConsoleSigBit() )
  222.         | ( 1L << GetSerialSigBit()  )
  223.         | ( 1L << GetTimerSigBit()   );
  224. }
  225.  
  226. /*-------------------------------------------------------*/
  227. /*  writeBlock: write a sector to disk             */
  228. /*-------------------------------------------------------*/
  229.  
  230. static int writeBlock( fd, buffer, length )
  231. FILE *fd;
  232. register unsigned char *buffer;
  233. int length;
  234. {
  235.    register int j;
  236.  
  237.    for ( j=0; j < length; ++j, ++buffer)
  238.      {
  239.     if ( putc( *buffer, fd ) == EOF )
  240.            return EOF;
  241.      }
  242.    return length;
  243. }
  244.  
  245. /*-------------------------------------------------------*/
  246. /*  readBlock: read a sector from disk to buffer     */
  247. /*-------------------------------------------------------*/
  248.  
  249. static int readBlock( fd, buffer, maxlength )
  250. FILE *fd;
  251. register unsigned char *buffer;
  252. int maxlength;
  253. {
  254.    register int j;
  255.    unsigned char fgetc();
  256.  
  257.    for ( j=0; j < maxlength; ++j)
  258.      {
  259.         if ( !feof( fd ) )
  260.        *buffer++ = fgetc( fd );
  261.     else
  262.       {
  263.         if ( j > 0 )
  264.            --j;
  265.         break;
  266.           }
  267.      }
  268.    return j;
  269. }
  270.  
  271. /*--------------------------------------------------------------*/
  272. /*    CloseEmUp: write and chop the last block.        */
  273. /*--------------------------------------------------------------*/
  274.  
  275. static void CloseEmUp( sector )
  276. int sector;
  277. {
  278.    register int i;
  279.    int len;
  280.    ULONG filesize = 0;
  281.    register UBYTE c;
  282.    register char *buf;
  283.  
  284.  if ( sector > 0 )        /* skip if no data at all */
  285.   {
  286.     buf = &buffer[ (sector & 1) * SECSIZ ]; 
  287.     i = SECSIZ - 1;
  288.     c = '\0';
  289.  
  290.     if ( !autoChop ) 
  291.       {
  292.     c = 0xff;
  293.     PutString( "Chop Disabled: " );
  294.       }
  295.     else
  296.       {
  297.         c = buf[ i ];       /* c gets last char in block */
  298.         if ( (c == '\0') || (c == CPM_EOF) || (c == 0xFF) )
  299.       {
  300.          while ( i > 0 )
  301.            if ( buf[ --i ] != c )
  302.           break;
  303.       }
  304.       }
  305.  
  306.     if ( c == '\0' && buf[ i ] == CPM_EOF )
  307.     /* It may be an Arc file which normally ends in 1A00 so ... */
  308.     i++;
  309.     i++;
  310.     if ( writeBlock( fd, buf, i) != i )
  311.         PutString("Error writing final block\n");
  312.  
  313.     len = SECSIZ - i;
  314.     format( PutFormat, "%d bytes chopped from final block\n\xff", &len);
  315.  
  316.     filesize = ( --sector ) * (long)(SECSIZ) + (long)( i );
  317.     format( PutFormat, "File size: %ld\n\xff", &filesize);
  318.    }
  319.  
  320. }
  321.  
  322.  
  323. /*--------------------------------------------------------------*/
  324. /*    verify_checksum: return TRUE if checksum or crc ok.    */
  325. /*--------------------------------------------------------------*/
  326.  static BOOL verify_checksum() {
  327.    unsigned short sentCRC;
  328.  
  329.    if( crcflag )
  330.       {
  331.      do_crc(0); do_crc(0);
  332.          sentCRC =  ((unsigned short) readchar(CHARTIME,FALSE)) << 8;
  333.          sentCRC += ((unsigned short) readchar(CHARTIME,FALSE)) & 0xFF;
  334.          return (sentCRC == crc);
  335.       }
  336.    else
  337.       return ( checksum == readchar(CHARTIME,FALSE) );
  338. }
  339.  
  340. /*--------------------------------------------------------------*/
  341. /*    getfile: Do the handshake, read a sector, until EOT.    */
  342. /*--------------------------------------------------------------*/
  343.  
  344. static BOOL getfile()
  345. {
  346.    unsigned char ch, currsect, compsect, response;
  347.    int i, j;
  348.    register char *bptr;      /* rx buffer index */
  349.  
  350.    sector = 1;
  351.    response = (crcflag) ? 'C' : NAK;
  352.    cancel = FALSE;
  353.    FlushSerIO();
  354.  
  355.    FOREVER           /* sector receive loop */
  356.    {
  357.  
  358.       /*--------------------------------------------------------*/
  359.       /*    Handshake loop.                    */
  360.       /*--------------------------------------------------------*/
  361.  
  362.       for( i=1 ; !cancel && (i <= RETRYMAX) ; i++ )
  363.         {
  364.           sendchar( response );
  365.           if (timeout && (sector == 1) && ( i == RETRYMAX / 2) )
  366.             {
  367.         crcflag = (crcflag == FALSE);
  368.         response = (crcflag) ? 'C' : NAK;
  369.         PutString( "Sender not responding, switching to ");
  370.         PutString(  (crcflag) ? "CRC" : "Checksum");
  371.         PutString( " mode\n");
  372.             }
  373.           if( (ch = readchar(BLOCKTIME,TRUE)) == SOH )
  374.                break;
  375.           if( ch == EOT )   /* if EOT , we're done */
  376.            {
  377.          sendchar( ACK );
  378.              CloseEmUp( --sector );
  379.              return TRUE;
  380.            }
  381.          if( ch == CAN )  /* aborted */
  382.            {
  383.          purge( CHARTIME );
  384.              PutString( "Sender cancelled transmission.\n");
  385.              return FALSE;
  386.            }
  387.          if( i == RETRYMAX )   /* timed out */
  388.             {
  389.               PutString( "Sender not responding\nXMODEM Cancelled!\n" );
  390.               return FALSE;
  391.             }
  392.       } /* end for (i <= RETRYMAX) */
  393.  
  394.       if ( cancel )
  395.     {
  396.        purge( CHARTIME );
  397.        sendchar( CAN );
  398.        return FALSE;
  399.     }
  400.     
  401.       /*--------------------------------------------------------*/
  402.       /*     Here Comes a Block                */
  403.       /*--------------------------------------------------------*/
  404.  
  405.       currsect = readchar(CHARTIME,FALSE);
  406.       if ( !cancel )
  407.          compsect = readchar(CHARTIME,FALSE);
  408.  
  409.       checksum = crc = 0;
  410.       bptr = &buffer[ ( sector & 1 ) * SECSIZ ];
  411.       for( j = 0 ; !(cancel || timeout) && j < SECSIZ ; j++ )
  412.         {
  413.           *bptr = readchar( CHARTIME, FALSE );
  414.           do_crc( *bptr++ );
  415.         }
  416.  
  417.  
  418.       /*--------------------------------------------------------*/
  419.       /*    Any errors in receiving the block?        */
  420.       /*--------------------------------------------------------*/
  421.  
  422.       if ( cancel || timeout ) 
  423.      continue;
  424.  
  425.       response = NAK;      /* default response */
  426.       if (!verify_checksum())
  427.         {
  428.            PutString( "Checksum error\n");
  429.            continue;
  430.         }
  431.  
  432.       if ( (~currsect & 0xFF) != (compsect & 0xFF) )
  433.          continue;
  434.  
  435.       if ( (currsect == (sector - 1) & 0xFF ) )   /* dup sector number? */
  436.         {
  437.          response = ACK;
  438.          continue;                              /* ignore it */
  439.         }
  440.       if ( currsect != (sector & 0xFF) )         /* sequence error? */
  441.          {
  442.             PutString(  "Sector numbering error - Aborting." );
  443.             sendchar(CAN);
  444.             return FALSE;
  445.          }
  446.  
  447.       if ( sector > 1 )
  448.     if( writeBlock(fd, &buffer[ ((sector-1) & 1) * SECSIZ ], SECSIZ)
  449.            != SECSIZ)
  450.        {
  451.          PutString( "Error writing file\n");
  452.          sendchar(CAN);
  453.          return FALSE;
  454.        }
  455.  
  456.       format( PutFormat, "\x0bReceived block # %d    \n\xFF", §or );
  457.       response = ACK;      /* sector received ok */
  458.       sector++;
  459.  
  460.    } /* end FOREVER */
  461.  
  462. }
  463.  
  464.  
  465. /*--------------------------------------------------------------*/
  466. /*    XMODEM_Read_File: the external entry point for readfile.*/
  467. /*--------------------------------------------------------------*/
  468.  
  469. BOOL XMODEM_Read_File(file, xfermode, chopFlag)
  470. char *file;
  471. BOOL xfermode, chopFlag;
  472. {
  473.    BOOL status;
  474.  
  475.    cancel = FALSE;
  476.    crcflag = xfermode;
  477.    autoChop = chopFlag;
  478.    SetWaitMask();
  479.    
  480.    if ((fd = fopen(file, "w")) == NULL)
  481.      {
  482.        PutString( "Cannot Open File\n");
  483.        sendchar(CAN);
  484.        return FALSE;
  485.      }
  486.    else
  487.      PutString( "Receiving File\n\n" );
  488.  
  489.    status = getfile();
  490.    fclose( fd );
  491.    return status;
  492. }
  493.  
  494. /*--------------------------------------------------------------*/
  495. /*    doTellSize: tell file size and how long to send it.    */
  496. /*--------------------------------------------------------------*/
  497.  
  498. static void doTellSize(file, baud)
  499. char *file;
  500. int   baud;
  501. {
  502.    struct parms
  503.      {
  504.        LONG size;
  505.        WORD sectors;
  506.        WORD minutes;
  507.        WORD seconds;
  508.        WORD baudrate;
  509.      } args;
  510.    struct FileInfoBlock *FBlock;
  511.    long         *FLock;
  512.  
  513.    if ( (FLock = (struct FileLock *) Lock(file, ACCESS_READ)) == NULL)
  514.       return;
  515.  
  516.    if ( (FBlock = (struct FileInfoBlock *)
  517.      AllocMem( (long)sizeof(struct FileInfoBlock), (long)(MEMF_CHIP))) != NULL)
  518.    if ( Examine( FLock, FBlock) )
  519.      {
  520.     args.size = FBlock->fib_Size;
  521.     if (args.size != 0)
  522.           {
  523.         args.sectors = (args.size + SECSIZ - 1) / SECSIZ;
  524.         format( PutFormat, 
  525.         "File contains %ld bytes, %d sectors.\n\xff", &args.size );
  526.  
  527.        /* compute time for sending file */
  528.         args.baudrate = baud;
  529.         args.seconds = (ULONG)args.sectors * 133L * 11L / (ULONG)baud;
  530.         args.minutes = args.seconds / 60;
  531.         args.seconds %= 60;
  532.         format( PutFormat,
  533.     "Approximately %d minutes and %d seconds for transfer at %d bps.\n\xff",
  534.         &args.minutes );
  535.           }
  536.     FreeMem( FBlock, (long)sizeof(struct FileInfoBlock) );
  537.      }
  538.  
  539.    UnLock( FLock );
  540.    return;
  541. }
  542.  
  543. /*--------------------------------------------------------------*/
  544. /* sendsector: send a sector with resend on error from receiver.*/
  545. /*--------------------------------------------------------------*/
  546.  
  547. static int sendsector( buf )
  548. unsigned char *buf;
  549. {
  550.    int badblock;
  551.    register int i;
  552.    register unsigned char *bptr;
  553.  
  554.    badblock = 0;
  555.    cancel = FALSE;
  556.  
  557.    FOREVER
  558.    {
  559.       if( badblock == ERRORMAX )
  560.          return TIMEOUT;
  561.       if( cancel || check_abort() )
  562.          return ABORT;
  563.  
  564.       FlushSerIO();             /* flush any garbage from line */
  565.       format( PutFormat,  "\x0bSending sector  %d\n\xFF", §or );
  566.       sendchar( SOH );          /* start of block */
  567.       sendchar( sector );       /* sector number  */
  568.       sendchar( ~sector );      /* compliment of sector number */
  569.       checksum = crc = 0;       /* clear CRC and checksum */
  570.  
  571.       bptr = buf;
  572.       for( i = 0 ; i < SECSIZ ; i++ )
  573.         {
  574.           sendchar( *bptr );
  575.           do_crc( *bptr++ );
  576.         }
  577.       FlushSerIO();              /* flush garbage from receiver */
  578.       if( crcflag )
  579.       {
  580.          do_crc(0); do_crc(0);   /* cycle CRC generator */
  581.          sendchar( crc >> 8 );   /* High byte */
  582.          sendchar( crc );        /* Low byte */
  583.       }
  584.       else
  585.          sendchar( checksum );
  586.  
  587.       if( readchar(BLOCKTIME, TRUE ) == ACK)
  588.          break;      
  589.       else
  590.          badblock++;
  591.    }
  592.    sector++;            /* bump sector count   */
  593.    return OK;
  594. }
  595.  
  596.  
  597. /*--------------------------------------------------------------*/
  598. /*    fillbuf: fill the buffer. Return TRUE data to be sent.    */
  599. /*--------------------------------------------------------------*/
  600. static BOOL fillbuf()
  601. {
  602.    register int bytes_read;
  603.    register long *iptr;
  604.    char     padchar;
  605.  
  606.    bytes_read = readBlock(fd, buffer, SECSIZ);
  607.    if (bytes_read <= 0)    /* end of file */
  608.      {
  609.     if ( bytes_read < 0 )
  610.        PutString( "Error reading Send File. Xmodem Cancelled.\n" );
  611.     return FALSE;
  612.      }
  613.  
  614.    else if( bytes_read == SECSIZ )
  615.     return TRUE;
  616.  
  617.    /* Pad with Hunk ends if this is a load module */
  618.       
  619.    if ( (bytes_read >= 4) && ( (bytes_read % 4) == 0 ) )
  620.       iptr = (long * ) ((long) buffer + bytes_read - 4);
  621.    else
  622.       iptr = NULL;
  623.    if ( (iptr != NULL) && (*iptr == HUNKEND) )
  624.       for ( iptr++; bytes_read < SECSIZ; bytes_read += 4 )
  625.       *iptr++ = HUNKEND;
  626.  
  627.    /* Otherwise, make the padding character != last character of data */
  628.         
  629.    else
  630.       {
  631.     if ( buffer[ bytes_read - 1 ] != '\0' )
  632.        padchar = '\0';
  633.     else
  634.        padchar = CPM_EOF;
  635.         for(; bytes_read < SECSIZ; bytes_read++)
  636.        buffer[bytes_read] = padchar;
  637.       }    
  638.    return TRUE;
  639. }
  640.  
  641. /*--------------------------------------------------------------*/
  642. /*    putfile: Handshake & send the file by sector.        */
  643. /*--------------------------------------------------------------*/
  644.  
  645. static BOOL putfile()
  646. {
  647.    int timeoutcount, status;
  648.    UBYTE ch;
  649.  
  650.    sector = 1;  timeoutcount = 0;
  651.  
  652.    FlushSerIO();     /* flush any garbage in receiver */
  653.    do                /* start handshake with receiver */
  654.       {
  655.          ch = readchar(BLOCKTIME,TRUE);
  656.          if(timeout)
  657.            {
  658.              if(++timeoutcount == RETRYMAX)
  659.                {
  660.                   PutString( "No response, aborting\n");
  661.                   return FALSE;
  662.                }
  663.             }
  664.          if( cancel ) return FALSE;
  665.       } while (ch != NAK && ch != 'C');
  666.  
  667.    status = OK;
  668.    crcflag = (ch == 'C');
  669.    PutString(  crcflag ? "CRC" : "Checksum");
  670.    PutString( " mode requested\n\n");
  671.  
  672.    while( fillbuf() && status == OK )  /* fill file buffer until EOF */
  673.       status = sendsector( buffer );   /* send sector */
  674.  
  675.    cancel = FALSE;
  676.    timeoutcount = 0;
  677.    do
  678.      {
  679.       FlushSerIO();
  680.       sendchar( EOT );     /* end transmission */
  681.       if ( timeout )
  682.     {
  683.       if ( timeoutcount++ == RETRYMAX || cancel || check_abort())
  684.          return FALSE;
  685.       PutString( "Awaiting Acknowledge of EOT...\n" );
  686.     }
  687.      } while ( readchar( BLOCKTIME, TRUE ) != ACK);
  688.  
  689.    if ( status == TIMEOUT || status == ABORT || timeoutcount >= RETRYMAX )
  690.       return FALSE;
  691.    else
  692.       return TRUE;
  693. }
  694.  
  695. /*--------------------------------------------------------------*/
  696. /*    XMODEM_Send_File: External entry for Xmodem Send.    */
  697. /*--------------------------------------------------------------*/
  698.  
  699. XMODEM_Send_File(file, baudrate)
  700. char *file;
  701. int baudrate;
  702. {
  703.     int status, timeoutcount;
  704.     unsigned char ch;
  705.  
  706.     cancel = FALSE;
  707.     SetWaitMask();
  708.     
  709.     doTellSize(file, baudrate);
  710.     if ((fd = fopen(file, "r")) == NULL)
  711.       {
  712.         PutString( "Cannot Open Send File\n");
  713.         sendchar(EOT);
  714.         return FALSE;
  715.       }
  716.     else
  717.         PutString( "Sending File\n \n \n");
  718.     status = putfile();
  719.  
  720.     fclose( fd );
  721.     return status;
  722. }
  723.