home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / honeywellgcosb / hdps8.b next >
Text File  |  2020-01-01  |  92KB  |  3,490 lines

  1. /*  kermit/s/manif    >> Begin <<  */
  2. /*
  3.  *  Kermit-GCOS
  4.  *
  5.  *  An implementation of the Kermit file transfer protocol for
  6.  *  Honeywell GCOS systems.
  7.  *
  8.  *  Author:  John L. Huxtable
  9.  *           University of Kansas Academic Computing Services
  10.  *           Lawrence, Kansas 66045
  11.  *           HLSUA Site Code: UKAN
  12.  *  Date:    03/07/85
  13.  *
  14.  *  This is a remote-only Kermit since GCOS cannot originate
  15.  *  a connection with another system.  It can use either paper-tape
  16.  *  mode input or normal input for packets depending on whether
  17.  *  or not all ASCII printing characters can be received in normal
  18.  *  mode.  (The default is +TapeMode).  It can transfer text files
  19.  *  and with the BYtestream and BItsream format specifications
  20.  *  any file on GCOS.  Instead of wildcard send Kermit-GCOS contains
  21.  *  an indeX file option for specifying a file which contains a list
  22.  *  of file names and options for sending.  Kermit-GCOS has 8th-bit
  23.  *  prefixing and repeat count prefixing.  It does not have alternate
  24.  *  checksums.  It can act as a server with the following remote
  25.  *  commands:  Bye, CWD, Erase, Finish, Get, Logout, Send, Status
  26.  *  and Type.  Remote Directory and Remote Space are optionally
  27.  *  available, as determined by the values of flags wat_lc and
  28.  *  wat_smcl within the EXTRN definitions, below.  Commands can
  29.  *  be entered on the command line used to invoke Kermit-GCOS or
  30.  *  through an interactive prompt mode.  A HELP command provides
  31.  *  interactive users with limited support information.  See the
  32.  *  user documentation for more information.
  33.  *
  34.  *  For information about obtaining Kermit programs for other
  35.  *  systems, contact:
  36.  *
  37.  *  KERMIT Distribution
  38.  *  Columbia University Center for Computing Activities
  39.  *  7th Floor, Watson Laboratory
  40.  *  612 West 115th Street
  41.  *  New York, N.Y.  10025
  42.  */
  43.  
  44.  
  45. /*
  46.  *    MANIF    -- Manifests for Kermit.
  47.  */
  48.  
  49. %b/manif/.bset
  50. %b/manif/t.drls
  51.  
  52.  
  53. /*
  54.  *    .Bset Options.
  55.  */
  56.  
  57. OP.FILE        =  0;
  58. OP.QUERY    =  1;
  59. OP.DONE        =  2;
  60. OP.EXIT        =  3;
  61. OP.QUIT        =  4;
  62. OP.HELP        =  5;
  63. OP.RECV        =  6;
  64. OP.SEND        =  7;
  65. OP.SERV        =  8;
  66. OP.SET        =  9;
  67. OP.STAT        = 10;
  68. OP.INDEX    = 11;
  69. OP.FORMAT    = 12;
  70. OP.CHKSUM    = 13;
  71. OP.DEBUG    = 14;
  72. OP.DELAY    = 15;
  73. OP.RANDOM    = 16;
  74. OP.DISCARD    = 17;
  75. OP.RETRY    = 18;
  76. OP.PERM        = 19;
  77. OP.TAPMOD    = 20;
  78. OP.XON        = 21;
  79. OP.OVWRITE    = 22;
  80. OP.SEOL        = 23;
  81. OP.SPKST    = 24;
  82. OP.CWD        = 25;
  83.  
  84.  
  85. /*
  86.  *    Parameters which may need to be changed for your machine:
  87.  *        MAXPACK, BRKCHR, MY...
  88.  */
  89.  
  90.  
  91. STDIN    =  0;
  92. STDOUT    =  1;
  93.  
  94. EOF        = -1;
  95. ERR        = -1;
  96.  
  97. TRUE    = 1;
  98. FALSE    = 0;
  99.  
  100. _SIZE    = /4;
  101.  
  102. MAX_BRKS    = 10;
  103.  
  104. DBG_OFF        =  0;
  105. DBG_STATES    =  1;
  106. DBG_PACKETS    =  2;
  107. DBG_LOGFILE    =  4;
  108. DBG_ALL        = -1;
  109.  
  110. FM_TEXT    = 0;
  111. FM_ASA9    = 1;
  112. FM_BITS    = 2;
  113.  
  114. FL_SIZE    = 5;
  115. FL_NAME    = 0;
  116. FL_MODE    = 1;
  117. FL_RAND    = 2;
  118. FL_DISC    = 3;
  119. FL_PERM    = 4;
  120. FL_OVWR    = 5;
  121.  
  122. PAR_CMD    = 0;
  123. PAR_FIL    = 1;
  124. PAR_IDX    = 2;
  125. PAR_RMT    = 3;
  126.  
  127.  
  128. /*
  129.  *    Stuff what comes over comm-lines.
  130.  */
  131.  
  132. NULL    = 0;                    /*  ASCII NULL  */
  133. SOH        = 1;                    /*  Start of header  */
  134. LF        = 10;                    /*  ASCII Line Feed  */
  135. CR        = 13;                    /*  ASCII Carriage Return  */
  136. DC1        = 17;                    /*  ASCII DC1 (XON)  */
  137. DC3        = 19;                    /*  ASCII DC3 (XOFF)  */
  138. DEL        = 127;                    /*  Delete (rubout)  */
  139.  
  140.  
  141. /*
  142.  *    Offsets in the Send-init packet.
  143.  */
  144.  
  145. SI_MAXPACK    = 0;
  146. SI_MYTIME    = 1;
  147. SI_MYPAD    = 2;
  148. SI_MYPCHAR    = 3;
  149. SI_MYEOL    = 4;
  150. SI_MYQUOTE    = 5;
  151. SI_MYBQUOTE    = 6;
  152. SI_MYCHECK    = 7;
  153. SI_MYREPTC    = 8;
  154. SI_MYCAPS    = 9;
  155. SI_MYSIZE    = 10;
  156.  
  157.  
  158. /*
  159.  *    This kermit's init parameters
  160.  */
  161.  
  162. MAXPACK        = 94;                /*  Maximum incoming packet size (max 94)  */
  163. MYTIME        = 10;                /*  Seconds after which I should be timed out  */
  164. MYPAD        = 0;                /*  Number of padding characters I will need (max 94)  */
  165. MYPCHAR        = DEL;                /*  Padding character I need  */
  166. MYEOL        = DC3;                /*  End-Of-Line character I need  */
  167. MYQUOTE        = '#';                /*  Quote character I will use  */
  168. MYBQUOTE    = '&';                /*  Eighth-bit quote char:  ' ' => none  */
  169. MYREPTC        = '~';                /*  Repeat prefix:  ' ' => none  */
  170. MYCHECK        = '1';                /*  Checksum type: '1' => default  */
  171. MYCAPS        = (CAP_TIMO|CAP_SERV);    /*  capability mask  */
  172. CAP_TIMO    = 0;                /*  I can do timeouts: 0 => no, 040 => yes  */
  173. CAP_SERV    = 020;                /*  I have server mode: 0 => no, 020 => yes  */
  174. INIT_SIZ    = 10;                /*  number of parameters we will look at in an init pak  */
  175.  
  176. MAXTIM    = 30;                    /*  Maximum timeout interval  */
  177. MINTIM    = 2;                    /*  Minumum timeout interval  */
  178. MAXTRY    = 5;                    /*  Times to retry a packet  */
  179. ESCCHR    = '^';                    /*  connect mode escape char  */
  180.  
  181. MAXLINE    = 256;                    /*  Size of packet buffers  */
  182. MAXNAM    = 12;                    /*  Maximum name file name length  */
  183. /*  kermit/s/manif    >> End <<  */
  184. /*  kermit/s/extrn    >> Begin <<  */
  185. /*
  186.  *    EXTRN    -- EXTeRNal variables for Kermit.
  187.  */
  188.  
  189.  
  190. /*
  191.  *  Commands enabled?
  192.  */
  193.  
  194. wat_lc        { FALSE };            /*  Have Waterloo style LC command?  */
  195. wat_smcl    { FALSE };            /*  Have Waterloo style SMCL command?  */
  196. wat_cwd        { FALSE };            /*  Have Working Directories enabled?  */
  197.  
  198.  
  199. /*
  200.  *    .Bset Options.
  201.  */
  202.  
  203. .keep        { 1 };
  204. .process    { 0 };
  205.  
  206. .optab [] {
  207.     "?",                    BLNK_KWD,
  208.     "Done",                    COMM_KWD,
  209.     "Exit",                    COMM_KWD,
  210.     "Quit",                    COMM_KWD,
  211.     "Help",                    COMM_KWD,
  212.     "Receive",                COMM_KWD,
  213.     "SENd",                    COMM_KWD,
  214.     "SERver",                COMM_KWD,
  215.     "SET",                    COMM_KWD,
  216.     "STatus",                COMM_KWD,
  217.     "indeX",                SVAL_KWD,
  218.     "Format",                SVAL_KWD,
  219.     "Checksum",                SVAL_KWD,
  220.     "DeBug",                SVAL_KWD,
  221.     "Delay",                NVAL_KWD,
  222.     "Random",                PLUS_KWD|DASH_KWD,
  223.     "Discard",                PLUS_KWD|DASH_KWD,
  224.     "RetryPacket",            PLUS_KWD|DASH_KWD,
  225.     "Permanent",            PLUS_KWD|DASH_KWD,
  226.     "TapeMode",                PLUS_KWD|DASH_KWD,
  227.     "Xon",                    PLUS_KWD|DASH_KWD,
  228.     "OverWrite",            PLUS_KWD|DASH_KWD,
  229.     "SendEndOfLine",        NVAL_KWD,
  230.     "SendStartofPacket",    NVAL_KWD,
  231.     "ChangeWorkingDirectory",    SVAL_KWD,
  232.     -1 };
  233.  
  234.  
  235. /*
  236.  *    Global characters
  237.  */
  238.  
  239. stpkt    { SOH };        /*  Start-of-packet character to send (char)  */
  240. padchar    { NULL };        /*  Padding character to send (char)  */
  241. eol        { CR };            /*  End-Of-Line character to send (char)  */
  242. quotec    { '#' };        /*  Incoming quote char. for control chars (char)  */
  243. bquote    { MYBQUOTE };    /*  Incoming quote character for 8th-bit (char)  */
  244. reptc    { MYREPTC };    /*  Incoming repeat prefix character (char)  */
  245.  
  246. state;                    /*  Present state of the automaton (char)  */
  247. lastpk;                    /*  Last received packet type (char)  */
  248. recpkt [MAXLINE];        /*  Receive packet buffer (char)  */
  249. packet [MAXLINE];        /*  Packet buffer (char)  */
  250. filnam [MAXLINE _SIZE];    /*  current file name (char)  */
  251. msghdr    { "Kermit-GCOS" };    /*  Message header  */
  252.  
  253. wc    [MAXLINE _SIZE];    /*  Working Catalog (char)  */
  254.  
  255. /*
  256.  *     Global Variables
  257.  */
  258.  
  259. size;                /*  Size of present data (int)  */
  260. n;                    /*  Message number (int)  */
  261. rpsiz;                /*  Maximum receive packet size (int)  */
  262. spsiz    { 80 };        /*  Maximum send packet size (int)  */
  263. pad        { 0 };        /*  How much padding to send (int)  */
  264. timint    { 10 };        /*  Timeout for foreign host on sends (int)  */
  265. numtry;                /*  Times this packet retried (int)  */
  266. oldtry;                /*  Times previous packet retried (int)  */
  267. fd        { ERR };    /*  file pointer of file to read/write (file)  */
  268. fderr    { ERR };    /*  file to send debug output to  */
  269. fdtap    { ERR };    /*  file to read tape mode input from  */
  270. image    { FALSE };    /*  YES means 8-bit mode (int)  */
  271. remspd;                /*  speed of this tty (int)  */
  272. remote    { FALSE };    /*  YES means we're a remote host kermit (int)  */
  273. debug    { DBG_OFF };        /*  Type of debugging done (int)  */
  274. eoflg;                /*  EOF flag for Send Data state (int)  */
  275. hlpflg;                /*  Flag for Help command (int)  */
  276. rflg;                /*  Flag for receive mode (int)  */
  277. sflg;                /*  Flag for send mode (int)  */
  278. srvflg;                /*  Flag for server mode (int)  */
  279. setflg;                /*  Flag for Set command (int)  */
  280. stsflg;                /*  Flag for Status command (int)  */
  281. dobquo    { FALSE };    /*  YES => do 8th bit quoting (int)  */
  282. dorept    { FALSE };    /*  YES => do repeat prefixing (int)  */
  283. xonwait    { FALSE };    /*  YES => wait for XON before each packet send (int)  */
  284. imgflg    { FALSE };    /*  YES => image-mode command flag set (int)  */
  285. tapflg    { TRUE };    /*  YES => use paper tape mode for input  */
  286. binfil    { FM_TEXT };    /*  YES => do 8 bit i/o on this file (int)  */
  287.  
  288. chktype    { 1 };        /*  Checksum type  */
  289. delay    { 10 };        /*  Delay time  */
  290. randflg    { 0 };        /*  YES => make file random  */
  291. discflg    { 0 };        /*  YES => Discard incomplete file  */
  292. retryflg    { 1 };    /*  YES => Retry every packet  */
  293. prmflg    { 1 };        /*  YES => Always use permanent file  */
  294. ovrflg    { 0 };        /*  YES => Overwrite existing files  */
  295.  
  296. eolpend    { FALSE };    /*  TRUE => We have a pending eol  */
  297.  
  298. curflgs [] {        /*  Vector containing the current file mode settings.  */
  299.     0,                /*  File name  */
  300.     FM_TEXT,        /*  FL_BINF  */
  301.     FALSE,            /*  FL_RAND  */
  302.     FALSE,            /*  FL_DISC  */
  303.     TRUE,            /*  FL_PERM  */
  304.     FALSE            /*  FL_OVWR  */
  305.     };
  306.  
  307. crctab [] {
  308.     0000000,
  309.     0010201,
  310.     0020402,
  311.     0030603,
  312.     0041004,
  313.     0051205,
  314.     0061406,
  315.     0071607,
  316.     0102010,
  317.     0112211,
  318.     0122412,
  319.     0132613,
  320.     0143014,
  321.     0153215,
  322.     0163416,
  323.     0173617
  324.     };
  325.  
  326. crctb2 [] {
  327.     0000000,
  328.     0010611,
  329.     0021422,
  330.     0031233,
  331.     0043044,
  332.     0053655,
  333.     0062466,
  334.     0072277,
  335.     0106110,
  336.     0116701,
  337.     0127632,
  338.     0137323,
  339.     0145154,
  340.     0155745,
  341.     0164576,
  342.     0174367
  343.     };
  344.  
  345. fillst;                /*  List of files to send  */
  346. filcnt;                /*  Number of files supplied  */
  347.  
  348. /*
  349.  *    io error statuses from putrec and write (DRL DIO)
  350.  */
  351. io.err[] {
  352.     0,
  353.     "Not in aft",
  354.     "Device attention",
  355.     "End of reel",
  356.     "Physical end of file",
  357.     "Bad instruction",
  358.     "Status 06",
  359.     "Status 07",
  360.     "Channel busy",
  361.     "Status 11",
  362.     "Status 12",
  363.     "Data alert",
  364.     "Status 14",
  365.     "Status 15",
  366.     "Status 16",
  367.     "Physical end of file"
  368.     };
  369.  
  370. /*
  371.  *    Variables to handle paper tape mode buffering.
  372.  */
  373.  
  374. tpbuf[64];            /*  Buffer to hold tap* sectors  */
  375. tpcnt    { 0 };        /*  Number of characters in tap* buffer  */
  376. tpidx    { 0 };        /*  Index of next character in tap* buffer  */
  377. tpsect    { 0 };        /*  Next sector of tap* to be read  */
  378. tpeof    { 1 };        /*  Do we need another DRL T.TPIN?  */
  379.  
  380. /*
  381.  *    Variables to handle file buffering.
  382.  */
  383.  
  384. flbuf[320];            /*  Buffer to hold the current record  */
  385. flcnt    { 0 };        /*  Number of characters in the record  */
  386. flidx    { 0 };        /*  Index of next character in the record  */
  387. flseg    { 0 };        /*  Segment number of partitioned record  */
  388. flsec    { 0 };        /*  Index of sector to read next  */
  389. flcrcnt    { 0 };        /*  Count of pending carriage returns  */
  390. flunbuf[10] { 0 };    /*  Ungot character buffer  */
  391. /*  kermit/s/extrn    >> End <<  */
  392. /*  kermit/s/main    >> Begin <<  */
  393. /*
  394.  *    MAIN    -- MAINline for Kermit.
  395.  *
  396.  *    This is the main body of Kermit which calls the other
  397.  *    functions and procedures.
  398.  */
  399.  
  400. main() {
  401.     extrn    msghdr, wc, curflgs, fillst, filcnt;
  402.     auto    retn, cmdstr[MAXLINE _SIZE];
  403.  
  404.     printf( "%s:  Version 1.1*n*n", msghdr );
  405.     flush();
  406.  
  407.     getwd( wc );
  408.  
  409.     nobrks( MAX_BRKS );
  410.  
  411.     reread();
  412.     getstr( cmdstr );
  413.     rep.st( cmdstr, 0, "", .span( cmdstr, .break( cmdstr, 0, " " ), " " ) );
  414.  
  415.     retn    = getcmd( cmdstr, PAR_CMD, curflgs );
  416.  
  417.     if( retn == 'a' || !retn )        /*  It aborted  */
  418.         exit( ERR );                /*  End kermit with an error status  */
  419.     else if( retn == 'n' ) {        /*  No command given  */
  420.         repeat {
  421.             nobrks( MAX_BRKS );
  422.             prompt( "%s>", msghdr );
  423.             getstr( cmdstr );
  424.             if( char(cmdstr,0) == '!' ) {
  425.                 rep.st( cmdstr, 0, "", 1 );
  426.                 system( cmdstr );
  427.                 }
  428.             else {
  429.                 retn    = getcmd( cmdstr, PAR_CMD, curflgs );
  430.                 if( retn == 'd' )
  431.                     break;
  432.                 }
  433.             }
  434.         }
  435.  
  436.     }    /*  main  */
  437. /*  kermit/s/main    >> End <<  */
  438. /*  kermit/s/bufemp    >> Begin <<  */
  439. /*
  440.  *    BUFEMP    -- BUFfer EMPty.
  441.  *
  442.  *    Get data from an incoming packet into a file
  443.  *    Control-quoting, 8-bit & repeat prefixes are done.
  444.  *    Note that parity stripping was already done in spack.
  445.  *
  446.  *    Assumes putch works with 8-bit data.
  447.  *
  448.  *        buffer    -- the buffer
  449.  *        len        -- length
  450.  *
  451.  *        Returns 0 if successful, negative of the major
  452.  *        filesystem status if not.
  453.  */
  454.  
  455. bufemp( buffer, len ) {
  456.     extrn    quotec, bquote, reptc, dobquo, dorept;
  457.     auto    sts, nrep, i, j, c, c7, c8;
  458.  
  459.     for( i = 0; i < len; ++i ) {                    /*  Loop through data  */
  460.         c    = buffer[i];                            /*  Get character  */
  461.  
  462.         if( !dorept || c != reptc )                    /*  Repeat prefix?  */
  463.             nrep    = 1;
  464.         else {
  465.             nrep    = unchar(buffer[++i]);            /*  Get the count  */
  466.             c        = buffer[++i];                    /*  Next char  */
  467.             }
  468.  
  469.         if( !dobquo || c != bquote )                /*  Eighth-bit quote?  */
  470.             c8    = 0;
  471.         else {
  472.             c8    = 128;                                /*  save bit's value  */
  473.             c    = buffer[++i];                        /*  Next char  */
  474.             }
  475.  
  476.         if( c == quotec ) {                            /*  A quoted char  */
  477.             c    = buffer[++i];                        /*  get the next char  */
  478.             if( (c7 = mask(c)) >= '?' && c7 <= '_' )
  479.                 c    = ctl(c);                        /*  Controlify the char  */
  480.             }
  481.  
  482.         c    |= c8;                        /*  Or in eighth bit  */
  483.  
  484.         for( j = 1; j <= nrep; ++j )    /*  Put out correct number of chars  */
  485.             if( (sts = putch( c )) < 0 )
  486.                 return( sts );
  487.         }
  488.  
  489.     return( 0 );
  490.     }    /*  bufemp  */
  491. /*  kermit/s/bufemp    >> End <<  */
  492. /*  kermit/s/bufill    >> Begin <<  */
  493. /*
  494.  *    BUFILL    -- BUFfer FILL.
  495.  *
  496.  *    Get a bufferful of data from the file that's being sent.
  497.  *    Control-quoting, 8-bit & repeat prefixes are done.
  498.  *
  499.  *    Assumes that getch returns 8-bit data.
  500.  */
  501.  
  502. bufill( buffer ) {
  503.     extrn    spsiz, dobquo, dorept;
  504.     auto    c, c1, c7, i, j;
  505.  
  506.     /*
  507.      *    Loop on characters until end of file
  508.      *    or the packet is full.
  509.      */
  510.  
  511.     i    = 0;
  512.     while( i + 1 < spsiz - 9 && (c = getch()) != EOF ) {
  513.         /*
  514.          *    Repeat prefixing enabled,
  515.          *    cannot do repeat counts for CR-LFs.
  516.          */
  517.         if( dorept && c != '*r' && c != '*n' ) {
  518.             for( j = 1; (c1 = getch()) == c; ++j )        /*  look for repeated chars  */
  519.                 if( j >= 94 )            /*  94 char repeat limit  */
  520.                     break;
  521.             ungtch(c1);                /*  put back the one that didn't match  */
  522.             if( j < 3 ) {            /*  If less than threshold for doing repeat  */
  523.                 for( ; j > 1; --j )    /*  put them back  */
  524.                     ungtch(c);
  525.                 }
  526.             else {
  527.                 buffer[i++]    = MYREPTC;            /*  repeat prefix  */
  528.                 buffer[i++]    = tochar(j);        /*  repeat count  */
  529.                 }
  530.             }
  531.  
  532.         if( c > 127 && dobquo ) {        /*  If eighth bit on  */
  533.             buffer[i++]    = MYBQUOTE;        /*  add eighth-bit quote  */
  534.             c    = mask(c);                /*  strip down to seven bits  */
  535.             }
  536.  
  537.         /*
  538.          *    Do we need to quote this char?
  539.          */
  540.  
  541.         c7    = mask(c);                    /*  A seven bit version of c  */
  542.         if( c7 < ' ' || c7 == DEL || c7 == MYQUOTE
  543.             || (c7 == MYBQUOTE && dobquo) || (c7 == MYREPTC && dorept) ) {
  544.             buffer[i++]    = MYQUOTE;        /*  add quote char  */
  545.             if( c7 < ' ' || c7 == DEL )
  546.                 c    = ctl(c);            /*  de-controlify control char  */
  547.             }
  548.  
  549.         buffer[i++]    = c;                /*  Use the eight bit version  */
  550.  
  551.         }    /*  while  */
  552.  
  553.     buffer[i]    = '*0';
  554.  
  555.     return( i );
  556.     }    /*  bufill  */
  557. /*  kermit/s/bufill    >> End <<  */
  558. /*  kermit/s/chksum    >> Begin <<  */
  559. /*
  560.  *    CHKSUM    -- compute the CHecKSUM.
  561.  *        Add the upper two bits into the lower six.
  562.  *        The Kermit Protocol Manual details how the checksum is formed.
  563.  */
  564.  
  565. chksum( sum, len, num, type, data ) {
  566.     extrn    chktype, crctab, crctb2;
  567.     auto    i, ch;
  568.  
  569.     if( chktype == 1 || type == 'S' || type == 'I' || type == 'R' )
  570.         return( (((sum&0300) >> 6) + sum) & 077 );
  571.     else if( chktype == 2 )
  572.         return( sum & 07777 );
  573.     else if( chktype == 3 ) {
  574.         ch    = tochar(len+3);
  575.         sum    = crctab[(ch>>4)&017] ^ crctb2[ch&017];
  576.         ch    = tochar(num) ^ (sum & 0377);
  577.         sum    = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
  578.         ch    = type ^ (sum & 0377);
  579.         sum    = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
  580.         for( i = 0; (ch = data[i]) != '*0'; ++i ) {
  581.             ch    ^= (sum & 0377);
  582.             sum    = (sum>>8) ^ crctab[(ch>>4)&017] ^ crctb2[ch&017];
  583.             }
  584.         }
  585.  
  586.     return( sum );
  587.     }    /*  chksum  */
  588. /*  kermit/s/chksum    >> End <<  */
  589. /*  kermit/s/ctl    >> Begin <<  */
  590. /*
  591.  *    CTL    -- ConTroLlify a character.
  592.  *
  593.  *    Turns a control character into a printable charcter and vice versa
  594.  *    by toggling the control bit (ie. ^A becomes  A and A becomes ^A).
  595.  */
  596.  
  597. ctl( ch ) {
  598.     auto    mask;
  599.  
  600.     return( ch ^ 64 );        /*  toggle the control bit  */
  601.  
  602.     }    /*  ctl  */
  603. /*  kermit/s/ctl    >> End <<  */
  604. /*  kermit/s/eprintf    >> Begin <<  */
  605. /*
  606.  *    EPRINTF    -- Error PRINTF.
  607.  *        If the appropriate debug states are set,
  608.  *        do a printf to the Error output.
  609.  */
  610.  
  611. eprintf( type, format, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) {
  612.     extrn    fderr, debug;
  613.  
  614.     if( debug & type ) {
  615.         if( fderr == ERR )
  616.             fderr    = open( "kerm**dbg", "wu" );
  617.         printf( fderr, "%r", &format );
  618.         }
  619.  
  620.     }    /*  eprintf  */
  621. /*  kermit/s/eprintf    >> End <<  */
  622. /*  kermit/s/errmsg    >> Begin <<  */
  623. /*
  624.  *    ERRMSG    -- send an ERRor MeSsaGe.
  625.  *
  626.  *    Print error message, or send it.
  627.  */
  628.  
  629. errmsg( format, a1, a2, a3, a4, a5, a6, a7, a8, a9 ) {
  630.     extrn    n, remote, packet, msghdr;
  631.     auto    len, str[MAXLINE _SIZE];
  632.  
  633.     print( str, "%s: %r", msghdr, &format );
  634.  
  635.     eprintf( DBG_ALL, "%s*n", str );
  636.  
  637.     if( !remote )
  638.         printf( -4, "%s*n", str );
  639.     else {
  640.         lchar( str, MAXPACK, '*0' );        /*  Truncate to legal size  */
  641.         len    = str2pkt( packet, str );
  642.         spack( 'E', n, len, packet );        /*  Send the error packet  */
  643.         flusheol();
  644.         }
  645.  
  646.     }    /*  errmsg  */
  647. /*  kermit/s/errmsg    >> End <<  */
  648. /*  kermit/s/errpkt    >> Begin <<  */
  649. /*
  650.  *    ERRPKT    -- print an ERRor PacKeT.
  651.  */
  652.  
  653. errpkt( pkt ) {
  654.     auto    str[MAXLINE _SIZE];
  655.  
  656.     pkt2str( str, pkt );
  657.     eprintf( DBG_ALL, "Error from remote Kermit: %s*n", str );
  658.  
  659.     }    /*  errpkt  */
  660. /*  kermit/s/errpkt    >> End <<  */
  661. /*  kermit/s/failmsg    >> Begin <<  */
  662. /*
  663.  *    FAILMSG    -- send a FAILure MeSsaGe.
  664.  *
  665.  *    Send message about a protocol failure.
  666.  */
  667.  
  668. failmsg( oldstate ) {
  669.     extrn    fd, remote, state, lastpk, filnam;
  670.     auto    i, line[MAXLINE _SIZE];
  671.  
  672.     i    = 1;
  673.     switch( state ) {        /*  Find the appropriate error message  */
  674.     case 'a':                /*  a message was already received or sent  */
  675.         return;
  676.     case 'm':
  677.         concat( line, "Retry limit exceeded" );
  678.         break;
  679.     case 'n':
  680.         concat( line, "Wrong packet number received" );
  681.         break;
  682.     case 'w':
  683.         print( line, "Wrong packet type %c received", lastpk );
  684.         break;
  685.     default:
  686.         concat( line, "Illegal internal state" );
  687.         }
  688.  
  689.     concat( line, line, " while in state " );
  690.     addchar( line, oldstate );            /*  Give the state  */
  691.     if( fd != ERR) {                    /*  Give the file, if open  */
  692.         concat( line, line, ", in file ", filnam );
  693.         }
  694.  
  695.     concat( line, line, "  " );
  696.     errmsg( line );                /*  Send error message to appropriate place  */
  697.     if( remote )
  698.         eprintf( DBG_ALL, line );        /*  Send a copy to ERROUT  */
  699.  
  700.     }    /*  failmsg  */
  701. /*  kermit/s/failmsg    >> End <<  */
  702. /*  kermit/s/fclose    >> Begin <<  */
  703. /*
  704.  *    FCLOSE    -- File CLOSE routine.
  705.  *        Returns 0 if successful, the negative of the major
  706.  *        filesystem status if not.
  707.  */
  708.  
  709. fclose( fd, errflg ) {
  710.     extrn    discflg, randflg, filnam, flbuf;
  711.     auto    fd2, nsec, sts;
  712.  
  713.     if( !discflg ) {
  714.         close( fd );
  715.         return( 0 );
  716.         }
  717.  
  718.     if( !errflg ) {
  719.         close( fd );
  720.         fd    = open( "kerm**tmp", randflg ? "rufeb" : "rufe" );
  721.         fd2    = open( filnam, randflg ? "wufeb" : "wufe" );
  722.         if( fd2 < 0 )
  723.             return( fd2 );
  724.         for( nsec = 0; read( fd, flbuf, nsec, 320 ) > 0; ++nsec )
  725.             if( (sts = write( fd2, flbuf, nsec, 320 )) < 0 )
  726.                 return( sts );
  727.         close( fd2 );
  728.         }
  729.  
  730.     close( fd );
  731.     retfil( "kerm**tmp" );
  732.  
  733.     return( 0 );
  734.     }    /*  fclose  */
  735. /*  kermit/s/fclose    >> End <<  */
  736. /*  kermit/s/flusheol    >> Begin <<  */
  737. /*
  738.  *    FLUSHEOL    -- FLUSH the End Of Line if necessary.
  739.  */
  740.  
  741. flusheol() {
  742.     extrn    eolpend, eol;
  743.  
  744.     if( eolpend ) {
  745.         putchar( eol );
  746.         flush();
  747.         }
  748.  
  749.     }    /*  flusheol  */
  750. /*  kermit/s/flusheol    >> End <<  */
  751. /*  kermit/s/generic    >> Begin <<  */
  752. /*
  753.  *    GENERIC    -- execute a GENERIC command.
  754.  */
  755.  
  756. generic( num, cmd, packet, flags ) {
  757.     extrn    msghdr, wc, flsyerr, wat_lc, wat_smcl;
  758.     auto    len, str, str2, unit, buf, temp, arglst[1], sts[1];
  759.  
  760.     str    = allocate( MAXLINE _SIZE );
  761.  
  762.     switch( cmd ) {        /*  What is the command ?  */
  763.  
  764.     case 'F':                        /*  Finish, shut down Kermit  */
  765.         print( str, "%s: terminated.", msghdr );
  766.         len    = str2pkt( packet, str );
  767.         spack( 'Y', num, len, packet );    /*  Acknowledge receipt of command  */
  768.         return( FALSE );            /*  Exit Server Mode  */
  769.         break;
  770.  
  771.     case 'L':                        /*  Shut down Kermit and logout.  */
  772.         spack( 'Y', num, 0, 0 );        /*  Acknowledge receipt of command  */
  773.         flusheol();
  774.         quit();                        /*  Execute session logout  */
  775.         break;
  776.  
  777.     case 'C':                        /*  Change Working Directory  */
  778.         pkt2str( str, packet );
  779.         lchar( str, unchar(char(str,0)) + 1, '*0' );
  780.         rep.st( str, 0, "", 1 );
  781.         /*
  782.          *    There should be a validity check here.
  783.          */
  784.         if( !nullstr(str) ) {
  785.             if( char(str,0) == '/' )
  786.                 concat( wc, wc, str );
  787.             else
  788.                 concat( wc, str );
  789.             }
  790.         print( str, "%s: Working Directory = %s", msghdr, wc );
  791.         len    = str2pkt( packet, str );
  792.         spack( 'Y', num, len, packet );
  793.         break;
  794.  
  795.     case 'D':                        /*  Directory  */
  796.         if( !wat_lc ) {
  797.             errmsg( "Remote Directory command is not available." );
  798.             break;
  799.             }
  800.         pkt2str( str, packet );
  801.         rep.st( str, 0, "", 1 );
  802.         if( packet[0] == 0 )
  803.             concat( str, wc );
  804.         if( char(str,0) == '/' )
  805.             rep.st( str, 0, wc, 0 );
  806.         system( "lc %s >kerm**tmp", str );
  807.         mesgsw( "kerm**tmp", " " );
  808.         break;
  809.  
  810.     case 'E':                        /*  Erase (release) a file  */
  811.         pkt2str( str, packet );
  812.         rep.st( str, 0, "", 1 );
  813.         wdfile( str, flags[FL_PERM] );
  814.         buf    = allocate( 43 );
  815.         if( scaf( str, buf, 0, 43 ) < 0 )
  816.             errmsg( "%s: Bad pathname", str );
  817.         else {
  818.             temp    = allocate( 380 );
  819.             arglst[0]    = sts << 18;
  820.             arglst[1]    = buf << 18;
  821.             drl.drl( T.FLAC_, arglst, 22<<18 | temp );
  822.             sts[0]    = ( sts[0] >> 24 ) & 03777;
  823.             if( sts[0] != 0 )
  824.                 errmsg( "%s: %s", str, flsyerr[sts[0]] );
  825.             else {
  826.                 print( temp, "%s: File %s released.", msghdr, str );
  827.                 len    = str2pkt( packet, temp );
  828.                 spack( 'Y', num, len, packet );
  829.                 }
  830.             }
  831.         break;
  832.  
  833.     case 'H':                /*  print Help information  */
  834.         pkt2str( str, packet );
  835.         rep.st( str, 0, "", 1 );
  836.         if( packet[0] == 0 || nullstr(str) )
  837.             concat( str, "HELP" );
  838.         unit    = open( "kerm**tmp", "wu" );
  839.         prhelp( str, unit );
  840.         close( unit );
  841.         mesgsw( "kerm**tmp", " " );
  842.         break;
  843.  
  844.     case 'T':                        /*  Type (list) a file  */
  845.         pkt2str( str, packet );
  846.         rep.st( str, 0, "", 1 );
  847.         wdfile( str, flags[FL_PERM] );
  848.         mesgsw( str, str );
  849.         break;
  850.  
  851.     case 'U':                    /*  Disk Usage Query  */
  852.         if( !wat_smcl ) {
  853.             errmsg( "Remote Space command is not available." );
  854.             break;
  855.             }
  856.         system( "smcl >kerm**tmp" );
  857.         mesgsw( "kerm**tmp", " " );
  858.         break;
  859.  
  860.     case 'Q':                        /*  Server status Query  */
  861.         unit    = open( "kerm**tmp", "wu" );
  862.         printf( unit, "%s: Server Mode.*n", msghdr );
  863.         prsts( unit );
  864.         close( unit );
  865.         mesgsw( "kerm**tmp", " " );
  866.         break;
  867.  
  868.     case 'J':                        /*  produce a Journal  */
  869.         errmsg( "%s: Transaction logging is not yet implemented.", msghdr );
  870.         break;
  871.  
  872.     default:                        /*  Anything else  */
  873.         errmsg( "%c: generic command not implemented.", cmd );
  874.         }    /*  switch  */
  875.  
  876.     return( TRUE );
  877.     }    /*  generic  */
  878. /*  kermit/s/generic    >> End <<  */
  879. /*  kermit/s/get_rec    >> Begin <<  */
  880. /*
  881.  *    GET_REC    -- GET a RECord from a file.
  882.  */
  883.  
  884. get_rec() {
  885.     extrn    fd, binfil, flbuf, flidx, flcnt, flsec;
  886.     auto    sts;
  887.  
  888.     if( binfil != FM_TEXT ) {
  889.         sts    = read( fd, flbuf, flsec, 320 );
  890.         flsec    += 5;
  891.         if( sts < 0 ) {
  892.             sts    = - sts;
  893.             if( sts != 017 )
  894.                 eprintf( DBG_ALL, "On Read: status = %2o*n", sts );
  895.             return( FALSE );
  896.             }
  897.  
  898.         if( binfil == FM_ASA9 )
  899.             flcnt    = (320*4);
  900.         else
  901.             flcnt    = ((320/2)*9);
  902.         flidx    = 0;
  903.         }
  904.     else {
  905.         sts    = getrcp( fd );
  906.         if( sts < 0 ) {
  907.             return( FALSE );
  908.             }
  909.         copy( flbuf, sts, sts[0]>>18 );
  910.         flidx    = 0;
  911.         if( flbuf[0] == 01200600 && flbuf[1] == '*x*x*x*x' )
  912.             flbuf[0]    = 0600;
  913.         flcnt    = flbuf[0]>>16;
  914.         if( flcnt & 03 )
  915.             flcnt    -= 4;
  916.         sts    = (flbuf[0]>>10) & 03;
  917.         if( sts == 0 || sts == 3 ) {
  918.             lchar( &flbuf[1], flcnt++, '*r' );
  919.             lchar( &flbuf[1], flcnt++, '*n' );
  920.             }
  921.         }
  922.  
  923.     return( TRUE );
  924.     }    /*  get_rec  */
  925. /*  kermit/s/get_rec    >> End <<  */
  926. /*  kermit/s/getch    >> Begin <<  */
  927. /*
  928.  *    GETCH    -- GET a CHaracter from a file.
  929.  */
  930.  
  931. getch() {
  932.     extrn    flunbuf, flbuf, flidx, flcnt, binfil;
  933.     auto    ch;
  934.  
  935.     /*
  936.      *    If a character has been put back,
  937.      *    return it first.
  938.      */
  939.  
  940.     if( flunbuf[0] > 0 )
  941.         return( flunbuf[ flunbuf[0]-- ] );
  942.  
  943.     /*
  944.      *    Get a REAL character.
  945.      */
  946.  
  947.     if( /*flbuf[0] == 0170000 ||*/ (flidx >= flcnt && !get_rec()) )
  948.         return( EOF );
  949.  
  950.     if( binfil == FM_TEXT )
  951.         return( char( &flbuf[1], flidx++ ) );
  952.     else if( binfil == FM_ASA9 )
  953.         return( char( flbuf, flidx++ ) );
  954.  
  955.     switch( flidx % 9 ) {
  956.         case 0:    ch    = flbuf[2*(flidx/9)]>>28;        break;
  957.         case 1:    ch    = flbuf[2*(flidx/9)]>>20;        break;
  958.         case 2:    ch    = flbuf[2*(flidx/9)]>>12;        break;
  959.         case 3:    ch    = flbuf[2*(flidx/9)]>>4;        break;
  960.         case 4:    ch    = flbuf[2*(flidx/9)]<<4
  961.                     | flbuf[2*(flidx/9)+1]>>32;        break;
  962.         case 5:    ch    = flbuf[2*(flidx/9)+1]>>24;        break;
  963.         case 6:    ch    = flbuf[2*(flidx/9)+1]>>16;        break;
  964.         case 7:    ch    = flbuf[2*(flidx/9)+1]>>8;        break;
  965.         case 8:    ch    = flbuf[2*(flidx/9)+1];            break;
  966.         }    /*  switch  */
  967.  
  968.     ++flidx;
  969.     return( ch & 0377 );
  970.     }    /*  getch  */
  971. /*  kermit/s/getch    >> End <<  */
  972. /*  kermit/s/getcmd    >> Begin <<  */
  973. /*
  974.  *    GETCMD    -- GET and parse a CoMmanD.
  975.  *        Parse a command line and set options.
  976.  *        Execute the command if desired.
  977.  */
  978.  
  979. getcmd( cmdstr, cmdflg, in_flags ) {
  980.     extrn    fillst, filcnt, filnam, wc, .argtype;
  981.     extrn    rflg, sflg, srvflg;
  982.     extrn    fderr, fdtap, debug;
  983.     extrn    chktype, delay, retryflg, xonwait, tapflg, eol, stpkt, remote;
  984.     auto    argc, argv, buf, cmd, i, info, p, ix, tmp, retn, flags[FL_SIZE];
  985.     auto    nerrors, old_remote, randseen, formseen;
  986.  
  987.     buf    = allocate( 100 );
  988.     argv    = .bset( cmdstr, buf );
  989.     argc    = argv >> 18;
  990.     argv    &= 0777777;
  991.  
  992.     copy( flags, in_flags, FL_SIZE );
  993.     flags[FL_NAME]    = 0;
  994.  
  995.     nerrors    = 0;
  996.     randseen    = formseen    = FALSE;
  997.  
  998.     /*
  999.      *    Find the command (if any)
  1000.      */
  1001.  
  1002.     if( argc < 1 || (cmd = argv[0]>>18) < OP.DONE || cmd > OP.STAT )
  1003.         cmd    = OP.SET;
  1004.  
  1005.     if( cmdflg == PAR_RMT
  1006.         && !(cmd == OP.HELP || cmd == OP.SET || cmd == OP.STAT) )
  1007.         return( 'n' );
  1008.  
  1009.     for( i = 0; i < argc; ++i )
  1010.         switch( (info = argv[i]) >> 18 ) {
  1011.  
  1012.         case OP.FILE:
  1013.             if( cmdflg == PAR_CMD && cmd == OP.HELP ) {
  1014.                 prhelp( info );
  1015.                 return( TRUE );
  1016.                 }
  1017.             else if( cmdflg == PAR_CMD && i == 0 ) {
  1018.                 errmsg( "%s: Unknown command.", info );
  1019.                 return( 'n' );
  1020.                 }
  1021.             else if( cmdflg == PAR_CMD && cmd != OP.SEND ) {
  1022.                 errmsg( "%s: Filenames can only be used with SEND", info );
  1023.                 ++nerrors;
  1024.                 }
  1025.             break;
  1026.  
  1027.         case OP.INDEX:
  1028.             if( cmdflg == PAR_CMD && cmd != OP.SEND ) {
  1029.                 errmsg( "indeX=%s: Can only be used with SEND", info );
  1030.                 ++nerrors;
  1031.                 }
  1032.             break;
  1033.  
  1034.         case OP.QUERY:
  1035.             if( cmdflg == PAR_CMD && cmd == OP.HELP )
  1036.                 prhelp( "?" );
  1037.             else
  1038.                 prquery( cmd, i );
  1039.  
  1040.             return( TRUE );
  1041.  
  1042.         case OP.DONE::OP.STAT:
  1043.             break;
  1044.  
  1045.         case OP.FORMAT:
  1046.             formseen    = TRUE;
  1047.             if( .abbrv( "Text", info ) != -1 )
  1048.                 flags[FL_MODE]    = FM_TEXT;
  1049.             else if( .abbrv( "BYtestream", info ) != -1 )
  1050.                 flags[FL_MODE]    = FM_ASA9;
  1051.             else if( .abbrv( "BItstream", info ) != -1 )
  1052.                 flags[FL_MODE]    = FM_BITS;
  1053.             else {
  1054.                 errmsg( "Bad file format: Format=%s", info );
  1055.                 ++nerrors;
  1056.                 }
  1057.             break;
  1058.  
  1059.         case OP.CHKSUM:
  1060.             errmsg( "Checksum=%s: Option is not yet implemented.", info );
  1061.             ++nerrors;
  1062.             break;
  1063.             if( cmdflg != PAR_CMD ) {
  1064.                 errmsg( "Checksum=%s: Can only be used on command lines.", info );
  1065.                 ++nerrors;
  1066.                 }
  1067.             else {
  1068.                 if( .abbrv( "Single", info ) != -1 || equal( "1", info ) )
  1069.                     chktype    = 1;
  1070.                 else if( .abbrv( "Double", info ) != -1 || equal( "2", info ) )
  1071.                     chktype    = 2;
  1072.                 else if( .abbrv( "CyclicRedundancyCheck", info ) != -1
  1073.                         || equal( "3", info ) )
  1074.                     chktype    = 3;
  1075.                 else {
  1076.                     errmsg( "Checksum=%s: Bad Checksum type.", info );
  1077.                     ++nerrors;
  1078.                     }
  1079.                 }
  1080.             break;
  1081.  
  1082.         case OP.DEBUG:
  1083.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1084.                 errmsg( "DeBug=%s: Can only be used on command lines.", info  );
  1085.                 ++nerrors;
  1086.                 }
  1087.             else {
  1088.                 if( .abbrv( "Off", info ) != -1 )
  1089.                     debug    = DBG_OFF;
  1090.                 else if( .abbrv( "States", info ) != -1 )
  1091.                     debug    |= DBG_STATES;
  1092.                 else if( .abbrv( "Packets", info ) != -1 )
  1093.                     debug    |= DBG_PACKETS;
  1094.                 else if( .abbrv( "Logfile", info ) != -1 )
  1095.                     debug    |= DBG_LOGFILE;
  1096.                 else if( .abbrv( "All", info ) != -1 )
  1097.                     debug    |= DBG_ALL;
  1098.                 else {
  1099.                     errmsg( "DeBug=%s: Invalid debug setting.", info );
  1100.                     ++nerrors;
  1101.                     }
  1102.                 }
  1103.             break;
  1104.  
  1105.         case OP.DELAY:
  1106.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1107.                 errmsg( "Delay=%d: Can only be used on command lines.", *info );
  1108.                 ++nerrors;
  1109.                 }
  1110.             else {
  1111.                 if( *info < 0 ) {
  1112.                     errmsg( "Delay=%d: Delay must be positive", *info );
  1113.                     ++nerrors;
  1114.                     }
  1115.                 else
  1116.                     delay    = *info;
  1117.                 }
  1118.             break;
  1119.  
  1120.         case OP.SEOL:
  1121.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1122.                 errmsg( "SendEndOfLine=%d: Can only be used on command lines.", *info );
  1123.                 ++nerrors;
  1124.                 }
  1125.             else {
  1126.                 if( *info < 0 || *info > 127 ) {
  1127.                     errmsg( "SendEndOfLine=%d: Must be within 0..127.", *info );
  1128.                     ++nerrors;
  1129.                     }
  1130.                 else
  1131.                     eol    = *info;
  1132.                 }
  1133.             break;
  1134.  
  1135.         case OP.SPKST:
  1136.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1137.                 errmsg(
  1138.                     "SendStartofPacket=%d: Can only be used on command lines.",
  1139.                     *info );
  1140.                 ++nerrors;
  1141.                 }
  1142.             else {
  1143.                 if( *info < 0 || *info > 127 ) {
  1144.                     errmsg( "SendStartofPacket=%d: Must be within 0..127.",
  1145.                     *info );
  1146.                     ++nerrors;
  1147.                     }
  1148.                 else
  1149.                     stpkt    = *info;
  1150.                 }
  1151.             break;
  1152.  
  1153.         case OP.CWD:
  1154.             /*
  1155.              *    There should be a validity check here.
  1156.              */
  1157.             if( !nullstr(info) && !equal(info,"?") ) {
  1158.                 if( char(info,0) == '/' )
  1159.                     concat( wc, wc, info );
  1160.                 else
  1161.                     concat( wc, info );
  1162.                 }
  1163.             errmsg( "Working Directory = %s", wc );
  1164.             break;
  1165.  
  1166.         case OP.RANDOM:
  1167.             randseen    = TRUE;
  1168.             flags[FL_RAND]    = ( .argtype[i] == '+' );
  1169.             break;
  1170.  
  1171.         case OP.DISCARD:
  1172.             flags[FL_DISC]    = ( .argtype[i] == '+' );
  1173.             break;
  1174.  
  1175.         case OP.RETRY:
  1176.             errmsg( "%cRetryPacket: Option is not yet implemented.", .argtype[i] );
  1177.             ++nerrors;
  1178.             break;
  1179.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1180.                 errmsg( "%cRetryPacket: Must be used with SET.", .argtype[i] );
  1181.                 ++nerrors;
  1182.                 }
  1183.             else {
  1184.                 retryflg    = ( .argtype[i] == '+' );
  1185.                 }
  1186.             break;
  1187.  
  1188.         case OP.PERM:
  1189.             flags[FL_PERM]    = ( .argtype[i] == '+' );
  1190.             break;
  1191.  
  1192.         case OP.TAPMOD:
  1193.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1194.                 errmsg( "%cTapeMode: Must be used with SET.", .argtype[i] );
  1195.                 ++nerrors;
  1196.                 }
  1197.             else {
  1198.                 tapflg    = ( .argtype[i] == '+' );
  1199.                 }
  1200.             break;
  1201.  
  1202.         case OP.XON:
  1203.             errmsg( "%cXon: Option is not yet implemented.", .argtype[i] );
  1204.             ++nerrors;
  1205.             break;
  1206.             if( cmdflg != PAR_CMD && cmdflg != PAR_RMT ) {
  1207.                 errmsg( "%cXonwait: Must be used with SET.", .argtype[i] );
  1208.                 ++nerrors;
  1209.                 }
  1210.             else {
  1211.                 xonwait    = ( .argtype[i] == '+' );
  1212.                 }
  1213.             break;
  1214.  
  1215.         case OP.OVWRITE:
  1216.             flags[FL_OVWR]    = ( .argtype[i] == '+' );
  1217.             break;
  1218.  
  1219.         default:
  1220.             errmsg( "%s: Unknown option.", info );
  1221.             ++nerrors;
  1222.             }    /*  switch  */
  1223.  
  1224.     if( nerrors )
  1225.         return( 'n' );
  1226.  
  1227.     if( cmdflg != PAR_IDX ) {
  1228.         fillst        = getvec( 0 );
  1229.         fillst[0]    = 0;
  1230.         filcnt        = 0;
  1231.         }
  1232.  
  1233.     if( !randseen && formseen )
  1234.         flags[FL_RAND]    = ( flags[FL_MODE] != FM_TEXT );
  1235.  
  1236.     for( i = 0; i < argc; ++i )
  1237.         switch( (info = argv[i]) >> 18 ) {
  1238.  
  1239.         case OP.FILE:
  1240.             tmp    = allocate( MAXLINE _SIZE );
  1241.             concat( tmp, info );
  1242.             wdfile( tmp, flags[FL_PERM] );
  1243.  
  1244.             p    = getvec( FL_SIZE );
  1245.             copy( p, flags, FL_SIZE );
  1246.             p[FL_NAME]    = concat( getvec(length(tmp)/4), tmp );
  1247.  
  1248.             fillst    = addvec( fillst, 1 );
  1249.             fillst[++fillst[0]]    = p;
  1250.             break;
  1251.  
  1252.         case OP.INDEX:
  1253.             ix    = open( info, "rudfem" );
  1254.             if( ix > 0 ) {
  1255.                 tmp    = allocate( MAXLINE _SIZE );
  1256.                 while( getstr( ix, tmp ) ) {
  1257.                     getcmd( tmp, PAR_IDX, flags );
  1258.                     }
  1259.                 close( ix );
  1260.                 }
  1261.             break;
  1262.             }    /*  switch  */
  1263.  
  1264.     if( debug && fderr == ERR )
  1265.         fderr    = open( "kerm**dbg", "wu" );
  1266.  
  1267.     if( tapflg && fdtap == ERR )
  1268.         fdtap    = open( "tap**", "rwbut" );
  1269.  
  1270.     if( cmdflg == PAR_CMD ) {
  1271.         switch( cmd ) {
  1272.         case OP.DONE:
  1273.         case OP.EXIT:
  1274.         case OP.QUIT:
  1275.             retn    = 'd';
  1276.             break;
  1277.         case OP.HELP:
  1278.             prhelp( "HELP" );
  1279.             flush();
  1280.             retn    = TRUE;
  1281.             break;
  1282.         case OP.RECV:
  1283.             rflg    = 1;
  1284.             printf( "Escape back to your local KERMIT and enter a SEND command.*n" );
  1285.             flush();
  1286.             old_remote    = remote;
  1287.             remote    = TRUE;
  1288.             retn    = recsw( flags );    /*  Go to receive state  */
  1289.             remote    = old_remote;
  1290.             rflg    = 0;
  1291.             break;
  1292.         case OP.SEND:
  1293.             if( fillst[0] == 0 )        /*  If no file names given  */
  1294.                 printf( "No file names supplied.*n" );
  1295.             else {
  1296.                 sflg    = 1;
  1297.                 printf( "Escape back to your local KERMIT and enter RECEIVE mode.*n" );
  1298.                 flush();
  1299.                 old_remote    = remote;
  1300.                 remote    = TRUE;
  1301.                 retn    = sendsw('S');        /*  Go to send state  */
  1302.                 remote    = old_remote;
  1303.                 sflg    = 0;
  1304.                 }
  1305.             break;
  1306.         case OP.SERV:
  1307.             printf( "*nEntering Server Mode.*n" );
  1308.             printf( "Escape back to your local KERMIT " );
  1309.             printf( "and use server commands.*n" );
  1310.             printf( "To exit Server Mode, enter the FINISH " );
  1311.             printf( "command from your local KERMIT.*n" );
  1312.             printf( "To exit KERMIT in an emergency, " );
  1313.             printf( "enter control-D control-S (^D^S)*n" );
  1314.             flush();
  1315.  
  1316.             srvflg    = 1;
  1317.             old_remote    = remote;
  1318.             remote    = TRUE;
  1319.             retn    = server( flags );            /*  Invoke server  */
  1320.             remote    = old_remote;
  1321.             srvflg    = 0;
  1322.             printf( "*nExiting Server Mode.*n" );
  1323.             flush();
  1324.             break;
  1325.         case OP.SET:
  1326.             retn    = 'n';
  1327.             break;
  1328.         case OP.STAT:
  1329.             prsts();
  1330.             flush();
  1331.             break;
  1332.         default:
  1333.             retn    = 'n';                /*  No command given  */
  1334.             }    /*  switch  */
  1335.         }
  1336.  
  1337.     if( cmdflg == PAR_CMD && retn == 'n' )
  1338.         copy( in_flags, flags, FL_SIZE );
  1339.  
  1340.     return( retn );
  1341.     }    /*  getcmd  */
  1342. /*  kermit/s/getcmd    >> End <<  */
  1343. /*  kermit/s/getcomm    >> Begin <<  */
  1344. /*
  1345.  *    GETCOMM    -- GET a char from the COMMunications line.
  1346.  *        If not in paper tape mode, just do a getchar.
  1347.  *        If in paper tape mode, read the tap* buffer.
  1348.  */
  1349.  
  1350. getcomm() {
  1351.     extrn    tapflg, fdtap, tpbuf, tpcnt, tpidx, msghdr;
  1352.     auto    ch;
  1353.  
  1354.     if( !tapflg ) {
  1355.         ch    = getchar();
  1356.         eprintf( DBG_PACKETS, "%c", ch );
  1357.         if( nobrks() ) {
  1358.             printf( -4, "%s: Break key.*n", msghdr );
  1359.             printf( -4, "To exit KERMIT type control-D (^D)" );
  1360.             flush();
  1361.             nobrks( MAX_BRKS );
  1362.             }
  1363.         return( ch );
  1364.         }
  1365.  
  1366.     if( tpidx >= tpcnt )        /*  No characters left  */
  1367.         rdcomm();
  1368.  
  1369.     ch    = char( &tpbuf[2], tpidx++ );
  1370.     eprintf( DBG_PACKETS, "%c", ch );
  1371.  
  1372.     return( ch );
  1373.     }    /*  getcomm  */
  1374. /*  kermit/s/getcomm    >> End <<  */
  1375. /*  kermit/s/getfil    >> Begin <<  */
  1376. /*
  1377.  *    GETFIL    -- GET a FILe.
  1378.  *        Open a new file, overwriting any existing file.
  1379.  *        Returns 0 if successful, 'a' if no clash resolution possible,
  1380.  *        and the negative of the filact error status if the file could
  1381.  *        not be created or accessed.
  1382.  */
  1383.  
  1384. getfil() {
  1385.     extrn    fd, imgflg, filnam, fillst, filcnt, wc;
  1386.     extrn    binfil, randflg, discflg, prmflg, ovrflg;
  1387.     extrn    flbuf, flidx, flcnt, flseg, flcrcnt, flsec;
  1388.  
  1389.     concat( filnam, fillst[1][FL_NAME] );
  1390.     binfil    = fillst[1][FL_MODE];
  1391.     randflg    = fillst[1][FL_RAND];
  1392.     discflg    = fillst[1][FL_DISC];
  1393.     prmflg    = fillst[1][FL_PERM];
  1394.     ovrflg    = fillst[1][FL_OVWR];
  1395. eprintf( DBG_ALL, "Random=%d, Format=%d", randflg, binfil );
  1396.  
  1397.     /*
  1398.      *    Make sure filnam is properly constructed
  1399.      */
  1400.  
  1401.     innam( filnam, prmflg );
  1402.  
  1403.     if( !ovrflg )
  1404.         if( !rsclash( filnam ) )
  1405.             return( - 011 );    /*  Non-unique name  */
  1406.  
  1407.     if( discflg )
  1408.         retfil( "kerm**tmp" );
  1409.     fd    = open( discflg ? "kerm**tmp" : filnam, randflg ? "wufeb" : "wufe" );
  1410.     eprintf( DBG_ALL, "Attempt to open file: '%s'*n", filnam );
  1411.  
  1412.     if( fd < 0 )
  1413.         return( fd );                /*  Return FILACT status  */
  1414.  
  1415.     /*
  1416.      *    Initialize the file buffer.
  1417.      */
  1418.  
  1419.     if( binfil == FM_TEXT )
  1420.         flcnt    = 318*4;
  1421.     else if( binfil == FM_ASA9 )
  1422.         flcnt    = 320*4;
  1423.     else
  1424.         flcnt    = ((320/2)*9);
  1425.  
  1426.     flidx    = 0;
  1427.     flsec    = 0;
  1428.     flseg    = 0;
  1429.     flcrcnt    = 0;
  1430.     zero( flbuf, 320 );
  1431.  
  1432.     return( 0 );                    /*  Return file descriptor  */
  1433.     }    /*  getfil  */
  1434. /*  kermit/s/getfil    >> End <<  */
  1435. /*  kermit/s/getwd    >> Begin <<  */
  1436. /*
  1437.  *    GETWD    -- GET the initial Working Directory.
  1438.  *        If not on GCOS8, this is the logon userid.
  1439.  */
  1440.  
  1441. %b/manif/ust
  1442.  
  1443. getwd( wc ) {
  1444.     extrn    wat_cwd;
  1445.     auto    size, vec;
  1446.     auto    catf[.LCWD_*4 + 1];
  1447.  
  1448.     if( !wat_cwd || !.gcos8() ) {
  1449.         getumc( wc );
  1450.         return( wc );
  1451.         }
  1452.  
  1453.     ++catf;        /* step over the size word */
  1454.     p.ust( &catf[-1], .LCWD_*4+1, .LDUWD_ );
  1455.     size = (catf[-1] >> 16) + 1;
  1456.     size[vec = .vectr(size, size)] = -1;
  1457.     ++vec;
  1458.     .copy( vec, catf, size-2 );
  1459.     .unscaf( wc, vec, 1 );
  1460.     rlsevec( vec-1, vec[-1] );
  1461.  
  1462.     return( wc );
  1463.     }    /*  getwd  */
  1464. /*  kermit/s/getwd    >> End <<  */
  1465. /*  kermit/s/gnxtfl    >> Begin <<  */
  1466. /*
  1467.  *    GNXTFL    -- Get NeXT FiLe.
  1468.  *
  1469.  *    Get next file from command line.
  1470.  */
  1471.  
  1472. gnxtfl() {
  1473.     extrn    fd, imgflg, binfil, filnam, msghdr, fillst, filcnt, wc;
  1474.     extrn    randflg;
  1475.     extrn    flidx, flcnt, flsec, flseg, flcrcnt, flunbuf, flbuf;
  1476.     auto    tmp;
  1477.  
  1478.     if( filcnt >= fillst[0] )    /*  Otherwise, get next file name  */
  1479.         return( 'B' );            /*  No more names - break transmission  */
  1480.  
  1481.     concat( filnam, fillst[++filcnt][FL_NAME] );
  1482.     binfil    = fillst[filcnt][FL_MODE];
  1483.     randflg    = fillst[filcnt][FL_RAND];
  1484.  
  1485.     tmp    = fillst[filcnt][FL_NAME];
  1486.     rlsevec( tmp, length(tmp)/4 );
  1487.     rlsevec( fillst[filcnt], FL_SIZE );
  1488.  
  1489.     /*
  1490.      *    Make sure filnam is properly constructed
  1491.      */
  1492.  
  1493.     innam( filnam, fillst[filcnt][FL_PERM] );
  1494.     fd    = open( filnam, randflg ? "rufeb" : "rufe" );
  1495.     if( fd < 0 ) {                            /*  If it doesn't exist  */
  1496.         errmsg( "Can't open file %s", filnam );    /*  Send error message  */
  1497.         return( 'a' );                            /*  Abort  */
  1498.         }
  1499.  
  1500.     eprintf( DBG_ALL, "%s: sending file '%s'", msghdr, filnam );
  1501.  
  1502.     outnam( filnam );            /*  Put name into standard format  */
  1503.  
  1504.     eprintf( DBG_ALL, " as '%s'*n", filnam );
  1505.  
  1506.     flidx    = 0;
  1507.     flcnt    = 0;
  1508.     flsec    = 0;
  1509.     flseg    = 0;
  1510.     flcrcnt    = 0;
  1511.     flunbuf[0]    = 0;
  1512.     flbuf[0]    = 0;
  1513.  
  1514.     return( 'F' );            /*  Ready to send new file.  */
  1515.     }    /*  gnxtfl  */
  1516. /*  kermit/s/gnxtfl    >> End <<  */
  1517. /*  kermit/s/innam    >> Begin <<  */
  1518. /*
  1519.  *    INNAM    -- INput NAMe.
  1520.  *
  1521.  *    Change file name to a local compatible name.
  1522.  *
  1523.  *    *** MACHINE DEPENDENT SUBROUTINE ***
  1524.  *    Makes sure that an incoming file has a name that the local system
  1525.  *    recognizes as valid.
  1526.  */
  1527.  
  1528. innam( name, prmflg ) {
  1529.     auto    i, j, ch;
  1530.  
  1531.     /*
  1532.      *    Make sure that name
  1533.      *    contains only valid chars.
  1534.      */
  1535.  
  1536.     trim( lowercase(name) );
  1537.  
  1538.     if( !prmflg && .break( name, 0, "/$" ) == length(name) ) {
  1539.         if( length(name) > 8 )
  1540.             lchar( name, 8, '*0' );
  1541.         return;
  1542.         }
  1543.  
  1544.     for( i = j = 0; (ch = char(name,i)) != '*0'; ++i )
  1545.         if( any( ch, "abcdefghijklmnopqrstuvwxyz0123456789_.-/$*"**" ) != -1 )
  1546.             lchar( name, j++, ch );
  1547.     lchar( name, j, '*0' );
  1548.  
  1549.     /*
  1550.      *    Make sure name is
  1551.      *    properly constructed.
  1552.      */
  1553.  
  1554.     i    = 0;
  1555.     if( char(name,0) == '/' )
  1556.         i    = 1;
  1557.  
  1558.     for( ; i == 0 || char(name,i-1) == '/'; i = j + 1 ) {
  1559.         j    = .break( name, i, "/$*"" );
  1560.         if( j - i > 12 ) {
  1561.             rep.st( name, i+12, "", j-i-12 );
  1562.             j    = i + 12;
  1563.             }
  1564.         if( (ch = char(name,j)) == '$' ) {
  1565.             j    = .break( name, i = j + 1, "/$*"" );
  1566.             if( j - i > 12 ) {
  1567.                 rep.st( name, i+12, "", j-i-12 );
  1568.                 j    = i + 12;
  1569.                 }
  1570.             }
  1571.         }    /*  for  */
  1572.  
  1573.     }    /*  innam  */
  1574. /*  kermit/s/innam    >> End <<  */
  1575. /*  kermit/s/mask    >> Begin <<  */
  1576. /*
  1577.  *    MASK    -- MASK off the parity bit.
  1578.  *        Returns the lower seven bits.
  1579.  */
  1580.  
  1581. mask( c ) {
  1582.  
  1583.     return( c & 127 );
  1584.     }    /*  mask  */
  1585. /*  kermit/s/mask    >> End <<  */
  1586. /*  kermit/s/mesgsw    >> Begin <<  */
  1587. /*
  1588.  *    MESGSW    -- MESseGe SWitcher.
  1589.  *
  1590.  *    Mesgsw is the state table switcher for sending
  1591.  *    long replies to queries or commands.  It loops
  1592.  *    until either it finishes, or an error is found.
  1593.  *    The routines called by mesgsw are responsible
  1594.  *    for changing the state.
  1595.  *
  1596.  *    fname    -- name of file to be sent.
  1597.  *    sname    -- name to send.
  1598.  */
  1599.  
  1600. mesgsw( fname, sname ) {
  1601.     extrn    n, numtry, fd, state, fillst, filcnt;
  1602.     auto    lstate, llstate;
  1603.  
  1604.     fillst    = getvec(1);
  1605.     fillst[0]    = 1;
  1606.     filcnt    = 0;
  1607.  
  1608.     fillst[1]    = getvec( FL_SIZE );
  1609.     fillst[1][FL_NAME]    = concat( getvec(length(fname)/4), fname );
  1610.     fillst[1][FL_MODE]    = FM_TEXT;
  1611.     fillst[1][FL_RAND]    = FALSE;
  1612.     fillst[1][FL_DISC]    = FALSE;
  1613.     fillst[1][FL_PERM]    = FALSE;
  1614.     fillst[1][FL_OVWR]    = TRUE;
  1615.  
  1616.     state    = 'S';                /*  Start in Send-Init state  */
  1617.     n        = 0;                /*  Initialize message number  */
  1618.     numtry    = 0;                /*  Say no tries yet  */
  1619.  
  1620.     repeat {                    /*  Do this as long as necessary  */
  1621.         eprintf( DBG_STATES, "  mesgsw %c %d*n", state, n );
  1622.  
  1623.         switch( state ) {
  1624.         case 'D':                /*  Data-Send state  */
  1625.             state    = sdata();
  1626.             break;
  1627.         case 'F':                /*  File-Send  */
  1628.             state    = sfile(sname);
  1629.             break;
  1630.         case 'Z':                /*  End of File  */
  1631.             state    = seof();
  1632.             if( state == 'F' )                /*  If ready for next file  */
  1633.                 state    = 'B';                /*  Do Break  */
  1634.             break;
  1635.         case 'S':                    /*  Send Init  */
  1636.             state    = sinit();
  1637.             break;
  1638.         case 'B':            /*  Break-Send  */
  1639.             state    = sbreak();
  1640.             break;
  1641.         case 'C':            /*  Complete  */
  1642.             flusheol();
  1643.             return(TRUE);
  1644.         default:                        /*  Anything else is an error  */
  1645.             failmsg(llstate);            /*  Put out an error message  */
  1646.             if( fd != ERR ) {            /*  If file left open  */
  1647.                 close(fd);                /*  Close it  */
  1648.                 fd    = ERR;                /*  Remember it's closed  */
  1649.                 }
  1650.  
  1651.             flusheol();
  1652.             return(FALSE);        /*  Error return  */
  1653.             }
  1654.  
  1655.         llstate    = lstate;
  1656.         lstate    = state;            /*  Remember last state  */
  1657.         }
  1658.  
  1659.     }    /*  mesgsw  */
  1660. /*  kermit/s/mesgsw    >> End <<  */
  1661. /*  kermit/s/outnam    >> Begin <<  */
  1662. /*
  1663.  *    OUTNAM    -- convert an OUTgoing fileNAMe.
  1664.  *        This routine converts a local file name to
  1665.  *        a form recognizable by most other systems.
  1666.  *
  1667.  *    The format of the name is :
  1668.  *
  1669.  *        name.ext
  1670.  *
  1671.  *    Where "name" can be 8 characters long and "ext"
  1672.  *    can be 3 characters long or not even present.
  1673.  *    The characters should be uppercase.
  1674.  */
  1675.  
  1676. outnam( name ) {
  1677.     auto    i;
  1678.  
  1679.     uppercase( name );
  1680.  
  1681.     /*
  1682.      *    Strip off leading catalogs.
  1683.      */
  1684.  
  1685.     for( i = length(name)-1; i >= 0; --i )
  1686.         if( char(name,i) == '/' )
  1687.             break;
  1688.     if( i >= 0 )
  1689.         rep.st( name, 0, "", i+1 );
  1690.  
  1691.     /*
  1692.      *    Strip password (if any).
  1693.      */
  1694.  
  1695.     if( (i = any( '$', name )) != -1 )
  1696.         lchar( name, i, '*0' );
  1697.  
  1698.     /*
  1699.      *    Found a '.'
  1700.      */
  1701.  
  1702.     if( (i = any( '.', name )) >= 0 ) {
  1703.         if( i > 8 ) {                        /*  Name is too long  */
  1704.             movelr( name,8, name,i, length(name)-i+1 );
  1705.             i    = 8;
  1706.             }
  1707.         if( length(name)-i > 4 )            /*  If extension is too long  */
  1708.             lchar( name, i+4, '*0' );        /*  Truncate it.  */
  1709.         }
  1710.     else {
  1711.         if( length(name) > 8 )                /*  If name too long ....  */
  1712.             lchar( name, 8, '*0' );            /*  Truncate it.  */
  1713.         }
  1714.  
  1715.     return( name );
  1716.     }    /*  outnam  */
  1717. /*  kermit/s/outnam    >> End <<  */
  1718. /*  kermit/s/pkt2str    >> Begin <<  */
  1719. /*
  1720.  *    PKT2STR    -- convert a PacKeT TO a STRing.
  1721.  *        Packets are simple arrays of characters.
  1722.  *        Strings are packed four character per word.
  1723.  *        Control-quoting, 8-bit & repeat prefixes are done.
  1724.  */
  1725.  
  1726. pkt2str( str, pkt ) {
  1727.     extrn    quotec, bquote, reptc, dobquo, dorept;
  1728.     auto    nrep, i, j, len, c, c7, c8;
  1729.  
  1730.     /*
  1731.      *    Loop through data.
  1732.      */
  1733.  
  1734.     len    = 0;
  1735.  
  1736.     for( i = 0; (c = pkt[i]) != '*0'; ++i ) {
  1737.  
  1738.         if( !dorept || c != reptc )                    /*  Repeat prefix?  */
  1739.             nrep    = 1;
  1740.         else {
  1741.             nrep    = unchar(pkt[++i]);                /*  Get the count  */
  1742.             c        = pkt[++i];                        /*  Next char  */
  1743.             }
  1744.  
  1745.         if( !dobquo || c != bquote )                /*  Eighth-bit quote?  */
  1746.             c8    = 0;
  1747.         else {
  1748.             c8    = 128;                                /*  save bit's value  */
  1749.             c    = pkt[++i];                            /*  Next char  */
  1750.             }
  1751.  
  1752.         if( c == quotec ) {                            /*  A quoted char  */
  1753.             c    = pkt[++i];                            /*  get the next char  */
  1754.             if( (c7 = mask(c)) >= '?' && c7 <= '_' )
  1755.                 c    = ctl(c);                        /*  Controlify the char  */
  1756.             }
  1757.  
  1758.         c    |= c8;                        /*  Or in eighth bit  */
  1759.  
  1760.         for( j = 1; j <= nrep; ++j )    /*  Put out correct number of chars  */
  1761.             lchar( str, len++, c );
  1762.         }    /*  for  */
  1763.  
  1764.     lchar( str, len, '*0' );
  1765.  
  1766.     return( len );
  1767.     }    /*  pkt2str  */
  1768. /*  kermit/s/pkt2str    >> End <<  */
  1769. /*  kermit/s/prhelp    >> Begin <<  */
  1770. /*
  1771.  *    PRHELP    -- PRint a HELP message.
  1772.  */
  1773.  
  1774. prhelp( cmd, unit ) {
  1775.     auto    old_unit;
  1776.  
  1777.     if( nargs() < 2 )
  1778.         unit    = -4;
  1779.  
  1780.     old_unit    = .write( unit );
  1781.  
  1782.     printf( "*n" );
  1783.  
  1784.     if( .abbrv( "Done", cmd ) != -1
  1785.     || .abbrv( "Exit", cmd ) != -1 || .abbrv( "Quit", cmd ) != -1 ) {
  1786.         printf( "*
  1787.             * Done -- Exit KERMIT*n*
  1788.             * Exit -- Exit KERMIT*n*
  1789.             * Quit -- Exit KERMIT*n*
  1790.             *  *n*
  1791.             * Syntax:*n*
  1792.             *      Done*n*
  1793.             * or   Exit*n*
  1794.             * or   Quit*n*
  1795.             *  *n*
  1796.             * The Done, Exit and Quit commands exit KERMIT.*n*
  1797.             * No options are allowed for this command.*n" );
  1798.         }
  1799.     else if( equal( cmd, "?" ) ) {
  1800.         printf( "*
  1801.             * ? -- Query the allowed options*n*
  1802.             *  *n*
  1803.             * A ? symbol in place of a command or an option*n*
  1804.             * will cause a list of the possible commands or*n*
  1805.             * options at that point.  To get an explanation*n*
  1806.             * of the command/option,  use the Help command.*n" );
  1807.         }
  1808.     else if( .abbrv( "Help", cmd ) != -1 ) {
  1809.         printf( "*
  1810.             * Help -- Provide Basic Explanations*n*
  1811.             *  *n*
  1812.             * Syntax:*n*
  1813.             *      Help <command or option>*n*
  1814.             *  *n*
  1815.             * The Help command prints information about the <command>*n*
  1816.             * or <option> specified.*n*
  1817.             * *n*
  1818.             * Commands recognized:*n*
  1819.             *     ?        Done    Exit    Help    Quit*n*
  1820.             *     Receive  SENd    SERver  SET     STatus*n*
  1821.             * Options recognized:*n*
  1822.             *     CWD      DeBug     Delay      Discard    Format*n*
  1823.             *     indeX    filename  OverWrite  Permanent  Random*n*
  1824.             *     SendEOL  SendStartofPacket    TapeMode*n" );
  1825.         }
  1826.     else if( .abbrv( "Receive", cmd ) != -1 ) {
  1827.         printf( "*
  1828.             * Receive -- Receive a file*n*
  1829.             *  *n*
  1830.             * Syntax:*n*
  1831.             *      Receive [<option>]***n*
  1832.             *  *n*
  1833.             * The Receive command causes GCOS KERMIT to wait for files  to*n*
  1834.             * be  sent  from the local KERMIT.  The files will be accessed*n*
  1835.             * according to the current option settings, unless  overridden*n*
  1836.             * on the command line.*n*
  1837.             * *n*
  1838.             * The following options are recognized:*n*
  1839.             *     CWD      DeBug      Delay      Discard*n*
  1840.             *     Format   OverWrite  Permanent  Random*n*
  1841.             *     SendEOL  SendStartofPacket     TapeMode*n" );
  1842.         }
  1843.     else if( .abbrv( "SENd", cmd ) != -1 ) {
  1844.         printf( "*
  1845.             * SENd -- Send a File*n*
  1846.             *  *n*
  1847.             * Syntax:*n*
  1848.             *      SENd [<filespec>]** [<option>]***n*
  1849.             *  *n*
  1850.             * A  <filespec>  is  either  the  name  of a file  to  send  or*n*
  1851.             * *"indeX=filename*"  where the file contains KERMIT SENd command*n*
  1852.             * options and filespecs.*n*
  1853.             *  *n*
  1854.             *      The SENd command causes GCOS KERMIT to  send  files  to*n*
  1855.             * the  local  KERMIT.  The files will be sent according to the*n*
  1856.             * current option settings, unless overridden  on  the  command*n*
  1857.             * line  or  in  an  index file.  GCOS KERMIT will wait for the*n*
  1858.             * number of seconds  specified  in  the  Delay  option  before*n*
  1859.             * starting  to send the files.  This allows you time to escape*n*
  1860.             * back to your local KERMIT and type RECEIVE.*n*
  1861.             * *n*
  1862.             * The following options are recognized:*n*
  1863.             *     CWD      DeBug     Delay      Discard    Format*n*
  1864.             *     indeX    filename  OverWrite  Permanent  Random*n*
  1865.             *     SendEOL  SendStartofPacket    TapeMode*n" );
  1866.         }
  1867.     else if( .abbrv( "SERver", cmd ) != -1 ) {
  1868.         printf( "*
  1869.             * SERver -- Enter Server Mode*n*
  1870.             *  *n*
  1871.             * Syntax:*n*
  1872.             *      SERver [<option>]***n*
  1873.             *  *n*
  1874.             * The SERver command causes GCOS KERMIT to enter server  mode.*n*
  1875.             * While  in  server  mode,  GCOS KERMIT will wait for commands*n*
  1876.             * from your local KERMIT.  When a command is received,  it  is*n*
  1877.             * executed.   Server  mode is the preferred mode of operation,*n*
  1878.             * although not all local KERMITs support it.   If  your  local*n*
  1879.             * KERMIT  supports server mode, you should use it.  The use of*n*
  1880.             * server mode is detailed more fully below.*n*
  1881.             * *n*
  1882.             * The following options are recognized:*n*
  1883.             *     CWD      DeBug      Delay      Discard*n*
  1884.             *     Format   OverWrite  Permanent  Random*n*
  1885.             *     SendEOL  SendStartofPacket     TapeMode*n" );
  1886.         }
  1887.     else if( .abbrv( "SET", cmd ) != -1 ) {
  1888.         printf( "*
  1889.             * SET -- Set KERMIT Options*n*
  1890.             *  *n*
  1891.             * Syntax:*n*
  1892.             *      SET [<option>]***n*
  1893.             *  *n*
  1894.             * The SET command causes KERMIT to  set  its  options  as  you*n*
  1895.             * specify  on  the command line.  Normally, options given on a*n*
  1896.             * command line apply only to  that  command.   A  SET  command*n*
  1897.             * causes them to change until a later SET command is given.*n*
  1898.             * *n*
  1899.             * The following options are recognized:*n*
  1900.             *     CWD      DeBug      Delay      Discard*n*
  1901.             *     Format   OverWrite  Permanent  Random*n*
  1902.             *     SendEOL  SendStartofPacket     TapeMode*n" );
  1903.         }
  1904.     else if( .abbrv( "STatus", cmd ) != -1 ) {
  1905.         printf( "*
  1906.             * STatus -- Print Options Status*n*
  1907.             *  *n*
  1908.             * Syntax:*n*
  1909.             *      STatus*n*
  1910.             *  *n*
  1911.             * The STatus command causes GCOS KERMIT to print a list of the*n*
  1912.             * current option settings.*n*
  1913.             * No options are allowed for this command.*n" );
  1914.         }
  1915.     else if( .abbrv( "indeX", cmd ) != -1 ) {
  1916.         printf( "*
  1917.             * indeX=filename*n*
  1918.             *     causes the file specified to be read  and  each  line*n*
  1919.             *     treated  as  options  and files for the SENd command.*n*
  1920.             *     The files named are sent.  Index files may be nested.*n*
  1921.             *     The  only  options  allowed  within  index  files are*n*
  1922.             *     *"Format=*", +Random, +Discard, -Permanent, +OverWrite.*n*
  1923.             * *n*
  1924.             *     This option may only be used with the SENd command.*n" );
  1925.         }
  1926.     else if( .abbrv( "Format", cmd ) != -1 ) {
  1927.         printf( "*
  1928.             * Format=fileformat*n*
  1929.             *     causes any file transferred in either direction to be*n*
  1930.             *     treated  according to the file format specified.  The*n*
  1931.             *     formats are *"Text*",  *"BYtestream*",  and  *"BItstream*".*n*
  1932.             *     See  the section on file formats in the Writeup.  The*n*
  1933.             *     default is Text.*n*
  1934.             * *n*
  1935.             *     This option may be used with the commands:*n*
  1936.             *         Receive, SENd, SERver and SET.*n" );
  1937.         }
  1938.     else if( .abbrv( "DeBug", cmd ) != -1 ) {
  1939.         printf( "*
  1940.             * DeBug=function*n*
  1941.             *     specifies how much debugging  information  is  to  be*n*
  1942.             *     written to the debug file *"kerm**dbg*".  The default is*n*
  1943.             *     *"Off*", which means that no debugging information will*n*
  1944.             *     be  written.   *"States*"  causes  the current protocol*n*
  1945.             *     state to  be  written  when  it  changes.   *"Packets*"*n*
  1946.             *     causes  each  packet  sent or received to be written.*n*
  1947.             *     *"All*" causes all possible  debug  information  to  be*n*
  1948.             *     written.   This  information is rarely useful, unless*n*
  1949.             *     you suspect your communications line of  being  noisy*n*
  1950.             *     and wish to inspect what is being sent.*n*
  1951.             * *n*
  1952.             *     This option may be used with the commands:*n*
  1953.             *         Receive, SENd, SERver and SET.*n" );
  1954.         }
  1955.     else if( .abbrv( "Delay", cmd ) != -1 ) {
  1956.         printf( "*
  1957.             * Delay=nn*n*
  1958.             *     causes KERMIT to wait nn seconds before  sending  in-*n*
  1959.             *     formation  when  using the SENd command.  The default*n*
  1960.             *     is 10 seconds.*n*
  1961.             * *n*
  1962.             *     This option may be used with the commands:*n*
  1963.             *         Receive, SENd, SERver and SET.*n" );
  1964.         }
  1965.     else if( .abbrv( "Random", cmd ) != -1 ) {
  1966.         printf( "*
  1967.             * -Random*n*
  1968.             * +Random*n*
  1969.             *     causes KERMIT to access  the  file  as  random.   The*n*
  1970.             *     default  is  sequential.  For non-text files, +Random*n*
  1971.             *     must be specified.*n*
  1972.             * *n*
  1973.             *     This option may be used with the commands:*n*
  1974.             *         Receive, SENd, SERver and SET.*n" );
  1975.         }
  1976.     else if( .abbrv( "Discard", cmd ) != -1 ) {
  1977.         printf( "*
  1978.             * -Discard*n*
  1979.             * +Discard*n*
  1980.             *     causes KERMIT to discard an incomplete  transmission.*n*
  1981.             *     If you interrupt the transmission of a file, the file*n*
  1982.             *     will not be created on GCOS.  If -OverWrite is in ef-*n*
  1983.             *     fect, the previous contents will be unchanged.*n*
  1984.             * *n*
  1985.             *     This option may be used with the commands:*n*
  1986.             *         Receive, SENd, SERver and SET.*n" );
  1987.         }
  1988.     else if( .abbrv( "Permanent", cmd ) != -1 ) {
  1989.         printf( "*
  1990.             * +Permanent*n*
  1991.             * -Permanent*n*
  1992.             *     causes KERMIT to create the file as a permanent file.*n*
  1993.             *     This  is  the  default.   If -permanent is specified,*n*
  1994.             *     then the usual GCOS access conventions are used, i.e.*n*
  1995.             *     if  there  are  no  slashes  or  dollar  signs in the*n*
  1996.             *     filename and if the filename is less than or equal to*n*
  1997.             *     eight  characters long, and if a quick access file of*n*
  1998.             *     the same name does not already exist, the  file  will*n*
  1999.             *     be created as temporary.*n*
  2000.             * *n*
  2001.             *     This option may be used with the commands:*n*
  2002.             *         Receive, SENd, SERver and SET.*n" );
  2003.         }
  2004.     else if( .abbrv( "TapeMode", cmd ) != -1 ) {
  2005.         printf( "*
  2006.             * -TapeMode*n*
  2007.             * +TapeMode*n*
  2008.             *     causes KERMIT to use GCOS paper  tape  mode  to  read*n*
  2009.             *     packets.   This is the default.  At sites with Honey-*n*
  2010.             *     well's new front end  processors  (DN8),  paper  tape*n*
  2011.             *     mode  need not be used if the character delete is set*n*
  2012.             *     to a non-printing character (usually a  backspace  or*n*
  2013.             *     delete).   Since  GRTS  cannot  reset  the  character*n*
  2014.             *     delete (@), and since KERMIT does indeed transmit @s,*n*
  2015.             *     paper  tape  mode  must  be used to read the input to*n*
  2016.             *     preserve the @s.  This option conflicts with any flow*n*
  2017.             *     control done by anyone.*n*
  2018.             * *n*
  2019.             *     This option may be used with the commands:*n*
  2020.             *         Receive, SENd, SERver and SET.*n" );
  2021.         }
  2022.     else if( .abbrv( "OverWrite", cmd ) != -1 ) {
  2023.         printf( "*
  2024.             * -OverWrite*n*
  2025.             * +OverWrite*n*
  2026.             *     causes an existing file of the same name to be  over-*n*
  2027.             *     written.  The default is -OverWrite, which causes the*n*
  2028.             *     incomming file to be renamed to avoid conflicts.  The*n*
  2029.             *     file  is renamed by appending *"_n*" to the file, where*n*
  2030.             *     n is the smallest digit not resulting in a clash.  If*n*
  2031.             *     the  filename  is  too  long,  the underscore will be*n*
  2032.             *     omitted.   If  the  filename  is  still   too   long,*n*
  2033.             *     characters  will  be  deleted  from  the end until it*n*
  2034.             *     fits.*n*
  2035.             * *n*
  2036.             *     This option may be used with the commands:*n*
  2037.             *         Receive, SENd, SERver and SET.*n" );
  2038.         }
  2039.     else if( .abbrv( "SendEndOfLine", cmd ) != -1 ) {
  2040.         printf( "*
  2041.             * SendEndOfLine=nn*n*
  2042.             *     causes KERMIT to terminate its outgoing packets  with*n*
  2043.             *     the  ASCII  character whose decimal value is nn.  Ex-*n*
  2044.             *     ample:  SendEndOfLine=26 would cause KERMIT  to  ter-*n*
  2045.             *     minate its outgoing packets with a control-Z (decimal*n*
  2046.             *     ASCII value = 26).  The default  is  carriage  return*n*
  2047.             *     (value = 13).*n*
  2048.             * *n*
  2049.             *     This option may be used with the commands:*n*
  2050.             *         Receive, SENd, SERver and SET.*n" );
  2051.         }
  2052.     else if( .abbrv( "SendStartofPacket", cmd ) != -1 ) {
  2053.         printf( "*
  2054.             * SendStartofPacket=nn*n*
  2055.             *     causes KERMIT to start its outgoing packets with  the*n*
  2056.             *     ASCII  character whose decimal value is nn.  Example:*n*
  2057.             *     SendStartofPacket=26 would cause KERMIT to start  its*n*
  2058.             *     outgoing  packets  with  a  control-Z  (decimal ASCII*n*
  2059.             *     value = 26).  The default is control-A (value = 1).*n*
  2060.             * *n*
  2061.             *     This option may be used with the commands:*n*
  2062.             *         Receive, SENd, SERver and SET.*n" );
  2063.         }
  2064.     else if( .abbrv( "ChangeWorkingDirectory", cmd ) != -1 ) {
  2065.         printf( "*
  2066.             * ChangeWorkingDirectory=catalog*n*
  2067.             *     causes KERMIT to change its internal working directory*n*
  2068.             *     to  the  catalog given.   If the catalog begins with a*n*
  2069.             *     slash (/) it will be appended to the  current  working*n*
  2070.             *     directory.   If  the  catalog  is  null  or a question*n*
  2071.             *     mark (?) is given, the current working catalog will be*n*
  2072.             *     displayed.    The  working  catalog  is  where  KERMIT*n*
  2073.             *     creates/accesses files when they start  with  a  slash*n*
  2074.             *     or contain no slashes (quick access files).*n*
  2075.             * *n*
  2076.             *     This option may be used with the commands:*n*
  2077.             *         Receive, SENd, SERver and SET.*n" );
  2078.         }
  2079.     else {
  2080.         printf( "%s: Unrecognized command or option.*n", cmd );
  2081.         }
  2082.  
  2083.     printf( "*n" );
  2084.  
  2085.     .write( old_unit );
  2086.  
  2087.     }    /*  prhelp  */
  2088. /*  kermit/s/prhelp    >> End <<  */
  2089. /*  kermit/s/prquery    >> Begin <<  */
  2090. /*
  2091.  *    PRQUERY    -- PRint a response to a QUERY.
  2092.  */
  2093.  
  2094. prquery( cmd, argno ) {
  2095.     extrn    remote;
  2096.  
  2097.     printf( "*n" );
  2098.  
  2099.     if( argno == 0 ) {
  2100.         printf( "*
  2101.             * Commands recognized:*n*
  2102.             *     ?        Done    Exit    Help    Quit*n*
  2103.             *     Receive  SENd    SERver  SET     STatus*n" );
  2104.         }
  2105.     else if( cmd == OP.DONE
  2106.     || cmd == OP.EXIT
  2107.     || cmd == OP.QUIT
  2108.     || cmd == OP.STAT ) {
  2109.         printf( " No options are allowed for this command.*n" );
  2110.         }
  2111.     else if( cmd == OP.RECV || cmd == OP.SERV || cmd == OP.SET ) {
  2112.         printf( "*
  2113.             * The following options are recognized:*n*
  2114.             *     CWD      DeBug      Delay      Discard*n*
  2115.             *     Format   OverWrite  Permanent  Random*n*
  2116.             *     SendEOL  SendStartofPacket     TapeMode*n" );
  2117.         }
  2118.     else if( cmd == OP.SEND ) {
  2119.         printf( "*
  2120.             * The following options are recognized:*n*
  2121.             *     CWD      DeBug     Delay      Discard    Format*n*
  2122.             *     indeX    filename  OverWrite  Permanent  Random*n*
  2123.             *     SendEOL  SendStartofPacket    TapeMode*n" );
  2124.         }
  2125.     else {
  2126.         printf( "Unrecognized query command.*n" );
  2127.         }
  2128.  
  2129.     printf( "*n For information on specific commands/options, type*n" );
  2130.     printf( "     Help <command or option>*n*n" );
  2131.  
  2132.     }    /*  prquery  */
  2133. /*  kermit/s/prquery    >> End <<  */
  2134. /*  kermit/s/prsts    >> Begin <<  */
  2135. /*
  2136.  *    PRSTS    -- PRint the STatuS.
  2137.  */
  2138.  
  2139. prsts( unit ) {
  2140.     extrn    curflgs, chktype, debug, delay, retryflg, tapflg, wc;
  2141.     extrn    eol, stpkt;
  2142.     auto    old_unit;
  2143.  
  2144.     if( nargs() < 1 )
  2145.         unit    = -4;
  2146.  
  2147.     old_unit    = .write( unit );
  2148.  
  2149.     printf( "*nStatus:*n" );
  2150.  
  2151.     printf( "Format=" );
  2152.     if( curflgs[FL_MODE] == FM_TEXT )
  2153.         printf( "Text" );
  2154.     else if( curflgs[FL_MODE] == FM_ASA9 )
  2155.         printf( "Bytestream" );
  2156.     else if( curflgs[FL_MODE] == FM_BITS )
  2157.         printf( "Bitstream" );
  2158.     printf( "*n" );
  2159.  
  2160.     printf( "Debug=" );
  2161.     if( debug == DBG_OFF )
  2162.         printf( "Off " );
  2163.     if( debug & DBG_STATES )
  2164.         printf( "States " );
  2165.     if( debug & DBG_PACKETS )
  2166.         printf( "Packets " );
  2167.     if( debug & DBG_LOGFILE )
  2168.         printf( "Logfile " );
  2169.     printf( "*n" );
  2170.  
  2171.     printf( "Delay=%d*n", delay );
  2172.     printf( "%cRandom*n", curflgs[FL_RAND] ? '+' : '-' );
  2173.     printf( "%cDiscard*n", curflgs[FL_DISC] ? '+' : '-' );
  2174.     printf( "%cPermanent*n", curflgs[FL_PERM] ? '+' : '-' );
  2175.     printf( "%cOverWrite*n", curflgs[FL_OVWR] ? '+' : '-' );
  2176.     printf( "%cTapeMode*n", tapflg ? '+' : '-' );
  2177.     printf( "SendEndOfLine=%d*n", eol );
  2178.     printf( "SendStartofPacket=%d*n", stpkt );
  2179.     printf( "CWD=%s*n", wc );
  2180.  
  2181.     printf( "*n" );
  2182.  
  2183.     .write( old_unit );
  2184.     }    /*  prsts  */
  2185. /*  kermit/s/prsts    >> End <<  */
  2186. /*  kermit/s/put_eof    >> Begin <<  */
  2187. /*
  2188.  *    PUT_EOF    -- PUT an EOF to the file.
  2189.  */
  2190.  
  2191. put_eof() {
  2192.     extrn    binfil, fd, flbuf, flidx, flcnt, flseg, flsec;
  2193.     auto    sts;
  2194.  
  2195.     if( flidx == 0 )
  2196.         return;
  2197.  
  2198.     if( binfil != FM_TEXT ) {
  2199.         if( (sts = write( fd, flbuf, flsec, 320 )) < 0 )
  2200.             return( sts );
  2201.         flsec    += 5;
  2202.         flidx    = 0;
  2203.         return;
  2204.         }
  2205.  
  2206.     flbuf[0]    = ((flidx+3)/4) << 18;
  2207.     flbuf[0]    |= (flidx&03)<<16;
  2208.  
  2209.     while( flidx & 03 )
  2210.         lchar( &flbuf[1], flidx++, 0177 );
  2211.  
  2212.     if( flseg == 0 ) {
  2213.         flbuf[0]    |= (01 << 10) | 0600;
  2214.         }
  2215.     else {
  2216.         flbuf[0]    |= (02 << 10) | (flseg++);
  2217.         flseg    = 0;
  2218.         }
  2219.  
  2220.     if( (sts = putrec( fd, flbuf )) < 0 )
  2221.         return( sts );
  2222.  
  2223.     flidx    = 0;
  2224.  
  2225.     return( 0 );
  2226.     }    /*  put_eof  */
  2227. /*  kermit/s/put_eof    >> End <<  */
  2228. /*  kermit/s/put_rec    >> Begin <<  */
  2229. /*
  2230.  *    PUT_REC    -- PUT a physical RECord out to a file.
  2231.  */
  2232.  
  2233. put_rec( partflag ) {
  2234.     extrn    fd, binfil, flbuf, flidx, flcnt, flseg, flsec;
  2235.     auto    sts;
  2236.  
  2237.     if( binfil != FM_TEXT ) {
  2238.         if( (sts = write( fd, flbuf, flsec, 320 )) < 0 )
  2239.             return( sts );
  2240.         flsec    += 5;
  2241.         flidx    = 0;
  2242.         zero( flbuf, 320 );
  2243.         return;
  2244.         }
  2245.  
  2246.     if( flidx == 0 ) {
  2247.         flbuf[1]    = 0177 << 27;
  2248.         flidx    = 1;
  2249.         }
  2250.  
  2251.     flbuf[0]    = ((flidx+3)/4) << 18;
  2252.     flbuf[0]    |= (flidx&03)<<16;
  2253.  
  2254.     while( flidx & 03 )
  2255.         lchar( &flbuf[1], flidx++, 0177 );
  2256.  
  2257.     if( flseg == 0 ) {
  2258.         flbuf[0]    |= 0600;
  2259.         if( partflag ) {
  2260.             flbuf[0]    |= 01 << 10;
  2261.             flseg    = 1;
  2262.             }
  2263.         }
  2264.     else if( partflag )
  2265.         flbuf[0]    |= (02 << 10) | (flseg++);
  2266.     else {
  2267.         flbuf[0]    |= (03 << 10) | flseg;
  2268.         flseg    = 0;
  2269.         }
  2270.  
  2271.     if( (sts = putrec( fd, flbuf )) < 0 )
  2272.         return( sts );
  2273.  
  2274.     zero( flbuf, 320 );
  2275.  
  2276.     flidx    = 0;
  2277.  
  2278.     return( 0 );
  2279.     }    /*  put_rec  */
  2280. /*  kermit/s/put_rec    >> End <<  */
  2281. /*  kermit/s/putbuf    >> Begin <<  */
  2282. /*
  2283.  *    PUTBUF    -- outPUT a BUFfer of data.
  2284.  */
  2285.  
  2286. putbuf( line, len ) {
  2287.     extrn    pad, xonwait, padchar, eolpend;
  2288.     auto    i;
  2289.  
  2290.     eprintf( DBG_PACKETS, "      spack (raw):%s*n", line );
  2291.  
  2292.     /*
  2293.      *    Issue any padding.
  2294.      */
  2295.  
  2296.     for( i = 1; i <= pad; ++i )
  2297.         putchar( padchar );
  2298.  
  2299.     /*
  2300.      *    Send the packet.
  2301.      */
  2302.  
  2303.     for( i = 0; i < len; ++i )
  2304.         putchar( line[i] );
  2305.  
  2306.     /*
  2307.      *    Flush the output buffer.
  2308.      */
  2309.  
  2310.     eolpend    = TRUE;
  2311.     flush();
  2312.  
  2313.     }    /*  putbuf  */
  2314. /*  kermit/s/putbuf    >> End <<  */
  2315. /*  kermit/s/putch    >> Begin <<  */
  2316. /*
  2317.  *    PUTCH    -- PUT a CHaracter out to a file.
  2318.  *        Returns 0 if successful, the negative of
  2319.  *        the major filesystem status if not.
  2320.  */
  2321.  
  2322. putch( ch ) {
  2323.     extrn    binfil, flcrcnt, flbuf, flidx, flcnt;
  2324.     auto    i, sts;
  2325.  
  2326.     if( binfil == FM_ASA9 ) {
  2327.         if( flidx >= flcnt )
  2328.             if( (sts = put_rec( TRUE )) < 0 )
  2329.                 return( sts );
  2330.         lchar( flbuf, flidx++, ch );
  2331.         }
  2332.     else if( binfil == FM_BITS ) {
  2333.         if( flidx >= flcnt )
  2334.             if( (sts = put_rec( TRUE )) < 0 )
  2335.                 return( sts );
  2336.         switch( flidx % 9 ) {
  2337.             case 0:    flbuf[2*(flidx/9)]        = ch<<28;        break;
  2338.             case 1:    flbuf[2*(flidx/9)]        |= ch<<20;        break;
  2339.             case 2:    flbuf[2*(flidx/9)]        |= ch<<12;        break;
  2340.             case 3:    flbuf[2*(flidx/9)]        |= ch<<4;        break;
  2341.             case 4:    flbuf[2*(flidx/9)]        |= ch>>4;
  2342.                     flbuf[2*(flidx/9)+1]    = (ch&017)<<32;    break;
  2343.             case 5:    flbuf[2*(flidx/9)+1]    |= ch<<24;        break;
  2344.             case 6:    flbuf[2*(flidx/9)+1]    |= ch<<16;        break;
  2345.             case 7:    flbuf[2*(flidx/9)+1]    |= ch<<8;        break;
  2346.             case 8:    flbuf[2*(flidx/9)+1]    |= ch;            break;
  2347.             }    /*  switch  */
  2348.         ++flidx;
  2349.         }
  2350.     else if( ch == '*r' )
  2351.         ++flcrcnt;
  2352.     else {
  2353.         for( i = (ch=='*n')?1:0; i < flcrcnt; ++i ) {
  2354.             if( flidx >= flcnt )
  2355.                 if( (sts = put_rec( TRUE )) < 0 )
  2356.                     return( sts );
  2357.             lchar( &flbuf[1], flidx++, '*r' );
  2358.             }
  2359.  
  2360.         if( flcrcnt > 0 && ch == '*n' ) {
  2361.             if( (sts = put_rec( FALSE )) < 0 )
  2362.                 return( sts );
  2363.             }
  2364.         else {
  2365.             if( flidx >= flcnt )
  2366.                 if( (sts = put_rec( TRUE )) < 0 )
  2367.                     return( sts );
  2368.             lchar( &flbuf[1], flidx++, ch );
  2369.             }
  2370.         flcrcnt    = 0;
  2371.         }
  2372.  
  2373.     return( 0 );
  2374.     }    /*  putch  */
  2375. /*  kermit/s/putch    >> End <<  */
  2376. /*  kermit/s/quit    >> Begin <<  */
  2377. /*
  2378.  *    QUIT    -- QUIT kermit and logout the session.
  2379.  */
  2380.  
  2381. quit() {
  2382.  
  2383.     eprintf( DBG_ALL, "EXIT and logoff*n" );
  2384.     putchar( DC1 );
  2385.     flush();
  2386.  
  2387.     sum.up();        /*  Close all open files  */
  2388.  
  2389.     sleep( 5 );
  2390.     drl.drl( T.DISC_ );
  2391.  
  2392.     }    /*  quit  */
  2393. /*  kermit/s/quit    >> End <<  */
  2394. /*  kermit/s/rdata    >> Begin <<  */
  2395. /*
  2396.  *    RDATA    -- Receive DATA.
  2397.  */
  2398.  
  2399. rdata() {
  2400.     extrn    n, numtry, oldtry, fd, state, packet, filnam, flsyerr, io.err;
  2401.     auto    num, len, sts;
  2402.  
  2403.     if( numtry > MAXTRY )        /*  "Abort" if too many tries  */
  2404.         return( 'm' );
  2405.  
  2406.     ++numtry;
  2407.  
  2408.     switch( rpack(&len,&num,packet) ) {    /*  Get packet  */
  2409.     case 'D':                                /*  Got Data packet  */
  2410.         if( num != n ) {                    /*  Right packet ?  */
  2411.             if( oldtry > MAXTRY )            /*  No.  Too many tries  */
  2412.                 return( 'm' );
  2413.             ++oldtry;                        /*  give up  */
  2414.             if( num == ((n-1)&63) ) {        /*  Previous packet again ?  */
  2415.                 spack( 'Y', num, 0, 0 );    /*  Yes,  re-ACK it  */
  2416.                 numtry    = 0;                /*  Reset try counter  */
  2417.                 return( state );            /*  Stay in D, don't write out data!  */
  2418.                 }
  2419.             else
  2420.                 return( 'n' );            /*  Sorry!  Wrong number.  */
  2421.             }
  2422.  
  2423.         /*
  2424.          *    Got data with the right packet number.
  2425.          *    Write the data to the file.
  2426.          */
  2427.  
  2428.         if( (sts = bufemp( packet, len )) < 0 ) {
  2429.             errmsg( "%s: %s", filnam, io.err[-sts] );
  2430.             return( 'a' );
  2431.             }
  2432.  
  2433.         spack( 'Y', n, 0, 0 );        /*  Acknowledge the packet  */
  2434.         oldtry    = numtry;            /*  Reset the try counters  */
  2435.         numtry    = 0;                /*  ...  */
  2436.         n    = (n+1) & 63;            /*  Bump the packet number  */
  2437.         return( 'D' );                /*  Remain in data state  */
  2438.  
  2439.     case 'F':                        /*  Got a File Header  */
  2440.         if( oldtry > MAXTRY )        /*  If too many tries, "abort"  */
  2441.             return( 'm' );
  2442.         ++oldtry;
  2443.         if( num == ((n-1)&63) ) {    /*  It was the previous packet  */
  2444.             spack( 'Y', num, 0, 0 );    /*  ACK it again  */
  2445.             numtry    = 0;            /*  Reset try counter  */
  2446.             return( state );        /*  Stay in data state  */
  2447.             }
  2448.         else
  2449.             return( 'n' );            /*  Not previous packet, "abort"  */
  2450.  
  2451.     case 'Z':                        /*  End-Of-File  */
  2452.         if( num != n )                /*  Must have right packet number  */
  2453.             return( 'n' );
  2454.         spack( 'Y', n, 0, 0 );        /*  OK, ACK it.  */
  2455.  
  2456.         /*
  2457.          *    Flush possible final CR.
  2458.          *    Flush file system buffers.
  2459.          *    Close the file.
  2460.          */
  2461.  
  2462.         if( (sts = bufemp( packet, 0 )) < 0 || (sts = put_eof()) < 0 ) {
  2463.             errmsg( "%s: %s", filnam, io.err[-sts] );
  2464.             return( 'a' );
  2465.             }
  2466.         else if( (sts = fclose( fd, FALSE )) < 0 ) {
  2467.             errmsg( "%s: %s", filnam, flsyerr[-sts] );
  2468.             return( 'a' );
  2469.             }
  2470.         fd    = ERR;                    /*  Remember that file was closed  */
  2471.         n    = (n+1) & 63;            /*  Bump the packet number  */
  2472.         return( 'F' );                /*  Go back to Receive File state  */
  2473.  
  2474.     case 'c':                        /*  No good packet came  */
  2475.         spack( 'N', n, 0, 0 );        /*  NAK  */
  2476.         return( state );            /*  Keep waiting  */
  2477.     case 'E':                        /*  Error packet  */
  2478.         errpkt( packet );            /*  print it  */
  2479.         return( 'a' );                /*  Abort  */
  2480.     default:
  2481.         return( 'w' );                /*  Some other packet, "abort"  */
  2482.         }
  2483.  
  2484.     }    /*  rdata  */
  2485. /*  kermit/s/rdata    >> End <<  */
  2486. /*  kermit/s/rdcomm    >> Begin <<  */
  2487. /*
  2488.  *    RDCOMM    -- ReaD the COMMunications line.
  2489.  *        Use paper tape mode to read in input.
  2490.  */
  2491.  
  2492. rdcomm() {
  2493.     extrn    fdtap, tpidx, tpcnt, tpeof, tpsect, tpbuf, msghdr, eolpend, eol;
  2494.     auto    i, psw, tal, drv, eolstr[0];
  2495.  
  2496.     if( tpeof ) {
  2497. HELL:
  2498.         psw    = setpsw(0);
  2499.         rstpsw(010000000000);
  2500.  
  2501.         if( eolpend ) {
  2502.             eolstr[0]    = eol << 27;
  2503.             tal    = tallyb( eolstr, 0, 1 );
  2504.             }
  2505.         else
  2506.             tal = tallyb( "*n", 0, 1 );
  2507.  
  2508.         drv = (&tal<<18) | 0100;
  2509.         drl.drl(T.TPIN_,&drv<<18);
  2510.  
  2511.         rstpsw(-1);
  2512.         setpsw(psw);
  2513.  
  2514.         tpsect    = 0;
  2515.         }
  2516.  
  2517.     if( nobrks() ) {
  2518.         printf( -4, "%s: Break key.*n", msghdr );
  2519.         printf( -4, "To exit KERMIT type control-D control-S (^D^S)" );
  2520.         flush();
  2521.         nobrks( MAX_BRKS );
  2522.         goto HELL;
  2523.         }
  2524.  
  2525.     read( fdtap, tpbuf, tpsect++, 64 );
  2526.  
  2527.     if( (tpbuf[1] & 04) || (!(tpbuf[1] & 02) && (tpbuf[32] & 04)) )
  2528.         eprintf( DBG_PACKETS, "Timing error in sector %6o", tpbuf[0] & 0777777 );
  2529.  
  2530.     if( tpbuf[1] & 02 ) {        /*  last block  */
  2531.         tpeof    = TRUE;
  2532.         tpcnt    = tpbuf[1]>>18;
  2533.         }
  2534.     else {
  2535.         tpeof    = (tpbuf[32] & 02);
  2536.         tpcnt    = (tpbuf[1]>>18) + (tpbuf[32]>>18);
  2537.  
  2538.         copy( &tpbuf[32], &tpbuf[33], 32 );
  2539.         }
  2540.  
  2541.     lchar( &tpbuf[2], tpcnt++, DC3 );
  2542.  
  2543.     tpidx    = 0;
  2544.  
  2545.     }    /*  rdcomm  */
  2546. /*  kermit/s/rdcomm    >> End <<  */
  2547. /*  kermit/s/recsw    >> Begin <<  */
  2548. /*
  2549.  *    RECSW    -- RECeive SWitcher.
  2550.  *
  2551.  *    This is the state table switcher for receiving files.
  2552.  */
  2553.  
  2554. recsw( flags ) {
  2555.     extrn    n, numtry, fd, srvflg, state, filnam, flsyerr;
  2556.     auto    lstate, llstate, sts;
  2557.  
  2558.     if( srvflg == 1 )            /*  If in server mode  */
  2559.         state    = 'F';            /*  start in F state.  */
  2560.     else {
  2561.         state    = 'R';            /*  Receive is the start state  */
  2562.         n        = 0;            /*  Initialize message number  */
  2563.         numtry    = 0;            /*  Say no tries yet  */
  2564.         }
  2565.  
  2566.     repeat {            /*  Do until done  */
  2567.         eprintf( DBG_STATES, "  recsw %c %d*n", state, n );
  2568.  
  2569.         switch( state ) {
  2570.         case 'D':
  2571.             state    = rdata();    /*  Data receive state  */
  2572.             break;
  2573.         case 'F':
  2574.             state    = rfile( flags );    /*  File receive state  */
  2575.             break;
  2576.         case 'R':
  2577.             state    = rinit();    /*  Send initiate state  */
  2578.             break;
  2579.         case 'C':
  2580.             flusheol();
  2581.             return( TRUE );                /*  Complete state  */
  2582.         default:                        /*  Anything else is an error  */
  2583.             failmsg( llstate );            /*  Put out an error message  */
  2584.             if( fd != ERR ) {
  2585.                 /*
  2586.                  *    If the file was left open,
  2587.                  *    close it with an error status.
  2588.                  */
  2589.                 if( (sts = put_eof()) < 0
  2590.                 ||    (sts = fclose( fd, TRUE )) < 0 )
  2591.                     errmsg( "%s: %s", filnam, flsyerr[-sts] );
  2592.                 fd    = ERR;                /*  Remember it's closed  */
  2593.                 }
  2594.             flusheol();
  2595.             return( FALSE );            /*  Error return  */
  2596.             }
  2597.  
  2598.         llstate    = lstate;            /*  Remember last state  */
  2599.         lstate    = state;
  2600.         }
  2601.  
  2602.     }    /*  recsw  */
  2603. /*  kermit/s/recsw    >> End <<  */
  2604. /*  kermit/s/rfile    >> Begin <<  */
  2605. /*
  2606.  *    RFILE    -- Receive FILE header.
  2607.  */
  2608.  
  2609. rfile( flags ) {        
  2610.     extrn    n, numtry, oldtry, state, packet, fillst, flsyerr;
  2611.     auto    num, len, x, g, str;
  2612.  
  2613.     if( numtry > MAXTRY )                /*  If too many tries, "abort"  */
  2614.         return( 'm' );
  2615.     ++numtry;
  2616.  
  2617.     switch( rpack(&len,&num,packet) ) {        /*  Get a packet  */
  2618.     case 'S':                                /*  Send-Init, maybe our ACK lost  */
  2619.         if( oldtry > MAXTRY )                /*  If too many tries, "abort"  */
  2620.             return( 'm' );
  2621.         ++oldtry;
  2622.         if( num == ((n-1)&63) ) {            /*  Previous packet count mod 64?  */
  2623.             spar( packet );                    /*  Yes, ACK it again  */
  2624.             spack( 'Y', num, INIT_SIZ, packet );    /*  with our Send-Init parameters  */
  2625.             numtry    = 0;                    /*  Reset try counter  */
  2626.             return( state );                /*  Stay in this state  */
  2627.             }
  2628.         else
  2629.             return( 'n' );                    /*  Not previous packet, "abort"  */
  2630.  
  2631.     case 'Z':                                /*  End of File  */
  2632.         if( oldtry > MAXTRY )
  2633.             return( 'm' );
  2634.         ++oldtry;
  2635.         if( num == ((n-1)&63) ) {            /*  Previous packet, mod 64?  */
  2636.             spack( 'Y', num, 0, 0 );        /*  Yes,  ACK it again.  */
  2637.             numtry    = 0;                    /*  Reset try counter  */
  2638.             return( state );                /*  Stay in this state  */
  2639.             }
  2640.         else
  2641.             return( 'n' );                    /*  Not previous packet, "abort"  */
  2642.  
  2643.     case 'F':                                /*  File Header  */
  2644.         if( num != n )
  2645.             return( 'n' );                    /*  which is what we really want  */
  2646.                                             /*  The packet number must be right  */
  2647.         str    = allocate( MAXLINE _SIZE );
  2648.         pkt2str( str, packet );
  2649.         getcmd( str, PAR_FIL, flags );
  2650.         if( fillst[0] > 1 ) {
  2651.             errmsg( "%s: Only one file name allowed.", str );
  2652.             return( 'a' );
  2653.             }
  2654.         else if( fillst[0] < 1 ) {
  2655.             errmsg( "%s: No filename given.", str );
  2656.             return( 'a' );
  2657.             }
  2658.         g    = getfil();            /*  Try to open a new file  */
  2659.         if( g < 0 ) {
  2660.             errmsg( "%s: %s", str, flsyerr[-g] );
  2661.             return( 'a' );                    /*  Give up if can't  */
  2662.             }
  2663.         else if( g == 'a' ) {                /*  File already exists  */
  2664.             errmsg( "%s already exists", packet );
  2665.             return( 'a' );                    /*  Give up if can't  */
  2666.             }
  2667.  
  2668.         str2pkt( packet, str );
  2669.         spack( 'Y', n, length(packet), packet );    /*  Acknowledge the file header  */
  2670.         oldtry    = numtry;                    /*  Reset the try counters  */
  2671.         numtry    = 0;                        /*  ....  */
  2672.         n    = (n+1) & 63;                    /*  Bump packet number, mod 64  */
  2673.         return( 'D' );                        /*  Switch to Data state  */
  2674.  
  2675.     case 'B':                                /*  Break transmission (EOT)  */
  2676.         if( num != n )                        /*  Need right packet number here  */
  2677.              return( 'n' );
  2678.         spack( 'Y', n, 0, 0 );                /*  Say OK  */
  2679.         return( 'C' );                        /*  Go to complete state  */
  2680.  
  2681.     case 'c':                                /*  Couldn't get good packet  */
  2682.         spack( 'N', n, 0, 0 );                /*  NAK  */
  2683.         return( state );                    /*  Keep Waiting  */
  2684.     case 'E':                                /*  Error packet  */
  2685.         errpkt( packet );                    /*  print it  */
  2686.         return( 'a' );                        /*  Abort  */
  2687.     default:
  2688.         return( 'w' );                        /*  Some other packet, "abort"  */
  2689.         }
  2690.  
  2691.     }    /*  rfile  */
  2692. /*  kermit/s/rfile    >> End <<  */
  2693. /*  kermit/s/rinit    >> Begin <<  */        
  2694. /*
  2695.  *    RINIT    -- Receive INITialization.
  2696.  */
  2697.  
  2698. rinit() {
  2699.     extrn    n, numtry, oldtry, state, packet;
  2700.     auto    len, num;
  2701.  
  2702.     if( numtry > MAXTRY )            /*  If too many tries "abort"  */
  2703.         return( 'm' );
  2704.  
  2705.     ++numtry;
  2706.  
  2707.     switch( rpack(&len,&num,packet) ) {            /*  Get a packet  */
  2708.     case 'S':                        /*  Send-Init  */
  2709.         rpar(packet);                /*  Get the other side's init data  */
  2710.         spar(packet);                /*  Fill up packet with my init info  */
  2711.         spack( 'Y', n, INIT_SIZ, packet );    /*  ACK with my parameters  */
  2712.         oldtry    = numtry;            /*  Save old try count  */
  2713.         numtry    = 0;                /*  Start a new counter  */
  2714.         n    = (n+1) & 63;            /*  Bump packet number, mod 64  */
  2715.         return( 'F' );                /*  Enter file send state  */
  2716.  
  2717.     case 'c':                        /*  Didn't get packet  */
  2718.         spack( 'N', n, 0, 0 );        /*  NAK  */
  2719.         return( state );            /*  Keep waiting  */
  2720.     case 'E':                        /*  Error packet  */
  2721.         errpkt( packet );            /*  print it  */
  2722.         return( 'a' );                /*  Abort  */
  2723.     default:
  2724.         return( 'w' );                /*  Some other packet type, "abort"  */
  2725.         }
  2726.  
  2727.     }    /*  rinit  */
  2728. /*  kermit/s/rinit    >> End <<  */
  2729. /*  kermit/s/rpack    >> Begin <<  */
  2730. /*
  2731.  *    RPACK    -- Read a PACKet.
  2732.  */
  2733.  
  2734. rpack( len, num, data ) {
  2735.     extrn    lastpk, chktype;
  2736.     auto    i, checks, pcheck, ch, type;
  2737.  
  2738.     while( (ch = getcomm()) != SOH )        /*  wait for start of packet  */
  2739.         if( ch == '*004' )
  2740.             error( "I'm getting out of here!" );
  2741.  
  2742.     /*
  2743.      *    Got SOH, loop to get a packet.
  2744.      */
  2745.  
  2746.     repeat {
  2747.  
  2748.         ch    = getcomm();            /*  Get character  */
  2749.         if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2750.  
  2751.         checks    = ch;                /*  Start the checksum  */
  2752.         *len    = unchar(ch)-3;        /*  Character count  */
  2753.  
  2754.         ch    = getcomm();            /*  Get character  */
  2755.         if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2756.  
  2757.         checks    += ch;                /*  Accumulate checksum  */
  2758.         *num    = unchar(ch);        /*  Packet number  */
  2759.  
  2760.         ch    = getcomm();            /*  Get character  */
  2761.         if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2762.  
  2763.         checks    += ch;                /*  Accumulate checksum  */
  2764.         type    = ch;                /*  Packet type  */
  2765.  
  2766.         for( i = 0; i < *len; ++i ) {    /*  The data itself if any  */
  2767.             ch    = getcomm();            /*  Get character  */
  2768.             if( ch == SOH )    break;        /*  Resynch if SOH  */
  2769.  
  2770.             checks    += ch;                /*  Accumulate checksum  */
  2771.             data[i]    = ch;                /*  Put it in the data buffer  */
  2772.             }
  2773.         if( ch == SOH ) next;            /*  Resynch if SOH  */
  2774.  
  2775.         data[*len]    = '*0';                /*  Mark end of data  */
  2776.  
  2777.         ch    = getcomm();            /*  Get character  */
  2778.         if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2779.  
  2780.         pcheck    = unchar( ch );
  2781.  
  2782.         if( type != 'S' && type != 'I' && type != 'R' ) {
  2783.             if( chktype >= 2 ) {
  2784.                 ch    = getcomm();            /*  Get character  */
  2785.                 if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2786.  
  2787.                 pcheck    = (pcheck<<6) | unchar(ch);
  2788.                 }
  2789.  
  2790.             if( chktype >= 3 ) {
  2791.                 ch    = getcomm();            /*  Get character  */
  2792.                 if( ch == SOH )    next;        /*  Resynchronize if SOH  */
  2793.  
  2794.                 pcheck    = (pcheck<<6) | unchar(ch);
  2795.                 }
  2796.             }
  2797.  
  2798.         break;                        /*  Got checksum, done  */
  2799.         }    /*  repeat  */
  2800.  
  2801.     eprintf( DBG_PACKETS, "    rpack: %c %2d '%s'*n", type, *num, data );
  2802.  
  2803.     checks    = chksum(checks,*len,*num,type,data);    /*  Perform checksum  */
  2804.     if( checks != pcheck ) {            /*  Check the checks, fail if bad  */
  2805.         eprintf( DBG_PACKETS, "    rpack: checksum fail: %d/%d*n", pcheck, checks );
  2806.         return( lastpk = 'c' );            /*  indicate checksum failure  */
  2807.         }
  2808.  
  2809.     return( lastpk = type );            /*  All OK, return packet type  */
  2810.  
  2811.     }    /*  rpack  */
  2812. /*  kermit/s/rpack    >> End <<  */
  2813. /*  kermit/s/rpar    >> Begin <<  */
  2814. /*
  2815.  *    RPAR    -- Read PARameters.
  2816.  *
  2817.  *    Get the other side's send-init parameters.
  2818.  */
  2819.  
  2820. rpar( data ) {
  2821.     extrn    spsiz, pad, timint, dobquo, dorept;
  2822.     extrn    padchar, eol, quotec, bquote, reptc, chktype;
  2823.  
  2824.     dobquo    = FALSE;        /*  default: no eighth-bit quoting  */
  2825.     dorept    = FALSE;        /*  default: no repeat prefixing  */
  2826.  
  2827.     if( !data[SI_MAXPACK] )    return;
  2828.     /*  Maximum send packet  */
  2829.     spsiz    = min( MAXPACK, unchar(data[SI_MAXPACK]) );
  2830.  
  2831.     if( !data[SI_MYTIME] )    return;
  2832.     if( unchar(data[SI_MYTIME]) <= 0 )            /*  When I should time out on reads  */
  2833.         timint    = MAXTIM;
  2834.     else
  2835.         timint    = min( MAXTIM, max( MINTIM, unchar(data[SI_MYTIME]) ) );
  2836.  
  2837.     if( !data[SI_MYPAD] )    return;
  2838.     pad    = unchar(data[SI_MYPAD]);            /*  Number of pads to send  */
  2839.  
  2840.     if( !data[SI_MYPCHAR] )    return;
  2841.     padchar    = ctl(data[SI_MYPCHAR]);            /*  Padding character to send  */
  2842.  
  2843.     if( !data[SI_MYEOL] )    return;
  2844.     eol    = unchar(data[SI_MYEOL]);            /*  EOL character I must send  */
  2845.  
  2846.     if( !data[SI_MYQUOTE] )    return;
  2847.     quotec    = data[SI_MYQUOTE];            /*  Incoming data quote character  */
  2848.  
  2849.     if( !data[SI_MYBQUOTE] )    return;
  2850.     bquote    = data[SI_MYBQUOTE];            /*  Incoming binary quote character  */
  2851.     /*
  2852.      *    If I have quoting compiled in
  2853.      */
  2854.     if(        (MYBQUOTE >= 33 && MYBQUOTE <= 62)
  2855.         ||    (MYBQUOTE >= 96 && MYBQUOTE <= 126)
  2856.         ||    MYBQUOTE == 'Y' ) {
  2857.         if( (bquote >= 33 && bquote <=62) || (bquote >=96 && bquote <= 126) )
  2858.             dobquo    = TRUE;            /*  Eighth-bit quoting agreed, use his char  */
  2859.         else if( bquote == 'Y' ) {
  2860.             dobquo    = TRUE;            /*  Eighth-bit quoting agreed  */
  2861.             bquote    = MYBQUOTE;            /*  Use my char  */
  2862.             if( MYBQUOTE == 'Y' )
  2863.                 bquote    = '&';            /*  Both said 'Y': use '&'  */
  2864.             }
  2865.         }
  2866.  
  2867.     if( !data[SI_MYCHECKS] )    return;
  2868.  
  2869.     if( !data[SI_MYREPTC] )    return;
  2870.     reptc    = data[SI_MYREPTC];            /*  Incoming repeat prefix char  */
  2871.     if( ((reptc >= 33 && reptc <=62)
  2872.         || (reptc >=96 && reptc <= 126)) && reptc == MYREPTC )
  2873.         dorept    = TRUE;            /*  Our repeat prefixes agree, so use it  */
  2874.  
  2875.     }    /*  rpar  */
  2876. /*  kermit/s/rpar    >> End <<  */
  2877. /*  kermit/s/rsclash    >> Begin <<  */
  2878. /*
  2879.  *    RSCLASH    -- ReSolve filename CLASH.
  2880.  *        Rename the filename given, if necessary,
  2881.  *        so as not to conflict with existing files.
  2882.  */
  2883.  
  2884. rsclash( fname ) {
  2885.     extrn    randflg;
  2886.     auto    len, st, n, fd;
  2887.  
  2888.     len    = length(fname);
  2889.     for( st = len-1; st >= 0; --st )
  2890.         if( char(fname,st) == '/' )
  2891.             break;
  2892.  
  2893.     n    = 0;
  2894.     while( n < 10 && (fd = open( fname, randflg ? "rufeb" : "rufe" )) > 0 ) {
  2895.         eprintf( DBG_STATES, "File '%s' already exists.*n", fname );
  2896.         close( fd );
  2897.  
  2898.         ++n;
  2899.         if( n > 1 )
  2900.             lchar( fname, len-1, n + '0' );
  2901.         else if( len - st - 1 == 12 )
  2902.             lchar( fname, len-1, '1' );
  2903.         else if( len - st - 1 == 11 ) {
  2904.             concat( fname, fname, "1" );
  2905.             ++len;
  2906.             }
  2907.         else {
  2908.             concat( fname, fname, "_1" );
  2909.             len    += 2;
  2910.             }
  2911.         }
  2912.  
  2913.     /*
  2914.      *    Return TRUE if we have a unique filename,
  2915.      *    FALSE otherwise.
  2916.      */
  2917.  
  2918.     return( n < 10 );
  2919.     }    /*  rsclash  */
  2920. /*  kermit/s/rsclash    >> End <<  */
  2921. /*  kermit/s/sbreak    >> Begin <<  */
  2922. /*
  2923.  *    SBREAK    -- Send BREAK (eot).
  2924.  */
  2925.  
  2926. sbreak() {
  2927.     extrn    n, numtry, state, recpkt, packet;
  2928.     auto    num, len;
  2929.  
  2930.     if( numtry > MAXTRY )
  2931.         return( 'm' );    /*  If too many tries "abort"  */
  2932.     ++numtry;
  2933.  
  2934.     spack('B', n, 0, packet );    /*  Send a B packet  */
  2935.     switch( rpack(&len,&num,recpkt) ) {        /*  What was the reply  */
  2936.     case 'N':            /*  NAK, fail  */
  2937.         if( n != ((num-1)&63) )    /*  ...unless for prev. packet.  */
  2938.           return( state );
  2939.         break;
  2940.  
  2941.     case 'Y':            /*  ACK  */
  2942.         if( n != num )
  2943.             return( state );    /*  If wrong ACK, fail  */
  2944.         numtry    = 0;                /*  Reset try counter  */
  2945.         n    = (n+1) & 63;            /*  and bump packet count  */
  2946.         return( 'C' );            /*  Switch state to Complete  */
  2947.  
  2948.     case 'c':
  2949.         return(state);        /*  Receive failure, stay in state B  */
  2950.     case 'E':            /*  Error packet  */
  2951.         errpkt( recpkt );            /*  print it  */
  2952.         return( 'a' );            /*  Abort  */
  2953.     default:
  2954.         return( 'w' );            /*  Other, "abort"  */
  2955.         }
  2956.  
  2957.     }    /*  sbreak  */
  2958. /*  kermit/s/sbreak    >> End <<  */
  2959. /*  kermit/s/sdata    >> Begin <<  */
  2960. /*
  2961.  *    SDATA    -- Send file DATA.
  2962.  */
  2963.  
  2964. sdata() {
  2965.     extrn    size, n, numtry, state, recpkt, packet;
  2966.     auto    num, len;
  2967.  
  2968.     if( numtry > MAXTRY )            /*  If too many tries, give up  */
  2969.         return( 'm' );
  2970.     ++numtry;
  2971.  
  2972.     spack( 'D', n, size, packet );        /*  Send a D packet  */
  2973.  
  2974.     switch( rpack(&len,&num,recpkt) ) {        /*  What was the reply  */
  2975.     case 'N':            /*  NAK, just stay in this state,  */
  2976.         if( n != ((num-1)&63) )    /*  unless for previous packet.  */
  2977.             return( state );
  2978.  
  2979.     case 'Y':                /*  ACK  */
  2980.         if( n != num )            /*  If wrong ACK, fail  */
  2981.             return( state );
  2982.         numtry    = 0;            /*  Reset try counter  */
  2983.         n    = (n+1) & 63;            /*  Bump packet count  */
  2984.         size    = bufill(packet);            /*  Get data from file  */
  2985.         if( size == 0 ) {            /*  If EOF set state to that  */
  2986.             return( 'Z' );
  2987.             }
  2988.         return( 'D' );            /*  Got data, stay in state D  */
  2989.  
  2990.     case 'c':            /*  Receive failure, stay in D  */
  2991.         return( state );
  2992.     case 'E':            /*  Error packet  */
  2993.         errpkt( recpkt );            /*  print it  */
  2994.         return('a');            /*  Abort  */
  2995.     default:            /*  Anything else "abort"  */
  2996.         return( 'w' );
  2997.         }
  2998.  
  2999.     }    /*  sdata  */
  3000. /*  kermit/s/sdata    >> End <<  */
  3001. /*  kermit/s/sendsw    >> Begin <<  */
  3002. /*
  3003.  *    SENDSW    -- SEND SWitcher.
  3004.  *
  3005.  *    Sendsw is the state table switcher for sending
  3006.  *    files.  It loops until either it finishes, or
  3007.  *    an error is encountered.  The routines called by
  3008.  *    sendsw are responsible for changing the state.
  3009.  *
  3010.  *    sname    -- name of file to send (0 -> use args)
  3011.  *    start    -- state to start in -- 'S' or 'F'
  3012.  */
  3013.  
  3014. sendsw( start ) {
  3015.     extrn    n, numtry, fd, state;
  3016.     auto    lstate, llstate;
  3017.     auto    sname;
  3018.  
  3019.     sname    = 0;
  3020.     state    = start;            /*  Use indicated start state (usually 'S')  */
  3021.     n        = 0;                /*  Initialize message number  */
  3022.     numtry    = 0;                /*  Say no tries yet  */
  3023.  
  3024.     repeat {                    /*  Do this as long as necessary  */
  3025.         eprintf( DBG_STATES, "  sendsw %c %d*n", state, n );
  3026.  
  3027.         switch( state ) {
  3028.         case 'D':                /*  Data-Send state  */
  3029.             state    = sdata();
  3030.             break;
  3031.         case 'F':                /*  File-Send  */
  3032.             state    = sfile(0);
  3033.             break;
  3034.         case 'Z':                /*  End of File  */
  3035.             state    = seof();
  3036.             if( state == 'F' && sname )        /*  If ready for next file  */
  3037.                 state    = 'B';                /*  Do Break  */
  3038.             break;
  3039.         case 'S':                    /*  Send Init  */
  3040.             state    = sinit();
  3041.             break;
  3042.         case 'B':            /*  Break-Send  */
  3043.             state    = sbreak();
  3044.             break;
  3045.         case 'C':            /*  Complete  */
  3046.             flusheol();
  3047.             return(TRUE);
  3048.         default:                        /*  Anything else is an error  */
  3049.             failmsg(llstate);            /*  Put out an error message  */
  3050.             if( fd != ERR ) {            /*  If file left open  */
  3051.                 close(fd);                /*  Close it  */
  3052.                 fd    = ERR;                /*  Remember it's closed  */
  3053.                 }
  3054.  
  3055.             flusheol();
  3056.             return(FALSE);        /*  Error return  */
  3057.             }
  3058.  
  3059.         llstate    = lstate;
  3060.         lstate    = state;            /*  Remember last state  */
  3061.         }
  3062.  
  3063.     }    /*  sendsw  */
  3064. /*  kermit/s/sendsw    >> End <<  */
  3065. /*  kermit/s/seof    >> Begin <<  */
  3066. /*
  3067.  *    SEOF    -- Send End Of File.
  3068.  */
  3069.  
  3070. seof() {
  3071.     extrn    n, numtry, fd, state, recpkt, packet;
  3072.     auto    num, len;
  3073.  
  3074.     if( numtry > MAXTRY )            /*  If too many tries, give up  */
  3075.         return( 'm' );
  3076.  
  3077.     ++numtry;
  3078.  
  3079.     spack( 'Z', n, 0, packet );            /*  Send  a Z packet  */
  3080.  
  3081.     switch( rpack(&len,&num,recpkt) ) {    /*  What was the reply ?  */
  3082.     case 'B':            /*  NAK, fail  */
  3083.         if( n != ((num-1)&63) )    /*  unless for previous packet  */
  3084.             return(state);
  3085.  
  3086.     case 'Y':            /*  ACK  */
  3087.         if( n != num )                /*  If wrong ACK, hold out  */
  3088.             return( state );
  3089.         numtry    = 0;            /*  Reset try counter  */
  3090.         n    = (n+1) & 63;            /*  Bump packet count  */
  3091.         close(fd);            /*  Close the input file  */
  3092.         fd    = ERR;            /*  and flag that we did  */
  3093.         return( 'F' );            /*  Go to file header state  */
  3094.  
  3095.     case 'c':            /*  Receive failure, stay in state Z  */
  3096.         return( state );
  3097.     case 'E':            /*  Error packet  */
  3098.         errpkt( recpkt );            /*  print it  */
  3099.         return( 'a' );            /*  Abort  */
  3100.     default:                /*  Something else, "abort"  */
  3101.         return( 'w' );
  3102.         }
  3103.  
  3104.     }    /*  seof  */
  3105. /*  kermit/s/seof    >> End <<  */
  3106. /*  kermit/s/server    >> Begin <<  */
  3107. /*
  3108.  *    SERVER    -- process SERVER mode.
  3109.  *
  3110.  *    This is the state controller for the server mode of operation.
  3111.  */
  3112.  
  3113. server( flags ) {
  3114.     extrn    n, numtry, oldtry, fd, filnam, recpkt, packet;
  3115.     auto    len, num, junk, timeos, typ;
  3116.  
  3117.     n        = 0;            /*  Initialize message number  */
  3118.     numtry    = 0;            /*  Say no tries yet  */
  3119.     timeos    = 0;            /*  No timeouts seen yet  */
  3120.  
  3121.     repeat {            /*  Do until told to quit  */
  3122.         typ    = rpack( &len, &num, packet );            /*  Get a packet  */
  3123.         eprintf( DBG_STATES, "*nServer %c*n", typ );
  3124.  
  3125.         switch( typ ) {
  3126.         case 'S':                    /*  The other side wants to initialize  */
  3127.         case 'I':
  3128.             rpar(packet);            /*  Get other side's initial parameters  */
  3129.             spar(packet);            /*  Get my initial parameters  */
  3130.             spack( 'Y', n, INIT_SIZ, packet );    /*  Send ACK with my init parameters  */
  3131.             oldtry    = numtry;        /*  Reset try counters  */
  3132.             numtry    = 0;            /*  ....  */
  3133.             if( typ == 'S' ) {        /*  If this was a send-init packet  */
  3134.                 n    = (n+1) & 63;    /*  Increment packet count  */
  3135.                 recsw( flags );        /*  Go to receive state to receive file  */
  3136.                 }
  3137.             n    = 0;                /*  Reset packet count  */
  3138.             break;
  3139.  
  3140.         case 'R':                        /*  The other side wants to receive  */
  3141.             pkt2str( filnam, packet );    /*  To let packet array be reused  */
  3142.             getcmd( filnam, PAR_FIL, flags );
  3143.             sendsw( 'S' );            /*  Send the requested file  */
  3144.             n    = 0;
  3145.             break;
  3146.  
  3147.         case 'G':                        /*  Other side is sending a command  */
  3148.             if( !generic( num, packet[0], &packet[1], flags ) ) {
  3149.                 flusheol();
  3150.                 return;
  3151.                 }
  3152.             n    = 0;
  3153.             break;
  3154.  
  3155.         case 'X':            /*  Valid, but unimplemented  */
  3156.             errmsg( "Text headers are not implemented." );
  3157.             break;
  3158.  
  3159.         case 'C':
  3160.             errmsg( "Host commands are not implemented." );
  3161.             break;
  3162.  
  3163.         case 'K':
  3164.             errmsg( "Remote Kermit commands are not yet implemented." );
  3165.             break;
  3166.  
  3167.         case 'N':            /*  NAK: ignore it (some confusion)  */
  3168.             break;
  3169.  
  3170.         case 'a':            /*  EOF on line: abort  */
  3171.             flusheol();
  3172.             return( 'a' );
  3173.  
  3174.         case 'c':            /*  checksum err:  */
  3175.             spack( 'N', n, 0, 0 );            /*  NAK it  */
  3176.             n    = 0;
  3177.             break;
  3178.  
  3179.         case 't':            /*  timeout  */
  3180.             timeos    = (timeos+1) & 63;                /*  increment timeout counter  */
  3181.             if( timeos == 0 )            /*  If it rolls over (every fifth)  */
  3182.                 spack( 'N', n, 0, 0 );        /*  send out a NAK,  just to keep line active  */
  3183.             n    = 0;
  3184.             break;
  3185.  
  3186.         case 'E':            /*  Error packet  */
  3187.             errpkt( recpkt );            /*  print it  */
  3188.             break;
  3189.  
  3190.         default:                /*  Anything else, reset packet count, retry  */
  3191.             /*  Send an error message  */
  3192.             errmsg( "%c: not a valid Kermit server command", typ );
  3193.             n    = 0;            /*  Reset counter  */
  3194.             }
  3195.  
  3196.         if( fd != ERR ) {            /*  If a file was left open (xfer aborted)  */
  3197.             close( fd );            /*  Close it  */
  3198.             fd    = ERR;            /*  Remember closure  */
  3199.             }
  3200.         }
  3201.  
  3202.     }    /*  server  */
  3203. /*  kermit/s/server    >> End <<  */
  3204. /*  kermit/s/sfile    >> Begin <<  */
  3205. /*
  3206.  *    SFILE    -- Send FILE header.
  3207.  *
  3208.  *        sname = 0 if the default filename is to be sent,
  3209.  *                the filename to send otherwise.
  3210.  */
  3211.  
  3212. sfile( sname ) {
  3213.     extrn    size, n, numtry, remote, state, filnam, recpkt, packet;
  3214.     auto    num, len, g, str;
  3215.  
  3216.     g    = gnxtfl();                    /*  Open the file to be sent  */
  3217.     if( g != 'F' )                    /*  'F' => OK  */
  3218.         return( g );                /*  abort or break states  */
  3219.  
  3220.     if( numtry > MAXTRY )            /*  If too many tries, give up  */
  3221.         return( 'm' );
  3222.     ++numtry;
  3223.  
  3224.     if( sname )
  3225.         concat( filnam, sname );
  3226.     len    = str2pkt( packet, filnam );
  3227.     spack( 'F', n, len, packet );        /*  Send an F packet  */
  3228.     switch( rpack(&len,&num,recpkt) ) {    /*  What was the reply ?  */
  3229.     case 'N':                            /*  NAK, just stay in this state  */
  3230.         if( n != ((num-1)&63) )            /*  unless NAK for next packet  */
  3231.             return( state );
  3232.  
  3233.     case 'Y':                        /*  ACK  */
  3234.         if( n != num )                /*  If wrong ACK, stay in F state  */
  3235.             return( state );
  3236.         if( len > 0 && !remote ) {    /*  If the remote filename was returned  */
  3237.             str    = allocate( MAXLINE _SIZE );
  3238.             pkt2str( str, recpkt );
  3239.             errmsg( "file being saved as %s", str );    /*  print it out  */
  3240.             }
  3241.         numtry    = 0;                /*  Reset try counter  */
  3242.         n    = (n+1) & 63;            /*  Bump packet count  */
  3243.         size    = bufill(packet);    /*  Get first data from file  */
  3244.         return('D');                /*  Switch to state D  */
  3245.  
  3246.     case 'c':                        /*  Receive failure, stay in F state  */
  3247.     case 't':
  3248.         return( state );
  3249.     case 'E':                        /*  Error packet  */
  3250.         errpkt( recpkt );            /*  print it  */
  3251.         return( 'a' );                /*  Abort  */
  3252.     default:                        /*  Something else, just "abort"  */
  3253.         return( 'w' );
  3254.         }
  3255.  
  3256.     }    /*  sfile  */
  3257. /*  kermit/s/sfile    >> End <<  */
  3258. /*  kermit/s/sinit    >> Begin <<  */
  3259. /*
  3260.  *    SINIT    -- Send INITiate.
  3261.  *
  3262.  *    Send my parameters, get the other side's back.
  3263.  *
  3264.  *  The 10 second wait before sending the first packet gives
  3265.  *  the user time to get back to his local Kermit and set it
  3266.  *  to receive.
  3267.  */
  3268.  
  3269. sinit() {
  3270.     extrn    n, numtry, remote, sflg, delay;
  3271.     extrn    state, eol, quotec, recpkt, packet;
  3272.     auto    num, len;
  3273.  
  3274.     if( numtry > MAXTRY )            /*  If too many tries, give up  */
  3275.         return( 'm' );
  3276.     ++numtry;
  3277.  
  3278.     if( sflg && remote )            /*  If in send only (not server) mode  */
  3279.         sleep( delay );                /*  Wait delay seconds  */
  3280.  
  3281.     spar( packet );                        /*  Fill packet with init info  */
  3282.     spack( 'S', n, INIT_SIZ, packet );    /*  Send an S packet  */
  3283.     switch( rpack(&len,&num,recpkt) ) {    /*  What was reply ?  */
  3284.     case 'N':                        /*  NAK  */
  3285.         return( state );
  3286.  
  3287.     case 'Y':                        /*  ACK  */
  3288.         if( n != num )                /*  If wrong ACK, stay in S state  */
  3289.             return( state );
  3290.         rpar( recpkt );                /*  Get other sides init info  */
  3291.         if( eol == 0 )                /*  Check and set defaults  */
  3292.             eol    = CR;
  3293.         if( quotec == 0 )            /*  Control-prefix quote  */
  3294.             quotec    = '#';
  3295.         numtry    = 0;                /*  Reset try counter  */
  3296.         n    = (n + 1) & 63;            /*  Bump packet count, mod 64  */
  3297.  
  3298.         return( 'F' );                /*  Go to file header state  */
  3299.  
  3300.     case 'c':                        /*  Receive failure, stay in S state  */
  3301.     case 't':
  3302.         return( state );
  3303.     case 'E':                        /*  Error packet  */
  3304.         errpkt( recpkt );            /*  print it  */
  3305.         return( 'a' );                /*  Abort  */
  3306.     default:                        /*  Anything else just abort  */
  3307.         return( 'w' );
  3308.         }
  3309.  
  3310.     }    /*  sinit  */
  3311. /*  kermit/s/sinit    >> End <<  */
  3312. /*  kermit/s/spack    >> Begin <<  */
  3313. /*
  3314.  *    SPACK    -- Send a PACKet.
  3315.  *
  3316.  *    If no data is to be sent, data can be passed a zero.
  3317.  */
  3318.  
  3319. spack( type, num, len, data ) {
  3320.     extrn    pad, padchar, xonwait, chktype;
  3321.     auto    i, c, checks, bufptr, buffer[100];
  3322.  
  3323.     data[len]    = '*0';                /*  just to be sure  */
  3324.  
  3325.     if( data ) {
  3326.         eprintf( DBG_PACKETS, "    spack: %c %2d '", type, num );
  3327.         for( i = 0; (c = data[i]) != '*0'; ++i )
  3328.             eprintf( DBG_PACKETS, "%c", data[i] );
  3329.         eprintf( DBG_PACKETS, "'*n" );
  3330.         }
  3331.     else
  3332.         eprintf( DBG_PACKETS, "    spack: %c %2d*n", type, num );
  3333.  
  3334.     bufptr    = 0;                    /*  Initialize buffer pointer  */
  3335.  
  3336.     buffer[bufptr++]    = SOH;        /*  Packet marker, ASCII 1 (SOH)  */
  3337.     checks    = tochar(len+3);        /*  Initialize the checksum  */
  3338.     buffer[bufptr++]    = tochar(len+3);    /*  Send the character count  */
  3339.     checks    += tochar(num);            /*  Accumulate checksum  */
  3340.     buffer[bufptr++]    = tochar(num);        /*  Packet number  */
  3341.     checks    += type;                /*  Accumulate checksum  */
  3342.     buffer[bufptr++]    = type;        /*  Packet type  */
  3343.  
  3344.     for( i = 0; i < len; ++i ) {    /*  Loop for all data characters  */
  3345.         buffer[bufptr++]    = data[i];    /*  Get a character  */
  3346.         checks    += data[i];            /*  Accumulate checksum  */
  3347.         }
  3348.  
  3349.     /*
  3350.      *    Put the checksum in the packet.
  3351.      */
  3352.  
  3353.     checks    = chksum(checks,len,num,type,data);    /*  Perform checksum  */
  3354.  
  3355.     if( type != 'S' && type != 'I' && type != 'R' ) {
  3356.         if( chktype >= 3 )
  3357.             buffer[bufptr++]    = tochar( (checks>>12) & 017 );
  3358.         if( chktype >= 2 )
  3359.             buffer[bufptr++]    = tochar( (checks>>6) & 077 );
  3360.         }
  3361.     buffer[bufptr++]    = tochar( checks & 077 );
  3362.  
  3363.     buffer[bufptr]    = '*0';                    /*  Properly terminate packet  */
  3364.  
  3365.     if( xonwait ) {        /*  Now wait for DC1 (XON) 'prompt' character  */
  3366. /*
  3367.         while( (c = getchar()) != DC1 && c != SOH )
  3368. */
  3369.             ;
  3370.         }
  3371.  
  3372.     putbuf( buffer, bufptr );        /*  Send the packet  */
  3373.  
  3374.     }    /*  spack  */
  3375. /*  kermit/s/spack    >> End <<  */
  3376. /*  kermit/s/spar    >> Begin <<  */
  3377. /*
  3378.  *    SPAR    -- Set the PARameters.
  3379.  *
  3380.  *    Fill the data array with my send-init parameters
  3381.  *    Different machines may require different parameter definitions.
  3382.  */
  3383.  
  3384. spar( data ) {
  3385.     extrn    chktype, tapflg;
  3386.  
  3387.     data[SI_MAXPACK]    = tochar(MAXPACK);    /*  Biggest packet I can receive  */
  3388.     data[SI_MYTIME]        = tochar(MYTIME);    /*  When I want to be timed out  */
  3389.     data[SI_MYPAD]        = tochar(0);        /*  How much padding I need  */
  3390.     data[SI_MYPCHAR]    = ctl(MYPCHAR);        /*  Padding character I want  */
  3391.     if( tapflg )
  3392.         data[SI_MYEOL]    = tochar(MYEOL);    /*  End of Line character I want  */
  3393.     else
  3394.         data[SI_MYEOL]    = tochar( CR );
  3395.     data[SI_MYQUOTE]    = MYQUOTE;            /*  Control-Quote character I send  */
  3396.     data[SI_MYBQUOTE]    = MYBQUOTE;            /*  Binary-Quote character I send  */
  3397.     data[SI_MYCHECK]    = chktype + '0';    /*  My preferred type of checksum  */
  3398.     data[SI_MYREPTC]    = MYREPTC;            /*  Repeat-Quote character I send  */
  3399.     data[SI_MYCAPS]        = tochar(MYCAPS);    /*  My capabilities mask  */
  3400.     data[SI_MYSIZE]        = '*0';                /*  in case this gets printed  */
  3401.  
  3402.     }    /*  spar  */
  3403. /*  kermit/s/spar    >> End <<  */
  3404. /*  kermit/s/str2pkt    >> Begin <<  */
  3405. /*
  3406.  *    STR2PKT    -- convert a STRing TO a PacKeT.
  3407.  *        Strings are packed four character per word.
  3408.  *        Packets are simple arrays of characters.
  3409.  */
  3410.  
  3411. str2pkt( pkt, str ) {
  3412.     auto    i;
  3413.  
  3414.     for( i = 0; (pkt[i] = char(str,i)) != '*0'; ++i )
  3415.         ;
  3416.  
  3417.     return( i );
  3418.     }    /*  str2pkt  */
  3419. /*  kermit/s/str2pkt    >> End <<  */
  3420. /*  kermit/s/tochar    >> Begin <<  */
  3421. /*
  3422.  *    TOCHAR    -- convert a number TO a CHARacter.
  3423.  */
  3424.  
  3425. tochar( ch ) {
  3426.  
  3427.     return( ch + ' ' );
  3428.     }    /*  tochar  */
  3429. /*  kermit/s/tochar    >> End <<  */
  3430. /*  kermit/s/unchar    >> Begin <<  */
  3431. /*
  3432.  *    UNCHAR    -- UNdo the effect of toCHAR.
  3433.  */
  3434.  
  3435. unchar( ch ) {
  3436.  
  3437.     return( ch - ' ' );
  3438.     }    /*  unchar  */
  3439. /*  kermit/s/unchar    >> End <<  */
  3440. /*  kermit/s/ungtch    >> Begin <<  */
  3441. /*
  3442.  *    UNGTCH    -- UN-GeT a CHaracter.
  3443.  *        Put a character from a file back into the input stream.
  3444.  */
  3445.  
  3446. ungtch( ch ) {
  3447.     extrn    flunbuf;
  3448.  
  3449.     return( flunbuf[ ++flunbuf[0] ] = ch );
  3450.     }    /*  ungtch  */
  3451. /*  kermit/s/ungtch    >> End <<  */
  3452. /*  kermit/s/wdfile    >> Begin <<  */
  3453. /*
  3454.  *    WDFILE    -- prepend Working Directory to a FILE if necessary.
  3455.  */
  3456.  
  3457. wdfile( fname, prmflg ) {
  3458.     extrn    wc;
  3459.  
  3460.     if( prmflg && any( '/', fname ) == -1 )
  3461.         rep.st( fname, 0, "/", 0 );
  3462.  
  3463.     if( char( fname, 0 ) == '/' )
  3464.         rep.st( fname, 0, wc, 0 );
  3465.  
  3466.     }    /*  wdfile  */
  3467. /*  kermit/s/wdfile    >> End <<  */
  3468. /*  kermit/s/b_lib/rep.st.b    >> Begin <<  */
  3469. #Title h6600g8.196 rep.st,Replace substring
  3470. #lbl btnw
  3471. #ttldat 091482
  3472. #Copyright (c) 1982 by the University of Waterloo
  3473.  
  3474. rep.st(str, start, newstr, leng) {
  3475.     auto mover, newleng;
  3476.     extrn .mlr, .mrl;
  3477.  
  3478.     if (.nargs() < 4)
  3479.         leng = .lengt(str) - start;;
  3480. /*
  3481.  *    The #%*@ people at Waterloo got the conditions
  3482.  *    on the next line backwards!!!!!
  3483.  */
  3484.     mover = leng < (newleng = .lengt(newstr)) ? .mrl : .mlr;
  3485.     mover(str, start + newleng, str, start + leng,
  3486.           .lengt(str) - start - leng + 1);
  3487.     .mlr(str, start, newstr, 0, newleng);
  3488. }
  3489. /*  kermit/s/b_lib/rep.st.b    >> End <<  */
  3490.