home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_08_09 / 8n09097a < prev    next >
Text File  |  1990-07-26  |  17KB  |  590 lines

  1. /*------------------------------------------------
  2. XMODEMR.C
  3.  
  4. Author      Date         Description
  5. -------------------------------------------
  6. Jon Ward  22 Apr 90  Initial Revision.
  7. Jon Ward  23 Apr 90  Cleanup and modify for
  8.                XMODEM-1K and XMODEM-CRC.
  9. Jon Ward  26 Apr 90  Corrected implementation
  10.                of XMODEM-CRC.
  11. Jon Ward  7 Jun 90   Added more comments and a
  12.                little cleanup.
  13. ------------------------------------------------*/
  14.  
  15. #define XMODEM_LIB 1
  16.  
  17. #include <stdio.h>
  18. #include "xmodem.h"
  19.  
  20.  
  21. #define STATIC static     /* undef for debugging */
  22.  
  23. /*------------------------------------------------
  24. ------------------------------------------------*/
  25. struct send_n_wait_st
  26.   {
  27.   char char_to_send;
  28.   int retry_count;
  29.   long ms_timeout;
  30.   unsigned char *valid_responses;
  31.   int num_valid_responses;
  32.   };
  33.  
  34. STATIC unsigned char soh_stx_can [] =
  35.   { SOH, STX, CAN };
  36. STATIC unsigned char soh_stx_can_eot [] =
  37.   { SOH, STX, CAN, EOT };
  38.  
  39. STATIC struct send_n_wait_st crc_req =
  40.   {
  41.   'C',
  42.   CRC_RETRY_COUNT,
  43.   CRC_TIMEOUT,
  44.   soh_stx_can,
  45.   sizeof (soh_stx_can)
  46.   };
  47.  
  48. STATIC struct send_n_wait_st checksum_req =
  49.   {
  50.   NAK,
  51.   NAK_RETRY_COUNT,
  52.   NAK_TIMEOUT,
  53.   soh_stx_can,
  54.   sizeof (soh_stx_can)
  55.   };
  56.  
  57. STATIC struct send_n_wait_st pack_nak =
  58.   {
  59.   NAK,
  60.   NAK_RETRY_COUNT,
  61.   NAK_TIMEOUT,
  62.   soh_stx_can_eot,
  63.   sizeof (soh_stx_can_eot)
  64.   };
  65.  
  66. STATIC struct send_n_wait_st pack_ack =
  67.   {
  68.   ACK,
  69.   ACK_RETRY_COUNT,
  70.   ACK_TIMEOUT,
  71.   soh_stx_can_eot,
  72.   sizeof (soh_stx_can_eot)
  73.   };
  74.  
  75.  
  76. /*------------------------------------------------
  77. Error messages for error enums.
  78. ------------------------------------------------*/
  79. STATIC char *xmodem_errors [] =
  80.   {
  81.   "Transmission Successful",
  82.   "NULL Transmit function pointer",
  83.   "NULL Receive function pointer",
  84.   "Receiver cancelled the transfer",
  85.   "Sender cancelled the transfer",
  86.   "User cancelled the transfer",
  87.   "Error reading the file",
  88.   "Error writing the file",
  89.   "Timed out waiting for data pack ACK",
  90.   "Timed out waiting for initial NAK",
  91.   "Timed out waiting for SOH",
  92.   "Timed out waiting for data",
  93.   "Timed out waiting for final ACK",
  94.   "Invalid char waiting for SOH",
  95.   "Block mismatch in packet header",
  96.   "CRC is incorrect",
  97.   "Checksum is incorrect",
  98.   "Block out of sequence",
  99.   "Received character error",
  100.   "Modem is not online",
  101.   };
  102.  
  103.  
  104. /*------------------------------------------------
  105.         Local Function Prototypes
  106. ------------------------------------------------*/
  107. STATIC int xm_send_n_wait (
  108.   const struct send_n_wait_st *req,    /* request structure */
  109.   unsigned char *response,        /* response from sender */
  110.   xfunc *xmf);                /* xmodem external functions */
  111.  
  112. STATIC int xm_block_start (
  113.   xblock *xb,            /* xmodem block data */
  114.   unsigned char block_start,    /* block start char from sender */
  115.   xfunc *xmf);            /* xmodem external functions */
  116.  
  117. STATIC int xm_recv_block (
  118.   xblock *xb,            /* xmodem block data */
  119.   register xfunc *xmf);        /* xmodem external functions */
  120.  
  121.  
  122. /*------------------------------------------------
  123. This function receives a file transferred via
  124. XMODEM, XMODEM-1K or XMODEM/CRC.  The f argument
  125. represents the file to receive that has been
  126. opened for writing.
  127. ------------------------------------------------*/
  128. int xmodem_recv (
  129.   FILE *f,                    /* file to write to */
  130.   int (*transmit) (char),            /* xmit function */
  131.   int (*receive) (long, unsigned int *),    /* recv function */
  132.   void (*dispstat) (long, long, const char *),    /* display function */
  133.   int (*check_abort) (void))            /* manual abort function */
  134. {
  135. register int error;      /* gen purpose error var */
  136. unsigned char start_char; /* first char of block */
  137. unsigned char next_block; /* next block we expect */
  138. unsigned char last_block; /* last successful block */
  139. xblock xb;          /* xmodem block data */
  140. xfunc xmfuncs;          /* xmodem external functions */
  141.  
  142.  
  143. /*------------------------------------------------
  144. Initialize the function pointer structure.
  145. ------------------------------------------------*/
  146. if ((xmfuncs.dispstat = dispstat) == NULL)
  147.   xmfuncs.dispstat = xm_no_disp_func;
  148.  
  149. if ((xmfuncs.check_abort = check_abort) == NULL)
  150.   xmfuncs.check_abort = xm_no_abort_func;
  151.  
  152. if ((xmfuncs.transmit = transmit) == NULL)
  153.   return (xm_perror (XERR_XMIT_FUNC, &xmfuncs));
  154.  
  155. if ((xmfuncs.receive = receive) == NULL)
  156.   return (xm_perror (XERR_RCVR_FUNC, &xmfuncs));
  157.  
  158.  
  159. /*------------------------------------------------
  160. Initialize data for the first block and purge
  161. all data from the receive buffer.  Init the 
  162. number of bytes and blocks received and display
  163. some useful info.
  164. ------------------------------------------------*/
  165. next_block = last_block = 1;
  166.  
  167. xb.total_block_count = 0L;
  168. xb.total_byte_count = 0L;
  169.  
  170. (*xmfuncs.dispstat) (0L, 0L, "");
  171.  
  172. PURGE_RECEIVER(receive);
  173.  
  174.  
  175. /*------------------------------------------------
  176. Attempt to transfer using CRC-16 error detection.
  177. This involves sending the CRC begin character:
  178. 'C'.
  179. ------------------------------------------------*/
  180. xb.crc_used = 1;
  181. error = xm_send_n_wait (&crc_req,
  182.             &start_char,
  183.             &xmfuncs);
  184.  
  185.  
  186. /*------------------------------------------------
  187. If the sender did not respond to the CRC-16
  188. transfer request, then attempt to transfer using
  189. checksum error detection.
  190. ------------------------------------------------*/
  191. if (error == XERR_SOH_TIMEOUT)
  192.   {
  193.   xb.crc_used = 0;
  194.   error = xm_send_n_wait (&checksum_req,
  195.               &start_char,
  196.               &xmfuncs);
  197.   }
  198.  
  199.  
  200. /*------------------------------------------------
  201. If begin transfer request failed, return error.
  202. ------------------------------------------------*/
  203. if (error != XERR_OK)
  204.   return (error);
  205.  
  206.  
  207. /*------------------------------------------------
  208. If the starting character of the next block is
  209. an EOT, then we have completed transferring the
  210. file and we exit this loop.  Otherwise, we init
  211. the xmodem packet structure based on the first
  212. character of the packet.
  213. ------------------------------------------------*/
  214. while (start_char != EOT)
  215.   {
  216.   register int good_block;    /* NZ if packet was OK */
  217.  
  218.   error = xm_block_start (&xb,
  219.               start_char,
  220.               &xmfuncs);
  221.  
  222.   if (error != XERR_OK)
  223.     return (error);
  224.  
  225.   good_block = -1;        /* assume packet will be OK */
  226.  
  227.  
  228. /*------------------------------------------------
  229. Receive the packet.  If there was an error, then
  230. NAK it.  Otherwise, the packet was received OK.
  231. ------------------------------------------------*/
  232.   if (xm_recv_block (&xb, &xmfuncs) != XERR_OK)
  233.     {
  234.     good_block = 0;            /* bad block */
  235.     }
  236.  
  237. /*------------------------------------------------
  238. If this is the next expected packet, then append
  239. it to the file and update the last and next
  240. packet vars.
  241. ------------------------------------------------*/
  242.   else if (xb.block_num == next_block)
  243.     {
  244.     int bytes_written;    /* bytes written for this block */
  245.  
  246.     last_block = next_block;
  247.     next_block = (next_block + 1) % 256;
  248.  
  249.     bytes_written = fwrite (xb.buffer, 1, xb.buflen, f);
  250.  
  251.     xb.total_block_count++;
  252.     xb.total_byte_count += bytes_written;
  253.  
  254.     (*xmfuncs.dispstat) (xb.total_block_count,
  255.              xb.total_byte_count,
  256.              NULL);
  257.  
  258.     if (bytes_written != xb.buflen)
  259.       {
  260.       xm_send_cancel (transmit);
  261.       return (xm_perror (XERR_FILE_WRITE, &xmfuncs));
  262.       }
  263.     }
  264.  
  265.  
  266. /*------------------------------------------------
  267. If this is the previous packet, then the sender
  268. did not receive our ACK to that packet and
  269. resent it.  This is OK.  Just ACK the packet.
  270.  
  271. If the block number for this packet is completely
  272. out of sequence, cancel the transmission and
  273. return an error.
  274. ------------------------------------------------*/
  275.   else if (xb.block_num != last_block)
  276.     {
  277.     xm_send_cancel (transmit);
  278.     return (xm_perror (XERR_BLOCK_SEQUENCE, &xmfuncs));
  279.     }
  280.  
  281.  
  282. /*------------------------------------------------
  283. Here, good_block is non-zero if the block was
  284. received and processed with no problems.  If it
  285. was a good block, then we send an ACK.  A NAK is
  286. sent for bad blocks.
  287. ------------------------------------------------*/
  288.   if (good_block)
  289.     {
  290.     error = xm_send_n_wait (&pack_ack,
  291.                 &start_char,
  292.                 &xmfuncs);
  293.     }
  294.   else
  295.     {
  296.     PURGE_RECEIVER(receive);
  297.     error = xm_send_n_wait (&pack_nak,
  298.                 &start_char,
  299.                 &xmfuncs);
  300.     }
  301.  
  302.  
  303.   if (error != XERR_OK)
  304.     return (error);
  305.   }
  306.  
  307.  
  308. /*------------------------------------------------
  309. The whole file has been received, so attempt to
  310. send an ACK to the final EOT.
  311. ------------------------------------------------*/
  312. if ((*transmit) (ACK) != XMIT_OK)
  313.   return (xm_perror (XERR_OFFLINE, &xmfuncs));
  314.  
  315. return (xm_perror (XERR_OK, &xmfuncs));
  316. }
  317.  
  318.  
  319. /*------------------------------------------------
  320. Dummy function used in case caller did not supply
  321. a display function.
  322. ------------------------------------------------*/
  323. void xm_no_disp_func (
  324.   long a,
  325.   long b,
  326.   const char *buf)
  327. {
  328. a = a;
  329. b = b;
  330. buf = buf;    /* avoid compiler warnings */
  331. }
  332.  
  333.  
  334. /*------------------------------------------------
  335. Dummy function used in case caller did not supply
  336. a used abort function.
  337. ------------------------------------------------*/
  338. int xm_no_abort_func (void)
  339. {
  340. return (0);
  341. }
  342.  
  343.  
  344. /*------------------------------------------------
  345. This function transmits a character and waits for
  346. a response.  The req argument points to a
  347. structure containing info about the char to
  348. transmit, retry count, timeout, etc.  The
  349. response argument points to a storage place for
  350. the received character.  The xmf argument points
  351. to a structure of caller supplied functions.
  352.  
  353. Any errors encountered are returned.
  354. ------------------------------------------------*/
  355. STATIC int xm_send_n_wait (
  356.   const struct send_n_wait_st *req,    /* request structure */
  357.   unsigned char *response,        /* response from sender */
  358.   register xfunc *xmf)            /* xmodem external functions */
  359. {
  360. int j;
  361. unsigned int rerr;
  362.  
  363. for (j = 0; j < req->retry_count; j++)
  364.   {
  365.   register int rcvd_char;
  366.   register int i;
  367.  
  368. /*------------------------------------------------
  369. Check to see if the user aborted the transfer.
  370. ------------------------------------------------*/
  371.   if ((*xmf->check_abort) () != 0)
  372.     {
  373.     xm_send_cancel (xmf->transmit);
  374.     return (xm_perror (XERR_USER_CANCEL, xmf));
  375.     }
  376.  
  377.  
  378. /*------------------------------------------------
  379. Transmit the block response (or block start)
  380. character.
  381. ------------------------------------------------*/
  382.   if ((*xmf->transmit) (req->char_to_send) != XMIT_OK)
  383.     return (xm_perror (XERR_OFFLINE, xmf));
  384.  
  385.  
  386. /*------------------------------------------------
  387. Wait for a response.  If there isn't one or if a
  388. parity or similar error occurred, continue with
  389. next iteration of the retry loop.
  390. ------------------------------------------------*/
  391.   rcvd_char = (*xmf->receive) (req->ms_timeout, &rerr);
  392.  
  393.   if (rcvd_char == RECV_TIMEOUT)
  394.     continue;
  395.  
  396.   if (rerr != 0)
  397.     return (xm_perror (XERR_CHAR_ERROR, xmf));
  398.  
  399.  
  400. /*------------------------------------------------
  401. Initialize the response and check to see if it is
  402. valid.
  403. ------------------------------------------------*/
  404.   if (response != NULL)
  405.     *response = (unsigned char) rcvd_char;
  406.  
  407.   for (i = 0; i < req->num_valid_responses; i++)
  408.     if (rcvd_char == req->valid_responses [i])
  409.       return (XERR_OK);
  410.   }
  411.  
  412. return (xm_perror (XERR_SOH_TIMEOUT, xmf));
  413. }
  414.  
  415. /*------------------------------------------------
  416. This function analyzes valid block start
  417. characters to determine block size or in the case
  418. of CAN whether to abort the transmission.
  419.  
  420. Any errors encountered are returned.
  421. ------------------------------------------------*/
  422. STATIC int xm_block_start (
  423.   register xblock *xb,        /* xmodem block data */
  424.   unsigned char block_start,    /* block start char from sender */
  425.   register xfunc *xmf)        /* xmodem external functions */
  426. {
  427. switch (block_start)
  428.   {
  429.   case SOH:                /* NORMAL 128-byte block */
  430.     xb->buflen = XMODEM_BLOCK_SIZE;
  431.     return (XERR_OK);
  432.  
  433.   case STX:                /* 1024-byte block */
  434.     xb->buflen = XMODEM_1K_BLOCK_SIZE;
  435.     return (XERR_OK);
  436.  
  437.   case CAN:                /* Abort signal */
  438.     if ((*xmf->receive) (CAN_TIMEOUT, NULL) == CAN)
  439.       {
  440.       xm_send_cancel (xmf->transmit);
  441.       return (xm_perror (XERR_SEND_CANCEL, xmf));
  442.       }
  443.     break;
  444.   }
  445.  
  446. return (xm_perror (XERR_INVALID_SOH, xmf));
  447. }
  448.  
  449.  
  450. /*------------------------------------------------
  451. This function receives the block numbers, block
  452. data, and block checksum or CRC.  The received
  453. data is stored in the structure pointed to by the
  454. xb argument.  The block numbers are compared, and
  455. the checksum or CRC is verified.
  456.  
  457. Any errors encountered are returned.
  458. ------------------------------------------------*/
  459. STATIC int xm_recv_block (
  460.   xblock *xb,            /* xmodem block data */
  461.   register xfunc *xmf)        /* xmodem external functions */
  462. {
  463. register int i;
  464. unsigned int rerr;        /* receive error */
  465.  
  466.  
  467. /*------------------------------------------------
  468. Attempt to receive the block number.  If the
  469. receiver timesout or if there is a receive error,
  470. ignore the rest of the packet.
  471. ------------------------------------------------*/
  472. if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  473.   return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  474.  
  475. if (rerr != 0)
  476.   return (xm_perror (XERR_CHAR_ERROR, xmf));
  477.  
  478. xb->block_num = (unsigned char) i;
  479.  
  480.  
  481. /*------------------------------------------------
  482. Attempt to receive the one's complement of the
  483. block number.
  484. ------------------------------------------------*/
  485. if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  486.   return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  487.  
  488. if (rerr != 0)
  489.   return (xm_perror (XERR_CHAR_ERROR, xmf));
  490.  
  491. xb->not_block_num = (unsigned char) i;
  492.  
  493.  
  494. /*------------------------------------------------
  495. Make sure that the block number and one's
  496. complemented block number agree.
  497. ------------------------------------------------*/
  498. if ((255 - xb->block_num) != xb->not_block_num)
  499.   return (xm_perror (XERR_INVALID_BLOCK_NUM, xmf));
  500.  
  501.  
  502. /*------------------------------------------------
  503. Clear the CRC and checksum accumulators and
  504. receive the data block.
  505. ------------------------------------------------*/
  506. xb->crc = 0;
  507. xb->checksum = 0;
  508.  
  509. for (i = 0; i < xb->buflen; i++)
  510.   {
  511.   int rcvd_char;
  512.  
  513.   if ((rcvd_char = (*xmf->receive) (DATA_TIMEOUT, &rerr)) ==
  514.       RECV_TIMEOUT)
  515.     return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  516.  
  517.   if (rerr != 0)
  518.     return (xm_perror (XERR_CHAR_ERROR, xmf));
  519.  
  520.   xb->buffer [i] = (unsigned char) rcvd_char;
  521.  
  522.   if (xb->crc_used != 0)
  523.     xb->crc = xm_update_CRC (xb->crc, xb->buffer [i]);
  524.   else
  525.     xb->checksum += xb->buffer [i];
  526.   }
  527.  
  528.  
  529. /*------------------------------------------------
  530. Validate the CRC.
  531. ------------------------------------------------*/
  532. if (xb->crc_used)
  533.   {
  534.   if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  535.     return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  536.  
  537.   if (rerr != 0)
  538.     return (xm_perror (XERR_CHAR_ERROR, xmf));
  539.  
  540.   if ((unsigned char) i != (unsigned char) (xb->crc >> 8))
  541.     return (xm_perror (XERR_INVALID_CRC, xmf));
  542.  
  543.   if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  544.     return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  545.  
  546.   if (rerr != 0)
  547.     return (xm_perror (XERR_CHAR_ERROR, xmf));
  548.  
  549.   if ((unsigned char) i != (unsigned char) (xb->crc & 0xFF))
  550.     return (xm_perror (XERR_INVALID_CRC, xmf));
  551.   }
  552.  
  553.  
  554. /*------------------------------------------------
  555. Validate the checksum.
  556. ------------------------------------------------*/
  557. else
  558.   {
  559.   if ((i = (*xmf->receive) (DATA_TIMEOUT, &rerr)) == RECV_TIMEOUT)
  560.     return (xm_perror (XERR_DATA_TIMEOUT, xmf));
  561.  
  562.   if (rerr != 0)
  563.     return (xm_perror (XERR_CHAR_ERROR, xmf));
  564.  
  565.   if ((unsigned char) i != xb->checksum)
  566.     return (xm_perror (XERR_INVALID_CHECKSUM, xmf));
  567.   }
  568.  
  569. return (XERR_OK);
  570. }
  571.  
  572.  
  573. /*------------------------------------------------
  574. This function prints an XMODEM status message
  575. using the caller supplied display function.  The
  576. error argument is returned.
  577. ------------------------------------------------*/
  578. int xm_perror (
  579.   int error,        /* error number */
  580.   xfunc *xmf)        /* xmodem external functions */
  581. {
  582. xmf->dispstat (-1L, -1L, xmodem_errors [error]);
  583. return (error);
  584. }
  585.  
  586. /*------------------------------------------------
  587. ------------------------------------------------*/
  588.  
  589.  
  590.