/* :ts=8 */ /* WARNING -- This C source program generated by Wart preprocessor. */ /* Do not edit this file; edit the Wart-format source file instead, */ /* and then run it through Wart to produce a new C source file. */ /* Wart Version Info: */ char *wartv = "Wart Version 1A(005) Jan 1988"; /* * 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. * */ /* * Run this through the Wart preprocessor which comes with C Kermit. */ #include "kermitproto.h" static void decstr(), rinit(), error(), tinit(), spar(), zrtol(), zltor(); /* * 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, /* Block check type to request. */ limit = 5, /* Retry limit on receive */ warn = 0, /* 1 for warn before overwriting 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 */ /* * 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 */ ebq = '&', /* 8th-bit prefix */ ebqflg = 0, /* 8th-bit quoting flag */ xflag, /* Output to screen for generic server commands */ rq = 0, /* Received 8bq bid */ sq = 'Y', /* Sent 8bq bid */ rpt = 0, /* Repeat count */ rptq = '~', /* Repeat prefix */ 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 */ maxsiz, /* Maximum data size for packet */ rln, /* Length of received data field */ size, /* Current size of output packet data */ osize, /* Previous output packet data size */ 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 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 */ filnam[50], /* Current file name */ ssc, /* Start server command */ *sndpkt, /* Send packet. */ *rcvpkt, /* Receieve packet. */ *data, /* Data to send/receive before encode/decode */ *rdatap, /* Pointer to null-terminated data field */ *isp = NULL, /* Pointer to characters in memory */ *osp = NULL; /* Output string pointer */ /* * Forward declarations. Soon to be prototypes if the ANSI standard * committee keeps its promises. */ int input(), spack(), ack(); char *rpar(); /* * External routines provided. */ extern int ttinl(), ttol(), gnfile(); extern void ttflui(), tchar(), tmsg(), tmsgl(), sleep(); #define ERR(s) error(s); RESUME #define RESUME return /*lint -save -e525 -e527 We don't care how Wart formats! */ #define ssfil 1 #define ssdat 2 #define sseot 3 #define srini 4 #define srfil 5 #define srdat 6 #define sipkt 7 #define srgen 8 #define BEGIN state = int state = 0; void wart() { int c,actno; extern int tbl[]; while (1) { c = input(); if ((actno = tbl[c + state*128]) != -1) switch(actno) { case 1: { /* - 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. */ } } break; case 2: { tinit(); rinit(); BEGIN srini; } break; case 3: { /* Get */ tinit(); ssc = 0; if (sinit('I') < 0) { ERR("sinit"); } else BEGIN sipkt; } break; case 4: { /* Host */ tinit(); ssc = 'C'; if (sinit('I') < 0) { ERR("sinit"); } else BEGIN sipkt; } break; case 5: { /* Generic */ tinit(); ssc = 'G'; if (sinit('I') < 0) { ERR("sinit"); } else BEGIN sipkt; } break; case 6: { /* Immediate protocol abort */ spack('E', seq, "User aborted protocol", 21); closif(); closof(1); /* Close files, deleting output */ RESUME; } break; case 7: { /* - 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 */ /* 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. */ } } break; case 8: { /* - 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. */ } break; case 9: { RESUME; } break; case 10: { spar(rdatap); (void) ack1(rpar()); bctu = bctr; BEGIN srfil; } break; case 11: { (void) ack(); RESUME; } break; case 12: { if (rcvfil() < 0) { ERR("rcvfil"); } else { (void) ack(); BEGIN srdat; } } break; case 13: { if (cx) ack1("X"); else if (cz) ack1("Z"); else { if (decode() < 0) { ERR("decode"); } else (void) ack(); } } break; case 14: { /* Discard output file if the sender tells us so. */ if (closof(cx || cz || (rln == 1 && *rdatap == 'D')) < 0) { ERR("error closing file"); } else { (void) ack(); BEGIN srfil; } } break; case 15: { /* Got ACK for I packet */ spar(rdatap); /* Set parameters from it */ start = 'E'; /* Force entry into next state */ } break; case 16: { /* 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; } } break; case 17: { xflag = 1; decode(); RESUME; } break; case 18: { xflag = 1; ack(); BEGIN srdat; } break; case 19: { error(rdatap); (void) closif(); (void) closof(1); /* close files, discarding output */ RESUME; } break; case 20: { error("Unexpected packet type"); RESUME; } break; } } } int tbl[] = { -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 7, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 8, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 9, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 10, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 11, 20, 20, 19, 12, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 18, 20, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 13, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 14, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, -1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 16, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 15, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 10, 20, 20, 20, 20, 18, 17, 20, 20, 20, 20, 20, 20, 20, 6, 20, 4, 20, 20, 20, 5, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 3, 1, 20, 20, 2, 20, 20, 20, 20, 20, 20, 20, 20, 20, }; /*lint -restore */ /* * First, some handy macros. */ #define tochar(c) ((c) + 32) #define unchar(c) ((c) - 32) #define ctl(c) ((c) ^ 64) 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. */ } type = rpack(); /* No start state, read a packet. */ for (try = 0; rsn != seq || strchr("TQN",type); try++) { if (try > limit) { /* If too mahy tries, */ strcpy(data, "Timed out"); /* give up */ rdatap = data; /* Make up pretend 'E' packet */ return('E'); /* and return it */ } if (type == 'N' && rsn == ((seq + 1) & 63)) { /* NAK for next packet */ return('Y'); /* is ACK for current. */ } else { /* Otherwise, */ (void) resend(); /* resend previous packet. */ } type = rpack(); /* Try to read response. */ } ttflui(); /* Got a good one, clear buffer. */ return(type); /* Return its type. */ } static void nxtpkt() { seq = (seq + 1) & 63; /* Next packet number, mod 64 */ } static void 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 */ osp = NULL; /* ... */ *filnam = *sndpkt = *rcvpkt = '\0'; /* Clear string buffers */ } static void error(s) char *s; { /* Fatal error */ if (local) { /* If in local mode */ tmsg("Fatal error: "); /* Type message on console */ tmsgl(s); } else { /* Otherwise, */ (void) spack('E',seq,strlen(s),s); /* Send in error packet. */ } } static ack() { int x; /* Empty acknowledgement */ x = spack('Y',seq,0,""); /* Send the packet */ nxtpkt(); /* Increment the packet number */ return(x); } static ack1(s) char *s; { int x; /* Acknowledgement with data */ x = spack('Y',seq,strlen(s),s); /* Send the packet */ nxtpkt(); /* Increment packet number */ return(x); } static nak() { /* Negative acknowledgement */ return(spack('N',seq,0,"")); /* 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; s = rpar(); if (local == 0 && c == 'S' && server == 0) { tmsgl("Escape back to local system, give RECEIVE command..."); sleep(delay); } return(spack(c,seq,strlen(s),s)); } /* * scmd() -- send a preformatted Kermit server command string. */ static int scmd(t, s) char t, *s; { /* Send a packet of the given type */ encstr(s); /* Encode the command string */ spack(t,seq,size,data); return 0; } /* rinit() -- do whatever is needed to initialize receive. A no-op now, * but may later initialize counters and so on. */ static void 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[50]; 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, */ tmsg("Sending "); /* let user know we're */ tmsg(filnam); /* sending this file */ tmsg(" as "); /* under */ tmsgl(pktnam); /* this name */ } first = 1; /* Flag beginning of file */ maxsiz = spsiz - (bctr + 3); /* Maximum data length */ 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 ((x = getpkt(maxsiz)) == 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, */ if (local) tmsgl("OK"); /* if local, reassure user. */ nxtpkt(); return(spack('Z',seq,strlen(s),s)); /* Send Z packet */ } } /* seot -- send B packet. */ static seot() { nxtpkt(); if (local) tmsgl("Done"); return(spack('B',seq,0,"")); } static rcvfil() { /* Receive a file */ char myname[50]; 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) { tmsg("Receiving "); tmsg(filnam); tmsg(" as "); tmsgl(myname); } return 0; } } 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, *d; int n, len; { int i = 0, j, k; for (i = 0; i < spadn; i++) sndpkt[i] = spadc; /* Do requested padding */ sndpkt[i++] = smark; /* Packet mark */ k = i++; /* Remember this place */ sndpkt[i++] = tochar(n); /* Sequence number */ sndpkt[i++] = type; /* Packet type */ j = len + bctu; /* True length */ if (j > 95) { /* 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 */ j = chksum(sndpkt+k); sndpkt[i++] = tochar((j >> 6) & 077); sndpkt[i++] = tochar(j & 077); break; case 3: /* Type 3 - 16 bit CRC-CCITT */ j = chk3(sndpkt + k); sndpkt[i++] = tochar((j >> 12) & 017); sndpkt[i++] = tochar((j >> 6) & 077); sndpkt[i++] = tochar(j & 077); break; } sndpkt[i++] = seol; /* End of line */ sndpkt[i] = '\0'; /* Null string-terminator. */ sndpkl = i; /* Remember length. */ i = ttol(sndpkt,sndpkl); /* Send the packet. */ if (local && !xflag) tchar('.'); return(i); } static resend() { int x; if (*sndpkt) x = ttol(sndpkt,sndpkl); /* Send previous packet */ else x = nak(); /* or NAK if none */ if (local && !xflag) tchar('%'); /* Let the user know. */ return(x); } chk1(packet) char *packet; { /* Compute Kermit's */ int 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 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, x, type, rlnpos; char pbc[4]; /* Packet block check. */ rsn = rln = -1; /* In case of failure. */ *rcvpkt = '\0'; /* Initialize receive buffer. */ j = ttinl(rcvpkt,MAXRP,reol,stimo); /* Try to get a "line". */ if (j < 0) return('T'); /* Timed out. */ for (i = 0; rcvpkt[i] != rmark && (i < j); i++) /* Find mark. */ ; if (i == j) return('Q'); /* If no mark, bad packet. */ 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] = 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 (x = 0; x < bctu; x++) /* Copy the block check. */ pbc[x] = rcvpkt[j+x]; 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'); 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."); } 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 chk3(s) char *s; { unsigned int c, q; 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(crc); } /* * getpkt--Fill a packet to the maximum. Result goes in local data array * whose current length is indicated in global size. */ static getpkt(maxlen) int maxlen; { int i, next; static int c; static char remain[6] = {'\0', '\0', '\0', '\0', '\0', '\0'}; void encode(); if (first == 1) { /* If first time thru... */ first = 0; /* remember not to do this next time, */ remain[0] = '\0'; /* discard any old leftovers, */ c = gnchar(); /* get first character of file */ if (c < 0) { /* watching out for null file */ first = -1; return(size = 0); } } else if (first == -1) { /* EOF from last time? */ return(size = 0); } /* Copy any leftovers that didn't fit in the last packet. */ for (size = 0; (data[size] = remain[size]) != '\0'; size++) ; *remain = '\0'; /* Get, encode, and deposit the next character. */ rpt = 0; /* Initialize repeat counter. */ while (first > -1) { /* Until end of file or string... */ next = gnchar(); /* Look ahead one character */ if (next < 0) first = -1; /* If none, we're at EOF. */ osize = size; /* Remember current size. */ encode(c, next); /* Encode the character. */ c = next; /* Old next char is now current. */ if (size == maxlen) return(size); /* If just at end, done. */ if (size > maxlen) { /* Past end, must save some. */ for (i = 0; (remain[i] = data[osize+i]) != '\0'; i++) ; size = osize; data[size] = '\0'; return(size); /* Return size. */ } } return(size); /* EOF, return size. */ } static gnchar() { char c; if (isp) { /* From string in memory */ return((c = *isp++) > 0 ? c : -1); } else return(zgetc(text)); /* or from a file. */ } /* * Encodes the character a into the global data array, * and the global size is updated. * Global sctlq is the control prefix for sending data. */ static void encode(a, next) char a; int next; { int a7, b8; if (rptflg) { /* Doing run-length encoding? */ if (a == next) { /* Yes, got a run? */ if (++rpt < 94) { /* Yes count. */ return; } else if (rpt == 94) { /* If at maximum */ data[size++] = rptq; /* Emit prefix, */ data[size++] = tochar(rpt); /* and count, */ rpt = 0; /* and reset counter. */ } } else if (rpt == 1) { /* Run broken, only two? */ rpt = 0; /* Yes, do the character wice */ encode(a,-1); /* by calling self recursively. */ if (size <= maxsiz) osize = size; /* Watch for boundary */ rpt = 0; /* Call self second time. */ encode(a,-1); return; } else if (rpt > 1) { /* Run broken, more than two? */ data[size++] = rptq; /* Yes, emit prefix and count */ data[size++] = tochar(++rpt); rpt = 0; /* and reset counter. */ } } a7 = a & 127; /* Isolate low 7 bits */ b8 = a & 128; /* And "parity" bit */ if (ebqflg & b8) { /* If doing 8th-bit prefixing */ data[size++] = ebq; /* and 8th bit on, insert prefix */ a = a7; /* and clear the 8th bit. */ } if (a7 < 32 || a7 == 127) { /* If control character */ data[size++] = sctlq; /* insert control quote */ a = ctl(a); /* and make printable. */ } else if (a7 == sctlq) /* If data is control prefix, */ data[size++] = sctlq; /* prefix it. */ else if (ebqflg && a7 == ebq) /* If doing 8th-bit prefixing, */ data[size++] = sctlq; /* ditto for 8th-bit prefix. */ else if (rptflg && a7 == rptq) /* If doing run-length encoding, */ data[size++] = sctlq; /* ditto for repeat prefix. */ data[size++] = a; /* Finally, emit the character. */ data[size] = '\0'; /* Terminate string. */ } /* * Decodes the data pointed to by the global pointer rdatap. */ static decode() { int 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 = ctl(a); /* Yes, controllify */ } a |= b8; /* OR in the 8th bit. */ for (; rpt > 0; rpt--) if (pnchar(a) < 0) return -1; /* Output the character. */ } return(0); } static pnchar(c) int c; { /* Put next character. */ if (xflag) { /* To screen if desired. */ tchar(c); return(1); } else if (osp) { /* Or to string in memory... */ *osp++ = c; return(1); } else return(zputc(c,text)); /* Otherwise to file. */ } static encstr(s) char *s; { /* Fill a packet from the string */ first = 1; /* Start lookahead. */ isp = s; /* Set input string pointer */ (void) getpkt(spsiz); /* Fill a packet */ isp = NULL; /* Reset input string pointer */ return(size); /* Return data field length */ } static void 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 void 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 = ctl(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; } /* else a fatal error, but how do we terminate? */ } /* 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] = ctl(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(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() { void *malloc(); sndpkt = (char *) malloc((unsigned) (MAXSP+200)); rcvpkt = (char *) malloc((unsigned) (MAXRP+100)); data = (char *) malloc((unsigned) (MAXSP+1)); if (urpsiz > 94) { /* Long packets? */ rpsiz = (urpsiz > MAXRP - 20 ? MAXRP - 20 : urpsiz); lpcapr = 1; /* Request long packets */ bctr = 3; /* And 16 bit CRC's */ } else { /* No long packets */ lpcapr = 0; bctr = 1; /* Drop back to type 1 */ if (urpsiz < 10) /* Too small? */ rpsiz = 80; else rpsiz = DRPSIZ; } cx = cz = 0; /* Haven't aborted yet */ if (sndpkt == NULL || rcvpkt == NULL || data == NULL) tmsgl("Can't allocate memory for Kermit!!"); else wart(); if (sndpkt) free(sndpkt); if (rcvpkt) free(rcvpkt); if (data) free(data); } /* * That ends the system-independent Kermit modules. What follows * are the system-dependent ones. */ /* * Now for the file routines. I chose to use the z...() routines * written in terms of stdio. */ #include static FILE *ifp = NULL, *ofp = NULL; static zopeni(name) char *name; { ifp = fopen(name, "r"); if (ifp == NULL) return -1; else return 0; } static zopeno(name) char *name; { ofp = fopen(name, "w"); if (ofp == NULL) return -1; else return 0; } static zclosi() { int x; if (ifp == NULL) return 0; x = fclose(ifp); ifp = NULL; if (x < 0) return -1; else return 0; } static zcloso(discard) int discard; { int x; if (ofp == NULL) return 0; x = fclose(ofp); ofp = NULL; if (x < 0) return -1; else if (discard) if (unlink(filnam) < 0) return -1; return 0; } #include extern int convert; /* 0 for literal files, 1 for translate */ static void zrtol(s1,s2,warn) char *s1, *s2; int warn; { strcpy(s2,s1); /* for now */ if (convert) while (*s2 != '\0') { if (isalpha(*s2)) *s2 = tolower(*s2); s2++; } } static void zltor(s1,s2) char *s1, *s2; { char dotseen = 0; if (!convert) strcpy(s2, s1); else { 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 return next character from file. * If the text flag argument is nonzero, first convert to canonic form. */ static zgetc(text) { /* Get next char from file. */ #define MAXREC 100 /* Size of record buffer. */ static char recbuf[MAXREC + 1]; /* Big enough for MAXREC newlines */ static char *rbp; /* Buffer pointer */ static int i = 0; /* Buffer char counter */ int c; if (i == 0) { /* If the buffer is empty, */ /* read next line from file. */ for (i = 0; i < MAXREC - 1 && (c = getc(ifp)) != EOF && c != '\n'; i++) recbuf[i] = c; if (c == '\n') { /* Got newline */ if (text) { /* If in text mode */ recbuf[i++] = '\r'; /* Substitute CRLF */ } recbuf[i++] = c; } recbuf[i] = '\0'; /* Done, terminate buffer */ if (i == 0) return -1; /* If empty, indicate EOF */ rbp = recbuf; /* Remember position for next time */ } i--; /* Adjust the counter. */ return(*rbp++ & 0377); /* Return hext character */ } static zputc(c, text) int c, text; { /* Put character in file. */ unsigned int x; c &= 255; /* Undo any sign extension */ if (text && c == '\r') /* If in text mode, */ return 0; /* eliminate carriage returns. */ else { /* Otherwise, */ x = putc(c, ofp) & 255; /* output the character. */ if (c == 255) return 0; /* Special handling for all 1's */ return ((x != c) ? -1 : 0); /* Normal return code. */ } }