/* 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 *n* * *n* * The Help command prints information about the *n* * or