[comp.sources.amiga] v89i183: vt100 - terminal emulator v2.9, Part06/09

page%swap@Sun.COM (Bob Page) (10/20/89)

Submitted-by: acs@pccuts.pcc.amdahl.com (Tony Sumrall)
Posting-number: Volume 89, Issue 183
Archive-name: comm/vt100r29.6

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	kermit.c
#	kermitproto.c
# This is archive 6 of a 9-part kit.
# This archive created: Thu Oct 19 22:30:31 1989
echo "extracting kermit.c"
sed 's/^X//' << \SHAR_EOF > kermit.c
X/*************************************************************
X * vt100 terminal emulator - KERMIT protocol support
X *			:ts=8
X *
X *	v2.9 ACS - Kermit shouldn't NAK packet 0 but timeout waiting for
X *		   send-init then NAK if necessary; terminate each packet with
X  *		   EOL only; don't sendstring("\r") but sendchar() it.
X *	v2.8a 880230 ACS - saybye() won't do anything if not in kermit
X *			  mode.
X *	v2.7 870825 ACS - Fixed the "multiple-send" problem in
X *			  doksend() et al; show status using the *InfoMsg*()
X *			  routines in window.c; fixed erroneous calls to
X *			  spack() and rpack(); better error handling.
X *	v2.6 870227 DBW - bug fixes for all the stuff in v2.5
X *	v2.5 870214 DBW - more additions (see readme file)
X *	v2.4 861214 DBW - lots of fixes/additions (see readme file)
X *	v2.3 861101 DBW - minor bug fixes
X *	v2.2 861012 DBW - more of the same
X *	v2.1 860915 DBW - new features (see README)
X *	     860901 ACS - Added eight bit quoting
X *	     860830 Steve Drew Wild card support, err recovry,bugs.
X *	     860823 DBW - Integrated and rewrote lots of code
X *	     860811 Steve Drew multi filexfer, bugs, status line ect..
X *	v2.0 860809 DBW - Major rewrite
X *	v1.1 860720 DBW - Switches, 80 cols, colors, bug fixes
X *	v1.0 860712 DBW - First version released
X *
X *************************************************************/
X
X#include "vt100.h"
X
X#define MAXPACKSIZ 94	    /* Maximum msgpkt size */
X#define CR	   13	    /* ASCII Carriage Return */
X#define LF	   10	    /* ASCII line feed */
X#define SP	   32	    /* ASCII space */
X#define DEL	  127	    /* Delete (rubout) */
X
X#define MAXTRY	  5	   /* Times to retry a msgpkt */
X#define MYQUOTE  '#'	   /* Quote character I will use */
X#define MYRPTQ	 '~'	   /* Repeat quote character */
X#define MYEBQ	 '&'	   /* 8th bit prefix character */
X#define MYPAD	   0	   /* Number of padding charss I will need */
X#define MYPCHAR    0	   /* Padding character I need (NULL) */
X#define MYEOL	'\n'	/* End-Of-Line character I need */
X#define IDOLONG	2	/* I do LONG packets! */
X
X#define tochar(ch)  ((ch) + ' ')
X#define unchar(ch)  ((ch) - ' ')
X#define ctl(ch)     ((ch) ^ 64 )
X
X/* Global Variables */
X
Xint sending,	/* Indicates that we're sending, not receiving */
X    lastpkt,	/* Last successful packet # sent/received */
X    ulp,		/* Using LONG packets */
X    size,	/* Size of present data */
X    osize,	/* Size of last data entry */
X    rpsiz,	/* Maximum receive msgpkt size */
X    spsiz,	/* Maximum send msgpkt size */
X    timint,	/* Time interval to wait */
X    pad,		/* How much padding to send */
X    n,		/* Packet number */
X    tp,		/* total packets */
X    numtry,	/* Times this msgpkt retried */
X    retry,	/* total retries */
X    oldtry,	/* Times previous msgpkt retried */
X    sendabort,	/* flag for aborting send file  */
X    rptflg,	/* are we doing repeat quoting */
X    ebqflg,	/* are we doing 8th bit quoting */
X    notfirst,	/* is this the first file received */
X    first,	/* is this the first time in a file */
X    rpt,		/* current repeat count */
X    next,	/* what is the next character */
X    t;		/* current character */
Xlong totbytes;	/* total bytes xfered on this file */
X
Xchar state,	/* Present state of the automaton */
X    padchar,	/* Padding character to send */
X    eol,		/* End-Of-Line character to send */
X    quote,	/* Quote character in incoming data */
X    rptq,	/* Quote character for repeats */
X    ebq,		/* Quote character for 8th bit quoting */
X    ackpkt[MAXPACKSIZ+20],	/* ACK/NAK packet buffer */
X    *msgpkt = NULL,		/* Message Packet buffer is AllocMem()d */
X    *spackbuf = NULL,	/* Dynamically allocated buffer for spack() */
X    filnam[40],	/* remote file name */
X    snum[10],
X    mainmode[10];
X
XFILE *fp;	/* file for send/receive */
X
Xstatic void spack(), print_our_err(), print_host_err(),
X	    dostats(), ClearBuffer();
X
Xchar *
Xgetfname(name)	/* returns ptr to start of file name from spec */
Xchar *name;
X{
X    int l;
X
X    l = strlen(name);
X    while(l && name[l] != '/' && name[l] != ':') l--;
X    if (name[l] == '/' || name[l] == ':') l++;
X    return(name += l);
X}
X
Xdoksend(file,more)
Xchar *file;
Xint more;
X{
X    int amount, c, wild;
X    char *p, **list = NULL;
X
X    msgpkt    = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
X    spackbuf  = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
X    sending = 1;
X    if (!strcmp(file,"$")) { saybye(); return(USERABORT); }
X    p = file;
X    while(*p && *p != '*' && *p != '?') p++;
X    if (*p) {
X	wild = TRUE;
X	list = expand(file, &amount);
X	if (list == NULL) InfoMsg1Line("KERMIT: No wild card match");
X    }
X    else {
X	wild = FALSE;
X	amount = 1;
X    }
X    /*	  The "multiple send" problem is brought about by attempting to
X    ** send multiple files in a single "batch" (via globbing, e.g. *.foo)
X    ** to a remote kermit that is NOT in server mode.  A 'Z' packet
X    ** (meaning end-of-file) is sent after each of the files with a 'B'
X    ** packet (meaning end-of-batch) coming after the last 'Z' packet.
X    ** The old code reset the packet # on each iteration.  We do it
X    ** outside of the for loop. */
X    n = lastpkt = 0;
X    ulp = 0;	/* Assume we won't use LONG packets */
X    for (c = 0; c < amount; c++) {
X	if (wild == TRUE) p = list[c];
X	    else  p = file;
X	strcpy(filnam,getfname(p));
X	ttime = TTIME_KERMIT;
X	tp = retry = numtry = 0; totbytes = 0L;
X	if ((fp = fopen(p,"r")) == NULL) {
X	    InfoMsg2Line("KERMIT: Can't open send file:", p);
X	    continue;
X	}
X	strcpy(mainmode,"SEND");
X	ClearBuffer();
X
X	/*  This is another piece of the multiple-send fix.  Sendsw() needs
X	** to know 1) that this is the first file so it can send a send-init
X	** packet and 2) if this is the last file so it can send a B packet
X	** to indicate end-of-batch.  The last piece of the multiple-send fix
X	** is in sendsw() itself. */
X	if ( sendsw(c == 0, c >= (amount-1)) ) /* Successful send? */
X	    ScrollInfoMsg(1);
X	fclose(fp);
X    }
X    free_expand(list);
X    FreeMem(spackbuf,	(long)(MAXLONGPKS+20));
X    FreeMem(msgpkt,	(long)(MAXLONGPKS+20));
X    msgpkt = spackbuf = NULL;
X    return(TRUE);
X}
X
Xdokreceive(file,more)
Xchar *file;
Xint more;
X{
X    int retval;
X
X    msgpkt    = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
X    spackbuf  = (char *)AllocMem((long)(MAXLONGPKS+20), MEMF_PUBLIC|MEMF_CLEAR);
X    ttime = TTIME_KERMIT;
X    sending = 0;
X    if (!strcmp(file,"$")) { saybye(); return(USERABORT); }
X    strcpy(filnam, file);
X    if (server) strcpy(mainmode,"GET");
X    else        strcpy(mainmode,"RECV");
X    tp =  lastpkt = retry = n =  numtry = notfirst = 0; totbytes = 0L;
X    ClearBuffer();
X    retval  = recsw();
X    FreeMem(spackbuf,	(long)(MAXLONGPKS+20));
X    FreeMem(msgpkt,	(long)(MAXLONGPKS+20));
X    msgpkt = spackbuf = NULL;
X    return(retval);
X}
X
Xsendsw(firstfile, lastfile)
Xint firstfile, lastfile; /* Multiple-send fix */
X{
X    char sinit(), sfile(), sdata(), seof(), sbreak();
X    sendabort = 0;
X    /* Multiple-send fix.  If this is the first file of the batch then enter
X    ** send-init state otherwise just enter send-file state. */
X    if(firstfile)
X	state = 'S';
X    else
X	state = 'F';
X    while(TRUE) {
X	switch(state) {
X	case 'S':   state = sinit();  break;
X	case 'F':   state = sfile();  break;
X	case 'D':   state = sdata();  break;
X	case 'Z':   state = seof();   break;
X	case 'B':   if (lastfile || sendabort) {
X			/* Multiple-send fix.  If this is the last file then
X			** send a B packet to indicate end-of-batch. */
X			state = sbreak();
X			break;
X		     }
X		     return(TRUE);	/* Otherwise, just return. */
X	case 'C':   if (sendabort) return(FALSE);
X		    else return(TRUE);
X	case 'E':   dostats('E',"ERROR");  /* so print the err and abort */
X		    print_host_err(ackpkt);
X		    return(FALSE);
X	case 'A':   if (timeout == USERABORT) {
X			timeout = GOODREAD;
X			n = (n+1)%64;
X			sendabort = 1;
X			dostats('A',"ABORT");
X			strcpy(msgpkt, "D");
X			state = 'Z';
X			break;
X		    }
X		    if (timeout == TIMEOUT) dostats('A',"TMOUT");
X		    else { /* protocol error dectected by us */
X			dostats('A',"ERROR");
X			print_our_err();
X		    }
X		    return(FALSE);
X	default:    return(FALSE);
X	}
X    }
X}
X
Xchar sinit()
X{
X    int num, len;
X
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X    spar(msgpkt);
X
X    spack('S',n,13,msgpkt);
X    switch(rpack(&len,&num,ackpkt)) {
X    case 'N':	return(state);
X    case 'Y':	if (n != num) return(state);
X		rpar(ackpkt, len);
X		if (eol == 0) eol = '\n';
X		if (quote == 0) quote = MYQUOTE;
X		numtry = 0;
X		retry--;
X		n = (n+1)%64;
X		return('F');
X    case 'E':	return('E');
X    case FALSE:	if (timeout == USERABORT) state = 'A';
X		return(state);
X    default:	return('A');
X    }
X}
X
Xchar sfile()
X{
X    int num, len;
X
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    spack('F',n,strlen(filnam),filnam);
X    switch(rpack(&len,&num,ackpkt)) {
X    case 'N':
X	num = (--num<0 ? 63:num);
X	if (n != num) return(state);
X    case 'Y':
X	if (n != num) return(state);
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	first = 1;
X	size = getpkt();
X	return('D');
X    case 'E':
X	return('E');
X    case FALSE:	if (timeout == USERABORT) state = 'A';
X		   return(state);
X    default:
X	return('A');
X    }
X}
X
Xchar sdata()
X{
X    int num, len;
X
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    spack('D',n,size,msgpkt);
X    switch(rpack(&len,&num,ackpkt)) {
X    case 'N':
X	num = (--num<0 ? 63:num);
X	if (n != num) return(state);
X    case 'Y':
X	if (n != num) return(state);
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	if ((size = getpkt()) == 0) return('Z');
X	return('D');
X    case 'E':
X	return('E');
X    case FALSE: if (timeout == USERABORT) state = 'A';
X		   return(state);
X    default:
X	return('A');
X    }
X}
X
Xchar seof()
X{
X    int num, len;
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X/*   if (timeout == USERABORT) {*/	/* tell host to discard file */
X/*	timeout = GOODREAD;	*/
X/*	  spack('Z',n,1,"D");	*/
X/*	  }			*/
X/*   else			*/
X	spack('Z',n,sendabort,msgpkt);
X    switch(rpack(&len,&num,ackpkt)) {
X    case 'N':
X	num = (--num<0 ? 63:num);
X	if (n != num) return(state);
X    case 'Y':
X	if (n != num) return(state);
X	numtry = 0;
X	dostats('Z',"DONE");
X	retry--;
X	n = (n+1)%64;
X	return('B');
X    case 'E':	return('E');
X    case FALSE:	return(state);
X    default:	return('A');
X    }
X}
X
Xchar sbreak()
X{
X    int num, len;
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    spack('B',n,0,msgpkt);
X    switch (rpack(&len,&num,ackpkt)) {
X    case 'N':
X	num = (--num<0 ? 63:num);
X	if (n != num) return(state);
X    case 'Y':
X	if (n != num) return(state);
X	dostats('B', "DONE");
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	return('C');
X    case 'E':	return('E');
X    case FALSE:	return(state);
X    default:	return ('A');
X    }
X}
X
X/* timeout equals USERABORT so lets end the file and quit  */
X/* when host receives 'Z' packet with "D" in data field he */
X/* should discard the file.				   */
X/*
Xsabort()
X{
X    dostats(' ',"ABORT");
X    n = (n+1)%64;
X    retry--;
X    state = 'Z';
X    while (state == 'Z') state = seof();
X    while (state == 'B') state = sbreak();
X    return(FALSE);
X}
X*/
X
X
Xrecsw()
X{
X    char rinit(), rfile(), rdata();
X    int first_time = 1;
X
X    state = 'R';
X
X    while(TRUE) {
X	switch(state) {
X	case 'R':	ulp = 0;	/* Assume we won't use LONG packets */
X			state = rinit();
X			break;
X	case 'Z':
X	case 'F':	state = rfile(first_time); first_time = 0;
X			break;
X	case 'D':	state = rdata();
X			break;
X	case 'C':	return(TRUE);
X	case 'E':
X	case 'A':   /* easy way to cleanly abort
X			should really send and ACK
X			with "X" in data field and
X			wait for host to abort but
X			not all kermits support
X			this feature.		*/
X			if (timeout == USERABORT){
X			    /* send an error packet back   */
X			    dostats('A',"ABORT");
X			    spack('E',n,12,"User aborted");
X		        }
X		        else if (timeout == TIMEOUT) {
X			    /* we timed out waiting */
X			    /* will we need to spack here ?*/
X			    dostats('A',"TMOUT");
X			}
X			/* must be 'E' from host or we detected a protocol 
X			** error */
X			else dostats('A',"ERROR");
X
X			if (state == 'E') print_host_err(msgpkt);
X			else if (timeout == GOODREAD) /* tell host why */
X			    print_our_err();
X			    /* will this kill all files ?*/
X			do  {
X			    ttime = 2;
X			    readchar();
X			}  while (timeout == GOODREAD);
X			fclose(fp);
X			sendchar('\r');
X			return(FALSE);
X	default:	return(FALSE);
X	}
X    }
X}
X
Xchar rinit()
X{
X    int len, num, temp;
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    if (server)
X	spack('R',n,strlen(filnam),filnam);
X
X    switch(rpack(&len,&num,msgpkt)) {
X    case 'S':
X	rpar(msgpkt, len);
X	/*   Rpar() will set ulp if we can use long packets.  We can't use
X	** that value right away cause we've gotta ACK with normal pkts. */
X	temp = ulp; ulp = 0;
X	spar(msgpkt);
X	spack('Y',n,13,msgpkt);
X	ulp = temp;	/* Restore using long pkts flag */
X	oldtry = numtry;
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	return('F');
X    case 'E':
X	return('E');
X    case 'N':		/* Other side NAKed us... */
X	return(state); /* ...so try again	  */
X    case FALSE:
X	if (timeout == USERABORT) return('A');
X	if (timeout == TIMEOUT)   return(state); /* Resend Rec-init on a timeout */
X	spack('N',n,0,"");
X	return(state);
X    default:
X	return('A');
X    }
X}
X
Xchar rfile(first_time)
Xint first_time;
X{
X    int num, len, temp;
X    USHORT a, a7, b8;
X    char *fileptr, *buf;
X
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    switch(rpack(&len,&num,msgpkt)) {
X    case 'S':
X	if (oldtry++ > MAXTRY) return('A');
X	if (num == ((n==0) ? 63:n-1)) {
X	    /*   Rpar() will set ulp if we can use long packets.  We can't use
X	    ** that value right away cause we've gotta ACK with normal pkts. */
X	    temp = ulp; ulp = 0;
X	    spar(msgpkt);
X	    spack('Y',num,13,msgpkt);
X	    ulp = temp;
X	    numtry = 0;
X	    return(state);
X	}
X	else return('A');
X    case 'Z':
X	if (oldtry++ > MAXTRY) return('A');
X	if (num == ((n==0) ? 63:n-1)) {
X	    spack('Y',num,0,"");
X	    ScrollInfoMsg(1);
X	    numtry = 0;
X	    return(state);
X	}
X	else return('A');
X    case 'F':
X	if (num != n) return('A');
X	if(!first_time) {	/* Scroll the Z packet line up */
X	    dostats('Z', "DONE");
X	    ScrollInfoMsg(1);
X	}
X	buf = msgpkt;
X	fileptr = filnam;
X	while ((a = *buf++) != '\0') { /* Terminator added by rpack() */
X	    if (rptflg) {
X		if (a == rptq) {
X		    rpt = unchar(*buf++);
X		    a = *buf++;
X		}
X	    }
X	    b8 = 0;
X	    if (ebqflg) {		/* 8th-bit prefixing? */
X		if (a == ebq) {		/* Yes, got an 8th-bit prefix? */
X		    b8 = 0200;		/* Yes, remember this, */
X		    a = *buf++;		/* and get the prefixed character. */
X		}
X	    }
X	    if (a == quote) {
X		a  = *buf++;
X		a7 = a & 0177;
X		if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
X	    }
X	    a |= b8;
X	    if (rpt == 0) rpt = 1;
X	    if (p_mode == 1 && a == '\r') continue;
X	    for (; rpt > 0; rpt--) *fileptr++ = a;
X	    *fileptr = '\0';	/* Terminate the filename */
X	}
X
X	if (p_convert) {
X	    char *p;
X	    p = &filnam[0];
X	    while (*p) { *p = tolower(*p); p++; }
X	}
X	if (notfirst) {
X	    totbytes = 0L;
X	    dostats('F',"RECV");
X	}
X	else {	/* is the first file so emit actual file name from host */
X	    notfirst++;
X	}
X	if ((fp = fopen(filnam,"w")) == NULL) {
X	    InfoMsg2Line("KERMIT: Unable to create file:", filnam);
X	    strcpy(msgpkt,"VT100 - Kermit - cannot create file: ");
X	    strcat(msgpkt,filnam);
X	    spack('E',n,strlen(msgpkt),msgpkt); /* let host know */
X	    dostats('E',"ERROR");
X	    return ('\0');	 /* abort everything */
X	}
X	spack('Y',n,0,"");
X	oldtry = numtry;
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	return('D');
X
X    /* totaly done server sending no more */
X    case 'B':
X	if (num != n) return ('A');
X	spack('Y',n,0,"");
X	dostats('B', "DONE");
X	ScrollInfoMsg(1);
X	retry--;
X	return('C');
X    case 'E':
X	return('E');
X    case FALSE:
X	if (timeout == USERABORT) return('A');
X	spack('N',n,0,"");
X	return(state);
X    default:
X	return ('A');
X    }
X}
X
Xchar rdata()
X{
X    int num, len;
X    retry++;
X    if (numtry++ > MAXTRY) return('A');
X
X    switch(rpack(&len,&num,msgpkt)) {
X    case 'D':
X	if (num != n) {
X	    if (oldtry++ > MAXTRY) return('A');
X	    if (num == ((n==0) ? 63:n-1)) {
X	       spack('Y',num,6,msgpkt);
X	       numtry = 0;
X	       return(state);
X	    }
X	    else return('A');
X	}
X	decode();
X	spack('Y',n,0,"");
X	oldtry = numtry;
X	numtry = 0;
X	retry--;
X	n = (n+1)%64;
X	return('D');
X    case 'Z':
X	if (num != n) return('A');
X	spack('Y',n,0,"");
X	n = (n+1)%64;
X	dostats('Z',"DONE");
X	retry--;
X	fclose(fp);
X	return('Z');
X    case 'F':
X	if (oldtry++ > MAXTRY) return('A');
X	if (num == ((n==0) ? 63:n-1)) {
X	    spack('Y',num,0,"");
X	    numtry = 0;
X	    return(state);
X	}
X    case 'E':
X	return('E');
X    case FALSE:
X	if (timeout == USERABORT) return('A');
X	spack('N',n,0,"");
X	return(state);
X    default:
X	return('A');
X    }
X}
X
Xstatic void
Xspack(type,num,len,data)
Xchar type, *data;
Xint num, len;
X{
X    int i;
X    char chksum, t;
X    register char *bufp;
X
X    if(sending && (lastpkt != num)) {
X	tp++;
X	lastpkt = num;
X    }
X    dostats(type,mainmode);
X    bufp = spackbuf;
X    ClearBuffer();
X
X    for (i=1; i<=pad; i++) sendchar(padchar);
X
X    *bufp++ = SOH;
X    if(ulp && len > (MAXPACKSIZ-3))	/* Using long packets */
X	t = tochar(0);
X    else
X	t = tochar(len+3);
X    *bufp++ = t; chksum  = t;
X    t = tochar(num);
X    *bufp++ = t; chksum += t;
X    *bufp++ = type; chksum += type;
X    if(ulp && len > (MAXPACKSIZ-3)) {	/* Using long packets */
X	unsigned int pl = len + 1;
X
X	t = tochar(pl / 95);
X	*bufp++ = t; chksum += t;
X	t = tochar(pl % 95);
X	*bufp++ = t; chksum += t;
X	t = tochar((((chksum&0300) >> 6)+chksum)&077);
X	*bufp++ = t; chksum += t;
X    }
X    for (i=0; i<len; i++) {
X	*bufp++ = data[i]; chksum += data[i];
X    }
X    chksum = (((chksum&0300) >> 6)+chksum)&077;
X    *bufp++ = tochar(chksum);
X    if (eol)
X	*bufp++ = eol; /* Use sender's requested end-of-line */
X    else
X	*bufp++ = '\r';
X    *bufp   = '\0';
X    sendstring(spackbuf);
X}
X
Xrpack(len,num,data)
Xint *len, *num;
Xchar *data;
X{
X    int i, done;
X    char type, cchksum, rchksum;
X    char t = '\0';
X
X    do {
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X    } while (t != SOH);
X
X    done = FALSE;
X    while (!done) {
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X	if (t == SOH) continue;
X	cchksum = t;
X	*len = unchar(t)-3;
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X	if (t == SOH) continue;
X	cchksum += t;
X	*num = unchar(t);
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X	if (t == SOH) continue;
X	cchksum += t;
X	type = t;
X	if((*len == -3) && ulp) {	/* Using long packets */
X	    t = readchar();
X	    if (timeout != GOODREAD) return(FALSE);
X	    if (t == SOH) continue;
X	    cchksum += t;
X	    *len = unchar(t)*95;
X	    t = readchar();
X	    if (timeout != GOODREAD) return(FALSE);
X	    if (t == SOH) continue;
X	    cchksum += t;
X	    *len += unchar(t);
X	    (*len)--;
X	    t = readchar();
X	    if (timeout != GOODREAD) return(FALSE);
X	    if (t == SOH) continue;
X	    if(unchar(t) != ((((cchksum&0300) >> 6)+cchksum)&077)) return(FALSE);;
X	    cchksum += t;
X	}
X	for (i=0; i<*len; i++) {
X	    t = readchar();
X	    if (timeout != GOODREAD) return(FALSE);
X	    if (t == SOH) continue;
X	    cchksum = cchksum + t;
X	    data[i] = t;
X	}
X	data[*len] = 0;
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X	rchksum = unchar(t);
X	t = readchar();
X	if (timeout != GOODREAD) return(FALSE);
X	if (t == SOH) continue;
X	done = TRUE;
X    }
X    if(type != 'B' && type != 'Z')
X	dostats(type,mainmode);
X    cchksum = (((cchksum&0300) >> 6)+cchksum)&077;
X    if (cchksum != rchksum) return(FALSE);
X    if(!sending && (*num != lastpkt)) {
X	tp++;
X	lastpkt = *num;
X    }
X    return((int)type);
X}
X
Xgetpkt() {
X    int i,eof;
X
X    static char leftover[10] = { '\0', '\0', '\0', '\0', '\0',
X				'\0', '\0', '\0', '\0', '\0' };
X
X    if (first == 1) {
X	first = 0;
X	*leftover = '\0';
X	t = getc(fp);
X	if (t == EOF) {
X	    first = 1;
X	    return(size = 0);
X	}
X	totbytes++;
X    }
X    else if (first == -1) {
X	first = 1;
X	return(size = 0);
X    }
X    for (size = 0; (msgpkt[size] = leftover[size]) != '\0'; size++) ;
X    *leftover = '\0';
X    rpt = 0;
X    eof = 0;
X    while (!eof) {
X	next = getc(fp);
X	if (next == EOF) {
X	    first = -1;
X	    eof   =  1;
X	}
X	else totbytes++;
X	osize = size;
X	encode(t);
X	t = next;
X	if (size == spsiz-3) return(size);
X	if (size > spsiz-3) {
X	    for (i = 0; (leftover[i] = msgpkt[osize+i]) != '\0'; i++)
X		;
X	    size = osize;
X	    msgpkt[size] = '\0';
X	    return(size);
X	}
X    }
X    return(size);
X}
X
Xstatic void
Xencode(a)
Xchar a;
X{
X    int a7,b8;
X
X    if (p_mode == 1 && a == '\n') {
X	rpt = 0;
X	msgpkt[size++] = quote;
X	msgpkt[size++] = ctl('\r');
X	if (size <= spsiz-3) osize = size;
X	msgpkt[size++] = quote;
X	msgpkt[size++] = ctl('\n');
X	msgpkt[size]   = '\0';
X	return;
X    }
X    if (rptflg) {
X	if (a == next && (first == 0)) {
X	    if (++rpt < 94) return;
X	    else if (rpt == 94) {
X		msgpkt[size++] = rptq;
X		msgpkt[size++] = tochar(rpt);
X		rpt = 0;
X	    }
X	}
X	else if (rpt == 1) {
X	    rpt = 0;
X	    encode(a);
X	    if (size <= spsiz-3) osize = size;
X	    rpt = 0;
X	    encode(a);
X	    return;
X	}
X	else if (rpt > 1) {
X	    msgpkt[size++] = rptq;
X	    msgpkt[size++] = tochar(++rpt);
X	    rpt = 0;
X	}
X    }
X    a7 = a & 0177;
X    b8 = a & 0200;
X
X    if (ebqflg && b8) {			/* Do 8th bit prefix if necessary. */
X	msgpkt[size++] = ebq;
X	a = a7;
X    }
X
X    if ((a7 < SP) || (a7==DEL)) {
X	msgpkt[size++] = quote;
X	a = ctl(a);
X    }
X    if (a7 == quote) msgpkt[size++] = quote;
X    if ((rptflg) && (a7 == rptq)) msgpkt[size++] = quote;
X
X    if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th bit prefix */
X	msgpkt[size++] = quote;	/* if doing 8th-bit prefixes */
X
X    msgpkt[size++] = a;
X    msgpkt[size] = '\0';
X}
X
Xstatic void
Xdecode()
X{
X    USHORT  a, a7, b8;
X    char *buf;
X
X    buf = msgpkt;
X    rpt = 0;
X
X    while ((a = *buf++) != '\0') { /* Terminator added by rpack() */
X	if (rptflg) {
X	    if (a == rptq) {
X		rpt = unchar(*buf++);
X		a = *buf++;
X	    }
X	}
X	b8 = 0;
X	if (ebqflg) {		     /* 8th-bit prefixing? */
X	    if (a == ebq) {	     /* Yes, got an 8th-bit prefix? */
X		b8 = 0200;	     /* Yes, remember this, */
X		a = *buf++;	     /* and get the prefixed character. */
X	    }
X        }
X	if (a == quote) {
X	    a  = *buf++;
X	    a7 = a & 0177;
X	    if ((a7 >= 0100 && a7 <= 0137) || a7 == '?') a = ctl(a);
X	}
X	a |= b8;
X	if (rpt == 0) rpt = 1;
X	if (p_mode == 1 && a == '\r') continue;
X	totbytes += rpt;
X	for (; rpt > 0; rpt--) putc(a, fp);
X    }
X    return;
X}
X
Xstatic void
Xspar(data)
Xchar data[];
X{
X    data[0] = tochar(MAXPACKSIZ);
X    data[1] = tochar(TTIME_KERMIT);
X    data[2] = tochar(MYPAD);
X    data[3] = ctl(MYPCHAR);
X    data[4] = tochar(MYEOL);
X    data[5] = MYQUOTE;
X    if ((p_parity > 0) || ebqflg) {	   /* 8-bit quoting... */
X	data[6] = MYEBQ;	  /* If parity or flag on, send &. */
X	if ((ebq > 0040 && ebq < 0100) || /* If flag off, then turn it on  */
X	   (ebq > 0140 && ebq < 0177) || /* if other side has asked us to */
X	   (ebq == 'Y')) 
X	    ebqflg = 1;
X    }
X    else				    /* Normally, */
X	data[6] = 'Y';			    /* just say we're willing. */
X    data[7] = '1';
X    data[8] = MYRPTQ;
X    data[9] = tochar(IDOLONG);	/* Tell 'em I do LONG packets */
X    data[10] = tochar(0);	/* Don't do windows */
X    data[11] = tochar(p_kmaxpack / 95);
X    data[12] = tochar(p_kmaxpack % 95);
X    data[13] = '\0';
X}
X
Xstatic void
Xrpar(data, len)
Xchar data[];
Xint  len;
X{
X    int ospsiz;
X
X    spsiz   = unchar(data[0]);
X    ospsiz  = spsiz;
X    ttime   = unchar(data[1]);
X    pad     = unchar(data[2]);
X    padchar = ctl(data[3]);
X    eol     = unchar(data[4]);
X    quote   = data[5];
X    rptflg  = 0;
X    ebqflg  = 0;
X    if (len >= 6 && data[6] != 0) {
X	ebq = data[6];
X	if ((ebq > 040 && ebq < 0100) ||
X	    (ebq > 0140 && ebq < 0177)) ebqflg = 1;
X	else if (((p_parity > 0) || ebqflg) && (ebq == 'Y')) {
X	    ebqflg = 1;
X	    ebq = '&';
X	}
X	else ebqflg = 0;
X    }
X    if (len >= 8 && data[8] != 0) {
X	rptq    = data[8];
X	rptflg  = ((rptq > 040 && rptq < 0100) ||
X		   (rptq > 0140 && rptq < 0177));
X    }
X    if(len >= 9 && data[9] != 0) {
X	int capas;
X	for(capas=9; data[capas] & 1; capas++) ; /* Skip over continuations */
X	if((ulp = (data[9] & IDOLONG)) == IDOLONG) {
X	    spsiz = 500; /* Default if no packet size specified */
X	    if(len >= capas+3) {
X		spsiz = (unchar((data[capas+2])) * 95) + unchar(data[capas+3]);
X		if(spsiz > MAXLONGPKS)
X		    spsiz = MAXLONGPKS;
X		else if(spsiz < 10)	/* Reasonable? */
X		    spsiz = 500;
X	    }
X	}
X    }
X}
X
Xvoid
Xsaybye()
X{
X   int len,num;
X
X   if(msgpkt == NULL)	/* No msgpkt buffer... */
X	return;		/* ...so just return.  */
X
X   if(numreqs != 0)	/* Requester's up... */
X	Delay(5L);	/* ...so wait for Intuition, just in case. */
X   spack('G',n,1,"F");  /* shut down server no more files */
X   rpack(&len,&num,ackpkt);
X}
X
Xstatic void
Xprint_our_err()
X{
X    if (retry > MAXTRY || oldtry > MAXTRY) {
X	InfoMsg1Line("KERMIT: Too may retries for packet");
X	strcpy(msgpkt,"VT100 KERMIT: Too many retries for packet");
X    }
X    else {
X	InfoMsg1Line("KERMIT: Protocol Error");
X	strcpy(msgpkt,"VT100 KERMIT: Protocol Error");
X    }
X    spack('E',n,strlen(msgpkt),msgpkt);
X}
X
Xstatic void
Xprint_host_err(msg)
Xchar *msg;
X{
X    InfoMsg2Line("KERMIT: Host Error:", msg);
X}
X
Xstatic void
Xdostats(type,stat)
Xchar type,*stat;
X{
X    char *statusform = "%5s %-15.15s Pkt: %4d Retr: %2d Bytes: %6ld Type: %c",
X	 status[80];
X
X    if (type == 'Y' || type == 'N' || type == 'G') return;
X
X    sprintf(status, statusform, stat, filnam, tp, retry-1,
X	    (LONG)totbytes, type);
X    InfoMsgNoScroll(status);
X}
X
Xstatic void
XClearBuffer()
X{
X    AbortIO((struct IORequest *)Read_Request);
X    Wait(1L << Read_Request->IOSer.io_Message.mn_ReplyPort->mp_SigBit);
X    WaitIO((struct IORequest *)Read_Request);
X    Read_Request->IOSer.io_Command = CMD_CLEAR;
X    DoIO((struct IORequest *)Read_Request);
X    Read_Request->IOSer.io_Command = CMD_READ;
X    SendIO((struct IORequest *)Read_Request);
X}
SHAR_EOF
echo "extracting kermitproto.c"
sed 's/^X//' << \SHAR_EOF > kermitproto.c
X/*		:ts=8						*/
X
X/* WARNING -- This C source program generated by Wart preprocessor. */
X/* Do not edit this file; edit the Wart-format source file instead, */
X/* and then run it through Wart to produce a new C source file.     */
X
X/* Wart Version Info: */
Xchar *wartv = "Wart Version 1A(005) Jan 1988";
X
X/*
X * A completely new C Kermit module.
X *
X * Based on code from Frank Da Cruz's excellent book, _Kermit: A File
X * Transfer Protocol_, Digital Press, 1986.
X *
X * As this code is almost entirely from said book, it is certainly covered
X * by that book's copyright.  Basically, this means the code is freely
X * distributable, and can be used in a commercial program provided such
X * use does not increase the program's cost to the purchaser beyond a small
X * handling fee.
X *
X * Stephen Walton, swalton@solar.stanford.edu
X * Dept. of Physics and Astronomy
X * California State University, Northridge
X * Northridge, CA 91330
X *
X *			ORGANIZATION
X *
X *  This file is in three pieces, and could probably be broken into
X * two files.  Leading off are #DEFINE's and declarations of
X * global and external variables.  Following are the unmodified sources
X * from The Book.   Then are the z* file-handling routines written using
X * standard Unix-style (almost ANSI C) file routines.
X *
X * I make no apologies for the organization; my primary goals were (1) to
X * use the unmodified book source except for errors found by Lint, and
X * (2) to allow this file to be plugged into an otherwise unmodified
X * terminal program for the Commodore Amiga computer called VT100.  The
X * comments starting with the string "/*lint" are for the Lint program
X * of Gimpel Software, available for the Commodore Amiga and MS-DOS
X * machines.  I highly recommend it.
X *
X * A few words about style herein.  Both "book Kermit" and the official
X * C Kermit release make extensive use of global variables to set various
X * options and keep track of what is going on.  I don't like this, but
X * since example source code must be part of the Kermit specification,
X * I chose to keep that organization.  There are several places where I
X * I have tried to modularize things better.  First of all, everything
X * which is not needed outside of this file is declared "static".  Second,
X * I have kept the book Kermit code pretty much intact, except for some lint
X * related things and one extra convention:  tmsg() appends characters to
X * an existing status line, but tmsgl() is required to force that output to
X * be seen.  Hence, I've changed the last in each series of tmsg() calls to
X * tmsgl().
X */
X
X
X/*
X * Revision History--Versions 0 through 1 never left me.
X *
X * Version 0.5--Created and linted.
X * Version 0.6--Added RESUME to handling of "unknown packets".
X *	      --Added proto() function to wrap around wart() for handling
X *		of startup and cleanup functions.
X *	      --Changed handling of timeouts in input():  instead of an
X *		error("Timeout") call, it puts the "Timeout" message in
X *		the data field and pretends it read an 'E' packet.
X * Version 0.7--Fixed a problem with rpsiz and MAXRP.  I had set rpsiz
X *		to MAXRP in rpar() and was calling ttinl() with a max
X *		argument of MAXRP, which resulted in truncated packets;
X *		to wit, MAXRP+1 characters could come into ttinl counting
X *		the eol.  Created defines DRPSIZ/DSPSIZ for default values
X *		for rpsiz and spsiz, and increased MAXRP.
X *	      --Changed rcvpkt, sndpkt, and data so that they are pointers
X *		instead of static arrays, and allocate and de-allocate
X *		them in proto().
X * Version 0.8--Added client support.  To make a Get
X *		command, point the char *cmarg at the remote file
X *		specification and set the start state to 'r'
X *		(extern char start in the calling file).
X * Version 0.9--Added long packet support.  This required adding an extern
X *		int urpsiz which is set to the desired receive packet size
X *		by the user interface.
X *	      --Since the above required using the capas, I also added the
X *		code from C Kermit 4E(070) for negotiating window size
X *		in rpar() and spar(). Of course, we don't do windows yet.
X *	      --Fixed a bug which is also part of C Kermit 4E(070).
X *		If we tell the sender that we can receive a packet
X *		of size MAXRP, the packet can contain as many as
X *		MAXRP+5 characters, counting the EOL and extended
X *		headers.  So rpack() must be able to handle somewhat
X *		more characters than the actual maximum packet length.
X *		I defined MAXRP here to be 1024, but only allow
X *		rpsiz in proto() to be MAXRP-20.
X * Version 0.95--Added code to gracefully abort transfer.  This works
X *		via the cx and cz external variables and the state 'a'.
X * Version 1.0--Unleashed upon the world.
X *		
X */
X
X/*
X * Run this through the Wart preprocessor which comes with C Kermit.
X */
X
X#include "kermitproto.h"
X
Xstatic void decstr(), rinit(), error(), tinit(), spar(), zrtol(), zltor();
X
X/*
X * Here are the variables which need to be set to startup values, and which
X * also can be freely changed between protocol transfers.  At first I thought
X * to declare them all "extern" in order to force definition elsewhere.
X * On reflection, it makes sense to both declare them here and set them to
X * their default startup values.  Thus they can be ignored outside of this
X * module if you so desire.
X *
X * Note that the names are very systematic:  Names beginning with "r" have
X * to do with values I use for received packets;  those beginning with "s"
X * are values I use for sending packets.  Also note we set some, others are
X * set for us.  I have made the ones we get in spar() static (local),
X * and the ones we send in rpar() global.
X *
X * First the ints.
X */
X
Xint	cx = 0,
X	cz = 0,		/* Flags for aborting transfers.  cx (control-X)*
X			 * is set to 1 if an abort of the current file	*
X			 * is desired, cz (control-Z) if an abort of	*
X			 * an entire batch transfer is desired.		*/
X	rpsiz = MAXRP,	/* Maximum packet size				*/
X			/* Like most of the receive packet parameters,	*
X			 * this one is actually set by the sender.  But	*
X			 * since the sender has the option to not send	*
X			 * these, we must initialize to "reasonable"	*
X			 * defaults.					*/
X	bctr,		/* Block check type to request.			*/
X	limit = 5,	/* Retry limit on receive			*/
X	warn = 0,	/* 1 for warn before overwriting files		*/
X	rpadn = 0,	/* Number of pad characters I require.		*/
X	rtimo = 10;	/* How long I want you to wait before you	*
X			 * you time me out.				*/
X
Xchar	rmark = '\1',	/* Start of packet marker for receive		*/
X	reol = '\r',	/* End of packet marker for receive		*/
X	start = 0,	/* Start state					*/
X	sctlq = '#',	/* Control character quote character for send	*/
X	rpadc = '\0';	/* Pad character I want you to use		*/
X
X/*
X * Variables which MUST be set by the external interface.
X */
Xextern int
X	parity,		/* 0 for no parity--need for proper 8th-bit quote */
X	text,		/* Flag 1 for text file, 0 for binary file	*/
X	urpsiz;		/* Maximum receive packet size user wants.	*/
X
Xextern char
X	*cmarg;		/* Character string containing Kermit server cmd */
X
X/*
X * In a fit of cleverness, here are some macro defines for variables we *
X * aren't currently using. Only now we tell Lint to ignore constant	*
X * Booleans!
X */
X
X/*lint -e506 */
X
X#define local 1		/* Local mode flag--that is, I'm on your end	*/
X#define server 0	/* We are never server				*/
X#define delay 0		/* Time to delay before sending first packet	*/
X#define xpkt 0		/* Send X packet instead of F?			*/
X
X/*
X * This block of defines is strictly internal flags of various kinds.	*
X * I hope to Grid that I've got them all.  Someday this will be cleaner *
X */
Xstatic int
X	spsiz = DSPSIZ,	/* Maximum send packet size			*/
X	wsize = MAXWS,	/* Maximum window size				*/
X	sndpkl,		/* Size of packet currently being attempted	*/
X	filcnt,		/* Number of files transferred so far		*/
X	bctu = 1,	/* Block check type to use			*/
X	rqf,		/* Flag for 8th bit quote negotiations		*/
X	ebq = '&',	/* 8th-bit prefix				*/
X	ebqflg = 0,	/* 8th-bit quoting flag				*/
X	xflag,		/* Output to screen for generic server commands	*/
X	rq = 0,		/* Received 8bq bid				*/
X	sq = 'Y',	/* Sent 8bq bid					*/
X	rpt = 0,	/* Repeat count					*/
X	rptq = '~',	/* Repeat prefix				*/
X	rptflg = 0,	/* Repeat processing flag			*/
X	capas = 10,	/* Final position of inbound capas mask		*/
X	atcapr = 0,	/* Attribute capability requested		*/
X	atcapu = 0,	/* Attribute capability used			*/
X	swcapr = 0,	/* Sliding windows capability requested		*/
X	swcapu = 0,	/* Sliding windows capability used		*/
X	lpcapr = 0,	/* Long packets capability requested		*/
X	lpcapu = 0,	/* Long packets capability used			*/
X	rsn,		/* Received sequence number			*/
X	seq = 0,	/* Current sequence number			*/
X	maxsiz,		/* Maximum data size for packet			*/
X	rln,		/* Length of received data field		*/
X	size,		/* Current size of output packet data		*/
X	osize,		/* Previous output packet data size		*/
X	first = 0,	/* Some kind of lookahead flag			*/
X	stimo = 5,	/* Timeout interval for me to use		*/
X	spadn = 0;	/* Number of pad characters for me to use	*/
X
X#define atcapb 8	/* Attribute capability bit			*/
X#define swcapb 4	/* Sliding windows capability bit		*/
X#define lpcapb 2	/* Long packets capability bit			*/
X#define closif zclosi	/* I use closif() to close the input file in	*
X			 * case it needs to be more complex later, but	*
X			 * for now it just calls the z routine.		*/
X
Xstatic char
X	smark = '\1',	/* Start of packet marker for send		*/
X	spadc = '\0',	/* Pad character to use on sending		*/
X	seol = '\r',	/* End of packet marker for sending		*/
X	rctlq = '#',	/* Control character quote char for receiving	*/
X	filnam[50],	/* Current file name				*/
X	ssc,		/* Start server command				*/
X	*sndpkt,	/* Send packet.					*/
X	*rcvpkt,	/* Receieve packet.				*/
X	*data,		/* Data to send/receive before encode/decode	*/
X	*rdatap,	/* Pointer to null-terminated data field	*/
X	*isp = NULL,	/* Pointer to characters in memory		*/
X	*osp = NULL;	/* Output string pointer			*/
X
X/*
X * Forward declarations.  Soon to be prototypes if the ANSI standard
X * committee keeps its promises.
X */
Xint input(), spack(), ack();
Xchar *rpar();
X
X/*
X * External routines provided.
X */
X
Xextern int ttinl(), ttol(), gnfile();
Xextern void ttflui(), tchar(), tmsg(), tmsgl(), sleep();
X
X#define ERR(s) error(s); RESUME
X#define RESUME return
X
X/*lint -save -e525 -e527	We don't care how Wart formats!		*/
X
X#define ssfil 1
X#define ssdat 2
X#define sseot 3
X#define srini 4
X#define srfil 5
X#define srdat 6
X#define sipkt 7
X#define srgen 8
X
X#define BEGIN state =
X
Xint state = 0;
X
Xvoid
Xwart()
X{
X    int c,actno;
X    extern int tbl[];
X    while (1) {
X	c = input();
X	if ((actno = tbl[c + state*128]) != -1)
X	    switch(actno) {
Xcase 1:
X    {					/* - Start State - */
X    tinit();				/* Initialize transaction */
X    if (sinit('S') < 0) { ERR("sinit"); }	/* Build, send Send-Init. */
X    else {				/* If successful, */
X	filcnt = 0;			/* initialize file counter */
X	BEGIN ssfil;			/* and switch to ssfil state. */
X    }
X}
X    break;
Xcase 2:
X    { tinit(); rinit(); BEGIN srini; }
X    break;
Xcase 3:
X    {					/* Get */
X    tinit(); ssc = 0;
X    if (sinit('I') < 0) { ERR("sinit"); }
X    else BEGIN sipkt;
X}
X    break;
Xcase 4:
X    {					/* Host */
X    tinit(); ssc = 'C';
X    if (sinit('I') < 0) { ERR("sinit"); }
X    else BEGIN sipkt;
X}
X    break;
Xcase 5:
X    {					/* Generic */
X    tinit(); ssc = 'G';
X    if (sinit('I') < 0) { ERR("sinit"); }
X    else BEGIN sipkt;
X}
X    break;
Xcase 6:
X    {					/* Immediate protocol abort */
X    spack('E', seq, "User aborted protocol", 21);
X    closif();  closof(1);		/* Close files, deleting output */
X    RESUME;
X}
X    break;
Xcase 7:
X    {				/* - Send File State - */
X    if (filcnt++ == 0) spar(rdatap);	/* Set parameters if 1st time */
X    cx = 0;				/* Reset file interruption flag */
X    bctu = bctr;			/* Switch to negotiated block check */
X    /* Is there a file to send in an uncancelled batch? */
X    if (!cz && gnfile(filnam, sizeof(filnam)) > 0) {
X	if (sfile() < 0) { ERR("sfile"); }	/* Yes, open it, send F packet */
X	else BEGIN ssdat;		/* and if no error, switch state. */
X    } else {				/* No (more) files to send */
X	if (seot() < 0) { ERR("seot"); }	/* so send B packet */
X	else BEGIN sseot;		/* and switch to sseot state. */
X    }
X}
X    break;
Xcase 8:
X    {				/* - Send Data State - */
X    int x;
X    if (rln == 1 && *rdatap == 'X')	/* Did ACK contain X as data? */
X	cx = 1;				/* Yes, set control-x flag. */
X    else if (rln == 1 && *rdatap == 'Z') /* Did ACK contain Z as data? */
X	cz = 1;				/* Yes set control-z flag. */
X    /* Check here for cancellation of transfer and data left to send. */
X    if (cx || cz || (x = sdata()) == 0) {
X	if (seof((cx | cz) ? "D" : "") < 0) {	/* If not, send Z packet. */
X	    ERR("seof");
X	}
X	else BEGIN ssfil;		/* and go back to ssfil state. */
X    } else if (x < 0) { ERR("sdata"); }	/* Handle file i/o errors. */
X}
X    break;
Xcase 9:
X    { RESUME; }
X    break;
Xcase 10:
X    {
X    spar(rdatap);
X    (void) ack1(rpar());
X    bctu = bctr;
X    BEGIN srfil;
X}
X    break;
Xcase 11:
X    { (void) ack(); RESUME; }
X    break;
Xcase 12:
X    { if (rcvfil() < 0) { ERR("rcvfil"); } else { (void) ack(); BEGIN srdat; } }
X    break;
Xcase 13:
X    {
X   if (cx)
X	ack1("X");
X   else if (cz)
X	ack1("Z");
X   else {
X	if (decode() < 0) { ERR("decode"); } else (void) ack();
X   }
X}
X    break;
Xcase 14:
X    {
X    /* Discard output file if the sender tells us so. */
X    if (closof(cx || cz || (rln == 1 && *rdatap == 'D')) < 0) {
X	ERR("error closing file");
X    } else {
X	(void) ack(); BEGIN srfil;
X    }
X}
X    break;
Xcase 15:
X    {			/* Got ACK for I packet */
X    spar(rdatap);		/* Set parameters from it */
X    start = 'E';		/* Force entry into next state */
X}
X    break;
Xcase 16:
X    {			/* Got E for I packet */
X    if (ssc) {
X	if (scmd(ssc,cmarg) < 0) { ERR("scmd"); }
X	else BEGIN srgen;
X    } else {
X	if (scmd('R',cmarg) < 0) { ERR("scmd"); }
X	else BEGIN srini;
X    }
X}
X    break;
Xcase 17:
X    { xflag = 1; decode(); RESUME; }
X    break;
Xcase 18:
X    { xflag = 1; ack(); BEGIN srdat; }
X    break;
Xcase 19:
X    { error(rdatap);
X    (void) closif();
X    (void) closof(1);		/* close files, discarding output */
X    RESUME; }
X    break;
Xcase 20:
X    { error("Unexpected packet type"); RESUME; }
X    break;
X
X	    }
X    }
X}
X
Xint tbl[] = {
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20,  7, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20,  8, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20,  9, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 11, 20, 20, 19, 12, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 18, 20, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 13, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 14, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X-1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 15, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20, 20, 10, 20, 20, 20, 20, 18, 17, 20, 20, 20, 20, 20, 20, 
X20,  6, 20,  4, 20, 20, 20,  5, 20, 20, 20, 20, 20, 20, 20, 20, 
X20, 20,  3,  1, 20, 20,  2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 
X};
X
X
X/*lint -restore */
X
X/*
X * First, some handy macros.
X */
X
X#define tochar(c) ((c) + 32)
X#define unchar(c) ((c) - 32)
X#define ctl(c) ((c) ^ 64)
X
Xstatic
Xint
Xinput() {				/* Return packets	*/
X   int type, try;
X
X    if (start != 0) {			/* Start state in effect? */
X	type = start;			/* Yes, call it a packet type, */
X	start = 0;			/* nullify the start state, */
X	return(type);			/* and return the type. */
X    }
X    type = rpack();			/* No start state, read a packet. */
X
X    for (try = 0; rsn != seq || strchr("TQN",type); try++) {
X	if (try > limit) {		/* If too mahy tries, */
X	    strcpy(data, "Timed out");	/* give up */
X	    rdatap = data;		/* Make up pretend 'E' packet */
X	    return('E');		/* and return it */
X	}
X	if (type == 'N' && rsn == ((seq + 1) & 63)) {
X					/* NAK for next packet */
X	    return('Y');		/* is ACK for current. */
X	} else {			/* Otherwise, */
X	    (void) resend();		/* resend previous packet. */
X	}
X	type = rpack();			/* Try to read response. */
X    }
X    ttflui();				/* Got a good one, clear buffer. */
X    return(type);			/* Return its type. */
X}
X
Xstatic void
Xnxtpkt() {
X    seq = (seq + 1) & 63;		/* Next packet number, mod 64 */
X}
X
Xstatic void
Xtinit() {				/* Transaction initialization */
X    seq = 0;				/* Start off with packet 0 */
X    ebqflg = 0;				/* 8-bit quoting off */
X    sq = 'Y';				/* Normal 8-bit quote bid */
X    rqf = -1;				/* Flag other's bid not yet seen */
X    rptflg = 0;				/* No repeat counts by default */
X    bctu = 1;				/* Block check to use back to 1 */
X    xflag = 0;				/* Output normally to file */
X    osp = NULL;				/* ... */
X    *filnam = *sndpkt = *rcvpkt = '\0';  /* Clear string buffers */
X}
X
Xstatic void
Xerror(s) char *s; {			/* Fatal error */
X    if (local) {			/* If in local mode */
X	tmsg("Fatal error: ");		/* Type message on console */
X	tmsgl(s);
X    } else {				/* Otherwise, */
X	(void) spack('E',seq,strlen(s),s); /* Send in error packet. */
X    }
X}
X
Xstatic
Xack() {
X    int x;				/* Empty acknowledgement */
X    x = spack('Y',seq,0,"");		/* Send the packet */
X    nxtpkt();				/* Increment the packet number */
X    return(x);
X}
X
Xstatic
Xack1(s) char *s; {
X    int x;				/* Acknowledgement with data */
X    x = spack('Y',seq,strlen(s),s);	/* Send the packet */
X    nxtpkt();				/* Increment packet number */
X    return(x);
X}
X
Xstatic
Xnak() {					/* Negative acknowledgement */
X    return(spack('N',seq,0,""));	/* Never has data! */
X}
X
X/* Functions used by file sender. */
X
X/* sinit()--start the transaction by filling in the initialization string
X * and sending it in an S packet.
X */
X
Xstatic
Xsinit(c) char c; {
X    char *s;
X    s = rpar();
X    if (local == 0 && c == 'S' && server == 0) {
X	tmsgl("Escape back to local system, give RECEIVE command...");
X	sleep(delay);
X    }
X    return(spack(c,seq,strlen(s),s));
X}
X
X/*
X * scmd() -- send a preformatted Kermit server command string.
X */
Xstatic int
Xscmd(t, s) char t, *s; {	/* Send a packet of the given type */
X    encstr(s);			/* Encode the command string */
X    spack(t,seq,size,data);
X    return 0;
X}
X
X/* rinit() -- do whatever is needed to initialize receive.  A no-op now,
X * but may later initialize counters and so on.
X */
Xstatic void
Xrinit()
X{}
X
X/* sfile() -- open the file and send the File-Header packet.  Assumes that
X * the global string pointer filnam references the file name.
X */
X
Xstatic
Xsfile() {
X    int x;
X    char pktnam[50];
X    if (zopeni(filnam) < 0)		/* Try to open file. */
X	return -1;
X    zltor(filnam,pktnam);		/* OK, convert name. */
X    x = encstr(pktnam);			/* Encode the result */
X    if (local) {			/* If in local mode, */
X	tmsg("Sending ");		/* let user know we're */
X	tmsg(filnam);			/* sending this file */
X	tmsg(" as ");			/* under */
X	tmsgl(pktnam);			/* this name */
X    }
X    first = 1;				/* Flag beginning of file */
X    maxsiz = spsiz - (bctr + 3);	/* Maximum data length */
X    nxtpkt();				/* Increment packet number */
X    return(spack((xpkt ? 'X' : 'F'),seq,x,data)); /* Send packet */
X}
X
X/* sdata() -- get next packet's worth of data */
X
Xstatic
Xsdata() {
X    int x;
X    if ((x = getpkt(maxsiz)) == 0)	/* If no data left to send, */
X	return(0);			/* return EOF indication. */
X    nxtpkt();
X    return(spack('D',seq,x,data));	/* Send the data packet */
X}
X
X/* seof -- close the input file and send a Z packet. */
X
Xstatic
Xseof(s) char *s; {
X   if (closif() < 0)			/* Try to close the file. */
X	return -1;			/* On error, return failure. */
X    else {				/* Otherwise, */
X	if (local) tmsgl("OK");		/* if local, reassure user. */
X	nxtpkt();
X	return(spack('Z',seq,strlen(s),s));	/* Send Z packet */
X    }
X}
X
X/* seot -- send B packet. */
X
Xstatic
Xseot() {
X    nxtpkt();
X    if (local) tmsgl("Done");
X    return(spack('B',seq,0,""));
X}
X
Xstatic
Xrcvfil() {				/* Receive a file */
X    char myname[50];
X    decstr(filnam);			/* Decode name */
X    zrtol(filnam,myname,warn);		/* Convert to local form. */
X    if (zopeno(myname) < 0)
X	return -1;
X    else {				/* File open OK, give message. */
X	if (local && !xflag) {
X	    tmsg("Receiving "); tmsg(filnam); tmsg(" as "); tmsgl(myname);
X	}
X	return 0;
X    }
X}
X
Xstatic
Xclosof(nokeep) int nokeep; {		/* Close output file, but */
X    if (xflag) return 0;		/* not if it's the screen. */
X    if (zcloso(nokeep) < 0)
X	return -1;
X    return 0;
X}
X
Xstatic
Xspack(type,n,len,d) char type, *d; int n, len; {
X    int i = 0, j, k;
X
X    for (i = 0; i < spadn; i++)
X	sndpkt[i] = spadc;		/* Do requested padding */
X    sndpkt[i++] = smark;		/* Packet mark */
X    k = i++;				/* Remember this place */
X    sndpkt[i++] = tochar(n);		/* Sequence number */
X    sndpkt[i++] = type;			/* Packet type */
X    j = len + bctu;			/* True length */
X    if (j > 95) {			/* Long packet? */
X	sndpkt[k] = tochar(0);		/* Set LEN to 0 */
X	sndpkt[i++] = tochar(j / 95);	/* High part of length */
X	sndpkt[i++] = tochar(j % 95);	/* Low part of length */
X	sndpkt[i] = '\0';		/* Header checksum */
X	sndpkt[i++] = tochar(chk1(sndpkt+k));
X    } else
X	sndpkt[k] = tochar(j+2);	/* True length. */
X
X    for (j = len; j > 0; j--) {		/* Data */
X	sndpkt[i++] = *d++;
X    }
X    sndpkt[i] = '\0';			/* Null terminate. */
X    switch (bctu) {
X	case 1:				/* Type 1 - 6 bit checksum */
X	    sndpkt[i++] = tochar(chk1(sndpkt+k));
X	    break;
X	case 2:				/* Type 2 - 12 bit checksum */
X	    j = chksum(sndpkt+k);
X	    sndpkt[i++] = tochar((j >> 6) & 077);
X	    sndpkt[i++] = tochar(j & 077);
X	    break;
X	case 3:				/* Type 3 - 16 bit CRC-CCITT */
X	    j = chk3(sndpkt + k);
X	    sndpkt[i++] = tochar((j >> 12) & 017);
X	    sndpkt[i++] = tochar((j >> 6) & 077);
X	    sndpkt[i++] = tochar(j & 077);
X	    break;
X	}
X    sndpkt[i++] = seol;			/* End of line */
X    sndpkt[i] = '\0';			/* Null string-terminator. */
X    sndpkl = i;				/* Remember length. */
X    i = ttol(sndpkt,sndpkl);		/* Send the packet. */
X    if (local && !xflag) tchar('.');
X    return(i);
X}
X
Xstatic
Xresend() {
X    int x;
X    if (*sndpkt)
X	x = ttol(sndpkt,sndpkl);	/* Send previous packet */
X    else
X	x = nak();			/* or NAK if none */
X    if (local && !xflag) tchar('%');	/* Let the user know. */
X    return(x);
X}
X
Xchk1(packet) char *packet; {		/* Compute Kermit's */
X    int s, t;				/* 1-character block check. */
X    s = chksum(packet);			/* Get the arithmetic sum. */
X    t = (((s & 192) >> 6) + s) & 63;	/* Fold it into 6 bits. */
X    return(t);
X}
X
Xstatic
Xchksum(p) char *p; {			/* Compute the checksum. */
X    unsigned m;
X    long s;
X
X    m = (parity) ? 0177 : 0377;		/* Mask for parity bit.	*/
X    s = 0;
X    for (; *p != '\0'; p++)		/* For each character, */
X	s += *p & m;			/* accumulate the sum, */
X    return(s & 07777);			/* and then return it. */
X}
X
X/*
X * rpack reads a packet and returns the packet type, or else Q if the
X * packet was invalid, or T if a timeout occured.   Upon successful return,
X * sets the global variables:
X *	rsn	- the received sequence number
X *	rln	- length of the received data field
X *	rdatap	- a pointer to the null-terminated contents of the data field
X */
Xstatic
Xrpack() {
X    int i, j, x, type, rlnpos;
X    char pbc[4];			/* Packet block check. */
X
X    rsn = rln = -1;			/* In case of failure. */
X
X    *rcvpkt = '\0';			/* Initialize receive buffer. */
X    j = ttinl(rcvpkt,MAXRP,reol,stimo); /* Try to get a "line". */
X    if (j < 0) return('T');		/* Timed out. */
X
X    for (i = 0; rcvpkt[i] != rmark && (i < j); i++)	/* Find mark. */
X	;
X    if (i == j) return('Q');		/* If no mark, bad packet. */
X
X    rlnpos = ++i;			/* Got it, remember position. */
X    if ((j = unchar(rcvpkt[i++])) == 0) { /* Long packet? */
X    	j = rlnpos + 5;			/* Yes, check header */
X    	if (j > MAXRP) return('Q');	/* Be defensive. */
X    	x = rcvpkt[j];			/* Remember header checksum */
X    	rcvpkt[j] = '\0';
X    	if (unchar(x) != chk1(rcvpkt+rlnpos))	/* Check header */
X	    return('Q');
X	rcvpkt[j] = x;			/* Restore packet */
X	rln = unchar(rcvpkt[j-2]) * 95 + unchar(rcvpkt[j-1]) - bctu;
X	j = 3;
X    } else {
X	rln = j - bctu - 2;		/* Regular packet */
X	j = 0;				/* No extended header */
X    }
X    rsn = unchar(rcvpkt[i++]);		/* Sequence number. */
X    type = rcvpkt[i++];			/* Packet type */
X    i += j;				/* Skip extended header, if any */
X    rdatap = rcvpkt + i;		/* The data itself. */
X    j = rln + i;			/* Position of block check. */
X    if (j > MAXRP)
X	return('Q');			/* Be defensive! */
X    for (x = 0; x < bctu; x++)		/* Copy the block check. */
X	pbc[x] = rcvpkt[j+x];
X    rcvpkt[j] = '\0';
X    switch (bctu) {			/* Which block check type? */
X	case 1:
X	    if (unchar(*pbc) != chk1(rcvpkt+rlnpos)) return('Q');
X	    break;
X	case 2:
X	    x = unchar(*pbc) << 6 | unchar(pbc[1]);
X	    if (x != chksum(rcvpkt+rlnpos)) return('Q');
X	case 3:
X	    x = unchar(*pbc) << 12 | unchar(pbc[1]) << 6 | unchar(pbc[2]);
X	    if (x != chk3(rcvpkt+rlnpos)) return('Q');
X	    break;
X	default:
X	    error("Impossible block check type.");
X    }
X    return type;			/* Otherwise, return packet type */
X}
X
X/*
X * CHK3
X * Calculate the 16-bit CRC of a null-terminated string using a
X * byte-oriented tableless algorithm devised by Andy Lowry (Columbia
X * University).  The magic number 010201 is derived from the CRC-CCITT
X * polynomial x^16+x^12+x^5+1.
X */
Xstatic
Xchk3(s) char *s; {
X    unsigned int c, q;
X    long crc = 0;
X
X    while ((c = *s++) != '\0') {
X	if (parity) c &= 0177;
X	q = (crc ^ c) & 017;		/* Low order nybble */
X	crc = (crc >> 4) ^ (q * 010201);
X	q = (crc ^ (c >> 4)) & 017;	/* High order nybble */
X	crc = (crc >> 4) ^ (q * 010201);
X    }
X    return(crc);
X}
X
X/*
X * getpkt--Fill a packet to the maximum.  Result goes in local data array
X * whose current length is indicated in global size.
X */
Xstatic
Xgetpkt(maxlen) int maxlen; {
X    int i, next;
X    static int c;
X    static char remain[6] = {'\0', '\0', '\0', '\0', '\0', '\0'};
X    void encode();
X    
X    if (first == 1) {			/* If first time thru... */
X	first = 0;			/* remember not to do this next time, */
X	remain[0] = '\0';		/* discard any old leftovers, */
X	c = gnchar();			/* get first character of file */
X	if (c < 0) {			/* watching out for null file */
X	    first = -1;
X	    return(size = 0);
X	}
X    } else if (first == -1) {		/* EOF from last time? */
X	return(size = 0);
X    }
X	
X
X/* Copy any leftovers that didn't fit in the last packet. */
X
X    for (size = 0; (data[size] = remain[size]) != '\0'; size++)
X	;
X    *remain = '\0';
X
X/* Get, encode, and deposit the next character. */
X
X    rpt = 0;				/* Initialize repeat counter. */
X    
X    while (first > -1) {		/* Until end of file or string... */
X	next = gnchar();		/* Look ahead one character */
X	if (next < 0) first = -1;	/* If none, we're at EOF. */
X	osize = size;			/* Remember current size. */
X	encode(c, next);		/* Encode the character. */
X	c = next;			/* Old next char is now current. */
X	if (size == maxlen) return(size); /* If just at end, done. */
X	if (size > maxlen) {		/* Past end, must save some. */
X	    for (i = 0; (remain[i] = data[osize+i]) != '\0'; i++)
X		;
X	    size = osize;
X	    data[size] = '\0';
X	    return(size);		/* Return size. */
X	}
X    }
X    return(size);			/* EOF, return size. */
X}
X
Xstatic
Xgnchar() {
X   char c;
X
X    if (isp) {				/* From string in memory */
X	return((c = *isp++) > 0 ? c : -1);
X    } else
X	return(zgetc(text));		/* or from a file. */
X}
X
X/*
X * Encodes the character a into the global data array,
X * and the global size is updated.
X * Global sctlq is the control prefix for sending data.
X */
Xstatic void
Xencode(a, next) char a; int next; {
X    int a7, b8;
X
X    if (rptflg) {			/* Doing run-length encoding? */
X	if (a == next) {		/* Yes, got a run? */
X	    if (++rpt < 94) {		/* Yes count. */
X		return;
X	    } else if (rpt == 94) {	/* If at maximum */
X		data[size++] = rptq;	/* Emit prefix, */
X		data[size++] = tochar(rpt); /* and count, */
X		rpt = 0;		/* and reset counter. */
X	    }
X	} else if (rpt == 1) {		/* Run broken, only two? */
X	    rpt = 0;			/* Yes, do the character wice */
X	    encode(a,-1);		/* by calling self recursively. */
X	    if (size <= maxsiz) osize = size; /* Watch for boundary */
X	    rpt = 0;			/* Call self second time. */
X	    encode(a,-1);
X	    return;
X	} else if (rpt > 1) {		/* Run broken, more than two? */
X	    data[size++] = rptq;	/* Yes, emit prefix and count */
X	    data[size++] = tochar(++rpt);
X	    rpt = 0;			/* and reset counter. */
X	}
X    }
X    a7 = a & 127;			/* Isolate low 7 bits */
X    b8 = a & 128;			/* And "parity" bit */
X
X    if (ebqflg & b8) {			/* If doing 8th-bit prefixing */
X	data[size++] = ebq;		/* and 8th bit on, insert prefix */
X	a = a7;				/* and clear the 8th bit. */
X    }
X    if (a7 < 32 || a7 == 127) {		/* If control character */
X	data[size++] = sctlq;		/* insert control quote */
X	a = ctl(a);			/* and make printable. */
X    } else if (a7 == sctlq)		/* If data is control prefix, */
X	data[size++] = sctlq;		/* prefix it. */
X    else if (ebqflg && a7 == ebq)	/* If doing 8th-bit prefixing, */
X	data[size++] = sctlq;		/* ditto for 8th-bit prefix. */
X    else if (rptflg && a7 == rptq)	/* If doing run-length encoding, */
X	data[size++] = sctlq;		/* ditto for repeat prefix. */
X
X    data[size++] = a;			/* Finally, emit the character. */
X    data[size] = '\0';			/* Terminate string. */
X}
X
X/*
X * Decodes the data pointed to by the global pointer rdatap.
X */
Xstatic
Xdecode() {
X    int a, a7, b8;
X
X    while ((a = *rdatap++) != '\0') {
X	rpt = 1;			/* Initialize repeat count. */
X	if (rptflg) {			/* Repeat processing? */
X	    if (a == rptq) {		/* Yes, have repat prefix? */
X		rpt = unchar(*rdatap++); /* Yes, get count. */
X		a = *rdatap++;		/* and following character. */
X	    }
X	}
X	b8 = 0;				/* Assume 8th bit not on. */
X	if (ebqflg) {			/* Doing 8th-bit prefixing? */
X	    if (a == ebq) {		/* Yes, have 8th-bit prefix? */
X		b8 = 128;		/* Yes, remember bit 8 on */
X		a = *rdatap++;		/* and get following character. */
X	    }
X	}
X	if (a == rctlq) {		/* Is it control prefix? */
X	    a = *rdatap++;		/* Yes, get next character */
X	    a7 = a & 127;		/* and its low 7 bits. */
X	    if (a7 > 62 && a7 < 96)	/* Encoded control character? */
X	        a = ctl(a);		/* Yes, controllify */
X	}
X	a |= b8;			/* OR in the 8th bit. */
X	for (; rpt > 0; rpt--)
X	    if (pnchar(a) < 0) return -1; /* Output the character. */
X    }
X    return(0);
X}
X
Xstatic
Xpnchar(c) int c; {			/* Put next character. */
X    if (xflag) {			/* To screen if desired. */
X	tchar(c);
X	return(1);
X    } else if (osp) {			/* Or to string in memory... */
X	*osp++ = c;
X	return(1);
X    } else return(zputc(c,text));	/* Otherwise to file. */
X}
X
Xstatic
Xencstr(s) char *s; {			/* Fill a packet from the string */
X    first = 1;				/* Start lookahead. */
X    isp = s;				/* Set input string pointer */
X    (void) getpkt(spsiz);		/* Fill a packet */
X    isp = NULL;				/* Reset input string pointer */
X    return(size);			/* Return data field length */
X}
X
Xstatic void
Xdecstr(s) char *s; {			/* Decode packet data into a string */
X    osp = s;				/* Set output string pointer */
X    (void) decode();			/* Decode the string */
X    *osp = '\0';			/* Terminate the string */
X    osp = NULL;				/* Reset output string pointer */
X}
X
Xstatic void
Xspar(s) char *s; {			/* Set parameters */
X    int x;
X
X    s--;				/* Line up with field numbers. */
X    
X    /* Limit on size of outbound packets */
X    x = (rln >= 1) ? unchar(s[1]) : 80;
X    spsiz = (x < 10) ? 80 : x;
X
X    /* Timeout on inbound packets */
X    x = (rln >= 2) ? unchar(s[2]) : 5;
X    stimo = (x < 0) ? 5 : x;
X
X    
X    /* Outbound padding */
X    spadn = 0; spadc = '\0';
X    if (rln >= 3) {
X	spadn = unchar(s[3]);
X	if (rln >= 4)
X	    spadc = ctl(s[4]);
X    }
X    
X    /* Outbound packet terminator */
X    seol = (rln >= 5) ? unchar(s[5]) : '\r';
X    if (seol < 2 || seol > 31) seol = '\r';
X    
X    /* Control prefix */
X    x = (rln >= 6) ? s[6] : '#';
X    rctlq = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#';
X    
X    /* 8th-bit quoting */
X    rq = (rln >= 7) ? s[7] : 0;
X    if (rq == 'Y') rqf = 1;
X    else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2;
X    else rqf = 0;
X    
X    switch (rqf) {
X	case 0: ebqflg = 0; break;
X	case 1: if (parity) { ebqflg = 1; ebq = '&'; } break;
X	case 2: if (ebqflg = (ebq == sq || sq == 'Y')) ebq = rq;
X    }
X
X    /* Block check */
X    x = 1;
X    if (rln >= 8) {
X	x = s[8] - '0';
X	if (x < 1 || x > 3) x = 1;
X    }
X    bctr = x;
X
X    /* Repeat prefix */
X    if (rln >= 9) {
X	rptq = s[9];
X	rptflg = ((rptq > 32 && rptq < 63) || (rptq > 95 && rptq < 127));
X    } else rptflg = 0;
X
X    /* Capabilities */
X    atcapu = lpcapu = swcapu = 0;	/* No capabilities by default	*/
X    if (rln >= 10) {
X	x = unchar(s[10]);
X	atcapu = (x & atcapb) && atcapr;	/* Attribute packets */
X	lpcapu = (x & lpcapb) && lpcapr;	/* Long packets */
X	swcapu = (x & swcapb) && swcapr;	/* Sliding windows */
X	for (capas = 10; (unchar(s[capas]) & 1) && (rln >= capas); capas++)
X	    ;					/* Skip to capas + 1 */
X    }
X
X    /* Long packets */
X    if (lpcapu) {			/* Flag set above */
X	if (rln > capas+2) {
X	    x = unchar(s[capas+2]) * 95 + unchar(s[capas+3]);
X	    spsiz = x > MAXSP ? MAXSP : x;
X	}
X	/* else a fatal error, but how do we terminate? */
X    }
X
X    /* Sliding windows */
X    if (swcapu) {
X	if (rln > capas+1) {
X	    x = unchar(s[capas+1]);
X	    wsize = x > MAXWS ? MAXWS : x;
X	} else
X	    wsize = 1;
X    }
X}
X
X/* Fill the array with my send-init parameters */
X
Xstatic char *
Xrpar() {
X    data[1] = tochar(DRPSIZ);		/* Biggest packet I can receive */
X    data[2] = tochar(rtimo);		/* When I want to be timed out */
X    data[3] = tochar(rpadn);		/* How much padding I need */
X    data[4] = ctl(rpadc);		/* Padding character I want */
X    data[5] = tochar(reol);		/* End-of-Line character I want */
X    data[6] = sctlq;			/* Control-Quote character I send */
X    switch(rqf) {			/* 8th-bit prefix */
X	case -1:
X	case  1: if (parity) ebq = sq = '&'; break;
X	case  0:
X	case  2: break;
X    }
X    data[7] = sq;
X    data[8] = bctr + '0';		/* Block Check Type */
X    if (rptflg) data[9] = rptq; else data[9] = '~';
X    data[10] = tochar(atcapr?atcapb:0 | lpcapr?lpcapb:0 | swcapr?swcapb:0);
X    capas = 10;
X    data[capas+1] = tochar(swcapr ? wsize : 0);	/* Window size */
X    data[capas+2] = tochar(rpsiz / 95);	/* Long packet size */
X    data[capas+3] = tochar(rpsiz % 95);	/* ... */
X    data[capas+4] = '\0';
X    return(data+1);			/* Return a pointer to the string */
X}
X
X/*
X * proto()--Kermit protocol entry point.  Set your start state and call
X * this, NOT wart().  Modified to set long packets capability on the
X * basis of the packet size set in the external user interface.
X */
Xvoid
Xproto()
X{
X    void *malloc();
X
X    sndpkt = (char *) malloc((unsigned) (MAXSP+200));
X    rcvpkt = (char *) malloc((unsigned) (MAXRP+100));
X    data = (char *) malloc((unsigned) (MAXSP+1));
X    if (urpsiz > 94) {				/* Long packets? */
X	rpsiz = (urpsiz > MAXRP - 20 ? MAXRP - 20 : urpsiz);
X	lpcapr = 1;				/* Request long packets */
X	bctr = 3;				/* And 16 bit CRC's	*/
X    } else {					/* No long packets */
X	lpcapr = 0;
X	bctr = 1;				/* Drop back to type 1	*/
X	if (urpsiz < 10)			/* Too small?	*/
X	    rpsiz = 80;
X	else
X	    rpsiz = DRPSIZ;
X    }
X    cx = cz = 0;				/* Haven't aborted yet */
X    if (sndpkt == NULL || rcvpkt == NULL || data == NULL)
X	tmsgl("Can't allocate memory for Kermit!!");
X    else
X	wart();
X    if (sndpkt) free(sndpkt);
X    if (rcvpkt) free(rcvpkt);
X    if (data) free(data);
X}
X    
X/*
X * That ends the system-independent Kermit modules.  What follows
X * are the system-dependent ones.
X */
X
X/*
X * Now for the file routines.  I chose to use the z...() routines
X * written in terms of stdio.
X */
X
X#include <stdio.h>
X
Xstatic FILE *ifp = NULL, *ofp = NULL;
X
Xstatic
Xzopeni(name) char *name; {
X    ifp = fopen(name, "r");
X    if (ifp == NULL)
X	return -1;
X    else
X	return 0;
X}
X
Xstatic
Xzopeno(name) char *name; {
X    ofp = fopen(name, "w");
X    if (ofp == NULL)
X	return -1;
X    else
X	return 0;
X}
X
Xstatic
Xzclosi() {
X    int x;
X
X    if (ifp == NULL)
X	return 0;
X    x = fclose(ifp);
X    ifp = NULL;
X    if (x < 0)
X	return -1;
X    else
X	return 0;
X}
X
Xstatic
Xzcloso(discard) int discard; {
X    int x;
X
X    if (ofp == NULL)
X	return 0;
X    x = fclose(ofp);
X    ofp = NULL;
X    if (x < 0)
X	return -1;
X    else if (discard)
X	if (unlink(filnam) < 0)
X		return -1;
X    return 0;
X}
X
X#include <ctype.h>
X
Xextern int convert;		/* 0 for literal files, 1 for translate */
X
Xstatic void
Xzrtol(s1,s2,warn) char *s1, *s2; int warn; {
X    strcpy(s2,s1);		/* for now */
X    if (convert)
X	while (*s2 != '\0') {
X	    if (isalpha(*s2))
X		*s2 = tolower(*s2);
X	    s2++;
X	}
X}
X
Xstatic void
Xzltor(s1,s2) char *s1, *s2; {
X    char dotseen = 0;
X
X    if (!convert)
X	strcpy(s2, s1);
X    else {
X	while (*s1 != '\0') {
X	    if (islower(*s1))
X		*s2 = toupper(*s1);
X	    else if (isalnum(*s1))
X		*s2 = *s1;
X	    else if (*s1 == '.')
X		if (!dotseen) {
X		    dotseen = 1;
X		    *s2 = *s1;
X		} else
X		    *s2 = 'X';
X	    /* else a character we're not prepared to handle. */
X	    s1++; s2++;
X    	}
X	*s2 = '\0';
X    }
X}
X
X/*
X * System-dependent function to return next character from file.
X * If the text flag argument is nonzero, first convert to canonic form. 
X */
Xstatic
Xzgetc(text) {				/* Get next char from file. */
X#define MAXREC 100			/* Size of record buffer. */
X
X    static char recbuf[MAXREC + 1];	/* Big enough for MAXREC newlines */
X    static char *rbp;			/* Buffer pointer */
X    static int i = 0;			/* Buffer char counter */
X    int c;
X
X    if (i == 0) {			/* If the buffer is empty, */
X					/* read next line from file. */
X	for (i = 0;
X	     i < MAXREC - 1 && (c = getc(ifp)) != EOF && c != '\n';
X	     i++)
X	    recbuf[i] = c;
X	if (c == '\n') {		/* Got newline		*/
X	    if (text) {			/* If in text mode	*/
X		recbuf[i++] = '\r';	/* Substitute CRLF	*/
X	    }
X	    recbuf[i++] = c;
X	}
X	recbuf[i] = '\0';		/* Done, terminate buffer */
X	if (i == 0) return -1;		/* If empty, indicate EOF */
X	rbp = recbuf;			/* Remember position for next time */
X    }
X    i--;				/* Adjust the counter. */
X    return(*rbp++ & 0377);		/* Return hext character */
X}
X
Xstatic
Xzputc(c, text) int c, text; {		/* Put character in file. */
X    unsigned int x;
X
X    c &= 255;				/* Undo any sign extension */
X    if (text && c == '\r')		/* If in text mode, */
X    	return 0;			/* eliminate carriage returns. */
X    else {				/* Otherwise, */
X	x = putc(c, ofp) & 255;		/* output the character. */
X	if (c == 255) return 0;		/* Special handling for all 1's */
X	return ((x != c) ? -1 : 0);	/* Normal return code. */
X    }
X}
SHAR_EOF
echo "End of archive 6 (of 9)"
# if you want to concatenate archives, remove anything after this line
exit