/*% cc -O -DLDP -DUNIX -DDIAGS %  -o isz; size isz;
 ****************************************************************************
 *
 * isz.c By Chuck Forsberg,  Omen Technology INC
 *  Copyright 2002 Omen Technology INC All Rights Reserved
 *  Entry: see sample main() in code below
 *
 *  External functions called:
 *
 * sendline(c)  sends a character to host
 * mputs(s) sends null terminated string to host
 *
 * purgeline()  empty receive queue
 * int readline(tenths) read char from host, timeout in seconds/10
        return received byte 0 to 0377
        negative return values indicate error:
                return -1 on recoverable error
                return -2 on timeout
        trap fatal errors (loss of carrier, etc.), close file
 * checkline() return non 0 if readline has 1 or more bytes ready
 *
 * zfilbuf() fill buffer with blklen bytes, set Eofseen if EOT, return count
 * zseek(whence) clear EOF, errors, seek
 * zclose(status) close the input dataset associated with zfilbuf() et al.
 *      N.B: zclose might be called after the file is closed.
 *
 * When trapping on errors, close file, call canit() unless line dropped
 *
 ****************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <errno.h>

#include <tt8lib.h>
#include "izm.h"
#include "io_zmod.h"

extern int errno;

// COLETTI Globals used in ISZ
char *secbuf;
char *filebuf;          /* BIGBUF for file  */
int  Eofahead;          /* will we be running out of gas soon? */
unsigned long Txleft;   /* number of characters left in FILE buf */
unsigned long filesize;
unsigned long filebufsize;
jmp_buf  tohere;        /* For Carrier Drop Out and CTS Timeout in readline() and checkline() and sendline() */
//

/* Globals used in ISZ */
FILE *in;
int Zmodem=0;           /* ZMODEM protocol requested by receiver */
unsigned Txwindow=0;    /* Control the size of the transmitted window */
unsigned Txwspac;       /* Spacing between zcrcq requests */
unsigned Txwcnt;        /* Counter used to space ack requests */
long vpos = 0;          /* Number of bytes read from file */
long Lrxpos;            /* Receiver's last reported offset */
int Lastrx;
char Lzconv;            /* Local ZMODEM file conversion request */
int blklen=1024;        /* length of transmitted records */
int Eofseen;            /* EOF seen on input set by zfilbuf */
unsigned Rxbuflen = 16384;      /* Receiver's max buffer length */
int Tframlen = 0;       /* Override for tx frame length */
int Rxflags = 0;
long Lastsync;          /* Last offset to which we got a ZRPOS */
int Beenhereb4;         /* How many times we've been ZRPOS'd same place */

/* Globals used in ISZ and IZM functions */
char Rxhdr[4];          /* Received header */
char Txhdr[8];          /* Transmitted header */
int  Zctlesc;           /* Encode control characters */
int  Zrwindow = 1400;   /* RX window size (controls garbage count) */
int  Rxtimeout = 600;
int  Txfcs32;           /* TURE means send binary frames with 32 bit FCS */
long bytcnt;
long Rxpos;             /* Received file position */
long Txpos;             /* Transmitted file position */


int SZ(char *fname, char *fsize)
{

        int err_code = 0;

        if( UsbActive() == TRUE )
          printfUSB("\nWelcome to ZMODEM\n");

        if( ModemCDActive() != TRUE ){
          if( UsbActive() == TRUE )
            printfUSB("\nZMODEM CD Inactive on Entry!\n");
          return(-10);
          }

        filebufsize = 16384U;        //set file buf size a multiple of ZMODEM sub-packet
        if( filebufsize % blklen ){
          return(-20);
          }

        if( (err_code = setjmp(tohere)) != OK ){
          if( in != NULL )          //how rude, hanging up like that!
            fclose(in);
          if( filebuf != NULL )
            free(filebuf);
          if( UsbActive() == TRUE )
            printfUSB("\nEnding ZMODEM Transfer via SetJmp ret=%d\n", err_code);
          return( err_code );
          }

        if( UsbActive() == TRUE )
          printfUSB("\nStarting ZMODEM Transfer of file %s with %s bytes\n", fname, fsize);

        Lzconv = ZCRESUM;         /* Ask receiver for crash recovery */

        startz();                 /* Initialize receiver */

        err_code = wcs(fname, fsize);

        if( in != NULL )
          fclose(in);
        if( filebuf != NULL )
          free(filebuf);

        if( UsbActive() == TRUE )
          printfUSB("\nEnding ZMODEM Transfer ret=%d\n", err_code);

        if( err_code == OK )      /* Orderly conclusion of ZMODEM session */
          saybibi();              /* forces the remote ZMODEM session to exit */

        return( err_code );

}

startz()
{
#ifdef BOTH
        Rxtimeout = 600;
#endif
        Zmodem = 0;
//      mputs("rz\r");
        stohdr(0L);
        zshhdr(ZRQINIT, Txhdr);
}

wcs(char *name, char *size)
{

//COLETTI ADDED CODE STARTS HERE

        in = NULL;
        filebuf = NULL;

        filesize = strtoul(size, NULL, 10);
        if( filesize <= 0 )
          return(-30);

        filebuf = malloc(filebufsize);
        if( filebuf == NULL )
          return(-40);

        in = fopen(name, "rb");
        if( in == NULL ){
          fclose(in);
          free(filebuf);
          filebuf = NULL;
          return(-50);
          }

        if( setvbuf(in, filebuf, _IOFBF, filebufsize) != OK ) {
          fclose(in);
          free(filebuf);
          in = NULL;
          filebuf = NULL;
          return(-60);
          }

        //set Eofahead, pre-zfilbuf, don't need to but be safe for later...
        if( filesize < filebufsize )
          Eofahead = 1;
        else
          Eofahead = 0;

        //set Txleft HERE!
        Txleft = 0;

        //keep it simple and use filebuf itself to send out filename
        secbuf = filebuf;

//COLETTI ADDED CODE FINISH HERE

        Eofseen = 0; vpos = 0;

        switch (wctxpn(name, size)) {
        case ZSKIP:
        case ZFERR:
                return OK;
        case OK:
                break;
        default:
                return ERROR;
        }
        return 0;
}

/*
 * generate and transmit pathname block consisting of
 *  pathname (null terminated),
 */
wctxpn(char *name, char *size)
{

        long n, s;
        int length;

        if (!Zmodem)
                if (getnak())
                        return ERROR;
        Zmodem = 1;

        sprintf(secbuf,"%s%c%s%c", name, 0, size, 0);
        n=strlen(name);
        s=strlen(size);
        length = n + s + 2;

        return zsendfile(secbuf, length);

}

int
zseek(n)
long n;
{
//COLETTI ADDED CODE START  HERE
        Txleft = 0;
//COLETTI ADDED CODE FINISH HERE
        clearerr(in);   /* Clear EOF */
        return (fseek(in, n, 0));
}

/* Fill buffer with blklen chars */
size_t zfilbuf()
{
        register size_t length;

        length = fast_fread(NULL, sizeof(char), blklen, in);
        //length = fread(secbuf, sizeof(char), blklen, in);

        if(length < filebufsize)
          Eofahead = 1;
        else
          Eofahead = 0;

        return length;

}

zclose(status)
{
        if (in != NULL)
          fclose(in);
        if(filebuf != NULL)
          free(filebuf);
        in = NULL;
        filebuf = NULL;

}

getnak()
{
        register firstch;

        Lastrx = 0;
        for (;;) {
                switch (firstch = readline(800)) {
                case ZPAD:
                        if (getzrxinit())
                                return ERROR;
                        return FALSE;
                case TIMEOUT:
                        return TRUE;
                case CAN:
                        if ((firstch = readline(20)) == CAN && Lastrx == CAN)
                                return TRUE;
                default:
                        break;
                }
                Lastrx = firstch;
        }
}

/*
 * Get the receiver's init parameters
 */
getzrxinit()
{
        register n;

        for (n=10; --n>=0; ) {
                
                switch (zgethdr()) {
                case ZCHALLENGE:        /* Echo receiver's challenge numbr */
                        stohdr(Rxpos);
                        zshhdr(ZACK, Txhdr);
                        continue;
                case ZRINIT:
                        Rxflags = 0377 & Rxhdr[ZF0];
#ifndef XEDCRC32
                        Txfcs32 = Rxflags & CANFC32;
#endif
                        Zctlesc |= Rxflags & TESCCTL;
                        Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
                        if ( !(Rxflags & CANFDX))
                                Txwindow = 0;
                        /* Override to force shorter frame length */
                        if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
                                Rxbuflen = Tframlen;
                        if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
                                Rxbuflen = Tframlen;
                        /* Set initial subpacket length */
                        if (Rxbuflen && blklen>Rxbuflen)
                                blklen = Rxbuflen;
                        return OK;
                case ZCAN:
                case TIMEOUT:
                        return ERROR;
                default:
                        zshhdr(ZNAK, Txhdr);
                        continue;
                }
        }
        return ERROR;
}


/* Send file name and related info */
zsendfile(buf, blen)
char *buf;
{
        register c;

        for (;;) {
                stohdr(0L);
                Txhdr[ZF0] = Lzconv;    /* file conversion request */
                zsbhdr(ZFILE, Txhdr);
                zsda32(buf, blen, ZCRCW);
again:
                c = zgethdr();
                switch (c) {
                case ZRINIT:
                        while ((c = readline(50)) > 0)
                                if (c == ZPAD) {
                                        goto again;
                                }
                        continue;
                default:
                case ZCAN:
                case TIMEOUT:
                case ZABORT:
                case ZFIN:
                        return ERROR;
                case ZSKIP:
                        zclose(ERROR); return c;
                case ZRPOS:
                        /*
                         * Suppress zcrcw request otherwise triggered by
                         * lastyunc==bytcnt
                         */
                        if (Rxpos && zseek(Rxpos))
                                return ERROR;
                        Lastsync = (bytcnt = Txpos = Rxpos) -1;
                        return zsendfdata();
                }
        }
}


/* Send the data in the file */
zsendfdata()
{
        register c, e, n;
        register newcnt;
        int junkcount;          /* Counts garbage chars received by TX */

        Lrxpos = 0;
        junkcount = 0;
        Beenhereb4 = FALSE;

        goto somemore;

waitack:
        junkcount = 0;
        c = getinsync(0);
gotack:
        switch (c) {
        default:
        case ZCAN:
                zclose(ERROR);
                return ERROR;
        case ZSKIP:
                zclose(ERROR);
                return c;
        case ZACK:
        case ZRPOS:
                break;
        case ZRINIT:
                zclose(ERROR);
                return OK;
        }

        while (checkline()) {
                switch (readline(1))
                {
                case XOFF:              /* Wait a while for an XON */
                case XOFF|0200:
                        readline(100); continue;
                case CAN:
                case ZPAD:
                        c = getinsync(1);
                        goto gotack;
                }
        }

somemore:
        newcnt = Rxbuflen;
        Txwcnt = 0;
        e = ZCRCE;

        do {
                if (e == ZCRCE) {
                        stohdr(Txpos);
                        zsbhdr(ZDATA, Txhdr);
                }

//COLETTI ADDED CODE STARTS HERE

                if( Txleft == 0 ){
                  Txleft = zfilbuf();
                  if( Eofahead == 0 )
                    n = blklen;
                  }

                if( Eofahead == 1 ){
                  if( Txleft < blklen ){
                    n = Txleft;
                    Eofseen = 1;
                    }
                  else
                    n = blklen;
                  }

                secbuf = (char *)in->_bp;

//COLETTI ADDED CODE FINISH HERE

                if (Eofseen)
                        e = ZCRCE;
                else if (junkcount > 3)
                        e = ZCRCW;
                else if (bytcnt == Lastsync)
                        e = ZCRCW;
                else if (Rxbuflen && (newcnt -= n) <= 0)
                        e = ZCRCW;
                else if (Txwindow && (Txwcnt += n) >= Txwspac) {
                        Txwcnt = 0;  e = ZCRCQ;
                }
                else
                        e = ZCRCG;

                zsda32(secbuf, n, e);
                bytcnt = Txpos += n;


                if (e == ZCRCW)
                        goto waitack;
                while (checkline()) {
                        switch (readline(1))
                        {
                        case CAN:
                        case ZPAD:
                                c = getinsync(1);
                                if (c == ZACK)
                                        break;
                                /* zcrce - dinna wanna starta ping-pong game */
                                zsda32(secbuf, 0, ZCRCE);
                                goto gotack;
                        case XOFF:              /* Wait a while for an XON */
                        case XOFF|0200:
                                readline(100);
                        default:
                                ++junkcount;
                        }
                }
                if (Txwindow) {
                        while ((Txpos - Lrxpos) >= Txwindow) {
                                if (e != ZCRCQ)
                                        zsda32(secbuf, 0, e = ZCRCQ);
                                c = getinsync(1);
                                if (c != ZACK) {
                                        zsda32(secbuf, 0, ZCRCE);
                                        goto gotack;
                                }
                        }
                }

//COLETTI ADDED CODE STARTS HERE

                in->_bp += n;
                Txleft -= n;

//COLETTI ADDED CODE FINISH HERE

        } while (!Eofseen);

        stohdr(Txpos);
        zshhdr(ZEOF, Txhdr);
        for (;;) {
                switch (getinsync(0)) {
                case ZACK:
                        continue;
                case ZRPOS:
                        goto somemore;
                case ZRINIT:
                        zclose(OK);
                        return OK;
                case ZSKIP:
                        zclose(ERROR);
                        return c;
                default:
                        zclose(ERROR);
                        return ERROR;
                }
        }
}

/*
 * Respond to receiver's complaint, get back in sync with receiver
 */
getinsync(flag)
{
        register c;

        for (;;) {
                c = zgethdr();
                switch (c) {
                case ZCAN:
                case ZABORT:
                case ZFIN:
                case TIMEOUT:
                        return ERROR;
                case ZRPOS:
                        /* ************************************* */
                        /*  If sending to a buffered modem, you  */
                        /*   might send a break at this point to */
                        /*   dump the modem's buffer.            */
                        if (zseek(Rxpos))
                                return ERROR;
                        Eofseen = 0;
                        bytcnt = Lrxpos = Txpos = Rxpos;
                        if (Lastsync == Rxpos) {
                                if (++Beenhereb4 > 4)
                                        if (blklen > 32)
                                                blklen /= 2;
                        }
                        Lastsync = Rxpos;
                        return c;
                case ZACK:
                        Lrxpos = Rxpos;
                        if (flag || Txpos == Rxpos)
                                return ZACK;
                        continue;
                case ZRINIT:
                        return c;
                case ZSKIP:
                        return c;
                case ERROR:
                default:
                        zshhdr(ZNAK, Txhdr);
                        continue;
                }
        }
}


/* Say "bibi" to the receiver, try to do it cleanly */
void
saybibi()
{
        for (;;) {
                stohdr(0L);             /* CAF Was zsbhdr - minor change */
                zshhdr(ZFIN, Txhdr);    /*  to make debugging easier */
                switch (zgethdr()) {
                case ZFIN:
                        sendline('O'); sendline('O');
                /*FALLTHRU*/
                case ZCAN:
                case TIMEOUT:
                        return;
                }
        }
}

/* End of isz.c */
