www.pudn.com > commutil.zip > YMODEM.CPP


// ******************************************************************** // 
//                                                                      // 
//      YMODEM.CPP                                                      // 
//      Copyright (c) 1993, Michael Holmes and Bob Flanders             // 
//      C++ Communication Utilities                                     // 
//                                                                      // 
//      Chapter 6: Transferring Files                                   // 
//      Last changed in chapter 6                                       // 
//                                                                      // 
//      This file contains the functions to implement a communications  // 
//      class to support the YMODEM protocol.  This class is built on   // 
//      the Protocol class and relies on an instance of the Comm class  // 
//      to access the communications port.                              // 
//                                                                      // 
// ******************************************************************** // 
 
 
struct ym_stat                              // ymodem status information 
    { 
    unsigned 
    long filelen,                               // file length 
         totalbytes,                            // total bytes transferred 
         left;                                  // bytes left to transfer 
    int  error,                                 // error count 
         nfiles;                                // number of files 
    char dir,                                   // direction: 0=rcv, 1=send 
        *filename;                              // current filename 
    void *work;                                 // user work pointer 
    }; 
 
 
class YModem : Protocol 
    { 
    public: 
        YModem(Comm *ci,                    // define an YMODEM instance 
               int  (*sr)(struct ym_stat *, 
                          int = 0) = 0, 
               void *w = 0); 
 
        int   Receive(void),                // receive a file 
              Send(char *SendTable[]);      // send a file 
 
       ~YModem();                           // instance destructor 
 
 
 
    private: 
        Comm *c;                            // comm instance pointer 
 
        void BuildPacket(int);              // read/build a packet 
 
        int  pktsz,                         // packet size 
             cip,                           // characters in packet 
             fh,                            // file handle 
             canflag,                       // cancel flag 
             Wait1st(int timeout),          // await first char routine 
             FillPacket(int len),           // packet fill wait routine 
             ChkPacket(void),               // check received packet 
             WaitChar(long secs),           // wait for character 
             Error(int maxerr = 10),        // tally an error 
             (*status)(struct ym_stat *ym,  // status routine 
                       int mtype = 0); 
 
        struct ym_stat *ym;                 // ..and status array 
 
        char seq,                           // sequence number 
              buf[1029],                     // packet buffer 
             fn[65],                        // space for file name 
            *p,                             // ..and packet pointer 
             lsr, msr;                      // line and modem status 
    }; 
                                            // packet buffer layout 
#define YM_SEQ      buf[1]                  // packet sequence number 
#define YM_CSEQ     buf[2]                  // ..complementary seq nbr 
#define YM_DATA     buf[3]                  // ..start of data 
#define YM_CRC      buf[131]                // ..crc field - 128 block 
#define YM_CRC1     buf[1027]               // ..crc field - 1024 block 
 
#define NEWFLE      creatnew(fn, FA_NORMAL)     // create a new file 
#define OPEN4READ   open(ym->filename, S_IREAD) // open file for read 
 
 
/* ******************************************************************** * 
 * 
 *  YModem -- define a protocol transfer instance 
 * 
 * ******************************************************************** */ 
 
YModem::YModem(Comm *ci,                    // comm instance to use 
               int  (*sr)(struct ym_stat *, // status routine address 
                          int = 0), 
               void *w)                     // user work pointer 
{ 
 
c = ci;                                     // save addr of comm instance 
status = sr;                                // ..and status routine 
 
ym = new struct ym_stat;                    // allocate for status fields 
memset(ym, 0, sizeof(struct ym_stat));      // ..initialize storage 
ym->work = w;                               // ..and save work pointer 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Receive -- receive a file using YMODEM protocol 
 * 
 *  returns: 0 = successful 
 *           1 = output file already exists 
 *           2 = user cancelled transfer 
 *           3 = fatal protocol error (too many errors) 
 *           4 = sender cancelled transfer 
 *           5 = write error 
 * 
 * ******************************************************************** */ 
 
int     YModem::Receive(void)               // download path name 
{ 
int     rc = 0,                             // return code 
        loop = 1;                           // main loop control 
 
UINT    oldlcr;                             // old value of LCR 
 
enum rcvstates                              // states when receiving 
         { 
         AskHdr,                            // ask for a header packet 
         WaitHdr,                           // await header 1st char 
         EOTHdr,                            // EOT processor 
         HdrTmo,                            // timeout waiting for hdr 
         GetHdr,                            // get rest of header 
         Cancel,                            // cancel rest of transfer 
         NakHdr,                            // NAK the header packet 
         ProcHdr,                           // process header packet 
         AckHdr,                            // ACK a header packet 
         Done,                              // transfer complete 
         AckPkt,                            // ACK a packet 
         Get1st,                            // await packet 1st char 
         Get1024,                           // get 1024-byte packet 
         Get128,                            // get 128-byte packet 
         GetPkt,                            // get rest of packet 
         NakPkt,                            // NAK a data packet 
         ProcPkt,                           // process a data packet 
         AckPrev,                           // ACK previous packet 
         EndFile                            // process end of file 
         } state; 
 
oldlcr = c->Set8n();                        // set 8 data bits, no parity 
 
state = AskHdr;                             // set initial state 
 
c->IClear();                                // clear input buffer 
ym->totalbytes = 0;                         // clear total bytes transferred 
ym->nfiles = 0;                             // clear number of files 
ym->dir = 0;                                // direction = receive 
canflag = 0;                                // clear cancel flag 
(status)(ym, 3);                            // .. display start message 
 
/* ********************************************************************* 
 * 
 *  The following state table show the states of the YMODEM transfer 
 *  protocol when receiving files. 
 * 
 * ******************************************************************** 
 * 
 *   State        Action        Event         New state 
 *   -------      -------       -----         --------- 
 *   AskHdr       Seqno = 0 
 *                error = 0 
 *                send C                      WaitHdr 
 * 
 * 
 *   WaitHdr                    Timeout(5)    HdrTmo 
 *                              SOH           GetHdr 
 *                              EOT           EOTHdr 
 *                              2 CAN         Cancel (Cancelled by sender) 
 * 
 * 
 *   EOTHdr       Send ACK 
 *                error = 0                   AskHdr 
 * 
 * 
 *   HdrTmo                     User ESC      Cancel (Cancelled by user) 
 *                              ++Errors>20   Cancel (Timeout error) 
 *                                            AskHdr 
 * 
 * 
 *   GetHdr                     Timeout(1)   NakHdr 
 *                              132 bytes    ProcHdr 
 *                Addbyte                    GetHdr 
 * 
 * 
 *   Cancel       Send 2 CAN                  
 * 
 * 
 *   NakHdr       ++Error>10?                Cancel (Too many errors) 
 *                              User ESC     Cancel (Cancelled by user) 
 *                Send NAK                   WaitHdr 
 * 
 * 
 *   ProcHdr                    Seq# bad     Cancel (Sequence error) 
 *                              Bad CRC      NakHdr 
 *                              NoFileName   Done 
 *                Open file     Open error   Cancel (File error) 
 *                Save len 
 *                                           AckHdr 
 * 
 * 
 *   AckHdr                     User ESC     Cancel (Cancelled by user) 
 *                Send Ack 
 *                Send C 
 *                error = 0 
 *                Inc Seqno                  Get1st 
 * 
 * 
 *   Done         Send ACK                    
 * 
 * 
 *   AckPkt                     User ESC     Cancel (Cancelled by user) 
 *                Send Ack 
 *                error = 0 
 *                Inc Seqno                  Get1st 
 * 
 * 
 *   Get1st                     Timeout(10)  NakPkt 
 *                              2 CAN        Cancel (Cancelled by sender) 
 *                              EOT          EndFile 
 *                              SOH          Get128 
 *                              STX          Get1024 
 * 
 * 
 *   Get1024      LEN=1028                   GetPkt 
 * 
 * 
 *   Get128       LEN=132                    GetPkt 
 * 
 * 
 *   GetPkt                     Timeout(1)   NakPkt 
 *                              LEN bytes    ProcPkt 
 *                AddByte                    GetPkt 
 * 
 * 
 *   NakPkt       Error++>10?                Cancel (Too many errors) 
 *                              User ESC     Cancel (Cancelled by user) 
 *                Send Nak                   Get1st 
 * 
 * 
 *   ProcPkt                    Seq# -1?     AckPrev 
 *                              Seq# Bad     Cancel (Sequence error) 
 *                              Bad CRC      NakPkt 
 *                Write Data    Error?       Cancel (File write error) 
 *                                           AckPkt 
 * 
 * 
 *   AckPrev      Send ACK                   Get1st 
 * 
 * 
 *   EndFile      Close FIle 
 *                Error = 0;                 EOTHdr 
 * 
 * *********************************************************************/ 
 
while (loop)                                // loop till end requested 
    { 
    switch (state)                          // handle each state 
        { 
        case AskHdr:                        // ask for header 
             seq = 0;                       // zero the sequence number 
             fh = 0;                        // show no file open 
             ym->error = 0;                 // no errors 
             ym->filelen = ym->left = 0;    // no data transferred 
 
             c->Write('C');                 // send the letter C 
             state = WaitHdr;               // .. wait for the header 
             break; 
 
        case WaitHdr:                       // wait for first character 
             switch (Wait1st(5))            // ..and process return code 
                { 
                case 0:                     // end of transmission 
                    state = EOTHdr;         // process EOT 
                    break;                  // ..and exit loop 
 
                case 1:                     // SOH received 
                    state = GetHdr;         // set up to receive packet 
 
                case 2:                     // STX received 
                    break;                  // .. Ignore it, continue 
 
                case 3:                     // timeout 
                    state = HdrTmo;         // .. process timeout 
                    break;                  // .. continue processing 
 
                case 4:                     // CAN received 
                    if (++canflag == 2)     // q. two in a row? 
                        {                   // a. yes .. cancel receive 
                        rc = 4;             // .. show the reason 
                        state = Cancel;     // .. go to cancel state 
                        } 
                    break;                  // .. continue processing 
                } 
             break;                         // ..then loop around 
 
        case EOTHdr:                        // EOT received for header 
             c->Write(ACK);                 // .. tell them we got it 
             ym->error = 0;                 // .. reset the error count 
             state = AskHdr;                // .. wait for a header 
             continue;                      // .. loop around 
 
        case HdrTmo:                        // timeout awaiting header 
             if ((status)(ym))              // q. user request cancel? 
                { 
                rc = 2;                     // a. yes .. user requested 
                state = Cancel;             // .. cancel the download 
                continue;                   // .. loop around 
                } 
 
             if (Error(20))                 // q. 20 errors? 
                { 
                rc = 10;                    // a. yes .. timeout error 
                state = Cancel;             // .. cancel the download 
                continue;                   // .. loop around 
                } 
 
             state = AskHdr;                // try again for header 
             continue;                      // .. loop around 
 
        case GetHdr:                        // get remainder of header 
             switch (FillPacket(133))       // .. from the sender 
                { 
                case 0:                     // packet received 
                     state = ProcHdr;       // .. process the header 
                     break; 
 
                case 1:                     // timeout 
                     state = NakHdr;        // .. NAK the packet 
                     break; 
                } 
             continue; 
 
        case Cancel:                        // Cancel the transfer 
             Purge(c, 1);                   // .. purge receive buffer 
             c->Write(CAN);                 // .. write a cancel 
             c->Write(CAN);                 // .. twice 
             loop = 0;                      // .. done processing 
             break;                         // .. and get out 
 
        case NakHdr:                        // NAK the header packet 
             if (Error(10))                 // q. more than 10 errors? 
                { 
                rc = 3;                     // a. yes .. too many errors 
                state = Cancel;             // .. cancel the download 
                continue; 
                } 
 
             if ((status)(ym))              // q. user cancel? 
                { 
                rc = 2;                     // a. yes .. user requested 
                state = Cancel;             // .. cancel the download 
                continue;                   // .. loop around 
                } 
 
             Purge(c, 1);                   // clear receive buffer 
             c->Write(NAK);                 // tell them header was bad 
             state = WaitHdr;               // wait for a resend 
             continue;                      // .. loop around 
 
        case ProcHdr:                       // process header packet 
             switch(ChkPacket())            // packet process ok? 
                 { 
                 case 0:                    // packet ok 
 
                   if (YM_DATA == 0)        // q. file name? 
                       { 
                       state = Done;        // a. no .. done with transfer 
                       break; 
                       } 
 
                   strcpy(fn, &YM_DATA);    // copy in the file name 
                   touppers(fn);            // .. make it uppercase 
                   ym->filename = fn;       // .. and save it for later 
 
                   _fmode = O_BINARY;       // set global for binary files 
 
                   unlink(fn);              // make sure no other exists 
 
                   if ((fh = NEWFLE) == -1) // q. create new output ok? 
                       { 
                       rc = 5;              // a. no .. unable to create 
                       state=Cancel;        // .. cancel the transfer 
                       break; 
                       } 
 
                   sscanf(&YM_DATA+1+       // Scan for the file length 
                          strlen(&YM_DATA), 
                          "%lu", 
                          &ym->filelen); 
 
                   ym->left = ym->filelen;  // bytes left to transfer 
 
                   (status)(ym, 1);         // display new file & length 
                   state = AckHdr;          // ACK the header packet 
                   break; 
 
                 case 1:                    // sequence matches previous 
                 case 2:                    // bad sequence number 
                   rc = 12;                 // .. unrecoverable error 
                   state = Cancel;          // .. cancel the transfer 
 
                 case 4:                    // too many errors 
                   rc = 3;                  // .. set the return code 
                   state = Cancel;          // .. cancel the transfer 
                   break; 
 
                 case 3:                    // error in packet 
                   state = NakHdr;          // NAK the header packet 
                   break; 
                 } 
             continue;                      // .. loop around 
 
        case AckHdr:                        // ACK header packet 
             if ((status)(ym))              // q. user escape? 
                {                           // a. yes .. 
                rc = 4;                     // .. show user cancelled 
                state = Cancel;             // .. cancel the transfer 
                break; 
                } 
 
             c->Write(ACK);                 // send an ACK 
             c->Write('C');                 // .. then start the file 
 
             seq++;                         // go to next sequence number 
             state = Get1st;                // get first char of packet 
             ym->error = 0;                 // reset error count 
             continue;                      // .. continue processing 
 
        case Done:                          // All files transferred 
             rc = 0;                        // .. show successful 
             loop = 0;                      // .. get out of loop 
             c->Write(ACK);                 // .. ack that last packet 
             (status)(ym, 2);               // .. display final message 
             continue;                      // .. continue processing 
 
        case AckPkt:                        // ACK last packet 
             if ((status)(ym))              // q. user escape? 
                {                           // a. yes .. 
                rc = 4;                     // .. show user cancelled 
                state = Cancel;             // .. cancel the transfer 
                break; 
                } 
 
             c->Write(ACK);                 // send an ACK 
             seq++;                         // go to next sequence number 
             state = Get1st;                // get first char of packet 
             ym->error = 0;                 // .. reset the error count 
             continue;                      // .. continue processing 
 
        case Get1st:                        // get first char of packet 
             switch (Wait1st(10))           // ..and process return code 
                { 
                case 0:                     // end of transmission 
                    state = EndFile;        // process EOT 
                    break;                  // ..and exit loop 
 
                case 1:                     // SOH received 
                    state = Get128;         // get a 128-byte packet 
                    break; 
 
                case 2:                     // STX received 
                    state = Get1024;        // get a 1024-byte packet 
                    break; 
 
                case 3:                     // timeout 
                    state = NakPkt;         // .. NAK packet 
                    break;                  // .. continue processing 
 
                case 4:                     // CAN received? 
                    if (++canflag == 2)     // q. two in a row? 
                        {                   // a. yes .. cancel the receive 
                        rc = 4;             // .. show the reason 
                        state = Cancel;     // .. go to cancel state 
                        } 
                    break;                  // continue processing 
                } 
             continue;                      // ..then loop around 
 
        case Get1024:                       // get a 1024-byte packet 
             pktsz = 1029;                  // .. set length for packet 
             state = GetPkt;                // .. get the packet 
             continue; 
 
        case Get128:                        // get a 128-byte packet 
             pktsz = 133;                   // .. set length for packet 
             state = GetPkt;                // .. get the packet 
             continue; 
 
        case GetPkt:                        // get the packet 
             switch (FillPacket(pktsz))     // .. from the sender 
                { 
                case 0:                     // packet received 
                     state = ProcPkt;       // .. process the header 
                     break; 
 
                case 1:                     // timeout 
                     state = NakPkt;        // .. NAK the packet 
                     break; 
                } 
             continue; 
 
        case NakPkt:                        // NAK a data packet 
             if (Error(10))                 // q. too many errors? 
                 {                          // a. yes .. 
                 rc = 3;                    // .. show too many errors 
                 state = Cancel;            // .. cancel the transfer 
                 continue;                  // .. continue processing 
                 } 
 
             if ((status)(ym))              // q. user cancel transfer? 
                 {                          // a. yes .. 
                 rc = 4;                    // .. show user cancelled 
                 state = Cancel;            // .. cancel the transfer 
                 continue;                  // .. continue processing 
                 } 
 
             Purge(c, 1);                   // purge the receive buffer 
             c->Write(NAK);                 // NAK the packet 
             state = Get1st;                // .. retry the receive 
             continue;                      // .. continue processing 
 
        case ProcPkt:                       // process a packet 
             switch(ChkPacket())            // q. did packet process ok? 
                 { 
                 case 0:                    // a. yes .. packet ok 
 
                   pktsz -= 5;              // get the data length 
                   state = AckPkt;          // .. ACK this packet 
 
                   if (ym->left == 0)       // q. anything left to write? 
                       break;               // a. no .. skip the write 
 
                   pktsz = pktsz < ym->left // get the size to write 
                           ? pktsz 
                           : (int) ym->left; 
 
                   write(fh, &YM_DATA,      // write the data 
                           pktsz); 
 
                   ym->left -= pktsz;       // show amount left to write 
                   break; 
 
                 case 1:                    // sequence matches previous 
                   state = AckPrev;         // .. ACK the previous message 
                   break; 
 
                 case 2:                    // bad sequence number 
                   rc = 12;                 // .. unrecoverable error 
                   state = Cancel;          // .. cancel the transfer 
 
                 case 3:                    // error in packet 
                   state = NakPkt;          // NAK the header packet 
                   break; 
 
                 case 4:                    // too many errors 
                   rc = 3;                  // .. set the return code 
                   state = Cancel;          // .. cancel the transfer 
                   break; 
                 } 
             continue;                      // .. loop around 
 
        case AckPrev:                       // ACK previous packet 
             c->Write(ACK);                 // .. send an ACK 
             ym->error = 0;                 // .. reset the error count 
             state = Get1st;                // .. get first char of packet 
             continue; 
 
        case EndFile:                       // Complete this file 
             close(fh);                     // ..close the file 
             rc = 0;                        // ..no error 
             fh = 0;                        // ..show file closed 
             ym->totalbytes += ym->filelen; // ..add to bytes sent 
             ym->nfiles++;                  // ..increment file count 
             state = EOTHdr;                // ..process the EOT 
             continue;                      // .. continue processing 
        } 
    } 
 
if (fh)                                     // q. file open? 
    close(fh);                              // a. yes .. close it 
 
c->SetLine(oldlcr);                         // restore comm parameters 
 
return(rc);                                 // give user final state 
 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  Send -- send a file using YMODEM protocol 
 * 
 *  returns: 0 = successful 
 *           1 = input file not found 
 *           2 = user cancelled transfer 
 *           3 = fatal protocol error (too many errors) 
 *           4 = receiver cancelled transfer 
 *           5 = file error 
 * 
 * ******************************************************************** */ 
 
int     YModem::Send(char *SendTable[])     // files to send 
{ 
int     rc = 0,                             // return code 
        fileidx,                            // next file to send 
        loop = 1;                           // main loop control 
 
UINT    oldlcr;                             // old value of LCR 
 
enum sendstates 
         { 
         StartSnd,                          // start the send 
         WaitC,                             // wait for C 
         Cancel,                            // cancel the transfer 
         NextFile,                          // get next file to send 
         BldHdr,                            // build the header packet 
         SendHdr,                           // send header packet 
         WaitHdrAck,                        // wait for ACK for header 
         HdrAckTmo,                         // timeout waiting for ACK 
         NakHdr,                            // NAK received for header 
         WaitC2,                            // wait for C to start file 
         BldPkt,                            // build file packet 
         Bld128,                            // build 128-byte packet 
         Bld1024,                           // build 1024-byte packet 
         ComplPkt,                          // complete the packet 
         SendPkt,                           // send the packet 
         WaitPktAck,                        // wait for ACK for packet 
         PktAckTmo,                         // timeout awaiting ACK 
         NakPkt,                            // NAK received for packet 
         ProcEof,                           // process end of file 
         SendEot,                           // send the EOT character 
         WaitEotAck,                        // wait for ACK for EOT 
         TmoEotAck,                         // timeout on EOT ACK 
         NakEot,                            // NAK received for EOT 
         LastPkt,                           // build final packet 
         SendLast,                          // send final packet 
         WaitLastAck,                       // wait for last ACK 
         LastAckTmo,                        // timeout awaiting ACK 
         NakLast                            // last packet NAK'd 
         } state;                           // current send state 
 
 
 
oldlcr = c->Set8n();                        // set 8 data bits, no parity 
c->IClear();                                // clear input buffer 
 
state = StartSnd;                           // set initial state 
 
ym->totalbytes = 0;                         // clear total bytes transferred 
ym->nfiles = 0;                             // clear number of files 
ym->dir = 1;                                // direction = send 
canflag = 0;                                // clear cancel flag 
(status)(ym, 3);                            // .. display start message 
 
/* ********************************************************************* 
 * 
 *  The following state table show the states of the YMODEM transfer 
 *  protocol when sending files. 
 * 
 * ******************************************************************** 
 * 
 *   State        Action          Event         New state 
 *   -------      -------         -----         --------- 
 *   StartSnd     fileidx = -1                  WaitC 
 * 
 * 
 *   WaitC                        2 CAN         Cancel (By Receiver) 
 *                                User ESC      Cancel (By User) 
 *                                Timeout(60)   Cancel (Timeout) 
 *                                "C"           NextFile 
 * 
 * 
 *   Cancel       Send 2 CAN                     
 * 
 * 
 *   NextFile                     ++fileidx>4   LastPkt 
 *                                              BldHdr 
 * 
 * 
 *   BldHdr       seq = 0 
 *                open file       Open error    NextFile 
 *                get length 
 *                left = filelen 
 *                build header 
 *                Calc CRC                      SendHdr 
 * 
 * 
 *   SendHdr      SendHeaderRec                 WaitHdrAck 
 * 
 * 
 *   WaitHdrAck                   Timeout(60)   HdrAckTmo 
 *                                User ESC      Cancel (By user) 
 *                                NAK           NakHdr 
 *                                2 CAN         Cancel (By receiver) 
 *                                ACK           WaitC2 
 * 
 * 
 *   HdrAckTmo                    ++Error>5     Cancel (Timeout) 
 *                                              SendHdr 
 * 
 *   NakHdr                       ++Error>10    Cancel (Too many errors) 
 *                                              SendHdr 
 * 
 *   WaitC2                       Timeout(60)   HdrAckTmo 
 *                                User ESC      Cancel (By user) 
 *                                CAN           Cancel (By receiver) 
 *                                "C"           BldPkt 
 * 
 * 
 *   BldPkt       seq++ 
 *                left = 0                      ProcEof 
 *                left<1024                     Bld128 
 *                                              Bld1024 
 * 
 *   Bld128       1st = SOH 
 *                read 128 
 *                left -= 128                   ComplPkt 
 * 
 * 
 *   Bld1024      1st = STX 
 *                read 1024 
 *                left -= 1024                  ComplPkt 
 * 
 * 
 *   ComplPkt     insert seqnos 
 *                calc CRC                      SendPkt 
 * 
 * 
 *   SendPkt      Send packet                   WaitPktAck 
 * 
 * 
 *   WaitPktAck                   Timeout(60)   PktAckTmo 
 *                                User ESC      Cancel (By user) 
 *                                NAK           NakPkt 
 *                                2 CAN         Cancel (By receiver) 
 *                                ACK           BldPkt 
 * 
 * 
 *   PktAckTmo                    ++Error > 5   Cancel (Timeout) 
 *                                              SendPkt 
 * 
 * 
 *   NakPkt                       ++Error > 10  Cancel (Too many errors) 
 *                                              SendPkt 
 * 
 * 
 *   ProcEof       reset variables              SendEot 
 * 
 * 
 *   SendEot       Send EOT                     WaitEotAck 
 * 
 * 
 *   WaitEotAck                   Timeout(10)   TmoEotAck 
 *                                User ESC      Cancel (By user) 
 *                                NAK           NakEot 
 *                                2 CAN         Cancel (By Receiver) 
 *                                ACK           WaitC 
 * 
 * 
 *   TmoEotAck                    ++Error>10    Cancel (Timeout) 
 *                                              SendEot 
 * 
 * 
 *   NakEot                       ++Error>10    Cancel (Too many errors) 
 *                                              SendEot 
 * 
 * 
 *   LastPkt      seq = 0 
 *                zero packet 
 *                build packet                  SendLast 
 * 
 * 
 * 
 *   SendLast     Send Last Packet              WaitLastAck 
 * 
 * 
 *   WaitLastAck                  Timeout(60)   LastAckTmo 
 *                                User ESC      Cancel (By user) 
 *                                NAK           NakLast 
 *                                2 CAN         Cancel (By Receiver) 
 *                                ACK            
 * 
 * 
 *   LastAckTmo                   ++Error>5     Cancel (Timeout) 
 *                                              SendLast 
 * 
 * 
 *   NakLast                      ++Error>10    Cancel (Too many errors) 
 *                                              SendLast 
 * 
 * *********************************************************************/ 
 
while (loop)                                // loop until finished 
    { 
    switch(state)                           // process next state 
        { 
        case StartSnd:                      // start the send process 
             fileidx = -1;                  // .. init file index 
             state = WaitC;                 // .. wait for C 
             continue; 
 
        case WaitC:                         // wait for C 
             fh = 0;                        // show no file open 
             ym->error = 0;                 // no errors 
             ym->filelen = ym->left = 0;    // no data transferred 
 
             switch(WaitChar(60))           // wait for first char 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
 
                 case -2:                   // timeout 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case 'C':                  // C received 
                      state = NextFile;     // .. get the next file 
                      break; 
                 } 
             continue;                      // continue with next state 
 
        case Cancel:                        // cancel the transfer 
             Purge(c, 1);                   // .. purge receive buffer 
             c->Write(CAN);                 // .. write a CAN 
             c->Write(CAN);                 // .. twice 
             loop = 0;                      // .. exit the loop 
             continue;                      // .. go to bottom of loop 
 
        case NextFile:                      // error opening file 
             if (++fileidx > 4)             // q. all files sent? 
                {                           // a. yes .. 
                state = LastPkt;            // .. send last packet 
                continue; 
                } 
 
             if (SendTable[fileidx] != NULL)// q. entry filled in? 
                state = BldHdr;             // a. yes .. send the header 
             continue; 
 
        case BldHdr:                        // build the header packet 
             seq = 0;                       // .. set the sequence number 
             ym->filename =                 // .. get the file name 
                    SendTable[fileidx]; 
 
             _fmode = O_BINARY;             // set global for binary files 
 
             if ((fh = OPEN4READ) == -1)    // q. open ok? 
                {                           // a. no .. 
                state = NextFile;           // .. get next file, if any 
                break; 
                } 
 
             ym->filelen = ym->left =       // get length of file 
                   lseek(fh, 0l, SEEK_END); 
 
             lseek(fh, 0L, SEEK_SET);       // return to beginning of file 
 
             (status)(ym, 1);               // dispay file being sent 
 
             memset(buf, 0, 131);           // reset the packet 
 
             sprintf(&YM_DATA, "%s",        // place filename in 
                          ym->filename);    // .. in header 
 
             sprintf(&YM_DATA+              // place file length 
                      (strlen(&YM_DATA)+1), // .. after filename 
                      "%lu ", ym->filelen); // .. in header 
 
             BuildPacket(0);                // build short packet 
             state = SendHdr;               // .. and send the packet 
             continue; 
 
        case SendHdr:                       // send header packet 
             c->Write(buf, 133);            // .. send the packet 
             state = WaitHdrAck;            // .. and wait for an ACK 
             continue; 
 
        case WaitHdrAck:                    // wait for ACK for header 
             switch(WaitChar(60))           // .. get the response 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case -2:                   // timeout 
                      state = HdrAckTmo;    // .. timeout awaiting ACK 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case NAK:                  // NAK received 
                      state = NakHdr;       // .. process the NAK 
                      break; 
 
                 case ACK:                  // ACK received 
                      state = WaitC2;       // .. wait for second C 
                      break; 
                 } 
             continue; 
 
        case HdrAckTmo:                     // timeout waiting for ACK 
             if (Error(5))                  // q. enough errors? 
                {                           // a. yes .. timeout error 
                rc = 10;                    // .. show timeout error 
                state = Cancel;             // .. cancel the send 
                break; 
                } 
 
             state = SendHdr;               // else .. resend the header 
             continue; 
 
        case NakHdr:                        // NAK received for header 
             if (Error(10))                 // q. enough errors? 
                 {                          // a. yes .. too many 
                 rc = 3;                    // .. show too many errors 
                 state = Cancel;            // .. cancel the send 
                 break; 
                 } 
 
             state = SendHdr;               // else .. send header again 
             continue; 
 
        case WaitC2:                        // wait for C to start file 
             ym->error = 0;                 // .. reset the error count 
 
             switch(WaitChar(60))           // wait for a character 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case -2:                   // timeout 
                      state = HdrAckTmo;    // .. act like we missed ACK 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case 'C':                  // C received 
                      state = BldPkt;       // build the header packet 
                      ym->error = 0;        // reset error count 
                      break; 
                 } 
             continue;                      // continue with next state 
 
        case BldPkt:                        // build file packet 
             seq++;                         // .. calc next sequence no. 
             memset(&YM_DATA, 0, 1024);     // .. clear the buffer 
 
             if (ym->left == 0)             // q. anything left to send? 
                {                           // a. no .. process EOF. 
                state = ProcEof;            // .. go to that state 
                break; 
                } 
 
             if (ym ->left < 1024)          // q. 1024-bytes left? 
                state = Bld128;             // a. no .. use small packets 
              else 
                state = Bld1024;            // else .. use large ones 
             continue; 
 
        case Bld128:                        // build 128-byte packet 
             pktsz = (ym->left < 128)       // determine packet size based 
                         ? (int) ym->left   // .. on bytes left in file 
                         : 128; 
 
             read(fh, &YM_DATA, pktsz);     // .. read the data in 
             ym->left -= pktsz;             // .. and adjust bytes left 
 
             BuildPacket(0);                // build short packet 
             state = SendPkt;               // send the packet 
             continue; 
 
        case Bld1024:                       // build 1024-byte packet 
             pktsz = 1024;                  // .. set the packet size 
 
             read(fh, &YM_DATA, pktsz);     // .. read the data in 
             ym->left -= pktsz;             // .. and adjust bytes left 
 
             BuildPacket(1);                // build long packet 
             state = SendPkt;               // send the packet 
             continue; 
 
        case SendPkt:                       // send the packet 
             c->Write(buf, buf[0] == SOH    // .. send packet 
                           ? 133 : 1029);   // .. short or long 
             state = WaitPktAck;            // .. wait for response 
             continue; 
 
        case WaitPktAck:                    // wait for ACK for packet 
             switch(WaitChar(60))           // .. get the response 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case -2:                   // timeout 
                      state = PktAckTmo;    // .. timeout awaiting ACK 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case NAK:                  // NAK received 
                      state = NakPkt;       // .. process the NAK 
                      break; 
 
                 case ACK:                  // ACK received 
                      ym->error = 0;        // .. reset the error count 
                      state = BldPkt;       // .. build next packet 
                      break; 
                 } 
             continue; 
 
        case PktAckTmo:                     // Timeout awaiting ACK 
             if (Error(5))                  // q. enough errors? 
                {                           // a. yes .. timeout error 
                rc = 10;                    // .. show timeout error 
                state = Cancel;             // .. cancel the send 
                break; 
                } 
 
             state = SendPkt;               // else .. resend the packet 
             continue; 
 
        case NakPkt:                        // NAK received for packet 
             if (Error(10))                 // q. enough errors? 
                 {                          // a. yes .. too many 
                 rc = 3;                    // .. show too many errors 
                 state = Cancel;            // .. cancel the send 
                 break; 
                 } 
 
             state = SendPkt;               // else .. resent packet 
             continue; 
 
        case ProcEof:                       // process end of file 
             close(fh);                     // .. close the file 
             rc = 0;                        // .. no error 
             fh = 0;                        // .. show file closed 
             ym->totalbytes += ym->filelen; // .. add to bytes sent 
             ym->nfiles++;                  // .. increment file count 
 
             state = SendEot;               // Send out an EOT 
             continue; 
 
        case SendEot:                       // send the EOT 
             c->Write(EOT);                 // .. send the EOT 
             state = WaitEotAck;            // .. wait for the ACK 
             continue; 
 
        case WaitEotAck:                    // wait for ACK for EOT 
             switch(WaitChar(10))           // .. get the response 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case -2:                   // timeout 
                      state = TmoEotAck;    // .. timeout awaiting ACK 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case NAK:                  // NAK received 
                      state = NakEot;       // .. process the NAK 
                      break; 
 
                 case ACK:                  // ACK received 
                      state = WaitC;        // .. Start next file 
                      break; 
                 } 
             continue; 
 
        case TmoEotAck:                     // timeout on EOT ACK 
             if (Error(10))                 // q. enough errors? 
                {                           // a. yes .. timeout error 
                rc = 10;                    // .. show timeout error 
                state = Cancel;             // .. cancel the send 
                break; 
                } 
 
             state = SendEot;               // else .. resend EOT 
             continue; 
 
        case NakEot:                        // NAK received for EOT 
             if (Error(10))                 // q. enough errors? 
                 {                          // a. yes .. too many 
                 rc = 3;                    // .. show too many errors 
                 state = Cancel;            // .. cancel the send 
                 break; 
                 } 
 
             state = SendEot;               // else .. resent EOT 
             continue; 
 
        case LastPkt:                       // build final packet 
             seq = 0;                       // .. reset sequence number 
             ym->error = 0;                 // .. reset error count 
             memset(&YM_DATA, 0, 128);      // .. and the packet data 
             BuildPacket(0);                // .. finish small packet 
             state = SendLast;              // .. send that packet 
             continue; 
 
        case SendLast:                      // send final packet 
             c->Write(buf, 133);            // .. send the data 
             state = WaitLastAck;           // .. wait for the ACK 
             continue; 
 
        case WaitLastAck:                   // wait for last ACK 
             switch(WaitChar(10))           // .. get the response 
                 { 
                 case -1:                   // user cancelled 
                      rc = 14;              // .. show user cancelled 
                      state = Cancel;       // .. cancel the send 
                      break; 
 
                 case -2:                   // timeout 
                      state = LastAckTmo;   // .. timeout awaiting ACK 
                      break; 
 
                 case CAN:                  // CAN received 
                      if (++canflag == 2)   // q. two in a row? 
                          {                 // a. yes .. receiver did it 
                          rc = 15;          // .. show receiver cancelled 
                          state = Cancel;   // .. and cancel the send 
                          } 
                      break; 
 
                 case NAK:                  // NAK received 
                      state = NakLast;      // .. process the NAK 
                      break; 
 
                 case ACK:                  // ACK received 
                      rc = 0;               // .. show successful 
                      loop = 0;             // .. get out of loop 
                      (status)(ym, 2);      // .. display final message 
                      break;                // .. continue processing 
                 } 
             continue; 
 
        case LastAckTmo:                    // timeout awaiting ACK 
             if (Error(10))                 // q. enough errors? 
                {                           // a. yes .. timeout error 
                rc = 10;                    // .. show timeout error 
                state = Cancel;             // .. cancel the send 
                break; 
                } 
 
             state = SendLast;              // else .. resend EOT 
             continue; 
 
        case NakLast:                       // last packet NAK'd 
             if (Error(10))                 // q. enough errors? 
                 {                          // a. yes .. too many 
                 rc = 3;                    // .. show too many errors 
                 state = Cancel;            // .. cancel the send 
                 break; 
                 } 
 
             state = SendLast;              // else .. resent EOT 
             continue; 
        } 
 
    } 
 
if (fh)                                     // q. file open? 
    close(fh);                              // a. yes .. close it 
 
c->SetLine(oldlcr);                         // restore comm parameters 
 
return(rc);                                 // give user final state 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  Wait1st -- wait for 1st byte of response 
 * 
 *  returns: 0 = EOT received, close file 
 *           1 = SOH received, get packet data (128) 
 *           2 = STX received, get packet data (1024) 
 *           3 = timeout 
 *           4 = CAN received, cancel download 
 *           5 = C (Start send) 
 *           6 = ACK 
 *           7 = NAK 
 * 
 * ******************************************************************** */ 
 
int     YModem::Wait1st(int timeout)        // timeout value 
{ 
long    timer;                              // timeout clock 
 
 
timer = SECS(timeout);                      // init timer 
 
for(;;)                                     // set up a loop 
    { 
    if (TimeOut(&timer))                    // q. timeout waiting around? 
        return(3);                          // else .. show a timeout 
 
    if (c->Read(buf, &msr, &lsr) != 0)      // q. anything available? 
        continue;                           // a. no .. then wait more 
 
    if (buf[0] != CAN)                      // q. CAN character? 
        canflag = 0;                        // a. no .. reset cancel flag 
 
    switch (buf[0])                         // check received character 
        { 
        case SOH:                           // SOH character 
            p = &buf[1];                    // reset buffer pointer 
            cip = 1;                        // ..characters in packet 
            return(1);                      // ..and start next state 
 
        case STX:                           // STX character 
            p = &buf[1];                    // reset buffer pointer 
            cip = 1;                        // ..set chars in packet 
            return(2);                      // ..show STX received 
 
        case EOT:                           // EOT character 
            return(0);                      // ..and return to caller 
 
        case CAN:                           // CAN character 
            return(4);                      // ..and return with error 
 
        case 'C':                           // C - start send 
            return(5);                      // ..return proper value 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  BuildPacket -- place common fields in packet 
 * 
 *  entry: YM_DATA contains packet 
 * 
 *  returns: 0 = sucessful, 
 *           1 = buffer filled, send to receiver 
 *           2 = user cancelled transfer 
 *           3 = fatal protocol error (too many errors) 
 *           4 = receiver cancelled transfer 
 *           5 = file error 
 * 
 * ******************************************************************** */ 
 
void    YModem::BuildPacket(int flag)       // length of pkt, TRUE=1024 
{ 
 
buf[0] = (flag) ? STX : SOH;                // set record type 
 
YM_SEQ = seq;                               // save new sequence number 
YM_CSEQ = ~seq;                             // ..and complement 
 
if (flag)                                   // q. 1024-byte packet? 
   *(UINT *) &YM_CRC1 = CRC(&YM_DATA, 1024);// a. yes .. calc for long packet 
 else 
   *(UINT *) &YM_CRC = CRC(&YM_DATA, 128);  // else .. calc for short packet 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  WaitChar -- wait for a char from the receiver 
 * 
 *  returns: -1 = user cancelled transfer 
 *           -2 = timeout 
 *           >0 = character received 
 * 
 * ******************************************************************** */ 
 
int     YModem::WaitChar(long secs)         // timeout value 
{ 
char    wc;                                 // work character 
long    timer;                              // timeout clock 
 
 
timer = SECS(1);                            // init timer to one second 
 
if ((status)(ym))                           // q. cancel requested? 
   return(-1);                              // a. yes .. tell the caller 
 
for (;;)                                    // set up a loop 
    { 
    if (TimeOut(&timer))                    // q. timeout waiting around? 
        {                                   // a. yes .. process an error 
        if ((status)(ym))                   // q. user want to cancel? 
            return(-1);                     // a. yes .. cancel download 
 
        if (--secs == 0)                    // q. did we time out? 
            return(-2);                     // a. yes .. return error 
         else 
            timer = SECS(1);                // else .. restart timer 
        } 
 
    if (c->Read(&wc, &msr, &lsr) != 0)      // q. anything available? 
        continue;                           // a. no .. then wait more 
 
    if (wc != CAN)                          // q. CAN character? 
        canflag = 0;                        // a. no .. reset cancel flag 
 
    return((unsigned char) wc);             // else.. return character 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  FillPacket() -- Receive a packet 
 * 
 *  returns: 0 = packet complete 
 *           1 = timeout 
 * 
 * ******************************************************************** */ 
 
int     YModem::FillPacket(int len)         // length of packet 
{ 
long    timer;                              // timeout clock 
 
 
timer = SECS(1);                            // init timer for 1 second 
 
for(;;)                                     // set up a loop 
    { 
    if (TimeOut(&timer))                    // q. timeout waiting around? 
        return(1);                          // a. yes .. show timeout 
 
    if (c->Read(p, &msr, &lsr) != 0)        // q. anything available? 
        continue;                           // a. no .. then wait more 
 
    p++;                                    // next buffer position 
    timer = SECS(1);                        // ..and restart timer 
 
    if (++cip == len)                       // q. buffer full? 
        break;                              // a. yes .. packet received 
    } 
 
return(0); 
} 
 
/* ******************************************************************** * 
 * 
 *  ChkPacket() -- Check received packet 
 * 
 *  returns: 0 = packet ok 
 *           1 = sequence matches previous packet 
 *           2 = sequence error 
 *           3 = CRC error/sequence numbers don't match 
 *           4 = too many errors 
 * 
 * ******************************************************************** */ 
 
int     YModem::ChkPacket(void) 
{ 
int  len;                                   // length of packet 
UINT *crc;                                  // pointer to CRC 
 
if (buf[0] == SOH)                          // q. short packet? 
   { 
   len = 128;                               // a. yes .. length is 128 
   crc = (UINT *) &YM_CRC;                  // .. and get addr of CRC 
   } 
 else 
   { 
   len = 1024;                              // else .. length is 1024 
   crc = (UINT *) &YM_CRC1;                 // .. CRC is further out 
   } 
 
if (CRC(&YM_DATA, len) != *crc)             // q. does the crc match? 
    return(Error(10) ? 4 : 3);              // a. no .. restart packet 
 
if (YM_SEQ != ~YM_CSEQ)                     // q. seq nbrs consistent? 
    return(Error(10) ? 4 : 3);              // a. no .. restart packet 
 
if (YM_SEQ != seq)                          // q. expected sequence nbr? 
    {                                       // a. no .. check things out 
    if (YM_SEQ == (seq - 1))                // q. one packet back? 
        return(Error(10) ? 4 : 1);          // a. yes .. bypass duplicate 
     else 
        return(2);                          // else .. return fatal error 
    } 
 
return(0);                                  // return packet ok 
 
} 
 
 
/* ******************************************************************** * 
 * 
 *  Error -- tally an error and possibly cancel the transfer 
 * 
 * ******************************************************************** */ 
 
int     YModem::Error(int maxerr) 
{ 
 
return(++(ym->error) > maxerr);             // return true if too many 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  ~YModem -- class destructor 
 * 
 * ******************************************************************** */ 
 
YModem::~YModem(void) 
{ 
 
delete ym;                                  // release status control blk 
 
}