home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fred Fish Collection 1.5
/
ffcollection-1-5-1992-11.iso
/
ff_disks
/
300-399
/
ff330.lzh
/
Vt100
/
Src.lzh
/
Src
/
kermitproto.c
< prev
next >
Wrap
C/C++ Source or Header
|
1990-03-01
|
42KB
|
1,338 lines
/* :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 <stdio.h>
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 <ctype.h>
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. */
}
}