home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-12-21 | 60.5 KB | 2,089 lines |
- /*
- * A completely new C Kermit module.
- *
- * Based on code from Frank Da Cruz's excellent book, _Kermit: A File
- * Transfer Protocol_, Digital Press, 1986.
- *
- * As this code is almost entirely from said book, it is certainly covered
- * by that book's copyright. Basically, this means the code is freely
- * distributable, and can be used in a commercial program provided such
- * use does not increase the program's cost to the purchaser beyond a small
- * handling fee.
- *
- * Stephen Walton, swalton@solar.stanford.edu
- * Dept. of Physics and Astronomy
- * California State University, Northridge
- * Northridge, CA 91330
- *
- * ORGANIZATION
- *
- * This file is in three pieces, and could probably be broken into
- * two files. Leading off are #DEFINE's and declarations of
- * global and external variables. Following are the unmodified sources
- * from The Book. Then are the z* file-handling routines written using
- * standard Unix-style (almost ANSI C) file routines.
- *
- * I make no apologies for the organization; my primary goals were (1) to
- * use the unmodified book source except for errors found by Lint, and
- * (2) to allow this file to be plugged into an otherwise unmodified
- * terminal program for the Commodore Amiga computer called VT100. The
- * comments starting with the string "lint" are for the Lint program
- * of Gimpel Software, available for the Commodore Amiga and MS-DOS
- * machines. I highly recommend it.
- *
- * A few words about style herein. Both "book Kermit" and the official
- * C Kermit release make extensive use of global variables to set various
- * options and keep track of what is going on. I don't like this, but
- * since example source code must be part of the Kermit specification,
- * I chose to keep that organization. There are several places where I
- * I have tried to modularize things better. First of all, everything
- * which is not needed outside of this file is declared "static". Second,
- * I have kept the book Kermit code pretty much intact, except for some lint
- * related things and one extra convention: tmsg() appends characters to
- * an existing status line, but tmsgl() is required to force that output to
- * be seen. Hence, I've changed the last in each series of tmsg() calls to
- * tmsgl().
- */
-
-
- /*
- * Revision History--Versions 0 through 1 never left me.
- *
- * Version 0.5--Created and linted.
- * Version 0.6--Added RESUME to handling of "unknown packets".
- * --Added proto() function to wrap around wart() for handling
- * of startup and cleanup functions.
- * --Changed handling of timeouts in input(): instead of an
- * error("Timeout") call, it puts the "Timeout" message in
- * the data field and pretends it read an 'E' packet.
- * Version 0.7--Fixed a problem with rpsiz and MAXRP. I had set rpsiz
- * to MAXRP in rpar() and was calling ttinl() with a max
- * argument of MAXRP, which resulted in truncated packets;
- * to wit, MAXRP+1 characters could come into ttinl counting
- * the eol. Created defines DRPSIZ/DSPSIZ for default values
- * for rpsiz and spsiz, and increased MAXRP.
- * --Changed rcvpkt, sndpkt, and data so that they are pointers
- * instead of static arrays, and allocate and de-allocate
- * them in proto().
- * Version 0.8--Added client support. To make a Get
- * command, point the char *cmarg at the remote file
- * specification and set the start state to 'r'
- * (extern char start in the calling file).
- * Version 0.9--Added long packet support. This required adding an extern
- * int urpsiz which is set to the desired receive packet size
- * by the user interface.
- * --Since the above required using the capas, I also added the
- * code from C Kermit 4E(070) for negotiating window size
- * in rpar() and spar(). Of course, we don't do windows yet.
- * --Fixed a bug which is also part of C Kermit 4E(070).
- * If we tell the sender that we can receive a packet
- * of size MAXRP, the packet can contain as many as
- * MAXRP+5 characters, counting the EOL and extended
- * headers. So rpack() must be able to handle somewhat
- * more characters than the actual maximum packet length.
- * I defined MAXRP here to be 1024, but only allow
- * rpsiz in proto() to be MAXRP-20.
- * Version 0.95--Added code to gracefully abort transfer. This works
- * via the cx and cz external variables and the state 'a'.
- * Version 1.0--Unleashed upon the world.
- * Version 1.1--Marco Papa (papa on BIX, papa@pollux.usc.edu on Internet)
- * added the code between the #ifdef XPRKERMIT...#endif
- * pairs, which turns this code into a routine which can
- * be used as an Amiga external file transfer protocol.
- * Upon getting it back from him, I fixed two lingering
- * bugs: there was an "if (ebqflg & b8)" in encode()
- * which should be &&, and the first argument of encode()
- * needed to be declared as an int, not a char.
- * --Allow user to set bctr (block check to request) himself.
- * --If urpsiz is illegal, we set rpsiz to a legal value; I also
- * set urpsiz to this as well, so the user knows what's up.
- * Version 1.2--Added a typedef of CHAR to unsigned char to allow this code
- * to work with the latest version of Wart (V 1A(006) dated
- * 12 January 1989.
- * Version 1.3--21 December 1989
- * (1) Added a tflush() call to the end of decode(). This
- * gives the user an opportunity to flush out the messages
- * from X packets, if the I/O is buffered.
- *
- * Version 1.4-- 5 January 1989
- * (1) Added the counters for characters on a per-file and
- * per-transaction basis. Later, we can use the per-file
- * ones and a timer to figure out how long we have to go
- * on the current file.
- * (2) Started replacing tchar() calls with calls to the
- * screen() function, again a la C Kermit. #define's for
- * the manifest constants for screen() are in kermitproto.h,
- * copied from C Kermit.
- *
- * Version 1.6-- 18 August 1990 (Version 1.4 was released)
- * (1) Added ANSI-specific items, in particular the #include
- * of "kermitproto.h" and "xproto.h" which have all of the
- * prototypes.
- * (2) Fixed gnfile(), which was not being properly re-
- * initialized if a send was canceled.
- *
- * Version 1.63--2 December 1990
- * (1) Changed the behavior of the xpr chkint() function so
- * that the error exit is taken if (*xpr_chkabort)() returns
- * -1: an error packet is made up and sent and the protocol
- * quits immediately.
- * (2) Discovered that the file character count in gnchar()
- * skipped nulls! Fixed.
- *
- * Version 1.64--5 December 1990
- * Fixed zgetc(), which was hanging onto characters from the
- * previous file transfer if it was cancelled.
- *
- * Version 1.67--14 December 1990 (includes 1.65 through 1.67 changes)
- * Added buffered I/O from C Kermit 5A(163).
- * Replaced getpkt() with version from C Kermit 5A. Got rid
- * of encode(), since it is built in.
- * Fixed a long standing block check 2 bug--a missing break
- * in the case statement in rpack()!
- *
- * Version 1.72--18 December 1990
- * Fixed *MAJOR* bugs in getpkt() from C Kermit: maxsize()
- * is spurious, and should be bufmax; and the leftover[]
- * array needs to be 7 characters long, not 6: the worst
- * case is two successive eight-bit-quoted control chars.
- */
-
- /*
- * Run this through the Wart preprocessor which comes with C Kermit.
- */
-
- #ifndef _lint
- static char rcsid[] = "$Header: Work:src/xprkermit/RCS/kermitproto.w,v 1.3 91/11/09 06:49:18 swalton Exp Locker: swalton $";
- #endif
-
- #ifdef XPRKERMIT
- extern long (*xupdate)(), (*xswrite)(), (*xfopen)(), (*xfclose)(), (*xfread)(),
- (*xfwrite)(), (*xsread)(), (*xchkabort)(void), (*xffirst)(), (*xfnext)(),
- (*xsflush)(void), (*xfinfo)(), (*xgets)(), (*xunlink)(), (*xsquery)(void),
- (*xchkmisc)(void);
- extern long brkflag;
- #include "types.h"
- #include "xprkermit.h"
- #endif
-
- #include "kermitproto.h"
- #include <stdlib.h>
- #include <string.h>
-
-
- /* kermitproto.c */
- static int input(void);
- static void errpkt(char *s);
- #pragma regcall(errpkt(a0))
- static int nxtpkt(void);
- static int tinit(void);
- static int error(char *s);
- #pragma regcall(error(a0))
- static int ack(void);
- static int ack1(char *s);
- #pragma regcall(ack1(a0))
- static int nak(void);
- static int sinit(int c);
- #pragma regcall(sinit(d0))
- static int scmd(int t, char *s);
- #pragma regcall(scmd(d0,a0))
- static int rinit(void);
- static void resetc(void);
- static int sfile(void);
- static int sdata(void);
- static int seof(char *s);
- #pragma regcall(seof(a0))
- static int seot(void);
- static int rcvfil(void);
- static int reof(char *);
- #pragma regcall(reof(a0))
- static void fstats(void);
- static int closof(int nokeep);
- #pragma regcall(closof(d0))
- static int spack(int type, int n, int len, CHAR *d);
- #pragma regcall(spack(d0,d1,d2,a0))
- static int resend(void);
- static int rpack(void);
- static unsigned chksum(CHAR *s);
- #pragma regcall(chksum(a0))
- static unsigned chk1(CHAR *s);
- #pragma regcall(chk1(a0))
- static unsigned chk3(CHAR *s);
- #pragma regcall(chk3(a0))
- static int getpkt(int maxlen);
- #pragma regcall(getpkt(d0))
- static int decode(void);
- static int encstr(char *s);
- #pragma regcall(encstr(a0))
- static int decstr(char *s);
- #pragma regcall(decstr(a0))
- static int spar(CHAR *s);
- #pragma regcall(spar(a0))
- static char *rpar(void);
- static int zclear(void);
- static int zopeni(char *name);
- #pragma regcall(zopeni(a0))
- static int zopeno(char *name);
- #pragma regcall(zopeno(a0))
- static int zclosi(void);
- static int zcloso(int discard);
- #pragma regcall(zcloso(d0))
- static int zrtol(char *s1, char *s2, int warn);
- #pragma regcall(zrtol(a0,a1,d0))
- static int zltor(char *s1, char *s2);
- #pragma regcall(zltor(a0,a1))
- static int zinfill(void);
- static int zoutdump(void);
- static int tflush(void);
-
- /*
- * Buffered I/O stuff stolen from real C Kermit
- */
- #define INBUFSIZE 1024 /* Size of record buffer. */
- #define OBUFSIZE 1024 /* Size of output buffer. */
- #define MAXNAMELEN 256 /* Maximum file name length */
-
- static CHAR *zinbuffer, *zoutbuffer;
- static CHAR *zinptr, *zoutptr; /* Buffer pointers */
- static int zincnt, zoutcnt; /* Buffer char counters */
-
-
- /* get the next char; sorta like a getc() macro */
- #define zminchar() (((--zincnt)>=0) ? ((int)(*zinptr++) & 0377) : zinfill())
-
- /* stuff a character into the input buffer */
- #define zmstuff(c) zinptr--, *zinptr = c, zincnt++
-
- /* put a character to a file, like putchar() macro */
- #define zmchout(c) \
- ((*zoutptr++=(CHAR)(c)),(((++zoutcnt)>=OBUFSIZE)?zoutdump():0))
-
- /*
- * Here are the variables which need to be set to startup values, and which
- * also can be freely changed between protocol transfers. At first I thought
- * to declare them all "extern" in order to force definition elsewhere.
- * On reflection, it makes sense to both declare them here and set them to
- * their default startup values. Thus they can be ignored outside of this
- * module if you so desire.
- *
- * Note that the names are very systematic: Names beginning with "r" have
- * to do with values I use for received packets; those beginning with "s"
- * are values I use for sending packets. Also note we set some, others are
- * set for us. I have made the ones we get in spar() static (local),
- * and the ones we send in rpar() global.
- *
- * First the ints.
- */
-
- int cx = 0,
- cz = 0, /* Flags for aborting transfers. cx (control-X)*
- * is set to 1 if an abort of the current file *
- * is desired, cz (control-Z) if an abort of *
- * an entire batch transfer is desired. */
- rpsiz = MAXRP, /* Maximum packet size */
- /* Like most of the receive packet parameters, *
- * this one is actually set by the sender. But *
- * since the sender has the option to not send *
- * these, we must initialize to "reasonable" *
- * defaults. */
- bctr = 1, /* Block check type to request. */
- limit = 5, /* Retry limit on receive */
- warn = 0, /* 1 for warn before overwriting files */
- keep = 0, /* 1 to keep incomplete files */
- rpadn = 0, /* Number of pad characters I require. */
- rtimo = 10; /* How long I want you to wait before you *
- * you time me out. */
-
- CHAR rmark = '\1', /* Start of packet marker for receive */
- reol = '\r', /* End of packet marker for receive */
- start = 0, /* Start state */
- sctlq = '#', /* Control character quote character for send */
- rpadc = '\0'; /* Pad character I want you to use */
-
- /*
- * Variables which MUST be set by the external interface.
- */
- extern int
- parity, /* 0 for no parity--need for proper 8th-bit quote */
- text, /* Flag 1 for text file, 0 for binary file */
- urpsiz; /* Maximum receive packet size user wants. */
-
- extern char
- *cmarg; /* Character string containing Kermit server cmd */
-
- /*
- * Variables having to do with the status of the file transfer.
- */
- static long tfc, /* File chararacters sent/received, total. */
- ffc, /* " " " , current file. */
- timeouts, /* Number of timeouts */
- tlci, /* Comm line characters in, total. */
- flci, /* " " " ", current file. */
- tlco, /* " " " ", total. */
- flco; /* " " " ", current file. */
- /*
- * In a fit of cleverness, here are some macro defines for variables we *
- * aren't currently using. Only now we tell Lint to ignore constant *
- * Booleans!
- */
-
- /*lint -e506 */
-
- #define local 1 /* Local mode flag--that is, I'm on your end */
- #define server 0 /* We are never server */
- #define delay 0 /* Time to delay before sending first packet */
- #define xpkt 0 /* Send X packet instead of F? */
-
- /*
- * This block of defines is strictly internal flags of various kinds. *
- * I hope to Grid that I've got them all. Someday this will be cleaner *
- */
- static int
- spsiz = DSPSIZ, /* Maximum send packet size */
- wsize = MAXWS, /* Maximum window size */
- sndpkl, /* Size of packet currently being attempted */
- filcnt, /* Number of files transferred so far */
- bctu = 1, /* Block check type to use */
- rqf, /* Flag for 8th bit quote negotiations */
- ebqflg = 0, /* 8th-bit quoting flag */
- xflag, /* Output to screen for generic server commands */
- rq = 0, /* Received 8bq bid */
- rpt = 0, /* Repeat count */
- rptflg = 0, /* Repeat processing flag */
- capas = 10, /* Final position of inbound capas mask */
- atcapr = 0, /* Attribute capability requested */
- atcapu = 0, /* Attribute capability used */
- swcapr = 0, /* Sliding windows capability requested */
- swcapu = 0, /* Sliding windows capability used */
- lpcapr = 0, /* Long packets capability requested */
- lpcapu = 0, /* Long packets capability used */
- rsn, /* Received sequence number */
- seq = 0, /* Current sequence number */
- rln, /* Length of received data field */
- size, /* Current size of output packet data */
- first = 0, /* Some kind of lookahead flag */
- stimo = 5, /* Timeout interval for me to use */
- spadn = 0; /* Number of pad characters for me to use */
-
- #define atcapb 8 /* Attribute capability bit */
- #define swcapb 4 /* Sliding windows capability bit */
- #define lpcapb 2 /* Long packets capability bit */
- #define closif zclosi /* I use closif() to close the input file in *
- * case it needs to be more complex later, but *
- * for now it just calls the z routine. */
-
- static char
- filnam[MAXNAMELEN], /* Current file name */
- myname[MAXNAMELEN], /* Current local file name */
- ssc, /* Start server command */
- memstr, /* Use in-memory string? */
- *osp = NULL; /* Output string pointer */
-
- static CHAR
- smark = '\1', /* Start of packet marker for send */
- spadc = '\0', /* Pad character to use on sending */
- seol = '\r', /* End of packet marker for sending */
- rctlq = '#', /* Control character quote char for receiving */
- ebq = '&', /* 8th-bit prefix */
- sq = 'Y', /* Sent 8bq bid */
- rptq = '~', /* Repeat prefix */
- *memptr = NULL, /* Pointer to characters in memory */
- *sndpkt, /* Send packet. */
- *rcvpkt, /* Receieve packet. */
- *data, /* Data to send/receive before encode/decode */
- *rdatap; /* Pointer to null-terminated data field */
-
- #define ERR(s) error(s); RESUME
- #define RESUME return
-
- /*lint -save -e525 -e527 We don't care how Wart formats! */
- /*lint -e716 -e744 -e570 -esym(745,wart) Other Wart peculiarities */
-
- %states ssfil ssdat sseot
- %states srini srfil srdat
- %states sipkt srgen
- %%
-
- /* Start states */
-
- s { /* - Start State - */
- tinit(); /* Initialize transaction */
- if (sinit('S') < 0) { ERR("sinit"); } /* Build, send Send-Init. */
- else { /* If successful, */
- filcnt = 0; /* initialize file counter */
- BEGIN ssfil; /* and switch to ssfil state. */
- }
- }
- v { tinit(); rinit(); BEGIN srini; } /* - Receive - */
-
- r { /* Get */
- tinit(); ssc = 0;
- if (sinit('I') < 0) { ERR("sinit"); }
- else BEGIN sipkt;
- }
-
- c { /* Host */
- tinit(); ssc = 'C';
- if (sinit('I') < 0) { ERR("sinit"); }
- else BEGIN sipkt;
- }
-
- g { /* Generic */
- tinit(); ssc = 'G';
- if (sinit('I') < 0) { ERR("sinit"); }
- else BEGIN sipkt;
- }
-
- a { /* Immediate protocol abort */
- spack('E', seq, 21, (CHAR *) "User aborted protocol");
- closif(); closof(1); /* Close files, deleting output */
- RESUME;
- }
-
- /* Dynamic states, sending file(s) */
-
- <ssfil>Y { /* - Send File State - */
- if (filcnt++ == 0) spar(rdatap); /* Set parameters if 1st time */
- cx = 0; /* Reset file interruption flag */
- bctu = bctr; /* Switch to negotiated block check */
- resetc(); /* Reset global counters. */
- #ifdef XPRKERMIT
- ST_Display_CRC(bctu);
- #endif
- /* Is there a file to send in an uncancelled batch? */
- if (!cz && gnfile(filnam, sizeof(filnam)) > 0) {
- if (sfile() < 0) { ERR("sfile"); } /* Yes, open it, send F packet */
- else BEGIN ssdat; /* and if no error, switch state. */
- } else { /* No (more) files to send */
- if (seot() < 0) { ERR("seot"); } /* so send B packet */
- else BEGIN sseot; /* and switch to sseot state. */
- }
- }
- <ssdat>Y { /* - Send Data State - */
- int x;
- if (rln == 1 && *rdatap == 'X') /* Did ACK contain X as data? */
- cx = 1; /* Yes, set control-x flag. */
- else if (rln == 1 && *rdatap == 'Z') /* Did ACK contain Z as data? */
- cz = 1; /* Yes set control-z flag. */
- /* Check here for cancellation of transfer and data left to send. */
- if (cx || cz || (x = sdata()) == 0) {
- if (seof((cx || cz) ? "D" : "") < 0) { /* If not, send Z packet. */
- ERR("seof");
- }
- else BEGIN ssfil; /* and go back to ssfil state. */
- } else if (x < 0) { ERR("sdata"); } /* Handle file i/o errors. */
- }
- <sseot>Y { RESUME; } /* - Send B, done. */
-
- /* Dynamic states, receiving file(s) */
-
- <srini,srgen>S {
- resetc();
- spar(rdatap);
- (void) ack1(rpar());
- bctu = bctr;
- BEGIN srfil;
- }
-
- <srfil>B { (void) ack(); RESUME; }
-
- <srfil>F { if (rcvfil() < 0) { ERR("rcvfil"); } else { (void) ack(); BEGIN srdat; } }
-
- <srdat>D {
- if (cx)
- ack1("X");
- else if (cz)
- ack1("Z");
- else if (decode() < 0) {
- ERR("Error writing data");
- } else
- (void) ack();
- }
-
- <srdat>Z {
- if (reof(filnam) < 0) { /* Close & dispose of the file */
- errpkt("Can't close file"); /* If problem, send error message */
- RESUME; /* and quit */
- } else { /* otherwise */
- ack(); /* acknowledge the EOF packet */
- BEGIN srfil; /* and await another file */
- }
- }
-
- /* Dynamic states, server mode */
-
- <sipkt>Y { /* Got ACK for I packet */
- spar(rdatap); /* Set parameters from it */
- start = 'E'; /* Force entry into next state */
- }
-
- <sipkt>E { /* Got E for I packet */
- if (ssc) {
- if (scmd(ssc,cmarg) < 0) { ERR("scmd"); }
- else BEGIN srgen;
- } else {
- if (scmd('R',cmarg) < 0) { ERR("scmd"); }
- else BEGIN srini;
- }
- }
-
- <srgen>Y { xflag = 1; decode(); RESUME; }
-
- <srgen,srfil>X { xflag = 1; decode(); ack(); BEGIN srdat; }
-
- /* Error state */
-
- E { error((char *) rdatap);
- (void) closif();
- (void) closof(1); /* close files, discarding output */
- RESUME; }
-
- . { error("Unexpected packet type"); RESUME; }
- /* Handle unwanted packet types. */
- %%
-
- /*lint -restore -esym(551,tlci,tlco,flci,flco,rptn,atcapu,tfc) */
-
- static void
- errpkt(char *msg) {
- spack('E', seq, (int) strlen(msg), (CHAR *) msg);
- }
-
- static
- int
- input() { /* Return packets */
- int type, try;
-
- if (start != 0) { /* Start state in effect? */
- type = start; /* Yes, call it a packet type, */
- start = 0; /* nullify the start state, */
- return(type); /* and return the type. */
- }
- for (try = 0; ; try++) {
- type = rpack(); /* No start state, read a packet. */
- /*
- * Some checks we do every packet.
- */
- if (try > limit) { /* If too mahy tries, */
- strcpy((char *) data, "Too many retries"); /* give up */
- rdatap = data; /* Make up pretend 'E' packet */
- type = 'E'; /* and return it */
- break;
- }
- #ifdef XPRKERMIT
- if (xchkmisc)
- (void) (*xchkmisc)();
- #endif
- if (chkint() < 0) { /* Check for console interrupt. */
- errpkt("User cancelled.");
- strcpy((char *) data, "User cancelled.");
- rdatap = data;
- type = 'E';
- break;
- }
- /*
- * Now see if we got the packet we want.
- */
- if (rsn == seq && strchr("TQN",type) == 0) /* packet we want */
- break;
- else if (type == 'N' && rsn == ((seq + 1) & 63)) {
- /* NAK for next packet */
- type = 'Y'; /* is ACK for current. */
- break;
- } else { /* Otherwise, */
- (void) resend(); /* resend previous packet. */
- }
- }
- ttflui(); /* Got a good one, clear buffer. */
- return(type); /* Return its type. */
- }
-
- static
- nxtpkt() {
- seq = (seq + 1) & 63; /* Next packet number, mod 64 */
- }
-
- /* R E S E T C -- Reset per-transaction character counters */
- static void resetc(void) {
- tfc = tlci = tlco = 0; /* Total file chars, line chars in & out */
- timeouts = 0; /* Timeouts */
- }
-
- static
- tinit() { /* Transaction initialization */
- seq = 0; /* Start off with packet 0 */
- ebqflg = 0; /* 8-bit quoting off */
- sq = 'Y'; /* Normal 8-bit quote bid */
- rqf = -1; /* Flag other's bid not yet seen */
- rptflg = 0; /* No repeat counts by default */
- bctu = 1; /* Block check to use back to 1 */
- xflag = 0; /* Output normally to file */
- memstr = 0; /* Reset memory-string flag */
- memptr = NULL; /* and poiinter */
- osp = NULL; /* ... */
- cx = cz = 0; /* Reset interrupt flags */
- *filnam = *sndpkt = *rcvpkt = '\0'; /* Clear string buffers */
- spsiz = rpsiz; /* Initial send-packet size */
- }
-
- static
- error(s) char *s; { /* Fatal error */
- if (local) { /* If in local mode */
- screen(SCR_EM,0,0L,s); /* Type message on console */
- } else { /* Otherwise, */
- (void) spack('E',seq,(int) strlen(s), (CHAR *) s); /* Send in error packet. */
- }
- return;
- }
-
- static
- ack() {
- int x; /* Empty acknowledgement */
- x = spack('Y',seq,0,(CHAR *) ""); /* Send the packet */
- nxtpkt(); /* Increment the packet number */
- return(x);
- }
-
- static
- ack1(s) char *s; {
- int x; /* Acknowledgement with data */
- x = spack('Y',seq,(int)strlen(s),(CHAR *)s);
- nxtpkt(); /* Increment packet number */
- return(x);
- }
-
- static
- nak() { /* Negative acknowledgement */
- return(spack('N',seq,0,(CHAR *)"")); /* Never has data! */
- }
-
- /* Functions used by file sender. */
-
- /* sinit()--start the transaction by filling in the initialization string
- * and sending it in an S packet.
- */
-
- static
- sinit(c) char c; {
- char *s;
-
- gninit(); /* Initialize gnfile() function. */
- s = rpar();
- if (local == 0 && c == 'S' && server == 0) {
- #ifdef XPRKERMIT
- ST_Display_String(STMsg,"Escape back to local system, RECEIVE command");
- #else
- tmsgl("Escape back to local system, give RECEIVE command...");
- #endif
- sleep(delay);
- }
- return(spack(c,seq,(int)strlen(s),(CHAR *)s));
- }
-
- /*
- * scmd() -- send a preformatted Kermit server command string.
- */
- static
- scmd(t, s) char t, *s; { /* Send a packet of the given type */
- encstr(s); /* Encode the command string */
- spack(t,seq,size,data);
- }
-
- /* rinit() -- do whatever is needed to initialize receive. Now a no-op.
- */
- static
- rinit()
- {
- }
-
- /* sfile() -- open the file and send the File-Header packet. Assumes that
- * the global string pointer filnam references the file name.
- */
-
- static
- sfile() {
- int x;
- char pktnam[MAXNAMELEN];
- if (zopeni(filnam) < 0) /* Try to open file. */
- return -1;
- zltor(filnam,pktnam); /* OK, convert name. */
- x = encstr(pktnam); /* Encode the result */
- if (local) { /* If in local mode, */
- #ifdef XPRKERMIT
- ST_Display_String(STFile, pktnam);
- ST_Display_String(STUplSize, filnam);
- #else
- tmsg("Sending "); /* let user know we're */
- tmsg(filnam); /* sending this file */
- tmsg(" as "); /* under */
- tmsgl(pktnam); /* this name */
- #endif
- }
- first = 1; /* Flag beginning of file */
- ffc = flci = flco = 0; /* Zero per-file char count */
- nxtpkt(); /* Increment packet number */
- return(spack((xpkt ? 'X' : 'F'),seq,x,data)); /* Send packet */
- }
-
- /* sdata() -- get next packet's worth of data */
-
- static
- sdata() {
- int x;
-
- if (spsiz > 94) /* Long packets */
- x = getpkt(spsiz - bctu - 6);
- else /* Short packets. */
- x = getpkt(spsiz - bctu - 3);
- if (x == 0) /* If no data left to send, */
- return(0); /* return EOF indication. */
- nxtpkt();
- return(spack('D',seq,x,data)); /* Send the data packet */
- }
-
- /* seof -- close the input file and send a Z packet. */
-
- static
- seof(s) char *s; {
- if (closif() < 0) /* Try to close the file. */
- return -1; /* On error, return failure. */
- else { /* Otherwise, */
- #ifdef XPRKERMIT
- if (local) ST_Display_String(STMsg,"OK");
- #else
- if (local) tmsgl("OK"); /* if local, reassure user. */
- #endif
- nxtpkt();
- return(spack('Z',seq,(int)strlen(s),(CHAR *) s)); /* Send Z packet */
- }
- }
-
- /* seot -- send B packet. */
-
- static
- seot() {
- nxtpkt();
- #ifdef XPRKERMIT
- if (local) ST_Display_String(STMsg,"Done");
- #else
- if (local) tmsgl("Done");
- #endif
- return(spack('B',seq,0,(CHAR *) ""));
- }
-
- static
- rcvfil() { /* Receive a file */
-
- ffc = flci = flco = 0; /* Initialize per-file char count */
- decstr(filnam); /* Decode name */
- zrtol(filnam,myname,warn); /* Convert to local form. */
- if (zopeno(myname) < 0)
- return -1;
- else { /* File open OK, give message. */
- if (local && !xflag) {
- #ifdef XPRKERMIT
- ST_Display_String(STFile,myname);
- ST_Display_CRC(bctu);
- #else
- tmsg("Receiving "); tmsg(filnam); tmsg(" as "); tmsgl(myname);
- #endif
- }
- return 0;
- }
- }
-
- /* R E O F -- Receive End Of File */
-
- static
- reof(f) char *f; {
- int x;
- char *p;
-
- if (cx == 0) cx = (*rdatap == 'D'); /* Got discard directive? */
- x = closof((cx || cz) && (keep == 0));
- if (cx || cz) {
- cx = 0;
- if (keep)
- p = "*** Incomplete file";
- else
- p = "*** Discarding file";;
- screen(SCR_EM, 0, 0L, p);
- } else {
- fstats();
- }
- *filnam = '\0';
- return(x);
- }
-
- static void /* Update statistics */
- fstats() {
- tfc += ffc;
- }
-
- static
- closof(nokeep) int nokeep; { /* Close output file, but */
- if (xflag) return 0; /* not if it's the screen. */
- if (zcloso(nokeep) < 0)
- return -1;
- return 0;
- }
-
- static
- spack(type,n,len,d) char type; int n, len; CHAR *d; {
- int i = 0, j, k;
- unsigned int x;
- CHAR *sohp; /* Mark start of packet data. */
-
- for (i = 0; i < spadn; i++)
- sndpkt[i] = spadc; /* Do requested padding */
- sohp = sndpkt + i;
- sndpkt[i++] = smark; /* Packet mark */
- k = i++; /* Remember this place */
- sndpkt[i++] = tochar(n); /* Sequence number */
- sndpkt[i++] = (CHAR) type; /* Packet type */
- j = len + bctu; /* True length */
- if (j > 92) { /* Long packet? */
- sndpkt[k] = tochar(0); /* Set LEN to 0 */
- sndpkt[i++] = tochar(j / 95); /* High part of length */
- sndpkt[i++] = tochar(j % 95); /* Low part of length */
- sndpkt[i] = '\0'; /* Header checksum */
- sndpkt[i++] = tochar(chk1(sndpkt+k));
- } else
- sndpkt[k] = tochar(j+2); /* True length. */
-
- for (j = len; j > 0; j--) { /* Data */
- sndpkt[i++] = *d++;
- }
- sndpkt[i] = '\0'; /* Null terminate. */
- switch (bctu) {
- case 1: /* Type 1 - 6 bit checksum */
- sndpkt[i++] = tochar(chk1(sndpkt+k));
- break;
- case 2: /* Type 2 - 12 bit checksum */
- x = chksum(sndpkt+k);
- sndpkt[i++] = tochar((x >> 6) & 077);
- sndpkt[i++] = tochar(x & 077);
- break;
- case 3: /* Type 3 - 16 bit CRC-CCITT */
- x = chk3(sndpkt + k);
- sndpkt[i++] = tochar((x >> 12) & 017);
- sndpkt[i++] = tochar((x >> 6) & 077);
- sndpkt[i++] = tochar(x & 077);
- break;
- default:
- return -1; /* Error message here? */
- }
- sndpkt[i++] = seol; /* End of line */
- sndpkt[i] = '\0'; /* Null string-terminat or. */
- sndpkl = i; /* Remember length. */
- i = ttol((char *) sndpkt,sndpkl); /* Send the packet. */
- tlco += sndpkl; /* Count characters output. */
- flco += sndpkl;
- screen(SCR_PT, type, (long) n, (char *) sohp);
- return(i);
- }
-
- static
- resend() {
- int x;
- if (*sndpkt)
- x = ttol((char *) sndpkt,sndpkl); /* Send previous packet */
- else
- x = nak(); /* or NAK if none */
- if (local && !xflag) tchar('%'); /* Let the user know. */
- return(x);
- }
-
- static unsigned
- chk1(packet) CHAR *packet; { /* Compute Kermit's */
- unsigned s, t; /* 1-character block check. */
- s = chksum(packet); /* Get the arithmetic sum. */
- t = (((s & 192) >> 6) + s) & 63; /* Fold it into 6 bits. */
- return(t);
- }
-
- static unsigned
- chksum(p) CHAR *p; { /* Compute the checksum. */
- unsigned m;
- long s;
-
- m = (parity) ? 0177 : 0377; /* Mask for parity bit. */
- s = 0;
- for (; *p != '\0'; p++) /* For each character, */
- s += *p & m; /* accumulate the sum, */
- return(s & 07777); /* and then return it. */
- }
-
- /*
- * rpack reads a packet and returns the packet type, or else Q if the
- * packet was invalid, or T if a timeout occured. Upon successful return,
- * sets the global variables:
- * rsn - the received sequence number
- * rln - length of the received data field
- * rdatap - a pointer to the null-terminated contents of the data field
- */
- static
- rpack() {
- int i, j, type, rlnpos;
- unsigned x;
- CHAR pbc[4]; /* Packet block check. */
- CHAR *sohp; /* Start of packet data. */
-
- rsn = rln = -1; /* In case of failure. */
-
- *rcvpkt = '\0'; /* Initialize receive buffer. */
- j = ttinl((char *) rcvpkt,MAXRP,reol,stimo);
- if (j < 0) {
- if (j == -1) {
- screen(SCR_PT, 'T', (long) rsn, "");
- return('T'); /* Timed out. */
- } else
- return(j);
- }
-
- tlci += j; /* Count received characters. */
- flci += j; /* On per-file basis. */
- for (i = 0; rcvpkt[i] != rmark && (i < j); i++) /* Find mark. */
- ;
- if (i == j) return('Q'); /* If no mark, bad packet. */
- sohp = rcvpkt+i;
-
- rlnpos = ++i; /* Got it, remember position. */
- if ((j = unchar(rcvpkt[i++])) == 0) { /* Long packet? */
- j = rlnpos + 5; /* Yes, check header */
- if (j > MAXRP) return('Q'); /* Be defensive. */
- x = rcvpkt[j]; /* Remember header checksum */
- rcvpkt[j] = '\0';
- if (unchar(x) != chk1(rcvpkt+rlnpos)) /* Check header */
- return('Q');
- rcvpkt[j] = (CHAR) x; /* Restore packet */
- rln = unchar(rcvpkt[j-2]) * 95 + unchar(rcvpkt[j-1]) - bctu;
- j = 3;
- } else {
- rln = j - bctu - 2; /* Regular packet */
- j = 0; /* No extended header */
- }
- rsn = unchar(rcvpkt[i++]); /* Sequence number. */
- type = rcvpkt[i++]; /* Packet type */
- i += j; /* Skip extended header, if any */
- rdatap = rcvpkt + i; /* The data itself. */
- j = rln + i; /* Position of block check. */
- if (j > MAXRP)
- return('Q'); /* Be defensive! */
- for (i = 0; i < bctu; i++) /* Copy the block check. */
- pbc[i] = rcvpkt[j+i];
- rcvpkt[j] = '\0';
- switch (bctu) { /* Which block check type? */
- case 1:
- if (unchar(*pbc) != chk1(rcvpkt+rlnpos)) return('Q');
- break;
- case 2:
- x = unchar(*pbc) << 6 | unchar(pbc[1]);
- if (x != chksum(rcvpkt+rlnpos)) return('Q');
- break;
- case 3:
- x = unchar(*pbc) << 12 | unchar(pbc[1]) << 6 | unchar(pbc[2]);
- if (x != chk3(rcvpkt+rlnpos)) return('Q');
- break;
- default:
- error("Impossible block check type.");
- }
- screen(SCR_PT, type, (long) rsn, (char *) sohp);
- return type; /* Otherwise, return packet type */
- }
-
- /*
- * CHK3
- * Calculate the 16-bit CRC of a null-terminated string using a
- * byte-oriented tableless algorithm devised by Andy Lowry (Columbia
- * University). The magic number 010201 is derived from the CRC-CCITT
- * polynomial x^16+x^12+x^5+1.
- */
- static unsigned
- chk3(s) CHAR *s; {
- unsigned c, q;
- unsigned long crc = 0;
-
- while ((c = *s++) != '\0') {
- if (parity) c &= 0177;
- q = (crc ^ c) & 017; /* Low order nybble */
- crc = (crc >> 4) ^ (q * 010201);
- q = (crc ^ (c >> 4)) & 017; /* High order nybble */
- crc = (crc >> 4) ^ (q * 010201);
- }
- return((unsigned) crc);
- }
-
- /* G E T P K T -- Fill a packet data field */
-
- /*
- Gets characters from the current source -- file or memory string.
- Encodes the data into the packet, filling the packet optimally.
- Set first = 1 when calling for the first time on a given input stream
- (string or file).
-
- Uses global variables:
- t -- current character.
- first -- flag: 1 to start up, 0 for input in progress, -1 for EOF.
- next -- next character.
- data -- pointer to the packet data buffer.
- size -- number of characters in the data buffer.
- memstr - flag that input is coming from a memory string instead of a file.
- memptr - pointer to string in memory.
- (*sx)() character set translation function
-
- Returns the size as value of the function, and also sets global "size",
- and fills (and null-terminates) the global data array. Returns 0 upon eof.
-
- Rewritten by Paul W. Placeway (PWP) of Ohio State University, March 1989.
- Incorporates old getchx() and encode() inline to eliminate function calls,
- uses buffered input for much-improved efficiency, and clears up some
- confusion with line termination (CRLF vs LF vs CR).
- */
-
- /*
- * Below defines for using the C Kermit 5A getpkt() routine in this module.
- */
- #define NLCHAR '\n'
- #define LF '\n'
- #define SP ' '
- #define DEL 0177
- #define debug(a,b,c,d)
- #define binary (!text)
- #define cxseen cx
- #define fmask 0377
- #define myctlq sctlq
- #define NOCSETS
-
- static CHAR t, next;
- static int rptn;
-
- getpkt(bufmax) int bufmax; { /* Fill one packet buffer */
- register CHAR rt = t, rnext = next; /* register shadows of the globals */
- register CHAR *dp, *odp, *p1, *p2; /* pointers... */
- register int x; /* Loop index. */
- register int a7; /* Low 7 bits of character */
- static CHAR leftover[7] = { '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
-
- if (first == 1) { /* If first time thru... */
- ffc = 0;
- first = 0; /* remember, */
- *leftover = '\0'; /* discard any interrupted leftovers, */
- /* get first character of file into t, watching out for null file */
- if (memstr) {
- if ((rt = *memptr++) == '\0') { /* end of string ==> EOF */
- first = -1;
- size = 0;
- debug(F100,"getpkt: empty string","",0);
- return (0);
- }
- } else {
- if ((x = zminchar()) < 0) { /* End of file or input error */
- first = -1;
- size = 0;
- if (x == -2) { /* Error */
- debug(F100,"getpkt: input error","",0);
- cxseen = 1;
- } else debug(F100,"getpkt: empty file","",0);
- return(0);
- }
- ffc++; /* Count a file character */
- rt = x;
- debug(F101,"getpkt zminchar","",rt);
- }
-
- #ifndef NOCSETS
- debug(F101,"getpkt about to call translate function","",rt);
- if (!binary && sx) rt = (*sx)(rt); /* Translate */
- debug(F101," translate function returns","",rt);
- #endif /* NOCSETS */
-
- rt &= fmask; /* bytesize mask */
-
- /* PWP: handling of NLCHAR is done later (in the while loop)... */
-
- } else if ((first == -1) && (*leftover == '\0')) /* EOF from last time? */
- return(size = 0);
-
- /* Do any leftovers */
-
- dp = data;
- for (p1 = leftover; (*dp = *p1) != '\0'; p1++, dp++) /* copy leftovers */
- ;
- *leftover = '\0';
- if (first == -1)
- return(size = (dp - data)); /* Handle final leftovers */
-
- /* Now fill up the rest of the packet. */
- rpt = 0; /* Clear out any old repeat count. */
- while (first > -1) { /* Until EOF... */
- if (memstr) { /* get next character */
- if ((rnext = *memptr++) == '\0') { /* end of string ==> EOF */
- first = -1; /* Flag eof for next time. */
- } else {
- #ifndef NOCSETS
- if (!binary && sx) rnext = (*sx)(rnext); /* Translate */
- #endif /* NOCSETS */
- ffc++;
- rnext &= fmask; /* Bytesize mask. */
- }
- } else {
- if ((x = zminchar()) < 0) { /* End of file or error */
- first = -1; /* Flag eof for next time. */
- if (x == -2) cxseen = 1; /* If error, cancel this file */
- } else {
- #ifndef NOCSETS
- if (!binary && sx) x = (*sx)(x); /* Translate */
- #endif /* NOCSETS */
- rnext = x & fmask; /* Bytesize mask. */
- ffc++; /* Count it */
- }
- }
-
- /* PWP: handling of NLCHAR is done in a bit... */
-
- odp = dp; /* Remember current position. */
-
- /* PWP: the encode() procedure, in-line (for speed) */
- if (rptflg) { /* Repeat processing? */
- if (
- #ifdef NLCHAR
- /*
- * PWP: this is a bit esoteric, so bear with me.
- * If the next char is really CRLF, then we cannot
- * be doing a repeat (unless CR,CR,LF which becomes
- * "~ <n-1> CR CR LF", which is OK but not most efficient).
- * I just plain don't worry about this case. The actual
- * conversion from NL to CRLF is done after the rptflg if...
- */
- (binary || (rnext != NLCHAR)) &&
- #endif /* NLCHAR */
- rt == rnext && (first == 0)) { /* Got a run... */
- if (++rpt < 94) { /* Below max, just count */
- continue; /* go back and get another */
- }
- else if (rpt == 94) { /* Reached max, must dump */
- *dp++ = rptq;
- *dp++ = tochar(rpt);
- rptn += rpt;
- rpt = 0;
- }
- } else if (rpt == 1) { /* Run broken, only 2? */
- /*
- * PWP: Must encode two characters. This is handled
- * later, with a bit of blue smoke and mirrors, after
- * the first character is encoded.
- */
- } else if (rpt > 1) { /* More than two */
- *dp++ = rptq; /* Insert the repeat prefix */
- *dp++ = tochar(++rpt); /* and count. */
- rptn += rpt;
- rpt = 0; /* Reset repeat counter. */
- }
- }
-
- #ifdef NLCHAR
- /*
- * PWP: even more esoteric NLCHAR handling. Remember, at
- * this point t may still be the _system_ NLCHAR. If so,
- * we do it here.
- */
- if (!binary && (rt == NLCHAR)) {
- *dp++ = myctlq; /* just put in the encoding directly */
- *dp++ = 'M'; /* == ctrl(CR) */
- if ((dp-data) <= bufmax) odp = dp; /* check packet bounds */
- rt = LF;
- }
- #endif
-
- /* meta control stuff fixed by fdc */
- a7 = rt & 0177; /* Low 7 bits of character */
- if (ebqflg && (rt & 0200)) { /* Do 8th bit prefix if necessary. */
- *dp++ = ebq;
- rt = a7;
- }
- if ((a7 < SP) || (a7 == DEL)) { /* Do control prefix if necessary */
- *dp++ = myctlq;
- rt = ctrl(rt);
- }
- if (a7 == myctlq) /* Prefix the control prefix */
- *dp++ = myctlq;
-
- if ((rptflg) && (a7 == rptq)) /* If it's the repeat prefix, */
- *dp++ = myctlq; /* quote it if doing repeat counts. */
-
- if ((ebqflg) && (a7 == ebq)) /* Prefix the 8th bit prefix */
- *dp++ = myctlq; /* if doing 8th-bit prefixes */
-
- *dp++ = rt; /* Finally, insert the character */
-
- if (rpt == 1) { /* Exactly two copies? */
- rpt = 0;
- p2 = dp; /* save current size temporarily */
- for (p1 = odp; p1 < p2; p1++) /* copy the old chars over again */
- *dp++ = *p1;
- if ((p2-data) <= bufmax) odp = p2; /* check packet bounds */
- }
- rt = rnext; /* Next is now current. */
- if ((dp-data) >= bufmax) { /* If too big, save some for next. */
- size = (dp-data);
- *dp = '\0'; /* mark (current) the end. */
- if ((dp-data) > bufmax) { /* if packet is overfull */
- for (p1 = leftover, p2=odp; (*p1 = *p2) != '\0'; p1++,p2++)
- ;
- debug(F111,"getpkt leftover",leftover,size);
- debug(F101," osize","",(odp-data));
- size = (odp-data); /* Return truncated packet. */
- *odp = '\0'; /* mark real end */
- } else { /* If the packet is exactly full, */
- debug(F101,"getpkt exact fit","",size);
- }
- t = rt; next = rnext; /* save for next time */
- return(size);
- }
- } /* Otherwise, keep filling. */
- size = (dp-data);
- *dp = '\0'; /* mark (current) the end. */
- debug(F111,"getpkt eof/eot",data,size); /* Fell thru before packet full, */
- return(size); /* return partially filled last packet. */
- }
-
- /*
- * Decodes the data pointed to by the global pointer rdatap.
- */
- static
- decode() {
- unsigned a, a7, b8;
-
- while ((a = *rdatap++) != '\0') {
- rpt = 1; /* Initialize repeat count. */
- if (rptflg) { /* Repeat processing? */
- if (a == rptq) { /* Yes, have repat prefix? */
- rpt = unchar(*rdatap++); /* Yes, get count. */
- a = *rdatap++; /* and following character. */
- }
- }
- b8 = 0; /* Assume 8th bit not on. */
- if (ebqflg) { /* Doing 8th-bit prefixing? */
- if (a == ebq) { /* Yes, have 8th-bit prefix? */
- b8 = 128; /* Yes, remember bit 8 on */
- a = *rdatap++; /* and get following character. */
- }
- }
- if (a == rctlq) { /* Is it control prefix? */
- a = *rdatap++; /* Yes, get next character */
- a7 = a & 127; /* and its low 7 bits. */
- if (a7 > 62 && a7 < 96) /* Encoded control character? */
- a = ctrl(a); /* Yes, controllify */
- }
- a |= b8; /* OR in the 8th bit. */
- if (text)
- if (a == '\r') continue; /* Discard carriage returns. */
- for (; rpt > 0; rpt--) {
- if (osp)
- *osp++ = a;
- else if (xflag) /* If flagged... */
- tchar(a); /* ... output to terminal */
- else {
- if (zmchout(a) < 0) return -1; /* Output the character. */
- ffc++;
- }
- }
- }
- tflush(); /* Flush out message (if xflag) */
- return(0);
- }
-
- static
- encstr(s) char *s; { /* Fill a packet from the string */
- first = 1; /* Start lookahead. */
- memptr = (CHAR *) s; /* Set input string pointer */
- memstr = 1;
- (void) getpkt(spsiz); /* Fill a packet */
- memstr = 0; /* Reset input string pointer */
- return(size); /* Return data field length */
- }
-
- static
- decstr(s) char *s; { /* Decode packet data into a string */
- osp = s; /* Set output string pointer */
- (void) decode(); /* Decode the string */
- *osp = '\0'; /* Terminate the string */
- osp = NULL; /* Reset output string pointer */
- }
-
- static
- spar(s) CHAR *s; { /* Set parameters */
- int x;
-
- s--; /* Line up with field numbers. */
-
- /* Limit on size of outbound packets */
- x = (rln >= 1) ? unchar(s[1]) : 80;
- spsiz = (x < 10) ? 80 : x;
-
- /* Timeout on inbound packets */
- x = (rln >= 2) ? unchar(s[2]) : 5;
- stimo = (x < 0) ? 5 : x;
-
-
- /* Outbound padding */
- spadn = 0; spadc = '\0';
- if (rln >= 3) {
- spadn = unchar(s[3]);
- if (rln >= 4)
- spadc = ctrl(s[4]);
- }
-
- /* Outbound packet terminator */
- seol = (rln >= 5) ? unchar(s[5]) : '\r';
- if (seol < 2 || seol > 31) seol = '\r';
-
- /* Control prefix */
- x = (rln >= 6) ? s[6] : '#';
- rctlq = ((x > 32 && x < 63) || (x > 95 && x < 127)) ? x : '#';
-
- /* 8th-bit quoting */
- rq = (rln >= 7) ? s[7] : 0;
- if (rq == 'Y') rqf = 1;
- else if ((rq > 32 && rq < 63) || (rq > 95 && rq < 127)) rqf = 2;
- else rqf = 0;
-
- switch (rqf) {
- case 0: ebqflg = 0; break;
- case 1: if (parity) { ebqflg = 1; ebq = '&'; } break;
- case 2: if (ebqflg = (ebq == sq || sq == 'Y')) ebq = rq;
- }
-
- /* Block check */
- x = 1;
- if (rln >= 8) {
- x = s[8] - '0';
- if (x < 1 || x > 3) x = 1;
- }
- bctr = x;
-
- /* Repeat prefix */
- if (rln >= 9) {
- rptq = s[9];
- rptflg = ((rptq > 32 && rptq < 63) || (rptq > 95 && rptq < 127));
- } else rptflg = 0;
-
- /* Capabilities */
- atcapu = lpcapu = swcapu = 0; /* No capabilities by default */
- if (rln >= 10) {
- x = unchar(s[10]);
- atcapu = (x & atcapb) && atcapr; /* Attribute packets */
- lpcapu = (x & lpcapb) && lpcapr; /* Long packets */
- swcapu = (x & swcapb) && swcapr; /* Sliding windows */
- for (capas = 10; (unchar(s[capas]) & 1) && (rln >= capas); capas++)
- ; /* Skip to capas + 1 */
- }
-
- /* Long packets */
- if (lpcapu) { /* Flag set above */
- if (rln > capas+2) {
- x = unchar(s[capas+2]) * 95 + unchar(s[capas+3]);
- spsiz = x > MAXSP ? MAXSP : x;
- }
- if (spsiz < 10) spsiz = 80; /* Be defensive... */
- }
-
- /* Sliding windows */
- if (swcapu) {
- if (rln > capas+1) {
- x = unchar(s[capas+1]);
- wsize = x > MAXWS ? MAXWS : x;
- } else
- wsize = 1;
- }
- }
-
- /* Fill the array with my send-init parameters */
-
- static char *
- rpar() {
- data[1] = tochar(DRPSIZ); /* Biggest packet I can receive */
- data[2] = tochar(rtimo); /* When I want to be timed out */
- data[3] = tochar(rpadn); /* How much padding I need */
- data[4] = ctrl(rpadc); /* Padding character I want */
- data[5] = tochar(reol); /* End-of-Line character I want */
- data[6] = sctlq; /* Control-Quote character I send */
- switch(rqf) { /* 8th-bit prefix */
- case -1:
- case 1: if (parity) ebq = sq = '&'; break;
- case 0:
- case 2: break;
- }
- data[7] = sq;
- data[8] = bctr + '0'; /* Block Check Type */
- if (rptflg) data[9] = rptq; else data[9] = '~';
- data[10] = tochar(atcapr?atcapb:0 | lpcapr?lpcapb:0 | swcapr?swcapb:0);
- capas = 10;
- data[capas+1] = tochar(swcapr ? wsize : 0); /* Window size */
- data[capas+2] = tochar(rpsiz / 95); /* Long packet size */
- data[capas+3] = tochar(rpsiz % 95); /* ... */
- data[capas+4] = '\0';
- return((char *) (data+1)); /* Return a pointer to the string */
- }
-
- /*
- * proto()--Kermit protocol entry point. Set your start state and call
- * this, NOT wart(). Modified to set long packets capability on the
- * basis of the packet size set in the external user interface.
- */
- void
- proto()
- {
- #ifdef XPRKERMIT
- struct XPR_UPDATE xpru;
-
- xpru.xpru_protocol = "XPR-Kermit";
- xpru.xpru_updatemask = XPRU_PROTOCOL;
- calla(xupdate, &xpru);
- zclear();
- #endif
- sndpkt = malloc(MAXSP+100);
- rcvpkt = malloc(MAXRP+200);
- data = malloc(MAXSP+50);
- zinbuffer = malloc(INBUFSIZE);
- zoutbuffer = malloc(OBUFSIZE);
- if (urpsiz > 94) { /* Long packets? */
- rpsiz = (urpsiz > MAXRP - 20 ? MAXRP - 20 : urpsiz);
- lpcapr = 1; /* Request long packets */
- } else { /* No long packets */
- lpcapr = 0;
- if (urpsiz < 10) /* Too small? */
- rpsiz = 80;
- else
- rpsiz = DRPSIZ;
- }
- urpsiz = rpsiz;
- cx = cz = 0; /* Haven't aborted yet */
- if (bctr < 1)
- bctr = 1; /* Legal block check? */
- if (bctr > 3)
- bctr = 3;
- if (sndpkt == NULL || rcvpkt == NULL || data == NULL || zinbuffer == NULL ||
- zoutbuffer == NULL)
- #ifdef XPRKERMIT
- ST_Display_String(STMsg,"Can't allocate memory");
- #else
- tmsgl("Can't allocate memory for Kermit!!");
- #endif
- else
- wart();
- if (sndpkt) { free(sndpkt); sndpkt = NULL; }
- if (rcvpkt) { free(rcvpkt); rcvpkt = NULL; }
- if (data) { free(data); data = NULL; }
- if (zinbuffer) { free(zinbuffer); zinbuffer = NULL; }
- if (zoutbuffer) { free(zoutbuffer); zoutbuffer = NULL; }
- }
-
- /*
- * That ends the system-independent Kermit modules. What follows
- * are the system-dependent ones.
- */
-
- /*
- * File I/O modules for XPR Kermit.
- */
- #define EOF -1
- typedef struct FILE FILE;
-
- static FILE *ifp = NULL, *ofp = NULL;
-
- static
- zclear()
- {
- ifp = NULL; ofp = NULL;
- }
-
- static
- zopeni(name) char *name; {
-
- zincnt = 0;
- ifp = (FILE *) callaa(xfopen, name, "r");
- if (ifp == NULL)
- return -1;
- else
- return 0;
- }
-
- #include <dos/dos.h>
- #include <clib/dos_protos.h>
- #include <pragmas/dos_pragmas.h>
-
- static
- zopeno(name) char *name; {
-
- zoutcnt = 0;
- zoutptr = zoutbuffer;
-
- ofp = (FILE *) callaa(xfopen, name, "w");
- if (ofp == NULL)
- return -1;
- else
- return 0;
- }
-
- static
- zclosi() {
- int x;
-
- if (ifp == NULL)
- return 0;
- x = calla(xfclose, ifp);
- ifp = NULL;
- if (x < 0)
- return -1;
- else
- return 0;
- }
-
- static
- zcloso(discard) int discard; {
- int x;
-
- if (ofp == NULL)
- return 0;
- if (zoutcnt > 0)
- if (zoutdump() != 0)
- return -1;
- x = calla(xfclose, ofp);
- ofp = NULL;
- if (x < 0)
- return -1;
- else if (discard) {
- if (xunlink != NULL) {
- if (calla(xunlink, myname) < 0) {
- return -1;
- }
- }
- return 0;
- }
- return 0;
- }
-
- #include <ctype.h>
-
- extern int convert; /* 0 for literal files, 1 for translate */
-
- /* Z R T O L -- Convert remote filename into local form */
-
- /* For AMIGA, this means changing uppercase letters to lowercase. */
-
- zrtol(name,name2,mywarn) char *name, *name2; int mywarn; {
- if (convert) {
- for ( ; *name != '\0'; name++) {
- *name2++ = isupper(*name) ? tolower(*name) : *name;
- }
- *name2 = '\0';
- } else
- strcpy(name2,name);
- }
-
- /* name from local to remote format */
-
- static
- zltor(s1,s2) char *s1, *s2; {
- char *EndPath();
- char dotseen = 0;
-
- if (!convert)
- strcpy(s2, s1);
- else {
- s1 = EndPath(s1); /* strip dir/disk */
- while (*s1 != '\0') {
- if (islower(*s1))
- *s2 = toupper(*s1);
- else if (isalnum(*s1))
- *s2 = *s1;
- else if (*s1 == '.')
- if (!dotseen) {
- dotseen = 1;
- *s2 = *s1;
- } else
- *s2 = 'X';
- /* else a character we're not prepared to handle. */
- s1++; s2++;
- }
- *s2 = '\0';
- }
- }
-
- /*
- * System-dependent function to do buffered input and output.
- * Text conversion now done in the packet routines.
- */
- static
- zinfill(void) {
- zincnt = calladda(xfread, zinbuffer, sizeof (char), INBUFSIZE, ifp);
- if (zincnt == 0) return (-1); /* end of file */
- zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
- zincnt--; /* one less char in buffer */
- return((int)(*zinptr++) & 0377); /* because we return the first */
- }
-
- static
- zoutdump(void) {
- zoutptr = zoutbuffer; /* reset buffer pointer in all cases */
- if (zoutcnt == 0) { /* nothing to output */
- return(0);
- } else if (zoutcnt < 0) { /* unexpected negative value */
- zoutcnt = 0; /* reset output buffer count */
- return(-1); /* and fail. */
- }
- if (calladda(xfwrite, zoutbuffer, sizeof (char), zoutcnt, ofp)) {
- zoutcnt = 0; /* reset output buffer count */
- return(0); /* things worked OK */
- } else {
- zoutcnt = 0; /* reset output buffer count */
- return(-1); /* error return */
- }
- }
-
- int ttol(s, n)
- char *s;
- int n;
- {
- long status;
-
- status = callad(xswrite, s, (long) n);
- if (status == 0) return(n);
- else return(-1);
- }
-
- #include "timer.h"
- #include <clib/exec_protos.h>
- #include <pragmas/exec_pragmas.h>
-
- #ifdef MYREAD
- /* Private buffer for myread() and its companions. Not for use by anything
- * else. ttflui() is allowed to reset them to initial values. ttchk() is
- * allowed to read my_count.
- *
- * my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
- *
- * A global parity mask variable could be useful too. We could use it to
- * let myread() strip the parity on its own, instead of stripping sign
- * bits as it does now.
- */
-
- #define MYBUFLEN 256
- static CHAR mybuf[MYBUFLEN]; /* Buffer, including push back */
- static int my_count = 0; /* Number of chars still in mybuf */
- static int my_item = -1; /* Last index read from mybuf[] */
-
- /* myread() -- Efficient read of one character from communications line.
- *
- * Uses a private buffer to minimize the number of expensive read() system
- * calls. Essentially performs the equivalent of read() of 1 character, which
- * is then returned. By reading all available input from the system buffers
- * to the private buffer in one chunk, and then working from this buffer, the
- * number of system calls is reduced in any case where more than one character
- * arrives during the processing of the previous chunk, for instance high
- * baud rates or network type connections where input arrives in packets.
- * If the time needed for a read() system call approaches the time for more
- * than one character to arrive, then this mechanism automatically compensates
- * for that by performing bigger read()s less frequently. If the system load
- * is high, the same mechanism compensates for that too.
- *
- * myread() is a macro that returns the next character from the buffer. If the
- * buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
- * returns.
- *
- * This should be efficient enough for any one-character-at-a-time loops.
- * For even better efficiency you might use memcpy()/bcopy() or such between
- * buffers (since they are often better optimized for copying), but it may not
- * be worth it if you have to take an extra pass over the buffer to strip
- * parity and check for CTRL-C anyway.
- *
- * Note that if you have been using myread() from another program module, you
- * may have some trouble accessing this macro version and the private variables
- * it uses. In that case, just add a function in this module, that invokes the
- * macro.
- */
- #define myread(timo) (--my_count < 0 ? mygetbuf(timo) : 255 & (int)mybuf[++my_item])
-
- /*
- * System-dependent mygetbuf() routine. Fills buffer with as many characters
- * as it can get, provided it can get the next one within timo seconds.
- * Returns the number of characters read, or 0 if none were read within
- * timo seconds, or a negative value if some other error occured.
- */
- static int
- myfillbuf(int timo) {
- long n;
-
- /*
- * Find out how many characters are available, if any.
- * We can only do this if xsquery is non-NULL.
- */
- if (xsquery != NULL && (n = (*xsquery)()) > 0) {
- calladd(xsread, mybuf, n, 0L);
- return n;
- } else
- /*
- * xsread() returns exactly what we want: zero if no
- * character is received within timo seconds, and -1
- * on error. Might as well ask for a bufferful,
- * provided first one arrives soon enough.
- */
- return(calladd(xsread, &mybuf[0], (long) MYBUFLEN ,
- timo*1000000L));
- }
-
- /* mygetbuf(timo) -- Fill buffer for myread() and return first character,
- * within timo seconds.
- *
- * This function is what myread() uses when it can't get the next character
- * directly from its buffer. First, it calls a system dependent myfillbuf()
- * to read at least one new character into the buffer, and then it returns
- * the first character just as myread() would have done. This function also
- * is responsible for all error conditions that myread() can indicate.
- *
- * Returns: When OK => a positive character, 0 or greater.
- * When TIMEOUT=> -1
- * When error => -2.
- *
- */
- static int
- mygetbuf(int timo) {
- my_count = myfillbuf(timo);
- if (my_count <= 0)
- return(my_count - 1);
- --my_count;
- return(255 & (int)mybuf[my_item = 0]);
- }
- #endif /*MYREAD*/
-
- /*
- * T T I N L
- *
- * Return a line in the buffer pointed to by s from the serial line.
- * The line must end with eol and be no longer than max characters.
- * timeout is a timeout interval in seconds.
- *
- * Returns
- * > 0 Success--number of characters read
- * = 0 Shouldn't happen!
- * -1 Timeout
- * -2 Error of some other kind
- *
- * This version for XPR Kermit. We ignore negative returns from
- * the xpr_sread() function, preferring to take care of these
- * via the timeout mechanism.
- */
- int ttinl(s, max, eol, timeout)
- char *s;
- int max, timeout, eol;
- {
- int x = 0;
- long i;
- struct timerequest *Timereq;
- unsigned mask;
-
- *s = '\0';
- mask = (parity ? 0177 : 0377);
- /*
- * Set up and post timer request for overall timeout.
- * As a compromise between correct and efficient, I only check
- * the overall timeout if a one-character read with a one second
- * timeout fails first.
- */
- if (!(Timereq = CreateTimer(UNIT_VBLANK)))
- return -1;
- Timereq->tr_time.tv_secs = timeout;
- Timereq->tr_time.tv_micro = 0;
- Timereq->tr_node.io_Command = TR_ADDREQUEST;
- SendIO((struct IORequest *) Timereq);
- #ifndef MYREAD
- /*
- * Willy Langeveld assures me that, since serial.device does its
- * own buffering, calling xsread once for each character isn't
- * going to take too long.
- *
- */
- for (x = 0; x < max; ) {
- if ((i = calladd(xsread, s, 1L, 1000000L)) < 0) {
- if (chkint() < 0) {
- x = -2;
- goto leave;
- }
- } else if (i == 1) {
- x++;
- if ((*s++ &= mask) == eol)
- break;
- } else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
- x = -1; /* Flag timeout abort */
- goto leave;
- }
- }
- *s = '\0'; /* Normal termination */
- leave: /* Common cleanup code */
- AbortIO((struct IORequest *) Timereq);
- WaitIO((struct IORequest *) Timereq);
- DeleteTimer(Timereq);
- return(x);
- #else
- /*
- * The following call to myread sets up a 1 second per-character
- * timeout. We have to be careful not to return from ttinl until
- * the entire timeout interval has passed, however.
- */
-
- for (x = 0; x < max; ) {
- if ((*s = myread(1)) < -1) {
- /*
- * The following code deals with the case that
- * xsread() returns a negative value if the
- * user has clicked on the "Cancel File" or
- * "Cancel Batch" gadgets. However, we don't
- * want to abort receiving the current packet
- * in this case; calling chkint() now sets
- * the global cx and/or cz flags, but in the
- * case of a complete abort, chkint() returns
- * a negative value.
- */
- if (chkint() < 0) {
- x = -2; /* Error */
- goto leave;
- }
- } else if (CheckIO((struct IORequest *) Timereq)) { /* timeout */
- x = -1; /* Flag timeout abort */
- goto leave;
- } else if (*s >= 0) { /* Successful get of character */
- x++;
- if ((*s++ &= mask) == eol)
- break;
- }
-
- }
- *s = '\0'; /* Normal termination */
- leave: /* Common cleanup code */
- AbortIO((struct IORequest *) Timereq);
- WaitIO((struct IORequest *) Timereq);
- DeleteTimer(Timereq);
- return(x);
- #endif /*MYREAD*/
- }
-
- /*
- * This is the XPR Kermit version of chkint. We allow multiple levels
- * of interrupt, if available.
- */
- int chkint(void) /* Check for console interrupts. */
- {
- int x;
-
- x = (*xchkabort)();
- switch (x) {
- case 0: /* No abort pending */
- return 0;
- case 1: /* Abort file only. */
- cx = 1;
- break;
- case 2: /* Abort file and batch. */
- cx = cz = 1;
- break;
- case -1: /* Emergency escape -- kill all */
- cx = cz = 1;
- return -1;
- default:
- ;
- }
- return 1;
- }
-
- void ttflui()
- {
- (void) (*xsflush)();
- #ifdef MYREAD
- my_count = 0;
- my_item = -1;
- #endif
- }
-
- void sleep(sec)
- int sec;
- {
- struct timeval tv;
-
- if (sec) {
- tv.tv_secs = sec;
- tv.tv_micro = 0;
- MyDelay(&tv, UNIT_VBLANK);
- }
- }
-
- #define MAXMESSAGE 50
- static char message[MAXMESSAGE+1];
- static int nmess = 0;
- static long errors = 0, blocks = 0;
-
- /* screen(f,c,n,s)
- f - argument descriptor
- c - a character or small integer
- n - a long integer
- s - a string.
- Fill in this routine with the appropriate display update for the system.
- This version for XPR Kermit.
- */
-
- screen(f,c,n,s) int f; long n; char c; char *s; {
- struct XPR_UPDATE xpru;
-
- if (!local || xflag) return;
- switch (f) {
- case SCR_PT: /* Packet Transferred. */
- if (c == 'Y') return; /* Don't bother with ACK's. */
- xpru.xpru_packettype = c;
- xpru.xpru_blocksize = strlen(s);
- xpru.xpru_blocks = ++blocks;
- if (c == 'D' || c == 'Z') { /* If data or EOF packet ... */
- xpru.xpru_bytes = ffc; /* ... update transfer count */
- xpru.xpru_updatemask = XPRU_BYTES;
- } else if (c == 'T') { /* Timeout */
- xpru.xpru_timeouts = timeouts++;
- xpru.xpru_updatemask = XPRU_TIMEOUTS;
- } else
- xpru.xpru_updatemask = 0;
- xpru.xpru_updatemask |= XPRU_PACKETTYPE | XPRU_BLOCKSIZE |
- XPRU_BLOCKS;
- break;
- case SCR_EM: /* Error message. */
- xpru.xpru_errormsg = s;
- xpru.xpru_updatemask = XPRU_ERRORMSG;
- break;
- default:
- return;
- }
- calla(xupdate, &xpru);
- }
-
- void tchar(c)
- char c;
- {
- struct XPR_UPDATE xpru;
-
- if (c == '%') { /* retry */
- xpru.xpru_errors = ++errors;
- xpru.xpru_updatemask = XPRU_ERRORS;
- } else if (c == '#') { /* fake it */
- xpru.xpru_errors = errors = 0;
- xpru.xpru_blocks = blocks = 0;
- nmess = 0; /* Make sure message empty. */
- xpru.xpru_updatemask = XPRU_BLOCKS | XPRU_ERRORS;
- } else {
- message[nmess++] = c;
- if (nmess >= MAXMESSAGE)
- tflush();
- return;
- }
- calla(xupdate, &xpru);
- }
-
- static
- tflush()
- {
- struct XPR_UPDATE xpru;
-
- if (nmess <= 0)
- return;
- message[nmess] = '\0';
- xpru.xpru_errormsg = message;
- xpru.xpru_updatemask = XPRU_ERRORMSG;
- calla(xupdate, &xpru);
- nmess = 0;
- }
-
- ST_Display_String(Item, S)
- int Item;
- char *S;
- {
- struct XPR_UPDATE xpru;
-
- switch (Item) {
- case STMsg:
- xpru.xpru_msg = S;
- xpru.xpru_updatemask = XPRU_MSG; break;
- case STFile:
- xpru.xpru_filename = S;
- xpru.xpru_updatemask = XPRU_FILENAME; break;
- case STUplSize:
- xpru.xpru_filesize = callad(xfinfo, S, 1L);
- xpru.xpru_updatemask = XPRU_FILESIZE; break;
- default:
- return;
- }
- calla(xupdate, &xpru);
- }
-
- ST_Display_CRC(Item)
- int Item;
- {
- struct XPR_UPDATE xpru;
-
- switch (Item) {
- case 1:
- xpru.xpru_blockcheck = "Check-6"; break;
- case 2:
- xpru.xpru_blockcheck = "Check-12"; break;
- case 3:
- xpru.xpru_blockcheck = "CRC-16"; break;
- default:
- return;
- }
- xpru.xpru_updatemask = XPRU_BLOCKCHECK;
-
- calla(xupdate, &xpru);
- }
-
- extern char *p_pattern;
-
- #ifndef TRUE
- #define TRUE 1
- #define FALSE 0
- #endif
-
- static int firsttime = TRUE;
-
- int gnfile(fname, len)
- char *fname;
- int len; /* it is 50 */
- {
- char buffer[256];
- static long gnstate;
-
- if (firsttime) {
- gnstate = callaa(xffirst, buffer, p_pattern);
- if (gnstate) firsttime = FALSE;
- } else {
- gnstate = calldaa(xfnext, gnstate, buffer, p_pattern);
- if (gnstate == 0L) firsttime = TRUE;
- }
- if (gnstate && (int)strlen(buffer) < len) {
- strcpy(fname, buffer);
- return((int)gnstate);
- } else return(0);
- }
-
- void gninit(void) {
- firsttime = TRUE;
- }
-
- char *EndPath(p)
- char *p;
- {
- char *q;
-
- q = p;
- while (*p != '\0') {
- if ((*p == '/') || (*p == ':'))
- q = p + 1;
- p++;
- }
- return(q);
- }
-