home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff330.lzh / Vt100 / Src.lzh / Src / xmodem.c < prev   
C/C++ Source or Header  |  1990-03-01  |  17KB  |  598 lines

  1. static char rcsid[] = "$RCSfile: xmodem.c,v $ $Revision: 1.3 $";
  2.  
  3. /*************************************************************
  4.  * vt100 terminal emulator - XMODEM protocol support
  5.  *            :ts=8
  6.  *
  7.  * $Log:    xmodem.c,v $
  8.  * Revision 1.3  89/12/14  20:33:37  acs
  9.  * 1) Use longs for byte count in xmodem transfers.
  10.  * 2) Remove commented out code.
  11.  * 
  12.  * Revision 1.2  89/12/12  13:39:05  acs
  13.  * multi_xfer() will pass "$" to called routines.  This means that
  14.  * XMODEM_Read_File(), XMODEM_Send_File() and the kermit routines need
  15.  * to check for a filename == "$".
  16.  * 
  17.  *    v2.9 ACS - multi_xfer() no longer looks for $ -- kermit does,
  18.  *           readchar() now infers ttime of 100,000 micros if ttime == 0
  19.  *           (for newkermit); readchar() doesn't output a TIMED OUT msg
  20.  *           (because of newkermit); speed up sendstring().
  21.  *    v2.7 870825 ACS - Make multi_xfer() non-recursive; on non-ESC in
  22.  *              readchar() re-do the main window's title. 
  23.  *    v2.6 870227 DBW - bug fixes for all the stuff in v2.5
  24.  *    v2.5 870214 DBW - more additions (see readme file)
  25.  *    v2.4 861214 DBW - lots of fixes/additions (see readme file)
  26.  *    v2.3 861101 DBW - minor bug fixes
  27.  *    v2.2 861012 DBW - more of the same
  28.  *    v2.1 860915 DBW - new features (see README)
  29.  *         860901 ACS - Added Parity and Word Length and support code
  30.  *         860823 DBW - Integrated and rewrote lots of code
  31.  *         860815 Steve Drew: readchar inproved with real timeouts
  32.  *    v2.0 860809 DBW - Major rewrite
  33.  *    v1.1 860720 DBW    - Switches, 80 cols, colors, bug fixes
  34.  *    v1.0 860712 DBW    - First version released
  35.  *
  36.  *************************************************************/
  37.  
  38. #include "vt100.h"
  39.  
  40. int enablexon = TRUE;
  41.  
  42. extern struct IntuiText MyTitle;
  43.  
  44. static unsigned long parity_settings[4] = {
  45.     0x96696996,
  46.     0x69969669,
  47.     0x69969669,
  48.     0x96696996 };
  49.  
  50. /* crctab calculated by Mark G. Mendel, Network Systems Corporation */
  51. static unsigned short crctab[256] = {
  52.     0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
  53.     0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
  54.     0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
  55.     0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
  56.     0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
  57.     0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
  58.     0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
  59.     0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
  60.     0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
  61.     0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
  62.     0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
  63.     0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
  64.     0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
  65.     0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
  66.     0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
  67.     0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
  68.     0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
  69.     0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
  70.     0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
  71.     0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
  72.     0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
  73.     0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
  74.     0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
  75.     0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
  76.     0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
  77.     0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
  78.     0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
  79.     0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
  80.     0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
  81.     0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
  82.     0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
  83.     0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
  84. };
  85.  
  86. /*
  87.  * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. 
  88.  *  NOTE: First srgument must be in range 0 to 255.
  89.  *        Second argument is referenced twice.
  90.  * 
  91.  * Programmers may incorporate any or all code into their programs, 
  92.  * giving proper credit within the source. Publication of the 
  93.  * source routines is permitted so long as proper credit is given 
  94.  * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, 
  95.  * Omen Technology.
  96.  */
  97.  
  98. #define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)
  99.  
  100. /************************************************************
  101. * Send a string (using sendchar below)
  102. ************************************************************/
  103.  
  104. void sendstring(s)
  105. register char *s;
  106. {
  107.     char    data[20];
  108.     register char *cp = data;
  109.     int        i;
  110.     LONG    oldlength = Write_Request->IOSer.io_Length;
  111.     APTR    saveaddr =  Write_Request->IOSer.io_Data;
  112.  
  113.     Write_Request->IOSer.io_Length = sizeof(data)-1;
  114.     Write_Request->IOSer.io_Data = (APTR) &(data[0]);
  115.  
  116.     if (enablexon)
  117.     No_XON();
  118.  
  119.     while(i = *(s++)) {
  120.     *(cp++) = addparity(i);
  121.     if( (cp - data) == sizeof(data)-1) {
  122.         *cp = '\0';
  123.         do
  124.         DoIO((struct IORequest *)Write_Request);
  125.         while(Write_Request->IOSer.io_Error != 0);
  126.         cp = data;
  127.     }
  128.     }
  129.     if(cp > data) {
  130.     *(cp++) = '\0';
  131.     Write_Request->IOSer.io_Length = strlen(data);
  132.     do
  133.         DoIO((struct IORequest *)Write_Request);
  134.     while(Write_Request->IOSer.io_Error != 0);
  135.     }
  136.  
  137.     Write_Request->IOSer.io_Length = oldlength;
  138.     Write_Request->IOSer.io_Data   = saveaddr;
  139.  
  140.     if (enablexon)
  141.     No_XON();
  142. }
  143.  
  144. /**************************************************************/
  145. /* send char and read char functions for the xmodem function */
  146. /************************************************************/
  147. void sendchar(ch)
  148. int ch;
  149. {
  150.     if (enablexon)
  151.     No_XON();
  152.  
  153.     rs_out[0] = addparity(ch);
  154.  
  155.     do {
  156.     DoIO((struct IORequest *)Write_Request);
  157.     } while(Write_Request->IOSer.io_Error != 0);
  158.  
  159.     if (enablexon)
  160.     Do_XON();
  161. }
  162.  
  163. static int
  164. addparity(ch)
  165. register int ch;
  166. {
  167.     int        i, j, k;
  168.  
  169.     if(p_parity > 0)
  170.     switch (p_parity) {
  171.     case 1: /* mark */
  172.         ch = (ch & 0x7F) | 0x80;
  173.         break;
  174.  
  175.     case 2: /* space */
  176.         ch &= 0x7F;
  177.         break;
  178.  
  179.     case 3:    /* even */
  180.     case 4: /* odd  */
  181.         i = (ch >> 5) & 0x3;
  182.         j = ch & 0x1F;
  183.         k = ((parity_settings[i] >> j) & 0x1) << 7;
  184.         if (p_parity == 3)            /* even parity */
  185.         ch = (ch & 0x7F) | k;
  186.         else                    /* odd parity */
  187.         ch = (ch & 0x7F) | (k ^ 0x80);
  188.     }
  189.     return(ch & 0xFF);
  190. }
  191.  
  192. /* send a break to the host */
  193. void sendbreak()
  194. {
  195.     AbortIO((struct IORequest *)Read_Request);
  196.     Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
  197.     WaitIO((struct IORequest *)Read_Request);
  198.     Read_Request->IOSer.io_Command = SDCMD_BREAK;
  199.     DoIO((struct IORequest *)Read_Request);
  200.     Read_Request->IOSer.io_Command = CMD_READ;
  201.     SendIO((struct IORequest *)Read_Request);
  202. }
  203.  
  204. int readchar()
  205. {
  206.     int rd,ch;
  207.     ULONG class, waitmask;
  208.     USHORT code;
  209.  
  210.     if(ttime == 0)
  211.     Timer.tr_time.tv_micro = 100000;
  212.     else
  213.     Timer.tr_time.tv_micro = 0;
  214.     Timer.tr_time.tv_secs = ttime;
  215.     SendIO((struct IORequest *)&Timer.tr_node);
  216.  
  217.     rd = FALSE;
  218.     waitmask = ((1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit) |
  219.         ( 1L << mywindow->UserPort->mp_SigBit) |
  220.         ( 1L << Timer_Port->mp_SigBit));
  221.     if(reqwinup)
  222.     waitmask |= (1L << reqwindow->UserPort->mp_SigBit);
  223.     while (rd == FALSE) {
  224.     Wait(waitmask);
  225.     if (CheckIO((struct IORequest *)Read_Request)) {
  226.         WaitIO((struct IORequest *)Read_Request);
  227.         ch=rs_in[0];
  228.         rd = TRUE;
  229.         SendIO((struct IORequest *)Read_Request);
  230.     }
  231.     if(reqwinup &&
  232.       (NewMessage=(struct IntuiMessage *)GetMsg(reqwindow->UserPort))) {
  233.         class = NewMessage->Class;
  234.         ReplyMsg((struct Message *)NewMessage);
  235.         if(class == NEWSIZE)
  236.         ReqNewSize(reqwindow->Height, reqwindow->Width);
  237.     }
  238.     if (NewMessage=(struct IntuiMessage *)GetMsg(mywindow->UserPort)) {
  239.        class = NewMessage->Class;
  240.        code = NewMessage->Code;
  241.        ReplyMsg((struct Message *)NewMessage);
  242.        if ((class == RAWKEY) && (code == 69)) {
  243.         if(!CheckIO((struct IORequest *)&Timer))
  244.             AbortIO((struct IORequest *)&Timer);
  245.         Wait (1L << Timer_Port->mp_SigBit);
  246.         WaitIO((struct IORequest *)&Timer.tr_node);
  247.         InfoMsg1Line("ERROR: User aborted transfer");
  248.         timeout = USERABORT;
  249.         return('\0');
  250.        }
  251.        PrintIText(mywindow->RPort, &MyTitle, 0L, 0L);
  252.     }
  253.  
  254.     if (rd == FALSE && CheckIO((struct IORequest *)&Timer)) {
  255. /*        InfoMsg1Line("ERROR: Timeout waiting for character"); */
  256.         timeout = TIMEOUT;
  257.         return('\0');
  258.     }
  259.     } /* end while */
  260.     if(!CheckIO((struct IORequest *)&Timer))
  261.     AbortIO((struct IORequest *)&Timer);
  262.     Wait (1L << Timer_Port->mp_SigBit);
  263.     WaitIO((struct IORequest *)&Timer.tr_node);
  264.     timeout = GOODREAD;
  265.     return(ch & (p_parity == 0 ? 0xFF : 0x7F));
  266. }
  267.  
  268. void No_XON()
  269. {
  270.  
  271.     /* turn off XON/XOFF processing */
  272.     enablexon = FALSE;
  273.     Write_Request->io_SerFlags |= SERF_XDISABLED;
  274.     Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
  275.     DoIO((struct IORequest *)Write_Request);
  276.     Write_Request->IOSer.io_Command = CMD_WRITE;
  277. }
  278.  
  279. void Do_XON()
  280. {
  281.     /* turn on XON/XOFF processing */
  282.     enablexon = TRUE;
  283.     Write_Request->io_SerFlags &= ~SERF_XDISABLED;
  284.     Write_Request->IOSer.io_Command = SDCMD_SETPARAMS;
  285.     DoIO((struct IORequest *)Write_Request);
  286.     Write_Request->IOSer.io_Command = CMD_WRITE;
  287. }
  288.  
  289. /**************************************/
  290. /* xmodem send and recieve functions */
  291. /************************************/
  292.  
  293. int XMODEM_Read_File(file)
  294. char *file;
  295. {
  296.     int        firstchar, sectnum, sectcurr, sectcomp, errors, errorflag,
  297.         c, good_sect, nak_char, retval = FALSE;
  298.     unsigned int   checksum, j, bufptr;
  299.     unsigned short crc;
  300.     char    scrstr2[40];
  301.  
  302.     if(strcmp(file, "$") == 0)
  303.     return TRUE;
  304.  
  305.     bytes_xferred = 0L;
  306.     ttime = TTIME_SHORT;
  307.  
  308.     if( (bufr = AllocMem((long)BufSize, MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
  309.     InfoMsg1Line("XMODEM: Can't get a buffer.");
  310.     return FALSE;
  311.     }
  312.  
  313.     if ((fd = creat(file, 0)) < 0) {
  314.     InfoMsg2Line("XMODEM Can't Open File:",file);
  315.     goto exit;
  316.     } else
  317.     InfoMsg1Line("XMODEM Receive, <esc> in VT100 window to abort");
  318.  
  319.     sectnum = errors = bufptr = firstchar = 0;
  320.     if(p_xproto == 2)
  321.     nak_char = 'C';
  322.     else
  323.     nak_char = NAK;
  324.     No_XON();
  325.     sendchar(nak_char);
  326.     while (firstchar != EOT && errors != ERRORMAX) {
  327.     errorflag = FALSE;
  328.  
  329.     while( (firstchar = readchar()) != SOH && firstchar != EOT) {
  330.         if (timeout != GOODREAD) {
  331.         if (timeout == USERABORT || errors++ == ERRORMAX)
  332.             goto exit;
  333.         }
  334.         sendchar(nak_char);
  335.     }
  336.  
  337.     if  (firstchar == SOH) {
  338.         sprintf(scrstr2,"%s: Block: %4d Bytes: %ld",
  339.             p_xproto==2?"XmodemCRC":"Xmodem", sectnum,
  340.             ((long)sectnum)*SECSIZ);
  341.         InfoMsgNoScroll(scrstr2);
  342.         sectcurr = readchar();
  343.         if (timeout != GOODREAD)
  344.         goto exit;
  345.         sectcomp = readchar();
  346.         if (timeout != GOODREAD)
  347.         goto exit;
  348.         if ((sectcurr + sectcomp) == 255) {
  349.         if (sectcurr == ((sectnum + 1) & 0xff)) {
  350.             checksum = 0; crc = 0;
  351.             for (j = bufptr; j < (bufptr + SECSIZ); j++) {
  352.             bufr[j] = readchar();
  353.             if (timeout != GOODREAD)
  354.                 goto exit;
  355.             checksum = (checksum + bufr[j]) & 0xff;
  356.             crc = updcrc(((unsigned int)bufr[j] & 0xff),  crc);
  357.             }
  358.             c = readchar();
  359.             if(timeout != GOODREAD) {
  360.                 errorflag = TRUE;
  361.             if(timeout == USERABORT)
  362.                 goto exit;
  363.             }
  364.             if(p_xproto == 2) {
  365.             crc = updcrc(((unsigned int)c & 0xff), crc);
  366.             c = readchar();
  367.             if(timeout != GOODREAD) {
  368.                 errorflag = TRUE;
  369.                 if(timeout == USERABORT)
  370.                 goto exit;
  371.             }
  372.             crc = updcrc(((unsigned int)c & 0xff), crc);
  373.             good_sect = (crc == 0);
  374.             } else
  375.             good_sect = (checksum == c);
  376.             if (!good_sect) {
  377.                 errorflag = TRUE;
  378.             if(timeout == USERABORT)
  379.                 goto exit;
  380.             } else {
  381.             errors = 0;
  382.             sectnum++;
  383.             bufptr += SECSIZ;
  384.             bytes_xferred += SECSIZ;
  385.             if (bufptr == BufSize) {
  386.                 if (write(fd, bufr, BufSize-SECSIZ) == EOF) {
  387.                 InfoMsg1Line("XMODEM: Error Writing File");
  388.                 goto exit;
  389.                 }
  390.                 bufptr = SECSIZ;
  391.                 for (j = 0; j < SECSIZ; j++)
  392.                 bufr[j] = bufr[(BufSize-SECSIZ)+j];
  393.             }
  394.             sendchar(ACK);
  395.             }
  396.         } else {
  397.             /* got a duplicate sector */
  398.             if (sectcurr == (sectnum & 0xff)) {
  399.             /* wait until we time out for 5secs */
  400.             do {
  401.                 readchar();
  402.             } while (timeout == GOODREAD);
  403.             if (timeout == USERABORT)
  404.                 goto exit;
  405.             InfoMsg1Line("XMODEM: Received Duplicate Sector");
  406.             sendchar(ACK);
  407.             }
  408.             else errorflag = TRUE;
  409.         }
  410.         } else errorflag = TRUE;
  411.     }
  412.     if (errorflag == TRUE) {
  413.         errors++;
  414.         InfoMsg1Line("XMODEM: Error");
  415.         sendchar(nak_char);
  416.     }
  417.     }        /* end while */
  418.     if ((firstchar == EOT) && (errors < ERRORMAX)) {
  419.     sendchar(ACK);
  420.     if (bufptr) {
  421.         if(p_autochop) {
  422.         /* use firstchar to remember the last char for chopping */
  423.         if((firstchar = bufr[--bufptr]) == 0 || firstchar == 0x1A)
  424.             while (bufptr && bufr[--bufptr] == firstchar)
  425.             ;
  426.         bufptr++;
  427.         }
  428.         write(fd, bufr, bufptr);
  429.     }
  430.     close(fd);
  431.     ScrollInfoMsg(1);
  432.     retval = TRUE;
  433.     }
  434. exit:
  435.     Do_XON();
  436.     FreeMem(bufr, (long)BufSize);
  437.     bufr = NULL;
  438.     return retval;
  439. }
  440.  
  441. int XMODEM_Send_File(file)
  442. char *file;
  443. {
  444.     int        sectnum, bytes_to_send, size, attempts, c, use_crc = 0,
  445.         retval = FALSE;
  446.     unsigned    checksum, j, bufptr;
  447.     unsigned short crc;
  448.     char    scrstr2[40];
  449.  
  450.     if(strcmp(file, "$") == 0)
  451.     return TRUE;
  452.  
  453.     bytes_xferred = 0;
  454.     ttime = TTIME_LONG;
  455.  
  456.     if( (bufr = AllocMem((long)BufSize, MEMF_PUBLIC|MEMF_CLEAR)) == NULL) {
  457.     InfoMsg1Line("XMODEM: Can't get a buffer.");
  458.     return FALSE;
  459.     }
  460.  
  461.     if ((fd = open(file, 0)) < 0) {
  462.     InfoMsg1Line("XMODEM: Cannot Open Send File");
  463.     FreeMem(bufr, (long)BufSize);
  464.     bufr = NULL;
  465.     return FALSE;
  466.     } else
  467.     InfoMsg1Line("XMODEM Send, <esc> from VT100 window to abort");
  468.     attempts = 0;
  469.     sectnum = 1;
  470.     No_XON();
  471.     /* wait for sync char */
  472.     j=1;
  473.     while (((c = readchar()) != NAK) && (c != 'C') && (j++ < ERRORMAX))
  474.     if (timeout == USERABORT)
  475.         goto bad_exit;
  476.  
  477.     if (j >= (ERRORMAX)) {
  478.     InfoMsg1Line("XMODEM: Receiver not sending");
  479.     goto bad_exit;
  480.     }
  481.  
  482.     if(c == 'C')
  483.     use_crc = 1;
  484.     while ((bytes_to_send = read(fd, bufr, BufSize)) &&
  485.         attempts != RETRYMAX) {
  486.     if (bytes_to_send == EOF) {
  487.         InfoMsg1Line("XMODEM: Error Reading File");
  488.         goto bad_exit;
  489.     }
  490.  
  491.     bufptr = 0;
  492.     while (bytes_to_send > 0 && attempts != RETRYMAX) {
  493.         attempts = 0;
  494.         sprintf(scrstr2,"%s: Sending Block: %4d Bytes: %ld",
  495.             use_crc?"XmodemCRC":"Xmodem", sectnum,
  496.             ((long)sectnum)*SECSIZ);
  497.         size = SECSIZ <= bytes_to_send ? SECSIZ : bytes_to_send;
  498.         bytes_to_send -= size;
  499.         do {
  500.         InfoMsgNoScroll(scrstr2);
  501.         sendchar(SOH);
  502.         sendchar(sectnum);
  503.         sendchar(~sectnum);
  504.         checksum = 0; crc = 0;
  505.         for (j = bufptr; j < bufptr + size; j++) {
  506.             sendchar(bufr[j]);        /* send buffer data */
  507.             checksum += bufr[j];
  508.             crc = updcrc(((unsigned int)bufr[j] & 0xff), crc);
  509.         }
  510.         if( size < SECSIZ ) {        /* check if we need to pad */
  511.             c = bufr[j-1] ? 0 : 0x1A;    /* choose correct padding */
  512.             j = SECSIZ - size;
  513.             checksum += j * c;
  514.             while ( j-- ) {
  515.             if(use_crc)
  516.                 crc = updcrc(c, crc);
  517.             sendchar(c);        /* send padding */
  518.             }
  519.         }
  520.         if(use_crc) {
  521.             crc = updcrc(0, updcrc(0, crc));
  522.             sendchar(crc >> 8);
  523.             sendchar(crc & 0xff);
  524.         } else
  525.             sendchar(checksum);
  526.         attempts++;
  527.         c = readchar();
  528.         if (timeout == USERABORT) {
  529.             InfoMsg1Line("XMODEM: ABORTED");
  530.             goto bad_exit;
  531.         }
  532.         } while ((c != ACK) && (attempts != RETRYMAX));
  533.         bufptr += size;
  534.         bytes_xferred += size;
  535.         sectnum++;
  536.     }
  537.     }
  538.     close(fd);
  539.     if (attempts == RETRYMAX) {
  540.     InfoMsg1Line("XMODEM: No Acknowledgment, ABORTING");
  541.     goto bad_exit;
  542.     } else {
  543.     attempts = 0;
  544.     do {
  545.         sendchar(EOT);
  546.         attempts++;
  547.     } while ((readchar() != ACK) &&
  548.              (attempts != RETRYMAX) &&
  549.              (timeout != USERABORT)) ;
  550.     if (attempts == RETRYMAX)
  551.         InfoMsg1Line("XMODEM: No end of file");
  552.     }
  553.     ScrollInfoMsg(1);
  554.     retval = TRUE;
  555.  
  556. bad_exit:
  557.     Do_XON();
  558.     FreeMem(bufr, (long)BufSize);
  559.     bufr = NULL;
  560.     return retval;
  561. }
  562.  
  563. /* allow for multi file xfers separated by commas under
  564.     kermit and XMODEM */
  565.  
  566. void multi_xfer(name,mode,do_send)
  567. char *name;
  568. int (*mode)();
  569. int do_send;
  570.     {
  571.     int done = 0;
  572.     int status;
  573.     char *p, *name_start;
  574.  
  575.     fd = -1;
  576.     timeout = USERABORT - 1;
  577.     for(p=name_start=name; !done && timeout != USERABORT; name_start=++p)
  578.     {
  579.     while(*p == ' ') p++;
  580.     while(*p && *p != ',' && *p != ' ') p++;
  581.     if (*p == '\0') {
  582.         done = TRUE;
  583.         multi = 0;
  584.     }
  585.     else
  586.         multi = 1;
  587.     *p = '\0';
  588.  
  589.     status = ((*mode)(name_start, multi));
  590.     if (status == FALSE && fd >= 0) close(fd);
  591.     }
  592.     server = 0;
  593.     multi = 0;
  594.     if(p_xbeep)
  595.     cmd_beep(0L);
  596.     }
  597.  
  598.