| /* | 
|   zm.c - zmodem protocol handling lowlevelstuff | 
|   Copyright (C) until 1998 Chuck Forsberg (OMEN Technology Inc) | 
|   Copyright (C) 1996, 1997 Uwe Ohse | 
|   | 
|   This program is free software; you can redistribute it and/or modify | 
|   it under the terms of the GNU General Public License as published by | 
|   the Free Software Foundation; either version 2, or (at your option) | 
|   any later version. | 
|   | 
|   This program is distributed in the hope that it will be useful, | 
|   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|   GNU General Public License for more details. | 
|   | 
|   You should have received a copy of the GNU General Public License | 
|   along with this program; if not, write to the Free Software | 
|   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | 
|   02111-1307, USA. | 
|   | 
|   originally written by Chuck Forsberg | 
| */ | 
| /* historical comment: -- uwe | 
|  *   Z M . C | 
|  *    ZMODEM protocol primitives | 
|  *    05-09-88  Chuck Forsberg Omen Technology Inc | 
|  * | 
|  * Entry point Functions: | 
|  *    zsbhdr(type, hdr) send binary header | 
|  *    zshhdr(type, hdr) send hex header | 
|  *    zgethdr(hdr, eflag) receive header - binary or hex | 
|  *    zsdata(buf, len, frameend) send data | 
|  *    zrdata(buf, len, bytes_received) receive data | 
|  *    stohdr(pos) store position data in Txhdr | 
|  *    long rclhdr(hdr) recover position offset from header | 
|  */ | 
|   | 
|   | 
| #include "zglobal.h" | 
|   | 
| #include <stdio.h> | 
|   | 
| unsigned int Rxtimeout = 100;        /* Tenths of seconds to wait for something */ | 
|   | 
| /* Globals used by ZMODEM functions */ | 
| int Rxframeind;        /* ZBIN ZBIN32, or ZHEX type of frame received */ | 
| int Rxtype;        /* Type of header received */ | 
| char Rxhdr[4];        /* Received header */ | 
| char Txhdr[4];        /* Transmitted header */ | 
| long Txpos;        /* Transmitted file position */ | 
| int Txfcs32;        /* TRUE means send binary frames with 32 bit FCS */ | 
| int Crc32t;        /* Display flag indicating 32 bit CRC being sent */ | 
| int Crc32;        /* Display flag indicating 32 bit CRC being received */ | 
| int Znulls;        /* Number of nulls to send at beginning of ZDATA hdr */ | 
| char Attn[ZATTNLEN+1];    /* Attention string rx sends to tx on err */ | 
|   | 
| static char lastsent;    /* Last char we sent */ | 
| int turbo_escape; | 
| int bytes_per_error=0; | 
|   | 
| static const char *frametypes[] = { | 
|     "Carrier Lost",        /* -3 */ | 
|     "TIMEOUT",        /* -2 */ | 
|     "ERROR",        /* -1 */ | 
| #define FTOFFSET 3 | 
|     "ZRQINIT", | 
|     "ZRINIT", | 
|     "ZSINIT", | 
|     "ZACK", | 
|     "ZFILE", | 
|     "ZSKIP", | 
|     "ZNAK", | 
|     "ZABORT", | 
|     "ZFIN", | 
|     "ZRPOS", | 
|     "ZDATA", | 
|     "ZEOF", | 
|     "ZFERR", | 
|     "ZCRC", | 
|     "ZCHALLENGE", | 
|     "ZCOMPL", | 
|     "ZCAN", | 
|     "ZFREECNT", | 
|     "ZCOMMAND", | 
|     "ZSTDERR", | 
|     "xxxxx" | 
| #define FRTYPES 22    /* Total number of frame types in this array */ | 
|             /*  not including psuedo negative entries */ | 
| }; | 
|   | 
| #define badcrc _("Bad CRC") | 
| /* static char *badcrc = "Bad CRC"; */ | 
| static inline int noxrd7 __P ((void)); | 
| static inline int zdlread __P ((void)); | 
| static int zdlread2 __P ((int)) LRZSZ_ATTRIB_REGPARM(1); | 
| static inline int zgeth1 __P ((void)); | 
| static void zputhex __P ((int c, char *pos)); | 
| static inline int zgethex __P ((void)); | 
| static int zrbhdr __P ((char *hdr)); | 
| static int zrbhdr32 __P ((char *hdr)); | 
| static int zrhhdr __P ((char *hdr)); | 
| static char zsendline_tab[256]; | 
| static int zrdat32 __P ((char *buf, int length, size_t *)); | 
| static void zsbh32 __P ((char *hdr, int type)); | 
|   | 
| extern int zmodem_requested; | 
| extern void xsendline(int c); | 
| extern void send_data(int fd, char *buf, int size); | 
| extern void flushmo(void); | 
|   | 
| /* | 
|  * Read a character from the modem line with timeout. | 
|  *  Eat parity, XON and XOFF characters. | 
|  */ | 
| static inline int | 
| noxrd7(void) | 
| { | 
|     register int c; | 
|   | 
|     for (;;) { | 
|         if ((c = READLINE_PF(Rxtimeout)) < 0) | 
|             return c; | 
|         switch (c &= 0177) { | 
|         case XON: | 
|         case XOFF: | 
|             continue; | 
|         default: | 
|             if (Zctlesc && !(c & 0140)) | 
|                 continue; | 
|         case '\r': | 
|         case '\n': | 
|         case ZDLE: | 
|             return c; | 
|         } | 
|     } | 
| } | 
|   | 
| static inline int | 
| zgeth1(void) | 
| { | 
|     register int c, n; | 
|   | 
|     if ((c = noxrd7()) < 0) | 
|         return c; | 
|     n = c - '0'; | 
|     if (n > 9) | 
|         n -= ('a' - ':'); | 
|     if (n & ~0xF) | 
|         return ERROR; | 
|     if ((c = noxrd7()) < 0) | 
|         return c; | 
|     c -= '0'; | 
|     if (c > 9) | 
|         c -= ('a' - ':'); | 
|     if (c & ~0xF) | 
|         return ERROR; | 
|     c += (n<<4); | 
|     return c; | 
| } | 
|   | 
| /* Decode two lower case hex digits into an 8 bit byte value */ | 
| static inline int | 
| zgethex(void) | 
| { | 
|     register int c; | 
|   | 
|     c = zgeth1(); | 
|     VPRINTF(9,("zgethex: %02X", c)); | 
|     return c; | 
| } | 
|   | 
| /* | 
|  * Read a byte, checking for ZMODEM escape encoding | 
|  *  including CAN*5 which represents a quick abort | 
|  */ | 
| static inline int | 
| zdlread(void) | 
| { | 
|     int c; | 
|     /* Quick check for non control characters */ | 
|     if ((c = READLINE_PF(Rxtimeout)) & 0140) | 
|         return c; | 
|     return zdlread2(c); | 
| } | 
| /* no, i don't like gotos. -- uwe */ | 
| static int | 
| zdlread2(int c) | 
| { | 
|     goto jump_over; /* bad style */ | 
|   | 
| again: | 
|     /* Quick check for non control characters */ | 
|     if ((c = READLINE_PF(Rxtimeout)) & 0140) | 
|         return c; | 
| jump_over: | 
|     switch (c) { | 
|     case ZDLE: | 
|         break; | 
|     case XON: | 
|     case (XON|0200): | 
|     case XOFF: | 
|     case (XOFF|0200): | 
|         goto again; | 
|     default: | 
|         if (Zctlesc && !(c & 0140)) { | 
|             goto again; | 
|         } | 
|         return c; | 
|     } | 
| again2: | 
|     if ((c = READLINE_PF(Rxtimeout)) < 0) | 
|         return c; | 
|     if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0) | 
|         return c; | 
|     if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0) | 
|         return c; | 
|     if (c == CAN && (c = READLINE_PF(Rxtimeout)) < 0) | 
|         return c; | 
|     switch (c) { | 
|     case CAN: | 
|         return GOTCAN; | 
|     case ZCRCE: | 
|     case ZCRCG: | 
|     case ZCRCQ: | 
|     case ZCRCW: | 
|         return (c | GOTOR); | 
|     case ZRUB0: | 
|         return 0177; | 
|     case ZRUB1: | 
|         return 0377; | 
|     case XON: | 
|     case (XON|0200): | 
|     case XOFF: | 
|     case (XOFF|0200): | 
|         goto again2; | 
|     default: | 
|         if (Zctlesc && ! (c & 0140)) { | 
|             goto again2; | 
|         } | 
|         if ((c & 0140) ==  0100) | 
|             return (c ^ 0100); | 
|         break; | 
|     } | 
|     VPRINTF(2,(_("Bad escape sequence %x"), c)); | 
|     return ERROR; | 
| } | 
|   | 
|   | 
|   | 
| /* | 
|  * Send character c with ZMODEM escape sequence encoding. | 
|  *  Escape XON, XOFF. Escape CR following @ (Telenet net escape) | 
|  */ | 
| inline void  | 
| zsendline(int c) | 
| { | 
|   | 
|     switch(zsendline_tab[(unsigned) (c&=0377)]) | 
|     { | 
|     case 0:  | 
|         xsendline(lastsent = c);  | 
|         break; | 
|     case 1: | 
|         xsendline(ZDLE); | 
|         c ^= 0100; | 
|         xsendline(lastsent = c); | 
|         break; | 
|     case 2: | 
|         if ((lastsent & 0177) != '@') { | 
|             xsendline(lastsent = c); | 
|         } else { | 
|             xsendline(ZDLE); | 
|             c ^= 0100; | 
|             xsendline(lastsent = c); | 
|         } | 
|         break; | 
|     } | 
| } | 
|   | 
| static inline void  | 
| zsendline_s(const char *s, size_t count)  | 
| { | 
|     const char *end=s+count; | 
|     while(s!=end) { | 
|         int last_esc=0; | 
|         const char *t=s; | 
|         while (t!=end) { | 
|             last_esc=zsendline_tab[(unsigned) ((*t) & 0377)]; | 
|             if (last_esc)  | 
|                 break; | 
|             t++; | 
|         } | 
|         if (t!=s) { | 
|             //fwrite(s,(size_t)(t-s),1,stdout); | 
|             send_data(1, (char *)s, t-s); | 
|             lastsent=t[-1]; | 
|             s=t; | 
|         } | 
|         if (last_esc) { | 
|             int c=*s; | 
|             switch(last_esc) { | 
|             case 0:  | 
|                 xsendline(lastsent = c);  | 
|                 break; | 
|             case 1: | 
|                 xsendline(ZDLE); | 
|                 c ^= 0100; | 
|                 xsendline(lastsent = c); | 
|                 break; | 
|             case 2: | 
|                 if ((lastsent & 0177) != '@') { | 
|                     xsendline(lastsent = c); | 
|                 } else { | 
|                     xsendline(ZDLE); | 
|                     c ^= 0100; | 
|                     xsendline(lastsent = c); | 
|                 } | 
|                 break; | 
|             } | 
|             s++; | 
|         } | 
|     } | 
| } | 
|   | 
|   | 
| /* Send ZMODEM binary header hdr of type type */ | 
| void  | 
| zsbhdr(int type, char *hdr) | 
| { | 
|     register int n; | 
|     register unsigned short crc; | 
|   | 
|     VPRINTF(3,("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr))); | 
|     if (type == ZDATA) | 
|         for (n = Znulls; --n >=0; ) | 
|             xsendline(0); | 
|   | 
|     xsendline(ZPAD); xsendline(ZDLE); | 
|   | 
|     Crc32t=Txfcs32; | 
|     if (Crc32t) | 
|         zsbh32(hdr, type); | 
|     else { | 
|         xsendline(ZBIN); zsendline(type); crc = updcrc(type, 0); | 
|   | 
|         for (n=4; --n >= 0; ++hdr) { | 
|             zsendline(*hdr); | 
|             crc = updcrc((0377& *hdr), crc); | 
|         } | 
|         crc = updcrc(0,updcrc(0,crc)); | 
|         zsendline(crc>>8); | 
|         zsendline(crc); | 
|     } | 
|     if (type != ZDATA) | 
|         flushmo(); | 
| } | 
|   | 
|   | 
| /* Send ZMODEM binary header hdr of type type */ | 
| static void | 
| zsbh32(char *hdr, int type) | 
| { | 
|     register int n; | 
|     register unsigned long crc; | 
|   | 
|     xsendline(ZBIN32);  zsendline(type); | 
|     crc = 0xFFFFFFFFL; crc = UPDC32(type, crc); | 
|   | 
|     for (n=4; --n >= 0; ++hdr) { | 
|         crc = UPDC32((0377 & *hdr), crc); | 
|         zsendline(*hdr); | 
|     } | 
|     crc = ~crc; | 
|     for (n=4; --n >= 0;) { | 
|         zsendline((int)crc); | 
|         crc >>= 8; | 
|     } | 
| } | 
|   | 
| /* Send ZMODEM HEX header hdr of type type */ | 
| void  | 
| zshhdr(int type, char *hdr) | 
| { | 
|     register int n; | 
|     register unsigned short crc; | 
|     char s[30]; | 
|     size_t len; | 
|   | 
|     VPRINTF(3,("zshhdr: %s %lx", frametypes[(type & 0x7f)+FTOFFSET], rclhdr(hdr))); | 
|     s[0]=ZPAD; | 
|     s[1]=ZPAD; | 
|     s[2]=ZDLE; | 
|     s[3]=ZHEX; | 
|     zputhex(type & 0x7f ,s+4); | 
|     len=6; | 
|     Crc32t = 0; | 
|   | 
|     crc = updcrc((type & 0x7f), 0); | 
|     for (n=4; --n >= 0; ++hdr) { | 
|         zputhex(*hdr,s+len);  | 
|         len += 2; | 
|         crc = updcrc((0377 & *hdr), crc); | 
|     } | 
|     crc = updcrc(0,updcrc(0,crc)); | 
|     zputhex(crc>>8,s+len);  | 
|     zputhex(crc,s+len+2); | 
|     len+=4; | 
|   | 
|     /* Make it printable on remote machine */ | 
|     s[len++]=015; | 
|     s[len++]=0212; | 
|     /* | 
|      * Uncork the remote in case a fake XOFF has stopped data flow | 
|      */ | 
|     if (type != ZFIN && type != ZACK) | 
|     { | 
|         s[len++]=021; | 
|     } | 
|     flushmo(); | 
|     send_data(1,s,len); | 
| } | 
|   | 
| /* | 
|  * Send binary array buf of length length, with ending ZDLE sequence frameend | 
|  */ | 
| static const char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"}; | 
| void  | 
| zsdata(const char *buf, size_t length, int frameend) | 
| { | 
|     register unsigned short crc; | 
|   | 
|     VPRINTF(3,("zsdata: %lu %s", (unsigned long) length,  | 
|         Zendnames[(frameend-ZCRCE)&3])); | 
|     crc = 0; | 
|     do { | 
|         zsendline(*buf); crc = updcrc((0377 & *buf), crc); | 
|         buf++; | 
|     } while (--length>0); | 
|     xsendline(ZDLE); xsendline(frameend); | 
|     crc = updcrc(frameend, crc); | 
|   | 
|     crc = updcrc(0,updcrc(0,crc)); | 
|     zsendline(crc>>8); zsendline(crc); | 
|     if (frameend == ZCRCW) { | 
|         xsendline(XON);  flushmo(); | 
|     } | 
| } | 
|   | 
| void | 
| zsda32(const char *buf, size_t length, int frameend) | 
| { | 
|     int c; | 
|     unsigned long crc; | 
|     int i; | 
|     VPRINTF(3,("zsdat32: %d %s", (u32)length, Zendnames[(frameend-ZCRCE)&3])); | 
|   | 
|     crc = 0xFFFFFFFFL; | 
|     zsendline_s(buf,length); | 
|     for (; length; length--) { | 
|         c = *buf & 0377; | 
|         crc = UPDC32(c, crc); | 
|         buf++; | 
|     } | 
|     xsendline(ZDLE); xsendline(frameend); | 
|     crc = UPDC32(frameend, crc); | 
|   | 
|     crc = ~crc; | 
|     for (i=4; --i >= 0;) { | 
|         c=(int) crc; | 
|         if (c & 0140) | 
|             xsendline(lastsent = c); | 
|         else | 
|             zsendline(c); | 
|         crc >>= 8; | 
|     } | 
|     if (frameend == ZCRCW) { | 
|         xsendline(XON);  flushmo(); | 
|     } | 
| } | 
|   | 
| #if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ <= 4) | 
| #  undef DEBUG_BLOCKSIZE | 
| #endif | 
|   | 
| #ifdef DEBUG_BLOCKSIZE | 
| struct debug_blocksize { | 
|     int size; | 
|     long count; | 
| }; | 
| struct debug_blocksize blocksizes[]={ | 
|     {32,0}, | 
|     {64,0}, | 
|     {128,0}, | 
|     {256,0}, | 
|     {512,0}, | 
|     {1024,0}, | 
|     {2048,0}, | 
|     {4096,0}, | 
|     {8192,0}, | 
|     {0,0} | 
| }; | 
| static inline void | 
| count_blk(int size) | 
| { | 
|     int i; | 
|     for (i=0;blocksizes[i].size;i++) { | 
|         if (blocksizes[i].size==size) { | 
|             blocksizes[i].count++; | 
|             return; | 
|         } | 
|     } | 
|     blocksizes[i].count++; | 
| } | 
|   | 
| static void printout_blocksizes(void) __attribute__((__destructor__)); | 
| static void  | 
| printout_blocksizes(void)  | 
| { | 
|     int i; | 
|     for (i=0;blocksizes[i].size;i++) { | 
|         if (blocksizes[i].count) { | 
|             lsyslog(LOG_DEBUG,"%4d byte: %ld blocks\n", | 
|                    blocksizes[i].size,blocksizes[i].count); | 
|         } | 
|     } | 
|     if (blocksizes[i].count) { | 
|         lsyslog(LOG_DEBUG,"unk. byte: %ld blocks", | 
|                blocksizes[i].count); | 
|     } | 
| } | 
| #define COUNT_BLK(x) count_blk(x) | 
| #else | 
| #define COUNT_BLK(x) | 
| #endif | 
|   | 
| /* | 
|  * Receive array buf of max length with ending ZDLE sequence | 
|  *  and CRC.  Returns the ending character or error code. | 
|  *  NB: On errors may store length+1 bytes! | 
|  */ | 
| int | 
| zrdata(char *buf, int length, size_t *bytes_received) | 
| { | 
|     register int c; | 
|     register unsigned short crc; | 
|     register char *end; | 
|     register int d; | 
|   | 
|     *bytes_received=0; | 
|     if (Rxframeind == ZBIN32) | 
|         return zrdat32(buf, length, bytes_received); | 
|   | 
|     crc = 0;  end = buf + length; | 
|     while (buf <= end) { | 
|         if ((c = zdlread()) & ~0377) { | 
| crcfoo: | 
|             switch (c) { | 
|             case GOTCRCE: | 
|             case GOTCRCG: | 
|             case GOTCRCQ: | 
|             case GOTCRCW: | 
|                 {  | 
|                     d = c; | 
|                     c &= 0377; | 
|                     crc = updcrc(c, crc); | 
|                     if ((c = zdlread()) & ~0377) | 
|                         goto crcfoo; | 
|                     crc = updcrc(c, crc); | 
|                     if ((c = zdlread()) & ~0377) | 
|                         goto crcfoo; | 
|                     crc = updcrc(c, crc); | 
|                     if (crc & 0xFFFF) { | 
|                         zperr(badcrc); | 
|                         return ERROR; | 
|                     } | 
|                     *bytes_received = length - (end - buf); | 
|                     COUNT_BLK(*bytes_received); | 
|                     VPRINTF(3,("zrdata: %lu  %s", (unsigned long) (*bytes_received),  | 
|                             Zendnames[(d-GOTCRCE)&3])); | 
|                     return d; | 
|                 } | 
|             case GOTCAN: | 
|                 zperr(_("Sender Canceled")); | 
|                 return ZCAN; | 
|             case TIMEOUT: | 
|                 zperr(_("TIMEOUT")); | 
|                 return c; | 
|             default: | 
|                 zperr(_("Bad data subpacket")); | 
|                 return c; | 
|             } | 
|         } | 
|         *buf++ = c; | 
|         crc = updcrc(c, crc); | 
|     } | 
|     zperr(_("Data subpacket too long")); | 
|     return ERROR; | 
| } | 
|   | 
| static int | 
| zrdat32(char *buf, int length, size_t *bytes_received) | 
| { | 
|     register int c; | 
|     register unsigned long crc; | 
|     register char *end; | 
|     register int d; | 
|   | 
|     crc = 0xFFFFFFFFL;  end = buf + length; | 
|     while (buf <= end) { | 
|         if ((c = zdlread()) & ~0377) { | 
| crcfoo: | 
|             switch (c) { | 
|             case GOTCRCE: | 
|             case GOTCRCG: | 
|             case GOTCRCQ: | 
|             case GOTCRCW: | 
|                 d = c; | 
|                 c &= 0377; | 
|                 crc = UPDC32(c, crc); | 
|                 if ((c = zdlread()) & ~0377) | 
|                     goto crcfoo; | 
|                 crc = UPDC32(c, crc); | 
|                 if ((c = zdlread()) & ~0377) | 
|                     goto crcfoo; | 
|                 crc = UPDC32(c, crc); | 
|                 if ((c = zdlread()) & ~0377) | 
|                     goto crcfoo; | 
|                 crc = UPDC32(c, crc); | 
|                 if ((c = zdlread()) & ~0377) | 
|                     goto crcfoo; | 
|                 crc = UPDC32(c, crc); | 
|                 if (crc != 0xDEBB20E3) { | 
|                     zperr(badcrc); | 
|                     return ERROR; | 
|                 } | 
|                 *bytes_received = length - (end - buf); | 
|                 COUNT_BLK(*bytes_received); | 
|                 VPRINTF(3,("zrdat32: %lu %s", (unsigned long) *bytes_received,  | 
|                     Zendnames[(d-GOTCRCE)&3])); | 
|                 return d; | 
|             case GOTCAN: | 
|                 zperr(_("Sender Canceled")); | 
|                 return ZCAN; | 
|             case TIMEOUT: | 
|                 zperr(_("TIMEOUT")); | 
|                 return c; | 
|             default: | 
|                 zperr(_("Bad data subpacket")); | 
|                 return c; | 
|             } | 
|         } | 
|         *buf++ = c; | 
|         crc = UPDC32(c, crc); | 
|     } | 
|     zperr(_("Data subpacket too long")); | 
|     return ERROR; | 
| } | 
|   | 
| /* | 
|  * Read a ZMODEM header to hdr, either binary or hex. | 
|  *  eflag controls local display of non zmodem characters: | 
|  *    0:  no display | 
|  *    1:  display printing characters only | 
|  *    2:  display all non ZMODEM characters | 
|  *  On success, set Zmodem to 1, set Rxpos and return type of header. | 
|  *   Otherwise return negative on error. | 
|  *   Return ERROR instantly if ZCRCW sequence, for fast error recovery. | 
|  */ | 
| int | 
| zgethdr(char *hdr, int eflag, size_t *Rxpos) | 
| { | 
|     register int c, cancount; | 
|     unsigned int max_garbage; /* Max bytes before start of frame */ | 
|     size_t rxpos=0; /* keep gcc happy */ | 
|   | 
|     max_garbage = Zrwindow + Baudrate; | 
|     Rxframeind = Rxtype = 0; | 
|   | 
| startover: | 
|     cancount = 5; | 
| again: | 
|     /* Return immediate ERROR if ZCRCW sequence seen */ | 
|     switch (c = READLINE_PF(Rxtimeout)) { | 
|     case RCDO: | 
|     case TIMEOUT: | 
|         goto fifi; | 
|     case CAN: | 
| gotcan: | 
|         if (--cancount <= 0) { | 
|             c = ZCAN; goto fifi; | 
|         } | 
|         switch (c = READLINE_PF(1)) { | 
|         case TIMEOUT: | 
|             goto again; | 
|         case ZCRCW: | 
|             c = ERROR; | 
|         /* **** FALL THRU TO **** */ | 
|         case RCDO: | 
|             goto fifi; | 
|         default: | 
|             break; | 
|         case CAN: | 
|             if (--cancount <= 0) { | 
|                 c = ZCAN; goto fifi; | 
|             } | 
|             goto again; | 
|         } | 
|     /* **** FALL THRU TO **** */ | 
|     default: | 
| agn2: | 
|         if ( --max_garbage == 0) { | 
|             zperr(_("Garbage count exceeded")); | 
|             return(ERROR); | 
|         } | 
|         //if (eflag && ((c &= 0177) & 0140) && Verbose) | 
|         //    vchar(c); | 
|         //else if (eflag > 1 && Verbose) | 
|         //    vchar(c); | 
|         goto startover; | 
|     case ZPAD|0200:        /* This is what we want. */ | 
|     case ZPAD:        /* This is what we want. */ | 
|         break; | 
|     } | 
|     cancount = 5; | 
| splat: | 
|     switch (c = noxrd7()) { | 
|     case ZPAD: | 
|         goto splat; | 
|     case RCDO: | 
|     case TIMEOUT: | 
|         goto fifi; | 
|     default: | 
|         goto agn2; | 
|     case ZDLE:        /* This is what we want. */ | 
|         break; | 
|     } | 
|   | 
|     switch (c = noxrd7()) { | 
|     case RCDO: | 
|     case TIMEOUT: | 
|         goto fifi; | 
|     case ZBIN: | 
|         Rxframeind = ZBIN;  Crc32 = FALSE; | 
|         c =  zrbhdr(hdr); | 
|         break; | 
|     case ZBIN32: | 
|         Crc32 = Rxframeind = ZBIN32; | 
|         c =  zrbhdr32(hdr); | 
|         break; | 
|     case ZHEX: | 
|         Rxframeind = ZHEX;  Crc32 = FALSE; | 
|         c =  zrhhdr(hdr); | 
|         break; | 
|     case CAN: | 
|         goto gotcan; | 
|     default: | 
|         goto agn2; | 
|     } | 
|     rxpos = hdr[ZP3] & 0377; | 
|     rxpos = (rxpos<<8) + (hdr[ZP2] & 0377); | 
|     rxpos = (rxpos<<8) + (hdr[ZP1] & 0377); | 
|     rxpos = (rxpos<<8) + (hdr[ZP0] & 0377); | 
| fifi: | 
|     switch (c) { | 
|     case GOTCAN: | 
|         c = ZCAN; | 
|     /* **** FALL THRU TO **** */ | 
|     case ZNAK: | 
|     case ZCAN: | 
|     case ERROR: | 
|     case TIMEOUT: | 
|     case RCDO: | 
|         zperr(_("Got %s"), frametypes[c+FTOFFSET]); | 
|     /* **** FALL THRU TO **** */ | 
|     default: | 
|         if (c >= -3 && c <= FRTYPES) | 
|             VPRINTF(3,("zgethdr: %s %lx", frametypes[c+FTOFFSET], (unsigned long) rxpos)); | 
|         else | 
|             VPRINTF(3,("zgethdr: %d %lx", c, (unsigned long) rxpos)); | 
|     } | 
|     if (Rxpos) | 
|         *Rxpos=rxpos; | 
|     return c; | 
| } | 
|   | 
| /* Receive a binary style header (type and position) */ | 
| static int  | 
| zrbhdr(char *hdr) | 
| { | 
|     register int c, n; | 
|     register unsigned short crc; | 
|   | 
|     if ((c = zdlread()) & ~0377) | 
|         return c; | 
|     Rxtype = c; | 
|     crc = updcrc(c, 0); | 
|   | 
|     for (n=4; --n >= 0; ++hdr) { | 
|         if ((c = zdlread()) & ~0377) | 
|             return c; | 
|         crc = updcrc(c, crc); | 
|         *hdr = c; | 
|     } | 
|     if ((c = zdlread()) & ~0377) | 
|         return c; | 
|     crc = updcrc(c, crc); | 
|     if ((c = zdlread()) & ~0377) | 
|         return c; | 
|     crc = updcrc(c, crc); | 
|     if (crc & 0xFFFF) { | 
|         zperr(badcrc);  | 
|         return ERROR; | 
|     } | 
|     protocol = ZM_ZMODEM; | 
|     zmodem_requested=TRUE; | 
|     return Rxtype; | 
| } | 
|   | 
| /* Receive a binary style header (type and position) with 32 bit FCS */ | 
| static int | 
| zrbhdr32(char *hdr) | 
| { | 
|     register int c, n; | 
|     register unsigned long crc; | 
|   | 
|     if ((c = zdlread()) & ~0377) | 
|         return c; | 
|     Rxtype = c; | 
|     crc = 0xFFFFFFFFL; crc = UPDC32(c, crc); | 
| #ifdef DEBUGZ | 
|     VPRINTF(3,("zrbhdr32 c=%X  crc=%lX", c, crc)i); | 
| #endif | 
|   | 
|     for (n=4; --n >= 0; ++hdr) { | 
|         if ((c = zdlread()) & ~0377) | 
|             return c; | 
|         crc = UPDC32(c, crc); | 
|         *hdr = c; | 
| #ifdef DEBUGZ | 
|         VPRINTF(3,("zrbhdr32 c=%X  crc=%lX", c, crc)); | 
| #endif | 
|     } | 
|     for (n=4; --n >= 0;) { | 
|         if ((c = zdlread()) & ~0377) | 
|             return c; | 
|         crc = UPDC32(c, crc); | 
| #ifdef DEBUGZ | 
|         VPRINTF(3,("zrbhdr32 c=%X  crc=%lX", c, crc)); | 
| #endif | 
|     } | 
|     if (crc != 0xDEBB20E3) { | 
|         zperr(badcrc); | 
|         return ERROR; | 
|     } | 
|     protocol = ZM_ZMODEM; | 
|     zmodem_requested=TRUE; | 
|     return Rxtype; | 
| } | 
|   | 
|   | 
| /* Receive a hex style header (type and position) */ | 
| static int  | 
| zrhhdr(char *hdr) | 
| { | 
|     register int c; | 
|     register unsigned short crc; | 
|     register int n; | 
|   | 
|     if ((c = zgethex()) < 0) | 
|         return c; | 
|     Rxtype = c; | 
|     crc = updcrc(c, 0); | 
|   | 
|     for (n=4; --n >= 0; ++hdr) { | 
|         if ((c = zgethex()) < 0) | 
|             return c; | 
|         crc = updcrc(c, crc); | 
|         *hdr = c; | 
|     } | 
|     if ((c = zgethex()) < 0) | 
|         return c; | 
|     crc = updcrc(c, crc); | 
|     if ((c = zgethex()) < 0) | 
|         return c; | 
|     crc = updcrc(c, crc); | 
|     if (crc & 0xFFFF) { | 
|         zperr(badcrc); return ERROR; | 
|     } | 
|     switch ( c = READLINE_PF(1)) { | 
|     case 0215: | 
|         /* **** FALL THRU TO **** */ | 
|     case 015: | 
|          /* Throw away possible cr/lf */ | 
|         READLINE_PF(1); | 
|         break; | 
|     } | 
|     protocol = ZM_ZMODEM; | 
|     zmodem_requested=TRUE; | 
|     return Rxtype; | 
| } | 
|   | 
| /* Write a byte as two hex digits */ | 
| static void  | 
| zputhex(int c, char *pos) | 
| { | 
|     static char    digits[]    = "0123456789abcdef"; | 
|   | 
|     VPRINTF(9,("zputhex: %02X", c)); | 
|     pos[0]=digits[(c&0xF0)>>4]; | 
|     pos[1]=digits[c&0x0F]; | 
| } | 
|   | 
| void | 
| zsendline_init(void) | 
| { | 
|     int i; | 
|     for (i=0;i<256;i++) {     | 
|         if (i & 0140) | 
|             zsendline_tab[i]=0; | 
|         else { | 
|             switch(i) | 
|             { | 
|             case ZDLE: | 
|             case XOFF: /* ^Q */ | 
|             case XON: /* ^S */ | 
|             case (XOFF | 0200): | 
|             case (XON | 0200): | 
|                 zsendline_tab[i]=1; | 
|                 break; | 
|             case 020: /* ^P */ | 
|             case 0220: | 
|                 if (turbo_escape) | 
|                     zsendline_tab[i]=0; | 
|                 else | 
|                     zsendline_tab[i]=1; | 
|                 break; | 
|             case 015: | 
|             case 0215: | 
|                 if (Zctlesc) | 
|                     zsendline_tab[i]=1; | 
|                 else if (!turbo_escape) | 
|                     zsendline_tab[i]=2; | 
|                 else  | 
|                     zsendline_tab[i]=0; | 
|                 break; | 
|             default: | 
|                 if (Zctlesc) | 
|                     zsendline_tab[i]=1; | 
|                 else | 
|                     zsendline_tab[i]=0; | 
|             } | 
|         } | 
|     } | 
| } | 
|   | 
|   | 
|   | 
| /* Store pos in Txhdr */ | 
| void  | 
| stohdr(size_t pos) | 
| { | 
|     long lpos=(long) pos; | 
|     Txhdr[ZP0] = lpos; | 
|     Txhdr[ZP1] = lpos>>8; | 
|     Txhdr[ZP2] = lpos>>16; | 
|     Txhdr[ZP3] = lpos>>24; | 
| } | 
|   | 
| /* Recover a long integer from a header */ | 
| long | 
| rclhdr(char *hdr) | 
| { | 
|     long l; | 
|   | 
|     l = (hdr[ZP3] & 0377); | 
|     l = (l << 8) | (hdr[ZP2] & 0377); | 
|     l = (l << 8) | (hdr[ZP1] & 0377); | 
|     l = (l << 8) | (hdr[ZP0] & 0377); | 
|     return l; | 
| } | 
|   | 
| /* End of zm.c */ |