home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / dgmvux / dgmvux.c next >
C/C++ Source or Header  |  2020-01-01  |  52KB  |  2,004 lines

  1. /*
  2.  *  K e r m i t     File Transfer Utility
  3.  *
  4.  *  UNIX Kermit, Columbia University, 1981, 1982, 1983
  5.  *    Bill Catchings, Bob Cattani, Chris Maio, Frank da Cruz, Alan Crosswell
  6.  *
  7.  *  Also:   Jim Guyton, Rand Corporation
  8.  *        Walter Underwood, Ford Aerospace
  9.  *        David Ragozin, Univ. Washinton
  10.  *        John Sambrook, Univ. Washington, Bioengineering.
  11.  *
  12.  *  usage:  kermit c [lbe line baud escapechar]        to connect
  13.  *        kermit s [d..iflb line baud] file ...    to send files
  14.  *        kermit r [d..iflb line baud]        to receive files
  15.  *
  16.  *  where   c=connect, s=send, r=receive,
  17.  *        d=debug, i=image mode, f=no filename conversion, l=tty line,
  18.  *        b=baud rate, e=escape char.
  19.  *
  20.  *  If UCB4X then 
  21.  *
  22.  *  After first c, s, or r enters command mode with  'Kermit: ' prompt
  23.  *
  24.  *    Commands:    c[lbe line baud esc.char]     to (re)connect
  25.  *              s[diflb line baud] file ...    to send file(s)
  26.  *              r[diflb line baud]        to receive
  27.  *              ![cmd]                    for shell escape
  28.  *              q                to quit
  29.  *            ? or h                to print this list
  30.  *     NOTE:
  31.  *        In command mode l(ine) or b(aud) parameters for c,s or r 
  32.  *        need not be specified if unchanged from previous command(s).
  33.  *
  34.  *  endif UCB4X  may work on others but tested only on 4.1 and 4.1c
  35.  */
  36.  
  37. /*
  38.  *  Modification History:
  39.  *  Oct. 17 Included fixes from Alan Crosswell (CUCCA) for IBM_UTS:
  40.  *        - Changed MYEOL character from \n to \r.
  41.  *        - Change char to int in bufill so getc would return -1 on
  42.  *          EOF instead of 255 (-1 truncated to 8 bits)
  43.  *        - Added read() in rpack to eat the EOL character
  44.  *        - Added fflush() call in printmsg to force the output
  45.  *        NOTE: The last three changes are not conditionally compiled
  46.  *          since they should work equally well on any system.
  47.  *
  48.  *        Changed Berkeley 4.x conditional compilation flag from
  49.  *        UNIX4X to UCB4X.
  50.  *        Added support for error packets and cleaned up the printing
  51.  *        routines.
  52.  *
  53.  *  Feb. '84 Multiple command execution for UCB4X by David Ragozin (UW)
  54.  *
  55.  *  Jul. '85 Ported to Data General MV/UX (hosted on AOS/VS) UNIX.
  56.  *
  57.  */
  58.  
  59. #include <stdio.h>        /* Standard UNIX definitions */
  60. #include <errno.h>        /* error definitions */
  61.  
  62. /* Conditional compilation for different machines/operating systems */
  63. /* One and only one of the following lines should be 1 */
  64.  
  65. #define UCB4X        0        /* Berkeley 4.x UNIX */
  66. #define TOPS_20        0        /* TOPS-20 */
  67. #define VAX_VMS        0        /* VAX/VMS (not yet impletmented) */
  68. #define IBM_UTS        0        /* Amdahl UTS on IBM systems */
  69. #define MVUX        1        /* Data General MV/UX */
  70.  
  71. /* Conditional compilation for the different Unix variants */
  72. /* 0 means don't compile it, nonzero means do */
  73.  
  74. #if UCB4X
  75. #define V6_LIBS        0        /* Dont't use retrofit libraries */
  76. #define NO_FIONREAD 0        /* We have ioctl(FIONREAD,...) for flushinput() */
  77. #define NO_TANDEM   0        /* We have TANDEM line discipline (xon/xoff) */
  78. #endif
  79.  
  80. #if IBM_UTS
  81. #define V6_LIBS        0        /* Don't use retrofit libraries */
  82. #define NO_FIONREAD 1        /* No ioctl(FIONREAD,...) for flushinput() */
  83. #define NO_TANDEM   1        /* No TANDEM line discipline (xon/xoff) */
  84. #endif
  85.  
  86. #if MVUX
  87. #define    V6_LIBS        0        /* Don't use retrofit libraries */
  88. #define NO_FIONREAD 1        /* No ioctl(FIONREAD,...) for flushinput() */
  89. #define NO_TANDEM   1       /* No TANDEM line discipline (xon/xoff) */
  90. #define CRMOD        0        /* No CRMOD on MV/UX, so ... */
  91. #endif
  92.  
  93. #if V6_LIBS
  94. #include <retrofit/sgtty.h>
  95. #include <retrofit/signal.h>
  96. #include <retrofit/setjmp.h>
  97. #else
  98. #include <sgtty.h>
  99. #include <signal.h>
  100. #include <setjmp.h>
  101. #endif /* V6_LIBS */
  102.  
  103. #if MVUX
  104. #include <multitask.h>
  105. #include <termio.h>
  106. #endif
  107.  
  108. #if NO_TANDEM
  109. #define TANDEM        0     
  110. #endif
  111.  
  112. /*
  113. **  Manifest constants.
  114. */
  115.  
  116. #define MAXPACK        94        /* maximum packet size */
  117. #define SOH        1        /* start of header */
  118. #define BELL        7        /* ASCII bell */
  119. #define CR        13        /* ASCII carriage return */
  120. #define SP        32        /* ASCII space */
  121. #define DEL        127        /* delete (rubout) */
  122. #define ESCCHR        '^'        /* default escape character */
  123. #define MAXTRY        10        /* times to retry a packet */
  124. #define MYQUOTE        '#'        /* quote character */
  125. #define MYPAD        0        /* number of pad characters needed */
  126. #define MYPCHAR        0        /* padding character */
  127. #define SHELL        "/bin/sh"    /* default shell to use */
  128. #define    TTY        "/dev/tty"
  129.  
  130. #if IBM_UTS
  131. #define MYEOL        '\r'        /* EOL character for UTS */
  132. #else
  133. #define MYEOL        '\n'        /* EOL character for others */
  134. #endif
  135.  
  136. #define MYTIME        10        /* timeout interval */
  137. #define MAXTIM        60        /* maximum timeout interval */
  138. #define MINTIM        2        /* minimum timeout interval */
  139. #define CMDLINE        BUFSIZ        /* maximum command line size */
  140.  
  141. #define TRUE        -1        /* boolean constants */
  142. #define FALSE         0
  143.  
  144. #if MVUX
  145. #define     LISTENID    16        /* task id of listener task */
  146. #define  TALKID        17        /* task id of talker task */
  147. #define  LISTENPRI    1        /* priority of listener */
  148. #define  TALKPRI    2        /* priority of talker */
  149. #define  STACK        1024        /* default stack size */
  150. #endif
  151.  
  152. /*
  153. **  Macro definitions.
  154. */
  155.  
  156. #define tochar(ch)    ((ch) + ' ')    /* control character to printable */
  157. #define unchar(ch)    ((ch) - ' ')    /* printable to control character */
  158. #define ctl(ch)        ((ch) ^ 64 )    /* toggle control bit */
  159. #define strip(ch)    ((ch) & 0x7f)    /* strip parity (eigth) bit */
  160. #define stty(fd, p)    ioctl(fd, TIOCSETP, p)
  161. #define gtty(fd, p)    ioctl(fd, TIOCGETP, p)
  162.  
  163. /*
  164. **  Type and structure definitions.
  165. */
  166.  
  167. typedef enum _cmdtype
  168. {
  169.     cmd_connect,            /* connect to remote */
  170.     cmd_help,            /* print Kermit help */
  171.     cmd_none,            /* no valid command */
  172.     cmd_quit,            /* exit Kermit */
  173.     cmd_receive,            /* receive files */
  174.     cmd_send,            /* send files */
  175.     cmd_shell            /* UNIX shell command */
  176. } cmdtype;
  177.  
  178. /*
  179. **  Imported functions.
  180. */
  181.  
  182. extern int        fclose();    /* close stream */
  183. extern char *        fgets();    /* read string from stream */
  184. extern FILE *        fopen();    /* open stream */
  185. extern char *        getenv();    /* get environment variable */
  186. extern int        pclose();    /* close pipe from process */
  187. extern FILE *        popen();    /* open pipe to process */
  188. extern char *        strcpy();    /* copy string */
  189.  
  190. /*
  191. **  Local functions.
  192. */
  193.  
  194. static void        bufemp();    /* empty buffer of data */
  195. static int        bufill();    /* get buffer of data */
  196. static int        clkint();    /* clock handler */
  197. static int        closeline();    /* close communication line */
  198. static void        connect();    /* connect function */
  199. static void        defaults();    /* establish default characteristics */
  200. static void        doconnect();    /* execute a connect command */
  201. static void        dohelp();    /* print help menu */
  202. static void        doreceive();    /* execute a receive command */
  203. static void        dosend();    /* execute a send command */
  204. static void        doshell();    /* execute a shell command */
  205. static int        error();    /* print error message */
  206. static void        flushinput();    /* flush input queue */
  207. static int        getcmdline();    /* get next command line */
  208. static int        mapspeed();    /* map baud rate */
  209. static int        openline();    /* open communications line */
  210. static cmdtype        parsecmd();    /* parse command line */
  211. static void        prerrpkt();    /* print error packet */
  212. static void        printmsg();    /* print message */
  213. static int        recsw();    /* receive file state machine */
  214. static int        rdata();    /* receive file data */
  215. static int        rfile();    /* receive file header */
  216. static int        rinit();    /* receive initialization */
  217. static int        rpack();    /* receive a packet */
  218. static void        rpar();        /* get others send-init parameters */
  219. static int        sbreak();    /* send a break */
  220. static int        sdata();    /* send file data */
  221. static int        sendsw();    /* send file state machine */
  222. static int        seof();        /* send eof */
  223. static int        sfile();    /* send file header */
  224. static int        sinit();    /* send initiate */
  225. static int        spack();    /* send packet */
  226. static void        spar();        /* load send-init parameters */
  227. static void        usage();    /* print usage and quit */
  228.  
  229. #if MVUX
  230. static void        listener();    /* listener sub-task */
  231. static void        talker();    /* talker sub-task */
  232. #endif
  233.  
  234. /*
  235. **  Exported functions.
  236. */
  237.  
  238. int            main();        /* program entry point */
  239.  
  240. /*
  241. **  Imported data structures.
  242. */
  243. /* none */
  244.  
  245. /*
  246. **  Local data structures
  247. */
  248.  
  249. static FILE *        crfp;        /* communications read file pointer */
  250. static FILE *        cwfp;        /* communications write file pointer */
  251.  
  252. static int        lflag;        /* true if line option given */
  253. static int        lopen;        /* true if line is open */
  254. static char        llast[BUFSIZ];    /* last line name */
  255. static char        lname[BUFSIZ];    /* line name */
  256. static FILE *        lrfp;        /* line read file pointer */
  257. static FILE *        lwfp;        /* line write file pointer */
  258.  
  259. static int        topen;        /* true if terminal is open */
  260. static char        tname[BUFSIZ];    /* terminal name */
  261. static FILE *        trfp;        /* terminal read file pointer */
  262. static FILE *        twfp;        /* terminal write file pointer */
  263.  
  264. static int        speed;        /* baud rate of communications line */
  265.  
  266. static int        size;        /* size of present data */
  267. static int        rpsiz;        /* maximum receive packet size */
  268. static int        spsiz;        /* maximum send packet size */
  269. static int        pad;        /* how much padding to send */
  270. static int        timint;        /* timeout for foreign host on sends */
  271. static int        n;        /* packet number */
  272. static int        numtry;        /* number of retries for this packet */
  273. static int        oldtry;        /* times previous packet retried */
  274. static int        terminate;    /* termination flag for connect */
  275. static int        remote;        /* -1 means we're a remote kermit */
  276. static int        image;        /* -1 means 8 bit mode */
  277. static int        debug;        /* debugging level */
  278. static int        convert;    /* do file name case conversion */
  279.  
  280. static int        file;        /* current file number */
  281. static int        files;        /* number of files to send */
  282. static char *        filename;    /* current file name */
  283. static char *        filenames[256];    /* pointers to file names */
  284.  
  285. static char        state;        /* present state of the automaton */
  286. static char        padchar;    /* padding character */
  287. static char        eol;        /* end of line character to send */
  288. static char        escchr;        /* connect command escape char */
  289. static char        quote;        /* quote character in incoming data */
  290. static char        recpkt[MAXPACK];/* receive packet buffer */
  291. static char        packet[MAXPACK];/* packet buffer */
  292. static char        shargs[BUFSIZ];    /* arguments to a shell command */
  293.  
  294. static FILE *        fp;        /* file pointer for current disk file */
  295. static jmp_buf        env;        /* jump buffer for timeouts */
  296.  
  297. /*
  298.  *  m a i n
  299.  *
  300.  *  Main routine - Initialize and enter command loop: parse command and options,
  301.  *  set up the tty lines, dispatch to the appropriate routine and get next
  302.  *  command.
  303.  */
  304.  
  305. int main(argc, argv)
  306. int argc;                    /* argument count */
  307. char * argv[];                    /* argument vector */
  308. {
  309.     int        c;            /* current character */
  310.     int        ac;            /* argument count */
  311.     char *        av[256];        /* argument vector */
  312.     char **        avp;            /* argument vector pointer */
  313.     cmdtype        cmd;            /* command */
  314.     char        cmdline[BUFSIZ];    /* command line */
  315.  
  316. /*
  317. **    Set up so that first iteration uses the command line as
  318. **    the line to parse.  Establish the default values for 
  319. **    certain ``global'' variables.
  320. */
  321.     ac = argc - 1;
  322.     avp = ++argv;
  323.     defaults();
  324. /*
  325. **    Top level loop.  This loop obtains until the quit command is
  326. **    received.
  327. */
  328.     cmd = cmd_none;
  329.     while (cmd != cmd_quit)
  330.     {
  331. /*
  332. **        Parse and execute the current command line.
  333. */
  334.         cmd = parsecmd(ac, avp);
  335. /*
  336. **        In order to support the transfer of files, the communications
  337. **        lines must be opened and closed in a certain order.  The code
  338. **        that follows is an attempt to do just that.
  339. */
  340.         if (cmd == cmd_connect)
  341.         {
  342.         remote = 0;            /* connect is always local */
  343.         if (lflag)            /* if line specified */
  344.         {
  345.             if (lopen)            /* if a line is open */
  346.             {
  347.             if (strcmp(lname, llast) != 0)
  348.             {
  349.                 closeline(lrfp, lwfp);
  350.                 lopen = 0;
  351.                 if (openline(lname, &lrfp, &lwfp) == -1)
  352.                 cmd = cmd_none;
  353.                 else
  354.                 {
  355.                 strcpy(lname, llast);
  356.                 lopen = 1;
  357.                 }
  358.             }
  359.             }
  360.             else
  361.             {
  362.             if (openline(lname, &lrfp, &lwfp) == -1)
  363.             {
  364.                 lopen = 0;
  365.                 cmd = cmd_none;
  366.             }
  367.             else
  368.             {
  369.                 strcpy(lname, llast);
  370.                 lopen = 1;
  371.             }
  372.             }
  373.         }
  374.         else if (lopen)            /* if line already open */
  375.             printmsg("Connection continued on line %s.\n", lname);
  376.         else                /* no line open; none given */
  377.         {
  378.             error("No line specified for connect command.\n");
  379.             cmd = cmd_none;
  380.         }
  381.         }
  382.         else if (cmd == cmd_send || cmd == cmd_receive)
  383.         {
  384.         if (lflag)            /* if line specified */
  385.         {
  386.             remote = 0;            /* local */
  387.             if (lopen)            /* if a line is open */
  388.             {
  389.             if (strcmp(lname, llast) != 0)
  390.             {
  391.                 closeline(lrfp, lwfp);
  392.                 lopen = 0;
  393.                 if (openline(lname, &lrfp, &lwfp) == -1)
  394.                 cmd = cmd_none;
  395.                 else
  396.                 {
  397.                 strcpy(lname, llast);
  398.                 lopen = 1;
  399.                 }
  400.             }
  401.             }
  402.             else
  403.             {
  404.             if (openline(lname, &lrfp, &lwfp) == -1)
  405.             {
  406.                 lopen = 0;
  407.                 cmd = cmd_none;
  408.             }
  409.             else
  410.             {
  411.                 strcpy(lname, llast);
  412.                 lopen = 1;
  413.             }
  414.             }
  415.             crfp = lrfp;
  416.             cwfp = lwfp;
  417.         }
  418.         else if (lopen)            /* open line; use it */
  419.         {
  420.             remote = 0;
  421.             crfp = lrfp;
  422.             cwfp = cwfp;
  423.         }
  424.         else        /* no open line, no line given; use tty */
  425.         {
  426.             remote = 1;            /* remote */
  427.             if (topen == 0)        /* if tty not open */
  428.             {
  429.             if (openline(tname, &trfp, &twfp) == -1)
  430.                 cmd = cmd_none;
  431.             }
  432.             crfp = trfp;
  433.             cwfp = twfp;
  434.         }
  435.         }
  436. /*
  437. **        Dispatch to the appropriate execution function depending
  438. **        on the value of cmd.
  439. */
  440.         switch (cmd)
  441.         {
  442.         case cmd_connect:
  443.             doconnect();
  444.             break;
  445.         
  446.         case cmd_receive:
  447.             doreceive();
  448.             break;
  449.  
  450.         case cmd_send:
  451.             dosend();
  452.             break;
  453.  
  454.         case cmd_shell:
  455.             doshell();
  456.             break;
  457.          
  458.         case cmd_help:
  459.             dohelp();
  460.             break;
  461.         
  462.         case cmd_quit:
  463.             break;
  464.         }
  465. /*
  466. **        If the last command was not a quit command then get the next
  467. **        command line from the user.
  468. */
  469.         if (cmd != cmd_quit)
  470.         ac = getcmdline(avp = av, cmdline);
  471.     }
  472. }
  473.  
  474.  
  475. /*
  476.  *    b u f e m p
  477.  *
  478.  *  Put data from an incoming packet into a file.
  479.  */
  480.  
  481. static void bufemp(buffer,len)
  482. char  buffer[];                /* Buffer */
  483. int   len;                /* Length */
  484. {
  485.     int i;                /* Counter */
  486.     char t;                /* Character holder */
  487.  
  488.     for (i=0; i<len; i++)        /* Loop thru the data field */
  489.     {
  490.     t = buffer[i];            /* Get character */
  491.     if (t == MYQUOTE)        /* Control quote? */
  492.     {                /* Yes */
  493.         t = buffer[++i];        /* Get the quoted character */
  494.         if ((t & 0177) != MYQUOTE)    /* Low order bits match quote char? */
  495.         t = ctl(t);        /* No, uncontrollify it */
  496.     }
  497.     if (t==CR && !image)        /* Don't pass CR if in image mode */
  498.         continue;
  499.  
  500.     putc(t,fp);
  501.     }
  502. }
  503.  
  504.  
  505. /*
  506.  *  b u f i l l
  507.  *
  508.  *  Get a bufferful of data from the file that's being sent.
  509.  *  Only control-quoting is done; 8-bit & repeat count prefixes are
  510.  *  not handled.
  511.  */
  512.  
  513. static int bufill(buffer)
  514. char buffer[];                /* Buffer */
  515. {
  516.     int i,                /* Loop index */
  517.         t;                /* Char read from file */
  518.     char t7;                /* 7-bit version of above */
  519.  
  520.     i = 0;                /* Init data buffer pointer */
  521.     while((t = getc(fp)) != EOF)    /* Get the next character */
  522.     {
  523.     t7 = t & 0177;            /* Get low order 7 bits */
  524.  
  525.     if (t7 < SP || t7==DEL || t7==quote) /* Does this char require */
  526.     {                    /* special handling? */
  527.         if (t=='\n' && !image)
  528.         {                /* Do LF->CRLF mapping if !image */
  529.         buffer[i++] = quote;
  530.         buffer[i++] = ctl('\r');
  531.         }
  532.         buffer[i++] = quote;    /* Quote the character */
  533.         if (t7 != quote)
  534.         {
  535.         t = ctl(t);        /* and uncontrolify */
  536.         t7 = ctl(t7);
  537.         }
  538.     }
  539.     if (image)
  540.         buffer[i++] = t;        /* Deposit the character itself */
  541.     else
  542.         buffer[i++] = t7;
  543.  
  544.     if (i >= spsiz-8) return(i);    /* Check length */
  545.     }
  546.     if (i==0) return(EOF);        /* Wind up here only on EOF */
  547.     return(i);                /* Handle partial buffer */
  548. }
  549.  
  550.  
  551. /*
  552.  *  c l k i n t
  553.  *
  554.  *  Clock interrupt handler.
  555.  */
  556.  
  557. static int clkint()            /* Timer interrupt handler */
  558. {
  559.     longjmp(env,TRUE);            /* Tell rpack to give up */
  560. }
  561.  
  562.  
  563. /*
  564.  *  c l o s e l i n e
  565.  *  
  566.  *  Function closeline() closes the given file pointers.  If an 
  567.  *  error is detected then a diagnostic is written and -1 is returned.
  568.  *  If no error is detected then 0 is returned.
  569.  */
  570.  
  571. static int closeline(rfp, wfp)
  572. FILE * rfp;                    /* read file pointer */
  573. FILE * wfp;                    /* write file pointer */
  574. {
  575.  
  576.     if (fclose(rfp) == -1)
  577.     {
  578.         error("Can't close read file pointer.");
  579.         error("Error code: %d.", errno);
  580.         return -1;
  581.     }
  582.     if (fclose(wfp) == -1)
  583.     {
  584.         error("Can't close write file pointer.");
  585.         error("Error code: %d.", errno);
  586.         return -1;
  587.     }
  588.     return 0;
  589. }
  590.  
  591.  
  592. /*
  593.  *  c o n n e c t
  594.  *
  595.  *  Establish a virtual terminal connection with the remote host.
  596.  */
  597.  
  598. static void connect()
  599. {
  600.  
  601. /*
  602. **    Under MV/UX a true fork() call does not exist; however, we can
  603. **    create multiple asynchronous tasks with a program.  We create
  604. **    two sub-tasks, ``listener'' and ``talker'' to handle the data
  605. **    communication.  For the main task to regain control one or both
  606. **    of the tasks must set the terminate flag; therefore it is cleared
  607. **    before either task is initiated.
  608. */
  609.     terminate = 0;
  610.     if (mtask(listener, STACK, LISTENID, LISTENPRI) != 0)
  611.     {
  612.         error("Can't initiate listener task.");
  613.         error("Error code is: %d.\n", errno);
  614.         return;
  615.     }
  616.     if (mtask(talker, STACK, TALKID, TALKPRI) != 0)
  617.     {
  618.         error("Can't initiate talker task.");
  619.         error("Error code is: %d.\n", errno);
  620.         return;
  621.     }
  622. /*
  623. **    After creating the sub-tasks the main task (this thread)
  624. **    enters the following loop.  The main task runs once every
  625. **    two seconds; on each iteration it checks the ``terminate''
  626. **    flag.  When it finds the terminate flag set it pops out of
  627. **    the loop and terminates the ``listener'' and ``talker''
  628. **    sub-tasks.
  629. */
  630.     while (terminate == 0)
  631.         sleep(2);
  632.     if (midkill(LISTENID) == -1)
  633.     {
  634.         error("Can't terminate listener task.");
  635.         return;
  636.     }
  637.     if (midkill(TALKID) == -1)
  638.     {
  639.         error("Can't terminate talker task.");
  640.         return;
  641.     }
  642. }
  643.  
  644.  
  645. /*
  646.  *    d e f a u l t s 
  647.  *
  648.  *    Establish default values for the myriad global variables.
  649.  *
  650.  */
  651.  
  652. static void defaults()
  653. {
  654.  
  655. #if UCB4X | MVUX            /* Default to 7-bit masking, CRLF */
  656.     image = FALSE;            /* translation and filename case */
  657.     convert++;            /* conversion for UNIX systems */
  658. #else
  659.     image = TRUE;            /* Default to no processing for */
  660.     convert = FALSE;        /* non-UNIX systems */
  661. #endif
  662.     eol = CR;            /* eol for outgoing packets */
  663.     quote = '#';            /* for quoting control characters */
  664.     pad = 0;            /* no padding */
  665.     padchar = 0;            /* pad with ASCII nul */
  666.     escchr = ESCCHR;        /* default escape character */
  667.     strcpy(lname, "");        /* no line name */
  668.     strcpy(llast, "");        /* no default either */
  669.     strcpy(tname, TTY);        /* terminal name */
  670. }
  671.  
  672.  
  673. /*
  674.  *  d o c o n n e c t 
  675.  *
  676.  *  Top level control for connect command.
  677.  *
  678.  */
  679.  
  680. static void doconnect()
  681. {
  682.  
  683. /*
  684. **    Open the communications line to the terminal.  This line can be
  685. **    closed when the connect command completes.
  686. */
  687.     if (openline(tname, &trfp, &twfp) == -1)
  688.         return;
  689. /*
  690. **    Inform the user that the connection is established and how
  691. **    to return.  Then invoke connect() to actually manage the
  692. **    connection.
  693. */
  694.     printf("Connected.  Use %cc to return to command level.\n", escchr);
  695.     connect();        
  696.     printf("Disconnected.\n");
  697. /*
  698. **    Close the line to the terminal.
  699. */
  700.     (void) closeline(trfp, twfp);
  701. }
  702.  
  703. /*
  704.  *  d o h e l p
  705.  *
  706.  *  Print a simple help menu on the terminal.
  707.  *
  708.  */
  709.  
  710. static void dohelp()
  711. {
  712.  
  713. /*
  714. **  Print a help screen for the user.
  715. */
  716.     printf("\nKermit Help:\n\n");
  717.     printf("When you receive the prompt 'Kermit: ' you are expected to\n");
  718.     printf("reply with a command.  The following commands are supported:\n\n");
  719.     printf("\tc[lbe line baud escape]             - connect to remote.\n");
  720.     printf("\ts[diflb line baud escape] file(s)   - send file(s).\n");
  721.     printf("\tr[diflb line baud escape]           - receive file(s).\n");
  722.     printf("\t! [command]                         - shell escape.\n");
  723.     printf("\t?                                   - this help screen.\n");
  724.     printf("\tq                                   - quit.\n\n");
  725. #if 0
  726.     printf("Commands:    c[lbe line baud esc.char]     (to reconnect)\n");
  727.     printf("or:        s[diflb line baud] file ...    (to send file(s))\n");
  728.     printf("or:        r[diflb line baud]        (to receive )\n");
  729.     printf("or:        ![cmd]                    (for shell escape)\n");
  730.     printf("or:        q                (TO QUIT)\n");
  731. #endif
  732. #if 0
  733.     printf("Usage: kermit c[le line esc.char]         (connect mode)\n");
  734.     printf("or:       kermit s[difl line] file ...         (send mode)\n");
  735.     printf("or:       kermit r[difl line]             (receive mode)\n");
  736.     exit(1);
  737. #endif
  738. }
  739.  
  740.  
  741. /*
  742.  *  d o r e c e i v e
  743.  *
  744.  *  Top level control for receive command.
  745.  *
  746.  */
  747.  
  748. static void doreceive()
  749. {
  750.  
  751. /*
  752. **    Do the receive and print the resulting status.
  753. */
  754.     if (recsw() == FALSE)
  755.         printmsg("Receive failed.");
  756.     else
  757.         printmsg("Receive completed.");
  758. }
  759.  
  760.  
  761. /*
  762.  *  d o s e n d
  763.  *
  764.  *  Top level control for send command.
  765.  *
  766.  */
  767.  
  768. static void dosend()
  769. {
  770.  
  771. /*
  772. **    Get the filename and do the send.
  773. */
  774.     filename = filenames[file++];
  775.     fp = NULL;        /* Indicate no file open yet */
  776.     if (sendsw() == FALSE)    /* Send the file(s) */
  777.         printmsg("Send failed.");/* Report failure */
  778.     else            /*  or */
  779.         printmsg("Done.");    /* success */
  780. }
  781.  
  782.  
  783. /*
  784.  *  d o s h e l l
  785.  *
  786.  *  Function doshell() runs a shell command for the user.
  787.  *
  788.  */
  789.  
  790. static void doshell()
  791. {
  792.     int    args;            /* true if args to shell */
  793.     char *    s;            /* work pointer */
  794.     int    (* savehup)();        /* old SIGHUP value */    
  795.     int    (* saveint)();        /* old SIGINT value */
  796.     int    (* savequit)();        /* old SIGQUIT value */
  797.     char     path[BUFSIZ];        /* pathname to shell */
  798.     char *    base;            /* pointer to base name of shell */
  799.     int    code;            /* return code from wait() */
  800.     int    pid;            /* child process id */
  801.  
  802. /*
  803. **    Get the shell to use.  The full pathname to the shell is kept in
  804. **    path; a pointer, base, is used to point at the basename of the
  805. **    shell.  Finally, scan the shargs string to decide if any arguments
  806. **    were given.
  807. */
  808.     if (getenv("SHELL") != NULL)
  809.         strcpy(path, getenv("SHELL"));
  810.     else
  811.         strcpy(path, SHELL);
  812.     for (s = base = path; *s; )
  813.         if (*s++ == '/')
  814.         base = s;
  815.     for (s = shargs, args = 0; *s; s++)
  816.         if (*s != ' ' && *s != '\t')
  817.         args = 1;
  818. /*
  819. **    Save the current values of SIGHUP, SIGINT and SIGQUIT.
  820. **    Then execute the shell command line.  Finally, restore
  821. **    signal handling.
  822. */
  823.     savehup = signal(SIGHUP, SIG_IGN);
  824.     saveint = signal(SIGINT, SIG_IGN);
  825.     savequit = signal(SIGQUIT, SIG_IGN);
  826. /*
  827. **    The child does an execl() call.  If the call fails then
  828. **    a diagnostic is written and the child exits().
  829. */
  830.     if ((pid = vfork()) == 0)        /* child */
  831.     {
  832.         if (args)
  833.         execl(path, base, "-c", shargs, 0);
  834.         else
  835.         execl(path, base, 0);
  836.         error("doshell: execl() has failed.");
  837.         _exit(1);
  838.     }
  839. /*
  840. **    The parent waits for the child to terminate, then restores
  841. **    signals.
  842. */
  843.     else                    /* parent */
  844.     {
  845.         while (wait(&code) != pid)
  846.         ;
  847.         (void) signal(SIGHUP, savehup);
  848.         (void) signal(SIGINT, saveint);
  849.         (void) signal(SIGQUIT, savequit);
  850. /*
  851. **        Clean up the argument list for the next pass, if any.  
  852. **        Write a new-line to stdout so that Kermit prompt is positioned
  853. **        correctly.  
  854. */
  855.         strcpy(shargs, "");
  856.         fputs("\n", stdout);
  857.     }
  858. }
  859.  
  860.  
  861. /*
  862.  *  e r r o r
  863.  *
  864.  *  Print error message.
  865.  *
  866.  *  If local, print error message with printmsg.
  867.  *  If remote, send an error packet with the message.
  868.  */
  869.  
  870. /*VARARGS1*/
  871. static int error(fmt, a1, a2, a3, a4, a5)
  872. char *fmt;
  873. {
  874.     char msg[80];
  875.     int len;
  876.  
  877.     if (remote)
  878.     {
  879.     sprintf(msg,fmt,a1,a2,a3,a4,a5); /* Make it a string */
  880.     len = strlen(msg);
  881.     spack('E',n,len,msg);        /* Send the error packet */
  882.     }
  883.     else
  884.     printmsg(fmt, a1, a2, a3, a4, a5);
  885. }
  886.  
  887.  
  888. /*
  889.  *  f l u s h i n p u t
  890.  *
  891.  *  Dump all pending input to clear stacked up NACK's.
  892.  *  (Implemented only for Berkeley Unix at this time).
  893.  */
  894.  
  895. #if UCB4X&(~NO_FIONREAD)
  896. static void flushinput()
  897. {
  898.     long int count;            /* Number of bytes ready to read */
  899.     long int i;                /* Number of bytes to read in loop */
  900.  
  901.     ioctl(ttyfd, FIONREAD, &count);    /* See how many bytes pending read */
  902.     if (!count) return;            /* If zero, then no input to flush */
  903.  
  904.     while (count)            /* Loop till all are flushed */
  905.     {
  906.     i = (count<sizeof(recpkt)) ?    /* Read min of count and size of */
  907.         count : sizeof(recpkt);    /*  the read buffer */
  908.     read(ttyfd, recpkt, i);        /* Read a bunch */
  909.     count -= i;            /* Subtract from amount to read */
  910.     }
  911. }
  912. #else
  913. static void flushinput()        /* Null version for non-Berkeley Unix */
  914. {}
  915. #endif /* UCB4X&(~FIONREAD) */
  916.  
  917.  
  918. /*
  919.  *  g e t c m d l i n e
  920.  *
  921.  *  This function gets the next Kermit command line from the users
  922.  *  terminal.  The Kermit prompt is written and a single line is 
  923.  *  read.  The line is then expanded by 'echo'ing it through a shell.
  924.  *  Once expanded, the arguments, ac and av, are set up as if by
  925.  *  the C start-off routines.  This function returns the number of
  926.  *  arguments on the command line.
  927.  */
  928.  
  929. static int getcmdline(av, cmdline)
  930. char * av[];                    /* argument vector */
  931. char * cmdline;                    /* returned command line */
  932. {
  933.     int        ac;            /* argument count */
  934.     char *        s;            /* work pointer */
  935.     char         buf[BUFSIZ];        /* work buffer */
  936.     int        length;            /* command length */
  937.     FILE *        pfp;            /* for popen() / pclose() */
  938.  
  939. /*
  940. **    Print the Kermit prompt and read the line typed by the user.
  941. **    If the users responds with ^D then hand-craft a quit command.
  942. **    Clip off the delimeter, usually \n.
  943. */
  944.     printf("Kermit: ");
  945.     if (fgets(cmdline, CMDLINE, stdin) == NULL)
  946.     {
  947.         printf("[EOT]\n");
  948.         strcpy(cmdline, "q\n");
  949.     }
  950.     if ((length = strlen(s = cmdline)) > 0)
  951.         cmdline[length - 1] = 0;
  952. /*
  953. **    Hand-craft a command line to expand the command line.  Then 
  954. **    use popen() / pclose() to get the results of the expansion.
  955. */
  956. #if 0
  957.     sprintf(buf, "/bin/echo %s", cmdline);
  958.     if ((pfp = popen(buf, "r")) == NULL)
  959.     {
  960.         perror("getcmdline: popen");
  961.         exit(1);
  962.     }
  963.     if ((s = fgets(cmdline, CMDLINE, pfp)) == NULL)
  964.     {
  965.         perror("getcmdline: fgets");
  966.         exit(1);
  967.     }
  968.     if (pclose(pfp) == -1)
  969.     {
  970.         perror("getcmdline: pclose");
  971.         exit(1);
  972.     }
  973. #endif
  974. /*
  975. **    Finally, set up ac and av.  Note that each argument in cmdline
  976. **    is terminated with a NULL so that it looks just like a 'real'
  977. **    argv from C runtimes.
  978. */
  979.     while (*s == ' ' || *s == '\t')            /* skip whitespace */
  980.         s++;
  981.     for (av[ac = 0] = NULL; *s; ac++)
  982.     {
  983.         av[ac] = s;                    /* point to argument */
  984.         while (*s && *s != ' ' && *s != '\t')    /* skip argument body */
  985.         s++;
  986.         if (*s)                    /* if more arguments */
  987.         *s++ = 0;
  988.         while (*s == ' ' || *s == '\t')        /* skip whitespace */
  989.         s++;
  990.     }
  991.     av[ac] = NULL;
  992.     return ac;
  993. }
  994.  
  995.  
  996. #if MVUX
  997. /*
  998.  *  l i s t e n e r
  999.  *
  1000.  *  Special MV/UX sub-task to listen for characters from the remote system.
  1001.  */
  1002.  
  1003. static void listener()
  1004. {
  1005.     int        cc;                /* I/O buffer */
  1006.  
  1007.     while (1)
  1008.     {
  1009.     cc = fgetc(lrfp);
  1010.     fputc(cc, twfp);
  1011.     }
  1012. }
  1013. #endif
  1014.  
  1015.  
  1016. /*
  1017.  *  m a p s p e e d
  1018.  *
  1019.  *  Map an integer baud rate (300, 1200, etc.) to the appropriate manifest
  1020.  *  value (B300, B1200, etc.).  Returns manifest or -1 for illegal baud.
  1021.  */
  1022.  
  1023. static int mapspeed(baud)
  1024. int baud;                    /* input baud rate */
  1025. {
  1026.     int    manifest;            /* manifest value */
  1027.  
  1028.     switch (baud)
  1029.     {
  1030.         case 110:    
  1031.         manifest = B110;
  1032.         break;
  1033.  
  1034.         case 150:
  1035.         manifest = B150;
  1036.         break;
  1037.  
  1038.         case 300:
  1039.         manifest = B300;
  1040.         break;
  1041.     
  1042.         case 1200:
  1043.         manifest = B1200;
  1044.         break;
  1045.  
  1046.         case 2400:
  1047.         manifest = B2400;
  1048.         break;
  1049.  
  1050.         case 4800:
  1051.         manifest = B4800;
  1052.         break;
  1053.  
  1054.         case 9600:
  1055.         manifest = B9600;
  1056.         break;
  1057.  
  1058.         default:            /* error case */
  1059.         manifest = -1;
  1060.         break;
  1061.     }
  1062.     return manifest;
  1063. }
  1064.  
  1065.  
  1066. /*
  1067.  *  o p e n l i n e 
  1068.  *  
  1069.  *  Function openline() opens read and write file pointers to the 
  1070.  *  given file, assumed to be a communications line.  If an error
  1071.  *  is detected then a diagnostic is written and -1 is returned.
  1072.  *  On no error, 0 is returned.
  1073.  *
  1074.  */
  1075.  
  1076. static int openline(name, rfp, wfp)
  1077. char * name;                    /* device name to open */
  1078. FILE ** rfp;                    /* read file pointer */
  1079. FILE ** wfp;                    /* write file pointer */
  1080. {
  1081. #if MVUX
  1082.     char *        rmode = "j";        /* DG binary read */
  1083.     char *        wmode = "k";        /* DG binary write */
  1084. #else
  1085.     char *        rmode = "r";        /* normal read */
  1086.     char *        wmode = "w";        /* normal write */
  1087. #endif
  1088.  
  1089.     if ((*rfp = fopen(name, rmode)) == NULL)
  1090.     {
  1091.         error("Can't open %s for mode %s.", name, rmode);
  1092.         error("Error code: %d.", errno);
  1093.         return -1;
  1094.     }
  1095.     if ((*wfp = fopen(name, wmode)) == NULL)
  1096.     {
  1097.         error("Can't open %s for mode %s.", name, wmode);
  1098.         error("Error code: %d.", errno);
  1099.         return -1;
  1100.     }
  1101.     setbuf(*rfp, NULL);
  1102.     setbuf(*wfp, NULL);
  1103.     return 0;
  1104. }
  1105.  
  1106.  
  1107. /*
  1108.  *  p a r s e c m d 
  1109.  *
  1110.  *  Function parsecmd() does a simple minded parse of the given command
  1111.  *  line.  While it would be nice to conform the UNIX standard and use
  1112.  *  getopt() to parse this is not done.  The change would like cause more
  1113.  *  conflict than good.  Sigh.  
  1114.  *
  1115.  *  If the command line does not contain a Kermit command letter this 
  1116.  *  function returns ``cmd_none''.
  1117.  *
  1118.  */
  1119.  
  1120. static cmdtype parsecmd(ac, av)
  1121. int ac;                        /* argument count */
  1122. char * av[];                    /* argument vector */
  1123. {
  1124.     int        c;            /* current character */
  1125.     cmdtype        cmd;            /* current command */
  1126.     int        index;            /* current argument index */
  1127.     char *        s;            /* work pointer */
  1128.  
  1129. /*
  1130. **    The default Kermit standard for command lines requires that the
  1131. **    first argument begin with a command, then optional arguments.
  1132. **    Check that this is the case, returning cmd_none in the case of
  1133. **    an empty command line.
  1134. */
  1135.     cmd = cmd_none;
  1136.     if (ac == 0)                /* empty command line */
  1137.         return cmd;
  1138. /*
  1139. **    Parse the first argument.  This is the command plus any of the
  1140. **    options.
  1141. */
  1142.     remote = 0;                /* default to local */
  1143.     s = av[index = 0];
  1144.     switch (c = *s++)
  1145.     {
  1146.         case 'c':                /* connect command */
  1147.         cmd = cmd_connect;
  1148.         break;
  1149.  
  1150.         case 'r':                /* receive command */
  1151.         cmd = cmd_receive;
  1152.         remote = 1;            /* default to remote */
  1153.         break;
  1154.  
  1155.         case 's':                /* send command */
  1156.         cmd = cmd_send;    
  1157.         remote = 1;            /* default to remote */
  1158.         break;
  1159.  
  1160.         case '!':                /* shell command */
  1161.         cmd = cmd_shell;
  1162.         while (av[++index] != NULL)
  1163.         {
  1164.             strcat(shargs, " ");
  1165.             strcat(shargs, av[index]);
  1166.         }
  1167.         break;
  1168.  
  1169.         case '?':                /* help command */
  1170.         cmd = cmd_help;    
  1171.         break;
  1172.  
  1173.         case 'q':                /* quit command */
  1174.         cmd = cmd_quit;
  1175.         break;
  1176.  
  1177.         default:                /* unknown command */
  1178.         error("Not a valid command: '%c'.  Use '?' for help.", c);
  1179.         return cmd_none;
  1180.         break;
  1181.     }
  1182. /*
  1183. **    If the command was one of [c,r,s] then continue parsing for options.
  1184. **    Otherwise return immediately to caller.
  1185. */
  1186.     if (cmd == cmd_connect || cmd == cmd_receive || cmd == cmd_send)
  1187.     {
  1188.         while (*s)            /* while more arguments */
  1189.         {
  1190.         switch (c = *s++)
  1191.         {
  1192.             case 'd':
  1193.             debug++;    /* debug */
  1194.             break;
  1195.  
  1196.             case 'i':        /* image mode */
  1197.             image = 1;
  1198.             break;
  1199.  
  1200.             case 'f':        /* convert file names */
  1201.             convert = 1;
  1202.             break;
  1203.  
  1204.             case 'b':        /* baud rate (speed) */
  1205.             if (av[++index] == NULL)
  1206.             {
  1207.                 error("Option 'b' requires baud rate.");
  1208.                 return cmd_none;
  1209.             }
  1210.             speed = atoi(av[index]);
  1211.             break;
  1212.  
  1213.             case 'e':
  1214.             if (av[++index] == NULL)
  1215.             {
  1216.                 error("Option 'e' requires escape character.");
  1217.                 return cmd_none;
  1218.             }
  1219.             escchr = av[index][0];
  1220.             break;
  1221.  
  1222.             case 'l':        /* line */
  1223.             lflag = 1;
  1224.             remote = 0;
  1225.             if (av[++index] == NULL)
  1226.             {
  1227.                 error("Option 'l' requires a line name.");
  1228.                 return cmd_none;
  1229.             }
  1230.             strcpy(lname, av[index]);
  1231.             strcpy(llast, av[index]);
  1232.             break;
  1233.  
  1234.             default:
  1235.             error("Not a valid option: '%c'.  Use ? for help.", c);
  1236.             return cmd_help;
  1237.             break;
  1238.         }
  1239.         }
  1240. /*
  1241. **        Now, if the command was cmd_send, pick up all files.
  1242. */
  1243.         if (cmd == cmd_send)
  1244.         {
  1245.         for (files = 0, index++; av[index] != NULL; files++, index++)
  1246.             filenames[files] = av[index];
  1247.         if (files == 0)
  1248.         {
  1249.             error("No files specified for send command.");
  1250.             return cmd_help;
  1251.         }
  1252.         }
  1253.     }
  1254.     return cmd;
  1255. }
  1256.  
  1257.  
  1258. /*
  1259.  *  p r e r r p k t
  1260.  *
  1261.  *  Print contents of error packet received from remote host.
  1262.  */
  1263.  
  1264. static void prerrpkt(msg)
  1265. char * msg;
  1266. {
  1267.     printf("Kermit aborting with following error from remote host:\n%s\n",msg);
  1268. }
  1269.  
  1270.  
  1271. /*
  1272.  *  p r i n t m s g
  1273.  *
  1274.  *  Print error message on standard output if not remote.
  1275.  */
  1276.  
  1277. /*VARARGS1*/
  1278. static void printmsg(fmt, a1, a2, a3, a4, a5)
  1279. char * fmt;
  1280. {
  1281.     if (! remote)
  1282.     {
  1283.     printf(fmt, a1, a2, a3, a4, a5);
  1284.     printf("\n");
  1285.     fflush(stdout);               /* force output  (UTS needs it) */
  1286.     }
  1287. }
  1288.  
  1289.  
  1290. /*
  1291.  *  r d a t a
  1292.  *
  1293.  *  Receive Data
  1294.  */
  1295.  
  1296. static int rdata()
  1297. {
  1298.     int num, len;            /* Packet number, length */
  1299.     if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
  1300.  
  1301.     switch(rpack(&len,&num,packet))    /* Get packet */
  1302.     {
  1303.     case 'D':            /* Got Data packet */
  1304.         if (num != n)        /* Right packet? */
  1305.         {                /* No */
  1306.         if (oldtry++ > MAXTRY)
  1307.             return('A');    /* If too many tries, abort */
  1308.         if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
  1309.         {            /* Previous packet again? */
  1310.             spack('Y',num,6,packet); /* Yes, re-ACK it */
  1311.             numtry = 0;        /* Reset try counter */
  1312.             return(state);    /* Don't write out data! */
  1313.         }
  1314.         else return('A');    /* sorry, wrong number */
  1315.         }
  1316.         /* Got data with right packet number */
  1317.         bufemp(packet,len);        /* Write the data to the file */
  1318.         spack('Y',n,0,0);        /* Acknowledge the packet */
  1319.         oldtry = numtry;        /* Reset the try counters */
  1320.         numtry = 0;            /* ... */
  1321.         n = (n+1)%64;        /* Bump packet number, mod 64 */
  1322.         return('D');        /* Remain in data state */
  1323.  
  1324.     case 'F':            /* Got a File Header */
  1325.         if (oldtry++ > MAXTRY)
  1326.         return('A');        /* If too many tries, "abort" */
  1327.         if (num == ((n==0) ? 63:n-1)) /* Else check packet number */
  1328.         {                /* It was the previous one */
  1329.         spack('Y',num,0,0);    /* ACK it again */
  1330.         numtry = 0;        /* Reset try counter */
  1331.         return(state);        /* Stay in Data state */
  1332.         }
  1333.         else return('A');        /* Not previous packet, "abort" */
  1334.  
  1335.     case 'Z':            /* End-Of-File */
  1336.         if (num != n) return('A');    /* Must have right packet number */
  1337.         spack('Y',n,0,0);        /* OK, ACK it. */
  1338.         fclose(fp);            /* Close the file */
  1339.         n = (n+1)%64;        /* Bump packet number */
  1340.         return('F');        /* Go back to Receive File state */
  1341.  
  1342.      case 'E':            /* Error packet received */
  1343.          prerrpkt(recpkt);        /* Print it out and */
  1344.          return('A');        /* abort */
  1345.  
  1346.     case FALSE:            /* Didn't get packet */
  1347.         spack('N',n,0,0);        /* Return a NAK */
  1348.         return(state);        /* Keep trying */
  1349.  
  1350.     default:     return('A');    /* Some other packet, "abort" */
  1351.     }
  1352. }
  1353.  
  1354.  
  1355. /*
  1356.  *  r e c s w
  1357.  *
  1358.  *  This is the state table switcher for receiving files.
  1359.  */
  1360.  
  1361. static int recsw()
  1362. {
  1363.  
  1364.     state = 'R';            /* Receive-Init is the start state */
  1365.     n = 0;                /* Initialize message number */
  1366.     numtry = 0;                /* Say no tries yet */
  1367.  
  1368.     while(TRUE)
  1369.     {
  1370.     if (debug) printf(" recsw state: %c\n",state);
  1371.     switch(state)            /* Do until done */
  1372.     {
  1373.         case 'R':    state = rinit(); break; /* Receive-Init */
  1374.         case 'F':    state = rfile(); break; /* Receive-File */
  1375.         case 'D':    state = rdata(); break; /* Receive-Data */
  1376.         case 'C':    return(TRUE);        /* Complete state */
  1377.         case 'A':    return(FALSE);        /* "Abort" state */
  1378.     }
  1379.     }
  1380. }
  1381.  
  1382.  
  1383. /*
  1384.  *  r f i l e
  1385.  *
  1386.  *  Receive File Header
  1387.  */
  1388.  
  1389. static int rfile()
  1390. {
  1391.     int num, len;            /* Packet number, length */
  1392.     char filnam1[50];            /* Holds the converted file name */
  1393.  
  1394.     if (numtry++ > MAXTRY) return('A'); /* "abort" if too many tries */
  1395.  
  1396.     switch(rpack(&len,&num,packet))    /* Get a packet */
  1397.     {
  1398.     case 'S':            /* Send-Init, maybe our ACK lost */
  1399.         if (oldtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
  1400.         if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
  1401.         {                /* Yes, ACK it again with  */
  1402.         spar(packet);        /* our Send-Init parameters */
  1403.         spack('Y',num,6,packet);
  1404.         numtry = 0;        /* Reset try counter */
  1405.         return(state);        /* Stay in this state */
  1406.         }
  1407.         else return('A');        /* Not previous packet, "abort" */
  1408.  
  1409.     case 'Z':            /* End-Of-File */
  1410.         if (oldtry++ > MAXTRY) return('A');
  1411.         if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
  1412.         {                /* Yes, ACK it again. */
  1413.         spack('Y',num,0,0);
  1414.         numtry = 0;
  1415.         return(state);        /* Stay in this state */
  1416.         }
  1417.         else return('A');        /* Not previous packet, "abort" */
  1418.  
  1419.     case 'F':            /* File Header (just what we want) */
  1420.         if (num != n) return('A');    /* The packet number must be right */
  1421.         strcpy(filnam1, packet);    /* Copy the file name */
  1422.  
  1423.         if (convert)        /* Convert upper case to lower */
  1424.         for (filename=filnam1; *filename != '\0'; filename++)
  1425.             if (*filename >= 'A' && *filename <= 'Z')
  1426.             *filename |= 040;
  1427.  
  1428.         if ((fp=fopen(filnam1,"w"))==NULL) /* Try to open a new file */
  1429.         {
  1430.         error("cannot create %s",filnam1); /* Give up if can't */
  1431.         return('A');
  1432.         }
  1433.         else            /* OK, give message */
  1434.         printmsg("Receiving %s as %s",packet,filnam1);
  1435.  
  1436.         spack('Y',n,0,0);        /* Acknowledge the file header */
  1437.         oldtry = numtry;        /* Reset try counters */
  1438.         numtry = 0;            /* ... */
  1439.         n = (n+1)%64;        /* Bump packet number, mod 64 */
  1440.         return('D');        /* Switch to Data state */
  1441.  
  1442.     case 'B':            /* Break transmission (EOT) */
  1443.         if (num != n) return ('A'); /* Need right packet number here */
  1444.         spack('Y',n,0,0);        /* Say OK */
  1445.         return('C');        /* Go to complete state */
  1446.  
  1447.      case 'E':            /* Error packet received */
  1448.          prerrpkt(recpkt);        /* Print it out and */
  1449.          return('A');        /* abort */
  1450.  
  1451.     case FALSE:            /* Didn't get packet */
  1452.         spack('N',n,0,0);        /* Return a NAK */
  1453.         return(state);        /* Keep trying */
  1454.  
  1455.     default:    return ('A');    /* Some other packet, "abort" */
  1456.     }
  1457. }
  1458.  
  1459.  
  1460. /*
  1461.  *  r i n i t
  1462.  *
  1463.  *  Receive Initialization
  1464.  */
  1465.   
  1466. static int rinit()
  1467. {
  1468.     int len, num;            /* Packet length, number */
  1469.  
  1470.     if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  1471.  
  1472.     switch(rpack(&len,&num,packet))    /* Get a packet */
  1473.     {
  1474.     case 'S':            /* Send-Init */
  1475.         rpar(packet);        /* Get the other side's init data */
  1476.         spar(packet);        /* Fill up packet with my init info */
  1477.         spack('Y',n,6,packet);    /* ACK with my parameters */
  1478.         oldtry = numtry;        /* Save old try count */
  1479.         numtry = 0;            /* Start a new counter */
  1480.         n = (n+1)%64;        /* Bump packet number, mod 64 */
  1481.         return('F');        /* Enter File-Receive state */
  1482.  
  1483.      case 'E':            /* Error packet received */
  1484.          prerrpkt(recpkt);        /* Print it out and */
  1485.          return('A');        /* abort */
  1486.  
  1487.     case FALSE:            /* Didn't get packet */
  1488.         spack('N',n,0,0);        /* Return a NAK */
  1489.         return(state);        /* Keep trying */
  1490.  
  1491.     default:     return('A');    /* Some other packet type, "abort" */
  1492.     }
  1493. }
  1494.  
  1495.  
  1496. /*
  1497.  *  r p a c k
  1498.  *
  1499.  *  Read a Packet
  1500.  */
  1501.  
  1502. static int rpack(len,num,data)
  1503. int *len, *num;                /* Packet length, number */
  1504. char *data;                /* Packet data */
  1505. {
  1506.     int i, done;            /* Data character number, loop exit */
  1507.      char t,                /* Current input character */
  1508.      type,                /* Packet type */
  1509.      cchksum,            /* Our (computed) checksum */
  1510.      rchksum;            /* Checksum received from other host */
  1511.  
  1512. #if UCB4X | MVUX            /* TOPS-20 can't handle timeouts... */
  1513.     if (setjmp(env)) return FALSE;    /* Timed out, fail */
  1514.     signal(SIGALRM,clkint);        /* Setup the timeout */
  1515.     if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
  1516.     alarm(timint);
  1517. #endif /* UCB4X */
  1518.  
  1519.     while (t != SOH)            /* Wait for packet header */
  1520.     {
  1521.     t = fgetc(crfp);
  1522.     t &= 0177;            /* Handle parity */
  1523.     }
  1524.  
  1525.     done = FALSE;            /* Got SOH, init loop */
  1526.     while (!done)            /* Loop to get a packet */
  1527.     {
  1528.     t = fgetc(crfp);        /* Get Character */
  1529.     if (!image) t &= 0177;        /* Handle parity */
  1530.     if (t == SOH) continue;        /* Resynchronize if SOH */
  1531.     cchksum = t;            /* Start the checksum */
  1532.     *len = unchar(t)-3;        /* Character count */
  1533.  
  1534.     t = fgetc(crfp);        /* Get character */
  1535.     if (!image) t &= 0177;        /* Handle parity */
  1536.     if (t == SOH) continue;        /* Resynchronize if SOH */
  1537.     cchksum = cchksum + t;        /* Update checksum */
  1538.     *num = unchar(t);        /* Packet number */
  1539.  
  1540.     t = fgetc(crfp);        /* Get character */
  1541.     if (!image) t &= 0177;        /* Handle parity */
  1542.     if (t == SOH) continue;        /* Resynchronize if SOH */
  1543.     cchksum = cchksum + t;        /* Update checksum */
  1544.     type = t;            /* Packet type */
  1545.  
  1546.     for (i=0; i<*len; i++)        /* The data itself, if any */
  1547.     {                /* Loop for character count */
  1548.         t = fgetc(crfp);        /* Get character */
  1549.         if (!image) t &= 0177;    /* Handle parity */
  1550.         if (t == SOH) continue;    /* Resynch if SOH */
  1551.         cchksum = cchksum + t;    /* Update checksum */
  1552.         data[i] = t;        /* Put it in the data buffer */
  1553.     }
  1554.     data[*len] = 0;            /* Mark the end of the data */
  1555.  
  1556.     t = fgetc(crfp);        /* Get last character (checksum) */
  1557.     rchksum = unchar(t);        /* Convert to numeric */
  1558.     t = fgetc(crfp);        /* Get EOL character and toss it */
  1559.     if (!image) t &= 0177;        /* Handle parity */
  1560.     if (t == SOH) continue;        /* Resynchronize if SOH */
  1561.     done = TRUE;            /* Got checksum, done */
  1562.     }
  1563.  
  1564. #if UCB4X | MVUX
  1565.     alarm(0);                /* Disable the timer interrupt */
  1566. #endif
  1567.  
  1568.     if (debug>1)            /* Display incoming packet */
  1569.     {
  1570.     if (data != NULL)
  1571.         data[*len] = '\0';        /* Null-terminate data to print it */
  1572.     printf("  rpack type: %c\n",type);
  1573.     printf("     num:  %d\n",*num);
  1574.     printf("     len:  %d\n",*len);
  1575.     if (data != NULL)
  1576.         printf("        data: \"%s\"\n",data);
  1577.     }
  1578.                     /* Fold in bits 7,8 to compute */
  1579.     cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
  1580.  
  1581.     if (cchksum != rchksum) return(FALSE);
  1582.  
  1583.     return(type);            /* All OK, return packet type */
  1584. }
  1585.  
  1586.  
  1587. /*  r p a r
  1588.  *
  1589.  *  Get the other host's send-init parameters
  1590.  *
  1591.  */
  1592.  
  1593. static void rpar(data)
  1594. char data[];
  1595. {
  1596.     spsiz = unchar(data[0]);        /* Maximum send packet size */
  1597.     timint = unchar(data[1]);        /* When I should time out */
  1598.     pad = unchar(data[2]);        /* Number of pads to send */
  1599.     padchar = ctl(data[3]);        /* Padding character to send */
  1600.     eol = unchar(data[4]);        /* EOL character I must send */
  1601.     quote = data[5];            /* Incoming data quote character */
  1602. }
  1603.  
  1604.  
  1605. /*
  1606.  *  s b r e a k
  1607.  *
  1608.  *  Send Break (EOT)
  1609.  */
  1610.  
  1611. static int sbreak()
  1612. {
  1613.     int num, len;            /* Packet number, length */
  1614.     if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
  1615.  
  1616.     spack('B',n,0,packet);        /* Send a B packet */
  1617.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  1618.     {
  1619.     case 'N':            /* NAK, just stay in this state, */
  1620.         num = (--num<0 ? 63:num);    /* unless NAK for previous packet, */
  1621.         if (n != num)        /* which is just like an ACK for */
  1622.         return(state);        /* this packet so fall thru to... */
  1623.  
  1624.     case 'Y':            /* ACK */
  1625.         if (n != num) return(state); /* If wrong ACK, fail */
  1626.         numtry = 0;            /* Reset try counter */
  1627.         n = (n+1)%64;        /* and bump packet count */
  1628.         return('C');        /* Switch state to Complete */
  1629.  
  1630.      case 'E':            /* Error packet received */
  1631.          prerrpkt(recpkt);        /* Print it out and */
  1632.          return('A');        /* abort */
  1633.  
  1634.     case FALSE: return(state);    /* Receive failure, stay in B */
  1635.  
  1636.     default:    return ('A');    /* Other, "abort" */
  1637.    }
  1638. }
  1639.  
  1640.  
  1641. /*
  1642.  *  s d a t a
  1643.  *
  1644.  *  Send File Data
  1645.  */
  1646.  
  1647. static int sdata()
  1648. {
  1649.     int num, len;            /* Packet number, length */
  1650.  
  1651.     if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
  1652.  
  1653.     spack('D',n,size,packet);        /* Send a D packet */
  1654.     switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  1655.     {            
  1656.     case 'N':            /* NAK, just stay in this state, */
  1657.         num = (--num<0 ? 63:num);    /* unless it's NAK for next packet */
  1658.         if (n != num)        /* which is just like an ACK for */
  1659.         return(state);        /* this packet so fall thru to... */
  1660.         
  1661.     case 'Y':            /* ACK */
  1662.         if (n != num) return(state); /* If wrong ACK, fail */
  1663.         numtry = 0;            /* Reset try counter */
  1664.         n = (n+1)%64;        /* Bump packet count */
  1665.         if ((size = bufill(packet)) == EOF) /* Get data from file */
  1666.         return('Z');        /* If EOF set state to that */
  1667.         return('D');        /* Got data, stay in state D */
  1668.  
  1669.      case 'E':            /* Error packet received */
  1670.          prerrpkt(recpkt);        /* Print it out and */
  1671.          return('A');        /* abort */
  1672.  
  1673.     case FALSE: return(state);    /* Receive failure, stay in D */
  1674.  
  1675.     default:    return('A');    /* Anything else, "abort" */
  1676.     }
  1677. }
  1678.  
  1679.  
  1680. /*
  1681.  *  s e n d s w
  1682.  *
  1683.  *  Sendsw is the state table switcher for sending files.  It loops until
  1684.  *  either it finishes, or an error is encountered.  The routines called
  1685.  *  by sendsw are responsible for changing the state.
  1686.  *
  1687.  */
  1688.  
  1689. static int sendsw()
  1690. {
  1691.  
  1692.     state = 'S';            /* Send initiate is the start state */
  1693.     n = 0;                /* Initialize message number */
  1694.     numtry = 0;                /* Say no tries yet */
  1695.     while(TRUE)                /* Do this as long as necessary */
  1696.     {
  1697.     if (debug) printf("sendsw state: %c\n",state);
  1698.     switch(state)
  1699.     {
  1700.         case 'S':    state = sinit();  break; /* Send-Init */
  1701.         case 'F':    state = sfile();  break; /* Send-File */
  1702.         case 'D':    state = sdata();  break; /* Send-Data */
  1703.         case 'Z':    state = seof();      break; /* Send-End-of-File */
  1704.         case 'B':    state = sbreak(); break; /* Send-Break */
  1705.         case 'C':    return (TRUE);         /* Complete */
  1706.         case 'A':    return (FALSE);         /* "Abort" */
  1707.         default:    return (FALSE);         /* Unknown, fail */
  1708.     }
  1709.     }
  1710. }
  1711.  
  1712.  
  1713. /*
  1714.  *  s e o f
  1715.  *
  1716.  *  Send End-Of-File.
  1717.  */
  1718.  
  1719. static int seof()
  1720. {
  1721.     int num, len;            /* Packet number, length */
  1722.     if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
  1723.  
  1724.     spack('Z',n,0,packet);        /* Send a 'Z' packet */
  1725.     switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  1726.     {
  1727.     case 'N':            /* NAK, just stay in this state, */
  1728.         num = (--num<0 ? 63:num);    /* unless it's NAK for next packet, */
  1729.         if (n != num)        /* which is just like an ACK for */
  1730.         return(state);        /* this packet so fall thru to... */
  1731.  
  1732.     case 'Y':            /* ACK */
  1733.         if (n != num) return(state); /* If wrong ACK, hold out */
  1734.         numtry = 0;            /* Reset try counter */
  1735.         n = (n+1)%64;        /* and bump packet count */
  1736.         if (debug) printf("      Closing input file %s, ", filename);
  1737.         fclose(fp);            /* Close the input file */
  1738.         fp = NULL;            /* Set flag indicating no file open */ 
  1739.  
  1740.         if (debug) printf("looking for next file...\n");
  1741.         if (file == files)        /* Any more files ? */
  1742.         return('B');        /* No */
  1743.         filename = filenames[file++];
  1744.         if (debug) printf("      New file is %s\n", filename);
  1745.         return('F');        /* More files, switch state to F */
  1746.  
  1747.      case 'E':            /* Error packet received */
  1748.          prerrpkt(recpkt);        /* Print it out and */
  1749.          return('A');        /* abort */
  1750.  
  1751.     case FALSE: return(state);    /* Receive failure, stay in Z */
  1752.  
  1753.     default:    return('A');    /* Something else, "abort" */
  1754.     }
  1755. }
  1756.  
  1757.  
  1758. /*
  1759.  *  s f i l e
  1760.  *
  1761.  *  Send File Header.
  1762.  */
  1763.  
  1764. static int sfile()
  1765. {
  1766.     int num, len;            /* Packet number, length */
  1767.     char filnam1[50],            /* Converted file name */
  1768.     *newfilnam,            /* Pointer to file name to send */
  1769.     *cp;                /* char pointer */
  1770.  
  1771.     if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
  1772.     
  1773.     if (fp == NULL)            /* If not already open, */
  1774.     {    if (debug) printf("   Opening %s for sending.\n", filename);
  1775.     fp = fopen(filename,"r");    /* open the file to be sent */
  1776.     if (fp == NULL)            /* If bad file pointer, give up */
  1777.     {
  1778.         error("Cannot open file %s", filename);
  1779.         return('A');
  1780.     }
  1781.     }
  1782.  
  1783.     strcpy(filnam1, filename);        /* Copy file name */
  1784.     newfilnam = cp = filnam1;
  1785.     while (*cp != '\0')            /* Strip off all leading directory */
  1786.     if (*cp++ == '/')        /* names (ie. up to the last /). */
  1787.         newfilnam = cp;
  1788.  
  1789.     if (convert)            /* Convert lower case to upper    */
  1790.     for (cp = newfilnam; *cp != '\0'; cp++)
  1791.         if (*cp >= 'a' && *cp <= 'z')
  1792.         *cp ^= 040;
  1793.  
  1794.     len = cp - newfilnam;        /* Compute length of new filename */
  1795.  
  1796.     printmsg("Sending %s as %s",filename,newfilnam);
  1797.  
  1798.     spack('F',n,len,newfilnam);        /* Send an F packet */
  1799.     switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  1800.     {            
  1801.     case 'N':            /* NAK, just stay in this state, */
  1802.         num = (--num<0 ? 63:num);    /* unless it's NAK for next packet */
  1803.         if (n != num)        /* which is just like an ACK for */ 
  1804.         return(state);        /* this packet so fall thru to... */
  1805.  
  1806.     case 'Y':            /* ACK */
  1807.         if (n != num) return(state); /* If wrong ACK, stay in F state */
  1808.         numtry = 0;            /* Reset try counter */
  1809.         n = (n+1)%64;        /* Bump packet count */
  1810.         size = bufill(packet);    /* Get first data from file */
  1811.         return('D');        /* Switch state to D */
  1812.  
  1813.      case 'E':            /* Error packet received */
  1814.          prerrpkt(recpkt);        /* Print it out and */
  1815.          return('A');        /* abort */
  1816.  
  1817.         case FALSE: return(state);    /* Receive failure, stay in F state */
  1818.  
  1819.         default:    return('A');    /* Something else, just "abort" */
  1820.     }
  1821. }
  1822.  
  1823.  
  1824. /*
  1825.  *  s i n i t
  1826.  *
  1827.  *  Send Initiate: send this host's parameters and get other side's back.
  1828.  */
  1829.  
  1830. static int sinit()
  1831. {
  1832.     int num, len;            /* Packet number, length */
  1833.  
  1834.     if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
  1835.     spar(packet);            /* Fill up init info packet */
  1836.  
  1837.     flushinput();            /* Flush pending input */
  1838.  
  1839.     spack('S',n,6,packet);        /* Send an S packet */
  1840.     switch(rpack(&len,&num,recpkt))    /* What was the reply? */
  1841.     {
  1842.     case 'N':  return(state);    /* NAK, try it again */
  1843.  
  1844.     case 'Y':            /* ACK */
  1845.         if (n != num)        /* If wrong ACK, stay in S state */
  1846.         return(state);        /* and try again */
  1847.         rpar(recpkt);        /* Get other side's init info */
  1848.  
  1849.         if (eol == 0) eol = '\n';    /* Check and set defaults */
  1850.         if (quote == 0) quote = '#';
  1851.  
  1852.         numtry = 0;            /* Reset try counter */
  1853.         n = (n+1)%64;        /* Bump packet count */
  1854.         return('F');        /* OK, switch state to F */
  1855.  
  1856.      case 'E':            /* Error packet received */
  1857.          prerrpkt(recpkt);        /* Print it out and */
  1858.          return('A');        /* abort */
  1859.  
  1860.     case FALSE: return(state);    /* Receive failure, try again */
  1861.  
  1862.     default: return('A');        /* Anythig else, just "abort" */
  1863.    }
  1864.  }
  1865.  
  1866.  
  1867. /*
  1868.  *  s p a c k
  1869.  *
  1870.  *  Send a Packet
  1871.  */
  1872.  
  1873. static int spack(type,num,len,data)
  1874. char type, *data;
  1875. int num, len;
  1876. {
  1877.     int i;                /* Character loop counter */
  1878.     char chksum, buffer[100];        /* Checksum, packet buffer */
  1879.     register char *bufp;        /* Buffer pointer */
  1880.  
  1881.     if (debug>1)            /* Display outgoing packet */
  1882.     {
  1883.     if (data != NULL)
  1884.         data[len] = '\0';        /* Null-terminate data to print it */
  1885.     printf("  spack type: %c\n",type);
  1886.     printf("     num:  %d\n",num);
  1887.     printf("     len:  %d\n",len);
  1888.     if (data != NULL)
  1889.         printf("        data: \"%s\"\n",data);
  1890.     }
  1891.   
  1892.     bufp = buffer;            /* Set up buffer pointer */
  1893.     for (i=1; i<=pad; i++)         /* Issue any padding */
  1894.     fputc(padchar, cwfp);
  1895.  
  1896.     *bufp++ = SOH;            /* Packet marker, ASCII 1 (SOH) */
  1897.     *bufp++ = tochar(len+3);        /* Send the character count */
  1898.     chksum  = tochar(len+3);        /* Initialize the checksum */
  1899.     *bufp++ = tochar(num);        /* Packet number */
  1900.     chksum += tochar(num);        /* Update checksum */
  1901.     *bufp++ = type;            /* Packet type */
  1902.     chksum += type;            /* Update checksum */
  1903.  
  1904.     for (i=0; i<len; i++)        /* Loop for all data characters */
  1905.     {
  1906.     *bufp++ = data[i];        /* Get a character */
  1907.     chksum += data[i];        /* Update checksum */
  1908.     }
  1909.     chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
  1910.     *bufp++ = tochar(chksum);        /* Put it in the packet */
  1911.     *bufp = eol;            /* Extra-packet line terminator */
  1912.     fwrite(buffer, 1, bufp - buffer + 1, cwfp);    /* Send the packet */
  1913. }
  1914.  
  1915.  
  1916. /*
  1917.  *  s p a r
  1918.  *
  1919.  *  Fill the data array with my send-init parameters
  1920.  *
  1921.  */
  1922.  
  1923. static void spar(data)
  1924. char data[];
  1925. {
  1926.     data[0] = tochar(MAXPACK);        /* Biggest packet I can receive */
  1927.     data[1] = tochar(MYTIME);        /* When I want to be timed out */
  1928.     data[2] = tochar(MYPAD);        /* How much padding I need */
  1929.     data[3] = ctl(MYPCHAR);        /* Padding character I want */
  1930.     data[4] = tochar(MYEOL);        /* End-Of-Line character I want */
  1931.     data[5] = MYQUOTE;            /* Control-Quote character I send */
  1932. }
  1933.  
  1934.  
  1935. #if MVUX
  1936. /*
  1937.  *  t a l k e r 
  1938.  *
  1939.  *  Special MV/UX sub-task to send keyboard characters to remote system.
  1940.  *
  1941.  */
  1942.  
  1943. static void talker()
  1944. {
  1945.     int    cc;                /* I/O buffer */
  1946.  
  1947.     while (1)
  1948.     {
  1949.         if ((cc = strip(fgetc(trfp))) == escchr)
  1950.         {
  1951.         if ((cc = strip(fgetc(trfp))) == escchr)
  1952.             fputc(escchr, lwfp);
  1953.         else
  1954.         {
  1955.             switch (cc)
  1956.             {
  1957.             case '#':        /* simulate break */
  1958.                 break;
  1959.             
  1960.             case 'c':        /* return to local machine */
  1961.             case 'C':
  1962.                 terminate = 1;
  1963.                 break;
  1964.  
  1965.             case 'h':        /* verify running status */
  1966.             case 'H':
  1967.                 fprintf(twfp, "\r\n(Kermit)\r\n");
  1968.                 fputs("\r\n\r\n", twfp);
  1969.                 break;
  1970.  
  1971.             default:
  1972.                 fputc(BELL, twfp);
  1973.                 break;
  1974.             }
  1975.         }
  1976.         }
  1977.         else
  1978.         fputc(cc, lwfp);
  1979.     }
  1980. }
  1981. #endif
  1982.  
  1983.  
  1984. /*
  1985.  *  u s a g e 
  1986.  *
  1987.  *  Print summary of usage info and quit
  1988.  *
  1989.  */
  1990.  
  1991. static void usage()
  1992. {
  1993. #if UCB4X | MVUX
  1994.     printf("Usage: kermit c[lbe line baud esc.char]     (connect mode)\n");
  1995.     printf("or:       kermit s[diflb line baud] file ...     (send mode)\n");
  1996.     printf("or:       kermit r[diflb line baud]         (receive mode)\n");
  1997. #else
  1998.     printf("Usage: kermit c[le line esc.char]         (connect mode)\n");
  1999.     printf("or:       kermit s[difl line] file ...         (send mode)\n");
  2000.     printf("or:       kermit r[difl line]             (receive mode)\n");
  2001. #endif
  2002.     exit(1);
  2003. }
  2004.