home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_08_09 / 8n09104a < prev    next >
Text File  |  1990-06-24  |  13KB  |  480 lines

  1.  
  2. /*------------------------------------------------
  3. XMODEMS.C
  4.  
  5. Author      Date         Description
  6. -------------------------------------------
  7. Jon Ward  22 Apr 90  Initial Revision.
  8. Jon Ward  23 Apr 90  Cleanup and modify for
  9.                XMODEM-1K and XMODEM-CRC.
  10. Jon Ward  26 Apr 90  Corrected implementation of
  11.                XMODEM-CRC.
  12. ------------------------------------------------*/
  13. #define XMODEM_LIB 1
  14.  
  15. #include <stdio.h>
  16. #include "xmodem.h"
  17.  
  18.  
  19. #define STATIC static    /* undef for debugging */
  20.  
  21. /*------------------------------------------------
  22.         Local Function Prototypes
  23. ------------------------------------------------*/
  24. STATIC int xm_send_file (
  25.   FILE *f,        /* file pointer */
  26.   xblock *xb,        /* pointer to block data */
  27.   xfunc *xmf);        /* xmodem external functions */
  28.  
  29. STATIC int xm_send_block (
  30.   xblock *xb,        /* pointer to block data */
  31.   xfunc *xmf);        /* xmodem external functions */
  32.  
  33.  
  34. /*------------------------------------------------
  35. This function sends a file via XMODEM, XMODEM-1K
  36. or XMODEM/CRC.  The f argument represents the
  37. file to send that has been opened for reading.
  38. ------------------------------------------------*/
  39. int xmodem_send (
  40.   int block_size,                /* maximum block size */
  41.   FILE *f,                    /* file to write to */
  42.   int (*transmit) (char),            /* xmit function */
  43.   int (*receive) (long, unsigned int *),    /* recv function */
  44.   void (*dispstat) (long, long, const char *),    /* display function */
  45.   int (*check_abort) (void))            /* manual abort function */
  46. {
  47. xblock xb;            /* block data */
  48. xfunc xmfuncs;            /* xmodem external functions */
  49. register int error_count;    /* counter for errors */
  50. register int can_count;        /* cancel counter */
  51. int error;            /* gen error var */
  52. unsigned int rerr;        /* received char error */
  53.  
  54. /*------------------------------------------------
  55. Initialize the function pointer structure.
  56. ------------------------------------------------*/
  57. if ((xmfuncs.dispstat = dispstat) == NULL)
  58.   xmfuncs.dispstat = xm_no_disp_func;
  59.  
  60. if ((xmfuncs.check_abort = check_abort) == NULL)
  61.   xmfuncs.check_abort = xm_no_abort_func;
  62.  
  63. if ((xmfuncs.transmit = transmit) == NULL)
  64.   return (xm_perror (XERR_XMIT_FUNC, &xmfuncs));
  65.  
  66. if ((xmfuncs.receive = receive) == NULL)
  67.   return (xm_perror (XERR_RCVR_FUNC, &xmfuncs));
  68.  
  69.  
  70. /*------------------------------------------------
  71. ------------------------------------------------*/
  72. xb.total_block_count = 0L;
  73. xb.total_byte_count = 0L;
  74.  
  75. (*xmfuncs.dispstat) (0L, 0L, "");
  76.  
  77. PURGE_RECEIVER(receive);
  78.  
  79.  
  80. /*------------------------------------------------
  81. Purge all data from the receive buffer.  Then we
  82. wait for 1 full minute.  If we receive no
  83. characters during that time, then we return
  84. indicating the file was not transferred.  If we
  85. receive a NAK, then we begin transmission.  If
  86. we receive an excessive number of garbage
  87. characters, then we return indicating no file
  88. transfer.
  89. ------------------------------------------------*/
  90. for (error_count = 0, can_count = 0; 1; )
  91.   {
  92.   int rcvd_char;
  93.  
  94.   if (error_count >= START_XMIT_RETRY_COUNT)
  95.     return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
  96.  
  97.   /* try for 1 minute */
  98.   rcvd_char = (*xmfuncs.receive) (START_XMIT_TIMEOUT, &rerr);
  99.  
  100.   if (rerr != 0)
  101.     {
  102.     xm_send_cancel (xmfuncs.transmit);
  103.     return (xm_perror (XERR_CHAR_ERROR, &xmfuncs));
  104.     }
  105.  
  106.  
  107.   switch (rcvd_char)
  108.     {
  109.     case RECV_TIMEOUT:
  110.       xm_send_cancel (xmfuncs.transmit);
  111.       return (xm_perror (XERR_NAK_TIMEOUT, &xmfuncs));
  112.  
  113.     case 'C':
  114.       xb.crc_used = 1;
  115.       break;
  116.  
  117.     case NAK:
  118.       xb.crc_used = 0;
  119.       break;
  120.  
  121.     case CAN:
  122.       if (++can_count >= CAN_COUNT_ABORT)
  123.     {
  124.     xm_send_cancel (xmfuncs.transmit);
  125.     return (xm_perror (XERR_RCVR_CANCEL, &xmfuncs));
  126.     }
  127.       continue;
  128.  
  129.     default:
  130.       can_count = 0;
  131.       error_count++;
  132.       continue;
  133.     }
  134.  
  135.   break;
  136.   }
  137.  
  138.  
  139. /*------------------------------------------------
  140. Setup the block size and the start of block
  141. character and send the file.
  142. ------------------------------------------------*/
  143. if (block_size == XMODEM_1K_BLOCK_SIZE)
  144.   {
  145.   xb.buflen = XMODEM_1K_BLOCK_SIZE;
  146.   xb.start_char = STX;
  147.   }
  148. else
  149.   {
  150.   xb.buflen = XMODEM_BLOCK_SIZE;
  151.   xb.start_char = SOH;
  152.   }
  153.  
  154. if ((error = xm_send_file (f, &xb, &xmfuncs)) != XERR_OK)
  155.   return (error);
  156.  
  157.  
  158. /*------------------------------------------------
  159. Now, we send an EOT to the receiver and we
  160. expect to get an ACK within 1 minute.  If we
  161. don't then we timeout and report an error.  If
  162. we get any character other than an ACK, we
  163. retransmit the EOT.  If we get an ACK, then we
  164. exit with hopeful success.
  165. ------------------------------------------------*/
  166. while (1)
  167.   {
  168.   if ((*xmfuncs.transmit) (EOT) != XMIT_OK)
  169.     return (xm_perror (XERR_OFFLINE, &xmfuncs));
  170.  
  171.   switch ((*receive) (START_XMIT_TIMEOUT, &rerr))
  172.     {
  173.     case RECV_TIMEOUT:
  174.       return (xm_perror (XERR_LAST_ACK_TIMEOUT, &xmfuncs));
  175.  
  176.     case ACK:
  177.       if (rerr != 0)
  178.     continue;
  179.  
  180.       break;
  181.  
  182.     default:
  183.       continue;
  184.     }
  185.  
  186.   break;
  187.   }
  188.  
  189. return (xm_perror (XERR_OK, &xmfuncs));
  190. }
  191.  
  192.  
  193. /*------------------------------------------------
  194. This function transmits the file associated with
  195. the file pointer f according to the requirements
  196. of the XMODEM transfer protocol.
  197. ------------------------------------------------*/
  198. STATIC int xm_send_file (
  199.   FILE *f,            /* file pointer */
  200.   register xblock *xb,        /* pointer to block data */
  201.   register xfunc *xmf)        /* xmodem external functions */
  202. {
  203. int done;
  204.  
  205. /*------------------------------------------------
  206. Repeat until we have sent the whole file.
  207. ------------------------------------------------*/
  208. for (done = 0, xb->block_num = 1; !done; xb->block_num++)
  209.   {
  210.   size_t bytes_read;
  211.   int error;
  212.  
  213.  
  214.  
  215. /*------------------------------------------------
  216. Read a block of data.  If it was not a full
  217. block, then check for a file error.  If it was a
  218. file error, cancel the transfer and return an
  219. error.  Otherwise, pad the remainder of the
  220. block with CPM EOFs.
  221. ------------------------------------------------*/
  222.   bytes_read = fread (xb->buffer, 1, xb->buflen, f);
  223.  
  224.   if (bytes_read != xb->buflen)
  225.     {
  226.     register int i;
  227.  
  228.     if (ferror (f))
  229.       {
  230.       xm_send_cancel (xmf->transmit);
  231.       return (xm_perror (XERR_FILE_READ, xmf));
  232.       }
  233.  
  234.     for (i = bytes_read; i < xb->buflen; i++)
  235.       xb->buffer [i] = CPMEOF;
  236.  
  237.     done = -1;
  238.     }
  239.  
  240. /*------------------------------------------------
  241. Calculate the one's complement block number and
  242. send the block.
  243. ------------------------------------------------*/
  244.   xb->not_block_num = (unsigned char) (255 - xb->block_num);
  245.  
  246.   if ((error = xm_send_block (xb, xmf)) != XERR_OK)
  247.     return (error);
  248.   }
  249.  
  250. return (XERR_OK);
  251. }
  252.  
  253.  
  254. /*------------------------------------------------
  255. This function transmits the block described by the
  256. structure pointed to by the xb argument.  The
  257. transmit and receive arguments point to functions
  258. that transmit and receive data to and from the
  259. modem.  A value of 0 is returned to indicate that
  260. the block was successfully sent.  A non-zero
  261. return value indicates an offline condition.
  262. ------------------------------------------------*/
  263. STATIC int xm_send_block (
  264.   register xblock *xb,        /* pointer to block data */
  265.   register xfunc *xmf)        /* xmodem external functions */
  266. {  
  267. int error_count;
  268.  
  269. error_count = 0;
  270.  
  271. while (1)
  272.   {
  273.   int i;
  274.   int can_count;        /* number of CANS received */
  275.   int block_resp;        /* response to block */
  276.   unsigned rerr;        /* received char error */
  277.  
  278.  
  279. /*------------------------------------------------
  280. If there were too many errors, then exit with
  281. error.
  282. ------------------------------------------------*/
  283.   if (error_count > BLOCK_RETRY_COUNT)
  284.     {
  285.     xm_send_cancel (xmf->transmit);
  286.     return (xm_perror (XERR_ACK_TIMEOUT, xmf));
  287.     }
  288.  
  289.  
  290. /*------------------------------------------------
  291. Check for user abort and process it if
  292. applicable.
  293. ------------------------------------------------*/
  294.   if ((*xmf->check_abort) () != 0)
  295.     {
  296.     xm_send_cancel (xmf->transmit);
  297.     return (xm_perror (XERR_USER_CANCEL, xmf));
  298.     }
  299.  
  300.  
  301. /*------------------------------------------------
  302. Transmit the block start character (it's
  303. different depending on whether we're sending
  304. 1024-BYTE or 128-BYTE packets.
  305.  
  306. Send the block number.
  307.  
  308. Send the one's complement of the block number.
  309. ------------------------------------------------*/
  310.   if ((*xmf->transmit) (xb->start_char) != XMIT_OK)
  311.     return (xm_perror (XERR_OFFLINE, xmf));
  312.  
  313.   if ((*xmf->transmit) (xb->block_num) != XMIT_OK)
  314.     return (xm_perror (XERR_OFFLINE, xmf));
  315.  
  316.   if ((*xmf->transmit) (xb->not_block_num) != XMIT_OK)
  317.     return (xm_perror (XERR_OFFLINE, xmf));
  318.  
  319.  
  320. /*------------------------------------------------
  321. Clear the CRC and checksum and send the data
  322. block while building the CRC or checksum.
  323. ------------------------------------------------*/
  324.   xb->crc = 0;
  325.   xb->checksum = 0;
  326.  
  327.   for (i = 0; i < xb->buflen; i++)
  328.     {
  329.     if ((*xmf->transmit) (xb->buffer [i]) != XMIT_OK)
  330.       return (xm_perror (XERR_OFFLINE, xmf));
  331.  
  332.     if (xb->crc_used != 0)
  333.       xb->crc = xm_update_CRC (xb->crc, xb->buffer [i]);
  334.     else
  335.       xb->checksum += xb->buffer [i];
  336.     }
  337.  
  338.       
  339. /*------------------------------------------------
  340. Send the CRC or checksum.  If we send the CRC,
  341. we must send the High BYTE first.
  342. ------------------------------------------------*/
  343.   if (xb->crc_used == 0)
  344.     {
  345.     if ((*xmf->transmit) (xb->checksum) != XMIT_OK)
  346.       return (xm_perror (XERR_OFFLINE, xmf));
  347.     }
  348.   else
  349.     {
  350.     if ((*xmf->transmit) ((unsigned char) (xb->crc >> 8)) != XMIT_OK)
  351.       return (xm_perror (XERR_OFFLINE, xmf));
  352.  
  353.     if ((*xmf->transmit) ((unsigned char) (xb->crc & 0xFF)) != XMIT_OK)
  354.       return (xm_perror (XERR_OFFLINE, xmf));
  355.     }
  356.  
  357.  
  358. /*------------------------------------------------
  359. Wait for the receiver to respond with an ACK or
  360. a NAK.  If we timeout waiting, then return an
  361. error code.  If we get a NAK, retransmit the
  362. block.  If we gat an ACK, then return with
  363. status OK.  If we receive 2 consecutive CANs,
  364. then we abort the transmission.
  365. ------------------------------------------------*/
  366.   can_count = 0;
  367.  
  368. GET_BLOCK_RESPONSE:
  369.   block_resp = (*xmf->receive) (BLOCK_RESP_TIMEOUT, &rerr);
  370.  
  371.   if (rerr != 0)
  372.     {
  373.     xm_perror (XERR_CHAR_ERROR, xmf);
  374.     goto GET_BLOCK_RESPONSE;
  375.     }
  376.  
  377.   switch (block_resp)
  378.     {
  379.     case ACK:
  380.       error_count = 0;
  381.  
  382.       xb->total_block_count++;
  383.       xb->total_byte_count += xb->buflen;
  384.  
  385.       (*xmf->dispstat) (xb->total_block_count,
  386.             xb->total_byte_count,
  387.             NULL);
  388.       break;
  389.  
  390.     case CAN:
  391.       if (++can_count >= CAN_COUNT_ABORT)
  392.     {
  393.     xm_send_cancel (xmf->transmit);
  394.     return (xm_perror (XERR_RCVR_CANCEL, xmf));
  395.     }
  396.       goto GET_BLOCK_RESPONSE;
  397.  
  398.  
  399. /*------------------------------------------------
  400. I have seen 2 ways of handling this problem. One
  401. is to return an error indicating that the sender
  402. never received a packet ACK.  Another method
  403. retransmits the packet just sent in hopes that
  404. the receiver will get its ACK together.  The
  405. second one is what I did.
  406. ------------------------------------------------*/
  407.     case RECV_TIMEOUT:
  408.       error_count++;
  409. #if 0
  410.       return (xm_perror (XERR_ACK_TIMEOUT, xmf));
  411. #else
  412.       xm_perror (XERR_ACK_TIMEOUT, xmf);
  413.       continue;
  414. #endif
  415.  
  416.     case NAK:
  417.       xm_perror (XERR_BLOCK_NAK, xmf);
  418.       error_count++;
  419.       continue;
  420.  
  421.     default:
  422.       goto GET_BLOCK_RESPONSE;
  423.     }
  424.  
  425.   break;
  426.   }
  427.  
  428. return (XERR_OK);
  429. }
  430.  
  431. /*------------------------------------------------
  432. This function updates a CRC accumulator for xmodem
  433. CCITT CRC.
  434. ------------------------------------------------*/
  435. unsigned int xm_update_CRC (
  436.   register unsigned int crc,    /* current CRC */
  437.   unsigned int c)        /* character to add to CRC */
  438. {
  439. static unsigned int crc_polynomial = 0x1021;
  440. register int i;
  441.  
  442. c <<= 8;
  443.  
  444. for (i = 0; i < 8; i++)
  445.   {
  446.   if ((c ^ crc) & 0x8000)
  447.     crc = (crc << 1) ^ crc_polynomial;
  448.   else
  449.     crc <<= 1;
  450.  
  451.   c <<= 1;
  452.   }
  453.  
  454. return (crc);
  455. }
  456.  
  457.  
  458. /*------------------------------------------------
  459. This function sends 8 CANs and 8 BSs as done by
  460. YAM.  This is used to cancel an X or Y Modem send
  461. or receive.
  462. ------------------------------------------------*/
  463. void xm_send_cancel (
  464.   int (*transmit) (char))    /* transmit function */
  465. {
  466. register int i;
  467.  
  468. for (i = 0; i < 8; i++)
  469.   (*transmit) (CAN);
  470.  
  471. for (i = 0; i < 8; i++)
  472.   (*transmit) (BACKSPACE);
  473. }
  474.  
  475. /*------------------------------------------------
  476. ------------------------------------------------*/
  477.  
  478.  
  479.  
  480.