#define EXTRADEBUG
/*
 *  K e r m i t  File Transfer Utility seriously hacked for local use with uw
 *
 *  Currently allows only one kermit session to exist in one window concurent.
 *
 *  Adapted from UNIX Kermit, Columbia University, 1981, 1982, 1983
 *      Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
 *
 *  Also:   Jim Guyton, Rand Corporation
 *          Walter Underwood, Ford Aerospace
 *
 */

/*
 * March 28, 1988
 *	    Hacked into form usable in uw.  Converted fsm's to be driven
 *	    by received packet events.  Rewrote rpack routine.
 */

#ifdef __GNUC__
#  include <gemfast.h>
#  include <aesbind.h>
#  include <vdibind.h>
#else
#  include <obdefs.h>
#  include <gemdefs.h>
#endif
#include <osbind.h>
#include <stdio.h>          /* Standard UNIX definitions */
#include <time.h>
#include "wind.h"
#include "windefs.h"

#define error printmsg
#define chari int	    /* items of type chari should be char, but, mwc
				 insists on adjusting them to int anyway */
/* Symbol Definitions */

#define MAXPACKSIZ  94      /* Maximum packet size */
#define SOH         1       /* Start of header */
#define CR          13      /* ASCII Carriage Return */
#define SP          32      /* ASCII space */
#define DEL         127     /* Delete (rubout) */

#define MAXTRY      10      /* Times to retry a packet */
#define MYQUOTE     '#'     /* Quote character I will use */
#define MYPAD       0       /* Number of padding characters I will need */
#define MYPCHAR     0       /* Padding character I need (NULL) */

#define MYEOL       '\r'    /* End-Of-Line character I need */

#define MYTIME      10      /* Seconds after which I should be timed out */
#define MAXTIM      60      /* Maximum timeout interval */
#define MINTIM      10      /* Minumum timeout interval */

#ifndef TRUE
#define TRUE        -1      /* Boolean constants */
#endif
#define FALSE       0
#define GETPACK     2	    /* return constant */

/* Macro Definitions */

/*
 *  f l u s h i n p u t
 *
 *  Dump all pending input to clear stacked up NACK's.
 */

#define flushinput()	/* no explicit call to flush needed for uw */

/*
 * tochar: converts a control character to a printable one by adding a space.
 *
 * unchar: undoes tochar.
 *
 * ctl:    converts between control characters and printable characters by
 *         toggling the control bit (ie. ^A becomes A and A becomes ^A).
 */
#define tochar(ch)  ((ch) + ' ')
#define unchar(ch)  ((ch) - ' ')
#define ctl(ch)     ((ch) ^ 64 )
#define abs(exp) (((exp) >= 0) ? (exp) : -(exp))


/* Global Variables */

extern int	mouse;				/* is mouse visible ? */
extern	struct	wi_str	w[];

int     size,               /* Size of present data */
        rpsiz,              /* Maximum receive packet size */
        spsiz,              /* Maximum send packet size */
        pad,                /* How much padding to send */
        reqtimint,          /* timeout interval I request */
        timint,             /* Timeout for foreign host on sends */
        n,                  /* Packet number */
        numtry,             /* Times this packet retried */
        oldtry,             /* Times previous packet retried */
        image,              /* -1 means 8-bit mode */
        debug,              /* indicates level of debugging output (0=none) */
        filnamcnv,          /* -1 means do file name case conversions */
        filecount,          /* Number of files left to send */
	kermwdes,	    /* Window descripter for kermit window */
	kermport;	    /* port number for kermit window */

clock_t	timestamp;	    /* Time last packet was received */

char    sflg, rflg;         /* flags for RECEIVE, SEND */
char    state,              /* Present state of the automaton */
        padchar,            /* Padding character to send */
        eol,                /* End-Of-Line character to send */
        quote,              /* Quote character in incoming data */
        **filelist,         /* List of files to be sent */
	*filnam,            /* Current file name */
        recpkt[MAXPACKSIZ+1], /* Receive packet buffer */
        packet[MAXPACKSIZ+1]; /* Packet buffer */

FILE    *fp,                /* File pointer for current disk file */
        *log;               /* File pointer for Logfile */


/*
 *  kerminit
 *
 *  initalization routine - initalize and dispatch to the appropriate routine.
 */

int kerminit(curwin)
int curwin;
{
    OBJECT *obj_tmp;
    static char path[40] = ".\\*.*";
    static char file[40] = "";
    static char filename[80];	    /* space for file and directory */
    int butt;

/*  Initialize these values and hope the first packet will get across OK */

    if (kermwdes && w[kermwdes].kerm_act) return (-1);
    kermwdes = curwin;
    					/* If kermit already active, return. */
    w[kermwdes].kerm_act = TRUE;	/* mark current window to send output
    					   to kermit instead of emulator */
    kermport = find_port(curwin);
    eol = CR;                           /* EOL for outgoing packets */
    quote = '#';                        /* Standard control-quote char "#" */
    pad = 0;                            /* No padding */
    padchar = '\0';                     /* Use null if any padding wanted */
    timint = MYTIME;                    /* default timint */

 
/*
 * Set direction and paramaters debug, filnamcnv, image with dialog
 */
    rsrc_gaddr(R_TREE, KERMPARM, &obj_tmp);
    if (obj_tmp[ASCIIMOD].ob_state == NORMAL &&
      obj_tmp[IMAGEMOD].ob_state == NORMAL)
    {					/* initalize object and statics */
        objc_change(obj_tmp, ASCIIMOD, 0, 0, 0, 0, 0, SELECTED, 0);
        objc_change(obj_tmp, CONVNAME, 0, 0, 0, 0, 0, SELECTED, 0);
        objc_change(obj_tmp, RECFILE, 0, 0, 0, 0, 0, SELECTED, 0);

	path[0] = Dgetdrv() + 'a';
	path[1] = ':';
	Dgetpath(path+2, 0);
	strcat(path, "\\*.*");
    }
    if ((butt = s_dial(KERMPARM, 3)) == KERMEXIT) return(kermterm());
    if (obj_tmp[DEBUGENA].ob_state == SELECTED)
    {
        debug = obj_tmp[DEBUG1].ob_state + obj_tmp[DEBUG2].ob_state
	+ obj_tmp[DEBUG3].ob_state;
    }
    if (butt == RECFILE) obj_tmp[RECFILE].ob_state = SELECTED;
    if (butt == SENDFILE) obj_tmp[SENDFILE].ob_state = SELECTED;
    rflg = obj_tmp[RECFILE].ob_state;
    sflg = obj_tmp[SENDFILE].ob_state;
    image = (obj_tmp[IMAGEMOD].ob_state == SELECTED);
    filnamcnv = obj_tmp[CONVNAME].ob_state; /* conversion for UNIX systems */
    
/* All set up, now execute the command that was given. */

    if (debug)
    {
        printmsg("debuging level = %d\n",debug);

        if (sflg) printmsg("Send command\n");
        if (rflg) printmsg("Receive command\n");
    }
  
    if (sflg)                           /* Send command */ 
    {
    	extern char * rindex();

	if (!mouse)
	{
	    graf_mouse(M_ON, NULL);
	    ++mouse;
	}
        fsel_input(path, file, &butt);
	if (! butt) return(kermterm()); 
	strcpy(filename, path);
	if (debug > 2) printmsg("directory %s", filename);
	filnam = rindex(filename, '\\');
	if (filnam) *(++filnam) = '\0';
	strcat(filename, file);
        filnam = filename;		/* Get file to send */
        if (debug > 2) printmsg("sending %s",filnam);
        fp = NULL;                      /* Indicate no file open yet */
        filelist = NULL;                /* Set up the rest of the file list */
        filecount = 0;                  /* Number of files left to send */
	state = 'S';                    /* Send initiate is the start state */
	n = 0;                          /* Initialize message number */
	numtry = 0;                     /* Say no tries yet */
        w[kermwdes].kerm_act = TRUE;	/* mark current window to send output
    					   to kermit instead of emulator */
	timestamp = clock();		/* reset timer */
	sendsw();
    }

    if (rflg)                           /* Receive command */
    {
	state = 'R';                    /* Receive-Init is the start state */
	n = 0;                          /* Initialize message number */
	numtry = 0;                     /* Say no tries yet */
	timestamp = clock();		/* reset timer */
    }
    
    return(0);
}

/*
 * kermterm - Terminate kermit and tell uw not to call us again.
 */
kermterm()
{
    sflg = rflg = 0;
    w[kermwdes].kerm_act = FALSE;
    kermwdes = 0;
}

kermtimchk()
{
    if (!w[kermwdes].kerm_act)	/* is kermit active? */
    {
	kermwdes = 0;
	return;
    }
    if ((clock() - timestamp) / CLK_TCK > timint)	/* timeout ? */
    {
	rpack(NULL, "", "");	/* tell FSM about time out */
    }
}

/*
 *  s e n d s w
 *
 *  Sendsw is the state table switcher for sending files.  It loops until
 *  either it finishes, or an error is encountered.  The routines called
 *  by sendsw are responsible for changing the state.
 *
 */

int sendsw()
{
    chari sinit(), sfile(), sdata(), seof(), sbreak(), nstate;

    nstate = state;
    while(TRUE)                         /* Do this as long as necessary */
    {
        if (debug) printmsg("sendsw state: %c  nstate: %c\n",state,nstate);
	while (nstate != 'I')
	{
            switch(nstate)
            {
                case 'S':   nstate = sinit();  break; /* Send-Init */
                case 'F':   nstate = sfile();  break; /* Send-File */
                case 'D':   nstate = sdata();  break; /* Send-Data */
                case 'Z':   nstate = seof();   break; /* Send-End-of-File */
                case 'B':   nstate = sbreak(); break; /* Send-Break */
            }
	    switch(nstate)
	    {
                case 'C':   printmsg("\aDone.\a");
	    		    return (TRUE);           /* Complete */
                case 'A':   printmsg("Send Failed.");
	    		    return (FALSE);          /* "Abort" */
	    }
	    if (nstate != 'I')
	    {
	        state = nstate;
	        if (numtry++ > MAXTRY) return(FALSE); /* If too many tries, give up */
	    }
	}
	return(GETPACK);
    }
}


/*
 *  s i n i t
 *
 *  Send Initiate: send this host's parameters and get other side's back.
 */

chari sinit()
{
    int num, len;                       /* Packet number, length */
    static int sent = 0;

    if (! sent) {
	reqtimint = MYTIME;
	spar(packet);                   /* Fill up init info packet */
	flushinput();                   /* Flush pending input */
	spack('S',n,6,packet);          /* Send an S packet */

	sent = 1;
	return('I');			/* get packet to read */
    }
    sent = 0;
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':  return(state);       /* NAK, try it again */

        case 'Y':                       /* ACK */
            if (n != num)               /* If wrong ACK, stay in S state */
                return(state);          /* and try again */
            rpar(recpkt);               /* Get other side's init info */

            if (eol == 0) eol = '\n';   /* Check and set defaults */
            if (quote == 0) quote = '#';
            if(abs(timint - reqtimint) < 3) timint = reqtimint + 3;
            /* guarentee diference of > 3 for sounds for timint */

            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;               /* Bump packet count */
            return('F');                /* OK, switch state to F */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE: return(state);      /* Receive failure, try again */

        default: return('A');           /* Anything else, just "abort" */
   }
 }


/*
 *  s f i l e
 *
 *  Send File Header.
 */

chari sfile()
{
    int num, len;                       /* Packet number, length */
    char filnam1[50],                   /* Converted file name */
        *newfilnam,                     /* Pointer to file name to send */
        *cp;                            /* char pointer */
    static int sent = 0;

    if (! sent) {
        if (fp == NULL)                     /* If not already open, */
        {   if (debug) printmsg("   Opening %s for sending.\n",filnam);
            fp = fopen(filnam, image ? "rb" : "r");         /* open the file to be sent */
            if (fp == NULL)                 /* If bad file pointer, give up */
            {
                error("Cannot open file %s",filnam);
                return('A');
            }
        }

        strcpy(filnam1, filnam);            /* Copy file name */
        newfilnam = cp = filnam1;
        while (*cp != '\0')                 /* Strip off all leading directory */
            if (*cp++ == '\\')               /* names (ie. up to the last /). */
                newfilnam = cp;

        if (filnamcnv)                      /* Convert lower case to upper  */
            for (cp = newfilnam; *cp != '\0'; cp++)
                if (*cp >= 'a' && *cp <= 'z')
                    *cp ^= 040;

        len = cp - newfilnam;               /* Compute length of new filename */

        printmsg("Sending %s as %s",filnam,newfilnam);

        spack('F',n,len,newfilnam);         /* Send an F packet */
	sent = 1;
	
	return('I');			/* get packet to read */
    }
    sent = 0;
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {                   
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet */
            if (n != num)               /* which is just like an ACK for */ 
                return(state);          /* this packet so fall thru to... */

        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, stay in F state */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;               /* Bump packet count */
            size = bufill(packet);      /* Get first data from file */
            return('D');                /* Switch state to D */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE: return(state);      /* Receive failure, stay in F state */

        default:    return('A');        /* Something else, just "abort" */
    }
}


/*
 *  s d a t a
 *
 *  Send File Data
 */

chari sdata()
{
    int num, len;                       /* Packet number, length */
    static int sent = 0;

    if (! sent) {
        spack('D',n,size,packet);           /* Send a D packet */
	sent = 1;
	return('I');			/* get packet to read */
    }
    sent = 0;
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {               
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */
                
        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, fail */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;               /* Bump packet count */
            if ((size = bufill(packet)) == EOF) /* Get data from file */
                return('Z');            /* If EOF set state to that */
            return('D');                /* Got data, stay in state D */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE: return(state);      /* Receive failure, stay in D */

        default:    return('A');        /* Anything else, "abort" */
    }
}


/*
 *  s e o f
 *
 *  Send End-Of-File.
 */

chari seof()
{
    int num, len;                       /* Packet number, length */
    static int sent = 0;

    if (! sent) {
        spack('Z',n,0,packet);              /* Send a 'Z' packet */

	sent = 1;
	return('I');			/* get packet to read */
    }
    sent = 0;
    switch(rpack(&len,&num,recpkt))     /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless it's NAK for next packet, */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */

        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, hold out */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;               /* and bump packet count */
            if (debug) printmsg("   Closing input file %s, ",filnam);
            fclose(fp);                 /* Close the input file */
            fp = NULL;                  /* Set flag indicating no file open */ 

            if (debug) printmsg("looking for next file...\n");
            if (gnxtfl() == FALSE)      /* No more files go? */
                return('B');            /* if not, break, EOT, all done */
            if (debug) printmsg("   New file is %s\n",filnam);
            return('F');                /* More files, switch state to F */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE: return(state);      /* Receive failure, stay in Z */

        default:    return('A');        /* Something else, "abort" */
    }
}


/*
 *  s b r e a k
 *
 *  Send Break (EOT)
 */

chari sbreak()
{
    int num, len;                       /* Packet number, length */
    static int sent = 0;

    if (! sent) {
        spack('B',n,0,packet);              /* Send a B packet */

	sent = 1;
	return('I');			/* get packet to read */
    }
    sent = 0;
    switch (rpack(&len,&num,recpkt))    /* What was the reply? */
    {
        case 'N':                       /* NAK, just stay in this state, */
            num = (--num<0 ? 63:num);   /* unless NAK for previous packet, */
            if (n != num)               /* which is just like an ACK for */
                return(state);          /* this packet so fall thru to... */

        case 'Y':                       /* ACK */
            if (n != num) return(state); /* If wrong ACK, fail */
            numtry = 0;                 /* Reset try counter */
            n = (n+1)%64;               /* and bump packet count */
            return('C');                /* Switch state to Complete */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE: return(state);      /* Receive failure, stay in B */

        default:    return ('A');       /* Other, "abort" */
   }
}


/*
 *  r e c s w
 *
 *  This is the state table switcher for receiving files.
 */




recsw()
{
    chari rinit(), rfile(), rdata();     /* Use these procedures */

    if (debug) printmsg(" recsw state: %c\n",state);
    switch(state)                   /* Do until done */
    {
        case 'R':   state = rinit(); break; /* Receive-Init */
        case 'F':   state = rfile(); break; /* Receive-File */
        case 'D':   state = rdata(); break; /* Receive-Data */
    }
    if (numtry++ > MAXTRY) return(FALSE); /* If too many tries, give up */
    switch(state)                   /* Do until done */
    {
        case 'C':   printmsg("\aDone.\a");
		    return(TRUE);           /* Complete state */
        case 'A':   printmsg("Receive Failed.");
		    return(FALSE);          /* "Abort" state */
    }
    return(GETPACK);			    /* get the next packet */
}

    
/*
 *  r i n i t
 *
 *  Receive Initialization
 */
  
chari rinit()
{
    int len, num;                       /* Packet length, number */

    switch(rpack(&len,&num,packet))     /* Get a packet */
    {
        case 'S':                       /* Send-Init */
            rpar(packet);               /* Get the other side's init data */
            if (timint > MYTIME + 3) reqtimint = timint - 4;
            else reqtimint = timint + 4;
            spar(packet);               /* Fill up packet with my init info */
            flushinput();               /* get rid of unwanted nak's */
            spack('Y',n,6,packet);      /* ACK with my parameters */
            oldtry = numtry;            /* Save old try count */
            numtry = 0;                 /* Start a new counter */
            n = (n+1)%64;               /* Bump packet number, mod 64 */
            return('F');                /* Enter File-Receive state */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,NULL);        /* Return a NAK */
            return(state);              /* Keep trying */

        default:     return('A');       /* Some other packet type, "abort" */
    }
}


/*
 *  r f i l e
 *
 *  Receive File Header
 */

chari rfile()
{
    int num, len;                       /* Packet number, length */
    char filnam1[50];                   /* Holds the converted file name */

    switch(rpack(&len,&num,packet))     /* Get a packet */
    {
        case 'S':                       /* Send-Init, maybe our ACK lost */
            if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
            if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
            {                           /* Yes, ACK it again with  */
                spar(packet);           /* our Send-Init parameters */
                spack('Y',num,6,packet);
                numtry = 0;             /* Reset try counter */
                return(state);          /* Stay in this state */
            }
            else return('A');           /* Not previous packet, "abort" */

        case 'Z':                       /* End-Of-File */
            if (oldtry++ > MAXTRY) return('A');
            if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
            {                           /* Yes, ACK it again. */
                spack('Y',num,0,NULL);
                numtry = 0;
                return(state);          /* Stay in this state */
            }
            else return('A');           /* Not previous packet, "abort" */

        case 'F':                       /* File Header (just what we want) */
            if (num != n) return('A');  /* The packet number must be right */
            strcpy(filnam1, packet);    /* Copy the file name */

            if (filnamcnv)              /* Convert upper case to lower */
                for (filnam=filnam1; *filnam != '\0'; filnam++)
                    if (*filnam >= 'A' && *filnam <= 'Z')
                        *filnam |= 040;

            if ((fp=fopen(filnam1, image ? "wb" : "w"))==NULL) /* Try to open a new file */
            {
                error("Cannot create %s",filnam1); /* Give up if can't */
                return('A');
            }
            else                        /* OK, give message */

                printmsg("Receiving %s as %s",packet,filnam1);

            spack('Y',n,0,NULL);        /* Acknowledge the file header */
            oldtry = numtry;            /* Reset try counters */
            numtry = 0;                 /* ... */
            n = (n+1)%64;               /* Bump packet number, mod 64 */
            return('D');                /* Switch to Data state */

        case 'B':                       /* Break transmission (EOT) */
            if (num != n) return ('A'); /* Need right packet number here */
            spack('Y',n,0,NULL);        /* Say OK */
            return('C');                /* Go to complete state */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,NULL);        /* Return a NAK */
            return(state);              /* Keep trying */

        default:    return ('A');       /* Some other packet, "abort" */
    }
}


/*
 *  r d a t a
 *
 *  Receive Data
 */

chari rdata()
{
    int num, len;                       /* Packet number, length */

    switch(rpack(&len,&num,packet))     /* Get packet */
    {
        case 'D':                       /* Got Data packet */
#ifdef EXTRADEBUG
	    if (debug > 2) printmsg("Got Data Packet");
#endif
            if (num != n)               /* Right packet? */
            {                           /* No */
                if (oldtry++ > MAXTRY)
                    return('A');        /* If too many tries, abort */
                if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
                {                       /* Previous packet again? */
                    spack('Y',num,6,packet); /* Yes, re-ACK it */
                    numtry = 0;         /* Reset try counter */
                    return(state);      /* Don't write out data! */
                }
                else return('A');       /* sorry, wrong number */
            }
            /* Got data with right packet number */
            spack('Y',n,0,NULL);        /* Acknowledge the packet */
#ifdef EXTRADEBUG
	    if (debug > 2) printmsg("Calling bufemp");
#endif
            bufemp(packet,len);         /* Write the data to the file */
            oldtry = numtry;            /* Reset the try counters */
            numtry = 0;                 /* ... */
            n = (n+1)%64;               /* Bump packet number, mod 64 */
            return('D');                /* Remain in data state */

        case 'F':                       /* Got a File Header */
            if (oldtry++ > MAXTRY)
                return('A');            /* If too many tries, "abort" */
            if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
            {                           /* It was the previous one */
                spack('Y',num,0,NULL);  /* ACK it again */
                numtry = 0;             /* Reset try counter */
                return(state);          /* Stay in Data state */
            }
            else return('A');           /* Not previous packet, "abort" */

        case 'Z':                       /* End-Of-File */
            if (num != n) return('A');  /* Must have right packet number */
            spack('Y',n,0,NULL);        /* OK, ACK it. */
            fclose(fp);                 /* Close the file */
            n = (n+1)%64;               /* Bump packet number */
            return('F');                /* Go back to Receive File state */

        case 'E':                       /* Error packet received */
            prerrpkt(recpkt);           /* Print it out and */
            return('A');                /* abort */

        case FALSE:                     /* Didn't get packet */
            spack('N',n,0,NULL);        /* Return a NAK */
            return(state);              /* Keep trying */

        default:     return('A');       /* Some other packet, "abort" */
    }
}


/*
 *      KERMIT utilities.
 */


/*
 *  s p a c k
 *
 *  Send a Packet
 */

spack(type,num,len,data)
chari type;
char  *data;
int num, len;
{
    int i;                              /* Character loop counter */
    char chksum, buffer[100];           /* Checksum, packet buffer */
    register char *bufp;                /* Buffer pointer */

    if (debug>1)                        /* Display outgoing packet */
    {
        printmsg("  spack type: %c\n",type);
        printmsg("         num:  %d\n",num);
        printmsg("         len:  %d\n",len);
        if (data != NULL)
	{
            data[len] = '\0';           /* Null-terminate data to print it */
            printmsg("        data: \"%s\"\n",data);
	}
    }
  
    bufp = buffer;                      /* Set up buffer pointer */
    for (i=1; i<=pad; i++) proto_out(kermport,&padchar,1); /* Issue any padding */

    *bufp++ = SOH;                      /* Packet marker, ASCII 1 (SOH) */
    *bufp++ = tochar(len+3);            /* Send the character count */
    chksum  = tochar(len+3);            /* Initialize the checksum */
    *bufp++ = tochar(num);              /* Packet number */
    chksum += tochar(num);              /* Update checksum */
    *bufp++ = type;                     /* Packet type */
    chksum += type;                     /* Update checksum */

    for (i=0; i<len; i++)               /* Loop for all data characters */
    {
        *bufp++ = data[i];              /* Get a character */
        chksum += data[i];              /* Update checksum */
    }
    chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
    *bufp++ = tochar(chksum);           /* Put it in the packet */
    *bufp = eol;                        /* Extra-packet line terminator */
    proto_out(kermport, buffer, bufp-buffer+1);
    					/* Send the packet */
#ifdef EXTRADEBUG
    if (debug > 2) printmsg("Returning from spack");
#endif
}

/*
 *  r p a c k
 *
 *  Read a Packet
 *  This routine is called both to buffer data from the host to the kermit
 *  window and to retreive those buffers once they contain a packet.  This
 *  routine calls the kermit state machine when it has a complete packet.
 *  The kermit state machine in turn calls this routine to retreive the packet.
 *  Thus, this routine is indirectly recursive to two levels.  Receive timeouts
 *  and bad packets cause FALSE to be returned to the state machine.  The uw
 *  control loop calls us with num == NULL to pass us a string of data from
 *  the host.  The uw control loop calls us with len == NULL if the timout
 *  counter (kerm_time) exceeds timint without us reseting it.
 */
#ifdef EXTRADEBUG
#define getnext {t = *dptr++; \
	        if (debug>2) printmsg( "char %d state %c", t, recstate);\
		if ((t & 0177) == SOH) { \
			recstate = 'L'; /* restart packet collection */\
			break; } \
		if (!image) t &= 0177;	/* Handle parity */ \
		if (t == 0) break; /* end of this input buffer */ }
#else
#define getnext {t = *dptr++; \
		if ((t & 0177) == SOH) { \
			recstate = 'L'; /* restart packet collection */\
			break; } \
		if (!image) t &= 0177;	/* Handle parity */ \
		if (t == 0) break; /* end of this input buffer */ }
#endif
rpack(len,num,data)
int *len, *num;                         /* Packet length, number */
char *data;                             /* Packet data */
{
    static int i;                       /* Data character number */
    static int leng;			/* length of data packet in buffer */
    static char type;			/* packet type */
    static int numb;			/* number of data packet in buffer */
    static int timedout = 0;		/* did we time out? */
    char t;                             /* Current input character */
    static char cchksum,                        /* Our (computed) checksum */
        rchksum;                        /* Checksum received from other host */
    char *dptr;				/* pointer to next data byte */
    static char mybuf[MAXPACKSIZ];	/* buffer used to acumulate packet */
    static char recstate = 'S';		/* start with S state */

    if (len == NULL) {	/* timed out */
        timedout = TRUE;
	if (sflg)
	{
	    if (sendsw() != GETPACK)    /* Send the file(s) */
                return(kermterm());
	}
	else if (rflg)
	{
            if (recsw() != GETPACK)     /* Receive the file(s) */
                return(kermterm());
	}
        return (0);
    }
    if (len != NULL && num != NULL)	/* received packet or timed out */
    {
	*len = leng;
	*num = numb;
	timestamp = clock();		/* reset timer */
	if (timedout)
	{
	    printmsg("Timeout.");
	    timedout = 0;
	    return(FALSE);
	}
	for (i=0; i<leng; i++)
	    data[i] = mybuf[i];
        if (data != NULL)
            data[*len] = '\0';          /* Null-terminate data to print it */
        if (debug>1)                    /* Display incoming packet */
        {
            printmsg("  rpack type: %c\n",type);
            printmsg("         num:  %d\n",*num);
            printmsg("         len:  %d\n",*len);
            if (data != NULL)
                printmsg("        data: \"%s\"\n",data);
        }
                                        /* Fold in bits 7,8 to compute */
        cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */

        if (cchksum != rchksum) return(FALSE);

        return(type);                       /* All OK, return packet type */
    }
    /* num == NULL so collect data for kermit */
    dptr = data;
    t = -1;
    while (t)
    {
      switch(recstate)
      {
        case 'S':
	    while (1)	        /* Wait for packet header */
	        getnext;
	    break;
	case 'L':			/* get packet length */
            getnext;			/* get next character */
            cchksum = t;                    /* Start the checksum */
            leng = unchar(t)-3;             /* Character count */
	    if (leng > MAXPACKSIZ)
	        leng = MAXPACKSIZ;
	    i = 0;
	    recstate = 'N';
	    /* Fall Through */

	case 'N':			    /* get packet number */
            getnext
            cchksum = cchksum + t;          /* Update checksum */
            numb = unchar(t);               /* Packet number */
	    recstate = 'T';
	    /* Fall Through */

	case 'T':			    /* get packet type */
            getnext;
            cchksum = cchksum + t;          /* Update checksum */
            type = t;                       /* Packet type */
	    recstate = 'D';
	    /* Fall Through */

	case 'D':			    /* get packet data */
            for (; i<leng; i++)             /* The data itself, if any */
            {                               /* Loop for character count */
                getnext;
                cchksum = cchksum + t;      /* Update checksum */
                mybuf[i] = t;                /* Put it in the data buffer */
            }
	    if (i == leng) 
	        recstate = 'C';
	    break;

	case 'C':
            mybuf[leng] = 0;                 /* Mark the end of the data */
            getnext;
            rchksum = unchar(t);            /* Convert to numeric */
	    recstate = 'E';		    /* Done with packet receipt */
	    t = 0;			    /* Dispose of remaining data */
      }
    }
    if (recstate == 'E') {
        recstate = 'S';
	if (sflg)
	{
	    if (sendsw() != GETPACK)        /* Send the file(s) */
                return(kermterm());
	}
	else if (rflg)
	{
            if (recsw() != GETPACK)         /* Receive the file(s) */
                return(kermterm());
	}
    }
    return(0);
}


/*
 *  b u f i l l
 *
 *  Get a bufferful of data from the file that's being sent.
 *  Only control-quoting is done; 8-bit & repeat count prefixes are
 *  not handled.
 */

bufill(buffer)
char buffer[];                          /* Buffer */
{
    int i,                              /* Loop index */
        t;                              /* Char read from file */
    char t7;                            /* 7-bit version of above */

    i = 0;                              /* Init data buffer pointer */
    while((t = getc(fp)) != EOF)        /* Get the next character */
    {
        t7 = t & 0177;                  /* Get low order 7 bits */

        if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
        {                                   /* special handling? */
            if (t=='\n' && !image)
            {                           /* Do LF->CRLF mapping if !image */
                buffer[i++] = quote;
                buffer[i++] = ctl('\r');
            }
            buffer[i++] = quote;        /* Quote the character */
            if (t7 != quote)
            {
                t = ctl(t);             /* and uncontrolify */
                t7 = ctl(t7);
            }
        }
        if (image)
            buffer[i++] = t;            /* Deposit the character itself */
        else
            buffer[i++] = t7;

        if (i >= spsiz-8) return(i);    /* Check length */
    }
    if (i==0) return(EOF);              /* Wind up here only on EOF */
    return(i);                          /* Handle partial buffer */
}


/*
 *      b u f e m p
 *
 *  Put data from an incoming packet into a file.
 */

bufemp(buffer,len)
char  buffer[];                         /* Buffer */
int   len;                              /* Length */
{
    int i;                              /* Counter */
    char t;                             /* Character holder */

#ifdef EXTRADEBUG
	    if (debug > 2) printmsg("Entered bufemp");
#endif
    for (i=0; i<len; i++)               /* Loop thru the data field */
    {
        t = buffer[i];                  /* Get character */
        if (t == MYQUOTE)               /* Control quote? */
        {                               /* Yes */
            t = buffer[++i];            /* Get the quoted character */
            if ((t & 0177) != MYQUOTE)  /* Low order bits match quote char? */
                t = ctl(t);             /* No, uncontrollify it */
        }
        if (t==CR && !image)            /* Don't pass CR if not in image mode */
            continue;
#ifdef EXTRADEBUG
	    if (debug > 2) printmsg("writing t=%c", t);
#endif
        putc(t,fp);
    }
}



/*
 *  g n x t f l
 *
 *  Get next file in a file group
 */

gnxtfl()
{
    if (filecount-- == 0) return FALSE; /* If no more, fail */
    if (debug) printmsg("   gnxtfl: filelist = \"%s\"\n",*filelist);
    filnam = *(filelist++);
    return TRUE;                   /* else succeed */
}


/*
 *  s p a r
 *
 *  Fill the data array with my send-init parameters
 *
 */

spar(data)
char data[];
{
    data[0] = tochar(MAXPACKSIZ);          /* Biggest packet I can receive */
    data[1] = tochar(reqtimint);           /* When I want to be timed out */
    data[2] = tochar(MYPAD);            /* How much padding I need */
    data[3] = ctl(MYPCHAR);             /* Padding character I want */
    data[4] = tochar(MYEOL);            /* End-Of-Line character I want */
    data[5] = MYQUOTE;                  /* Control-Quote character I send */
}


/*  r p a r
 *
 *  Get the other host's send-init parameters
 *
 */

rpar(data)
char data[];
{
    spsiz = unchar(data[0]);            /* Maximum send packet size */
    timint = unchar(data[1]);           /* When I should time out */
    if (timint > MAXTIM) timint = MAXTIM;
    if (timint < MINTIM) timint = MINTIM;
    pad = unchar(data[2]);              /* Number of pads to send */
    padchar = ctl(data[3]);             /* Padding character to send */
    eol = unchar(data[4]);              /* EOL character I must send */
    quote = data[5];                    /* Incoming data quote character */
}
 

/*
 *  Kermit printing routines:
 *
 *  printmsg -  like printf with "Kermit: " prepended
 *  prerrpkt - print contents of error packet received from remote host
 */


/*
 *  p r i n t m s g
 *
 *  Print message kermit window
 */

/*VARARGS1*/
printmsg(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
	char *sbuf[100];
        w_output(kermwdes,"Kermit: ");
        sprintf(sbuf,fmt,a1,a2,a3,a4,a5);
        w_output(kermwdes,sbuf);
        w_output(kermwdes,"\n\r");
}


/*
 *  p r e r r p k t
 *
 *  Print contents of error packet received from remote host.
 */
prerrpkt(msg)
char *msg;
{
    printmsg("Kermit aborting with following error from remote host:\n%s",msg);
    return;
}
