home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
honeywellgcosb
/
hdps8.b
next >
Wrap
Text File
|
2020-01-01
|
92KB
|
3,490 lines
/* kermit/s/manif >> Begin << */
/*
* Kermit-GCOS
*
* An implementation of the Kermit file transfer protocol for
* Honeywell GCOS systems.
*
* Author: John L. Huxtable
* University of Kansas Academic Computing Services
* Lawrence, Kansas 66045
* HLSUA Site Code: UKAN
* Date: 03/07/85
*
* This is a remote-only Kermit since GCOS cannot originate
* a connection with another system. It can use either paper-tape
* mode input or normal input for packets depending on whether
* or not all ASCII printing characters can be received in normal
* mode. (The default is +TapeMode). It can transfer text files
* and with the BYtestream and BItsream format specifications
* any file on GCOS. Instead of wildcard send Kermit-GCOS contains
* an indeX file option for specifying a file which contains a list
* of file names and options for sending. Kermit-GCOS has 8th-bit
* prefixing and repeat count prefixing. It does not have alternate
* checksums. It can act as a server with the following remote
* commands: Bye, CWD, Erase, Finish, Get, Logout, Send, Status
* and Type. Remote Directory and Remote Space are optionally
* available, as determined by the values of flags wat_lc and
* wat_smcl within the EXTRN definitions, below. Commands can
* be entered on the command line used to invoke Kermit-GCOS or
* through an interactive prompt mode. A HELP command provides
* interactive users with limited support information. See the
* user documentation for more information.
*
* For information about obtaining Kermit programs for other
* systems, contact:
*
* KERMIT Distribution
* Columbia University Center for Computing Activities
* 7th Floor, Watson Laboratory
* 612 West 115th Street
* New York, N.Y. 10025
*/
/*
* MANIF -- Manifests for Kermit.
*/
%b/manif/.bset
%b/manif/t.drls
/*
* .Bset Options.
*/
OP.FILE = 0;
OP.QUERY = 1;
OP.DONE = 2;
OP.EXIT = 3;
OP.QUIT = 4;
OP.HELP = 5;
OP.RECV = 6;
OP.SEND = 7;
OP.SERV = 8;
OP.SET = 9;
OP.STAT = 10;
OP.INDEX = 11;
OP.FORMAT = 12;
OP.CHKSUM = 13;
OP.DEBUG = 14;
OP.DELAY = 15;
OP.RANDOM = 16;
OP.DISCARD = 17;
OP.RETRY = 18;
OP.PERM = 19;
OP.TAPMOD = 20;
OP.XON = 21;
OP.OVWRITE = 22;
OP.SEOL = 23;
OP.SPKST = 24;
OP.CWD = 25;
/*
* Parameters which may need to be changed for your machine:
* MAXPACK, BRKCHR, MY...
*/
STDIN = 0;
STDOUT = 1;
EOF = -1;
ERR = -1;
TRUE = 1;
FALSE = 0;
_SIZE = /4;
MAX_BRKS = 10;
DBG_OFF = 0;
DBG_STATES = 1;
DBG_PACKETS = 2;
DBG_LOGFILE = 4;
DBG_ALL = -1;
FM_TEXT = 0;
FM_ASA9 = 1;
FM_BITS = 2;
FL_SIZE = 5;
FL_NAME = 0;
FL_MODE = 1;
FL_RAND = 2;
FL_DISC = 3;
FL_PERM = 4;
FL_OVWR = 5;
PAR_CMD = 0;
PAR_FIL = 1;
PAR_IDX = 2;
PAR_RMT = 3;
/*
* Stuff what comes over comm-lines.
*/
NULL = 0; /* ASCII NULL */
SOH = 1; /* Start of header */
LF = 10; /* ASCII Line Feed */
CR = 13; /* ASCII Carriage Return */
DC1 = 17; /* ASCII DC1 (XON) */
DC3 = 19; /* ASCII DC3 (XOFF) */
DEL = 127; /* Delete (rubout) */
/*
* Offsets in the Send-init packet.
*/
SI_MAXPACK = 0;
SI_MYTIME = 1;
SI_MYPAD = 2;
SI_MYPCHAR = 3;
SI_MYEOL = 4;
SI_MYQUOTE = 5;
SI_MYBQUOTE = 6;
SI_MYCHECK = 7;
SI_MYREPTC = 8;
SI_MYCAPS = 9;
SI_MYSIZE = 10;
/*
* This kermit's init parameters
*/
MAXPACK = 94; /* Maximum incoming packet size (max 94) */
MYTIME = 10; /* Seconds after which I should be timed out */
MYPAD = 0; /* Number of padding characters I will need (max 94) */
MYPCHAR = DEL; /* Padding character I need */
MYEOL = DC3; /* End-Of-Line character I need */
MYQUOTE = '#'; /* Quote character I will use */
MYBQUOTE = '&'; /* Eighth-bit quote char: ' ' => none */
MYREPTC = '~'; /* Repeat prefix: ' ' => none */
MYCHECK = '1'; /* Checksum type: '1' => default */
MYCAPS = (CAP_TIMO|CAP_SERV); /* capability mask */
CAP_TIMO = 0; /* I can do timeouts: 0 => no, 040 => yes */
CAP_SERV = 020; /* I have server mode: 0 => no, 020 => yes */
INIT_SIZ = 10; /* number of parameters we will look at in an init pak */
MAXTIM = 30; /* Maximum timeout interval */
MINTIM = 2; /* Minumum timeout interval */
MAXTRY = 5; /* Times to retry a packet */
ESCCHR = '^'; /* connect mode escape char */
MAXLINE = 256; /* Size of packet buffers */
MAXNAM = 12; /* Maximum name file name length */
/* kermit/s/manif >> End << */
/* kermit/s/extrn >> Begin << */
/*
* EXTRN -- EXTeRNal variables for Kermit.
*/
/*
* Commands enabled?
*/
wat_lc { FALSE }; /* Have Waterloo style LC command? */
wat_smcl { FALSE }; /* Have Waterloo style SMCL command? */
wat_cwd { FALSE }; /* Have Working Directories enabled? */
/*
* .Bset Options.
*/
.keep { 1 };
.process { 0 };
.optab [] {
"?", BLNK_KWD,
"Done", COMM_KWD,
"Exit", COMM_KWD,
"Quit", COMM_KWD,
"Help", COMM_KWD,
"Receive", COMM_KWD,
"SENd", COMM_KWD,
"SERver", COMM_KWD,
"SET", COMM_KWD,
"STatus", COMM_KWD,
"indeX", SVAL_KWD,
"Format", SVAL_KWD,
"Checksum", SVAL_KWD,
"DeBug", SVAL_KWD,
"Delay", NVAL_KWD,
"Random", PLUS_KWD|DASH_KWD,
"Discard", PLUS_KWD|DASH_KWD,
"RetryPacket", PLUS_KWD|DASH_KWD,
"Permanent", PLUS_KWD|DASH_KWD,
"TapeMode", PLUS_KWD|DASH_KWD,
"Xon", PLUS_KWD|DASH_KWD,
"OverWrite", PLUS_KWD|DASH_KWD,
"SendEndOfLine", NVAL_KWD,
"SendStartofPacket", NVAL_KWD,
"ChangeWorkingDirectory", SVAL_KWD,
-1 };
/*
* Global characters
*/
stpkt { SOH }; /* Start-of-packet character to send (char) */
padchar { NULL }; /* Padding character to send (char) */
eol { CR }; /* End-Of-Line character to send (char) */
quotec { '#' }; /* Incoming quote char. for control chars (char) */
bquote { MYBQUOTE }; /* Incoming quote character for 8th-bit (char) */
reptc { MYREPTC }; /* Incoming repeat prefix character (char) */
state; /* Present state of the automaton (char) */
lastpk; /* Last received packet type (char) */
recpkt [MAXLINE]; /* Receive packet buffer (char) */
packet [MAXLINE]; /* Packet buffer (char) */
filnam [MAXLINE _SIZE]; /* current file name (char) */
msghdr { "Kermit-GCOS" }; /* Message header */
wc [MAXLINE _SIZE]; /* Working Catalog (char) */
/*
* Global Variables
*/
size; /* Size of present data (int) */
n; /* Message number (int) */
rpsiz; /* Maximum receive packet size (int) */
spsiz { 80 }; /* Maximum send packet size (int) */
pad { 0 }; /* How much padding to send (int) */
timint { 10 }; /* Timeout for foreign host on sends (int) */
numtry; /* Times this packet retried (int) */
oldtry; /* Times previous packet retried (int) */
fd { ERR }; /* file pointer of file to read/write (file) */
fderr { ERR }; /* file to send debug output to */
fdtap { ERR }; /* file to read tape mode input from */
image { FALSE }; /* YES means 8-bit mode (int) */
remspd; /* speed of this tty (int) */
remote { FALSE }; /* YES means we're a remote host kermit (int) */
debug { DBG_OFF }; /* Type of debugging done (int) */
eoflg; /* EOF flag for Send Data state (int) */
hlpflg; /* Flag for Help command (int) */
rflg; /* Flag for receive mode (int) */
sflg; /* Flag for send mode (int) */
srvflg; /* Flag for server mode (int) */
setflg; /* Flag for Set command (int) */
stsflg; /* Flag for Status command (int) */
dobquo { FALSE }; /* YES => do 8th bit quoting (int) */
dorept { FALSE }; /* YES => do repeat prefixing (int) */
xonwait { FALSE }; /* YES => wait for XON before each packet send (int) */
imgflg { FALSE }; /* YES => image-mode command flag set (int) */
tapflg { TRUE }; /* YES => use paper tape mode for input */
binfil { FM_TEXT }; /* YES => do 8 bit i/o on this file (int) */
chktype { 1 }; /* Checksum type */
delay { 10 }; /* Delay time */
randflg { 0 }; /* YES => make file random */
discflg { 0 }; /* YES => Discard incomplete file */
retryflg { 1 }; /* YES => Retry every packet */
prmflg { 1 }; /* YES => Always use permanent file */
ovrflg { 0 }; /* YES => Overwrite existing files */
eolpend { FALSE }; /* TRUE => We have a pending eol */
curflgs [] { /* Vector containing the current file mode settings. */
0, /* File name */
FM_TEXT, /* FL_BINF */
FALSE, /* FL_RAND */
FALSE, /* FL_DISC */
TRUE, /* FL_PERM */
FALSE /* FL_OVWR */
};
crctab [] {
0000000,
0010201,
0020402,
0030603,
0041004,
0051205,
0061406,
0071607,
0102010,
0112211,
0122412,
0132613,
0143014,
0153215,
0163416,
0173617
};
crctb2 [] {
0000000,
0010611,
0021422,
0031233,
0043044,
0053655,
0062466,
0072277,
0106110,
0116701,
0127632,
0137323,
0145154,
0155745,
0164576,
0174367
};
fillst; /* List of files to send */
filcnt; /* Number of files supplied */
/*
* io error statuses from putrec and write (DRL DIO)
*/
io.err[] {
0,
"Not in aft",
"Device attention",
"End of reel",
"Physical end of file",
"Bad instruction",
"Status 06",
"Status 07",
"Channel busy",
"Status 11",
"Status 12",
"Data alert",
"Status 14",
"Status 15",
"Status 16",
"Physical end of file"
};
/*
* Variables to handle paper tape mode buffering.
*/
tpbuf[64]; /* Buffer to hold tap* sectors */
tpcnt { 0 }; /* Number of characters in tap* buffer */
tpidx { 0 }; /* Index of next character in tap* buffer */
tpsect { 0 }; /* Next sector of tap* to be read */
tpeof { 1 }; /* Do we need another DRL T.TPIN? */
/*
* Variables to handle file buffering.
*/
flbuf[320]; /* Buffer to hold the current record */
flcnt { 0 }; /* Number of characters in the record */
flidx { 0 }; /* Index of next character in the record */
flseg { 0 }; /* Segment number of partitioned record */
flsec { 0 }; /* Index of sector to read next */
flcrcnt { 0 }; /* Count of pending carriage returns */
flunbuf[10] { 0 }; /* Ungot character buffer */
/* kermit/s/extrn >> End << */
/* kermit/s/main >> Begin << */
/*
* MAIN -- MAINline for Kermit.
*
* This is the main body of Kermit which calls the other
* functions and procedures.
*/
main() {
extrn msghdr, wc, curflgs, fillst, filcnt;
auto retn, cmdstr[MAXLINE _SIZE];
printf( "%s: Version 1.1*n*n", msghdr );
flush();
getwd( wc );
nobrks( MAX_BRKS );
reread();
getstr( cmdstr );
rep.st( cmdstr, 0, "", .span( cmdstr, .break( cmdstr, 0, " " ), " " ) );
retn = getcmd( cmdstr, PAR_CMD, curflgs );
if( retn == 'a' || !retn ) /* It aborted */
exit( ERR ); /* End kermit with an error status */
else if( retn == 'n' ) { /* No command given */
repeat {
nobrks( MAX_BRKS );
prompt( "%s>", msghdr );
getstr( cmdstr );
if( char(cmdstr,0) == '!' ) {
rep.st( cmdstr, 0, "", 1 );
system( cmdstr );
}
else {
retn = getcmd( cmdstr, PAR_CMD, curflgs );
if( retn == 'd' )
break;
}
}
}
} /* main */
/* kermit/s/main >> End << */
/* kermit/s/bufemp >> Begin << */
/*
* BUFEMP -- BUFfer EMPty.
*
* Get data from an incoming packet into a file
* Control-quoting, 8-bit & repeat prefixes are done.
* Note that parity stripping was already done in spack.
*
* Assumes putch works with 8-bit data.
*
* buffer -- the buffer
* len -- length
*
* Returns 0 if successful, negative of the major
* filesystem status if not.
*/
bufemp( buffer, len ) {
extrn quotec, bquote, reptc, dobquo, dorept;
auto sts, nrep, i, j, c, c7, c8;
for( i = 0; i < len; ++i ) { /* Loop through data */
c = buffer[i]; /* Get character */
if( !dorept || c != reptc ) /* Repeat prefix? */
nrep = 1;
else {
nrep = unchar(buffer[++i]); /* Get the count */
c = buffer[++i]; /* Next char */
}
if( !dobquo || c != bquote ) /* Eighth-bit quote? */
c8 = 0;
else {
c8 = 128; /* save bit's value */
c = buffer[++i]; /* Next char */
}
if( c == quotec ) { /* A quoted char */
c = buffer[++i]; /* get the next char */
if( (c7 = mask(c)) >= '?' && c7 <= '_' )
c = ctl(c); /* Controlify the char */
}
c |= c8; /* Or in eighth bit */
for( j = 1; j <= nrep; ++j ) /* Put out correct number of chars */
if( (sts = putch( c )) < 0 )
return( sts );
}
return( 0 );
} /* bufemp */
/* kermit/s/bufemp >> End << */
/* kermit/s/bufill >> Begin << */
/*
* BUFILL -- BUFfer FILL.
*
* Get a bufferful of data from the file that's being sent.
* Control-quoting, 8-bit & repeat prefixes are done.
*
* Assumes that getch returns 8-bit data.
*/
bufill( buffer ) {
extrn spsiz, dobquo, dorept;
auto c, c1, c7, i, j;
/*
* Loop on characters until end of file
* or the packet is full.
*/
i = 0;
while( i + 1 < spsiz - 9 && (c = getch()) != EOF ) {
/*
* Repeat prefixing enabled,
* cannot do repeat counts for CR-LFs.
*/
if( dorept && c != '*r' && c != '*n' ) {
for( j = 1; (c1 = getch()) == c; ++j ) /* look for repeated chars */
if( j >= 94 ) /* 94 char repeat limit */
break;
ungtch(c1); /* put back the one that didn't match */
if( j < 3 ) { /* If less than threshold for doing repeat */
for( ; j > 1; --j ) /* put them back */
ungtch(c);
}
else {
buffer[i++] = MYREPTC; /* repeat prefix */
buffer[i++] = tochar(j); /* repeat count */
}
}
if( c > 127 && dobquo ) { /* If eighth bit on */
buffer[i++] = MYBQUOTE; /* add eighth-bit quote */
c = mask(c); /* strip down to seven bits */
}
/*
* Do we need to quote this char?
*/
c7 = mask(c); /* A seven bit version of c */
if( c7 < ' ' || c7 == DEL || c7 == MYQUOTE
|| (c7 == MYBQUOTE && dobquo) || (c7 == MYREPTC && dorept) ) {
buffer[i++] = MYQUOTE; /* add quote char */
if( c7 < ' ' || c7 == DEL )
c = ctl(c); /* de-controlify control char */
}
buffer[i++] = c; /* Use the eight bit version */
} /* while */
buffer[i] = '*0';
return( i );
} /* bufill */
/* kermit/s/bufill >> End << */
/* kermit/s/chksum >> Begin << */
/*
* CHKSUM -- compute the CHecKSUM.
* Add the upper two bits into the lower six.
* The Kermit Protocol Manual details how the checksum is formed.
*/
chksum( sum, len, num, type, data ) {
extrn chktype, crctab, crctb2;
auto i, ch;
if( chktype == 1 || type == 'S' || type == 'I' || type == 'R' )
return( (((sum&0300) >> 6) + sum) & 077 );
else if( chktype == 2 )
return( sum & 07777 );
else if( chktype == 3 ) {
ch = tochar(len+3);
sum = crctab[(ch>>4)&017] ^ crctb2[ch&017];
ch = tochar(num) ^ (sum & 0377);
sum = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
ch = type ^ (sum & 0377);
sum = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
for( i = 0; (ch = data[i]) != '*0'; ++i ) {
ch ^= (sum & 0377);
sum = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
}
}
return( sum );
} /* chksum */
/* kermit/s/chksum >> End << */
/* kermit/s/ctl >> Begin << */
/*
* CTL -- ConTroLlify a character.
*
* Turns a control character into a printable charcter and vice versa
* by toggling the control bit (ie. ^A becomes A and A becomes ^A).
*/
ctl( ch ) {
auto mask;
return( ch ^ 64 ); /* toggle the control bit */
} /* ctl */
/* kermit/s/ctl >> End << */
/* kermit/s/eprintf >> Begin << */
/*
* EPRINTF -- Error PRINTF.
* If the appropriate debug states are set,
* do a printf to the Error output.
*/
eprintf( type, format, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) {
extrn fderr, debug;
if( debug & type ) {
if( fderr == ERR )
fderr = open( "kerm**dbg", "wu" );
printf( fderr, "%r", &format );
}
} /* eprintf */
/* kermit/s/eprintf >> End << */
/* kermit/s/errmsg >> Begin << */
/*
* ERRMSG -- send an ERRor MeSsaGe.
*
* Print error message, or send it.
*/
errmsg( format, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) {
extrn n, remote, packet, msghdr;
auto len, str[MAXLINE _SIZE];
print( str, "%s: %r", msghdr, &format );
eprintf( DBG_ALL, "%s*n", str );
if( !remote )
printf( -4, "%s*n", str );
else {
lchar( str, MAXPACK, '*0' ); /* Truncate to legal size */
len = str2pkt( packet, str );
spack( 'E', n, len, packet ); /* Send the error packet */
flusheol();
}
} /* errmsg */
/* kermit/s/errmsg >> End << */
/* kermit/s/errpkt >> Begin << */
/*
* ERRPKT -- print an ERRor PacKeT.
*/
errpkt( pkt ) {
auto str[MAXLINE _SIZE];
pkt2str( str, pkt );
eprintf( DBG_ALL, "Error from remote Kermit: %s*n", str );
} /* errpkt */
/* kermit/s/errpkt >> End << */
/* kermit/s/failmsg >> Begin << */
/*
* FAILMSG -- send a FAILure MeSsaGe.
*
* Send message about a protocol failure.
*/
failmsg( oldstate ) {
extrn fd, remote, state, lastpk, filnam;
auto i, line[MAXLINE _SIZE];
i = 1;
switch( state ) { /* Find the appropriate error message */
case 'a': /* a message was already received or sent */
return;
case 'm':
concat( line, "Retry limit exceeded" );
break;
case 'n':
concat( line, "Wrong packet number received" );
break;
case 'w':
print( line, "Wrong packet type %c received", lastpk );
break;
default:
concat( line, "Illegal internal state" );
}
concat( line, line, " while in state " );
addchar( line, oldstate ); /* Give the state */
if( fd != ERR) { /* Give the file, if open */
concat( line, line, ", in file ", filnam );
}
concat( line, line, " " );
errmsg( line ); /* Send error message to appropriate place */
if( remote )
eprintf( DBG_ALL, line ); /* Send a copy to ERROUT */
} /* failmsg */
/* kermit/s/failmsg >> End << */
/* kermit/s/fclose >> Begin << */
/*
* FCLOSE -- File CLOSE routine.
* Returns 0 if successful, the negative of the major
* filesystem status if not.
*/
fclose( fd, errflg ) {
extrn discflg, randflg, filnam, flbuf;
auto fd2, nsec, sts;
if( !discflg ) {
close( fd );
return( 0 );
}
if( !errflg ) {
close( fd );
fd = open( "kerm**tmp", randflg ? "rufeb" : "rufe" );
fd2 = open( filnam, randflg ? "wufeb" : "wufe" );
if( fd2 < 0 )
return( fd2 );
for( nsec = 0; read( fd, flbuf, nsec, 320 ) > 0; ++nsec )
if( (sts = write( fd2, flbuf, nsec, 320 )) < 0 )
return( sts );
close( fd2 );
}
close( fd );
retfil( "kerm**tmp" );
return( 0 );
} /* fclose */
/* kermit/s/fclose >> End << */
/* kermit/s/flusheol >> Begin << */
/*
* FLUSHEOL -- FLUSH the End Of Line if necessary.
*/
flusheol() {
extrn eolpend, eol;
if( eolpend ) {
putchar( eol );
flush();
}
} /* flusheol */
/* kermit/s/flusheol >> End << */
/* kermit/s/generic >> Begin << */
/*
* GENERIC -- execute a GENERIC command.
*/
generic( num, cmd, packet, flags ) {
extrn msghdr, wc, flsyerr, wat_lc, wat_smcl;
auto len, str, str2, unit, buf, temp, arglst[1], sts[1];
str = allocate( MAXLINE _SIZE );
switch( cmd ) { /* What is the command ? */
case 'F': /* Finish, shut down Kermit */
print( str, "%s: terminated.", msghdr );
len = str2pkt( packet, str );
spack( 'Y', num, len, packet ); /* Acknowledge receipt of command */
return( FALSE ); /* Exit Server Mode */
break;
case 'L': /* Shut down Kermit and logout. */
spack( 'Y', num, 0, 0 ); /* Acknowledge receipt of command */
flusheol();
quit(); /* Execute session logout */
break;
case 'C': /* Change Working Directory */
pkt2str( str, packet );
lchar( str, unchar(char(str,0)) + 1, '*0' );
rep.st( str, 0, "", 1 );
/*
* There should be a validity check here.
*/
if( !nullstr(str) ) {
if( char(str,0) == '/' )
concat( wc, wc, str );
else
concat( wc, str );
}
print( str, "%s: Working Directory = %s", msghdr, wc );
len = str2pkt( packet, str );
spack( 'Y', num, len, packet );
break;
case 'D': /* Directory */
if( !wat_lc ) {
errmsg( "Remote Directory command is not available." );
break;
}
pkt2str( str, packet );
rep.st( str, 0, "", 1 );
if( packet[0] == 0 )
concat( str, wc );
if( char(str,0) == '/' )
rep.st( str, 0, wc, 0 );
system( "lc %s >kerm**tmp", str );
mesgsw( "kerm**tmp", " " );
break;
case 'E': /* Erase (release) a file */
pkt2str( str, packet );
rep.st( str, 0, "", 1 );
wdfile( str, flags[FL_PERM] );
buf = allocate( 43 );
if( scaf( str, buf, 0, 43 ) < 0 )
errmsg( "%s: Bad pathname", str );
else {
temp = allocate( 380 );
arglst[0] = sts << 18;
arglst[1] = buf << 18;
drl.drl( T.FLAC_, arglst, 22<<18 | temp );
sts[0] = ( sts[0] >> 24 ) & 03777;
if( sts[0] != 0 )
errmsg( "%s: %s", str, flsyerr[sts[0]] );
else {
print( temp, "%s: File %s released.", msghdr, str );
len = str2pkt( packet, temp );
spack( 'Y', num, len, packet );
}
}
break;
case 'H': /* print Help information */
pkt2str( str, packet );
rep.st( str, 0, "", 1 );
if( packet[0] == 0 || nullstr(str) )
concat( str, "HELP" );
unit = open( "kerm**tmp", "wu" );
prhelp( str, unit );
close( unit );
mesgsw( "kerm**tmp", " " );
break;
case 'T': /* Type (list) a file */
pkt2str( str, packet );
rep.st( str, 0, "", 1 );
wdfile( str, flags[FL_PERM] );
mesgsw( str, str );
break;
case 'U': /* Disk Usage Query */
if( !wat_smcl ) {
errmsg( "Remote Space command is not available." );
break;
}
system( "smcl >kerm**tmp" );
mesgsw( "kerm**tmp", " " );
break;
case 'Q': /* Server status Query */
unit = open( "kerm**tmp", "wu" );
printf( unit, "%s: Server Mode.*n", msghdr );
prsts( unit );
close( unit );
mesgsw( "kerm**tmp", " " );
break;
case 'J': /* produce a Journal */
errmsg( "%s: Transaction logging is not yet implemented.", msghdr );
break;
default: /* Anything else */
errmsg( "%c: generic command not implemented.", cmd );
} /* switch */
return( TRUE );
} /* generic */
/* kermit/s/generic >> End << */
/* kermit/s/get_rec >> Begin << */
/*
* GET_REC -- GET a RECord from a file.
*/
get_rec() {
extrn fd, binfil, flbuf, flidx, flcnt, flsec;
auto sts;
if( binfil != FM_TEXT ) {
sts = read( fd, flbuf, flsec, 320 );
flsec += 5;
if( sts < 0 ) {
sts = - sts;
if( sts != 017 )
eprintf( DBG_ALL, "On Read: status = %2o*n", sts );
return( FALSE );
}
if( binfil == FM_ASA9 )
flcnt = (320*4);
else
flcnt = ((320/2)*9);
flidx = 0;
}
else {
sts = getrcp( fd );
if( sts < 0 ) {
return( FALSE );
}
copy( flbuf, sts, sts[0]>>18 );
flidx = 0;
if( flbuf[0] == 01200600 && flbuf[1] == '*x*x*x*x' )
flbuf[0] = 0600;
flcnt = flbuf[0]>>16;
if( flcnt & 03 )
flcnt -= 4;
sts = (flbuf[0]>>10) & 03;
if( sts == 0 || sts == 3 ) {
lchar( &flbuf[1], flcnt++, '*r' );
lchar( &flbuf[1], flcnt++, '*n' );
}
}
return( TRUE );
} /* get_rec */
/* kermit/s/get_rec >> End << */
/* kermit/s/getch >> Begin << */
/*
* GETCH -- GET a CHaracter from a file.
*/
getch() {
extrn flunbuf, flbuf, flidx, flcnt, binfil;
auto ch;
/*
* If a character has been put back,
* return it first.
*/
if( flunbuf[0] > 0 )
return( flunbuf[ flunbuf[0]-- ] );
/*
* Get a REAL character.
*/
if( /*flbuf[0] == 0170000 ||*/ (flidx >= flcnt && !get_rec()) )
return( EOF );
if( binfil == FM_TEXT )
return( char( &flbuf[1], flidx++ ) );
else if( binfil == FM_ASA9 )
return( char( flbuf, flidx++ ) );
switch( flidx % 9 ) {
case 0: ch = flbuf[2*(flidx/9)]>>28; break;
case 1: ch = flbuf[2*(flidx/9)]>>20; break;
case 2: ch = flbuf[2*(flidx/9)]>>12; break;
case 3: ch = flbuf[2*(flidx/9)]>>4; break;
case 4: ch = flbuf[2*(flidx/9)]<<4
| flbuf[2*(flidx/9)+1]>>32; break;
case 5: ch = flbuf[2*(flidx/9)+1]>>24; break;
case 6: ch = flbuf[2*(flidx/9)+1]>>16; break;
case 7: ch = flbuf[2*(flidx/9)+1]>>8; break;
case 8: ch = flbuf[2*(flidx/9)+1]; break;
} /* switch */
++flidx;
return( ch & 0377 );
} /* getch */
/* kermit/s/getch >> End << */
/* kermit/s/getcmd >> Begin << */
/*
* GETCMD -- GET and parse a CoMmanD.
* Parse a command line and set options.
* Execute the command if desired.
*/
getcmd( cmdstr, cmdflg, in_flags ) {
extrn fillst, filcnt, filnam, wc, .argtype;
extrn rflg, sflg, srvflg;
extrn fderr, fdtap, debug;
extrn chktype, delay, retryflg, xonwait, tapflg, eol, stpkt, remote;
auto argc, argv, buf, cmd, i, info, p, ix, tmp, retn, flags[FL_SIZE];
auto nerrors, old_remote, randseen, formseen;
buf = allocate( 100 );
argv = .bset( cmdstr, buf );
argc = argv >> 18;
argv &= 0777777;
copy( flags, in_flags, FL_SIZE );
flags[FL_NAME] = 0;
nerrors = 0;
randseen = formseen = FALSE;
/*
* Find the command (if any)
*/
if( argc < 1 || (cmd = argv[0]>>18) < OP.DONE || cmd > OP.STAT )
cmd = OP.SET;
if( cmdflg == PAR_RMT
&& !(cmd == OP.HELP || cmd == OP.SET || cmd == OP.STAT) )
return( 'n' );
for( i = 0; i < argc; ++i )
switch( (info = argv[i]) >> 18 ) {
case OP.FILE:
if( cmdflg == PAR_CMD && cmd == OP.HELP ) {
prhelp( info );
return( TRUE );
}
else if( cmdflg == PAR_CMD && i == 0 ) {
errmsg( "%s: Unknown command.", info );
return( 'n' );
}
else if( cmdflg == PAR_CMD && cmd != OP.SEND ) {
errmsg( "%s: Filenames can only be used with SEND", info );
++nerrors;
}
break;
case OP.INDEX:
if( cmdflg == PAR_CMD && cmd != OP.SEND ) {
errmsg( "indeX=%s: Can only be used with SEND", info );
++nerrors;
}
break;
case OP.QUERY:
if( cmdflg == PAR_CMD && cmd == OP.HELP )
prhelp( "?" );
else
prquery( cmd, i );
return( TRUE );
case OP.DONE::OP.STAT:
break;
case OP.FORMAT:
formseen = TRUE;
if( .abbrv( "Text", info ) != -1 )
flags[FL_MODE] = FM_TEXT;
else if( .abbrv( "BYtestream", info ) != -1 )
flags[FL_MODE] = FM_ASA9;
else if( .abbrv( "BItstream", info ) != -1 )
flags[FL_MODE] = FM_BITS;
else {
errmsg( "Bad file format: Format=%s", info );
++nerrors;
}
break;
case OP.CHKSUM:
errmsg( "Checksum=%s: Option is not yet implemented.", info );
++nerrors;
break;
if( cmdflg != PAR_CMD ) {
errmsg( "Checksum=%s: Can only be used on command lines.", info );
++nerrors;
}
else {
if( .abbrv( "Single", info ) != -1 || equal( "1", info ) )
chktype = 1;
else if( .abbrv( "Double", info ) != -1 || equal( "2", info ) )
chktype = 2;
else if( .abbrv( "CyclicRedundancyCheck", info ) != -1
|| equal( "3", info ) )
chktype = 3;
else {
errmsg( "Checksum=%s: Bad Checksum type.", info );
++nerrors;
}
}
break;
case OP.DEBUG:
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "DeBug=%s: Can only be used on command lines.", info );
++nerrors;
}
else {
if( .abbrv( "Off", info ) != -1 )
debug = DBG_OFF;
else if( .abbrv( "States", info ) != -1 )
debug |= DBG_STATES;
else if( .abbrv( "Packets", info ) != -1 )
debug |= DBG_PACKETS;
else if( .abbrv( "Logfile", info ) != -1 )
debug |= DBG_LOGFILE;
else if( .abbrv( "All", info ) != -1 )
debug |= DBG_ALL;
else {
errmsg( "DeBug=%s: Invalid debug setting.", info );
++nerrors;
}
}
break;
case OP.DELAY:
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "Delay=%d: Can only be used on command lines.", *info );
++nerrors;
}
else {
if( *info < 0 ) {
errmsg( "Delay=%d: Delay must be positive", *info );
++nerrors;
}
else
delay = *info;
}
break;
case OP.SEOL:
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "SendEndOfLine=%d: Can only be used on command lines.", *info );
++nerrors;
}
else {
if( *info < 0 || *info > 127 ) {
errmsg( "SendEndOfLine=%d: Must be within 0..127.", *info );
++nerrors;
}
else
eol = *info;
}
break;
case OP.SPKST:
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg(
"SendStartofPacket=%d: Can only be used on command lines.",
*info );
++nerrors;
}
else {
if( *info < 0 || *info > 127 ) {
errmsg( "SendStartofPacket=%d: Must be within 0..127.",
*info );
++nerrors;
}
else
stpkt = *info;
}
break;
case OP.CWD:
/*
* There should be a validity check here.
*/
if( !nullstr(info) && !equal(info,"?") ) {
if( char(info,0) == '/' )
concat( wc, wc, info );
else
concat( wc, info );
}
errmsg( "Working Directory = %s", wc );
break;
case OP.RANDOM:
randseen = TRUE;
flags[FL_RAND] = ( .argtype[i] == '+' );
break;
case OP.DISCARD:
flags[FL_DISC] = ( .argtype[i] == '+' );
break;
case OP.RETRY:
errmsg( "%cRetryPacket: Option is not yet implemented.", .argtype[i] );
++nerrors;
break;
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "%cRetryPacket: Must be used with SET.", .argtype[i] );
++nerrors;
}
else {
retryflg = ( .argtype[i] == '+' );
}
break;
case OP.PERM:
flags[FL_PERM] = ( .argtype[i] == '+' );
break;
case OP.TAPMOD:
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "%cTapeMode: Must be used with SET.", .argtype[i] );
++nerrors;
}
else {
tapflg = ( .argtype[i] == '+' );
}
break;
case OP.XON:
errmsg( "%cXon: Option is not yet implemented.", .argtype[i] );
++nerrors;
break;
if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
errmsg( "%cXonwait: Must be used with SET.", .argtype[i] );
++nerrors;
}
else {
xonwait = ( .argtype[i] == '+' );
}
break;
case OP.OVWRITE:
flags[FL_OVWR] = ( .argtype[i] == '+' );
break;
default:
errmsg( "%s: Unknown option.", info );
++nerrors;
} /* switch */
if( nerrors )
return( 'n' );
if( cmdflg != PAR_IDX ) {
fillst = getvec( 0 );
fillst[0] = 0;
filcnt = 0;
}
if( !randseen && formseen )
flags[FL_RAND] = ( flags[FL_MODE] != FM_TEXT );
for( i = 0; i < argc; ++i )
switch( (info = argv[i]) >> 18 ) {
case OP.FILE:
tmp = allocate( MAXLINE _SIZE );
concat( tmp, info );
wdfile( tmp, flags[FL_PERM] );
p = getvec( FL_SIZE );
copy( p, flags, FL_SIZE );
p[FL_NAME] = concat( getvec(length(tmp)/4), tmp );
fillst = addvec( fillst, 1 );
fillst[++fillst[0]] = p;
break;
case OP.INDEX:
ix = open( info, "rudfem" );
if( ix > 0 ) {
tmp = allocate( MAXLINE _SIZE );
while( getstr( ix, tmp ) ) {
getcmd( tmp, PAR_IDX, flags );
}
close( ix );
}
break;
} /* switch */
if( debug && fderr == ERR )
fderr = open( "kerm**dbg", "wu" );
if( tapflg && fdtap == ERR )
fdtap = open( "tap**", "rwbut" );
if( cmdflg == PAR_CMD ) {
switch( cmd ) {
case OP.DONE:
case OP.EXIT:
case OP.QUIT:
retn = 'd';
break;
case OP.HELP:
prhelp( "HELP" );
flush();
retn = TRUE;
break;
case OP.RECV:
rflg = 1;
printf( "Escape back to your local KERMIT and enter a SEND command.*n" );
flush();
old_remote = remote;
remote = TRUE;
retn = recsw( flags ); /* Go to receive state */
remote = old_remote;
rflg = 0;
break;
case OP.SEND:
if( fillst[0] == 0 ) /* If no file names given */
printf( "No file names supplied.*n" );
else {
sflg = 1;
printf( "Escape back to your local KERMIT and enter RECEIVE mode.*n" );
flush();
old_remote = remote;
remote = TRUE;
retn = sendsw('S'); /* Go to send state */
remote = old_remote;
sflg = 0;
}
break;
case OP.SERV:
printf( "*nEntering Server Mode.*n" );
printf( "Escape back to your local KERMIT " );
printf( "and use server commands.*n" );
printf( "To exit Server Mode, enter the FINISH " );
printf( "command from your local KERMIT.*n" );
printf( "To exit KERMIT in an emergency, " );
printf( "enter control-D control-S (^D^S)*n" );
flush();
srvflg = 1;
old_remote = remote;
remote = TRUE;
retn = server( flags ); /* Invoke server */
remote = old_remote;
srvflg = 0;
printf( "*nExiting Server Mode.*n" );
flush();
break;
case OP.SET:
retn = 'n';
break;
case OP.STAT:
prsts();
flush();
break;
default:
retn = 'n'; /* No command given */
} /* switch */
}
if( cmdflg == PAR_CMD && retn == 'n' )
copy( in_flags, flags, FL_SIZE );
return( retn );
} /* getcmd */
/* kermit/s/getcmd >> End << */
/* kermit/s/getcomm >> Begin << */
/*
* GETCOMM -- GET a char from the COMMunications line.
* If not in paper tape mode, just do a getchar.
* If in paper tape mode, read the tap* buffer.
*/
getcomm() {
extrn tapflg, fdtap, tpbuf, tpcnt, tpidx, msghdr;
auto ch;
if( !tapflg ) {
ch = getchar();
eprintf( DBG_PACKETS, "%c", ch );
if( nobrks() ) {
printf( -4, "%s: Break key.*n", msghdr );
printf( -4, "To exit KERMIT type control-D (^D)" );
flush();
nobrks( MAX_BRKS );
}
return( ch );
}
if( tpidx >= tpcnt ) /* No characters left */
rdcomm();
ch = char( &tpbuf[2], tpidx++ );
eprintf( DBG_PACKETS, "%c", ch );
return( ch );
} /* getcomm */
/* kermit/s/getcomm >> End << */
/* kermit/s/getfil >> Begin << */
/*
* GETFIL -- GET a FILe.
* Open a new file, overwriting any existing file.
* Returns 0 if successful, 'a' if no clash resolution possible,
* and the negative of the filact error status if the file could
* not be created or accessed.
*/
getfil() {
extrn fd, imgflg, filnam, fillst, filcnt, wc;
extrn binfil, randflg, discflg, prmflg, ovrflg;
extrn flbuf, flidx, flcnt, flseg, flcrcnt, flsec;
concat( filnam, fillst[1][FL_NAME] );
binfil = fillst[1][FL_MODE];
randflg = fillst[1][FL_RAND];
discflg = fillst[1][FL_DISC];
prmflg = fillst[1][FL_PERM];
ovrflg = fillst[1][FL_OVWR];
eprintf( DBG_ALL, "Random=%d, Format=%d", randflg, binfil );
/*
* Make sure filnam is properly constructed
*/
innam( filnam, prmflg );
if( !ovrflg )
if( !rsclash( filnam ) )
return( - 011 ); /* Non-unique name */
if( discflg )
retfil( "kerm**tmp" );
fd = open( discflg ? "kerm**tmp" : filnam, randflg ? "wufeb" : "wufe" );
eprintf( DBG_ALL, "Attempt to open file: '%s'*n", filnam );
if( fd < 0 )
return( fd ); /* Return FILACT status */
/*
* Initialize the file buffer.
*/
if( binfil == FM_TEXT )
flcnt = 318*4;
else if( binfil == FM_ASA9 )
flcnt = 320*4;
else
flcnt = ((320/2)*9);
flidx = 0;
flsec = 0;
flseg = 0;
flcrcnt = 0;
zero( flbuf, 320 );
return( 0 ); /* Return file descriptor */
} /* getfil */
/* kermit/s/getfil >> End << */
/* kermit/s/getwd >> Begin << */
/*
* GETWD -- GET the initial Working Directory.
* If not on GCOS8, this is the logon userid.
*/
%b/manif/ust
getwd( wc ) {
extrn wat_cwd;
auto size, vec;
auto catf[.LCWD_*4 + 1];
if( !wat_cwd || !.gcos8() ) {
getumc( wc );
return( wc );
}
++catf; /* step over the size word */
p.ust( &catf[-1], .LCWD_*4+1, .LDUWD_ );
size = (catf[-1] >> 16) + 1;
size[vec = .vectr(size, size)] = -1;
++vec;
.copy( vec, catf, size-2 );
.unscaf( wc, vec, 1 );
rlsevec( vec-1, vec[-1] );
return( wc );
} /* getwd */
/* kermit/s/getwd >> End << */
/* kermit/s/gnxtfl >> Begin << */
/*
* GNXTFL -- Get NeXT FiLe.
*
* Get next file from command line.
*/
gnxtfl() {
extrn fd, imgflg, binfil, filnam, msghdr, fillst, filcnt, wc;
extrn randflg;
extrn flidx, flcnt, flsec, flseg, flcrcnt, flunbuf, flbuf;
auto tmp;
if( filcnt >= fillst[0] ) /* Otherwise, get next file name */
return( 'B' ); /* No more names - break transmission */
concat( filnam, fillst[++filcnt][FL_NAME] );
binfil = fillst[filcnt][FL_MODE];
randflg = fillst[filcnt][FL_RAND];
tmp = fillst[filcnt][FL_NAME];
rlsevec( tmp, length(tmp)/4 );
rlsevec( fillst[filcnt], FL_SIZE );
/*
* Make sure filnam is properly constructed
*/
innam( filnam, fillst[filcnt][FL_PERM] );
fd = open( filnam, randflg ? "rufeb" : "rufe" );
if( fd < 0 ) { /* If it doesn't exist */
errmsg( "Can't open file %s", filnam ); /* Send error message */
return( 'a' ); /* Abort */
}
eprintf( DBG_ALL, "%s: sending file '%s'", msghdr, filnam );
outnam( filnam ); /* Put name into standard format */
eprintf( DBG_ALL, " as '%s'*n", filnam );
flidx = 0;
flcnt = 0;
flsec = 0;
flseg = 0;
flcrcnt = 0;
flunbuf[0] = 0;
flbuf[0] = 0;
return( 'F' ); /* Ready to send new file. */
} /* gnxtfl */
/* kermit/s/gnxtfl >> End << */
/* kermit/s/innam >> Begin << */
/*
* INNAM -- INput NAMe.
*
* Change file name to a local compatible name.
*
* *** MACHINE DEPENDENT SUBROUTINE ***
* Makes sure that an incoming file has a name that the local system
* recognizes as valid.
*/
innam( name, prmflg ) {
auto i, j, ch;
/*
* Make sure that name
* contains only valid chars.
*/
trim( lowercase(name) );
if( !prmflg && .break( name, 0, "/$" ) == length(name) ) {
if( length(name) > 8 )
lchar( name, 8, '*0' );
return;
}
for( i = j = 0; (ch = char(name,i)) != '*0'; ++i )
if( any( ch, "abcdefghijklmnopqrstuvwxyz0123456789_.-/$*"**" ) != -1 )
lchar( name, j++, ch );
lchar( name, j, '*0' );
/*
* Make sure name is
* properly constructed.
*/
i = 0;
if( char(name,0) == '/' )
i = 1;
for( ; i == 0 || char(name,i-1) == '/'; i = j + 1 ) {
j = .break( name, i, "/$*"" );
if( j - i > 12 ) {
rep.st( name, i+12, "", j-i-12 );
j = i + 12;
}
if( (ch = char(name,j)) == '$' ) {
j = .break( name, i = j + 1, "/$*"" );
if( j - i > 12 ) {
rep.st( name, i+12, "", j-i-12 );
j = i + 12;
}
}
} /* for */
} /* innam */
/* kermit/s/innam >> End << */
/* kermit/s/mask >> Begin << */
/*
* MASK -- MASK off the parity bit.
* Returns the lower seven bits.
*/
mask( c ) {
return( c & 127 );
} /* mask */
/* kermit/s/mask >> End << */
/* kermit/s/mesgsw >> Begin << */
/*
* MESGSW -- MESseGe SWitcher.
*
* Mesgsw is the state table switcher for sending
* long replies to queries or commands. It loops
* until either it finishes, or an error is found.
* The routines called by mesgsw are responsible
* for changing the state.
*
* fname -- name of file to be sent.
* sname -- name to send.
*/
mesgsw( fname, sname ) {
extrn n, numtry, fd, state, fillst, filcnt;
auto lstate, llstate;
fillst = getvec(1);
fillst[0] = 1;
filcnt = 0;
fillst[1] = getvec( FL_SIZE );
fillst[1][FL_NAME] = concat( getvec(length(fname)/4), fname );
fillst[1][FL_MODE] = FM_TEXT;
fillst[1][FL_RAND] = FALSE;
fillst[1][FL_DISC] = FALSE;
fillst[1][FL_PERM] = FALSE;
fillst[1][FL_OVWR] = TRUE;
state = 'S'; /* Start in Send-Init state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
repeat { /* Do this as long as necessary */
eprintf( DBG_STATES, " mesgsw %c %d*n", state, n );
switch( state ) {
case 'D': /* Data-Send state */
state = sdata();
break;
case 'F': /* File-Send */
state = sfile(sname);
break;
case 'Z': /* End of File */
state = seof();
if( state == 'F' ) /* If ready for next file */
state = 'B'; /* Do Break */
break;
case 'S': /* Send Init */
state = sinit();
break;
case 'B': /* Break-Send */
state = sbreak();
break;
case 'C': /* Complete */
flusheol();
return(TRUE);
default: /* Anything else is an error */
failmsg(llstate); /* Put out an error message */
if( fd != ERR ) { /* If file left open */
close(fd); /* Close it */
fd = ERR; /* Remember it's closed */
}
flusheol();
return(FALSE); /* Error return */
}
llstate = lstate;
lstate = state; /* Remember last state */
}
} /* mesgsw */
/* kermit/s/mesgsw >> End << */
/* kermit/s/outnam >> Begin << */
/*
* OUTNAM -- convert an OUTgoing fileNAMe.
* This routine converts a local file name to
* a form recognizable by most other systems.
*
* The format of the name is :
*
* name.ext
*
* Where "name" can be 8 characters long and "ext"
* can be 3 characters long or not even present.
* The characters should be uppercase.
*/
outnam( name ) {
auto i;
uppercase( name );
/*
* Strip off leading catalogs.
*/
for( i = length(name)-1; i >= 0; --i )
if( char(name,i) == '/' )
break;
if( i >= 0 )
rep.st( name, 0, "", i+1 );
/*
* Strip password (if any).
*/
if( (i = any( '$', name )) != -1 )
lchar( name, i, '*0' );
/*
* Found a '.'
*/
if( (i = any( '.', name )) >= 0 ) {
if( i > 8 ) { /* Name is too long */
movelr( name,8, name,i, length(name)-i+1 );
i = 8;
}
if( length(name)-i > 4 ) /* If extension is too long */
lchar( name, i+4, '*0' ); /* Truncate it. */
}
else {
if( length(name) > 8 ) /* If name too long .... */
lchar( name, 8, '*0' ); /* Truncate it. */
}
return( name );
} /* outnam */
/* kermit/s/outnam >> End << */
/* kermit/s/pkt2str >> Begin << */
/*
* PKT2STR -- convert a PacKeT TO a STRing.
* Packets are simple arrays of characters.
* Strings are packed four character per word.
* Control-quoting, 8-bit & repeat prefixes are done.
*/
pkt2str( str, pkt ) {
extrn quotec, bquote, reptc, dobquo, dorept;
auto nrep, i, j, len, c, c7, c8;
/*
* Loop through data.
*/
len = 0;
for( i = 0; (c = pkt[i]) != '*0'; ++i ) {
if( !dorept || c != reptc ) /* Repeat prefix? */
nrep = 1;
else {
nrep = unchar(pkt[++i]); /* Get the count */
c = pkt[++i]; /* Next char */
}
if( !dobquo || c != bquote ) /* Eighth-bit quote? */
c8 = 0;
else {
c8 = 128; /* save bit's value */
c = pkt[++i]; /* Next char */
}
if( c == quotec ) { /* A quoted char */
c = pkt[++i]; /* get the next char */
if( (c7 = mask(c)) >= '?' && c7 <= '_' )
c = ctl(c); /* Controlify the char */
}
c |= c8; /* Or in eighth bit */
for( j = 1; j <= nrep; ++j ) /* Put out correct number of chars */
lchar( str, len++, c );
} /* for */
lchar( str, len, '*0' );
return( len );
} /* pkt2str */
/* kermit/s/pkt2str >> End << */
/* kermit/s/prhelp >> Begin << */
/*
* PRHELP -- PRint a HELP message.
*/
prhelp( cmd, unit ) {
auto old_unit;
if( nargs() < 2 )
unit = -4;
old_unit = .write( unit );
printf( "*n" );
if( .abbrv( "Done", cmd ) != -1
|| .abbrv( "Exit", cmd ) != -1 || .abbrv( "Quit", cmd ) != -1 ) {
printf( "*
* Done -- Exit KERMIT*n*
* Exit -- Exit KERMIT*n*
* Quit -- Exit KERMIT*n*
* *n*
* Syntax:*n*
* Done*n*
* or Exit*n*
* or Quit*n*
* *n*
* The Done, Exit and Quit commands exit KERMIT.*n*
* No options are allowed for this command.*n" );
}
else if( equal( cmd, "?" ) ) {
printf( "*
* ? -- Query the allowed options*n*
* *n*
* A ? symbol in place of a command or an option*n*
* will cause a list of the possible commands or*n*
* options at that point. To get an explanation*n*
* of the command/option, use the Help command.*n" );
}
else if( .abbrv( "Help", cmd ) != -1 ) {
printf( "*
* Help -- Provide Basic Explanations*n*
* *n*
* Syntax:*n*
* Help <command or option>*n*
* *n*
* The Help command prints information about the <command>*n*
* or <option> specified.*n*
* *n*
* Commands recognized:*n*
* ? Done Exit Help Quit*n*
* Receive SENd SERver SET STatus*n*
* Options recognized:*n*
* CWD DeBug Delay Discard Format*n*
* indeX filename OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( .abbrv( "Receive", cmd ) != -1 ) {
printf( "*
* Receive -- Receive a file*n*
* *n*
* Syntax:*n*
* Receive [<option>]***n*
* *n*
* The Receive command causes GCOS KERMIT to wait for files to*n*
* be sent from the local KERMIT. The files will be accessed*n*
* according to the current option settings, unless overridden*n*
* on the command line.*n*
* *n*
* The following options are recognized:*n*
* CWD DeBug Delay Discard*n*
* Format OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( .abbrv( "SENd", cmd ) != -1 ) {
printf( "*
* SENd -- Send a File*n*
* *n*
* Syntax:*n*
* SENd [<filespec>]** [<option>]***n*
* *n*
* A <filespec> is either the name of a file to send or*n*
* *"indeX=filename*" where the file contains KERMIT SENd command*n*
* options and filespecs.*n*
* *n*
* The SENd command causes GCOS KERMIT to send files to*n*
* the local KERMIT. The files will be sent according to the*n*
* current option settings, unless overridden on the command*n*
* line or in an index file. GCOS KERMIT will wait for the*n*
* number of seconds specified in the Delay option before*n*
* starting to send the files. This allows you time to escape*n*
* back to your local KERMIT and type RECEIVE.*n*
* *n*
* The following options are recognized:*n*
* CWD DeBug Delay Discard Format*n*
* indeX filename OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( .abbrv( "SERver", cmd ) != -1 ) {
printf( "*
* SERver -- Enter Server Mode*n*
* *n*
* Syntax:*n*
* SERver [<option>]***n*
* *n*
* The SERver command causes GCOS KERMIT to enter server mode.*n*
* While in server mode, GCOS KERMIT will wait for commands*n*
* from your local KERMIT. When a command is received, it is*n*
* executed. Server mode is the preferred mode of operation,*n*
* although not all local KERMITs support it. If your local*n*
* KERMIT supports server mode, you should use it. The use of*n*
* server mode is detailed more fully below.*n*
* *n*
* The following options are recognized:*n*
* CWD DeBug Delay Discard*n*
* Format OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( .abbrv( "SET", cmd ) != -1 ) {
printf( "*
* SET -- Set KERMIT Options*n*
* *n*
* Syntax:*n*
* SET [<option>]***n*
* *n*
* The SET command causes KERMIT to set its options as you*n*
* specify on the command line. Normally, options given on a*n*
* command line apply only to that command. A SET command*n*
* causes them to change until a later SET command is given.*n*
* *n*
* The following options are recognized:*n*
* CWD DeBug Delay Discard*n*
* Format OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( .abbrv( "STatus", cmd ) != -1 ) {
printf( "*
* STatus -- Print Options Status*n*
* *n*
* Syntax:*n*
* STatus*n*
* *n*
* The STatus command causes GCOS KERMIT to print a list of the*n*
* current option settings.*n*
* No options are allowed for this command.*n" );
}
else if( .abbrv( "indeX", cmd ) != -1 ) {
printf( "*
* indeX=filename*n*
* causes the file specified to be read and each line*n*
* treated as options and files for the SENd command.*n*
* The files named are sent. Index files may be nested.*n*
* The only options allowed within index files are*n*
* *"Format=*", +Random, +Discard, -Permanent, +OverWrite.*n*
* *n*
* This option may only be used with the SENd command.*n" );
}
else if( .abbrv( "Format", cmd ) != -1 ) {
printf( "*
* Format=fileformat*n*
* causes any file transferred in either direction to be*n*
* treated according to the file format specified. The*n*
* formats are *"Text*", *"BYtestream*", and *"BItstream*".*n*
* See the section on file formats in the Writeup. The*n*
* default is Text.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "DeBug", cmd ) != -1 ) {
printf( "*
* DeBug=function*n*
* specifies how much debugging information is to be*n*
* written to the debug file *"kerm**dbg*". The default is*n*
* *"Off*", which means that no debugging information will*n*
* be written. *"States*" causes the current protocol*n*
* state to be written when it changes. *"Packets*"*n*
* causes each packet sent or received to be written.*n*
* *"All*" causes all possible debug information to be*n*
* written. This information is rarely useful, unless*n*
* you suspect your communications line of being noisy*n*
* and wish to inspect what is being sent.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "Delay", cmd ) != -1 ) {
printf( "*
* Delay=nn*n*
* causes KERMIT to wait nn seconds before sending in-*n*
* formation when using the SENd command. The default*n*
* is 10 seconds.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "Random", cmd ) != -1 ) {
printf( "*
* -Random*n*
* +Random*n*
* causes KERMIT to access the file as random. The*n*
* default is sequential. For non-text files, +Random*n*
* must be specified.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "Discard", cmd ) != -1 ) {
printf( "*
* -Discard*n*
* +Discard*n*
* causes KERMIT to discard an incomplete transmission.*n*
* If you interrupt the transmission of a file, the file*n*
* will not be created on GCOS. If -OverWrite is in ef-*n*
* fect, the previous contents will be unchanged.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "Permanent", cmd ) != -1 ) {
printf( "*
* +Permanent*n*
* -Permanent*n*
* causes KERMIT to create the file as a permanent file.*n*
* This is the default. If -permanent is specified,*n*
* then the usual GCOS access conventions are used, i.e.*n*
* if there are no slashes or dollar signs in the*n*
* filename and if the filename is less than or equal to*n*
* eight characters long, and if a quick access file of*n*
* the same name does not already exist, the file will*n*
* be created as temporary.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "TapeMode", cmd ) != -1 ) {
printf( "*
* -TapeMode*n*
* +TapeMode*n*
* causes KERMIT to use GCOS paper tape mode to read*n*
* packets. This is the default. At sites with Honey-*n*
* well's new front end processors (DN8), paper tape*n*
* mode need not be used if the character delete is set*n*
* to a non-printing character (usually a backspace or*n*
* delete). Since GRTS cannot reset the character*n*
* delete (@), and since KERMIT does indeed transmit @s,*n*
* paper tape mode must be used to read the input to*n*
* preserve the @s. This option conflicts with any flow*n*
* control done by anyone.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "OverWrite", cmd ) != -1 ) {
printf( "*
* -OverWrite*n*
* +OverWrite*n*
* causes an existing file of the same name to be over-*n*
* written. The default is -OverWrite, which causes the*n*
* incomming file to be renamed to avoid conflicts. The*n*
* file is renamed by appending *"_n*" to the file, where*n*
* n is the smallest digit not resulting in a clash. If*n*
* the filename is too long, the underscore will be*n*
* omitted. If the filename is still too long,*n*
* characters will be deleted from the end until it*n*
* fits.*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "SendEndOfLine", cmd ) != -1 ) {
printf( "*
* SendEndOfLine=nn*n*
* causes KERMIT to terminate its outgoing packets with*n*
* the ASCII character whose decimal value is nn. Ex-*n*
* ample: SendEndOfLine=26 would cause KERMIT to ter-*n*
* minate its outgoing packets with a control-Z (decimal*n*
* ASCII value = 26). The default is carriage return*n*
* (value = 13).*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "SendStartofPacket", cmd ) != -1 ) {
printf( "*
* SendStartofPacket=nn*n*
* causes KERMIT to start its outgoing packets with the*n*
* ASCII character whose decimal value is nn. Example:*n*
* SendStartofPacket=26 would cause KERMIT to start its*n*
* outgoing packets with a control-Z (decimal ASCII*n*
* value = 26). The default is control-A (value = 1).*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else if( .abbrv( "ChangeWorkingDirectory", cmd ) != -1 ) {
printf( "*
* ChangeWorkingDirectory=catalog*n*
* causes KERMIT to change its internal working directory*n*
* to the catalog given. If the catalog begins with a*n*
* slash (/) it will be appended to the current working*n*
* directory. If the catalog is null or a question*n*
* mark (?) is given, the current working catalog will be*n*
* displayed. The working catalog is where KERMIT*n*
* creates/accesses files when they start with a slash*n*
* or contain no slashes (quick access files).*n*
* *n*
* This option may be used with the commands:*n*
* Receive, SENd, SERver and SET.*n" );
}
else {
printf( "%s: Unrecognized command or option.*n", cmd );
}
printf( "*n" );
.write( old_unit );
} /* prhelp */
/* kermit/s/prhelp >> End << */
/* kermit/s/prquery >> Begin << */
/*
* PRQUERY -- PRint a response to a QUERY.
*/
prquery( cmd, argno ) {
extrn remote;
printf( "*n" );
if( argno == 0 ) {
printf( "*
* Commands recognized:*n*
* ? Done Exit Help Quit*n*
* Receive SENd SERver SET STatus*n" );
}
else if( cmd == OP.DONE
|| cmd == OP.EXIT
|| cmd == OP.QUIT
|| cmd == OP.STAT ) {
printf( " No options are allowed for this command.*n" );
}
else if( cmd == OP.RECV || cmd == OP.SERV || cmd == OP.SET ) {
printf( "*
* The following options are recognized:*n*
* CWD DeBug Delay Discard*n*
* Format OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else if( cmd == OP.SEND ) {
printf( "*
* The following options are recognized:*n*
* CWD DeBug Delay Discard Format*n*
* indeX filename OverWrite Permanent Random*n*
* SendEOL SendStartofPacket TapeMode*n" );
}
else {
printf( "Unrecognized query command.*n" );
}
printf( "*n For information on specific commands/options, type*n" );
printf( " Help <command or option>*n*n" );
} /* prquery */
/* kermit/s/prquery >> End << */
/* kermit/s/prsts >> Begin << */
/*
* PRSTS -- PRint the STatuS.
*/
prsts( unit ) {
extrn curflgs, chktype, debug, delay, retryflg, tapflg, wc;
extrn eol, stpkt;
auto old_unit;
if( nargs() < 1 )
unit = -4;
old_unit = .write( unit );
printf( "*nStatus:*n" );
printf( "Format=" );
if( curflgs[FL_MODE] == FM_TEXT )
printf( "Text" );
else if( curflgs[FL_MODE] == FM_ASA9 )
printf( "Bytestream" );
else if( curflgs[FL_MODE] == FM_BITS )
printf( "Bitstream" );
printf( "*n" );
printf( "Debug=" );
if( debug == DBG_OFF )
printf( "Off " );
if( debug & DBG_STATES )
printf( "States " );
if( debug & DBG_PACKETS )
printf( "Packets " );
if( debug & DBG_LOGFILE )
printf( "Logfile " );
printf( "*n" );
printf( "Delay=%d*n", delay );
printf( "%cRandom*n", curflgs[FL_RAND] ? '+' : '-' );
printf( "%cDiscard*n", curflgs[FL_DISC] ? '+' : '-' );
printf( "%cPermanent*n", curflgs[FL_PERM] ? '+' : '-' );
printf( "%cOverWrite*n", curflgs[FL_OVWR] ? '+' : '-' );
printf( "%cTapeMode*n", tapflg ? '+' : '-' );
printf( "SendEndOfLine=%d*n", eol );
printf( "SendStartofPacket=%d*n", stpkt );
printf( "CWD=%s*n", wc );
printf( "*n" );
.write( old_unit );
} /* prsts */
/* kermit/s/prsts >> End << */
/* kermit/s/put_eof >> Begin << */
/*
* PUT_EOF -- PUT an EOF to the file.
*/
put_eof() {
extrn binfil, fd, flbuf, flidx, flcnt, flseg, flsec;
auto sts;
if( flidx == 0 )
return;
if( binfil != FM_TEXT ) {
if( (sts = write( fd, flbuf, flsec, 320 )) < 0 )
return( sts );
flsec += 5;
flidx = 0;
return;
}
flbuf[0] = ((flidx+3)/4) << 18;
flbuf[0] |= (flidx&03)<<16;
while( flidx & 03 )
lchar( &flbuf[1], flidx++, 0177 );
if( flseg == 0 ) {
flbuf[0] |= (01 << 10) | 0600;
}
else {
flbuf[0] |= (02 << 10) | (flseg++);
flseg = 0;
}
if( (sts = putrec( fd, flbuf )) < 0 )
return( sts );
flidx = 0;
return( 0 );
} /* put_eof */
/* kermit/s/put_eof >> End << */
/* kermit/s/put_rec >> Begin << */
/*
* PUT_REC -- PUT a physical RECord out to a file.
*/
put_rec( partflag ) {
extrn fd, binfil, flbuf, flidx, flcnt, flseg, flsec;
auto sts;
if( binfil != FM_TEXT ) {
if( (sts = write( fd, flbuf, flsec, 320 )) < 0 )
return( sts );
flsec += 5;
flidx = 0;
zero( flbuf, 320 );
return;
}
if( flidx == 0 ) {
flbuf[1] = 0177 << 27;
flidx = 1;
}
flbuf[0] = ((flidx+3)/4) << 18;
flbuf[0] |= (flidx&03)<<16;
while( flidx & 03 )
lchar( &flbuf[1], flidx++, 0177 );
if( flseg == 0 ) {
flbuf[0] |= 0600;
if( partflag ) {
flbuf[0] |= 01 << 10;
flseg = 1;
}
}
else if( partflag )
flbuf[0] |= (02 << 10) | (flseg++);
else {
flbuf[0] |= (03 << 10) | flseg;
flseg = 0;
}
if( (sts = putrec( fd, flbuf )) < 0 )
return( sts );
zero( flbuf, 320 );
flidx = 0;
return( 0 );
} /* put_rec */
/* kermit/s/put_rec >> End << */
/* kermit/s/putbuf >> Begin << */
/*
* PUTBUF -- outPUT a BUFfer of data.
*/
putbuf( line, len ) {
extrn pad, xonwait, padchar, eolpend;
auto i;
eprintf( DBG_PACKETS, " spack (raw):%s*n", line );
/*
* Issue any padding.
*/
for( i = 1; i <= pad; ++i )
putchar( padchar );
/*
* Send the packet.
*/
for( i = 0; i < len; ++i )
putchar( line[i] );
/*
* Flush the output buffer.
*/
eolpend = TRUE;
flush();
} /* putbuf */
/* kermit/s/putbuf >> End << */
/* kermit/s/putch >> Begin << */
/*
* PUTCH -- PUT a CHaracter out to a file.
* Returns 0 if successful, the negative of
* the major filesystem status if not.
*/
putch( ch ) {
extrn binfil, flcrcnt, flbuf, flidx, flcnt;
auto i, sts;
if( binfil == FM_ASA9 ) {
if( flidx >= flcnt )
if( (sts = put_rec( TRUE )) < 0 )
return( sts );
lchar( flbuf, flidx++, ch );
}
else if( binfil == FM_BITS ) {
if( flidx >= flcnt )
if( (sts = put_rec( TRUE )) < 0 )
return( sts );
switch( flidx % 9 ) {
case 0: flbuf[2*(flidx/9)] = ch<<28; break;
case 1: flbuf[2*(flidx/9)] |= ch<<20; break;
case 2: flbuf[2*(flidx/9)] |= ch<<12; break;
case 3: flbuf[2*(flidx/9)] |= ch<<4; break;
case 4: flbuf[2*(flidx/9)] |= ch>>4;
flbuf[2*(flidx/9)+1] = (ch&017)<<32; break;
case 5: flbuf[2*(flidx/9)+1] |= ch<<24; break;
case 6: flbuf[2*(flidx/9)+1] |= ch<<16; break;
case 7: flbuf[2*(flidx/9)+1] |= ch<<8; break;
case 8: flbuf[2*(flidx/9)+1] |= ch; break;
} /* switch */
++flidx;
}
else if( ch == '*r' )
++flcrcnt;
else {
for( i = (ch=='*n')?1:0; i < flcrcnt; ++i ) {
if( flidx >= flcnt )
if( (sts = put_rec( TRUE )) < 0 )
return( sts );
lchar( &flbuf[1], flidx++, '*r' );
}
if( flcrcnt > 0 && ch == '*n' ) {
if( (sts = put_rec( FALSE )) < 0 )
return( sts );
}
else {
if( flidx >= flcnt )
if( (sts = put_rec( TRUE )) < 0 )
return( sts );
lchar( &flbuf[1], flidx++, ch );
}
flcrcnt = 0;
}
return( 0 );
} /* putch */
/* kermit/s/putch >> End << */
/* kermit/s/quit >> Begin << */
/*
* QUIT -- QUIT kermit and logout the session.
*/
quit() {
eprintf( DBG_ALL, "EXIT and logoff*n" );
putchar( DC1 );
flush();
sum.up(); /* Close all open files */
sleep( 5 );
drl.drl( T.DISC_ );
} /* quit */
/* kermit/s/quit >> End << */
/* kermit/s/rdata >> Begin << */
/*
* RDATA -- Receive DATA.
*/
rdata() {
extrn n, numtry, oldtry, fd, state, packet, filnam, flsyerr, io.err;
auto num, len, sts;
if( numtry > MAXTRY ) /* "Abort" if too many tries */
return( 'm' );
++numtry;
switch( rpack(&len,&num,packet) ) { /* Get packet */
case 'D': /* Got Data packet */
if( num != n ) { /* Right packet ? */
if( oldtry > MAXTRY ) /* No. Too many tries */
return( 'm' );
++oldtry; /* give up */
if( num == ((n-1)&63) ) { /* Previous packet again ? */
spack( 'Y', num, 0, 0 ); /* Yes, re-ACK it */
numtry = 0; /* Reset try counter */
return( state ); /* Stay in D, don't write out data! */
}
else
return( 'n' ); /* Sorry! Wrong number. */
}
/*
* Got data with the right packet number.
* Write the data to the file.
*/
if( (sts = bufemp( packet, len )) < 0 ) {
errmsg( "%s: %s", filnam, io.err[-sts] );
return( 'a' );
}
spack( 'Y', n, 0, 0 ); /* Acknowledge the packet */
oldtry = numtry; /* Reset the try counters */
numtry = 0; /* ... */
n = (n+1) & 63; /* Bump the packet number */
return( 'D' ); /* Remain in data state */
case 'F': /* Got a File Header */
if( oldtry > MAXTRY ) /* If too many tries, "abort" */
return( 'm' );
++oldtry;
if( num == ((n-1)&63) ) { /* It was the previous packet */
spack( 'Y', num, 0, 0 ); /* ACK it again */
numtry = 0; /* Reset try counter */
return( state ); /* Stay in data state */
}
else
return( 'n' ); /* Not previous packet, "abort" */
case 'Z': /* End-Of-File */
if( num != n ) /* Must have right packet number */
return( 'n' );
spack( 'Y', n, 0, 0 ); /* OK, ACK it. */
/*
* Flush possible final CR.
* Flush file system buffers.
* Close the file.
*/
if( (sts = bufemp( packet, 0 )) < 0 || (sts = put_eof()) < 0 ) {
errmsg( "%s: %s", filnam, io.err[-sts] );
return( 'a' );
}
else if( (sts = fclose( fd, FALSE )) < 0 ) {
errmsg( "%s: %s", filnam, flsyerr[-sts] );
return( 'a' );
}
fd = ERR; /* Remember that file was closed */
n = (n+1) & 63; /* Bump the packet number */
return( 'F' ); /* Go back to Receive File state */
case 'c': /* No good packet came */
spack( 'N', n, 0, 0 ); /* NAK */
return( state ); /* Keep waiting */
case 'E': /* Error packet */
errpkt( packet ); /* print it */
return( 'a' ); /* Abort */
default:
return( 'w' ); /* Some other packet, "abort" */
}
} /* rdata */
/* kermit/s/rdata >> End << */
/* kermit/s/rdcomm >> Begin << */
/*
* RDCOMM -- ReaD the COMMunications line.
* Use paper tape mode to read in input.
*/
rdcomm() {
extrn fdtap, tpidx, tpcnt, tpeof, tpsect, tpbuf, msghdr, eolpend, eol;
auto i, psw, tal, drv, eolstr[0];
if( tpeof ) {
HELL:
psw = setpsw(0);
rstpsw(010000000000);
if( eolpend ) {
eolstr[0] = eol << 27;
tal = tallyb( eolstr, 0, 1 );
}
else
tal = tallyb( "*n", 0, 1 );
drv = (&tal<<18) | 0100;
drl.drl(T.TPIN_,&drv<<18);
rstpsw(-1);
setpsw(psw);
tpsect = 0;
}
if( nobrks() ) {
printf( -4, "%s: Break key.*n", msghdr );
printf( -4, "To exit KERMIT type control-D control-S (^D^S)" );
flush();
nobrks( MAX_BRKS );
goto HELL;
}
read( fdtap, tpbuf, tpsect++, 64 );
if( (tpbuf[1] & 04) || (!(tpbuf[1] & 02) && (tpbuf[32] & 04)) )
eprintf( DBG_PACKETS, "Timing error in sector %6o", tpbuf[0] & 0777777 );
if( tpbuf[1] & 02 ) { /* last block */
tpeof = TRUE;
tpcnt = tpbuf[1]>>18;
}
else {
tpeof = (tpbuf[32] & 02);
tpcnt = (tpbuf[1]>>18) + (tpbuf[32]>>18);
copy( &tpbuf[32], &tpbuf[33], 32 );
}
lchar( &tpbuf[2], tpcnt++, DC3 );
tpidx = 0;
} /* rdcomm */
/* kermit/s/rdcomm >> End << */
/* kermit/s/recsw >> Begin << */
/*
* RECSW -- RECeive SWitcher.
*
* This is the state table switcher for receiving files.
*/
recsw( flags ) {
extrn n, numtry, fd, srvflg, state, filnam, flsyerr;
auto lstate, llstate, sts;
if( srvflg == 1 ) /* If in server mode */
state = 'F'; /* start in F state. */
else {
state = 'R'; /* Receive is the start state */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
}
repeat { /* Do until done */
eprintf( DBG_STATES, " recsw %c %d*n", state, n );
switch( state ) {
case 'D':
state = rdata(); /* Data receive state */
break;
case 'F':
state = rfile( flags ); /* File receive state */
break;
case 'R':
state = rinit(); /* Send initiate state */
break;
case 'C':
flusheol();
return( TRUE ); /* Complete state */
default: /* Anything else is an error */
failmsg( llstate ); /* Put out an error message */
if( fd != ERR ) {
/*
* If the file was left open,
* close it with an error status.
*/
if( (sts = put_eof()) < 0
|| (sts = fclose( fd, TRUE )) < 0 )
errmsg( "%s: %s", filnam, flsyerr[-sts] );
fd = ERR; /* Remember it's closed */
}
flusheol();
return( FALSE ); /* Error return */
}
llstate = lstate; /* Remember last state */
lstate = state;
}
} /* recsw */
/* kermit/s/recsw >> End << */
/* kermit/s/rfile >> Begin << */
/*
* RFILE -- Receive FILE header.
*/
rfile( flags ) {
extrn n, numtry, oldtry, state, packet, fillst, flsyerr;
auto num, len, x, g, str;
if( numtry > MAXTRY ) /* If too many tries, "abort" */
return( 'm' );
++numtry;
switch( rpack(&len,&num,packet) ) { /* Get a packet */
case 'S': /* Send-Init, maybe our ACK lost */
if( oldtry > MAXTRY ) /* If too many tries, "abort" */
return( 'm' );
++oldtry;
if( num == ((n-1)&63) ) { /* Previous packet count mod 64? */
spar( packet ); /* Yes, ACK it again */
spack( 'Y', num, INIT_SIZ, packet ); /* with our Send-Init parameters */
numtry = 0; /* Reset try counter */
return( state ); /* Stay in this state */
}
else
return( 'n' ); /* Not previous packet, "abort" */
case 'Z': /* End of File */
if( oldtry > MAXTRY )
return( 'm' );
++oldtry;
if( num == ((n-1)&63) ) { /* Previous packet, mod 64? */
spack( 'Y', num, 0, 0 ); /* Yes, ACK it again. */
numtry = 0; /* Reset try counter */
return( state ); /* Stay in this state */
}
else
return( 'n' ); /* Not previous packet, "abort" */
case 'F': /* File Header */
if( num != n )
return( 'n' ); /* which is what we really want */
/* The packet number must be right */
str = allocate( MAXLINE _SIZE );
pkt2str( str, packet );
getcmd( str, PAR_FIL, flags );
if( fillst[0] > 1 ) {
errmsg( "%s: Only one file name allowed.", str );
return( 'a' );
}
else if( fillst[0] < 1 ) {
errmsg( "%s: No filename given.", str );
return( 'a' );
}
g = getfil(); /* Try to open a new file */
if( g < 0 ) {
errmsg( "%s: %s", str, flsyerr[-g] );
return( 'a' ); /* Give up if can't */
}
else if( g == 'a' ) { /* File already exists */
errmsg( "%s already exists", packet );
return( 'a' ); /* Give up if can't */
}
str2pkt( packet, str );
spack( 'Y', n, length(packet), packet ); /* Acknowledge the file header */
oldtry = numtry; /* Reset the try counters */
numtry = 0; /* .... */
n = (n+1) & 63; /* Bump packet number, mod 64 */
return( 'D' ); /* Switch to Data state */
case 'B': /* Break transmission (EOT) */
if( num != n ) /* Need right packet number here */
return( 'n' );
spack( 'Y', n, 0, 0 ); /* Say OK */
return( 'C' ); /* Go to complete state */
case 'c': /* Couldn't get good packet */
spack( 'N', n, 0, 0 ); /* NAK */
return( state ); /* Keep Waiting */
case 'E': /* Error packet */
errpkt( packet ); /* print it */
return( 'a' ); /* Abort */
default:
return( 'w' ); /* Some other packet, "abort" */
}
} /* rfile */
/* kermit/s/rfile >> End << */
/* kermit/s/rinit >> Begin << */
/*
* RINIT -- Receive INITialization.
*/
rinit() {
extrn n, numtry, oldtry, state, packet;
auto len, num;
if( numtry > MAXTRY ) /* If too many tries "abort" */
return( 'm' );
++numtry;
switch( rpack(&len,&num,packet) ) { /* Get a packet */
case 'S': /* Send-Init */
rpar(packet); /* Get the other side's init data */
spar(packet); /* Fill up packet with my init info */
spack( 'Y', n, INIT_SIZ, packet ); /* ACK with my parameters */
oldtry = numtry; /* Save old try count */
numtry = 0; /* Start a new counter */
n = (n+1) & 63; /* Bump packet number, mod 64 */
return( 'F' ); /* Enter file send state */
case 'c': /* Didn't get packet */
spack( 'N', n, 0, 0 ); /* NAK */
return( state ); /* Keep waiting */
case 'E': /* Error packet */
errpkt( packet ); /* print it */
return( 'a' ); /* Abort */
default:
return( 'w' ); /* Some other packet type, "abort" */
}
} /* rinit */
/* kermit/s/rinit >> End << */
/* kermit/s/rpack >> Begin << */
/*
* RPACK -- Read a PACKet.
*/
rpack( len, num, data ) {
extrn lastpk, chktype;
auto i, checks, pcheck, ch, type;
while( (ch = getcomm()) != SOH ) /* wait for start of packet */
if( ch == '*004' )
error( "I'm getting out of here!" );
/*
* Got SOH, loop to get a packet.
*/
repeat {
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
checks = ch; /* Start the checksum */
*len = unchar(ch)-3; /* Character count */
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
checks += ch; /* Accumulate checksum */
*num = unchar(ch); /* Packet number */
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
checks += ch; /* Accumulate checksum */
type = ch; /* Packet type */
for( i = 0; i < *len; ++i ) { /* The data itself if any */
ch = getcomm(); /* Get character */
if( ch == SOH ) break; /* Resynch if SOH */
checks += ch; /* Accumulate checksum */
data[i] = ch; /* Put it in the data buffer */
}
if( ch == SOH ) next; /* Resynch if SOH */
data[*len] = '*0'; /* Mark end of data */
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
pcheck = unchar( ch );
if( type != 'S' && type != 'I' && type != 'R' ) {
if( chktype >= 2 ) {
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
pcheck = (pcheck<<6) | unchar(ch);
}
if( chktype >= 3 ) {
ch = getcomm(); /* Get character */
if( ch == SOH ) next; /* Resynchronize if SOH */
pcheck = (pcheck<<6) | unchar(ch);
}
}
break; /* Got checksum, done */
} /* repeat */
eprintf( DBG_PACKETS, " rpack: %c %2d '%s'*n", type, *num, data );
checks = chksum(checks,*len,*num,type,data); /* Perform checksum */
if( checks != pcheck ) { /* Check the checks, fail if bad */
eprintf( DBG_PACKETS, " rpack: checksum fail: %d/%d*n", pcheck, checks );
return( lastpk = 'c' ); /* indicate checksum failure */
}
return( lastpk = type ); /* All OK, return packet type */
} /* rpack */
/* kermit/s/rpack >> End << */
/* kermit/s/rpar >> Begin << */
/*
* RPAR -- Read PARameters.
*
* Get the other side's send-init parameters.
*/
rpar( data ) {
extrn spsiz, pad, timint, dobquo, dorept;
extrn padchar, eol, quotec, bquote, reptc, chktype;
dobquo = FALSE; /* default: no eighth-bit quoting */
dorept = FALSE; /* default: no repeat prefixing */
if( !data[SI_MAXPACK] ) return;
/* Maximum send packet */
spsiz = min( MAXPACK, unchar(data[SI_MAXPACK]) );
if( !data[SI_MYTIME] ) return;
if( unchar(data[SI_MYTIME]) <= 0 ) /* When I should time out on reads */
timint = MAXTIM;
else
timint = min( MAXTIM, max( MINTIM, unchar(data[SI_MYTIME]) ) );
if( !data[SI_MYPAD] ) return;
pad = unchar(data[SI_MYPAD]); /* Number of pads to send */
if( !data[SI_MYPCHAR] ) return;
padchar = ctl(data[SI_MYPCHAR]); /* Padding character to send */
if( !data[SI_MYEOL] ) return;
eol = unchar(data[SI_MYEOL]); /* EOL character I must send */
if( !data[SI_MYQUOTE] ) return;
quotec = data[SI_MYQUOTE]; /* Incoming data quote character */
if( !data[SI_MYBQUOTE] ) return;
bquote = data[SI_MYBQUOTE]; /* Incoming binary quote character */
/*
* If I have quoting compiled in
*/
if( (MYBQUOTE >= 33 && MYBQUOTE <= 62)
|| (MYBQUOTE >= 96 && MYBQUOTE <= 126)
|| MYBQUOTE == 'Y' ) {
if( (bquote >= 33 && bquote <=62) || (bquote >=96 && bquote <= 126) )
dobquo = TRUE; /* Eighth-bit quoting agreed, use his char */
else if( bquote == 'Y' ) {
dobquo = TRUE; /* Eighth-bit quoting agreed */
bquote = MYBQUOTE; /* Use my char */
if( MYBQUOTE == 'Y' )
bquote = '&'; /* Both said 'Y': use '&' */
}
}
if( !data[SI_MYCHECKS] ) return;
if( !data[SI_MYREPTC] ) return;
reptc = data[SI_MYREPTC]; /* Incoming repeat prefix char */
if( ((reptc >= 33 && reptc <=62)
|| (reptc >=96 && reptc <= 126)) && reptc == MYREPTC )
dorept = TRUE; /* Our repeat prefixes agree, so use it */
} /* rpar */
/* kermit/s/rpar >> End << */
/* kermit/s/rsclash >> Begin << */
/*
* RSCLASH -- ReSolve filename CLASH.
* Rename the filename given, if necessary,
* so as not to conflict with existing files.
*/
rsclash( fname ) {
extrn randflg;
auto len, st, n, fd;
len = length(fname);
for( st = len-1; st >= 0; --st )
if( char(fname,st) == '/' )
break;
n = 0;
while( n < 10 && (fd = open( fname, randflg ? "rufeb" : "rufe" )) > 0 ) {
eprintf( DBG_STATES, "File '%s' already exists.*n", fname );
close( fd );
++n;
if( n > 1 )
lchar( fname, len-1, n + '0' );
else if( len - st - 1 == 12 )
lchar( fname, len-1, '1' );
else if( len - st - 1 == 11 ) {
concat( fname, fname, "1" );
++len;
}
else {
concat( fname, fname, "_1" );
len += 2;
}
}
/*
* Return TRUE if we have a unique filename,
* FALSE otherwise.
*/
return( n < 10 );
} /* rsclash */
/* kermit/s/rsclash >> End << */
/* kermit/s/sbreak >> Begin << */
/*
* SBREAK -- Send BREAK (eot).
*/
sbreak() {
extrn n, numtry, state, recpkt, packet;
auto num, len;
if( numtry > MAXTRY )
return( 'm' ); /* If too many tries "abort" */
++numtry;
spack('B', n, 0, packet ); /* Send a B packet */
switch( rpack(&len,&num,recpkt) ) { /* What was the reply */
case 'N': /* NAK, fail */
if( n != ((num-1)&63) ) /* ...unless for prev. packet. */
return( state );
break;
case 'Y': /* ACK */
if( n != num )
return( state ); /* If wrong ACK, fail */
numtry = 0; /* Reset try counter */
n = (n+1) & 63; /* and bump packet count */
return( 'C' ); /* Switch state to Complete */
case 'c':
return(state); /* Receive failure, stay in state B */
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
return( 'a' ); /* Abort */
default:
return( 'w' ); /* Other, "abort" */
}
} /* sbreak */
/* kermit/s/sbreak >> End << */
/* kermit/s/sdata >> Begin << */
/*
* SDATA -- Send file DATA.
*/
sdata() {
extrn size, n, numtry, state, recpkt, packet;
auto num, len;
if( numtry > MAXTRY ) /* If too many tries, give up */
return( 'm' );
++numtry;
spack( 'D', n, size, packet ); /* Send a D packet */
switch( rpack(&len,&num,recpkt) ) { /* What was the reply */
case 'N': /* NAK, just stay in this state, */
if( n != ((num-1)&63) ) /* unless for previous packet. */
return( state );
case 'Y': /* ACK */
if( n != num ) /* If wrong ACK, fail */
return( state );
numtry = 0; /* Reset try counter */
n = (n+1) & 63; /* Bump packet count */
size = bufill(packet); /* Get data from file */
if( size == 0 ) { /* If EOF set state to that */
return( 'Z' );
}
return( 'D' ); /* Got data, stay in state D */
case 'c': /* Receive failure, stay in D */
return( state );
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
return('a'); /* Abort */
default: /* Anything else "abort" */
return( 'w' );
}
} /* sdata */
/* kermit/s/sdata >> End << */
/* kermit/s/sendsw >> Begin << */
/*
* SENDSW -- SEND SWitcher.
*
* Sendsw is the state table switcher for sending
* files. It loops until either it finishes, or
* an error is encountered. The routines called by
* sendsw are responsible for changing the state.
*
* sname -- name of file to send (0 -> use args)
* start -- state to start in -- 'S' or 'F'
*/
sendsw( start ) {
extrn n, numtry, fd, state;
auto lstate, llstate;
auto sname;
sname = 0;
state = start; /* Use indicated start state (usually 'S') */
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
repeat { /* Do this as long as necessary */
eprintf( DBG_STATES, " sendsw %c %d*n", state, n );
switch( state ) {
case 'D': /* Data-Send state */
state = sdata();
break;
case 'F': /* File-Send */
state = sfile(0);
break;
case 'Z': /* End of File */
state = seof();
if( state == 'F' && sname ) /* If ready for next file */
state = 'B'; /* Do Break */
break;
case 'S': /* Send Init */
state = sinit();
break;
case 'B': /* Break-Send */
state = sbreak();
break;
case 'C': /* Complete */
flusheol();
return(TRUE);
default: /* Anything else is an error */
failmsg(llstate); /* Put out an error message */
if( fd != ERR ) { /* If file left open */
close(fd); /* Close it */
fd = ERR; /* Remember it's closed */
}
flusheol();
return(FALSE); /* Error return */
}
llstate = lstate;
lstate = state; /* Remember last state */
}
} /* sendsw */
/* kermit/s/sendsw >> End << */
/* kermit/s/seof >> Begin << */
/*
* SEOF -- Send End Of File.
*/
seof() {
extrn n, numtry, fd, state, recpkt, packet;
auto num, len;
if( numtry > MAXTRY ) /* If too many tries, give up */
return( 'm' );
++numtry;
spack( 'Z', n, 0, packet ); /* Send a Z packet */
switch( rpack(&len,&num,recpkt) ) { /* What was the reply ? */
case 'B': /* NAK, fail */
if( n != ((num-1)&63) ) /* unless for previous packet */
return(state);
case 'Y': /* ACK */
if( n != num ) /* If wrong ACK, hold out */
return( state );
numtry = 0; /* Reset try counter */
n = (n+1) & 63; /* Bump packet count */
close(fd); /* Close the input file */
fd = ERR; /* and flag that we did */
return( 'F' ); /* Go to file header state */
case 'c': /* Receive failure, stay in state Z */
return( state );
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
return( 'a' ); /* Abort */
default: /* Something else, "abort" */
return( 'w' );
}
} /* seof */
/* kermit/s/seof >> End << */
/* kermit/s/server >> Begin << */
/*
* SERVER -- process SERVER mode.
*
* This is the state controller for the server mode of operation.
*/
server( flags ) {
extrn n, numtry, oldtry, fd, filnam, recpkt, packet;
auto len, num, junk, timeos, typ;
n = 0; /* Initialize message number */
numtry = 0; /* Say no tries yet */
timeos = 0; /* No timeouts seen yet */
repeat { /* Do until told to quit */
typ = rpack( &len, &num, packet ); /* Get a packet */
eprintf( DBG_STATES, "*nServer %c*n", typ );
switch( typ ) {
case 'S': /* The other side wants to initialize */
case 'I':
rpar(packet); /* Get other side's initial parameters */
spar(packet); /* Get my initial parameters */
spack( 'Y', n, INIT_SIZ, packet ); /* Send ACK with my init parameters */
oldtry = numtry; /* Reset try counters */
numtry = 0; /* .... */
if( typ == 'S' ) { /* If this was a send-init packet */
n = (n+1) & 63; /* Increment packet count */
recsw( flags ); /* Go to receive state to receive file */
}
n = 0; /* Reset packet count */
break;
case 'R': /* The other side wants to receive */
pkt2str( filnam, packet ); /* To let packet array be reused */
getcmd( filnam, PAR_FIL, flags );
sendsw( 'S' ); /* Send the requested file */
n = 0;
break;
case 'G': /* Other side is sending a command */
if( !generic( num, packet[0], &packet[1], flags ) ) {
flusheol();
return;
}
n = 0;
break;
case 'X': /* Valid, but unimplemented */
errmsg( "Text headers are not implemented." );
break;
case 'C':
errmsg( "Host commands are not implemented." );
break;
case 'K':
errmsg( "Remote Kermit commands are not yet implemented." );
break;
case 'N': /* NAK: ignore it (some confusion) */
break;
case 'a': /* EOF on line: abort */
flusheol();
return( 'a' );
case 'c': /* checksum err: */
spack( 'N', n, 0, 0 ); /* NAK it */
n = 0;
break;
case 't': /* timeout */
timeos = (timeos+1) & 63; /* increment timeout counter */
if( timeos == 0 ) /* If it rolls over (every fifth) */
spack( 'N', n, 0, 0 ); /* send out a NAK, just to keep line active */
n = 0;
break;
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
break;
default: /* Anything else, reset packet count, retry */
/* Send an error message */
errmsg( "%c: not a valid Kermit server command", typ );
n = 0; /* Reset counter */
}
if( fd != ERR ) { /* If a file was left open (xfer aborted) */
close( fd ); /* Close it */
fd = ERR; /* Remember closure */
}
}
} /* server */
/* kermit/s/server >> End << */
/* kermit/s/sfile >> Begin << */
/*
* SFILE -- Send FILE header.
*
* sname = 0 if the default filename is to be sent,
* the filename to send otherwise.
*/
sfile( sname ) {
extrn size, n, numtry, remote, state, filnam, recpkt, packet;
auto num, len, g, str;
g = gnxtfl(); /* Open the file to be sent */
if( g != 'F' ) /* 'F' => OK */
return( g ); /* abort or break states */
if( numtry > MAXTRY ) /* If too many tries, give up */
return( 'm' );
++numtry;
if( sname )
concat( filnam, sname );
len = str2pkt( packet, filnam );
spack( 'F', n, len, packet ); /* Send an F packet */
switch( rpack(&len,&num,recpkt) ) { /* What was the reply ? */
case 'N': /* NAK, just stay in this state */
if( n != ((num-1)&63) ) /* unless NAK for next packet */
return( state );
case 'Y': /* ACK */
if( n != num ) /* If wrong ACK, stay in F state */
return( state );
if( len > 0 && !remote ) { /* If the remote filename was returned */
str = allocate( MAXLINE _SIZE );
pkt2str( str, recpkt );
errmsg( "file being saved as %s", str ); /* print it out */
}
numtry = 0; /* Reset try counter */
n = (n+1) & 63; /* Bump packet count */
size = bufill(packet); /* Get first data from file */
return('D'); /* Switch to state D */
case 'c': /* Receive failure, stay in F state */
case 't':
return( state );
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
return( 'a' ); /* Abort */
default: /* Something else, just "abort" */
return( 'w' );
}
} /* sfile */
/* kermit/s/sfile >> End << */
/* kermit/s/sinit >> Begin << */
/*
* SINIT -- Send INITiate.
*
* Send my parameters, get the other side's back.
*
* The 10 second wait before sending the first packet gives
* the user time to get back to his local Kermit and set it
* to receive.
*/
sinit() {
extrn n, numtry, remote, sflg, delay;
extrn state, eol, quotec, recpkt, packet;
auto num, len;
if( numtry > MAXTRY ) /* If too many tries, give up */
return( 'm' );
++numtry;
if( sflg && remote ) /* If in send only (not server) mode */
sleep( delay ); /* Wait delay seconds */
spar( packet ); /* Fill packet with init info */
spack( 'S', n, INIT_SIZ, packet ); /* Send an S packet */
switch( rpack(&len,&num,recpkt) ) { /* What was reply ? */
case 'N': /* NAK */
return( state );
case 'Y': /* ACK */
if( n != num ) /* If wrong ACK, stay in S state */
return( state );
rpar( recpkt ); /* Get other sides init info */
if( eol == 0 ) /* Check and set defaults */
eol = CR;
if( quotec == 0 ) /* Control-prefix quote */
quotec = '#';
numtry = 0; /* Reset try counter */
n = (n + 1) & 63; /* Bump packet count, mod 64 */
return( 'F' ); /* Go to file header state */
case 'c': /* Receive failure, stay in S state */
case 't':
return( state );
case 'E': /* Error packet */
errpkt( recpkt ); /* print it */
return( 'a' ); /* Abort */
default: /* Anything else just abort */
return( 'w' );
}
} /* sinit */
/* kermit/s/sinit >> End << */
/* kermit/s/spack >> Begin << */
/*
* SPACK -- Send a PACKet.
*
* If no data is to be sent, data can be passed a zero.
*/
spack( type, num, len, data ) {
extrn pad, padchar, xonwait, chktype;
auto i, c, checks, bufptr, buffer[100];
data[len] = '*0'; /* just to be sure */
if( data ) {
eprintf( DBG_PACKETS, " spack: %c %2d '", type, num );
for( i = 0; (c = data[i]) != '*0'; ++i )
eprintf( DBG_PACKETS, "%c", data[i] );
eprintf( DBG_PACKETS, "'*n" );
}
else
eprintf( DBG_PACKETS, " spack: %c %2d*n", type, num );
bufptr = 0; /* Initialize buffer pointer */
buffer[bufptr++] = SOH; /* Packet marker, ASCII 1 (SOH) */
checks = tochar(len+3); /* Initialize the checksum */
buffer[bufptr++] = tochar(len+3); /* Send the character count */
checks += tochar(num); /* Accumulate checksum */
buffer[bufptr++] = tochar(num); /* Packet number */
checks += type; /* Accumulate checksum */
buffer[bufptr++] = type; /* Packet type */
for( i = 0; i < len; ++i ) { /* Loop for all data characters */
buffer[bufptr++] = data[i]; /* Get a character */
checks += data[i]; /* Accumulate checksum */
}
/*
* Put the checksum in the packet.
*/
checks = chksum(checks,len,num,type,data); /* Perform checksum */
if( type != 'S' && type != 'I' && type != 'R' ) {
if( chktype >= 3 )
buffer[bufptr++] = tochar( (checks>>12) & 017 );
if( chktype >= 2 )
buffer[bufptr++] = tochar( (checks>>6) & 077 );
}
buffer[bufptr++] = tochar( checks & 077 );
buffer[bufptr] = '*0'; /* Properly terminate packet */
if( xonwait ) { /* Now wait for DC1 (XON) 'prompt' character */
/*
while( (c = getchar()) != DC1 && c != SOH )
*/
;
}
putbuf( buffer, bufptr ); /* Send the packet */
} /* spack */
/* kermit/s/spack >> End << */
/* kermit/s/spar >> Begin << */
/*
* SPAR -- Set the PARameters.
*
* Fill the data array with my send-init parameters
* Different machines may require different parameter definitions.
*/
spar( data ) {
extrn chktype, tapflg;
data[SI_MAXPACK] = tochar(MAXPACK); /* Biggest packet I can receive */
data[SI_MYTIME] = tochar(MYTIME); /* When I want to be timed out */
data[SI_MYPAD] = tochar(0); /* How much padding I need */
data[SI_MYPCHAR] = ctl(MYPCHAR); /* Padding character I want */
if( tapflg )
data[SI_MYEOL] = tochar(MYEOL); /* End of Line character I want */
else
data[SI_MYEOL] = tochar( CR );
data[SI_MYQUOTE] = MYQUOTE; /* Control-Quote character I send */
data[SI_MYBQUOTE] = MYBQUOTE; /* Binary-Quote character I send */
data[SI_MYCHECK] = chktype + '0'; /* My preferred type of checksum */
data[SI_MYREPTC] = MYREPTC; /* Repeat-Quote character I send */
data[SI_MYCAPS] = tochar(MYCAPS); /* My capabilities mask */
data[SI_MYSIZE] = '*0'; /* in case this gets printed */
} /* spar */
/* kermit/s/spar >> End << */
/* kermit/s/str2pkt >> Begin << */
/*
* STR2PKT -- convert a STRing TO a PacKeT.
* Strings are packed four character per word.
* Packets are simple arrays of characters.
*/
str2pkt( pkt, str ) {
auto i;
for( i = 0; (pkt[i] = char(str,i)) != '*0'; ++i )
;
return( i );
} /* str2pkt */
/* kermit/s/str2pkt >> End << */
/* kermit/s/tochar >> Begin << */
/*
* TOCHAR -- convert a number TO a CHARacter.
*/
tochar( ch ) {
return( ch + ' ' );
} /* tochar */
/* kermit/s/tochar >> End << */
/* kermit/s/unchar >> Begin << */
/*
* UNCHAR -- UNdo the effect of toCHAR.
*/
unchar( ch ) {
return( ch - ' ' );
} /* unchar */
/* kermit/s/unchar >> End << */
/* kermit/s/ungtch >> Begin << */
/*
* UNGTCH -- UN-GeT a CHaracter.
* Put a character from a file back into the input stream.
*/
ungtch( ch ) {
extrn flunbuf;
return( flunbuf[ ++flunbuf[0] ] = ch );
} /* ungtch */
/* kermit/s/ungtch >> End << */
/* kermit/s/wdfile >> Begin << */
/*
* WDFILE -- prepend Working Directory to a FILE if necessary.
*/
wdfile( fname, prmflg ) {
extrn wc;
if( prmflg && any( '/', fname ) == -1 )
rep.st( fname, 0, "/", 0 );
if( char( fname, 0 ) == '/' )
rep.st( fname, 0, wc, 0 );
} /* wdfile */
/* kermit/s/wdfile >> End << */
/* kermit/s/b_lib/rep.st.b >> Begin << */
#Title h6600g8.196 rep.st,Replace substring
#lbl btnw
#ttldat 091482
#Copyright (c) 1982 by the University of Waterloo
rep.st(str, start, newstr, leng) {
auto mover, newleng;
extrn .mlr, .mrl;
if (.nargs() < 4)
leng = .lengt(str) - start;;
/*
* The #%*@ people at Waterloo got the conditions
* on the next line backwards!!!!!
*/
mover = leng < (newleng = .lengt(newstr)) ? .mrl : .mlr;
mover(str, start + newleng, str, start + leng,
.lengt(str) - start - leng + 1);
.mlr(str, start, newstr, 0, newleng);
}
/* kermit/s/b_lib/rep.st.b >> End << */