/*
 *  ==================================
 *  K e r m i t	 File Transfer Utility
 *  ==================================
 *
 *  Derived 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
 *
 *
 *  This version of KERMIT was put together from the C-language example
 *  which was given in the back of the "KERMIT PROTOCOL MANUAL", Fourth
 *  Edition (revision 1, 4-Nov-83) which included the changes dated
 *  17-Oct-83 by Alan Crosswell (CUCCA) for the IBM_UTS flavor.
 *
 *  This version is a cleaned up subset of the above release.  Only
 *  the file transfer operations are implemented, as the remote terminal
 *  emulator has been implemented as a separate process.  Much has been
 *  done to make porting this code to different environments easier,
 *  although there is some DEC-specific stuff still here, most notably
 *  the stripping of filespecs to KERMIT "normal form".
 *
 *  The 'f' flag now controls whether or not file specifications are
 *  converted to KERMIT "normal form".  The assumed EOL character is
 *  the usual <CR> (15 octal).  The 'i' flag puts us into image
 *  mode (no newline to <CR><LF> conversions & parity stripping).
 *
 *  Added wildcarding on send operations.
 *
 *  Also, CONNECT has been removed.  The CONNECT function is provided
 *  by a separate task.  This task and the connect one will eventually
 *  be spawned from a controlling parent which handles the user interface
 *  menu-moron style.
 *
 *  Finally, several changes were made for use with the DECUS C compiler.
 *  The macros were removed and checksum variables were changed to unsigned
 *  integers (safer anyway).
 *
 *  By:
 *	Bob Denny
 *	Alisa Systems, Inc.
 *	Pasadena, CA 
 *	06-Jan-84
 *
 *  Modification history:
 *
 *  08-Jan-83 RBD	bufill() - Fix so naked <CR>'s are not eaten in
 *			ASCII mode.  Add V4.0 fields to SEND-INIT packet.
 *  09-Jan-83 RBD	Clean up this file so system-dependencies are
 *			all in KERPOSIO.C.  
 *  12-Jan-83 RBD	Fix truncation of file type on incoming files
 *			when converting to normal form.  Add more symbolic
 *			hooks: sleep, user exit, report file transfer
 *			progress and symbolic exit status codes.  Add
 *			more intelligent handling of "A" states, reporting
 *			the reason for the abort.   Add send delay (30 sec).
 *			Add server operation (GET, FINISH & LOGOUT).  Change
 *			names of init and restore generics as they are 
 *			responsible for all system-specific initialization.
 *
 */

/*
 *			*******************
 *			** W A R N I N G **
 *			*******************
 *
 *	The "Init-Info" server initialization transaction
 *	called for in the Protocol V4 is temporarily disabled
 *	here;  the Kermit-32 we got had the support for 'I'
 *	packets commented out ...  To re-enable server init
 *	protocol, remove the following #define:
 */

#define NOIPACKETS

/************/
#define POS
/************/

#ifdef POS
#define	RMS_11
#endif

#ifdef	RMS_11
#include <rmstdio.h>	    /* RMS-flavor standard I/O */
#else
#include <stdio.h>	    /* Vanilla standard I/O */
#endif

/* 
 * 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 MYEOL	    '\r'    /* I need a <CR> to terminate receives */
#define MYQBIN	    'N'	    /* I don't quote binary values */
#define MYCHKT	    '1'	    /* I do basic single-character checksum */
#define MYREPT	    ' '	    /* I don't do run-length encoding */

/*
 * System specific definitions
 */
#ifdef POS
#define MYDELAY     30      /* Delay before send-init (remote) */
#define MYTIME	    10	    /* Seconds after which I should be timed out */
#define MYPAD	    0	    /* Number of padding characters I will need */
#define MYPCHAR	    0	    /* Padding character I need (NULL) */
#define MYCAPAS1    10	    /* I have timeout capabilities only */
#define MAXTIM	    60	    /* Maximum timeout interval */
#define MINTIM	    2	    /* Minumum timeout interval */
#define DEF_BAUD    1200    /* Default baud rate */
#define COMM_DEV    NULL    /* NULL comm device name - hardwired for POS */
#endif
 
/* 
 * Global Variables 
 */
unsigned int
	size,		/* Size of present data */
	rpsiz,	    	/* Maximum receive packet size */
	spsiz,	    	/* Maximum send packet size */
	pad,	    	/* How much padding to send */
	timint,	    	/* Timeout for foreign host on sends */
	chkt,		/* Checksum type */
	capas1,		/* Capabilities mask 1 */
	n,	    	/* Packet number */
	numtry,	    	/* Times this packet retried */
	oldtry,	    	/* Times previous packet retried */
	remote,	    	/* TRUE means we're a remote kermit */
	image,	    	/* TRUE means 8-bit image mode (no newline hacking) */
	debug,	    	/* indicates level of debugging output (0=none) */
	stripfs,  	/* TRUE to strip filespecs to "normal form" */
        exstat,		/* Our exit status */
	filecount,  	/* Number of files left to send */
	aboflag;	/* Async user abort sets this to TRUE */
	
char	state,		/* Present state of the automaton */
	padchar,	/* Padding character to send */
	eol,		/* End-Of-Line character to send */
	qbin,		/* Binary quoting character */
	rept,		/* Run-length encoding character */
	quote;		/* Control-quote character in incoming data */

char	**filelist;	/* List of --> to files to be sent */

char	*commdev;	/* ASCII device spec of communication line */

char	filpat[32];	/* Current (wild) filespec pattern from list */
char	filnam[64];	/* Current file name */

char	recpkt[MAXPACKSIZ],	/* Receive packet buffer */
	packet[MAXPACKSIZ];	/* Packet buffer */

FILE	*fp;		/* File pointer for current disk file */

/*
 * Error message formats
 */
char	*retfmt = "<%s> - Retry limit reached";
char	*badpkt = "<%s> - '%c' packet out of sequence";
char	*unkpkt = "<%s> - Illegal or unknown packet ('%c') received";

extern int $$ferr;

/*
 * The following definitions map the system-specific functions
 * and status values.
 */
#ifdef POS

#include <cx.h>

#define SYS$INITIAL	xk_set
#define SYS$RESTORE	xk_rst
#define SYS$XMIT	xk_wrt
#define SYS$RCHAR	xk_rch
#define SYS$IFLUSH	xk_flu
#define SYS$GLFILES	ps_gfil
#define SYS$GRFILES	ps_xfil
#define SYS$PRMSG	ps_pmsg
#define SYS$ARMABORT	ps_arm
#define SYS$CANABORT	ps_can
#define SYS$USAGE	ps_usage
#define SYS$SLEEP	sleep
#define SYS$EXIT	exits
#define SYS$SUCC	EX$SUC
#define SYS$WARN	EX$WAR
#define SYS$ERR		EX$ERR
#define SYS$FATL	EX$SEV
#endif


/*
 *  m a i n
 *
 *  Main routine - parse command and options, set up the
 *  tty line, and dispatch to the appropriate routine.
 */

main(argc,argv)
int argc;				/* Character pointers to and count of */
char **argv;				/* command line arguments */
{
    char *cp;				/* char pointer */
    int speed,				/* speed of assigned tty, */
	rflg, 				/* Operation:	Receive */
	sflg,				/*		Send */
	gflg,				/*		Get (server rec.) */
	glflg,				/*		Logout */
	gfflg;				/*		Finish */

    char tolower();

    exstat = SYS$SUCC;			/* Assume we'll succeed */

    if (argc < 2) SYS$USAGE();		/* Make sure there's a command line */

    cp = *++argv; argv++; argc -= 2;	/* Set up pointers to args */

    /*  
     * Initialize these values and hope the 
     * first packet will get across OK 
     */
    eol = CR;				/* EOL for outgoing packets */
    quote = '#';			/* Standard control-quote char "#" */
    pad = 0;				/* No padding */
    padchar = NULL;			/* Use null if any padding wanted */

    /*
     * 'Parse' (I abhor that word!) the command line & set
     * various flags.
     */
    sflg = rflg = gflg = glflg = gfflg = 0;	/* Turn off all parse flags */
    speed = DEF_BAUD;			/* Set default baud rate */
    image = FALSE;			/* ASCII mode by default */
    stripfs =TRUE;			/* Strip filespecs to "normal" form */
    if(COMM_DEV == NULL)		/* If hardwired comm line, then ... */
	remote = FALSE;			/* ... we cannot act as a remote */
    else				/* Otherwise ... */
        remote = TRUE;			/* Assume remote unless 'l' arg */
    
    while ((*cp) != NULL)		/* Parse characters in first arg. */
	switch (tolower(*cp++))
	{
	    case 's': sflg++; break;	/* S = Send command */
	    case 'r': rflg++; break;	/* R = Receive command */
	    case 'g': gflg++; break;	/* G = Get command (server rec.) */
	    case 'q': glflg++; break;	/* Q = Generic Logout (server) */
	    case 'e': gfflg++; break;	/* E = Generic finish (server) */

	    case 'd':			/* D = Increment debug mode count */
		debug++; break;
		
	    case 'f':
		stripfs = FALSE;	/* F = don't strip filespecs  */
		break;

	    case 'i':
		image = TRUE;		/* I = image mode */
		break;

	    case 'b':			/* B = specify baud rate */
		if (argc--) speed = atoi(*argv++);
		else SYS$USAGE();
		if (debug) printf("Line speed to remote host is %d\n",speed);
		break;

	    case 'l':			/* L = Specify comm device spec */
		if (argc--) commdev = *argv++;
		else SYS$USAGE();
		remote = FALSE;		/* We are in local mode */
		if (debug) printf("Line to remote host is %s\n", commdev);
		break;

	    default:			/* Unknown option */
                SYS$USAGE();
	}
					/* Done parsing */
    if ((sflg+rflg+gflg+glflg+gfflg) != 1) /* Only one command allowed */
	SYS$USAGE();

    /*
     * Set up the Communications Port as required by KERMIT
     * (and specified baud rate) after saving the current state.
     */
    SYS$INITIAL(speed,commdev);

    /* 
     * All set up, now execute the command that was given. 
     */
    if (debug)
    {
	printf("Debugging level = %d\n\n",debug);

	if (sflg) printf("Send command\n\n");
	if (rflg) printf("Receive command\n\n");
	if (gflg) printf("Get command\n\n");
	if (glflg) printf("Generic logout\n\n");
	if (gfflg) printf("Generic finish\n\n");
    }
  
    if (sflg)				/* Send command */ 
    {
	filelist = argv;		/* Set up the file list */
	filecount = argc;		/* Number of files send */
	if(!gnxtfl())			/* Get 1st.  If none ... */
	{   if (remote)			/* Restore tty line if remote */
		SYS$RESTORE();
	    SYS$GLFILES();		/* Prompt user for local file list */
	    if (remote)			/* If remote, put tty back to raw */
		SYS$INITIAL();
	}
	if (sendsw() == FALSE)		/* Send the file(s) */
	    {
	    SYS$PRMSG("Send failed.");
	    exstat = SYS$ERR;
	    }
    }

    else if (rflg)				/* Receive command */
    {
	if (recsw() == FALSE)		/* Receive the file(s) */
	    {
	    SYS$PRMSG("Receive failed.");
	    exstat = SYS$ERR;
	    }
    }

    else if (gflg)				/* Get (server) command */ 
    {
	filelist = argv;		/* Set up the file list */
	filecount = argc;		/* Number of files send */
	if(filecount == 0)		/* If none ... */
	{   if (remote)			/* Restore tty line if remote */
		SYS$RESTORE();
	    SYS$GRFILES();		/* Prompt user for remote file list */
	    if (remote)			/* If remote, put tty back to raw */
		SYS$INITIAL();
	}
        strcpy(filnam, *filelist++);	/* Get 1st file */
        filecount--;

	if (getsw() == FALSE)		/* Get the file(s) */
	    {
	    SYS$PRMSG("Get failed.");
	    exstat = SYS$ERR;
	    }
    }

    else if(gfflg)			/* Generic finish */
	{
	if(gfinish() == FALSE)		/* If failed */
	    {
	    SYS$PRMSG("Failed to stop server.");
            exstat = SYS$ERR;
	    }
	else
	    SYS$PRMSG("Server KERMIT returned to command mode");
	}

    else if(glflg)
	{
	if(glogout() == FALSE)		/* If failed */
	    {
	    SYS$PRMSG("Failed to log out server.");
            exstat = SYS$ERR;
	    }
	else
	    SYS$PRMSG("Server KERMIT logged off.");
	}

    SYS$RESTORE();
    SYS$EXIT(exstat);
}


/*
 *  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.  If in remote mode,
 *  delays MYDELAY seconds before initiating.   
 *
 */

sendsw()
{
    char sinit(), sfile(), sdata(), seof(), sbreak();

    if(remote)				/* If we're a remote */
	{
	SYS$SLEEP(MYDELAY);		/* Delay for user to set up receive */
	aboflag = FALSE;		/* No aborts in remote mode */
	}
    else				/* If local */
	SYS$ARMABO(&aboflag);		/* Arm local user async abort */
    state = 'S';			/* Send initiate is the start state */
    n = 0;				/* Initialize message number */
    numtry = 0;				/* Say no tries yet */
    while(TRUE)				/* Do this as long as necessary */
    {
	if(aboflag)			/* If local user aborted */
	    {
	    SYS$PRMSG("User aborted file transfer");
	    switch(state)
		{
		case 'S':  state = 'A';  break;
		case 'F':  state = 'A';  break;
		case 'D':  state = 'Z';  break;
		}
	    }
	if (debug) printf("sendsw state: %c\n",state);
	switch(state)
	{
	    case 'S':	state = sinit();  break; /* Send-Init */
	    case 'F':	state = sfile();  break; /* Send-File */
	    case 'D':	state = sdata();  break; /* Send-Data */
	    case 'Z':	state = seof();	  break; /* Send-End-of-File */
	    case 'B':	state = sbreak(); break; /* Send-Break */
	    case 'C':	SYS$CANABO(); return (TRUE);	/* Complete */
	    case 'A':	SYS$CANABO(); return (FALSE);	/* "Abort" */
	    default:	SYS$CANABO(); return (FALSE);	/* Unknown, fail */
	}
    }
}


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

char sinit()
{
    int num, len;			/* Packet number, length */
    char  ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "sinit");	/* Say why */
	return('A');
	}

    spar(packet);			/* Fill up init info packet */

    SYS$IFLUSH();			/* Flush pending input */

    spack('S',n,10,packet);		/* Send an S packet */
    switch(ty = 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 = MYEOL;	/* Check and set defaults */
	    if (quote == 0) quote = '#';

	    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:			/* Anything else, just "abort" */
	    SYS$PRMSG(unkpkt, "sinit", ty);
	    return('A');
     }
 }


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

char sfile()
{
    int num, len;			/* Packet number, length */
    char filnam1[64],			/* Converted file name */
	*newfname,			/* Pointer to file name to send */
	*cp;				/* char pointer */
    char ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "sfile");	/* Say why */
	return('A');
	}
    
    if (filnam[0] == NULL)		/* If 1st wild open not done */
    {
	if (fp == NULL || fnext(fp) == NULL) /* If bad spec or no wild files */
	{
	    error("Cannot open file \"%s\"  RMS-11 code = %d", filpat, $$ferr);
	    return('A');
	}
        fgetname(fp, filnam);		/* New filespec, get (1st) name */
    }

    /*
     * If the 'stripfs' flag is set, convert the filename to KERMIT
     * "normal form" (at least take a good crack at it!).
     */
    if(stripfs)
    {
	strcpy(filnam1, filnam);	/* Make a copy of the file spec */

	if(cp = strrchr(filnam1, ';'))	/* Strip version number */
	    *cp = '\0';

	if(!(newfname = strrchr(filnam1, ']')))	/* Bypass NODE::DEV:[DIR] */
	{
	    if(!(newfname = strrchr(filnam1, ':')))
		newfname = filnam1;
	    else
		newfname++;
	}
	else
	    newfname++;

	if(strchr(filnam1, '.') == NULL)  /* If no '.' */
	    strcat(filnam1, '.');	/* Tack one on */
    }
    else
	newfname = filnam;

    SYS$PRMSG("Sending %s as %s",filnam,newfname);

    len = strlen(newfname);
    spack('F',n,len,newfname);		/* Send an F packet */
    switch(ty = 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:			/* Something else, just "abort" */
	    SYS$PRMSG(unkpkt, "sfile", ty);
	    return('A');
    }
}

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

char sdata()
{
    int num, len;			/* Packet number, length */
    char ty;				/* Received packet type */

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "sdata");	/* Say why */
	return('A');
	}

    spack('D',n,size,packet);		/* Send a D packet */
    switch(ty = 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:			/* Anything else, "abort" */
	    SYS$PRMSG(unkpkt, "sdata", ty);
	    return('A');
    }
}


/*
 *  s e o f
 *
 *  Send End-Of-File.
 *
 * If the user-abort flag is on, stick a 'D' into the data
 * field, telling the remote to junk the just sent file,
 * and skip the rest of the file list, going directly to
 * state 'B'.
 */

char seof()
{
    int num, len;			/* Packet number, length */
    char ty;				/* Received packet type */

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "seof");	/* Say why */
	return('A');
	}

    if(aboflag)				/* User abort? */
	{
	packet[0] = 'D';		/* Yes, tell remote to junk file */
	len = 1;
	}
    else
	len = 0;

    spack('Z',n,len,packet);		/* Send a 'Z' packet */
    switch(ty = 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) printf("	  Closing input file %s, ",filnam);
	    if(aboflag)			/* If aborting */
		return('B');		/* Skip rest of files */
	    /*
	     * Attempt to open the "next" file in a wild sequence,
	     * or if failed, get the next filespec in the list &
	     * try again.
	     */
	    if(debug) printf("    Looking for next file ...\n");
	    if(fnext(fp) == NULL)	/* If no more wild matches */
	    {
		if(gnxtfl() == FALSE)	/* If no more specs in list */
		    return('B');	/* Break, EOT, all done */
	    }
	    else
		fgetname(fp, filnam);	/* Next wild file is ready */

	    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:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "seof", ty);
	    return('A');

    }
}


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

char sbreak()
{
    int num, len;			/* Packet number, length */
    char ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "sbreak");	/* Say why */
	return('A');
	}


    spack('B',n,0,packet);		/* Send a B packet */
    switch (ty = 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:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "sbreak", ty);
	    return('A');

   }
}


/*
 *  r e c s w
 *
 *  This is the state table switcher for receiving files.
 *
 * Respond crudely to user abort requests.  We really should ACK
 * with 'Z' in the data field, then send an error packet only if
 * the sender fails to respond to the ACK+'Z'.  But ...
 */

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

    state = 'R';			/* Receive-Init is the start state */
    n = 0;				/* Initialize message number */
    numtry = 0;				/* Say no tries yet */

    if(remote)				/* If remote */
	aboflag = FALSE;		/* No aborts */
    else
	SYS$ARMABO(&aboflag);		/* Arm user abort */

    while(TRUE)
    {
	if(aboflag)			/* If local user aborted */
	    {
	    SYS$PRMSG("User aborted file transfer");
	    spack('E', n, 0, 0);	/* Kill sender crudely */
            state = 'A';
	    }
	if (debug) printf(" 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 */
	    case 'C':	SYS$CANABO();  return(TRUE);	/* Complete state */
	    case 'A':	SYS$CANABO();  return(FALSE);	/* "Abort" state */
	}
    }
}

    
/*
 *  r i n i t
 *
 *  Receive Initialization
 */
  
char rinit()
{
    int len, num;			/* Packet length, number */
    char ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "rinit");	/* Say why */
	return('A');
	}


    switch(ty = rpack(&len,&num,packet))	/* Get a packet */
    {
	case 'S':			/* Send-Init */
	    rpar(packet);		/* Get the other side's init data */
	    spar(packet);		/* Fill up packet with my init info */
	    spack('Y',n,10,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,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "rinit", ty);
	    return('A');

    }
}


/*
 *  r f i l e
 *
 *  Receive File Header
 */
static char recfname[64];		/* File currently being received */

char rfile()
{
    int num, len;			/* Packet number, length */
    char filnam1[64];			/* Work buffer for stripper */
    register char *dp, *cp;		/* Stripper work pointers */
    char ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "rfile");	/* Say why */
	return('A');
	}

    switch(ty = rpack(&len,&num,packet))	/* Get a packet */
    {
	case 'S':			/* Send-Init, maybe our ACK lost */
	    if (oldtry++ > MAXTRY)	/* If too many tries, give up */
	    {
		SYS$PRMSG(retfmt, "rfile ('S' ACK)");	/* Say why */
		return('A');
	    }
	    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,10,packet);
		numtry = 0;		/* Reset try counter */
		return(state);		/* Stay in this state */
	    }
	    else			/* Not previous packet, "abort" */
	    {
		SYS$PRMSG(badpkt, "rfile", ty);
		return('A');
	    }

	case 'Z':			/* End-Of-File */
	    if (oldtry++ > MAXTRY)	/* If too many tries, give up */
	    {
		SYS$PRMSG(retfmt, "rfile ('Z' ACK)");	/* Say why */
		return('A');
	    }


	    if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
	    {				/* Yes, ACK it again. */
		spack('Y',num,0,0);
		numtry = 0;
		return(state);		/* Stay in this state */
	    }
	    else			/* Not previous packet, "abort" */
	    {
		SYS$PRMSG(badpkt, "rfile", ty);
		return('A');
	    }

	case 'F':			/* File Header (just what we want) */
	    if (num != n)		/* The packet number must be right */
	    {
		SYS$PRMSG(badpkt, "rfile", ty);
		return('A');
	    }
	    strcpy(filnam1, packet);	/* Copy the file name */

	    /*
	     * The following attempts to strip incoming filespecs to
	     * KERMIT "normal form". 
	     */
	    if (stripfs)
	    {
		/*
		 * Point dp at the '.', adding one at the end if
		 * there isn't one.  Use the 1st '.' in the spec.
		 */
		while((dp = strchr(filnam1, '.')) == NULL)
		    strcat(filnam1, '.');
		/*
		 * Truncate spec at 3rd alphameric after the dot
		 * or 1st non-alphameric, whichever comes first.
		 */
		cp = dp + 1;
		while((*cp != NULL) && isalnum(*cp))
		    if((cp - dp) > 3)
			break;
		    else
			cp++;
		*cp = '\0';
		/*
		 * Back up from '.' till we hit 1st alphameric or the
		 * beginning.  Then use up to 9 alphamerics as the 
		 * file name.
		 */
		cp = dp - 1;
		while((cp > filnam1) && isalnum(*cp))
		    --cp;
		if(!isalnum(*cp))
		    cp++;
		if((dp - cp) > 9)
		{
		    cp[9] = '\0';
		    strcpy(recfname, cp);
		    strcat(recfname, dp);
		}
		else
		    strcpy(recfname, cp);
	    }
	    else
		strcpy(recfname, filnam1);

	    if ((fp=fopen(recfname, (image)?"wn":"w"))==NULL) /* Open new file */
	    {
		error("Cannot create %s",recfname); /* Give up if can't */
		return('A');
	    }
	    else			/* OK, give message */
		SYS$PRMSG("Receiving %s as %s",packet,recfname);

	    spack('Y',n,0,0);		/* 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)		/* Need right packet number here */
	    {
		SYS$PRMSG(badpkt, "rfile", ty);
		return('A');
	    }
	    spack('Y',n,0,0);		/* 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,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "rfile", ty);
	    return('A');

    }
}


/*
 *  r d a t a
 *
 *  Receive Data
 *
 *  Recognize 'D' in end-of-file packet as "delete file" signal
 */

char rdata()
{
    int num, len;			/* Packet number, length */
    char ty;				/* Received packet type */
    char rpack();

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "rdata");	/* Say why */
	return('A');
	}

    switch(ty = rpack(&len,&num,packet))	/* Get packet */
    {
	case 'D':			/* Got Data packet */
	    if (num != n)		/* Right packet? */
	    {				/* No */
	        if (oldtry++ > MAXTRY)	/* If too many tries, give up */
		{
		    SYS$PRMSG(retfmt, "rdata ('D' ACK)"); /* Say why */
		    return('A');
		}
		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			/* sorry, wrong number */
		{
		    SYS$PRMSG(badpkt, "rdata", ty);
		    return('A');
		}
	    }
	    /* Got data with right packet number */
	    bufemp(packet,len);		/* Write the data to the file */
	    spack('Y',n,0,0);		/* Acknowledge the packet */
	    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)	/* If too many tries, give up */
	    {
		SYS$PRMSG(retfmt, "rdata ('F' ACK)");	/* Say why */
		return('A');
	    }
	    if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
	    {				/* It was the previous one */
		spack('Y',num,0,0);	/* ACK it again */
		numtry = 0;		/* Reset try counter */
		return(state);		/* Stay in Data state */
	    }
	    else			/* sorry, wrong number */
	    {
	        SYS$PRMSG(badpkt, "rdata", ty);
	        return('A');
	    }

	case 'Z':			/* End-Of-File */
	    if (num != n) return('A');	/* Must have right packet number */
	    spack('Y',n,0,0);		/* OK, ACK it. */
	    fclose(fp);			/* Close the file */
	    if(len > 0 && packet[0] == 'D')	/* Sender says "Junk it"? */
		delete(recfname);		/* Yup ... junk 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,0);		/* Return a NAK */
	    return(state);		/* Keep trying */

	default:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "rdata", ty);
	    return('A');

    }
}


/*
 *  g e t s w 
 *
 * This is the state table switcher for 'get' from remote
 * server.  Mimics the receiving files state machine after
 * kicking the server in the butt.  Called for each file
 * in the list.
 */

getsw()
{
    char iinit(), srcmd(), rinit(), rfile(), rdata(), gcomp();

    state = 'I';			/* Init-Info is start state */
    n = 0;				/* Initialize message number */
    numtry = 0;				/* No tries yet */

    if(remote)				/* If remote */
	aboflag = FALSE;		/* No aborts */
    else
	SYS$ARMABO(&aboflag);		/* Arm user abort */

    while(TRUE)				/* Cycle ... */
    {
	if(aboflag)			/* If local user aborted */
	    {
	    SYS$PRMSG("User aborted file transfer");
	    if(state != 'R' && state != 'G')	/* If it got started */
	    	spack('E', n, 0, 0);	/* Kill sender crudely */
            state = 'A';
	    }
	if(debug) printf(" getsw state: %c\n", state);
	switch(state)
	{
	    case 'I':	state = iinit(); break;     /* Init-Info */
	    case 'G':	state = srcmd(); break;     /* Send "R" packet */
	    case 'R':	state = rinit(); break;	    /* Receive-Init */
	    case 'F':	state = rfile(); break;     /* Receive-File */
	    case 'D':	state = rdata(); break;     /* Receive-Data */
	    case 'C':	state = gcomp(); break;	    /* Request complate  */
	    case 'B':	SYS$CANABO(); return(TRUE);	/* End of requests */
	    case 'A':	SYS$CANABO(); return(FALSE);	/* "Abort" state */
	}
    }
}

/*
 *  i i n i t
 *
 *  Initialize Info:
 *	Send this host's parameters and get other side's back.
 *	Used for servers only.
 */
char iinit()
{
    int num, len;			/* Packet number, length */
    char  ty;				/* Received packet type */
    char rpack();


    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "iinit");	/* Say why */
	return('A');
	}

    spar(packet);			/* Fill up init info packet */

    SYS$IFLUSH();			/* Flush pending input */

#ifdef NOIPACKETS
    return('G');			/* If I packets not supported */
#endif

    spack('I',n,10,packet);		/* Send an I packet */
    switch(ty = 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 I state */
		return(state);		/* and try again */
	    rpar(recpkt);		/* Get other side's init info */

	    if (eol == 0) eol = MYEOL;	/* Check and set defaults */
	    if (quote == 0) quote = '#';

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

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

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

	default:			/* Anything else, just "abort" */
	    SYS$PRMSG(unkpkt, "iinit", ty);
	    return('A');
     }
}


/*
 *  s r c m d
 *
 *  Send 'R' packet to remote server.  Assumes filename is 'good'
 *  so doesn't strip to "normal firm".
 */

char srcmd()
{
    int len;				/* Packet number, length */

    if (numtry++ > MAXTRY)		/* If too many tries, give up */
	{
	SYS$PRMSG(retfmt, "srcmd");	/* Say why */
	return('A');
	}

    SYS$PRMSG("Requesting %s from server",filnam);

    len = strlen(filnam);

    SYS$IFLUSH();			/* Junk useless NAKs etc. */

    spack('R',n,len,filnam);		/* Send an R packet */
    return('R');			/* Immediately to 'R' state */
}

/*
 * g c o m p 
 *
 * Complete state for server receive (GET)
 */
char gcomp()
   {
   if(filecount-- == 0)			/* If no more files to request */
      return('B');			/* Really "complete" */
   strcpy(filnam, *filelist++);		/* More, get next remote spec */
   n = numtry = 0;			/* Reset counters */
   return('I');				/* Back to server init */
   }

/*
 * g f i n i s h
 *
 * GENERIC FINISH - Tell server to exit
 */
gfinish()
   {
   return(sgeneric('F'));
   }

/*
 * g l o g o u t
 *
 * GENERIC LOGOUT - Tell server to logout
 */
glogout()
   {
   return(sgeneric('L'));
   }

/*
 * s g e n e r i c
 *
 * Send a "generic" packet.  Always sequence number 0, wait for
 * ACK.  Return TRUE if ACK'ed OK, else FALSE if retries exhausted.
 * No state machine needed here.
 */
sgeneric(code)
char code;
   {
   int num, len;
   char ty;
   char rpack();

   n = numtry = 0;
   packet[0] = code;			/* Load the generic command */
   while(TRUE)				/* Cycle ... */
      {
      if(numtry++ > MAXTRY)		/* Fail if retries done */ 
         {
         SYS$PRMSG(retfmt, "sgeneric");
         return(FALSE);
         }

      spack('G', n, 1, packet);		/* Send generic command */
      switch(ty = rpack(&len, &num, recpkt))
         {
         case FALSE:			/* Receive failure, try again */
	 case 'N':			/* NAK, try again */
	    continue;

	 case 'Y':			/* ACK */
	    if (n != num)		/* If wrong sequence number */
               continue;		/* Try again */
            else
	       return(TRUE);		/* Operation complete */

	 case 'E':			/* Error packet received */
	    prerrpkt(recpkt);		/* Print it out and */
	    return(FALSE);		/* Report failure */

	 default:			/* Something else, "abort" */
	    SYS$PRMSG(unkpkt, "sbreak", ty);
	    return('A');
         }
      }
   }

/*
 *	KERMIT utilities.
 */

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

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

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

    bufp = buffer;			/* Reset buffer pointer */
    *bufp++ = SOH;			/* Packet marker, ASCII 1 (SOH) */
    *bufp++ = len+3+' ';		/* Send the character count */
    chksum  = len+3+' ';		/* Initialize the checksum */
    *bufp++ = num+' ';			/* Packet number */
    chksum += 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++ = chksum+' ';		/* Put it in the packet */
    *bufp++ = eol;			/* Extra-packet line terminator */
    if(debug > 1)
	printf("    checksum:  %03o\n",chksum);
    SYS$XMIT(buffer,bufp-buffer);	/* Send the packet */
}

/*
 *  r p a c k
 *
 *  Read a Packet
 */

char rpack(len,num,data)
int *len, *num;				/* Packet length, number */
char *data;				/* Packet data */
{
    int i, done;			/* Data character number, loop exit */
    char t,				/* Current input character */
	type,				/* Packet type */
	rchksum;			/* Checksum received from other host */

    register unsigned int cchksum;	/* Our (computed) checksum */

					/* Handle silly foreign timeouts */
    if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;

    t = 0377;				/* Make t != SOH */
    while (t != SOH)			/* Wait for packet header */
    {
	if(SYS$RCHAR(&t, timint) == FALSE)	/* Read char with timeout */
	    return(FALSE);		/* (timed out) */
	t &= 0177;			/* Handle parity */
    }

    done = FALSE;			/* Got SOH, init loop */
    while (!done)			/* Loop to get a packet */
    {
	if(SYS$RCHAR(&t, timint) == FALSE)	/* Read char with timeout */
	    return(FALSE);		/* (timed out) */
	if (!image) t &= 0177;		/* If ASCII, strip 8th bit */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = t;			/* Start the checksum */
	*len = t - ' ' - 3;		/* Character count */

	if(SYS$RCHAR(&t, timint) == FALSE)	/* Read char with timeout */
	    return(FALSE);		/* (timed out) */
	if (!image) t &= 0177;		/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = cchksum + t;		/* Update checksum */
	*num = t - ' ';		/* Packet number */

	if(SYS$RCHAR(&t, timint) == FALSE)	/* Read char with timeout */
	    return(FALSE);		/* (timed out) */
	if (!image) t &= 0177;		/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	cchksum = cchksum + t;		/* Update checksum */
	type = t;			/* Packet type */

	for (i=0; i<*len; i++)		/* The data itself, if any */
	{				/* Loop for character count */
	    if(SYS$RCHAR(&t, timint) == FALSE)	/* Read char with timeout */
		return(FALSE);		/* (timed out) */
	    if (!image) t &= 0177;	/* Handle parity */
	    if (t == SOH) continue;	/* Resynch if SOH */
	    cchksum = cchksum + t;	/* Update checksum */
	    data[i] = t;		/* Put it in the data buffer */
	}
	data[*len] = 0;			/* Mark the end of the data */

	if(SYS$RCHAR(&t, timint) == FALSE)	/* Read checksum with timeout */
	    return(FALSE);		/* (timed out) */
	rchksum = t - ' ';		/* Convert to numeric */
	if(SYS$RCHAR(&t, timint) == FALSE) /* Read EOL with timeout & junk it */
	    return(FALSE);		/* (timed out) */
	if (!image) t &= 0177;		/* Handle parity */
	if (t == SOH) continue;		/* Resynchronize if SOH */
	done = TRUE;			/* Got checksum, done */
    }

    if (debug>1)			/* Display incoming packet */
    {
	if (data != NULL)
	    data[*len] = '\0';		/* Null-terminate data to print it */
	printf("  rpack type: %c\n",type);
	printf("	 num:  %d\n",*num);
	printf("	 len:  %d\n",*len);
	if (data != NULL)
	    printf("	    data: \"%s\"\n",data);
    }
					/* Fold in bits 7,8 to compute */
    cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */

    if ((char)cchksum != rchksum) 
	{
	if(debug > 1)
	    printf("Bad checksum.  Ours = %03o  Packet = %03o\n",
		(char)cchksum, rchksum);
	return(FALSE);
	}

    return(type);			/* All OK, return packet type */
}


/*
 *  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++] = CR ^ 64;
	    }
	    buffer[i++] = quote;	/* Quote the character */
	    if (t7 != quote)
	    {
		t ^= 64;		/* and uncontrolify */
		t7 ^= 64;
	    }
	}
	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.  Strip <CR> only
 *  from <CR><LF> sequences.
 */
static int cr_seen = FALSE;		/* Newline hacking flag */

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

    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 ^= 64;		/* No, uncontrollify it */
	}

	if(!image)			/* If not image mode */
	{				/* Convert only <CR> to <CR><LF> */
	    if(t == CR && !cr_seen)	/* If 1st <CR> */
	    {
		cr_seen == TRUE;	/* Note it */
	    	continue;		/* For later */
	    }
	    else
	    {
		if(cr_seen && t != '\n') /* Put out last CR if this not LF */
		    putc(CR, fp);
		if(t != CR)		/* Clear CR flag unless this is CR */
		    cr_seen = FALSE;
	    }
        }

	putc(t,fp);
    }
}


/*
 *  g n x t f l
 *
 *  Get next file in a file group & handle wild filespecs
 */
gnxtfl()
{
    if (filecount-- == 0) return(FALSE); /* If no more, fail */
    if (debug) printf("	  gnxtfl: filelist = \"%s\"\n",*filelist);
    strcpy(filpat, *(filelist++));	/* Get the filespec pattern */
    fp = fwild(filpat, (image)?"rn":"r"); /* Set up possible wildcarding */
    filnam[0] = '\0';			/* Mark 1st fnext() not done */
    return(TRUE);			/* Let sfile() handle errors */
}


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

spar(data)
char data[];
{
    register char *cp;			/* This reduces code size */

    cp = data;
    *cp++ = MAXPACKSIZ + ' ';		/* Biggest packet I can receive */
    *cp++ = MYTIME + ' ';		/* When I want to be timed out */
    *cp++ = MYPAD + ' ';		/* How much padding I need */
    *cp++ = MYPCHAR + ' ';		/* Padding character I want */
    *cp++ = MYEOL + ' ';		/* End-Of-Line character I want */
    *cp++ = MYQUOTE;			/* Control-Quote character I send */
    *cp++ = MYQBIN;			/* Binary quote character I use */
    *cp++ = MYCHKT;			/* Checksum method I use */
    *cp++ = MYREPT;			/* My run-length encoding char */
    *cp++ = MYCAPAS1 + ' ';		/* My capabilities mask 1 */
}


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

rpar(data)
char data[];
{
    register char *cp;

    cp = data;
    spsiz = *cp++ - ' ';		/* Maximum send packet size */
    timint = *cp++ - ' ';		/* When I should time out */
    pad = *cp++ - ' ';			/* Number of pads to send */
    padchar = *cp++ ^ 64;		/* Padding character to send */
    eol = *cp++ - ' ';			/* EOL character I must send */
    quote = *cp++;			/* Incoming data quote character */
    qbin = *cp++;			/* Binary quoting character */
    chkt = *cp++;			/* His checksum type */
    rept = *cp++;			/* His run-length encoding char */
    capas1 = *cp - ' ';			/* His capabilities mask 1 */
}
 

/*
 *  Kermit printing routines:
 *
 *  error - Calls SYS$PRMSG if local kermit; sends a error packet if remote
 *  prerrpkt - Calls SYS$PRMSG to print incoming error packet
 */


/*
 *  e r r o r
 *
 *  Print error message.
 *
 *  If local, print error message with SYS$PRMSG.
 *  If remote, send an error packet with the message.
 */

/*VARARGS1*/
error(fmt, a1, a2, a3, a4, a5)
char *fmt;
{
    char msg[80];
    int len;

    if (remote)
    {
	sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
	len = strlen(msg);
	spack('E',n,len,msg);		/* Send the error packet */
    }
    else
	SYS$PRMSG(fmt, a1, a2, a3, a4, a5);

    return;
}

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