char *userv = "User Interface 4LC(001), 01 Jul 85"; /* C K U U S R -- "User Interface" for PC-DOS Kermit */ /* F. da Cruz, Columbia University Center for Computing Activities */ /* Modified for use with PC-DOS by: Jan A. van der Eijk, NUS Corp., July 1985 */ /* Copyright (C) 1985, Trustees of Columbia University in the City of New York. Permission is granted to any individual or institution to use, copy, or redistribute this software so long as it is not sold for profit, provided this copyright notice is retained. */ /* The ckuser module contains the terminal input and output functions for MS-DOS Kermit. It includes a simple Unix-style command line parser as well as an interactive prompting keyword command parser. It depends on the existence of 'C' facilities like fopen, fgets, feof, (f)printf, argv/argc, etc. Other functions that are likely to vary among 'C' implementations -- like setting terminal modes or interrupts -- are invoked via calls to functions that are defined in the system-dependent modules, ck?[ft]io.c. The command line parser processes any arguments found on the command line, as passed to main() via argv/argc. The interactive parser uses the facilities of the cmd package (developed for this program, but usable by any program). Any command parser may be substituted for this one. The only requirements for the Kermit command parser are these: 1. Set parameters via global variables like duplex, speed, ttname, etc. See ckmain.c for the declarations and descriptions of these variables. 2. If a command can be executed without the use of Kermit protocol, then execute the command directly and set the variable sstate to 0. Examples include 'set' commands, local directory listings, the 'connect' command. 3. If a command requires the Kermit protocol, set the following variables: sstate string data 'v' (enter receive mode) cmarg2 's' (send files) nfils, cmarg & cmarg2 OR cmlist cmlist is an array of pointers to strings. cmarg, cmarg2 are pointers to strings. nfils is an integer. cmarg can be a filename string (possibly wild), or a pointer to a prefabricated generic command string, or a pointer to a host command string. cmarg2 is the name to send a single file under, or the name under which to store an incoming file; must not be wild. cmlist is a list of nonwild filenames, such as passed via argv. nfils is an integer, interpreted as follows: -1: argument string is in cmarg, and should be expanded internally. >0: number of files to send, from cmlist. The screen() function is used to update the screen during file transfer. The tlog() function maintains a transaction log. The debug() function maintains a debugging log. The intmsg() and chkint() functions provide the user i/o for interrupting file transfers. */ /* Includes */ #include "stdios.h" #include #include "lckdeb.h" #include "lckerm.h" #include "lckcmd.h" #include "lckusr.h" #define KERMRC "kermit.ini" /* External Kermit Variables, see ckmain.c for description. */ extern int size, spsiz, rpsiz, npad, timint, rtimo, speed, local, server, displa, binary, fncnv, delay, parity, deblog, escape, xargc, flow, turn, duplex, cxseen, czseen, nfils, ckxech, pktlog, seslog, tralog, turnch, chklen, bctr, bctu, dfloc, mdmtyp, cdetect, rptflg, ebqflg, warn, quiet, cnflg, timef, mypadn, sldwnd, wndsiz, wndmax; extern long filcnt, tlci, tlco, ffc, tfc, fsize; extern char *versio, *protv, *ckxv, *ckzv, *fnsv, *connv, *dftty, *cmdv; extern char *ckxsys, *ckzsys, *cmarg, *cmarg2, **xargv, **cmlist; extern char mystch, stchr, sstate, mypadc, padch, eol, seol, ctlq, filnam[], ttname[]; extern char *DIRCMD, *PWDCMD; char *strcpy(), *getenv(); /* Declarations from cmd package */ extern char cmdbuf[]; /* Command buffer */ /* Declarations from ck?fio.c module */ extern char *SPACMD, *zhome(); /* Space command, home directory. */ extern int backgrd; /* Kermit executing in background */ /* The background flag is set by ckutio.c (via conint() ) to note whether */ /* this kermit is executing in background ('&' on shell command line). */ /* Variables and symbols local to this module */ char line[CMDBL+10], *lp; /* Character buffer for anything */ char debfil[50]; /* Debugging log file name */ char pktfil[50]; /* Packet log file name */ char sesfil[50]; /* Session log file name */ char trafil[50]; /* Transaction log file name */ int n, /* General purpose int */ cflg, /* Command-line connect cmd given */ action, /* Action selected on command line*/ repars, /* Reparse needed */ tlevel, debugp = 0, /* Debug print */ /* Take command level */ cwdf = 0; /* CWD has been done */ char *homdir; /* Pointer to home directory string */ char cmdstr[100]; /* C M D L I N -- Get arguments from command line */ /* Simple Unix-style command line parser, conforming with 'A Proposed Command Syntax Standard for Unix Systems', Hemenway & Armitage, Unix/World, Vol.1, No.3, 1984. */ cmdlin() { char x; cmarg = ""; /* Initialize. */ cmarg2 = ""; action = cflg = 0; while (--xargc > 0) { /* Go through command line words */ xargv++; debug(F111,"xargv",*xargv,xargc); if (**xargv == '-') { /* Got an option (begins with dash) */ x = *(*xargv+1); /* Get the option letter */ x = doarg(x); /* Go handle the option */ if (x < 0) exit(GOOD_EXIT); } else { /* No dash where expected */ usage(); exit(BAD_EXIT); } } debug(F101,"action","",action); if (!local) { if ((action == 'g') || (action == 'r') ) fatal("-l and -b required"); } if (*cmarg2 != 0) { if ((action != 's') && (action != 'r') && (action != 'v')) fatal("-a without -s, -r, or -g"); } if ((action == 's') || (action == 'v') || (action == 'r') || (action == 'x')) { if (local) displa = 1; } if (quiet) displa = 0; /* No display if quiet requested */ return(action); /* Then do any requested protocol */ } /* D O A R G -- Do a command-line argument. */ doarg(x) char x; { int z; char *xp; xp = *xargv+1; /* Pointer for bundled args */ while (x) { switch (x) { case 'f': if (action) fatal("conflicting actions"); action = setgen('F',"","",""); break; case 'r': /* receive */ if (action) fatal("conflicting actions"); action = 'v'; break; case 's': /* send */ if (action) fatal("conflicting actions"); if (*(xp+1)) fatal("invalid argument bundling after -s"); nfils = 0; /* Initialize file counter, flag */ cmlist = xargv+1; /* Remember this pointer */ while (--xargc > 0) { /* Traverse the list */ *xargv++; if (**xargv == '-') break; /* Check for '-' */ nfils++; /* Bump file counter */ } xargc++, *xargv--; /* Adjust argv/argc */ if (nfils < 1) fatal("missing filename for -s"); if ( nfils == 1) { /* Incase wild cards were specified */ cmarg = *cmlist; nfils = -1; } debug(F101,*xargv,"",nfils); action = 's'; break; /* cont'd... */ /* ...doarg(), cont'd */ case 'h': /* help */ usage(); return(-1); case 'c': cdetect = TRUE; break; case 'a': /* "as" */ if (*(xp+1)) fatal("invalid argument bundling after -a"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing name in -a"); cmarg2 = *xargv; break; case 'l': /* set line */ if (*(xp+1)) fatal("invalid argument bundling after -l"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("communication line device name missing"); /* strcpy(ttname,*xargv); */ zrtol(*xargv,ttname); /* if (strcmp(ttname,dftty) == 0) local = dfloc; else local = 1; */ local = (strcmp(ttname,CTTNAM) != 0); /* (better than old way) */ debug(F101,"local","",local); ttopen(ttname,&local,0); break; case 'b': /* set baud */ if (*(xp+1)) fatal("invalid argument bundling"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing baud"); z = atoi(*xargv); /* Convert to number */ if (chkspd(z) > -1) speed = z; /* Check it */ else fatal("unsupported baud rate"); break; case 'n': /* Treat files as non-binary */ binary = 0; break; /* cont'd... */ /* ...doarg(), cont'd */ case 'w': /* File warning */ warn = 0; break; case 'q': /* Quiet */ quiet = 1; break; case 'd': /* debug */ debugp = 1; #ifdef DEBUG debugp = 0; #endif debopn("debug.log"); break; case 'p': /* set parity */ if (*(xp+1)) fatal("invalid argument bundling"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing parity"); switch(x = **xargv) { case 'e': case 'o': case 'm': case 's': parity = x; break; case 'n': parity = 0; break; default: fatal("invalid parity"); } break; case 'm': /* set window size */ if (*(xp+1) || duplex ) fatal("invalid argument bundling"); *xargv++, xargc--; if ((xargc < 1) || (**xargv == '-')) fatal("missing window size"); z = atoi(*xargv); /* Convert to number */ if ( z > MYWND || z < 0 ) /* Check it */ fatal("invalid window size"); else wndmax = z; break; case 't': turn = 1; /* Line turnaround handshake */ turnch = XON; /* XON is turnaround character */ duplex = 1; /* Half duplex */ flow = 0; /* No flow control */ wndsiz = 0; /* No windowing */ sldwnd = 0; break; default: fatal("invalid argument, type 'kermit -h' for help"); } x = *++xp; /* See if options are bundled */ } return(0); } static char *hlp1[] = { "\n", " Kermit Receive and Send protocol module for MS-DOS operating system.\n", " Jan A. van der Eijk, NUS Corp. July 1985. \n", " PCKERMIT version 1.18, December 10, 1985 \n", " Usage: PCKERMIT [-x arg [-x arg]...[-yyy]..]]\n", " x is an option that requires an argument, y an option with no argument:\n", " -s file(s) send \n", " -r receive\n", " -a name alternate name, used with -s, -r\n", " -h help - print this message\n", settings -- default\n -l line communication line device ( COM1, COM2 ) [COM1]\n -b baud line speed, e.g. 1200 [1200]\n -p x parity, x is one of e,o,m,s,n [N] \n -m size maximum window size to use ( <= 31 ) [31] \n " -n non binary file transfer, affect CR/LF \n", " -t line turnaround handshake = xon, half duplex\n", " -w write over preexisting files\n", " -q be quiet during file transfer\n", " -c carrier detect signal required to be present\n", }; /* U S A G E */ usage() { conola(hlp1); } /* Misc */ fatal(msg) char *msg; { /* Fatal error message */ fprintf(stderr,"\r\nFatal: %s\n",msg); tlog(F110,"Fatal:",msg,0l); doexit(BAD_EXIT); /* Exit indicating failure */ } /* D O E X I T -- Exit from the program. */ doexit(exitstat) int exitstat; { sleep(2); ttclos(); /* Close external line, if any */ if (local) { strcpy(ttname,dftty); /* Restore default tty */ local = dfloc; /* And default remote/local status */ } if (deblog) { /* Close any open logs. */ debug(F100,"Debug Log Closed","",0); *debfil = '\0'; deblog = 0; zclose(ZDFILE); } if (tralog) { tlog(F100,"Transaction Log Closed","",0l); *trafil = '\0'; tralog = 0; zclose(ZTFILE); } exit(exitstat); /* Exit from the program. */ } /* B L D L E N -- Make length-encoded copy of string */ char * bldlen(str,dest) char *str, *dest; { int len; len = strlen(str); *dest = tochar(len); strcpy(dest+1,str); return(dest+len+1); } /* S E T G E N -- Construct a generic command */ setgen(type,arg1,arg2,arg3) char type, *arg1, *arg2, *arg3; { char *upstr, *cp; cp = cmdstr; *cp++ = type; *cp = NUL; if (*arg1 != NUL) { upstr = bldlen(arg1,cp); if (*arg2 != NUL) { upstr = bldlen(arg2,upstr); if (*arg3 != NUL) bldlen(arg3,upstr); } } cmarg = cmdstr; debug(F110,"setgen",cmarg,0); return('g'); } /* D E B O P N -- Open a debugging file */ debopn(s) char *s; { #ifdef DEBUG char *tp; zclose(ZDFILE); deblog = zopeno(ZDFILE,s); if (deblog > 0) { strcpy(debfil,s); debug(F110,"Debug Log ",versio,0); debug(F100,ckxsys,"",0); ztime(&tp); debug(F100,tp,"",0); } else *debfil = '\0'; return(deblog); #else return(0); #endif } /* C H K S P D -- Check if argument is a valid baud rate */ chkspd(x) int x; { switch (x) { case 0: case 110: case 150: case 300: case 600: case 1200: case 1800: case 2400: case 4800: case 9600: return(x); default: return(-1); } } /* I N T M S G -- Issue message about terminal interrupts */ intmsg(n) long n; { extern char *chstr(); char buf[80]; if ((!displa) || (quiet)) return; if (n == 1) { screen(SCR_TN,0,0l,"CTRL-F to cancel File, CTRL-R to Resend current packet"); screen(SCR_TN,0,0l,"CTRL-B to cancel Batch, CTRL-K to abort Kermit and exit"); screen(SCR_TN,0,0l,"CTRL-A for status report: "); } else screen(SCR_TU,0,0l," "); } /* C H K I N T -- Check for console interrupts */ /*** should rework not to destroy typeahead ***/ chkint() { int ch, cn; if ((!local) || (quiet)) return(0); /* Only do this if local & not quiet */ cn = conchk(); /* Any input waiting? */ debug(F101,"conchk","",cn); while (cn > 0) { /* Yes, read it. */ cn--; /* give read 5 seconds for interrupt character */ if ((ch = coninc(5)) < 0) return(0); switch (ch & 0177) { case 0001: /* CTRL-A */ screen(SCR_TN,0,0l,"^A Status report:"); screen(SCR_TN,0,0l," file type: "); if (binary) screen(SCR_TZ,0,0l,"binary"); else screen(SCR_TZ,0,0l,"text"); screen(SCR_QE,0,(long)filcnt," file number"); screen(SCR_QE,0,(long)ffc, " characters "); if (sldwnd) screen(SCR_QE,0,(long)wndsiz," window size"); screen(SCR_QE,0,(long)bctu, " block check"); screen(SCR_QE,0,(long)rptflg," compression"); screen(SCR_QE,0,(long)ebqflg," 8th-bit prefixing"); continue; case 0002: /* CTRL-B */ screen(SCR_TN,0,0l,"^B - Cancelling Batch "); czseen = 1; continue; case 0013: /* CTRL-K */ screen(SCR_TN,0,0l,"^K - Aborting Kermit "); errpkt("Tranfer aborted by otherside."); czseen = 1; cxseen = 1; clsif();clsof(); doexit(BAD_EXIT); continue; case 0006: /* CTRL-F */ screen(SCR_TN,0,0l,"^F - Cancelling File "); cxseen = 1; continue; case 0022: /* CTRL-R */ screen(SCR_TN,0,0l,"^R - Resending "); resend(); return(1); default: /* Anything else, just ignore */ screen(SCR_TU,0,0l," [Ignored] "); continue; } } return(0); } /* D E B U G -- Enter a record in the debugging log */ /* Call with a format, two strings, and a number: f - Format, a bit string in range 0-7. If bit x is on, then argument number x is printed. s1 - String, argument number 1. If selected, printed as is. s2 - String, argument number 2. If selected, printed in brackets. n - Int, argument 3. If selected, printed preceded by equals sign. f=0 is special: print s1,s2, and interpret n as a char. */ #ifdef DEBUG debug(f,s1,s2,n) int f, n; char *s1, *s2; { static char s[200]; char *sp = s; if (!deblog) return; /* If no debug log, don't */ switch (f) { case F000: /* 0, print both strings, */ sprintf(sp,"%s%s%c\n",s1,s2,n); /* and interpret n as a char */ zsout(ZDFILE,s); break; case F001: /* 1, "=n" */ sprintf(sp,"=%d\n",n); zsout(ZDFILE,s); break; case F010: /* 2, "[s2]" */ sprintf(sp,"[%s]\n",s2); zsout(ZDFILE,""); break; case F011: /* 3, "[s2]=n" */ sprintf(sp,"[%s]=%d\n",s2,n); zsout(ZDFILE,s); break; case F100: /* 4, "s1" */ zsoutl(ZDFILE,s1); break; case F101: /* 5, "s1=n" */ sprintf(sp,"%s=%d\n",s1,n); zsout(ZDFILE,s); break; case F110: /* 6, "s1[s2]" */ sprintf(sp,"%s[%s]\n",s1,s2); zsout(ZDFILE,s); break; case F111: /* 7, "s1[s2]=n" */ sprintf(sp,"%s[%s]=%d\n",s1,s2,n); zsout(ZDFILE,s); break; default: sprintf(sp,"\n?Invalid format for debug() - %d\n",n); zsout(ZDFILE,s); } } #endif #ifdef TLOG /* T L O G -- Log a record in the transaction file */ /* Call with a format and 3 arguments: two strings and a number: f - Format, a bit string in range 0-7, bit x is on, arg #x is printed. s1,s2 - String arguments 1 and 2. n - Int, argument 3. */ tlog(f,s1,s2,n) int f; long n; char *s1, *s2; { static char s[200]; char *sp = s; int x; if (!tralog) return; /* If no transaction log, don't */ switch (f) { case F000: /* 0 (special) "s1 n s2" */ sprintf(sp,"%s %ld %s\n",s1,n,s2); zsout(ZTFILE,s); break; case F001: /* 1, " n" */ sprintf(sp," %ld\n",n); zsout(ZTFILE,s); break; case F010: /* 2, "[s2]" */ x = strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; sprintf(sp,"[%s]\n",s2); zsout(ZTFILE,""); break; case F011: /* 3, "[s2] n" */ x = strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; sprintf(sp,"[%s] %ld\n",s2,n); zsout(ZTFILE,s); break; case F100: /* 4, "s1" */ zsoutl(ZTFILE,s1); break; case F101: /* 5, "s1: n" */ sprintf(sp,"%s: %ld\n",s1,n); zsout(ZTFILE,s); break; case F110: /* 6, "s1 s2" */ x = strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; sprintf(sp,"%s %s\n",s1,s2); zsout(ZTFILE,s); break; case F111: /* 7, "s1 s2: n" */ x = strlen(s2); if (s2[x] == '\n') s2[x] = '\0'; sprintf(sp,"%s %s: %ld\n",s1,s2,n); zsout(ZTFILE,s); break; default: sprintf(sp,"\n?Invalid format for tlog() - %ld\n",n); zsout(ZTFILE,s); } } #endif ermsg(msg) char *msg; { /* Print error message */ if (!quiet) fprintf(stderr,"\r\n%s - %s\n","PC-KERMIT>",msg); tlog(F110,"Error -",msg,0l); } perror(s) char *s; { extern int errno; fprintf(stderr," Error %d, %s \n ",errno,s); } /* S C R E E N -- Screen display function */ /* screen(f,c,n,s) f - argument descriptor c - a character or small integer n - a long integer s - a string. Fill in this routine with the appropriate display update for the system. This version is for a dumb tty. */ screen(f,c,n,s) int f; long n; char c; char *s; { static int p = 0; /* Screen position */ int len; /* Length of string */ char buf[120]; /* Output buffer */ len = strlen(s); /* Length of string */ if (!displa || quiet) return; /* No update if display flag off */ switch (f) { case SCR_FN: /* filename */ conoll(""); conol(s); conoc(SP); p = len + 1; return; case SCR_AN: /* as-name */ if (p + len > 75) { conoll(""); p = 0; } conol("=> "); conol(s); if ((p += (len + 3)) > 78) { conoll(""); p = 0; } return; case SCR_FS: /* file-size */ sprintf(buf,", Size: %ld",n); conoll(buf); p = 0; return; case SCR_DT: if ( p != 0) conoll(""); sprintf(buf,"File creation date: %2.2s/%2.2s/%2.2s -%s ",&s[4],&s[6],&s[2],& conoll(buf); p =0; return; case SCR_XD: /* x-packet data */ conoll(""); conoll(s); p = 0; return; case SCR_ST: /* File status */ switch (c) { case ST_OK: /* Transferred OK */ if ((p += 5) > 78) { conoll(""); p = 0; } conol(" [OK]"); p += 5; return; case ST_DISC: /* Discarded */ if ((p += 12) > 78) { conoll(""); p = 0; } conol(" [discarded]"); p += 12; return; case ST_INT: /* Interrupted */ if ((p += 14) > 78) { conoll(""); p = 0; } conol(" [interrupted]"); p += 14; return; case ST_SKIP: /* Skipped */ conoll(""); conol("Skipping "); conoll(s); p = 0; return; default: conoll("*** screen() called with bad status ***"); p = 0; return; } case SCR_PN: /* Packet number */ sprintf(buf,"\n%s: %ld",s,n); conol(buf); p += strlen(buf); return; case SCR_PT: /* Packet type or pseudotype */ if (c == 'Y') return; /* Don't bother with ACKs */ if (c == 'D') { /* Only show every 4th data packet */ if (n % 4) return; c = '.'; } else if (debugp) { sprintf(buf,"\n%s: %ld",s,n); conoll(buf); p = 0; return; } if (p++ > 78) { /* If near right margin, */ conoll(""); /* Start new line */ p = 1; /* and reset counter. */ } conoc(c); /* Display the character. */ return; case SCR_TC: /* transaction complete */ conoc(BEL); return; case SCR_EM: /* Error message */ conoll(""); conoc('?'); conoll(s); p = 0; return; /* +1 */ case SCR_WM: /* Warning message */ conoll(""); conoll(s); p = 0; return; case SCR_TU: /* Undelimited text */ if ((p += len) > 78) { conoll(""); p = len; } conol(s); return; case SCR_TN: /* Text delimited at beginning */ conoll(""); conol(s); p = len; return; case SCR_TZ: /* Text delimited at end */ if ((p += len) > 78) { conoll(""); } conoll(s); p = 0; return; case SCR_QE: /* Quantity equals */ conoll(""); sprintf(buf,"%s: %ld",s,n); conoll(buf); p = 0; return; default: conoll("*** screen() called with bad object ***"); p = 0; return; } }