home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / MODEM / UWPC201.ZIP / UW-SRC.ZIP / XMODEM.CPP < prev   
Encoding:
C/C++ Source or Header  |  1991-05-30  |  13.1 KB  |  492 lines

  1. //-------------------------------------------------------------------------
  2. //
  3. // XMODEM.CPP - Declarations for handling X/YMODEM file transfers.
  4. // 
  5. //  This file is part of UW/PC - a multi-window comms package for the PC.
  6. //  Copyright (C) 1990-1991  Rhys Weatherley
  7. //
  8. //  This program is free software; you can redistribute it and/or modify
  9. //  it under the terms of the GNU General Public License as published by
  10. //  the Free Software Foundation; either version 1, or (at your option)
  11. //  any later version.
  12. //
  13. //  This program is distributed in the hope that it will be useful,
  14. //  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. //  GNU General Public License for more details.
  17. //
  18. //  You should have received a copy of the GNU General Public License
  19. //  along with this program; if not, write to the Free Software
  20. //  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. //
  22. // Revision History:
  23. // ================
  24. //
  25. //  Version  DD/MM/YY  By  Description
  26. //  -------  --------  --  --------------------------------------
  27. //    1.1    11/05/91  RW  Original Version of XMODEM.CPP
  28. //
  29. // You may contact the author by:
  30. // =============================
  31. //
  32. //  e-mail: rhys@cs.uq.oz.au
  33. //    mail: Rhys Weatherley
  34. //          5 Horizon Drive
  35. //          Jamboree Heights
  36. //          Queensland 4074
  37. //        Australia
  38. //
  39. //-------------------------------------------------------------------------
  40.  
  41. #include <stdio.h>        // Standard I/O routines.
  42. #include "files.h"        // Declarations for this module.
  43. #include "uw.h"            // UW protocol master declarations.
  44. #include "timer.h"        // Timer handling routines.
  45. #include "display.h"        // Display handling routines.
  46.  
  47. #pragma    warn    -par
  48.  
  49. //
  50. // Uncomment the following line to turn on debugging writes.
  51. //
  52. // #define    XMOD_DEBUG    1
  53.  
  54. //
  55. // Define the allowable states the file transfer can be in.
  56. //
  57. #define    ST_XREC        0    // Start XMODEM receive (original).
  58. #define    ST_XREC_CRC    1    // Start XMODEM receive (CRC-16).
  59. #define    ST_XSEND    2    // Start XMODEM send (original).
  60. #define ST_XSEND_CRC    3    // Start XMODEM send (CRC-16).
  61. #define    ST_YREC        4    // Start YMODEM receive.
  62. #define    ST_YREC_G    5    // Start YMODEM/G receive.
  63. #define    ST_YSEND    6    // Start YMODEM send.
  64. #define    ST_XREC_CRC_2    7    // To switch from CRC-16 to checksum receive.
  65. #define    ST_HEAD_START    8    // Start of a block header.
  66. #define    ST_PURGE    9    // Purge the line.
  67. #define    ST_BLOCK_NUM    10    // Get the block number.
  68. #define    ST_COMP_BLOCK    11    // Get the complemented block number.
  69. #define    ST_READ_DATA    12    // Read data from the block.
  70. #define    ST_GET_CHKSUM    13    // Get the checksum for current block.
  71. #define    ST_SEND_DATA    14    // Send data to the remote host.
  72. #define    ST_SEND_STOP    15    // Send EOT to remote host.
  73.  
  74. //
  75. // Define the special X/YMODEM file transfer constants.
  76. //
  77. #define    SOH        0x01
  78. #define    STX        0x02
  79. #define    EOT        0x04
  80. #define    ACK        0x06
  81. #define    NAK        0x15
  82. #define    CAN        0x18
  83. #define    EOF_CHAR    0x1A
  84.  
  85. #define    TICKS_PER_SEC    19    // Set at 19 to give "at least" one second.
  86.  
  87. //
  88. // Define the starting states for each of the transfer types.
  89. //
  90. static    int    SendStates[] =
  91.       {ST_XSEND,ST_XSEND_CRC,ST_XSEND,ST_XSEND_CRC,
  92.        ST_YSEND,ST_YSEND,ST_YSEND};
  93. static    int    RecvStates[] =
  94.       {ST_XREC,ST_XREC_CRC,ST_XREC,ST_XREC_CRC,
  95.        ST_YREC,ST_YREC,ST_YREC_G};
  96.  
  97. UWXYFileTransfer::UWXYFileTransfer (UWDisplay *wind,int type,int recv,
  98.                     char *name) :
  99.         UWFileTransfer (wind)
  100. {
  101.   kind = type;
  102.   receive = recv;
  103.   if (!recv)
  104.     {
  105.       file = fopen (name,"rb");    // Open the file for binary mode reading.
  106.       state = SendStates[kind];    // Get the initial state to be processed.
  107.     }
  108.    else
  109.     {
  110.       file = fopen (name,"wb");    // Open the file for binary mode writing.
  111.       state = RecvStates[kind];    // Get the initial state to be processed.
  112.     }
  113.   blocknum = 1;
  114.   UWMaster.install (this);    // Install the file transfer object.
  115.   if (file == NULL)
  116.     UWMaster.remove ();        // Remove object if initialisation failed.
  117.    else
  118.     {
  119.       setvbuf (file,NULL,_IOFBF,BUFSIZ); // Set a file buffer if possible.
  120.       UWMaster.direct (1);    // Turn on direct Protocol 0 handling.
  121.     }
  122.   if (recv)
  123.     {
  124.       timer = TimerCount - 1;    // Simulate an immediate timeout.
  125.       timeout = 0;
  126.     }
  127.    else
  128.     timeout = -1;
  129.   crcblocks = -1;
  130.   bigblock = 0;
  131.   start = 1;
  132.   endfile = 0;
  133.   if (!recv)
  134.     readblock ();        // Read the first block from the file.
  135. } // UWXYFileTransfer::UWXYFileTransfer //
  136.  
  137. UWXYFileTransfer::~UWXYFileTransfer (void)
  138. {
  139.   if (file != NULL)
  140.     fclose (file);
  141.   UWMaster.direct (0);
  142. } // UWXYFileTransfer::~UWXYFileTransfer //
  143.  
  144. //
  145. // Define the names of the various transfer types.
  146. //
  147. static    char    *TransferNames[] =
  148.       {"XMDM","XMDMC","XMDM1","XMD1C","YMDM","YMDMB","YMDMG"};
  149.  
  150. char far *UWXYFileTransfer::name ()
  151. {
  152.   return ((char far *)TransferNames[kind]);
  153. } // UWXYFileTransfer::name //
  154.  
  155. // Cancel a transmission - a CAN character was received.
  156. void    UWXYFileTransfer::cancel (void)
  157. {
  158.   fclose (file);
  159.   file = NULL;        // Indicator for the destructor.
  160.   UWMaster.remove ();    // Remove us from the client stack.
  161.   UWMaster.direct (0);
  162. } // UWXYFileTransfer::cancel //
  163.  
  164. // Setup the current state to perform a line purge.
  165. // This uses a 2-second instead of the standard
  166. // XMODEM 1-second timeout to allow for load on the remote host.
  167. #define    purge()        (state = ST_PURGE, timer = TimerCount, \
  168.              timeout = 2 * TICKS_PER_SEC)
  169.  
  170. // Setup a 1-second timeout for characters in a block.
  171. #define    one_sec_timeout() (timer = TimerCount, timeout = TICKS_PER_SEC)
  172.  
  173. // Send the cancel sequence to the remote host.
  174. #define    cancel_seq()    { int x; for (x = 0;x < 10;++x) send (CAN); \
  175.                  for (x = 0;x < 10;++x) send (8); }
  176.  
  177. // Do some processing for the current state.  If
  178. // "ch" is -1, then a timeout has occurred.
  179. void    UWXYFileTransfer::process (int ch)
  180. {
  181. #ifdef    XMOD_DEBUG
  182.   /* if (ch != -1)
  183.     window -> send (ch); */
  184. #endif
  185.   switch (state)
  186.     {
  187.       case ST_XREC_CRC_2:
  188.       case ST_XREC:
  189.               if (ch == CAN)
  190.           {
  191.             cancel_seq ();
  192.             cancel ();
  193.             break;
  194.           }
  195.               if (ch != SOH && ch != STX && ch != EOT)
  196.           {
  197.             send (NAK);
  198.             timer = TimerCount;
  199.             timeout = 5 * TICKS_PER_SEC;
  200.             state = ST_XREC;
  201.             break;
  202.           }
  203.         if (crcblocks < 0)
  204.           {
  205.             if (state != ST_XREC)
  206.               crcblocks = 1;
  207.              else
  208.               crcblocks = 0;
  209.           }
  210.         // Fall through to receiving a block start //
  211.       case ST_HEAD_START:
  212. #ifdef    XMOD_DEBUG
  213.               window -> send ('H');
  214. #endif
  215.               if (ch == CAN || ch == EOT)
  216.           {
  217.             if (ch == EOT)
  218.               send (ACK);    // Acknowledge end of file and cancel.
  219.             cancel_seq ();
  220.             cancel ();
  221.             break;
  222.           }
  223.         if (ch != SOH && ch != STX)
  224.           {
  225.             purge ();
  226.             break;
  227.           }
  228.         if (ch == SOH)
  229.           bigblock = 0;
  230.          else
  231.           bigblock = 1;
  232.         one_sec_timeout ();
  233.         state = ST_BLOCK_NUM;
  234.         break;
  235.       case ST_XREC_CRC:
  236.               if (ch == -1)
  237.           {
  238.             send ('C');
  239.             timer = TimerCount;
  240.             timeout = 3 * TICKS_PER_SEC;
  241.             state = ST_XREC_CRC_2;    // Go back to normal transfer.
  242.             break;
  243.           }
  244.         state = ST_HEAD_START;
  245.         crcblocks = 1;
  246.         process (ch);
  247.         break;
  248.       case ST_XSEND:
  249. #ifdef    XMOD_DEBUG
  250.         window -> send ('H');
  251.         if (ch != -1)
  252.           window -> send (ch);
  253. #endif
  254.               if (ch == CAN)        // If a cancel character - abort.
  255.           {
  256.             cancel_seq ();
  257.             cancel ();
  258.             break;
  259.           }
  260.                else if (ch == ACK)    // Block acknowledged - get next
  261.           {
  262.             if (!start && !readblock ())
  263.               {
  264.                 // We are at the end of the file - send EOT's //
  265.                 state = ST_SEND_STOP;
  266.             send (EOT);
  267.             break;
  268.               }
  269.           }
  270.          else if (ch != NAK)    // Waiting for a NAK.
  271.           break;
  272.         if (bigblock)
  273.           send (STX);
  274.          else
  275.           send (SOH);
  276.         send (blocknum);
  277.         send (blocknum ^ 255);
  278.         state = ST_SEND_DATA;    // We want to send data to remote.
  279.         chksum = 0;
  280.         posn = 0;
  281.         start = 0;
  282.         break;
  283.       case ST_XSEND_CRC:
  284.       case ST_YREC:
  285.       case ST_YREC_G:
  286.       case ST_YSEND:
  287.               break;
  288.       case ST_PURGE:
  289. #ifdef    XMOD_DEBUG
  290.               window -> send ('P');
  291. #endif
  292.               if (ch == -1)
  293.           {
  294.             // Timeout occurred - line has been purged //
  295.             send (NAK);
  296.             timer = TimerCount;
  297.             timeout = 5 * TICKS_PER_SEC;
  298.             state = ST_XREC;        // Wait for a block again.
  299.           }
  300.          else
  301.           {
  302.             // Ignore the purged character and timeout again //
  303.             purge ();
  304.           }
  305.         break;
  306.       case ST_BLOCK_NUM:
  307. #ifdef    XMOD_DEBUG
  308.               window -> send ('B');
  309. #endif
  310.         if (ch != -1)
  311.           {
  312.             // We can continue for the expected or last block //
  313.             thisblock = ch;
  314.             state = ST_COMP_BLOCK;
  315.             one_sec_timeout ();
  316.           }
  317.          else
  318.           purge ();
  319.         break;
  320.       case ST_COMP_BLOCK:
  321. #ifdef    XMOD_DEBUG
  322.               window -> send ('b');
  323. #endif
  324.               if (ch != (thisblock ^ 255))
  325.           purge ();
  326.          else if (thisblock != blocknum &&
  327.                   thisblock != (blocknum - 1))
  328.           {
  329.             cancel_seq ();    // Gross loss of sync - abort.
  330.             cancel ();
  331.           }
  332.          else
  333.           {
  334.             posn = 0;        // Start reading data in block.
  335.             chksum = 0;
  336.             state = ST_READ_DATA;
  337.             one_sec_timeout ();
  338.           }
  339.         break;
  340.       case ST_READ_DATA:
  341. #ifdef    XMOD_DEBUG
  342.               if (posn == 0)
  343.           window -> send ('D');
  344. #endif
  345.               if (ch == -1)
  346.           {
  347.             purge ();
  348.             break;
  349.           }
  350.         chksum += ch;        // Update the checksum.
  351.         buffer[posn++] = ch;
  352.         if ((bigblock && posn >= 1024) || (!bigblock && posn >= 128))
  353.           {
  354.             // Got all of the data bytes - wait for checksum //
  355.             state = ST_GET_CHKSUM;
  356.             one_sec_timeout ();
  357.           }
  358.          else
  359.           one_sec_timeout ();    // Wait for next character.
  360.         break;
  361.       case ST_GET_CHKSUM:
  362. #ifdef    XMOD_DEBUG
  363.               window -> send ('C');
  364. #endif
  365.               if (ch == -1)
  366.           {
  367.             purge ();
  368.             break;
  369.           }
  370.         if ((chksum & 255) != ch)
  371.           purge ();        // Illegal checksum.
  372.          else
  373.           {
  374.             int temp;
  375.             if (thisblock == blocknum)
  376.               {
  377.                 // Write the block to the output file //
  378.                 for (temp = 0;temp < posn;++temp)
  379.               fputc (buffer[temp],file);
  380.             ++blocknum;    // Move onto the next block number.
  381.             blocknum &= 255;
  382.             UWMaster.status (); // Update the status line.
  383.               }
  384.             send (ACK);        // Acknowledge the block.
  385.             state = ST_XREC;    // Back around for next block.
  386.           }
  387.         break;
  388.       case ST_SEND_DATA:
  389. #ifdef    XMOD_DEBUG
  390.         if (posn == 0)
  391.           window -> send ('D');
  392. #endif
  393.               // Note: incoming characters are ignored during block send //
  394.         ch = buffer[posn++];
  395. #ifdef    XMOD_DEBUG
  396.         window -> send (ch);
  397. #endif
  398.         chksum += (ch & 255);
  399.         send (ch);            // Send next data character.
  400.         if ((bigblock && posn >= 1024) || (!bigblock && posn >= 128))
  401.           {
  402. #ifdef    XMOD_DEBUG
  403.             window -> send ('C');
  404. #endif
  405.             send (chksum & 255);    // Send the block's checksum.
  406.             state = ST_XSEND;        // Back to sending blocks.
  407.           }
  408.         break;
  409.       case ST_SEND_STOP:
  410. #ifdef    XMOD_DEBUG
  411.         window -> send ('E');
  412. #endif
  413.               if (ch != ACK && ch != CAN)
  414.           {
  415.             send (EOT);        // Re-send end of file marker.
  416.             break;
  417.           }
  418.         cancel_seq ();        // Cancel the transfer.
  419.         cancel ();
  420.         break;
  421.     } /* switch */
  422. } // UWXYFileTransfer::process //
  423.  
  424. // Read a block from the file to be sent.  Returns
  425. // non-zero if OK, or zero at the end of the file.
  426. int    UWXYFileTransfer::readblock (void)
  427. {
  428.   int posn,size,ch;
  429.   if (endfile)
  430.     return (0);            // The whole file has been sent.
  431.   posn = 0;
  432.   if (bigblock)
  433.     size = 1024;
  434.    else
  435.     size = 128;
  436.   while (size && (ch = fgetc (file)) != EOF)
  437.     {
  438.       // Fill the transmission buffer with the next block //
  439.       buffer[posn++] = ch;
  440.       --size;
  441.     } /* while */
  442.   while (size--)
  443.     buffer[posn++] = EOF_CHAR;    // Fill the rest of the block with ^Z's
  444.   if (ch == EOF)
  445.     endfile = 1;        // Mark the end of the file for next call.
  446.   if (!start)
  447.     {
  448.       blocknum = (blocknum + 1) & 255; // Increment the block count.
  449.       UWMaster.status ();    // Update the status line.
  450.     }
  451.   return (1);            // Ready the block to be sent.
  452. } // UWXYFileTransfer::readblock //
  453.  
  454. void    UWXYFileTransfer::key (int keypress)
  455. {
  456.   if (keypress == 033)        // Abort transfer if ESC pressed.
  457.     {
  458.       cancel_seq ();
  459.       cancel ();
  460.     }
  461.    else
  462.     defkey (keypress);        // Do the default key operation.
  463. } // UWXYFileTransfer::key //
  464.  
  465. void    UWXYFileTransfer::tick (void)
  466. {
  467.   if (timeout != -1 && (TimerCount - timer) > timeout)
  468.     {
  469.       timeout = -1;        // Disallow another timeout.
  470.       process (-1);        // Tell the state machine a timeout occurred.
  471.     }
  472.   if (state == ST_SEND_DATA)
  473.     process (-1);        // Need to send some data to remote.
  474. } // UWXYFileTransfer::tick //
  475.  
  476. char    *UWXYFileTransfer::getstatus (void)
  477. {
  478.   static char *sendnames[] =
  479.         {"XMODEM upload - ESC to abort - %0 blocks transferred"};
  480.   static char *recvnames[] =
  481.           {"XMODEM download - ESC to abort - %0 blocks transferred"};
  482.   if (receive)
  483.     return (recvnames[kind]);
  484.    else
  485.     return (sendnames[kind]);
  486. } // UWXYFileTransfer::getstatus //
  487.  
  488. int    UWXYFileTransfer::getstatarg (int digit)
  489. {
  490.   return (blocknum - 1);
  491. } // UWXYFileTransfer::getstatarg //
  492.