/* Copyright (C) Magna Carta Software, Inc. 1988-1990. All Rights Reserved. C COMMUNICATIONS TOOLKIT CCTZUTIL.C -- ZMODEM utility routines used by send and receive for CCT. */ #define CCT_DEVELOPMENT #include #include #include static short NEAR_ z_gethex_(COMM_PORT *p); static void NEAR_ z_puthex_(COMM_PORT *p, short c); static short NEAR_ z_s_bh32_(COMM_PORT *p, char *hdr, short htype); static short NEAR_ z_s_data32_(COMM_PORT *p, char *buf, short length, short frameend); static short NEAR_ z_r_bh32_(COMM_PORT *p, char *hdr); static short NEAR_ z_r_bh_(COMM_PORT *p, char *hdr); static short NEAR_ z_r_hh_(COMM_PORT *p, char *hdr); #include int Beenhereb4; /* How many times we've been ZRPOS'd same place */ #if 0 /* Local screen character display function */ void z_cout(COMM_PORT *p, short c) { if (((Z_PARMS *) p->ppb)->flags & 1) (*p->c_out)(p, c); } #endif /* Z_ENCODEC_ -- Encode a character to send with ZMODEM. Escape XON, XOFF. Escape CR following @ (Telenet net escape) Return Value: 0 -- success; */ short z_encodec_(COMM_PORT *p, short c) { static lastsent; Z_PARMS *z = p->ppb; /* IF NOT A CONTROL CHARACTER, SEND IMMEDIATELY */ if (c & 0X60) c_putc(p, lastsent = c); else switch (c &= 0XFF) { case ZDLE: c_putc(p, ZDLE); c_putc(p, lastsent = (c ^= 0X40)); break; case CR: case CR | 0X80: if (!isescctl(z) && (lastsent & 0X7F) != '@') goto sendit; /* **** FALL THRU TO **** */ case DLE: case XON: case XOFF: case DLE | 0X80: case XON | 0X80: case XOFF | 0X80: c_putc(p, ZDLE); c ^= 0X40; sendit: c_putc(p, lastsent = c); break; default: if (isescctl(z) && !(c & 0X60)) { c_putc(p, ZDLE); c ^= 0X40; } c_putc(p, lastsent = c); } return (0); } /* Z_R_BH32_ -- Receive a binary style header (type and position) with 32 bit FCS. */ static short NEAR_ z_r_bh32_(COMM_PORT *p, char *hdr) { short c, n; unsigned long crc; Z_PARMS *z = p->ppb; if ((c = z_decodec_(p)) & ~0XFF) return (c); z->rxtype = c; crc = 0xFFFFFFFFL; crc = UPDC32(c, crc); for (n=4; --n >= 0; ++hdr) { if ((c = z_decodec_(p)) & ~0XFF) return (c); crc = UPDC32(c, crc); *hdr = (char) c; } for (n=4; --n >= 0;) { if ((c = z_decodec_(p)) & ~0XFF) return (c); crc = UPDC32(c, crc); } if (crc != 0xDEBB20E3L) return (EOF); return (z->rxtype); } /* Z_R_BH_ -- Receive a binary style header (type and position) */ static short NEAR_ z_r_bh_(COMM_PORT *p, char *hdr) { WORD crc; short int c, n; Z_PARMS *z = p->ppb; /* RETURN IMMEDIATELY ON ERROR */ if ((c = z_decodec_(p)) & ~0XFF) return (c); z->rxtype = c; crc = updcrc_(c, 0); for (n=4; --n >= 0; ++hdr) { if ((c = z_decodec_(p)) & ~0XFF) return (c); crc = updcrc_(c, crc); *hdr = (char) c; } if ((c = z_decodec_(p)) & ~0XFF) return (c); crc = updcrc_(c, crc); if ((c = z_decodec_(p)) & ~0XFF) return (c); crc = updcrc_(c, crc); if (crc & 0xFFFF) return (EOF); return (z->rxtype); } /* Z_R_HH_ -- Receive a hex style header (type and position) */ static short NEAR_ z_r_hh_(COMM_PORT *p, char *hdr) { Z_PARMS *z = p->ppb; short c, n; WORD crc; /* GET THE PACKET TYPE BYTE */ if ((c = z_gethex_(p)) < 0) return (c); z->rxtype = c; crc = updcrc_(c, 0); /* GET THE FOUR BYTES OF THE HEADER */ for (n=4; --n >= 0; ++hdr) { if ((c = z_gethex_(p)) < 0) return (c); crc = updcrc_(c, crc); *hdr = (char) c; } if ((c = z_gethex_(p)) < 0) return (c); crc = updcrc_(c, crc); if ((c = z_gethex_(p)) < 0) return (c); crc = updcrc_(c, crc); if (crc & 0xFFFF) return (EOF); switch ( c = c_waitc(p, EOF, 100)) { case 0X8D: case CR: /* Throw away possible cr/lf */ c = c_waitc(p, EOF, 1); /* if (c == LF); */ break; case USER_CANCELLED: return (USER_CANCELLED); default: break; } return (z->rxtype); } /* Z_R_H_ -- 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. */ short z_r_h_(COMM_PORT *p, char *hdr, short eflag) { Z_PARMS *z = p->ppb; short cancount; short n = 0, ret = EOF, DONE = FALSE, lastchar = 0; n = (short) (z->rxwindow + z->speed); /* Max bytes before start of frame */ z->rxframeind = z->rxtype = 0; cancount = 5; /* WAIT FOR PACKET SENTINAL STRING 'ZPAD, ZDLE' */ for (; n-- > 0 && !DONE;) { switch(ret = c_waitc(p, EOF, z->rxtimeout)) { case ZPAD: case ZPAD | 0X80: ret = ZPAD; break; case ZDLE: if (lastchar == ZPAD) { /* IF 'ZPAD' RECEIVED LOOK FOR ZDLE */ ret = 0; DONE = TRUE; } else if (--cancount <= 0) DONE = ret = ZCAN; break; case USER_CANCELLED: ret = USER_CANCELLED; DONE = TRUE; break; case EOF: DONE = TRUE; break; default: break; } lastchar = ret; } /* IF SUCCESS, GET HEADER TYPE CHARACTER */ if (!ret) { z->crc32 = FALSE; switch ((ret = c_waitc(p, EOF, z->rxtimeout))) { case ZBIN: /* binary header */ z->rxframeind = ZBIN; ret = z_r_bh_(p, hdr); break; case ZBIN32: /* binary header */ z->crc32 = z->rxframeind = ZBIN32; ret = z_r_bh32_(p, hdr); break; case ZHEX: /* hex. header */ z->rxframeind = ZHEX; ret = z_r_hh_(p, hdr); break; default: /* nothing, user cancelled...*/ break; } z->rxpos = hdr[ZP3] & 0XFF; z->rxpos = (z->rxpos<<8) + (hdr[ZP2] & 0XFF); z->rxpos = (z->rxpos<<8) + (hdr[ZP1] & 0XFF); z->rxpos = (z->rxpos<<8) + (hdr[ZP0] & 0XFF); } return (ret); } /* Z_DECODEC_ -- Read a byte, checking for ZMODEM escape encoding including CAN*5 which represents a quick abort */ short z_decodec_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short c, i; short DONE = FALSE; do { /* IF NOT A CONTROL CHARACTER, RETURN IMMEDIATELY */ if ((c = c_waitc(p, EOF, z->rxtimeout)) & 0X60) return (c); switch (c) { case ZDLE: DONE = TRUE; break; case XOFF: /* screen out flow control chars. */ case XOFF | 0X80: case XON: case XON | 0X80: break; default: if (isescctl(z) && !(c & 0X60)) break; return (c); } } while (!DONE); /* ONLY COME HERE IF ZDLE DETECTED */ do { for (i=0; i < 4; i++) { c = c_waitc(p, EOF, z->rxtimeout); if (c < 0) return (c); if (c != CAN) break; } switch (c) { case CAN: return (GOTCAN); case ZCRCE: case ZCRCG: case ZCRCQ: case ZCRCW: return (c | GOTOR); case ZRUB0: return (0X7F); case ZRUB1: return (0XFF); case XOFF: case XOFF | 0X80: case XON: case XON | 0X80: break; default: if (isescctl(z) && !(c & 0X60)) break; if ((c & 0X60) == 0X40) return (c ^ 0X40); break; } } while (1); } /* Z_FILBUF_ -- File a ZMODEM send buffer with characters. */ unsigned short z_filbuf_(COMM_PORT *p) { Z_PARMS *z = p->ppb; char *b = z->txbuf; short c; WORD i; for (i=0; i < z->blklen; i++) { c = c_fgetc_(p->x); if (c != EOF) *b++ = (char) c; else { z->f_eof = TRUE; break; } } return (i); } /* Z_GETHEX_ -- Decode two lower case hex digits into an 8 bit byte value */ static short NEAR_ z_gethex_(COMM_PORT *p) { short c, n; if ((c = noxrd7_(p)) < 0) return (c); n = c - '0'; if (n > 9) n -= ('a' - ':'); if (n & ~0xF) return (EOF); if ((c = noxrd7_(p)) < 0) return (c); c -= '0'; if (c > 9) c -= ('a' - ':'); if (c & ~0xF) return (EOF); c += (n<<4); return (c); } /* NOXRD7_ -- read a character with timeout. Eat bit 8, XON and XOFF characters. */ short noxrd7_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short c; for (;;) { if ((c = c_waitc(p, EOF, z->rxtimeout)) < 0) return (c); switch (c &= 0X7F) { case XON: case XOFF: continue; default: if (isescctl(z) && !(c & 0X60)) continue; case CR: case LF: case ZDLE: return (c); } } } /* STOHDR_ -- Store long integer pos in txhdr */ void stohdr_(Z_PARMS *z, long pos) { z->txhdr[ZP0] = (char) pos; z->txhdr[ZP1] = (char) (pos >> 8); z->txhdr[ZP2] = (char) (pos >> 16); z->txhdr[ZP3] = (char) (pos >> 24); } /* Z_S_FIN_ -- Send ZFIN packet. */ short z_s_fin_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short ret; short DONE = FALSE; for (;!DONE;) { stohdr_(z, 0L); z_s_hh_(p, ZFIN, z->txhdr); switch ((ret = z_r_h_(p, z->rxhdr, 0))) { case ZFIN: c_putc(p, 'O'); c_putc(p, 'O'); ret = ZCOMPL; case ZCAN: case EOF: DONE = TRUE; break; } } return (ret); } /* Z_ACK_FIN_ -- ACK a ZFIN packet by sending a ZFIN and waiting for two 'O's. */ short z_ack_fin_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short i; short DONE = FALSE, ret = 0; for (i=0;i < 3 && !DONE;i++) { c_rxflush(p, EOF); stohdr_(z, 0L); z_s_hh_(p, ZFIN, z->txhdr); switch (ret = c_waitc(p, ZFIN, 1000)) { case ZFIN: c_waitc(p, ZFIN, 500); /* read second 'O' */ DONE = TRUE; break; case ZCAN: DONE = TRUE; break; case EOF: default: break; } } return (ret); } /* Z_S_ABORT_ -- Send ZABORT packet. */ short z_s_abort_(COMM_PORT *p) { Z_PARMS *z = p->ppb; short ret; short DONE = FALSE; for (;!DONE;) { stohdr_(z, 0L); z_s_hh_(p, ZABORT, z->txhdr); switch ((ret = z_r_h_(p, z->rxhdr, 0))) { case ZFIN: c_putc(p, 'O'); c_putc(p, 'O'); ret = ZCOMPL; case ZCAN: case EOF: DONE = TRUE; break; } } return (ret); } /* Z_INIT -- Initialize for ZMODEM transfers. Parameters: COMM_PORT *p -- pointer to the port; WORD capas -- capabilities (bit-wise ORing of our capabilities); */ Z_PARMS *z_init(COMM_PORT *p, WORD l_cap, WORD l_conv, WORD l_manag) { Z_PARMS *z; if (p->ppb != NULL) return (NULL); else { p->ppb = memcalloc(1, sizeof(Z_PARMS)); z = p->ppb; z->rxtimeout = 6000; z->cmdtries = 11; z->speed = get_speed(p); z->blklen = 128; z->rxwindow = 1400; z->x = p->x; z->l_cap = (BYTE) l_cap; z->l_conv = (BYTE) l_conv; z->l_manag = (BYTE) l_manag; z->retries = 10; } return (p->ppb); } /* Z_DEINIT -- Free memory used by ZMODEM file transfer. */ short z_deinit(COMM_PORT *p) { short ret = EOF; if (p->ppb != NULL) { memfree(p->ppb); p->ppb = NULL; ret = 0; } return (ret); } /* Z_S_HH_ -- Send ZMODEM HEX header hdr of type 'htype' */ short z_s_hh_(COMM_PORT *p, short htype, const char *hdr) { Z_PARMS *z = p->ppb; short n; WORD crc; c_putc(p, ZPAD); c_putc(p, ZPAD); c_putc(p, ZDLE); c_putc(p, ZHEX); z_puthex_(p, htype); z->crc32t = 0; crc = updcrc_(htype, 0); for (n=4; --n >= 0; ++hdr) { z_puthex_(p, *hdr); crc = updcrc_((0XFF & *hdr), crc); } crc = updcrc_(0, updcrc_(0,crc)); z_puthex_(p, crc>>8); z_puthex_(p, crc); /* MAKE IT PRINTABLE ON REMOTE MACHINE */ c_putc(p, CR); c_putc(p, 0X8A); /* SEND AN XON IN CASE LINE NOISE XOFF HAS STOPPED DATA FLOW */ if (htype != ZFIN && htype != ZACK) c_putc(p, XON); return (0); } /* Z_PUTHEX_ -- Send a binary value as two hex digits. */ static void NEAR_ z_puthex_(COMM_PORT *p, short c) { static char digits[] = "0123456789abcdef"; c_putc(p, digits[(c & 0XF0)>>4]); c_putc(p, digits[c & 0XF]); } /* Z_S_CMD_ -- Send command and related info. */ short z_s_cmd_(COMM_PORT *p, char *buf, short blen) { Z_PARMS *z = p->ppb; short c; z->errors = 0; for (;;) { stohdr_(z, (long) z->cmdnum); z->txhdr[ZF0] = (char) z->cmdack1; z_s_bh_(p, ZCOMMAND, z->txhdr); z_s_data_(p, buf, blen, ZCRCW); listen: z->rxtimeout = 10000; /* Ten second wait for resp. */ c = z_r_h_(p, z->rxhdr, 1); switch (c) { case ZRINIT: goto listen; /* CAF 8-21-87 */ case EOF: if (++z->errors > z->cmdtries) return (EOF); continue; case ZCAN: case ZABORT: case ZFIN: case ZSKIP: case ZRPOS: return (EOF); default: if (++z->errors > 20) return (EOF); continue; case ZCOMPL: return (0); case ZRQINIT: /* printf("******** RZ *******"); */ goto listen; } } } /* Z_S_BH32_ -- Send ZMODEM 32-bit CRC binary header hdr of type 'type' Return Value: 0 -- success; */ static short NEAR_ z_s_bh32_(COMM_PORT *p, char *hdr, short htype) { short n; unsigned long crc; c_putc(p, ZBIN32); z_encodec_(p, htype); crc = 0xFFFFFFFFL; crc = UPDC32(htype, crc); for (n=4; --n >= 0; ++hdr) { crc = UPDC32((0XFF & *hdr), crc); z_encodec_(p, *hdr); } crc = ~crc; for (n=4; --n >= 0;) { z_encodec_(p, (short) crc); /* crc >>= 8; */ crc = (crc >> 8) & 0XFFFFFFFFL; } return (0); } /* Z_S_BH_ -- Send ZMODEM binary header hdr of type 'type' Parameters; COMM_PORT * -- ptr. to the port; htype -- header type; hdr -- contents of header; Return Value: 0 -- success; */ short z_s_bh_(COMM_PORT *p, short htype, char *hdr) { Z_PARMS *z = p->ppb; short n; WORD crc; /* IF NULLS REQUIRED TO MARK TIME, SEND THEM */ if (htype == ZDATA) for (n = z->nulls; --n >=0; ) c_putc(p, 0); /* PACKET START SEQUENCE */ c_putc(p, ZPAD); c_putc(p, ZDLE); if (z->txfcs32) z_s_bh32_(p, hdr, htype); else { c_putc(p, ZBIN); z_encodec_(p, htype); crc = updcrc_(htype, 0); for (n=4; --n >= 0; ++hdr) { z_encodec_(p, *hdr); crc = updcrc_((0XFF & *hdr), crc); } crc = updcrc_(0, updcrc_(0,crc)); z_encodec_(p, crc>>8); z_encodec_(p, crc); } return (0); } /* Z_S_DATA32_ -- Send the data portion of a ZMODEM 32-bit CRC frame. Parmaeters: COMM_PORT *p -- pointer to the port; buf -- data to send; length -- length of buf; frameend -- frame terminator; */ static short NEAR_ z_s_data32_(COMM_PORT *p, char *buf, short length, short frameend) { short c; unsigned long crc; crc = 0xFFFFFFFFL; for (;--length >= 0; ++buf) { c = *buf & 0XFF; if (c & 0X60) c_putc(p, c); else z_encodec_(p, c); crc = UPDC32(c, crc); } c_putc(p, ZDLE); c_putc(p, frameend); crc = UPDC32(frameend, crc); crc = ~crc; for (length=4; --length >= 0;) { z_encodec_(p, (short) crc); /* crc >>= 8; */ crc = (crc >> 8) & 0XFFFFFFFFL; } return (0); } /* Z_S_DATA_ -- Send the data portion of a ZMODEM frame. */ short z_s_data_(COMM_PORT *p, char *buf, short length, short frameend) { WORD crc; Z_PARMS *z = p->ppb; if (z->txfcs32) z_s_data32_(p, buf, length, frameend); else { crc = 0; for (;--length >= 0; ++buf) { z_encodec_(p, *buf); crc = updcrc_((*buf & 0XFF), crc); } c_putc(p, ZDLE); c_putc(p, frameend); crc = updcrc_(frameend, crc); crc = updcrc_(0, updcrc_(0,crc)); z_encodec_(p, crc>>8); z_encodec_(p, crc); } if (frameend == ZCRCW) c_putc(p, XON); return (0); } /* RCLHDR_ -- Recover a long integer from a header */ unsigned 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); } /* Z_SYNC_ -- When sending, this routine is called if the receiver issues a query. Respond and get back in sync with receiver. */ short z_sync_(COMM_PORT *p, short flag) { short c, ret; Z_PARMS *z = p->ppb; XFER *x = p->x; short DONE = FALSE; for (;!DONE;) { c = z_r_h_(p, z->rxhdr, 0); switch (c) { case ZCAN: case ZABORT: case ZFIN: DONE = TRUE; ret = EOF; break; case ZRPOS: /* Dump buffered modem's buffer here */ if (fseekbuf_(x, z->rxpos, SEEK_SET)) return (EOF); z->f_eof = 0; z->bytcnt = z->lrxpos = z->txpos = z->rxpos; if (z->lastsync == z->rxpos) { if (++Beenhereb4 > 4 && z->blklen > 32) z->blklen /= 2; } z->lastsync = z->rxpos; DONE = TRUE; ret = c; break; case ZACK: z->lrxpos = z->rxpos; if (flag || z->txpos == z->rxpos) { DONE = TRUE; ret = ZACK; break; } continue; case ZRINIT: case ZSKIP: DONE = TRUE; ret = c; break; case EOF: default: z_s_bh_(p, ZNAK, z->txhdr); continue; } } return (ret); }