Logo Search packages:      
Sourcecode: qterm version File versions  Download package

qtermzmodem.cpp

#include "qtermzmodem.h"
#include "qtermtelnet.h"
#include "qtermframe.h"
#include "qterm.h"
#include "qtermconfig.h"

#include <qapplication.h>
#include <string.h>
#include <qfiledialog.h>
#include <qfileinfo.h>


extern QString fileCfg;
extern QString getOpenFileName(const QString&, QWidget*);

#ifdef QTERM_DEBUG // for zmodemlog()
#include <sys/time.h>
#endif

/*
 *  Crc calculation stuff
 */

/* crctab calculated by Mark G. Mendel, Network Systems Corporation */
unsigned short crctab[256] = {
    0x0000,  0x1021,  0x2042,  0x3063,  0x4084,  0x50a5,  0x60c6,  0x70e7,
    0x8108,  0x9129,  0xa14a,  0xb16b,  0xc18c,  0xd1ad,  0xe1ce,  0xf1ef,
    0x1231,  0x0210,  0x3273,  0x2252,  0x52b5,  0x4294,  0x72f7,  0x62d6,
    0x9339,  0x8318,  0xb37b,  0xa35a,  0xd3bd,  0xc39c,  0xf3ff,  0xe3de,
    0x2462,  0x3443,  0x0420,  0x1401,  0x64e6,  0x74c7,  0x44a4,  0x5485,
    0xa56a,  0xb54b,  0x8528,  0x9509,  0xe5ee,  0xf5cf,  0xc5ac,  0xd58d,
    0x3653,  0x2672,  0x1611,  0x0630,  0x76d7,  0x66f6,  0x5695,  0x46b4,
    0xb75b,  0xa77a,  0x9719,  0x8738,  0xf7df,  0xe7fe,  0xd79d,  0xc7bc,
    0x48c4,  0x58e5,  0x6886,  0x78a7,  0x0840,  0x1861,  0x2802,  0x3823,
    0xc9cc,  0xd9ed,  0xe98e,  0xf9af,  0x8948,  0x9969,  0xa90a,  0xb92b,
    0x5af5,  0x4ad4,  0x7ab7,  0x6a96,  0x1a71,  0x0a50,  0x3a33,  0x2a12,
    0xdbfd,  0xcbdc,  0xfbbf,  0xeb9e,  0x9b79,  0x8b58,  0xbb3b,  0xab1a,
    0x6ca6,  0x7c87,  0x4ce4,  0x5cc5,  0x2c22,  0x3c03,  0x0c60,  0x1c41,
    0xedae,  0xfd8f,  0xcdec,  0xddcd,  0xad2a,  0xbd0b,  0x8d68,  0x9d49,
    0x7e97,  0x6eb6,  0x5ed5,  0x4ef4,  0x3e13,  0x2e32,  0x1e51,  0x0e70,
    0xff9f,  0xefbe,  0xdfdd,  0xcffc,  0xbf1b,  0xaf3a,  0x9f59,  0x8f78,
    0x9188,  0x81a9,  0xb1ca,  0xa1eb,  0xd10c,  0xc12d,  0xf14e,  0xe16f,
    0x1080,  0x00a1,  0x30c2,  0x20e3,  0x5004,  0x4025,  0x7046,  0x6067,
    0x83b9,  0x9398,  0xa3fb,  0xb3da,  0xc33d,  0xd31c,  0xe37f,  0xf35e,
    0x02b1,  0x1290,  0x22f3,  0x32d2,  0x4235,  0x5214,  0x6277,  0x7256,
    0xb5ea,  0xa5cb,  0x95a8,  0x8589,  0xf56e,  0xe54f,  0xd52c,  0xc50d,
    0x34e2,  0x24c3,  0x14a0,  0x0481,  0x7466,  0x6447,  0x5424,  0x4405,
    0xa7db,  0xb7fa,  0x8799,  0x97b8,  0xe75f,  0xf77e,  0xc71d,  0xd73c,
    0x26d3,  0x36f2,  0x0691,  0x16b0,  0x6657,  0x7676,  0x4615,  0x5634,
    0xd94c,  0xc96d,  0xf90e,  0xe92f,  0x99c8,  0x89e9,  0xb98a,  0xa9ab,
    0x5844,  0x4865,  0x7806,  0x6827,  0x18c0,  0x08e1,  0x3882,  0x28a3,
    0xcb7d,  0xdb5c,  0xeb3f,  0xfb1e,  0x8bf9,  0x9bd8,  0xabbb,  0xbb9a,
    0x4a75,  0x5a54,  0x6a37,  0x7a16,  0x0af1,  0x1ad0,  0x2ab3,  0x3a92,
    0xfd2e,  0xed0f,  0xdd6c,  0xcd4d,  0xbdaa,  0xad8b,  0x9de8,  0x8dc9,
    0x7c26,  0x6c07,  0x5c64,  0x4c45,  0x3ca2,  0x2c83,  0x1ce0,  0x0cc1,
    0xef1f,  0xff3e,  0xcf5d,  0xdf7c,  0xaf9b,  0xbfba,  0x8fd9,  0x9ff8,
    0x6e17,  0x7e36,  0x4e55,  0x5e74,  0x2e93,  0x3eb2,  0x0ed1,  0x1ef0
};

/*
 * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. 
 *  NOTE: First argument must be in range 0 to 255.
 *        Second argument is referenced twice.
 * 
 * Programmers may incorporate any or all code into their programs, 
 * giving proper credit within the source. Publication of the 
 * source routines is permitted so long as proper credit is given 
 * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, 
 * Omen Technology.
 */

#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)

/*
 * Copyright (C) 1986 Gary S. Brown.  You may use this program, or
 * code or tables extracted from it, as desired without restriction.
 */

/* First, the polynomial itself and its table of feedback terms.  The  */
/* polynomial is                                                       */
/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
/* Note that we take it "backwards" and put the highest-order term in  */
/* the lowest-order bit.  The X^32 term is "implied"; the LSB is the   */
/* X^31 term, etc.  The X^0 term (usually shown as "+1") results in    */
/* the MSB being 1.                                                    */

/* Note that the usual hardware shift register implementation, which   */
/* is what we're using (we're merely optimizing it by doing eight-bit  */
/* chunks at a time) shifts bits into the lowest-order term.  In our   */
/* implementation, that means shifting towards the right.  Why do we   */
/* do it this way?  Because the calculated CRC must be transmitted in  */
/* order from highest-order term to lowest-order term.  UARTs transmit */
/* characters in order from LSB to MSB.  By storing the CRC this way,  */
/* we hand it to the UART in the order low-byte to high-byte; the UART */
/* sends each low-bit to hight-bit; and the result is transmission bit */
/* by bit from highest- to lowest-order term without requiring any bit */
/* shuffling on our part.  Reception works similarly.                  */

/* The feedback terms table consists of 256, 32-bit entries.  Notes:   */
/*                                                                     */
/*     The table can be generated at runtime if desired; code to do so */
/*     is shown later.  It might not be obvious, but the feedback      */
/*     terms simply represent the results of eight shift/xor opera-    */
/*     tions for all combinations of data and CRC register values.     */
/*                                                                     */
/*     The values must be right-shifted by eight bits by the "updcrc"  */
/*     logic; the shift must be unsigned (bring in zeroes).  On some   */
/*     hardware you could probably optimize the shift in assembler by  */
/*     using byte-swap instructions.                                   */

unsigned long cr3tab[] = { /* CRC polynomial 0xedb88320 */
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };

#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF))

#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)



#if 1
StateTable  QTermZmodem::RStartOps[] =  {
        {ZSINIT,&QTermZmodem::GotSinit,0,1,RSinitWait},     /* SINIT, wait for attn str */
        {ZFILE,&QTermZmodem::GotFile,0,0,RFileName},  /* FILE, wait for filename */
        {ZRQINIT,&QTermZmodem::SendRinit,0,1,RStart}, /* sender confused, resend */
        {ZFIN,&QTermZmodem::GotFin,1,0,RFinish},            /* sender shutting down */
        {ZNAK,&QTermZmodem::SendRinit,1,0,RStart},          /* RINIT was bad, resend */
        {ZFREECNT,&QTermZmodem::GotFreecnt,0,0,RStart},     /* sender wants free space */
        {ZCOMMAND,&QTermZmodem::GotCommand,0,0,CommandData}, /* sender wants command */
        {ZSTDERR,&QTermZmodem::GotStderr,0,0,StderrData},   /* sender wants to send msg */
        {ZRINIT, &QTermZmodem::ZmodemTInit,1,1,TStart},
        {99,&QTermZmodem::ZPF,0,0,RStart},                  /* anything else is an error */
      } ;

StateTable  QTermZmodem::RSinitWaitOps[] = {    /* waiting for data */
        {99,&QTermZmodem::ZPF,0,0,RSinitWait},
      } ;

StateTable  QTermZmodem::RFileNameOps[] = {     /* waiting for file name */
        {99,&QTermZmodem::ZPF,0,0,RFileName},
      } ;

StateTable  QTermZmodem::RCrcOps[] = {          /* waiting for CRC */
        {ZCRC,&QTermZmodem::GotFileCrc,0,0,RFile},            /* sender sent it */
        {ZNAK,&QTermZmodem::ResendCrcReq,0,0,RCrc},           /* ZCRC was bad, resend */
        {ZRQINIT,&QTermZmodem::SendRinit,1,1,RStart},   /* sender confused, restart */
        {ZFIN,&QTermZmodem::GotFin,1,1,RFinish},              /* sender signing off */
        {99,&QTermZmodem::ZPF,0,0,RCrc},
      } ;


StateTable  QTermZmodem::RFileOps[] = {         /* waiting for ZDATA */
        {ZDATA,&QTermZmodem::GotData,0,0,RData},              /* got it */
        {ZNAK,&QTermZmodem::ResendRpos,0,0,RFile},            /* ZRPOS was bad, resend */
        {ZEOF,&QTermZmodem::GotEof,0,0,RStart},         /* end of file */
        {ZRQINIT,&QTermZmodem::SendRinit,1,1,RStart},   /* sender confused, restart */
        {ZFILE,&QTermZmodem::ResendRpos,0,0,RFile},           /* ZRPOS was bad, resend */
        {ZFIN,&QTermZmodem::GotFin,1,1,RFinish},              /* sender signing off */
        {99,&QTermZmodem::ZPF,0,0,RFile},
      } ;


/* waiting for data, but a packet could possibly arrive due
       * to error recovery or something
       */
StateTable  QTermZmodem::RDataOps[] = {
        {ZRQINIT,&QTermZmodem::SendRinit,1,1,RStart}, /* sender confused, restart */
        {ZFILE,&QTermZmodem::GotFile,0,1,RFileName},  /* start a new file (??) */
        {ZNAK,&QTermZmodem::ResendRpos,1,1,RFile},          /* ZRPOS was bad, resend */
        {ZFIN,&QTermZmodem::GotFin,1,1,RFinish},            /* sender signing off */
        {ZDATA,&QTermZmodem::GotData,0,1,RData},            /* file data follows */
        {ZEOF,&QTermZmodem::GotEof,1,1,RStart},       /* end of file */
        {99,&QTermZmodem::ZPF,0,0,RData},
      } ;

      /* here if we've sent ZFERR or ZABORT.  Waiting for ZFIN */

StateTable  QTermZmodem::RFinishOps[] = {
        {ZRQINIT,&QTermZmodem::SendRinit,1,1,RStart}, /* sender confused, restart */
        {ZFILE,&QTermZmodem::GotFile,1,1,RFileName},  /* start a new file */
        {ZNAK,&QTermZmodem::GotFin,1,1,RFinish},            /* resend ZFIN */
        {ZFIN,&QTermZmodem::GotFin,1,1,RFinish},            /* sender signing off */
        {99,&QTermZmodem::ZPF,0,0,RFinish},
      } ;


            /* sent ZRQINIT, waiting for response */
StateTable  QTermZmodem::TStartOps[] = {  
        {ZRINIT,&QTermZmodem::GotRinit,1,1,TStart},
        {ZCHALLENGE,&QTermZmodem::AnswerChallenge,1,0,TStart},
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZNAK,&QTermZmodem::Ignore,0,0,TStart},
        {ZCOMMAND,&QTermZmodem::GotCommand,0,0,CommandData},
        {ZSTDERR,&QTermZmodem::GotStderr,0,0,StderrData},
        {99,&QTermZmodem::ZPF,0,0,TStart},
      } ;




            /* sent ZSINIT, waiting for response */
StateTable  QTermZmodem::TInitOps[] = {
        {ZACK,&QTermZmodem::RetDone,1,0,TInit},
        {ZNAK,&QTermZmodem::SendZSInit,1,0,TInit},
        {ZRINIT,&QTermZmodem::GotRinit,1,1,TInit},    /* redundant, but who cares */
        {ZCHALLENGE,&QTermZmodem::AnswerChallenge,1,0,TInit},
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZCOMMAND,&QTermZmodem::GotCommand,0,0,CommandData},
        {ZSTDERR,&QTermZmodem::GotStderr,0,0,StderrData},
        {99,&QTermZmodem::ZPF,0,0,TInit},
      } ;



            /* sent ZFILE, waiting for response */
StateTable  QTermZmodem::FileWaitOps[] = {
        {ZRPOS,&QTermZmodem::SendFileData,1,1,Sending},
        {ZSKIP,&QTermZmodem::SkipFile,1,0,TStart},
        {ZCRC,&QTermZmodem::SendFileCrc,1,0,FileWait},
        {ZNAK,&QTermZmodem::sendFilename,1,0,FileWait},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZCHALLENGE,&QTermZmodem::AnswerChallenge,1,0,FileWait},
        {ZCOMMAND,&QTermZmodem::GotCommand,0,0,CommandData},
        {ZSTDERR,&QTermZmodem::GotStderr,0,0,StderrData},
        {ZACK,&QTermZmodem::SendFileData,1,0,Sending}, // receiver always sends ZACK back
        {99,&QTermZmodem::ZPF,0,0,FileWait},
      } ;


            /* sent file CRC, waiting for response */
StateTable  QTermZmodem::CrcWaitOps[] = {
        {ZRPOS,&QTermZmodem::SendFileData,1,0,Sending},
        {ZSKIP,&QTermZmodem::SkipFile,1,0,FileWait},
        {ZNAK,&QTermZmodem::SendFileCrc,1,0,CrcWait},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZCRC,&QTermZmodem::SendFileCrc,0,0,CrcWait},
        {ZCHALLENGE,&QTermZmodem::AnswerChallenge,0,0,CrcWait},
        {ZCOMMAND,&QTermZmodem::GotCommand,0,0,CommandData},
        {ZSTDERR,&QTermZmodem::GotStderr,0,0,StderrData},
        {99,&QTermZmodem::ZPF,0,0,CrcWait},
      } ;


            /* sending data, interruptable */
StateTable  QTermZmodem::SendingOps[] = {
        {ZACK,&QTermZmodem::GotSendAck,0,0,Sending},
        {ZRPOS,&QTermZmodem::GotSendPos,1,1,Sending},
        {ZSKIP,&QTermZmodem::SkipFile,1,1,FileWait},
        {ZNAK,&QTermZmodem::GotSendNak,1,1,Sending},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {99,&QTermZmodem::ZPF,0,0,SendWait},
      } ;

            /* sent data, need to send EOF */
StateTable  QTermZmodem::SendDoneOps[] = {
        {ZACK,&QTermZmodem::GotSendDoneAck,0,0,SendWait},
        {ZRPOS,&QTermZmodem::GotSendPos,1,1,Sending},
        {ZSKIP,&QTermZmodem::SkipFile,1,1,FileWait},
        {ZNAK,&QTermZmodem::GotSendNak,1,1,Sending},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {99,&QTermZmodem::ZPF,0,0,SendWait},
      } ;


            /* sending data, waiting for ACK */
StateTable  QTermZmodem::SendWaitOps[] = {
        {ZACK,&QTermZmodem::GotSendWaitAck,0,0,Sending},
        {ZRPOS,&QTermZmodem::GotSendPos,0,0,SendWait},
        {ZSKIP,&QTermZmodem::SkipFile,1,1,FileWait},
        {ZNAK,&QTermZmodem::GotSendNak,0,0,Sending},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {99,&QTermZmodem::ZPF,0,0,SendWait},
      } ;


            /* sent ZEOF, waiting for new RINIT */
StateTable  QTermZmodem::SendEofOps[] = {
        {ZRINIT,&QTermZmodem::SkipFile,1,0,TFinish},  /* successful completion */
        {ZACK,&QTermZmodem::Ignore,0,0,SendEof},      /* probably ACK from last packet */
        {ZRPOS,&QTermZmodem::GotSendPos,1,1,SendWait},
        {ZSKIP,&QTermZmodem::SkipFile,1,1,TStart},
        {ZNAK,&QTermZmodem::ResendEof,1,0,SendEof},
        {ZRINIT,&QTermZmodem::sendFilename,1,1,FileWait},   /* rcvr confused, retry file */
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {99,&QTermZmodem::ZPF,0,0,SendEof},
      } ;

StateTable  QTermZmodem::TFinishOps[] = {
        {ZFIN,&QTermZmodem::OverAndOut,1,1,Done},
        {ZNAK,&QTermZmodem::ZmodemTFinish,1,1,TFinish},
        {ZRINIT,&QTermZmodem::ZmodemTFinish,1,1,TFinish},
        {ZABORT,&QTermZmodem::GotAbort,1,1,TFinish},
        {ZFERR,&QTermZmodem::GotAbort,1,1,TFinish},
        {99,&QTermZmodem::ZPF,0,0,TFinish},
      } ;



StateTable  QTermZmodem::CommandDataOps[] = {
        {99,&QTermZmodem::ZPF,0,0,CommandData},
      } ;

StateTable  QTermZmodem::CommandWaitOps[] = {
        {99,&QTermZmodem::ZPF,0,0,CommandWait},
      } ;

StateTable  QTermZmodem::StderrDataOps[] = {
        {99,&QTermZmodem::ZPF,0,0,StderrData},
      } ;

StateTable  QTermZmodem::DoneOps[] = {
        {99,&QTermZmodem::ZPF,0,0,Done},
      } ;

StateTable  * QTermZmodem::tables[] = {
        QTermZmodem::RStartOps,
        QTermZmodem::RSinitWaitOps,
        QTermZmodem::RFileNameOps,
        QTermZmodem::RCrcOps,
        QTermZmodem::RFileOps,
        QTermZmodem::RDataOps,
        QTermZmodem::RDataOps,      /* RDataErr is the same as RData */
        QTermZmodem::RFinishOps,

        QTermZmodem::TStartOps,
        QTermZmodem::TInitOps,
        QTermZmodem::FileWaitOps,
        QTermZmodem::CrcWaitOps,
        QTermZmodem::SendingOps,
        QTermZmodem::SendWaitOps,
        QTermZmodem::SendDoneOps,
        QTermZmodem::SendEofOps,
        QTermZmodem::TFinishOps,

        QTermZmodem::CommandDataOps,
        QTermZmodem::CommandWaitOps,
        QTermZmodem::StderrDataOps,
        QTermZmodem::DoneOps,
      } ;

const char *      QTermZmodem::hdrnames[] = {
        "ZRQINIT",
        "ZRINIT",
        "ZSINIT",
        "ZACK",
        "ZFILE",
        "ZSKIP",
        "ZNAK",
        "ZABORT",
        "ZFIN",
        "ZRPOS",
        "ZDATA",
        "ZEOF",
        "ZFERR",
        "ZCRC",
        "ZCHALLENGE",
        "ZCOMPL",
        "ZCAN",
        "ZFREECNT",
        "ZCOMMAND",
        "ZSTDERR",
      } ;


#endif






QTermZmodem::QTermZmodem(QObject *netinterface, int type)
{

//now set network interface Telnet

      connectionType=type;

// Get network interface
      if(connectionType == 0)
      {
            // telnet
            m_pTelnet= (QTermTelnet *)netinterface;
      }
      else if(connectionType ==1)
      {
            //ssh
            m_pTelnet= (QTermTelnet *)netinterface;

      }


      sending = false;

      transferstate = notransfer;

      zmodemTimer= new QTimer(this);
      connect(zmodemTimer, SIGNAL(timeout()), this, SLOT(ZmodemTimeout()));

#ifdef QTERM_DEBUG
      zmodemlogfile = fopen("zmodem.log","w+");
      fprintf(zmodemlogfile, "%s", "\n================================\n");
      fclose(zmodemlogfile);
#endif


//init struct INFO
      info.zrinitflags = CANFDX|CANOVIO|CANBRK|CANFC32 ;
      info.zsinitflags = 0 ;
      info.attn = NULL ;
      info.bufsize = 0 ;      /* full streaming */
      info.buffer = NULL;

      zerrno =0;
      lastPullByte = 0;

      ZmodemReset(&info);
// other init function not complete 

}

QTermZmodem::~QTermZmodem()
{
}

int QTermZmodem::ZmodemTInit(ZModem *info)
{
      int   err ;
      int   i ;

      info->state = TStart ;
      info->Protocol = ZMODEM ;
      info->crc32 = 0 ;
      info->packetCount = 0 ;
      info->errCount = 0 ;
      info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
      info->InputState = Idle ;
      info->canCount = info->chrCount = 0 ;
      info->windowCount = 0 ;
      info->filename = NULL ;
      info->bufsize = 0 ;
      info->interrupt = 0 ;
      info->waitflag = 0 ;
      
//    if( info->packetsize <= 0 )
        info->packetsize = 8096 ;
      info->windowsize = 0;
      /* we won't be receiving much data, pick a reasonable buffer
       * size (largest packet will do)
       */

      i = info->packetsize*2 ;

      if( info->buffer != NULL ) {
          free(info->buffer) ;
          info->buffer = NULL ;
      }                                           //since in the constructor function buffer is malloc
      
      if( i < 1024 ) i = 1024 ;
      info->buffer = (uchar *)malloc(i) ;

      ZIFlush(info) ;

      /* optional: send "rz\r" to remote end */
      if( DoInitRZ ) {
        if( (err = ZXmitStr((uchar *)"rz\r", 3, info)) )
          return err ;
      }

      if( (err = ZXmitHdr(ZRQINIT, ZHEX, zeros, info)) ) /* nudge receiver */
        return err ;

      info->timeout = 60 ;
    
      QTermConfig conf(fileCfg);
      QString path = QString::fromLocal8Bit(conf.getItemValue("global","openfiledialog"));
      strFileList = QFileDialog::getOpenFileNames("All files(*)", path);
      if(strFileList.count()!=0)
      {
            QStringList::Iterator itFile = strFileList.begin();
            QFileInfo fi(*itFile);
        conf.setItemValue("global","openfiledialog", fi.dirPath(true) );
            conf.save(fileCfg);     
      }
      
      zmodemlog("ZmodemTInit[%s]: sent ZRQINIT\n", sname(info)) ;

      return 0 ;
}


int QTermZmodem::ZmodemTFile(char *file, char *rfile, uint f0, uint f1, uint f2, uint f3, 
                                                      int filesRem, int bytesRem, ZModem *info) 
{
      if( file == NULL || (info->file = fopen(file, "rb")) == NULL )
        return ZmErrCantOpen ;

      info->fileEof = 0 ;
      info->filename = file ;
      if (rfile != NULL)
            info->rfilename = rfile;
      else
            info->rfilename = strdup("noname");
      info->filesRem = filesRem ;
      info->bytesRem = bytesRem ;
      info->fileFlags[3] = f0 ;
      info->fileFlags[2] = f1 ;
      info->fileFlags[1] = f2 ;
      info->fileFlags[0] = f3 ;
      info->offset = info->lastOffset = 0 ;
      info->len = info->date = info->fileType = info->mode = 0 ;
      if( info->filename != NULL )
      {
        struct stat buf ;
        if( stat(info->filename, &buf) == 0 ) {
          info->len = buf.st_size ;
          info->date = buf.st_mtime ;
          info->fileType = 0 ;
          info->mode = (buf.st_mode&0777)|0100000 ;
        }
      }

      if( info->Protocol == XMODEM )
        return YSendData(info) ;

      if( info->Protocol == YMODEM )
        return YSendFilename(info) ;

      info->state = FileWait ;
      ZStatus(FileBegin, info->bytesRem, info->rfilename);
      zmodemlog("ZmodemTFile[%s]: send ZFILE(%s)\n",
            sname(info), info->rfilename) ;
      return sendFilename(info) ;
}


int QTermZmodem::ZmodemTFinish(ZModem *info)
{
      int i;
      if( info->Protocol == XMODEM )
        return ZmDone ;

      if( info->Protocol == YMODEM )
        return YSendFin(info) ;

      info->state = TFinish ;
      if( info->buffer != NULL ) {
          free(info->buffer) ;
          info->buffer = NULL ;
      }
      zmodemlog("ZmodemTFinish[%s]: send ZFIN\n", sname(info)) ;
      i=ZXmitHdr(ZFIN, ZHEX, zeros, info) ;

      return i;
}



int QTermZmodem::ZmodemAbort(ZModem *info)
{
      uchar canistr[] = {
        CAN,CAN,CAN,CAN,CAN,CAN,CAN,CAN,8,8,8,8,8,8,8,8,8,8
      } ;
      info->state = Done ;
      ZIFlush(info) ;
      ZOFlush(info) ;

      transferstate = transferstop;  // transfer complete
      ZmodemReset(info);  //Tranfer complete, zmodem return to receive state

      zmodemlog("ZmodemAbort[%s]: send CAN\n", sname(info));
      return ZXmitStr(canistr, sizeof(canistr), info) ;
}


int QTermZmodem::ZmodemRInit(ZModem *info)
{
      info->packetCount = 0 ;
      info->offset = 0 ;
      info->errCount = 0 ;
      info->escCtrl = info->escHibit = info->atSign = info->escape = 0 ;
      info->InputState = Idle ;
      info->canCount = info->chrCount = 0 ;
      info->filename = NULL ;
      info->interrupt = 0 ;
      info->waitflag = 0 ;
      info->attn = NULL ;
      info->file = NULL ;

      if( info->buffer != NULL ) {
          free(info->buffer) ;
          info->buffer = NULL ;
      }
      
      info->buffer = (uchar *)malloc(1024) ;

      info->state = RStart ;
      info->timeoutCount = 0 ;

      ZIFlush(info) ;

      /* Don't send ZRINIT right away, there might be a ZRQINIT in
       * the input buffer.  Instead, set timeout to zero and return.
       * This will allow ZmodemRcv() to check the input stream first.
       * If nothing found, a ZRINIT will be sent immediately.
       */
      info->timeout = 0 ;

      zmodemlog("ZmodemRInit[%s]: flush input, new state = RStart\n",
          sname(info)) ;

      return 0 ;
}




int QTermZmodem::ZmodemRcv(uchar *str, int len, ZModem *info)
{
      register uchar    c ;
      int   err ;

      zmodemlog("zmodemRcv called");
      
      info->rcvlen = len ;
      
      while( --info->rcvlen >= 0 )
      {
        c = *str++ ;

        if( c == CAN ) {
          if( ++info->canCount >= 5 ) {
            ZStatus(RmtCancel, 0, NULL) ;
            return ZmErrCancel ;
          }
        }
        else
          info->canCount = 0 ;

        if( info->InputState == Ysend ) {
          if( (err = YsendChar(c, info)) )
            return err ;
        }
        else if( info->InputState == Yrcv ) {
          if( (err = YrcvChar(c, info)) )
            return err ;
        }

        else if( c != XON && c != XOFF )
        {
          /* now look at what we have */
          //fprintf(stderr, "%02x, ", c);
          switch( info->InputState )
          {
            case Idle:
            if( (err = IdleChar(c, info)) )
              return err ;
            break ;

            case Inhdr:
            if( (err = HdrChar(c, info)) )
              return err ;
            break ;

            case Indata:
            if( (err = DataChar(c, info)) )
              return err ;
            break ;

            case Finish:
            if( (err = FinishChar(c, info)) )
              return err ;
            break ;

            default:
            break ;
          }
        }
      }

      return 0 ;
}




int QTermZmodem::ZmodemTimeout(/*ZModem *info*/)
{
            /* timed out while waiting for input */

      ++info.timeoutCount ;

      zmodemlog("timeout %d [%s]\n", info.timeoutCount, sname(&info) ) ;

      switch( info.state ) {
        /* receive */
        case RStart:          /* waiting for INIT frame from other end */
          if( info.timeoutCount > 4 )
            return YmodemRInit(&info) ;

        case RSinitWait:
        case RFileName:
          if( info.timeout > 0 )
            ZStatus(SndTimeout, info.timeoutCount, NULL) ;
          if( info.timeoutCount > 4 )
            return ZmErrRcvTo ;
          info.state = RStart ;
          return SendRinit(&info) ;

        case RCrc:
        case RFile:
        case RData:
          ZStatus(SndTimeout, info.timeoutCount, NULL) ;
          if( info.timeoutCount > 2 ) {
            info.timeoutCount = 0 ;
            info.state = RStart ;
            return SendRinit(&info) ;
          }
          return info.state == RCrc ? ResendCrcReq(&info) : ResendRpos(&info) ;

        case RFinish:
          ZStatus(SndTimeout, info.timeoutCount, NULL) ;
          return ZmDone ;

        case YRStart:
        case YRDataWait:
        case YRData:
        case YREOF:
          return YrcvTimeout(&info) ;

        /* transmit */
        case TStart:          /* waiting for INIT frame from other end */
        case TInit:           /* sent INIT, waiting for ZACK */
        case FileWait:  /* sent file header, waiting for ZRPOS */
        case CrcWait:         /* sent file crc, waiting for ZRPOS */
        case SendWait:  /* waiting for ZACK */
        case SendEof:         /* sent EOF, waiting for ZACK */
        case TFinish:         /* sent ZFIN, waiting for ZFIN */
        case YTStart:
        case YTFile:
        case YTDataWait:
        case YTData:
        case YTEOF:
        case YTFin:
          ZStatus(RcvTimeout,0,NULL) ;
          return ZmErrRcvTo ;

        case Sending:         /* sending data subpackets, ready for int */
          return SendMoreFileData(&info) ;

        /* general */
        case CommandData:     /* waiting for command data */
        case StderrData:      /* waiting for stderr data */
          return ZmErrSndTo ;
        case CommandWait:     /* waiting for command to execute */
          return ZmErrCmdTo ;
        case Done:
          return ZmDone ;
        default:
          return 0 ;
      }
}



int QTermZmodem::ZmodemAttention(ZModem *info)
{
            /* attention received from remote end */
      if( info->state == Sending ) {
        ZOFlush(info) ;
        info->interrupt = 1 ;
      }
      return 0 ;
}

int QTermZmodem::YmodemTInit(ZModem *info)
{
      info->state = YTStart ;
      info->Protocol = YMODEM ;
      info->errCount = 0 ;
      info->InputState = Ysend ;
      info->canCount = info->chrCount = 0 ;
      info->windowCount = 0 ;
      info->filename = NULL ;

      if( info->packetsize != 1024 )
        info->packetsize = 128 ;

      info->buffer = (uchar *)malloc(1024) ;

      ZIFlush(info) ;
      ZFlowControl(0, info) ;

      info->timeout = 60 ;

      return 0 ;
}


int QTermZmodem::XmodemTInit(ZModem *info)
{
      (void) YmodemTInit(info) ;
      info->Protocol = XMODEM ;
      return 0 ;
}


int QTermZmodem::YmodemRInit(ZModem *info)
{
      info->errCount = 0 ;
      info->InputState = Yrcv ;
      info->canCount = info->chrCount = 0 ;
      info->noiseCount = 0 ;
      info->filename = NULL ;
      info->file = NULL ;

      if( info->buffer == NULL )
        info->buffer = (uchar *)malloc(1024) ;

      info->state = YRStart ;
      info->packetCount = -1 ;
      info->timeoutCount = 0 ;
      info->timeout = 10 ;
      info->offset = 0 ;

      ZIFlush(info) ;

      return ZXmitStr((uchar *)"C", 1, info) ;
}


int QTermZmodem::XmodemRInit(ZModem *info)
{
#if 0
      int   err ;

      state = Start ;
      protocol = prot ;
      ymodem = prot == Ymodem || prot == YmodemG ;

      if( ymodem )
        strcpy(xmDefPath, file) ;
      else
        strcpy(xmFilename, file) ;

      eotCount = errorCount = errorCount2 = 0 ;

      if( err=XmodemRStart() )
        return err ;

      state = Init ;
      packetId = ymodem ? 255 : 0 ;
      packetCount = 0 ;

      pktHdrLen = protocol == Xmodem ? 3 : 4 ;
#endif
      return 0 ;
}


ulong QTermZmodem::FileCrc(char *name)
{
      ulong crc ;
      FILE  *ifile = fopen(name, "rb") ;
      int   i ;

      if( ifile == NULL )     /* shouldn't happen, since we did access(2) */
        return 0 ;

      crc = 0xffffffff ;

      while( (i=fgetc(ifile)) != EOF )
        crc = UPDC32(i, crc) ;

      fclose(ifile) ;
      return ~crc ;
}



const char * QTermZmodem::sname(ZModem *info)
{
            return sname2(info->state) ;
}

const char * QTermZmodem::sname2(ZMState state)
{
      const char *names[] = {
        "RStart", "RSinitWait", "RFileName", "RCrc", "RFile", "RData",
        "RDataErr", "RFinish", "TStart", "TInit", "FileWait", "CrcWait",
        "Sending", "SendWait", "SendDone", "SendEof", "TFinish",
        "CommandData", "CommandWait", "StderrData", "Done", "YTStart",
        "YTFile", "YTDataWait", "YTData", "YTEOF", "YTFin", "YRStart",
        "YRDataWait", "YRData", "YREOF"} ;

      return names[(int)state] ;

}


int QTermZmodem::ZXmitChr(uchar c, ZModem *info)
{
      // to be completed
      return 0;
}

int QTermZmodem::ZXmitStr(uchar *str, int len, ZModem *info)
{
      //to be completed
      m_pTelnet->write((const char*)str,(uint)len);
      return 0;
}

void QTermZmodem::ZIFlush(ZModem *info)
{
      //to be completed
}

void QTermZmodem::ZOFlush(ZModem *info)
{
      //to be completed
}

int QTermZmodem::ZAttn(ZModem *info)
{
      //to be completed
      return 0;
}

void QTermZmodem::ZStatus(int type, int value, const char *status)
{
      emit ZmodemState(type, value, status);
      switch(type)
      {
            case  RcvByteCount:
                        qDebug("received %d bytes", value);
                        break;
            case  SndByteCount:
                        qDebug("sent %lx bytes", value);
                        break;
            case  RcvTimeout:
                        /* receiver did not respond, aborting */
                        qDebug("time out!");
                        break;
            case  SndTimeout:
                        /* value is # of consecutive send timeouts */
                        qDebug("time out after trying %d times", value);
                        break;
            case  RmtCancel:
                        /* remote end has cancelled */
                        qDebug("canceled by remote peer");
                        break;
            case  ProtocolErr:
                        /* protocol error has occurred, val=hdr */
                        qDebug("unhandled header %d at state %s", value, status);
                        break;
            case  RemoteMessage:
                        /* message from remote end */
                        qDebug("msg from remote peer: %s",status);
                        break;
            case  DataErr:    
                        /* data error, val=error count */
                        qDebug("data errors %d", value);
                        break;
            case  FileErr:
                        /* error writing file, val=errno */
                        qDebug("falied to write file");
                        break;
            case  FileBegin:
                        /* file transfer begins, str=name */
                        qDebug("starting file %s", status);
                        break;
            case  FileEnd:
                        /* file transfer ends, str=name */
                        qDebug("finishing file %s", status);
                        break;
            case  FileSkip:
                        /* file being skipped, str=name */
                        qDebug("skipping file %s", status);
                        break;
      }
}

FILE * QTermZmodem::ZOpenFile(char *name, ulong crc, ZModem *info)
{
      //to be complete
      FILE *rval;
      int apnd=0;
      QString str = ((QTermFrame *)qApp->mainWidget())->m_pref.strZmPath+G2U(name); // lazy, should use bbs2unicode
      rval = fopen(str.local8Bit(), apnd ? "ab" : "wb") ;

      if( rval == NULL )
        perror(name) ;

      return rval ;
}

int QTermZmodem::ZXmitHdr(int type, int format, uchar data[4], ZModem *info)
{
      if( format == ZBIN && info->crc32 )
        format = ZBIN32 ;

      switch( format ) {
        case ZHEX:
          return ZXmitHdrHex(type, data, info) ;

        case ZBIN:
          return ZXmitHdrBin(type, data, info) ;

        case ZBIN32:
          return ZXmitHdrBin32(type, data, info) ;

        default:
          return 0 ;
      }
}


int QTermZmodem::ZXmitHdrHex(int type, uchar data[4], ZModem *info)
{
      uchar buffer[128] ;
      uchar *ptr = buffer ;
      uint  crc ;
      int   i ;

      zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
          hdrnames[type], data[0], data[1], data[2], data[3],
          ZDec4(data)) ;

      *ptr++ = ZPAD ;
      *ptr++ = ZPAD ;
      *ptr++ = ZDLE ;
      *ptr++ = ZHEX ;

      ptr = putHex(ptr, type) ; crc = updcrc(type, 0) ;
      for( i=4; --i >= 0; ++data ) {
        ptr = putHex(ptr, *data) ;
        crc = updcrc(*data, crc) ;
      }
      crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
      ptr = putHex(ptr, (crc>>8)&0xff) ;
      ptr = putHex(ptr, crc&0xff) ;
      *ptr++ = '\r' ;
      *ptr++ = '\n' ;
      if( type != ZACK  &&  type != ZFIN )
        *ptr++ = XON ;

      return ZXmitStr(buffer, ptr-buffer, info) ;
}


int QTermZmodem::ZXmitHdrBin(int type, uchar data[4], ZModem *info)
{
      uchar buffer[128] ;
      uchar *ptr = buffer ;
      uint  crc ;
      int   len ;

      zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
          hdrnames[type], data[0], data[1], data[2], data[3],
          ZDec4(data)) ;

      *ptr++ = ZPAD ;
      *ptr++ = ZDLE ;
      *ptr++ = ZBIN ;

      ptr = putZdle(ptr, type, info) ; crc = updcrc(type, 0) ;
      for( len=4; --len >= 0; ++data ) {
        ptr = putZdle(ptr, *data, info) ;
        crc = updcrc(*data, crc) ;
      }
      crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
      ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
      ptr = putZdle(ptr, crc&0xff, info) ;

      len = ptr-buffer ;
      return ZXmitStr(buffer, len, info) ;
}

int QTermZmodem::ZXmitHdrBin32(int type, uchar data[4], ZModem *info)
{
      uchar buffer[128] ;
      uchar *ptr = buffer ;
      ulong crc ;
      int   len ;

      zmodemlog("sending  %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
          hdrnames[type], data[0], data[1], data[2], data[3],
          ZDec4(data)) ;

      *ptr++ = ZPAD ;
      *ptr++ = ZDLE ;
      *ptr++ = ZBIN32 ;
      ptr = putZdle(ptr, type, info) ; crc = UPDC32(type, 0xffffffffL) ;
      for( len=4; --len >= 0; ++data ) {
        ptr = putZdle(ptr, *data, info) ;
        crc = UPDC32(*data, crc) ;
      }
      crc = ~crc ;
      for(len=4; --len >= 0; crc >>= 8)
        ptr = putZdle(ptr, crc&0xff, info) ;

      len = ptr-buffer ;
      return ZXmitStr(buffer, len, info) ;
}


uchar * QTermZmodem::putZdle( uchar *ptr, uchar c, ZModem *info )
{
      uchar c2 = c & 0177 ;

      if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
          c2 == 0177  ||  (c2 == 015 && connectionType==0/*&& info->atSign*/)  ||
      #ifdef      COMMENT
          c2 == 035  ||  (c2 == '~' && info->lastCR)  ||
      #endif      /* COMMENT */
          c2 == 035  ||
          (c2 < 040 && info->escCtrl) )
      {
        *ptr++ = ZDLE ;
        if( c == 0177 )
          *ptr = ZRUB0 ;
        else if( c == 0377 )
          *ptr = ZRUB1 ;
        else
          *ptr = c^0100 ;
      }
      else
        *ptr = c ;

      info->atSign = c2 == '@' ;
      info->lastCR = c2 == '\r' ;

      return ++ptr ;
}


uchar * QTermZmodem::ZEnc4(ulong n)
{
      static      uchar buf[4] ;
      buf[0] = n&0xff ; n >>= 8 ;
      buf[1] = n&0xff ; n >>= 8 ;
      buf[2] = n&0xff ; n >>= 8 ;
      buf[3] = n&0xff ;
      return buf ;
}

ulong QTermZmodem::ZDec4(uchar buf[4])
{
      return buf[0] | (buf[1]<<8) | (buf[2]<<16) | (buf[3]<<24) ;
}




int QTermZmodem::YrcvChar( char c, register ZModem *info )
{
            int   err ;

      if( info->canCount >= 2 ) {
        ZStatus(RmtCancel, 0, NULL) ;
        return ZmErrCancel ;
      }

      switch( info->state ) {
        case YREOF:
          if( c == EOT ) {
            ZCloseFile(info) ; info->file = NULL ;
            ZStatus(FileEnd, 0, info->filename) ;
            if( info->filename != NULL )
            free(info->filename) ;
            if( (err = acceptPacket(info)) != 0 )
            return err ;
            info->packetCount = -1 ;
            info->offset = 0 ;
            info->state = YRStart ;
            return ZXmitStr((uchar *)"C", 1, info) ;
          }
          /* else, drop through */

        case YRStart:
        case YRDataWait:
          switch( c ) {
            case SOH:
            case STX:
            info->pktLen = c == SOH ? (128+4) : (1024+4) ;
            info->state = YRData ;
            info->chrCount = 0 ;
            info->timeout = 1 ;
            info->noiseCount = 0 ;
            info->crc = 0 ;
            break ;

            case EOT:
            /* ignore first EOT to protect against false eot */
            info->state = YREOF ;
            return rejectPacket(info) ;

            default:
            if( ++info->noiseCount > 135 )
              return ZXmitStr(NakStr, 1, info) ;
            break ;
          }
          break ;

        case YRData:
          info->buffer[info->chrCount++] = c ;
          if( info->chrCount >= info->pktLen )
            return ProcessPacket(info) ;
          break ;

        default:
          break ;
      }

      return 0 ;
}

int QTermZmodem::YrcvTimeout(  ZModem *info )
{
            switch( info->state )
      {
        case YRStart:
          if( info->timeoutCount >= 10 ) {
            (void) ZXmitStr(CanStr, 2, info) ;
            return ZmErrInitTo ;
          }
          return ZXmitStr((uchar *)"C", 1, info) ;

        case YRDataWait:
        case YREOF:
        case YRData:
          if( info->timeoutCount >= 10 ) {
            (void) ZXmitStr(CanStr, 2, info) ;
            return ZmErrRcvTo ;
          }
          return ZXmitStr(NakStr, 1, info) ;
        default: return 0 ;
      }
}


void QTermZmodem::ZIdleStr(uchar *buffer, int len, ZModem *info)
{
      //to be completed
}


int QTermZmodem::FinishChar( char c, register ZModem *info )
{
      if( c == 'O' ) {
        if( ++info->chrCount >= 2 )
          return ZmDone ;
      }
      else
        info->chrCount = 0 ;
      return 0 ;
}



int QTermZmodem::DataChar( uchar c, register ZModem *info )
{
      if( c == ZDLE ) {
        info->escape = 1 ;
        return 0 ;
      }

      if( info->escape ) {
        info->escape = 0 ;
        switch( c ) {
          case ZCRCE:
          case ZCRCG:
          case ZCRCQ:
          case ZCRCW:
            qDebug("Start check crc: %x", c);
            info->PacketType = c ;
            info->crcCount = (info->DataType == ZBIN32) ? 4 : 2 ;
            if( info->DataType == ZBIN )
              info->crc = updcrc(c, info->crc) ;
            else
              info->crc = UPDC32(c, info->crc) ;
            return 0 ;
          case ZRUB0: c = 0177 ; break ;
          case ZRUB1: c = 0377 ; break ;
          default: c ^= 0100 ; break ;
        }
      }
if(connectionType==0)
      if (lastPullByte == 0x0d && c == 0x00) {
            lastPullByte = 0;
            return 0;
      }
      else if (lastPullByte == 0xff && c == 0xff) {
            lastPullByte = 0;
            return 0;
      }
      
      lastPullByte = c;

      switch( info->DataType ) {
        /* TODO: are hex data packets ever used? */

        case ZBIN:
          qDebug("we got zbin: %04x", info->crc);
          info->crc = updcrc(c, info->crc) ;
          if( info->crcCount == 0 )
            info->buffer[info->chrCount++] = c ;
          else if( --info->crcCount == 0 ) {
            return ZDataReceived(info, (info->crc&0xffff) == 0) ;
          }
          break ;


        case ZBIN32:
          info->crc = UPDC32(c, info->crc) ;
          if( info->crcCount == 0 )
            info->buffer[info->chrCount++] = c ;
          else if( --info->crcCount == 0 ) {
          qDebug("we got zbin32: %08x", info->crc);
            return ZDataReceived(info, info->crc == 0xdebb20e3) ;
          }
          break ;
      }
      return 0 ;
}


int QTermZmodem::HdrChar( uchar c, register ZModem *info )
{
      int   i ;
      int   crc=0 ;

      if( c == ZDLE ) {
        info->escape = 1 ;
        return 0 ;
      }

      if( info->escape ) {
        info->escape = 0 ;
        switch( c ) {
          case ZRUB0: c = 0177 ; break ;
          case ZRUB1: c = 0377 ; break ;
          default: c ^= 0100 ; break ;
        }
      }

      if( info->chrCount == 0 ) {   /* waiting for format */
        switch( c ) {
          case ZHEX:
          case ZBIN:
          case ZBIN32:
            info->DataType = c ;
            info->chrCount = 1 ;
            info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
            memset(info->hdrData,0,sizeof(info->hdrData)) ;
            break ;
          default:
            info->InputState = Idle ;
            info->chrCount = 0 ;
            return ZXmitHdrHex(ZNAK, zeros, info) ;
        }
        return 0 ;
      }


      switch( info->DataType ) {
        /* hex header is 14 hex digits, cr, lf.  Optional xon is ignored */
        case ZHEX:
          if( info->chrCount <= 14  &&   !isxdigit(c) ) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            return ZXmitHdrHex(ZNAK, zeros, info) ;
          }

          if( info->chrCount <= 14 ) {
            i = (info->chrCount-1)/2 ;
            info->hdrData[i] = rcvHex(info->hdrData[i], c) ;
          }

          if( info->chrCount == 16 ) {
            crc = 0 ;
            for(i=0; i<7; ++i)
            crc = updcrc(info->hdrData[i], crc) ;
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if( (crc&0xffff) != 0 )
            return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
            return ZProtocol(info) ;
          }
          else
            ++info->chrCount ;
          break ;


        case ZBIN:
          /* binary header is type, 4 bytes data, 2 bytes CRC */
          info->hdrData[info->chrCount-1] = c ;
          info->crc = updcrc(c, info->crc) ;
          if( ++info->chrCount > 7 ) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if( (crc&0xffff) != 0 )
            return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
            return ZProtocol(info) ;
          }
          break ;


        case ZBIN32:
          /* binary32 header is type, 4 bytes data, 4 bytes CRC */
          info->hdrData[info->chrCount-1] = c ;
          info->crc = UPDC32(c, info->crc) ;
          if( ++info->chrCount > 9 ) {
            info->InputState = Idle ;
            info->chrCount = 0 ;
            if( info->crc != 0xdebb20e3 ) /* see note below */
            return ZXmitHdrHex(ZNAK, zeros, info) ;
            else
            return ZProtocol(info) ;
          }
          break ;
      }
      return 0 ;
}




int QTermZmodem::IdleChar(uchar c, register ZModem *info)
{
      if( info->chrCount == 0 )
      {
        if( c == ZPAD )
          ++info->chrCount ;
        else if( info->state == Sending  &&  ++info->noiseCount > MaxNoise )
          info->waitflag = 1 ;
        else if( info->state == TStart && (c == 'C' || c == 'G' || c == NAK) )
        {
          /* switch to ymodem */
          info->state = YTStart ;
          info->InputState = Ysend ;
          info->Protocol = YMODEM ;
          return YsendChar(c, info) ;
        }
        else
          ZIdleStr(&c, 1, info) ;
      }

      else
      {
        switch( c ) {
          case ZPAD:
            ++info->chrCount ;
            break ;
          case ZDLE:
            info->InputState = Inhdr ;
            info->chrCount=0 ;
            break ;
          default:
            while( --info->chrCount >= 0 )
            ZIdleStr((uchar *)"*", 1, info) ;
            info->chrCount = 0 ;
            break ;
        }
      }
      return 0 ;
}



int QTermZmodem::YsendChar( char c,  ZModem *info )
{
      int   err ;

      if( info->canCount >= 2 ) {
        ZStatus(RmtCancel, 0, NULL) ;
        return ZmErrCancel ;
      }

      switch( info->state ) {
        case YTStart:         /* wait for 'G', 'C' or NAK */
          switch( c ) {
            case 'G':         /* streaming YModem */
            case 'C':         /* CRC YModem */
            case NAK:         /* checksum YModem */
            info->PacketType = c ;
            return ZmDone ;
            default:
            return 0 ;
          }

        case YTFile:          /* sent filename, waiting for ACK or NAK */
          switch( c ) {
            case NAK:         /* resend */
            case 'C':
            case 'G':
            ZStatus(DataErr, ++info->errCount, NULL) ;
            return YSendFilename(info) ;
            case ACK:
            info->state = YTDataWait ;
            default:
            return 0 ;
          }

        case YTDataWait:      /* sent filename, waiting for G,C or NAK */
          switch( c ) {
            case NAK:
            case 'C':
            case 'G':
            info->chrCount = 0 ;
            if( info->PacketType == 'G' ) /* send it all at once */
            {
              while( info->state == YTData )
                if( (err = YSendData(info)) )
                  return err ;
              return 0 ;
            }
            else
              return YSendData(info) ;
            default:
            return 0 ;
          }

        case YTData:          /* sent data, waiting for ACK or NAK */
          switch( c ) {
            case 'C':
            case 'G':         /* protocol failure, resend filename */
            if( info->Protocol == YMODEM ) {
              ZStatus(DataErr, ++info->errCount, NULL) ;
              info->state = YTFile ;
              rewind(info->file) ;
              return YSendFilename(info) ;
            }
            /* else XModem, treat it like a NAK */
            case NAK:
            ZStatus(DataErr, ++info->errCount, NULL) ;
            return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
            case ACK:
            info->offset += info->ylen ;
            info->bufp += info->ylen ;
            info->chrCount -= info->ylen ;
            ZStatus(SndByteCount, info->offset, NULL) ;
            return YSendData(info) ;
            default:
            return 0 ;
          }

        case YTEOF:           /* sent EOF, waiting for ACK or NAK */
          switch( c ) {
            case NAK:
            return ZXmitStr(eotstr, 1, info) ;
            case ACK:
            info->state = info->Protocol == YMODEM ? YTStart : Done ;
            return ZmDone ;
            default:
            return 0 ;
          }

        case YTFin:           /* sent Fin, waiting for ACK or NAK */
          switch( c ) {
            case NAK:
            return YSendFin(info) ;
            case ACK:
            return ZmDone ;
            default:
            return 0 ;
          }
        default:
          return 0 ;
      }
}



int QTermZmodem::ZProtocol( register ZModem *info )
{
    register StateTable *table ;

      zmodemlog("received %s: %2.2x %2.2x %2.2x %2.2x = %lx\n",
          hdrnames[info->hdrData[0]], info->hdrData[1],
          info->hdrData[2], info->hdrData[3], info->hdrData[4],
          ZDec4(info->hdrData+1)) ;

      /* Flags are sent in F3 F2 F1 F0 order.  Data is sent in P0 P1 P2 P3 */

      info->timeoutCount = 0 ;
      info->noiseCount = 0 ;
      
//    zmodemTimer->start(info->timeout*1000);

      table = tables[(int)info->state] ;
      while( table->type != 99  &&  table->type != info->hdrData[0] )
        ++table ;

      zmodemlog("  state %s => %s, iflush=%d, oflush=%d, call %x\n",
          sname(info), sname2(table->newstate), table->IFlush,
          table->OFlush, table->func) ;

      info->state = table->newstate ;

      if( table->IFlush ) {info->rcvlen = 0 ; ZIFlush(info) ;}
      if( table->OFlush ) ZOFlush(info) ;
      return (this->*(table->func))(info) ;
}


int QTermZmodem::ZDataReceived( register ZModem *info, int crcGood )
{
      switch( info->state ) {
        case RSinitWait: return GotSinitData(info, crcGood) ;
        case RFileName: return GotFileName(info, crcGood) ;
        case RData: return GotFileData(info, crcGood) ;
        case CommandData: return GotCommandData(info, crcGood) ;
        case StderrData: return GotStderrData(info, crcGood) ;
        default: return ZPF(info) ;
      }
}


int QTermZmodem::ZPF( ZModem *info )
{
      info->waitflag = 1 ;    /* pause any in-progress transmission */
      ZStatus(ProtocolErr, info->hdrData[0], sname(info)) ;
      return 0 ;
}


int QTermZmodem::Ignore( ZModem *info )
{
      return 0 ;
}


int QTermZmodem::AnswerChallenge( register ZModem *info )
{
      return ZXmitHdrHex(ZACK, info->hdrData+1, info) ;
}


int QTermZmodem::GotAbort( register ZModem *info )
{
      ZStatus(RmtCancel, 0, NULL) ;
      return ZXmitHdrHex(ZFIN, zeros, info) ;
}


int QTermZmodem::GotCancel( ZModem *info )
{
      return ZmErrCancel ;
}


int QTermZmodem::GotCommand( ZModem *info )
{
      uchar rbuf[4] ;
      /* TODO: add command capability */

      
      /// EPERM not defined????????
      
      rbuf[0] = EPERM ;
      rbuf[1] = rbuf[2] = rbuf[3] = 0 ;
      return ZXmitHdrHex(ZCOMPL, rbuf, info) ;
}

int QTermZmodem::GotStderr( register ZModem *info )
{
      info->InputState = Indata ;
      info->chrCount = 0 ;
      return 0 ;
}


int QTermZmodem::RetDone( ZModem *info )
{


      return ZmDone ;
}


int QTermZmodem::GotCommandData( register ZModem *info , int crcGood)
{
      /* TODO */
      return 0 ;
}


int QTermZmodem::GotStderrData( register ZModem *info, int crcGood )
{
      info->buffer[info->chrCount] = '\0' ;
      ZStatus(RemoteMessage, info->chrCount, (char *)info->buffer) ;
      return 0 ;
}


int QTermZmodem::GotFileName(  ZModem *info, int crcGood )
{
      info->InputState = Idle ;
      info->chrCount=0 ;

      if( !crcGood ) {
      zmodemlog("GotFileName[%s]: bad crc, send ZNAK\n", sname(info)) ;
        info->state = RStart ;
        return ZXmitHdrHex(ZNAK, zeros, info) ;
      }

      parseFileName(info, (char *)info->buffer) ;

      if( (info->f1 & ZMMASK) == ZMCRC ) {
        info->state = RCrc ;
        return ZXmitHdrHex(ZCRC, zeros, info) ;
      }

      zmodemlog("GotFileName[%s]: good crc, call requestFile\n",
            sname(info)) ;
      info->state = RFile ;
      return requestFile(info,0) ;
}


int QTermZmodem::ResendCrcReq(ZModem *info)
{
      zmodemlog("ResendCrcReq[%s]: send ZCRC\n", sname(info)) ;
      return ZXmitHdrHex(ZCRC, zeros, info) ;
}


int QTermZmodem::GotSinitData(  ZModem *info, int crcGood )
{
      info->InputState = Idle ;
      info->chrCount=0 ;
      info->state = RStart ;

      zmodemlog("GotSinitData[%s]: crcGood=%d\n", sname(info), crcGood) ;

      if( !crcGood )
        return ZXmitHdrHex(ZNAK, zeros, info) ;

      if( info->attn != NULL )
        free(info->attn) ;
      info->attn = NULL ;
      if( info->buffer[0] != '\0' )
        info->attn = strdup((char *)info->buffer) ;
      return ZXmitHdrHex(ZACK, ZEnc4(SerialNo), info) ;
}


int QTermZmodem::ResendRpos(  ZModem *info )
{
      zmodemlog("ResendRpos[%s]: send ZRPOS(%ld)\n",
        sname(info), info->offset) ;
      return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
}


int QTermZmodem::GotFileData(  ZModem *info, int crcGood )
{
      /* OK, now what?  Fushing the buffers and executing the
       * attn sequence has likely chopped off the input stream
       * mid-packet.  Now we switch to idle mode and treat all
       * incoming stuff like noise until we get a new valid
       * packet.
       */

      if( !crcGood ) {        /* oh bugger, an error. */
        zmodemlog(
          "GotFileData[%s]: bad crc, send ZRPOS(%ld), new state = RFile\n",
          sname(info), info->offset) ;
        ZStatus(DataErr, ++info->errCount, NULL) ;
        if( info->errCount > MaxErrs ) {
          ZmodemAbort(info) ;
          return ZmDataErr ;
        }
        else {
          info->state = RFile ;
          info->InputState = Idle ;
          info->chrCount=0 ;
          return fileError(info, ZRPOS, info->offset) ;
        }
      }

      if( ZWriteFile(info->buffer, info->chrCount, info->file, info) )
      {
        /* RED ALERT!  Could not write the file. */
        ZStatus(FileErr, zerrno, NULL) ;
        info->state = RFinish ;
        info->InputState = Idle ;
        info->chrCount=0 ;
        return fileError(info, ZFERR, zerrno) ;
      }

      zmodemlog("GotFileData[%s]: %ld.%d,",
        sname(info), info->offset, info->chrCount) ;
      info->offset += info->chrCount ;
      ZStatus(RcvByteCount, info->offset, NULL) ;

      /* if this was the last data subpacket, leave data mode */
      if( info->PacketType == ZCRCE  ||  info->PacketType == ZCRCW ) {
        zmodemlog("  ZCRCE|ZCRCW, new state RFile") ;
        info->state = RFile ;
        info->InputState = Idle ;
        info->chrCount=0 ;
      }
      else {
        zmodemlog("  call dataSetup") ;
        (void) dataSetup(info) ;
      }

      if( info->PacketType == ZCRCQ || info->PacketType == ZCRCW ) {
        zmodemlog(",  send ZACK\n") ;
        return ZXmitHdrHex(ZACK, ZEnc4(info->offset), info) ;
      }
      else
      zmodemlog("\n") ;

      return 0 ;
}

int QTermZmodem::SendRinit(  ZModem *info )
{
      uchar dbuf[4] ;



#ifdef      COMMENT
      if( info->timeoutCount >= 5 )
        /* TODO: switch to Ymodem */
#endif      /* COMMENT */

      transferstate = transferstart; //transfer would be active, it must be set to false when transfer complete or abort
      zmodemlog("SendRinit[%s]: send ZRINIT\n", sname(info)) ;

      info->timeout = ResponseTime ;
      dbuf[0] = info->bufsize&0xff ;
      dbuf[1] = (info->bufsize>>8)&0xff ;
      dbuf[2] = 0 ;
      dbuf[3] = info->zrinitflags ;
      return ZXmitHdrHex(ZRINIT, dbuf, info) ;
}


int QTermZmodem::SendMoreFileData(  ZModem *info )
{
      int   type ;
      int   qfull = 0 ;
      int   err ;
      int   len ;       /* max # chars to send this packet */
      long  pending ;   /* # of characters sent but not acknowledged */

      /* ZCRCE: CRC next, frame ends, header follows
       * ZCRCG: CRC next, frame continues nonstop
       * ZCRCQ: CRC next, send ZACK, frame continues nonstop
       * ZCRCW: CRC next, send ZACK, frame ends, header follows
       */

      if( info->interrupt ) {
        /* Bugger, receiver sent an interrupt.  Enter a wait state
         * and see what they want.  Next header *should* be ZRPOS.
         */
        info->state = SendWait ;
        info->timeout = 60 ;
        return 0 ;
      }

      /* Find out how many bytes we can transfer in the next packet */

      len = info->packetsize ;

      pending = info->offset - info->lastOffset ;

      if( info->windowsize != 0 && info->windowsize - pending <= len ){
        len = info->windowsize - pending ;
        qfull = 1 ;
      }
      if( info->bufsize != 0  &&  info->bufsize - pending <= len ) {
        len = info->bufsize - pending ;
        qfull = 1 ;
      }

      if( len == 0 ) {  /* window still full, keep waiting */
        info->state = SendWait ;
        info->timeout = 60 ;
        return 0 ;
      }


      /* OK, we can safely transmit 'len' bytes of data.  Start reading
       * file until buffer is full.
       */

      len -= 10 ; /* Pre-deduct 10 bytes for trailing CRC */


      /* find out what kind of packet to send */
      if( info->waitflag ) {
        type = ZCRCW ;
        info->waitflag = 0 ;
      }
#ifdef      COMMENT
      else if( info->fileEof )
        type = ZCRCE ;
#endif      /* COMMENT */
      else if( qfull )
        type = ZCRCW ;
      else
        switch( info->Streaming ) {
          case Full:
          case Segmented: type = ZCRCG ; break ;

          case StrWindow:
            if( (info->windowCount += len) < info->windowsize/4 )
            type = ZCRCG ;
            else {
            type = ZCRCQ ;
            info->windowCount = 0 ;
            }
            break ;

          default:
          case SlidingWindow: type = ZCRCQ ; break ;
        }


      {
        int crc32 = info->crc32 ;
         int c=0, c2, atSign=0 ;
         ulong crc ;
         uchar *ptr = info->buffer ;

        crc = crc32 ? 0xffffffff : 0 ;

        /* read characters from file and put into buffer until buffer is
         * full or file is exhausted
         */


        while( len > 0  && (c = getc(info->file)) != EOF )
        {
          if( !crc32 )
            crc = updcrc(c, crc) ;
          else
            crc = UPDC32(c, crc) ;

          /* zmodem protocol requires that CAN(ZDLE), DLE, XON, XOFF and
           * a CR following '@' be escaped.  In addition, I escape '^]'
           * to protect telnet, "<CR>~." to protect rlogin, and ESC for good
           * measure.
           */
          c2 = c & 0177 ;
          if( c == ZDLE || c2 == 020 || c2 == 021 || c2 == 023 ||
            c2 == 0177  ||  c2 == '\r'  ||  c2 == '\n'  ||  c2 == 033  ||
            c2 == 035  || (c2 < 040 && info->escCtrl) )
          {
            *ptr++ = ZDLE ;
            if( c == 0177 )
            *ptr = ZRUB0 ;
            else if( c == 0377 )
            *ptr = ZRUB1 ;
            else
            *ptr = c^0100 ;
            len -= 2 ;
          }
          else {
            *ptr = c ;
            --len ;
          }
          ++ptr ;

          atSign = c2 == '@' ;
          ++info->offset ;
        }

        /* if we've reached file end, a ZEOF header will follow.  If
         * there's room in the outgoing buffer for it, end the packet
         * with ZCRCE and append the ZEOF header.  If there isn't room,
         * we'll have to do a ZCRCW
         */
        if( (info->fileEof = (c == EOF)) ) {
          if( qfull  ||  (info->bufsize != 0 && len < 24) )
            type = ZCRCW ;
          else
            type = ZCRCE ;
        }

        *ptr++ = ZDLE ;
        if( !crc32 )
          crc = updcrc(type, crc) ;
        else
          crc = UPDC32(type, crc) ;
        *ptr++ = type ;

        if( !crc32 ) {
          crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
          ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
          ptr = putZdle(ptr, crc&0xff, info) ;
        }
        else {
          crc = ~crc ;
          for(len=4; --len >= 0; crc >>= 8)
            ptr = putZdle(ptr, crc&0xff, info) ;
        }

        len = ptr - info->buffer ;
      }
      
      ZStatus(SndByteCount, info->offset, NULL) ;

      if( (err = ZXmitStr(info->buffer, len, info)) )
        return err ;

#ifdef      COMMENT
      if( (err = ZXmitData(ZBIN, len, uchar(type), info->buffer, info)) )
        return err ;
#endif      /* COMMENT */

      /* finally, do we want to wait after this packet? */

      switch( type ) {
        case ZCRCE:
          info->state = SendEof ;
          info->timeout = 60 ;
          return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
        case ZCRCW:
          info->state = info->fileEof ? SendDone : SendWait ;
          info->timeout = 60 ;
          break ;
        default:
          info->state = Sending ;
          info->timeout = 0 ;
          break ;
      }


#ifdef      COMMENT
      if( info->fileEof ) {   /* Yes, file is done, send EOF and wait */
        info->state = SendEof ;
        info->timeout = 60 ;
        return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
      }
      else if( type == ZCRCW ) {
        info->state = SendWait ;
        info->timeout = 60 ;
      }
      else {
        info->state = Sending ;
        info->timeout = 0 ;
      }
#endif      /* COMMENT */
      return 0 ;
}


uint QTermZmodem::rcvHex( uint i, char c )
{
      if( c <= '9' )
        c -= '0' ;
      else if( c <= 'F' )
        c -= 'A'-10 ;
      else
        c -= 'a'-10 ;
      return (i<<4)+c ;
}


int QTermZmodem::dataSetup( register ZModem *info )
{
      info->InputState = Indata ;
      info->chrCount = 0 ;
      info->crcCount = 0 ;
      info->crc = (info->DataType != ZBIN32) ? 0 : 0xffffffffL ;
      return 0 ;
}

int QTermZmodem::ZWriteFile(uchar *buffer, int len, FILE *file, ZModem *)
{
      
      return fwrite(buffer, 1, len, file) == len ? 0 : ZmErrSys ;;
}

int QTermZmodem::ZCloseFile(ZModem *info)
{
      //to be completed
      fclose(info->file) ;    
      return 0;
}

void QTermZmodem::ZFlowControl(int onoff, ZModem *info)
{
      //to be completed
}

int QTermZmodem::GotSinit(  ZModem *info )
{
      zmodemlog("GotSinit[%s]: call dataSetup\n", sname(info)) ;

      info->zsinitflags = info->hdrData[4] ;
      info->escCtrl = info->zsinitflags & TESCCTL ;
      info->escHibit = info->zsinitflags & TESC8 ;
      ZFlowControl(1, info) ;
      return dataSetup(info) ;
}

int QTermZmodem::GotFile(  ZModem *info )
{
      zmodemlog("GotFile[%s]: call dataSetup\n", sname(info)) ;

      info->errCount = 0 ;
      info->f0 = info->hdrData[4] ;
      info->f1 = info->hdrData[3] ;
      info->f2 = info->hdrData[2] ;
      info->f3 = info->hdrData[1] ;
      return dataSetup(info) ;
}

int QTermZmodem::GotFin(  ZModem *info )
{
      int i;
      zmodemlog("GotFin[%s]: send ZFIN\n", sname(info)) ;
      info->InputState = Finish ;
      info->chrCount = 0 ;
      if( info->filename != NULL )
        free(info->filename) ;
      i= ZXmitHdrHex(ZFIN, zeros, info) ;
      ZmodemReset( info);
      transferstate = transferstop;  // transfer complete
      return i;
}


int QTermZmodem::GotData(  ZModem *info )
{
      int   err ;

      zmodemlog("GotData[%s]:\n", sname(info)) ;

      if( ZDec4(info->hdrData+1) != info->offset ) {
        if( info->attn != NULL  &&  (err=ZAttn(info)) != 0 )
          return err ;
        zmodemlog("  bad, send ZRPOS(%ld)\n", info->offset);
        return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
      }

      /* Let's do it! */
      zmodemlog("  call dataSetup\n");
      return dataSetup(info) ;
}

int QTermZmodem::GotEof(  ZModem *info )
{
      zmodemlog("GotEof[%s]: offset=%ld\n", sname(info), info->offset) ;
      if( ZDec4(info->hdrData+1) != info->offset ) {
        zmodemlog("zdec4(info->hdrdata+1)=%ld\n",ZDec4(info->hdrData+1));
        zmodemlog("  bad length, state = RFile\n") ;
        info->state = RFile ;
        return 0 ;            /* it was probably spurious */
      }

      /* TODO: if we can't close the file, send a ZFERR */

      ZCloseFile(info) ; info->file = NULL ;
      ZStatus(FileEnd, 0, info->filename) ;
      if( info->filename != NULL ) {
        free(info->filename) ;
        info->filename = NULL ;
      }
      return SendRinit(info) ;
}

int QTermZmodem::GotFreecnt(  ZModem *info )
{
      /* TODO: how do we find free space on system? */
      return ZXmitHdrHex(ZACK, ZEnc4(0xffffffff), info) ;
}


int QTermZmodem::GotFileCrc(  ZModem *info )
{
      zmodemlog("GotFileCrc[%s]: call requestFile\n", sname(info)) ;
      return requestFile(info, ZDec4(info->hdrData+1)) ;
}

int QTermZmodem::requestFile(  ZModem *info, ulong crc )
{
      info->file = ZOpenFile((char *)info->buffer, crc, info) ;

      if( info->file == NULL ) {
        zmodemlog("requestFile[%s]: send ZSKIP\n", sname(info)) ;

        info->state = RStart ;
        ZStatus(FileSkip, 0, info->filename) ;
        return ZXmitHdrHex(ZSKIP, zeros, info) ;
      }
      else {
        zmodemlog("requestFile[%s]: send ZRPOS(%ld)\n",
          sname(info), info->offset) ;
        info->offset = info->f0 == ZCRESUM ? ftell(info->file) : 0 ;
        info->state = RFile ;
        ZStatus(FileBegin, info->len, info->filename) ;
        return ZXmitHdrHex(ZRPOS, ZEnc4(info->offset), info) ;
      }
}

void QTermZmodem::parseFileName(  ZModem *info, char *fileinfo )
{
      char  *ptr ;
      int   serial=0 ;

      info->len = info->mode = info->filesRem =
            info->bytesRem = info->fileType = 0 ;
      ptr = fileinfo + strlen(fileinfo) + 1 ;
      if( info->filename != NULL )
        free(info->filename) ;
      info->filename = strdup(fileinfo) ;
      sscanf(ptr, "%d %lo %o %o %d %d %d", &info->len, &info->date,
          &info->mode, &serial, &info->filesRem, &info->bytesRem,
          &info->fileType) ;
}



int QTermZmodem::fileError(  ZModem *info, int type, int data )
{
      int   err ;

      info->InputState = Idle ;
      info->chrCount=0 ;

      if( info->attn != NULL  &&  (err=ZAttn(info)) != 0 )
        return err ;
      return ZXmitHdrHex(type, ZEnc4(data), info) ;
}


int QTermZmodem::ProcessPacket(  ZModem *info )
{
      int   idx = (uchar) info->buffer[0] ;
      int   idxc = (uchar) info->buffer[1] ;
      int   crc0, crc1 ;
      int   err ;

      info->state = YRDataWait ;

      if( idxc != 255 - idx ) {
        ZStatus(DataErr, ++info->errCount, NULL) ;
        return rejectPacket(info) ;
      }

      if( idx == (info->packetCount%256) )      /* quietly ignore dup */
        return acceptPacket(info) ;

      if( idx != (info->packetCount+1)%256 ) { /* out of sequence */
        (void) ZXmitStr(CanStr, 2, info) ;
        return ZmErrSequence ;
      }

      crc0 = (uchar)info->buffer[info->pktLen-2] << 8 |
            (uchar)info->buffer[info->pktLen-1] ;
      crc1 = calcCrc(info->buffer+2, info->pktLen-4) ;
      if( crc0 != crc1 ) {
        ZStatus(DataErr, ++info->errCount, NULL) ;
        return rejectPacket(info) ;
      }

      ++info->packetCount ;

      if( info->packetCount == 0 )        /* packet 0 is filename */
      {
        if( info->buffer[2] == '\0' ) {   /* null filename is FIN */
          (void) acceptPacket(info) ;
          return ZmDone ;
        }

        parseFileName(info, (char *)info->buffer+2) ;
        info->file = ZOpenFile(info->filename, 0, info) ;
        if( info->file == NULL ) {
          (void) ZXmitStr(CanStr, 2, info) ;
          return ZmErrCantOpen ;
        }
        if( (err = acceptPacket(info)) != 0 )
          return err ;
        return ZXmitStr((uchar *)"C", 1, info) ;
      }


      if( ZWriteFile(info->buffer+2, info->pktLen-4, info->file, info) ) {
        ZStatus(FileErr, zerrno, NULL) ;
        (void) ZXmitStr(CanStr, 2, info) ;
        return ZmErrSys ;
      }
      info->offset += info->pktLen-4 ;
      ZStatus(RcvByteCount, info->offset, NULL) ;

      (void) acceptPacket(info) ;
      return 0 ;
}

int QTermZmodem::rejectPacket(  ZModem *info )
{
      info->timeout = 10 ;
      return ZXmitStr(NakStr, 1, info) ;
}


int QTermZmodem::acceptPacket(  ZModem *info )
{
      info->state = YRDataWait ;
      info->timeout = 10 ;
      return ZXmitStr(AckStr, 1, info) ;
}


int QTermZmodem::calcCrc( uchar *str, int len )
{
      int   crc = 0 ;
      while( --len >= 0 )
        crc = updcrc(*str++, crc) ;
      crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
      return crc & 0xffff ;
}


char * QTermZmodem::strdup( const char *str )
{
      char  *rval ;
      int   len = strlen(str) + 1 ;
      rval = (char *)malloc(len) ;
      strcpy(rval,str) ;
      return rval ;
}


int QTermZmodem::ZXmitData( int format, int len, uchar term, uchar *data, ZModem *info)
{
      uchar *ptr = info->buffer ;
      uint  crc ;

      if( format == ZBIN && info->crc32 )
        format = ZBIN32 ;

      zmodemlog("ZXmiteData: fmt=%c, len=%d, term=%c\n", format, len, term) ;

      crc = (format == ZBIN) ? 0 : 0xffffffff ;

      while( --len >= 0 ) {
        if( format == ZBIN )
          crc = updcrc(*data, crc) ;
        else
          crc = UPDC32(*data, crc) ;
        ptr = putZdle(ptr, *data++, info) ;
      }

      *ptr++ = ZDLE ;
      if( format == ZBIN )
        crc = updcrc(term, crc) ;
      else
        crc = UPDC32(term, crc) ;
      *ptr++ = term ;
      if( format == ZBIN ) {
        crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
        ptr = putZdle(ptr, (crc>>8)&0xff, info) ;
        ptr = putZdle(ptr, crc&0xff, info) ;
      }
      else {
        crc = ~crc ;
        for(len=4; --len >= 0; crc >>= 8)
          ptr = putZdle(ptr, crc&0xff, info) ;
      }

      return ZXmitStr(info->buffer, ptr-info->buffer, info) ;
}



int QTermZmodem::YXmitData( uchar *buffer, int len, ZModem *info )
{
      uchar hdr[3] ;
      uchar trail[2] ;
      ulong crc = 0 ;
      int   i, err ;

      hdr[0] = len == 1024 ? STX : SOH ;
      hdr[1] = info->packetCount ;
      hdr[2] = ~hdr[1] ;
      if( (err = ZXmitStr(hdr, 3, info)) ||
          (err = ZXmitStr(buffer, len, info)) )
        return err ;

      if( info->PacketType == NAK ) {     /* checksum */
        for(i=0; i<len; ++i)
          crc += buffer[i] ;
        trail[0] = crc % 256 ;
        return ZXmitStr(trail,1, info) ;
      }
      else {
        for(i=0; i<len; ++i)
          crc = updcrc(buffer[i], crc) ;
        crc = updcrc(0,crc) ; crc = updcrc(0,crc) ;
        trail[0] = crc / 256 ;
        trail[1] = crc % 256 ;
        return ZXmitStr(trail,2, info) ;
      }
}


int QTermZmodem::YSendFilename( ZModem *info )
{
      int   i,len ;
      uchar obuf[1024] ;
      uchar *ptr = obuf ;

      info->state = info->PacketType != 'G' ? YTFile : YTDataWait ;
      info->packetCount = 0 ;
      info->offset = 0 ;

      i = strlen(info->rfilename) ;
      memcpy(ptr, info->rfilename, i+1) ;  ptr += i+1 ;
      sprintf((char *)ptr, "%d %lo %o 0", info->len, info->date, info->mode);
      ptr += strlen((char *)ptr) ;
      *ptr++ = '\0' ;
      /* pad out to 128 bytes or 1024 bytes */
      i = ptr-obuf ;
      len = i>128 ? 1024 : 128 ;
      for(; i<len; ++i)
        *ptr++ = '\0' ;

      return YXmitData(obuf, len, info) ;
}

int QTermZmodem::YSendData( ZModem *info )
{
      int   i ;

      /* are there characters still in the read buffer? */

      if( info->chrCount <= 0 ) {
        info->bufp = 0 ;
        info->chrCount = fread(info->buffer, 1, info->packetsize, info->file);
        info->fileEof = feof(info->file) ;
      }

      if( info->chrCount <= 0 ) {
        fclose(info->file) ;
        info->state = YTEOF ;
        return ZXmitStr(eotstr, 1, info) ;
      }

      /* pad out to 128 bytes if needed */
      if( info->chrCount < 128 ) {
        i = 128 - info->chrCount ;
        memset(info->buffer + info->bufp + info->chrCount, 0x1a, i) ;
        info->chrCount = 128 ;
      }

      info->ylen = info->chrCount >= 1024 ? 1024 : 128 ;
      ++info->packetCount ;

      info->state = YTData ;

      return YXmitData(info->buffer + info->bufp, info->ylen, info) ;
}


int QTermZmodem::YSendFin( ZModem *info )
{
      uchar obuf[128] ;

      info->state = YTFin ;
      info->packetCount = 0 ;

      memset(obuf,0,128) ;

      return YXmitData(obuf, 128, info) ;
}

int QTermZmodem::sendFilename( ZModem *info )
{
      int   err ;
      int   i ;
      uchar obuf[2048] ;
      uchar *ptr = obuf ;

      info->state = FileWait ;

      if( (err = ZXmitHdr(ZFILE, ZBIN, info->fileFlags, info)) )
        return err ;

      i = strlen(info->rfilename) ;
      memcpy(ptr, info->rfilename, i+1) ;  ptr += i+1 ;
      sprintf((char *)ptr, "%d %lo %o 0 %d %d 0", info->len, info->date,
        info->mode, info->filesRem, info->bytesRem) ;
      ptr += strlen((char *)ptr) ;
      *ptr++ = '\0' ;

      return ZXmitData(ZBIN, ptr-obuf, ZCRCW, obuf, info) ;
}

int QTermZmodem::GotRinit(  ZModem *info )
{
      if(strFileList.count()==0)
            return ZmodemTFinish(info);


      info->bufsize = info->hdrData[1] + info->hdrData[2]*256 ;
      info->zrinitflags = info->hdrData[4] + info->hdrData[3]*256 ;
      info->crc32 = info->zrinitflags & CANFC32 ;
      info->escCtrl = info->zrinitflags & ESCCTL ;
      info->escHibit = info->zrinitflags & ESC8 ;

      /* Full streaming: If receiver can overlap I/O, and if
       * the sender can sample the reverse channel without hanging,
       * and the receiver has not specified a buffer size, then we
       * can simply blast away with ZCRCG packets.  If the receiver
       * detects an error, it sends an attn sequence and a new ZRPOS
       * header to restart the file where the error occurred.
       *
       * [note that zmodem8.doc does not define how much noise is
       * required to trigger a ZCRCW packet.  We arbitrarily choose
       * 64 bytes]
       *
       * If 'windowsize' is nonzero, and the receiver can do full
       * duplex, ZCRCQ packets are sent instead of ZCRCG, to keep track
       * of the number of characters in the queue.  If the queue fills
       * up, we pause and wait for a ZACK.
       *
       *
       * Full Streaming with Reverse Interrupt:  If sender cannot
       * sample the input stream, then we define an Attn sequence
       * that will be used to interrupt transmission.
       *
       *
       * Full Streaming with Sliding Window:  If sender cannot
       * sample input stream or respond to Attn signal, we send
       * several ZCRCQ packets until we're sure the receiver must
       * have sent back at least one ZACK.  Then we stop sending and
       * read that ZACK.  Then we send one more packet and so on.
       *
       *
       * Segmented Streaming:  If receiver cannot overlap I/O or can't do
       * full duplex and has specified a maximum receive buffer size,
       * whenever the buffer size is reached, we send a ZCRCW packet.
       *
       * TODO: what if receiver can't overlap, but hasn't set a buffer
       * size?
       *
       * ZCRCE: CRC next, frame ends, header follows
       * ZCRCG: CRC next, frame continues nonstop
       * ZCRCQ: CRC next, send ZACK, frame continues nonstop
       * ZCRCW: CRC next, send ZACK, frame ends, header follows
       */

      transferstate = transferstart;

      ZFlowControl(1,info) ;

      if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) &&
          (SendSample||SendAttn)  &&
          info->bufsize == 0 )
      {
        if( info->windowsize == 0 )
        {
          info->Streaming = Full ;
        }
        else
        {
          info->Streaming = StrWindow ;
        }
      }

      else if( (info->zrinitflags & (CANFDX|CANOVIO)) == (CANFDX|CANOVIO) &&
          info->bufsize == 0 )
      {
        info->Streaming = SlidingWindow ;
      }

      else
      {
        info->Streaming = Segmented ;
      }
      // get filenames to transfer
      zmodemlog("GotRinit[%s]\n", sname(info)) ;

      if( AlwaysSinit || info->zsinitflags != 0  ||  info->attn != NULL )
            SendZSInit(info) ;
      

      itFile = strFileList.begin();
      QFileInfo fi(*itFile);
      qDebug("files to be transfered %d", strFileList.count());
      char *filename = strdup(fi.absFilePath().latin1());
      char *rfilename = strdup(fi.fileName().latin1());
      ZmodemTFile( filename, rfilename,
                                    0,0,0,0, strFileList.count(), fi.size(), info);
      strFileList.erase(itFile);
      return ZmDone;
}


int QTermZmodem::SendZSInit( ZModem *info )
{
      int   err ;
      //char      *at = (info->attn != NULL) ? info->attn : "" ;
      char * at;
      if (info->attn != NULL)
            at = info->attn;
      else
            at = "";
      uchar fbuf[4] ;

      /* TODO: zmodem8.doc states: "If the ZSINIT header specifies
       * ESCCTL or ESC8, a HEX header is used, and the receiver
       * activates the specified ESC modes before reading the following
       * data subpacket." What does that mean?
       */

      zmodemlog("SendZSInit[%s]\n", sname(info)) ;

      info->state = TInit ;
      fbuf[0] = fbuf[1] = fbuf[2] = 0 ;
      fbuf[3] = info->zsinitflags ;
      if( (err = ZXmitHdr(ZSINIT, ZBIN, fbuf, info))  ||
          (err = ZXmitData(ZBIN, strlen(at)+1, ZCRCW, (uchar *)at, info)) )
      {
        return err ;
      }
      return 0 ;
}


int QTermZmodem::SendFileCrc(  ZModem *info )
{
      ulong crc ;

      crc = FileCrc(info->filename) ;

      zmodemlog("SendFileCrc[%s]: %lx\n", sname(info), crc) ;

      return ZXmitHdrHex(ZCRC, ZEnc4(crc), info) ;
}

int QTermZmodem::GotSendAck(  ZModem *info )
{
      ulong offset ;

      offset = ZDec4(info->hdrData+1) ;

      if( offset > info->lastOffset )
        info->lastOffset = offset ;

      zmodemlog("GotSendAck[%s]: %lx\n", sname(info), info->offset) ;

      return 0 ;        /* DONT send more data, that will happen
                         * later anyway */
}

int QTermZmodem::GotSendDoneAck(  ZModem *info )
{
      ulong offset ;

      offset = ZDec4(info->hdrData+1) ;

      if( offset > info->lastOffset )
        info->lastOffset = offset ;

      zmodemlog("GotSendDoneAck[%s]: %ld\n", sname(info), info->offset) ;

      info->state = SendEof ;
      info->timeout = 60 ;
      return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}

int QTermZmodem::GotSendNak(  ZModem *info )
{
      info->offset = info->zrposOffset ;

      fseek(info->file, info->offset, 0) ;

      /* TODO: what if fseek fails?  Send EOF? */

      zmodemlog("GotSendNak[%s]: %ld\n", sname(info), info->offset) ;

      return SendMoreFileData(info) ;
}

int QTermZmodem::GotSendWaitAck(  ZModem *info )
{

      ulong offset ;
      int   err ;

      offset = ZDec4(info->hdrData+1) ;

      printf("last=%lx, now=%lx\n", info->lastOffset, offset);

      if( offset > info->lastOffset )
        info->lastOffset = offset ;

      //receiver return -1 without setting this flag, kingson 00:07 14-07-04
      info->waitflag = 1 ;

      zmodemlog("GotSendWaitAck[%s]\n", sname(info), offset) ;

      if( (err = ZXmitHdr(ZDATA, ZBIN, info->hdrData+1, info)) )
        return err ;
      return SendMoreFileData(info) ;
}

int QTermZmodem::SkipFile(  ZModem *info )
{
      zmodemlog("SkipFile[%s]\n", sname(info)) ;
      ZStatus(FileEnd, 0, info->rfilename);
      fclose(info->file) ;
      
      // stupid SMTH doesnt send further command, kick
      // lets send files in the list
      info-> state= TStart;
      return GotRinit(info);
}

int QTermZmodem::GotSendPos(  ZModem *info )
{
      ZStatus(DataErr, ++info->errCount, NULL) ;
      info->waitflag = 1 ;          /* next pkt should wait, to resync */
      printf("GotSendPos, offset=%lx\n", info->offset);
      zmodemlog("GotSendPos[%s] %lx\n", sname(info), info->offset) ;
      return startFileData(info) ;
}

int QTermZmodem::SendFileData(  ZModem *info )
{
      info->waitflag = 0 ;
      return startFileData(info) ;
}

int QTermZmodem::ResendEof(  ZModem *info )
{
      return ZXmitHdrHex(ZEOF, ZEnc4(info->offset), info) ;
}

int QTermZmodem::OverAndOut( ZModem *info )
{
      zmodemlog("OverAndOut[%s]\n", sname(info)) ;

      ZXmitStr((uchar *)"OO", 2, info) ;

      transferstate = transferstop;  // transfer complete

      ZmodemReset(info);  //Tranfer complete, zmodem return to receive state

      return ZmDone ;
}

int QTermZmodem::startFileData(  ZModem *info )
{
      int   err ;

      info->offset =
      info->lastOffset =
      info->zrposOffset = ZDec4(info->hdrData+1) ;

      fseek(info->file, info->offset, 0) ;

      /* TODO: what if fseek fails?  Send EOF? */

      zmodemlog("startFileData[%s]: %lx\n", sname(info), info->offset) ;

      if( (err = ZXmitHdr(ZDATA, ZBIN, ZEnc4(info->offset), info)) )
        return err ;
      return SendMoreFileData(info) ;
}

uchar * QTermZmodem::putHex( uchar *ptr, uchar c )
{
      *ptr++ = hexChars[(c>>4)&0xf] ;
      *ptr++ = hexChars[c&0xf] ;
      return ptr ;
}


int QTermZmodem::ZmodemReset(ZModem * info)
{
      zmodemlog( "\nZmodemReset\n");

      ZmodemRInit( info);

      return 0;
}


void QTermZmodem::zmodemlog(const char *fmt, ... )
{
// only for debug
#ifdef QTERM_DEBUG
      va_list ap;
      struct timeval tv ;
      struct tm *tm ;
      static      int   do_ts = 1 ;
      
      zmodemlogfile = fopen("zmodem.log","a");

      if( zmodemlogfile == NULL )
        return ;

      if( do_ts ) {
        gettimeofday(&tv, NULL) ;
        tm = localtime(&tv.tv_sec) ;
        fprintf(zmodemlogfile, "%2.2d:%2.2d:%2.2d.%2.2ld: ",
          tm->tm_hour, tm->tm_min, tm->tm_sec,
          tv.tv_usec/10000) ;
      }
      do_ts = strchr(fmt, '\n') != NULL ;

      va_start(ap, fmt) ;
      vfprintf(zmodemlogfile, fmt, ap) ;
      va_end(ap) ;

      fclose(zmodemlogfile);
#endif

}

void QTermZmodem::zmodemCancel()
{
      ZmodemAbort(&info);
}
#include <qtermzmodem.moc>

Generated by  Doxygen 1.6.0   Back to index