/*
 *   I Z M . C
 *    ZMODEM protocol primitives
 *  Copyright 2002 Omen Technology INC All Rights Reserved
 *
 * Entry point Functions:
 *      zsbhdr(type, hdr) send binary header
 *      zshhdr(type, hdr) send hex header
 *      zgethdr() receive header - binary or hex
 *      zsdata(buf, len, frameend) send data
 *      zrdata(buf, len) receive data
 *      stohdr(pos) store position data in Txhdr
 *      long rclhdr(hdr) recover position offset from header
 */

// COLETTI GLOBAL VARS, MISC
//#define FLUSH
#define ZSTAB
#include "stdio.h"
#include "izm.h"
#include "icrctab.c"
//

#ifndef UNSL
#define UNSL
#endif

#ifndef XEDCRC32
int zrdat32(), zrbhdr32();
#endif

/* Globals used in ISZ and IZM functions */
extern char Rxhdr[4];          /* Received header */
extern char Txhdr[8];          /* Transmitted header */
extern int  Zctlesc;           /* Encode control characters */
extern int  Zrwindow;          /* RX window size (controls garbage count) */
extern int  Rxtimeout;
extern int  Txfcs32;           /* TURE means send binary frames with 32 bit FCS */
extern long bytcnt;
extern long Rxpos;             /* Received file position */
extern long Txpos;             /* Transmitted file position */

/* Globals used by IZM functions */
int Rxframeind;         /* ZBIN ZBIN32, or ZHEX type of frame received */
int Rxtype;             /* Type of header received */
int Rxcount;            /* Count of data bytes received */
int Crc32t;             /* Display flag indicating 32 bit CRC being sent */

#ifdef ZSTAB
unsigned char zstable[256] = {
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0120,0121,0,0123,0,0,0,0, 0130,0,0,0,0,0,0,0,

        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,

        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0320,0321,0,0323,0,0,0,0, 0,0,0,0,0,0,0,0,

        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
        0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,
#ifdef NOFF
        ZRUB1
#else
        0
#endif
};
#endif

#ifndef XEDCRC32
/* Send ZMODEM binary header hdr of type type */
void
zsbh32(hdr, type)
register char *hdr;
{
        register int n;
        register UNSL 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;
        }
}
#endif /* XEDCRC32 */

/* Send ZMODEM binary header hdr of type type */
void
zsbhdr(type, hdr)
register char *hdr;
{
        register int n;
        register unsigned short crc;

        xsendline(ZPAD); xsendline(ZDLE);

#ifndef XEDCRC32
        Crc32t=Txfcs32;
        if (Crc32t)
                zsbh32(hdr, type);
        else
#endif
        {
                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);
        }
}

/* Send ZMODEM HEX header hdr of type type */
void
zshhdr(type, hdr)
register char *hdr;
{
        register int n;
        register unsigned short crc;

        sendline(ZPAD); sendline(ZPAD); sendline(ZDLE); sendline(ZHEX);
        zputhex(type);
#ifndef XEDCRC32
        Crc32t = 0;
#endif

        crc = updcrc(type, 0);
        for (n=4; --n >= 0; ++hdr) {
                zputhex(*hdr); crc = updcrc((0377 & *hdr), crc);
        }
        crc = updcrc(0,updcrc(0,crc));
        zputhex(crc>>8); zputhex(crc);

        /* Make it printable on remote machine */
        sendline(015); sendline(0212);
        /*
         * Uncork the remote in case a fake XOFF has stopped data flow
         */
        if (type != ZFIN && type != ZACK)
                sendline(021);
#ifdef FLUSH
        flushmo();
#endif
}

/*
 * Send binary array buf of length length, with ending ZDLE sequence frameend
 */

#ifndef XEDCRC32
void
zsda32(buf, length, frameend)
char *buf;
register length;
{
        register char *p;
        register int c;
        register int d;
        register unsigned long crc;
        register unsigned long *pcrctab;

        p = (char *) buf;
        pcrctab = icr3tab;

        crc = 0xFFFFFFFFL;

        for (;--length >= 0;) {
                c = *p++ & 0377;
                crc = pcrctab[(crc ^ c) & 0xff] ^ ((crc >> 8) & 0x00FFFFFF);

                if (d = zstable[c]) {
                    sendline(ZDLE);
                    c = d;
                  } //end zstable if
                sendline(c);

        }  //end for loop

        xsendline(ZDLE); xsendline(frameend);
        crc = UPDC32(frameend, crc);

        crc = ~crc;
        for (length=4; --length >= 0;) {
                zsendline((int)crc);  crc >>= 8;
        }

        if (frameend == ZCRCW) {
                xsendline(XON);
#ifdef FLUSH
                flushmo();
#endif
        }
#ifdef FLUSH
        else if (frameend != ZCRCG)
                flushmoc();
#endif
}
#endif /* XEDCRC32 */

/*
 * 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!
 */
zrdata(buf, length)
register char *buf;
{
        register int c;
        register unsigned short crc;
        register char *end;
        register int d;

#ifndef XEDCRC32
        if (Rxframeind == ZBIN32)
                return zrdat32(buf, length);
#endif

        crc = 0;
        Rxcount = 0;  end = buf + length;
        while (buf <= end) {
                if ((c = zdlread()) & ~0377) {
crcfoo:
                        switch (c) {
                        case GOTCRCE:
                        case GOTCRCG:
                        case GOTCRCQ:
                        case GOTCRCW:
                                crc = updcrc((d=c)&0377, crc);
                                if ((c = zdlread()) & ~0377)
                                        goto crcfoo;
                                crc = updcrc(c, crc);
                                if ((c = zdlread()) & ~0377)
                                        goto crcfoo;
                                crc = updcrc(c, crc);
                                if (crc & 0xFFFF) {
                                        return ERROR;
                                }
                                Rxcount = length - (end - buf);
                                return d;
                        case GOTCAN:
                                return ZCAN;
                        case TIMEOUT:
                                return c;
                        default:
                                return c;
                        }
                }
                *buf++ = c;
                crc = updcrc(c, crc);
        }
        return ERROR;
}

#ifndef XEDCRC32
zrdat32(buf, length)
register char *buf;
{
        register int c;
        register unsigned long crc;
        register char *end;
        register int d;

        crc = 0xFFFFFFFFL;
        Rxcount = 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 = 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) {
                                        return ERROR;
                                }
                                Rxcount = length - (end - buf);
                                return d;
                        case GOTCAN:
                                return ZCAN;
                        case TIMEOUT:
                                return c;
                        default:
                                return c;
                        }
                }
                *buf++ = c;
                crc = UPDC32(c, crc);
        }
        return ERROR;
}
#endif  /* XEDCRC32 */


/*
 * Read a ZMODEM header to hdr, either binary or hex.
 *  On success, set Rxpos and return type of header.
 *   Otherwise return negative on error.
 *   Return ERROR instantly if ZCRCW sequence, for fast error recovery.
 */
zgethdr()
{
        register int c, n, cancount;

        n = Zrwindow + 19200;   /* Max bytes before start of frame */
        Rxframeind = Rxtype = 0;

startover:
        cancount = 5;
again:
        /* Return immediate ERROR if ZCRCW sequence seen */
        switch (c = readline(Rxtimeout)) {
        case TIMEOUT:
                goto fifi;
        case CAN:
gotcan:
                if (--cancount <= 0) {
                        c = ZCAN; goto fifi;
                }
                switch (c = readline(1)) {
                case TIMEOUT:
                        goto again;
                case ZCRCW:
                        c = ERROR;
                /*FALLTHRU*/
                        goto fifi;
                default:
                        break;
                case CAN:
                        if (--cancount <= 0) {
                                c = ZCAN; goto fifi;
                        }
                        goto again;
                }
        /*FALLTHRU*/
        default:
agn2:
                if ( --n == 0) {
                        return(ERROR);
                }
                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 TIMEOUT:
                goto fifi;
        default:
                goto agn2;
        case ZDLE:              /* This is what we want. */
                break;
        }

        switch (c = noxrd7()) {
        case TIMEOUT:
                goto fifi;
        case ZBIN:
                Rxframeind = ZBIN;
                c =  zrbhdr();
                break;
#ifndef XEDCRC32
        case ZBIN32:
                Rxframeind = ZBIN32;
                c =  zrbhdr32();
                break;
#endif
        case ZHEX:
                Rxframeind = ZHEX;
                c =  zrhhdr();
                break;
        case CAN:
                goto gotcan;
        default:
                goto agn2;
        }
        Rxpos = Rxhdr[ZP3] & 0377;
        Rxpos = (Rxpos<<8) + (Rxhdr[ZP2] & 0377);
        Rxpos = (Rxpos<<8) + (Rxhdr[ZP1] & 0377);
        Rxpos = (Rxpos<<8) + (Rxhdr[ZP0] & 0377);
fifi:
        switch (c) {
        case GOTCAN:
                c = ZCAN;
        /*FALLTHRU*/
        default:
                ;
        }
        return c;
}

/* Receive a binary style header (type and position) */
zrbhdr()
{
        register char *hdr = Rxhdr;
        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) {
                return ERROR;
        }
        return Rxtype;
}

#ifndef XEDCRC32
/* Receive a binary style header (type and position) with 32 bit FCS */
zrbhdr32()
{
        register char *hdr = Rxhdr;
        register int c, n;
        register unsigned long crc;

        if ((c = zdlread()) & ~0377)
                return c;
        Rxtype = c;
        crc = 0xFFFFFFFFL; crc = UPDC32(c, crc);
        for (n=4; --n >= 0; ++hdr) {
                if ((c = zdlread()) & ~0377)
                        return c;
                crc = UPDC32(c, crc);
                *hdr = c;
        }
        for (n=4; --n >= 0;) {
                if ((c = zdlread()) & ~0377)
                        return c;
                crc = UPDC32(c, crc);
        }
        if (crc != 0xDEBB20E3) {
                return ERROR;
        }
        return Rxtype;
}
#endif  /* XEDCRC32 */


/* Receive a hex style header (type and position) */
zrhhdr()
{
        register int c;
        register char *hdr = Rxhdr;
        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) {
                return ERROR;
        }
        switch ( c = readline(1)) {
        case 0215:
        case 015:
                /* Throw away possible cr/lf */
                c = readline(1);
        }
        return Rxtype;
}

/* Send a byte as two hex digits */
void
zputhex(c)
register int c;
{
        static char     digits[]        = "0123456789abcdef";

        sendline(digits[(c&0xF0)>>4]);
        sendline(digits[(c)&0xF]);
}

/*
 * Send character c with ZMODEM escape sequence encoding.
 *  Escape DLE, XON, XOFF. 
 */
void
zsendline(c)
register c;
#ifdef ZSTAB
{
        register d;

        c &= 0377;
        if (d = zstable[c]) {
                sendline(ZDLE); c = d;
        }
        sendline(c);
}
#else
{
#ifdef NOFF
        c &= 0377;
        if (c == 0377) {
                        xsendline(ZDLE);
                        c = ZRUB1;      /* Passes the 0140 mask below !! */
        }
#endif
        /* Quick check for non control characters */
        if (c & 0140)
                xsendline(c);
        else {
                switch (c &= 0377) {
                case ZDLE:
                case 020:
                case 021:
                case 023:
                case 0220:
                case 0221:
                case 0223:
                        xsendline(ZDLE);
                        c ^= 0100;
                        xsendline(c);
                        break;
                default:
                        if (Zctlesc) {
                                xsendline(ZDLE);
                                c ^= 0100;
                        }
                        xsendline(c);
                }
        }
}
#endif

/* Decode two lower case hex digits into an 8 bit byte value */
zgethex()
{
        register int c;

        c = zgeth1();
        return c;
}
zgeth1()
{
        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;
}

/*
 * Read a byte, checking for ZMODEM escape encoding
 *  including CAN*5 which represents a quick abort
 */
zdlread()
{
        register int c;

again:
        /* Quick check for non control characters */
        if ((c = readline(Rxtimeout)) & 0140)
                return c;
        switch (c) {
        case ZDLE:
                break;
        case 023:
        case 0223:
        case 021:
        case 0221:
                goto again;
        default:
                if (Zctlesc && !(c & 0140)) {
                        goto again;
                }
                return c;
        }
again2:
        if ((c = readline(Rxtimeout)) < 0)
                return c;
        if (c == CAN && (c = readline(Rxtimeout)) < 0)
                return c;
        if (c == CAN && (c = readline(Rxtimeout)) < 0)
                return c;
        if (c == CAN && (c = readline(Rxtimeout)) < 0)
                return c;
        switch (c) {
        case CAN:
                return GOTCAN;
        case ZCRCE:
        case ZCRCG:
        case ZCRCQ:
        case ZCRCW:
                return (c | GOTOR);
                /* DO NOT REMOVE THIS ENTRY!!! */
        case ZRUB0:
                return 0177;
                /* DO NOT REMOVE THIS ENTRY!!! (used for telnet) */
        case ZRUB1:
                return 0377;
        case 023:
        case 0223:
        case 021:
        case 0221:
                goto again2;
        default:
                if (Zctlesc && ! (c & 0140)) {
                        goto again2;
                }
                if ((c & 0140) ==  0100)
                        return (c ^ 0100);
                break;
        }
        return ERROR;
}

/*
 * Read a character from the modem line with timeout.
 *  Eat parity, XON and XOFF characters.
 */
noxrd7()
{
        register int c;

        for (;;) {
                if ((c = readline(Rxtimeout)) < 0)
                        return c;
                switch (c &= 0177) {
                case XON:
                case XOFF:
                        continue;
                default:
                        if (Zctlesc && !(c & 0140))
                                continue;
                /*FALLTHRU*/
                case '\r':
                case '\n':
                case ZDLE:
                        return c;
                }
        }
}

/* Store long integer pos in Txhdr */
void
stohdr(pos)
long pos;
{
        Txhdr[ZP0] = pos;
        Txhdr[ZP1] = pos>>8;
        Txhdr[ZP2] = pos>>16;
        Txhdr[ZP3] = pos>>24;
}

/* Recover a long integer from a header */
long
rclhdr(hdr)
register char *hdr;
{
        register 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;
}

/* send cancel string to get the other end to shut up */
void
canit()
{
        static char canistr[] = {
         24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
        };

        mputs(canistr);
        purgeline();
}

/* End of izm.c */
