home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archimedes / aracmd.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  31KB  |  1,090 lines

  1. /* -> c.ckacmd
  2.  */
  3.  
  4. char *cmdv = "Arthur cmd package (based on C-Kermit V1A(019)) 19 Dec 85";
  5.  
  6. /*  C K A C M D  --  Interactive command package for Arthur  */
  7. /*
  8.  Modelled after the DECSYSTEM-20 command parser (the COMND JSYS)
  9.  
  10.  Features:
  11.  . parses and verifies keywords, text strings, numbers, and other data
  12.  . displays appropriate menu or help message when user types "?"
  13.  . does keyword and filename completion when user types ESC
  14.  . accepts any unique abbreviation for a keyword
  15.  . allows keywords to have attributes, like "invisible"
  16.  . can supply defaults for fields omitted by user
  17.  . provides command line editing (character, word, and line deletion)
  18.  . accepts input from keyboard, command files, or redirected stdin
  19.  . allows for full or half duplex operation, character or line input
  20.  . settable prompt, protected from deletion
  21.  
  22.  Functions:
  23.   cmsetp - Set prompt (cmprom is prompt string, cmerrp is error msg prefix)
  24.   cmsavp - Save current prompt
  25.   prompt - Issue prompt 
  26.   cmini  - Clear the command buffer (before parsing a new command)
  27.   cmres  - Reset command buffer pointers (before reparsing)
  28.   cmkey  - Parse a keyword
  29.   cmnum  - Parse a number
  30.   cmifi  - Parse an input file name
  31.   cmofi  - Parse an output file name
  32.   cmfld  - Parse an arbitrary field
  33.   cmtxt  - Parse a text string
  34.   cmcfm  - Parse command confirmation (end of line)
  35.   stripq - Strip out backslash quotes from a string.
  36.  
  37.  Return codes:
  38.   -3: no input provided when required
  39.   -2: input was invalid
  40.   -1: reparse required (user deleted into a preceding field)
  41.    0 or greater: success
  42.   See individual functions for greater detail.
  43.  
  44.  Before using these routines, the caller should #include ckucmd.h, and
  45.  set the program's prompt by calling cmsetp().  If the file parsing
  46.  functions cmifi and cmofi are to be used, this module must be linked
  47.  with a ck?fio file system support module for the appropriate system,
  48.  e.g. ckufio for Unix.  If the caller puts the terminal in
  49.  character wakeup ("cbreak") mode with no echo, then these functions will
  50.  provide line editing -- character, word, and line deletion, as well as
  51.  keyword and filename completion upon ESC and help strings, keyword, or
  52.  file menus upon '?'.  If the caller puts the terminal into character
  53.  wakeup/noecho mode, care should be taken to restore it before exit from
  54.  or interruption of the program.  If the character wakeup mode is not
  55.  set, the system's own line editor may be used.
  56.  
  57.  Author: Frank da Cruz (SY.FDC@CU20B),
  58.  Columbia University Center for Computing Activities, January 1985.
  59.  Copyright (C) 1985, Trustees of Columbia University in the City of New York.
  60.  Permission is granted to any individual or institution to use, copy, or
  61.  redistribute this software so long as it is not sold for profit, provided
  62.  this copyright notice is retained. 
  63. */
  64.  
  65. #include "ckcdeb.h"         /* Formats for debug() */
  66. #include <stdio.h>          /* Standard C I/O package */
  67. #include <ctype.h>          /* Character types */
  68. #include "ckucmd.h"         /* Command parsing definitions */
  69. #ifdef ANSI
  70. #include <stdlib.h>
  71. #include <string.h>
  72. #include "ckafio.h"
  73. #include "ckatio.h"
  74. #include "ckamis.h"
  75. #endif
  76.  
  77. /* Local variables */
  78.  
  79. int psetf = 0,              /* Flag that prompt has been set */
  80.     cc = 0,             /* Character count */
  81.     dpx = 0;                /* Duplex (0 = full) */
  82.  
  83. int hw = HLPLW,             /* Help line width */
  84.     hc = HLPCW,             /* Help line column width */
  85.     hh,                 /* Current help column number */
  86.     hx;                 /* Current help line position */
  87.  
  88. #define PROML 60            /* Maximum length for prompt */
  89.  
  90. char cmprom[PROML+1];           /* Program's prompt */
  91. char *dfprom = "Command? ";     /* Default prompt */
  92.  
  93. char cmerrp[PROML+1];           /* Program's error message prefix */
  94.  
  95. int cmflgs;             /* Command flags */
  96.  
  97. char cmdbuf[CMDBL+4];           /* Command buffer */
  98. char hlpbuf[HLPBL+4];           /* Help string buffer */
  99. char atmbuf[ATMBL+4];           /* Atom buffer */
  100. char filbuf[ATMBL+4];           /* File name buffer */
  101.  
  102. /* Command buffer pointers */
  103.  
  104. static char *bp,            /* Current command buffer position */
  105.     *pp,                /* Start of current field */
  106.     *np;                /* Start of next field */
  107.  
  108. #ifndef ANSI
  109. long zchki();               /* From ck?fio.c. */
  110. #endif
  111.  
  112. /* eraseleft() { printf("\b \b"); } */  /* For systems other than Arthur */
  113.  
  114. /*  C M S E T P  --  Set the program prompt.  */
  115.  
  116. #ifdef ANSI
  117. void
  118. #endif
  119. cmsetp(s) char *s; {
  120. #ifdef ANSI
  121.     char *sx, *sy;
  122. #else
  123.     char *sx, *sy, *strncpy();
  124. #endif
  125.     psetf = 1;              /* Flag that prompt has been set. */
  126.     strncpy(cmprom,s,PROML - 1);    /* Copy the string. */
  127.     cmprom[PROML] = NUL;        /* Ensure null terminator. */
  128.     sx = cmprom; sy = cmerrp;       /* Also use as error message prefix. */
  129. #ifdef ANSI
  130.     while ( (*sy++ = *sx++) != '\0' ) ;         /* Copy. */
  131. #else
  132.     while (*sy++ = *sx++) ;         /* Copy. */
  133. #endif
  134.     sy -= 2; if (*sy == '>') *sy = NUL; /* Delete any final '>'. */
  135. }
  136.  
  137. /*  C M S A V P  --  Save a copy of the current prompt.  */
  138.  
  139. #ifdef ANSI
  140. void
  141. #endif
  142. cmsavp(s,n) int n; char s[]; {
  143. #ifndef ANSI
  144.     extern char *strncpy();                 /* +1   */
  145. #endif
  146.     strncpy(s,cmprom,n-1);
  147.     s[n] = NUL;
  148. }
  149.  
  150. /*  P R O M P T  --  Issue the program prompt.  */
  151.  
  152. #ifdef ANSI
  153. void
  154. #endif
  155. prompt() {
  156.     if (psetf == 0) cmsetp(dfprom); /* If no prompt set, set default. */
  157.     printf("\r%s",cmprom);      /* Print the prompt. */
  158. }
  159.  
  160.  
  161. /*  C M R E S  --  Reset pointers to beginning of command buffer.  */
  162.  
  163. #ifdef ANSI
  164. void
  165. #endif
  166. cmres() {  
  167.     cc = 0;             /* Reset character counter. */
  168.     pp = np = bp = cmdbuf;      /* Point to command buffer. */
  169.     cmflgs = -5;            /* Parse not yet started. */
  170. }
  171.  
  172.  
  173. /*  C M I N I  --  Clear the command and atom buffers, reset pointers.  */
  174.  
  175. /*
  176. The argument specifies who is to echo the user's typein --
  177.   1 means the cmd package echoes
  178.   0 somebody else (system, front end, terminal) echoes
  179. */
  180.  
  181. #ifdef ANSI
  182. void
  183. #endif
  184. cmini(d) int d; {
  185.     for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
  186.     *atmbuf = NUL;
  187.     dpx = d;
  188.     cmres();
  189. }
  190.  
  191. #ifdef ANSI
  192. void
  193. #endif
  194. stripq(s) char *s; {            /* Function to strip '\' quotes */
  195.     char *t;
  196.     while (*s) {
  197.     if (*s == '\\') {
  198.         for (t = s; *t != '\0'; t++) *t = *(t+1);
  199.     }
  200.     s++;
  201.     }
  202. }
  203.  
  204. /*  C M N U M  --  Parse a number in the indicated radix  */
  205.  
  206. /*  For now, only works for positive numbers in base 10.  */
  207.  
  208. /*
  209.  Returns
  210.    -3 if no input present when required,
  211.    -2 if user typed an illegal number,
  212.    -1 if reparse needed,
  213.     0 otherwise, with n set to number that was parsed
  214. */
  215.  
  216. #ifdef ANSI
  217. int
  218. #endif
  219. cmnum(xhlp,xdef,radix,n) char *xhlp, *xdef; int radix, *n; {
  220.     int x; char *s;
  221.  
  222.     if (radix != 10) {          /* Just do base 10 for now */
  223.     printf("cmnum: illegal radix - %d\n",radix);
  224.     return(-1);
  225.     }
  226.  
  227.     x = cmfld(xhlp,xdef,&s);
  228.     debug(F101,"cmnum: cmfld","",x);
  229.     if (x < 0) return(x);    /* Parse a field */
  230.  
  231.     if (digits(atmbuf)) {       /* Convert to number */
  232.     *n = atoi(atmbuf);
  233.     return(x);
  234.     } else {
  235.     printf("\n?not a number - %s\n",s);
  236.     return(-2); 
  237.     }
  238. }
  239.  
  240. /*  C M O F I  --  Parse the name of an output file  */
  241.  
  242. /*
  243.  Depends on the external function zchko(); if zchko() not available, use
  244.  cmfld() to parse output file names.
  245.  
  246.  Returns
  247.    -3 if no input present when required,
  248.    -2 if permission would be denied to create the file,
  249.    -1 if reparse needed,
  250.     0 or 1 otherwise, with xp pointing to name.
  251. */
  252.  
  253. #ifdef ANSI
  254. int
  255. #endif
  256. cmofi(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
  257.     int x; char *s;
  258.  
  259.     if (*xhlp == NUL) xhlp = "Output file";
  260.     *xp = "";
  261.  
  262.     if ((x = cmfld(xhlp,xdef,&s)) < 0) return(x);
  263.  
  264.     if (chkwld(s)) {
  265.     printf("\n?Wildcards not allowed - %s\n",s);
  266.     return(-2);
  267.     }
  268.     if (zchko(s) < 0) {
  269.     printf("\n?Write permission denied - %s\n",s);
  270.     return(-2);
  271.     } else {
  272.     *xp = s;
  273.     return(x);
  274.     }
  275. }
  276.  
  277. /*  C M I F I  --  Parse the name of an existing file  */
  278.  
  279. /*
  280.  This function depends on the external functions:
  281.    zchki()  - Check if input file exists and is readable.
  282.    zxpand() - Expand a wild file specification into a list.
  283.    znext()  - Return next file name from list.
  284.  If these functions aren't available, then use cmfld() to parse filenames.
  285. */
  286. /*
  287.  Returns
  288.    -4 EOF
  289.    -3 if no input present when required,
  290.    -2 if file does not exist or is not readable,
  291.    -1 if reparse needed,
  292.     0 or 1 otherwise, with:
  293.     xp pointing to name,
  294.         wild = 1 if name contains '*' or '?', 0 otherwise.
  295. */
  296.  
  297. #ifdef ANSI
  298. int
  299. #endif
  300. cmifi(xhlp,xdef,xp,wild) char *xhlp, *xdef, **xp; int *wild; {
  301.     int i, x, xc, y; char *sp;
  302.  
  303.     cc = xc = 0;            /* Initialize counts & pointers */
  304.     *xp = "";
  305.     if ((x = cmflgs) != 1) {        /* Already confirmed? */
  306.     x = getwd();            /* No, get a word */
  307.     } else {
  308.     cc = setatm(xdef);      /* If so, use default, if any. */
  309.     }
  310.     *xp = atmbuf;           /* Point to result. */
  311.     *wild = chkwld(*xp);
  312.  
  313.     while (1) {
  314.     xc += cc;           /* Count the characters. */
  315.     debug(F111,"cmifi: getwd",atmbuf,xc);
  316.         switch (x) {
  317.         case -4:            /* EOF */
  318.         case -2:            /* Out of space. */
  319.         case -1:            /* Reparse needed */
  320.             return(x);
  321.         case 0:         /* SP or NL */
  322.         case 1:
  323.             if (xc == 0) *xp = xdef;    /* If no input, return default. */
  324.         else *xp = atmbuf;
  325.         if (**xp == NUL) return(-3); /* If field empty, return -3. */
  326.         
  327.         /* If filespec is wild, see if there are any matches */
  328.  
  329.         *wild = chkwld(*xp);
  330.         debug(F101," *wild","",*wild);
  331.         if (*wild != 0) {
  332.             y = zxpand(*xp);
  333.             if (y == 0) {
  334.             printf("\n?No files match - %s\n",*xp);
  335.             return(-2);
  336.             } else if (y < 0) {
  337.             printf("\n?Too many files match - %s\n",*xp);
  338.             return(-2);
  339.             } else return(x);
  340.         }
  341.  
  342.         /* If not wild, see if it exists and is readable. */
  343.  
  344.         y = zchki(*xp);
  345.         if (y == -3) {
  346.             printf("\n?Read permission denied - %s\n",*xp);
  347.             return(-2);
  348.         } else if (y == -2) {
  349.             printf("\n?File not readable - %s\n",*xp);
  350.             return(-2);
  351.         } else if (y < 0) {
  352.             printf("\n?File not found - %s\n",*xp);
  353.             return(-2);
  354.         }
  355.         return(x);
  356.         case 2:         /* ESC */
  357.             if (xc == 0) {
  358.             if (*xdef != '\0') {
  359.             printf("%s ",xdef); /* If at beginning of field, */
  360.             addbuf(xdef);   /* supply default. */
  361.             cc = setatm(xdef);
  362.             } else {        /* No default */
  363.             beep();
  364.             }
  365.             break;
  366.         } 
  367. #ifdef ANSI
  368.         if ( (*wild = chkwld(*xp)) != 0 ) {  /* No completion if wild */
  369. #else
  370.         if (*wild = chkwld(*xp)) {  /* No completion if wild */
  371. #endif
  372.             beep();
  373.             break;
  374.         }
  375.         sp = atmbuf + cc;
  376.         *sp++ = '*';
  377.         *sp-- = '\0';
  378.         y = zxpand(atmbuf); /* Add * and expand list. */
  379.         *sp = '\0';     /* Remove *. */
  380.  
  381.         if (y == 0) {
  382.             printf("\n?No files match - %s\n",atmbuf);
  383.             return(-2);
  384.         } else if (y < 0) {
  385.             printf("\n?Too many files match - %s\n",atmbuf);
  386.             return(-2);
  387.         } else if (y > 1) { /* Not unique, just beep. */
  388.             beep();
  389.         } else {        /* Unique, complete it.  */
  390.             znext(filbuf);  /* Get whole name of file. */
  391.             sp = filbuf + cc;   /* Point past what user typed. */
  392.             printf("%s ",sp);   /* Complete the name. */
  393.             addbuf(sp);     /* Add the characters to cmdbuf. */
  394.             setatm(pp);     /* And to atmbuf. */
  395.             *xp = atmbuf;   /* Return pointer to atmbuf. */
  396.             return(cmflgs = 0);
  397.         }
  398.         break;
  399.         case 3:         /* Question mark */
  400.             if (*xhlp == NUL)
  401.                 printf(" Input file specification");
  402.         else
  403.             printf(" %s",xhlp);
  404.         if (xc > 0) {
  405.             sp = atmbuf + cc;   /* Insert * at end */
  406.             *sp++ = '*';
  407.             *sp-- = '\0';
  408.             y = zxpand(atmbuf);
  409.             *sp = '\0';
  410.             if (y == 0) {           
  411.             printf("\n?No files match - %s\n",atmbuf);
  412.             return(-2);
  413.             } else if (y < 0) {
  414.             printf("\n?Too many file match - %s\n",atmbuf);
  415.             return(-2);
  416.             } else {
  417.             printf(", one of the following:\n");
  418.             clrhlp();
  419.             for (i = 0; i < y; i++) {
  420.                 znext(filbuf);
  421.                 addhlp(filbuf);
  422.             }
  423.             dmphlp();
  424.             }
  425.         } else printf("\n");
  426.         printf("%s%s",cmprom,cmdbuf);
  427.         break;
  428.     }
  429.     x = getwd();
  430.     }
  431. }
  432.  
  433. /*  C H K W L D  --  Check for wildcard characters '*' or '?'  */
  434.  
  435. #ifdef ANSI
  436. int
  437. #endif
  438. chkwld(s) char *s; {
  439.  
  440.     for ( ; *s != '\0'; s++) {
  441.         if ((*s == '*') || (*s == '@'))
  442.         return(1);
  443.     }
  444.     return(0);
  445. }
  446.  
  447. /*  C M F L D  --  Parse an arbitrary field  */
  448. /*
  449.  Returns
  450.    -3 if no input present when required,
  451.    -2 if field too big for buffer,
  452.    -1 if reparse needed,
  453.     0 otherwise, xp pointing to string result.
  454. */
  455.  
  456. #ifdef ANSI
  457. int
  458. #endif
  459. cmfld(xhlp,xdef,xp) char *xhlp, *xdef, **xp; {
  460.     int x, xc;
  461.  
  462.     cc = xc = 0;            /* Initialize counts & pointers */
  463.     *xp = "";
  464.     if ((x = cmflgs) != 1) {        /* Already confirmed? */
  465.     x = getwd();            /* No, get a word */
  466.     } else {
  467.     cc = setatm(xdef);      /* If so, use default, if any. */
  468.     }
  469.     *xp = atmbuf;           /* Point to result. */
  470.  
  471.     while (1) {
  472.     xc += cc;           /* Count the characters. */
  473.     debug(F111,"cmfld: getwd",atmbuf,xc);
  474.     debug(F101," x","",x);
  475.         switch (x) {
  476.         case -4:            /* EOF */
  477.         case -2:            /* Out of space. */
  478.         case -1:            /* Reparse needed */
  479.             return(x);
  480.         case 0:         /* SP or NL */
  481.         case 1:
  482.             if (xc == 0) *xp = xdef;    /* If no input, return default. */
  483.         else *xp = atmbuf;
  484.         if (**xp == NUL) x = -3;    /* If field empty, return -3. */
  485.         return(x);
  486.         case 2:         /* ESC */
  487.             if (xc == 0) {
  488.             printf("%s ",xdef); /* If at beginning of field, */
  489.             addbuf(xdef);   /* supply default. */
  490.             cc = setatm(xdef);  /* Return as if whole field */
  491.             return(0);      /* typed, followed by space. */
  492.         } else {
  493.             beep(); /* Beep if already into field. */
  494.                 }           
  495.         break;
  496.         case 3:         /* Question mark */
  497.             if (*xhlp == NUL)
  498.             printf(" Please complete this field");
  499.         else
  500.                 printf(" %s",xhlp);
  501.         printf("\n%s%s",cmprom,cmdbuf);
  502.         break;
  503.     }
  504.     x = getwd();
  505.     }
  506. }
  507.  
  508. /*  C M T X T  --  Get a text string, including confirmation  */
  509.  
  510. /*
  511.   Print help message 'xhlp' if ? typed, supply default 'xdef' if null
  512.   string typed.  Returns
  513.  
  514.    -1 if reparse needed or buffer overflows.
  515.     1 otherwise.
  516.  
  517.   with cmflgs set to return code, and xp pointing to result string.
  518. */
  519.  
  520. #ifdef ANSI
  521. int
  522. #endif
  523. cmtxt(xhlp,xdef,xp) char *xhlp; char *xdef; char **xp; {
  524.  
  525.     int x;
  526.     static int xc;
  527.  
  528.     debug(F101,"cmtxt, cmflgs","",cmflgs);
  529.     cc = 0;             /* Start atmbuf counter off at 0 */
  530.     if (cmflgs == -1) {         /* If reparsing, */
  531.         xc = strlen(*xp);       /* get back the total text length, */
  532.     } else {                /* otherwise, */
  533.         *xp = "";           /* start fresh. */
  534.         xc = 0;
  535.     }
  536.     *atmbuf = NUL;          /* And empty atom buffer. */
  537.     if ((x = cmflgs) != 1) {
  538.     x = getwd();            /* Get first word. */
  539.     *xp = pp;           /* Save pointer to it. */
  540.     }
  541.     while (1) {
  542.     xc += cc;           /* Char count for all words. */
  543.     debug(F111,"cmtxt: getwd",atmbuf,xc);
  544.     debug(F101," x","",x);
  545.     switch (x) {
  546.         case -4:            /* EOF */
  547.         case -2:            /* Overflow */
  548.         case -1:            /* Deletion */
  549.             return(x);
  550.         case 0:         /* Space */
  551.             xc++;           /* Just count it */
  552.         break;
  553.         case 1:         /* CR or LF */
  554.             if (xc == 0) *xp = xdef;
  555.         return(x);
  556.         case 2:         /* ESC */
  557.             if (xc == 0) {
  558.             printf("%s ",xdef);
  559.             cc = addbuf(xdef);
  560.         } else {
  561.             beep();
  562.         }
  563.         break;
  564.         case 3:         /* Question Mark */
  565.             if (*xhlp == NUL)
  566.             printf(" Text string");
  567.         else
  568.             printf(" %s",xhlp);
  569.         printf("\n%s%s",cmprom,cmdbuf);
  570.         break;
  571.             default:
  572.             printf("\n?Unexpected return code from getwd() - %d\n",x);
  573.         return(-2);
  574.         }
  575.     x = getwd();
  576.     }
  577. }
  578.  
  579. /*  C M K E Y  --  Parse a keyword  */
  580.  
  581. /*
  582.  Call with:
  583.    table    --  keyword table, in 'struct keytab' format;
  584.    n        --  number of entries in table;
  585.    xhlp     --  pointer to help string;
  586.    xdef     --  pointer to default keyword;
  587.  
  588.  Returns:
  589.    -3       --  no input supplied and no default available
  590.    -2       --  input doesn't uniquely match a keyword in the table
  591.    -1       --  user deleted too much, command reparse required
  592.     n >= 0  --  value associated with keyword
  593. */
  594.  
  595. #ifdef ANSI
  596. int
  597. #endif
  598. cmkey(table,n,xhlp,xdef) struct keytab table[]; int n; char *xhlp, *xdef; {
  599.     int i, y, z, zz, xc;
  600.     char *xp;
  601.  
  602.     xc = cc = 0;            /* Clear character counters. */
  603.  
  604.     if ((zz = cmflgs) == 1)         /* Command already entered? */
  605.     setatm(xdef);
  606.     else zz = getwd(); 
  607.  
  608. debug(F101,"cmkey: table length","",n);
  609. debug(F101," cmflgs","",cmflgs);
  610. debug(F101," zz","",zz);
  611. while (1) {
  612.     xc += cc;
  613.     debug(F111,"cmkey: getwd",atmbuf,xc);
  614.  
  615.     switch(zz) {
  616.     case -4:            /* EOF */
  617.     case -2:            /* Buffer overflow */
  618.         case -1:            /* Or user did some deleting. */
  619.         return(zz);
  620.  
  621.     case 0:             /* User terminated word with space */
  622.     case 1:             /* or newline */
  623.         if (cc == 0) setatm(xdef);
  624.         y = lookup(table,atmbuf,n,&z);
  625.         switch (y) {
  626.         case -2:
  627.             printf("\n?Ambiguous - %s\n",atmbuf);
  628.             return(cmflgs = -2);
  629.         case -1:
  630.             printf("\n?Invalid - %s\n",atmbuf);
  631.             return(cmflgs = -2);
  632.         default:
  633.             break;
  634.         }
  635.         return(y);
  636.     case 2:             /* User terminated word with ESC */
  637.         if (cc == 0) {
  638.             if (*xdef != NUL) { /* Nothing in atmbuf */
  639.             printf("%s ",xdef); /* Supply default if any */
  640.             addbuf(xdef);
  641.             cc = setatm(xdef);
  642.             debug(F111,"cmkey: default",atmbuf,cc);
  643.         } else {
  644.             beep(); /* No default, just beep */
  645.             break;
  646.         }
  647.         }
  648.         y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */
  649.         debug(F111,"cmkey: esc",atmbuf,y);
  650.         if (y == -2) {
  651.         beep();
  652.         break;
  653.             }
  654.         if (y == -1) {
  655.         printf("\n?Invalid - %s\n",atmbuf);
  656.         return(cmflgs = -2);
  657.         }
  658.         xp = table[z].kwd + cc;
  659.             printf("%s ",xp);
  660.         addbuf(xp);
  661.         debug(F110,"cmkey: addbuf",cmdbuf,0);
  662.         return(y);
  663.     case 3:             /* User terminated word with "?" */
  664.         y = lookup(table,atmbuf,n,&z);
  665.         if (y > -1) {
  666.         printf(" %s\n%s%s",table[z].kwd,cmprom,cmdbuf);
  667.         break;
  668.         } else if (y == -1) {
  669.         printf("\n?Invalid\n");
  670.         return(cmflgs = -2);
  671.         }
  672.  
  673.         if (*xhlp == NUL)
  674.             printf(" One of the following:\n");
  675.         else
  676.             printf(" %s, one of the following:\n",xhlp);
  677.  
  678.         clrhlp();
  679.         for (i = 0; i < n; i++) {   
  680.         if (!strncmp(table[i].kwd,atmbuf,cc)
  681.                 && !test(table[i].flgs,CM_INV))
  682.             addhlp(table[i].kwd);
  683.         }
  684.         dmphlp();
  685.         printf("%s%s", cmprom, cmdbuf);
  686.         break;
  687.  
  688.         default:        
  689.         printf("\n%d - Unexpected return code from getwd\n",zz);
  690.         return(cmflgs = -2);
  691.         }
  692.     zz = getwd();
  693.     }
  694. }
  695.  
  696. /*  C M C F M  --  Parse command confirmation (end of line)  */
  697.  
  698. /*
  699.  Returns
  700.    -2: User typed anything but whitespace or newline
  701.    -1: Reparse needed
  702.     0: Confirmation was received
  703. */
  704.  
  705. #ifdef ANSI
  706. int
  707. #endif
  708. cmcfm() {
  709.     int x, xc;
  710.  
  711.     debug(F101,"cmcfm: cmflgs","",cmflgs);
  712.  
  713.     xc = cc = 0;
  714.     if (cmflgs == 1) return(0);
  715.  
  716.     while (1) {
  717.     x = getwd();
  718.     xc += cc;
  719.     debug(F111,"cmcfm: getwd",atmbuf,xc);
  720.         switch (x) {
  721.         case -4:            /* EOF */
  722.         case -2:
  723.         case -1:
  724.         return(x);
  725.  
  726.         case 0:         /* Space */
  727.             continue;
  728.         case 1:         /* End of line */
  729.             if (xc > 0) {
  730.             printf("?Not confirmed - %s\n",atmbuf);
  731.             return(-2);
  732.                 } else return(0);           
  733.         case 2:
  734.             beep();
  735.         continue;
  736.  
  737.             case 3:
  738.             if (xc > 0) {
  739.             printf("\n?Not confirmed - %s\n",atmbuf);
  740.             return(-2);
  741.         }
  742.             printf("\n Type a carriage return to confirm the command\n");
  743.         printf("%s%s",cmprom,cmdbuf);
  744.         continue;
  745.     }
  746.     }
  747. }
  748.  
  749. /* Keyword help routines */
  750.  
  751. /*  C L R H L P -- Initialize/Clear the help line buffer  */
  752.  
  753. #ifdef ANSI
  754. void
  755. #endif
  756. clrhlp() {              /* Clear the help buffer */
  757.     hlpbuf[0] = NUL;
  758.     hh = hx = 0;
  759. }
  760.  
  761.  
  762. /*  A D D H L P  --  Add a string to the help line buffer  */
  763.  
  764. #ifdef ANSI
  765. void
  766. #endif
  767. addhlp(s) char *s; {            /* Add a word to the help buffer */
  768.     int j;
  769.  
  770.     hh++;               /* Count this column */
  771.  
  772.     for (j = 0; j < hc; j++) {      /* Fill the column */
  773.     if (*s != NUL)          /* First with chars from the string */
  774.         hlpbuf[hx++] = *s++;
  775.     else {
  776.         if (hh < (hw / hc))     /* Then with spaces */
  777.             hlpbuf[hx++] = SP;
  778.         else {
  779.         hlpbuf[hx++] = NUL; /* If last column, no spaces. */
  780.         dmphlp();       /* Print it. */
  781.         return;
  782.         }
  783.         } 
  784.     }
  785.     if (*s != NUL)          /* Still some chars left in string? */
  786.     hlpbuf[hx-1] = '+';     /* Mark as too long for column. */
  787. }
  788.  
  789.  
  790. /*  D M P H L P  --  Dump the help line buffer  */
  791.  
  792. #ifdef ANSI
  793. void
  794. #endif
  795. dmphlp() {              /* Print the help buffer */
  796.     hlpbuf[hx++] = NUL;
  797.     printf(" %s\n",hlpbuf);
  798.     clrhlp();
  799. }
  800.  
  801.  
  802. /*  L O O K U P  --  Lookup the string in the given array of strings  */
  803.  
  804. /*
  805.  Call this way:  v = lookup(table,word,n,&x);
  806.  
  807.    table - a 'struct keytab' table.
  808.    word  - the target string to look up in the table.
  809.    n     - the number of elements in the table.
  810.    x     - address of an integer for returning the table array index.
  811.  
  812.  The keyword table must be arranged in ascending alphabetical order, and
  813.  all letters must be lowercase.
  814.  
  815.  Returns the keyword's associated value ( zero or greater ) if found,
  816.  with the variable x set to the array index, or:
  817.  
  818.   -3 if nothing to look up (target was null),
  819.   -2 if ambiguous,
  820.   -1 if not found.
  821.  
  822.  A match is successful if the target matches a keyword exactly, or if
  823.  the target is a prefix of exactly one keyword.  It is ambiguous if the
  824.  target matches two or more keywords from the table.
  825. */
  826.  
  827. #ifdef ANSI
  828. int
  829. #endif
  830. lookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
  831.  
  832.     int i, v, cmdlen;
  833.  
  834. /* Lowercase & get length of target, if it's null return code -3. */
  835.  
  836.     if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
  837.  
  838. /* Not null, look it up */
  839.  
  840.     for (i = 0; i < n-1; i++) {
  841. #ifdef ANSI
  842.     if ( !strcmp(table[i].kwd,cmd) ||
  843.            ( ((v = !strncmp(table[i].kwd,cmd,cmdlen))!=0) &&
  844.              (strncmp(table[i+1].kwd,cmd,cmdlen)!=0) )) {
  845. #else
  846.     if (!strcmp(table[i].kwd,cmd) ||
  847.            ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
  848.              strncmp(table[i+1].kwd,cmd,cmdlen))) {
  849. #endif
  850.         *x = i;
  851.         return(table[i].val);
  852.          }
  853.     if (v) return(-2);
  854.     }   
  855.  
  856. /* Last (or only) element */
  857.  
  858.     if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
  859.     *x = n-1;
  860.     return(table[n-1].val);
  861.     } else return(-1);
  862. }
  863.  
  864. /*  G E T W D  --  Gets a "word" from the command input stream  */
  865.  
  866. /*
  867. Usage: retcode = getwd();
  868.  
  869. Returns:
  870.  -4 if end of file (e.g. pipe broken)
  871.  -2 if command buffer overflows
  872.  -1 if user did some deleting
  873.   0 if word terminates with SP or tab
  874.   1 if ... CR
  875.   2 if ... ESC
  876.   3 if ... ?
  877.  
  878. With:
  879.   pp pointing to beginning of word in buffer
  880.   bp pointing to after current position
  881.   atmbuf containing a copy of the word
  882.   cc containing the number of characters in the word copied to atmbuf
  883. */
  884.  
  885. #ifdef ANSI
  886. int
  887. #endif
  888. getwd() {
  889.  
  890.     int c;              /* Current char */
  891.     static int inword = 0;      /* Flag for start of word found */
  892.     int quote = 0;          /* Flag for quote character */
  893.     int echof = 0;          /* Flag for whether to echo */
  894.     int ignore = 0;
  895.  
  896.     pp = np;                /* Start of current field */
  897.     debug(F101,"getwd: cmdbuf","",(int) cmdbuf);
  898.     debug(F101," bp","",(int) bp);
  899.     debug(F101," pp","",(int) pp);
  900.     debug(F110," cmdbuf",cmdbuf,0);
  901.  
  902.     while (bp < cmdbuf+CMDBL) {     /* Loop */
  903.  
  904.     ignore = echof = 0;     /* Flag for whether to echo */
  905.  
  906.     if ((c = *bp) == NUL) {     /* Get next character */
  907.         if (dpx) echof = 1;     /* from reparse buffer */
  908. /*old       c = getchar();*/        /* or from tty. */
  909. /*new*/     c = conin();
  910.         if ((c == EOF) || (c == 4)) return(-4);
  911.     } else ignore = 1;
  912.  
  913.     if (quote == 0) {
  914.  
  915.         if (!ignore && (c == '\\')) { /* Quote character */
  916.            quote = 1;
  917.            continue;
  918.             }
  919.         if (c == FF) {      /* Formfeed. */
  920.             c = NL;         /* Replace with newline */
  921.             system("set vdu -mode 0");  /* and clear the screen. */
  922.         }
  923.  
  924. /*      if (c == HT) c = SP;*/  /* Substitute space for tab. */
  925.             if (c == SP) {      /* If space */
  926.         *bp++ = c;      /* deposit it in buffer. */
  927.         if (echof) putchar(c);  /* echo it. */
  928.         if (inword == 0) {  /* If leading, gobble it. */
  929.             pp++;
  930.             continue;
  931.         } else {        /* If terminating, return. */
  932.             np = bp;
  933.             setatm(pp);
  934.             inword = 0;
  935.             return(cmflgs = 0);
  936.         }
  937.         }
  938.         if (c == NL || c == CR) {   /* CR, LF */
  939.         *bp = NUL;      /* End the string */
  940.         if (echof) {        /* If echoing, */
  941.             putchar(c);     /* echo the typein */
  942.             if (c == CR) putchar(NL); /* Arthur */
  943.         }
  944.         np = bp;        /* Where to start next field. */
  945.         setatm(pp);     /* Copy this field to atom buffer. */
  946.         inword = 0;
  947.         return(cmflgs = 1);
  948.         }
  949.         if (!ignore && (c == '?')) { /* Question mark */
  950.         putchar(c);
  951.         *bp = NUL;
  952.         setatm(pp);
  953.         return(cmflgs = 3);
  954.             }
  955.         if (c == HT/*ESC*/) {       /* ESC */
  956.         *bp = NUL;
  957.         setatm(pp);
  958.         return(cmflgs = 2);
  959.         }
  960.         if (c == BS || c == RUB) {  /* Character deletion */
  961.         if (bp > cmdbuf) {  /* If still in buffer... */
  962.                     eraseleft();        /* erase character from screen, */
  963.             bp--;       /* point behind it, */
  964.             if (*bp == SP) inword = 0; /* Flag if current field gone */
  965.             *bp = NUL;      /* Erase character from buffer. */
  966.         } else {        /* Otherwise, */
  967.             beep(); /* beep, */
  968.             cmres();        /* and start parsing a new command. */
  969.         }
  970.         if (pp < bp) continue;
  971.         else return(cmflgs = -1);
  972.         }
  973.         if (c == LDEL) {        /* ^U, line deletion */
  974.             while ((bp--) > cmdbuf) {
  975.                     eraseleft();     
  976.             *bp = NUL;
  977.         }
  978.         cmres();        /* Restart the command. */
  979.         inword = 0;
  980.         return(cmflgs = -1);
  981.         }
  982.             if (c == WDEL) {        /* ^W, word deletion */
  983.             if (bp <= cmdbuf) { /* Beep if nothing to delete */
  984.             beep();
  985.             cmres();
  986.             return(cmflgs = -1);
  987.         }
  988.         bp--;
  989.         for ( ; (bp >= cmdbuf) && (*bp == SP) ; bp--) {
  990.                     eraseleft();
  991.             *bp = NUL;
  992.         }
  993.         for ( ; (bp >= cmdbuf) && (*bp != SP) ; bp--) {
  994.                     eraseleft();
  995.             *bp = NUL;
  996.         }
  997.         bp++;
  998.         inword = 0;
  999.         return(cmflgs = -1);
  1000.         }
  1001.         if (c == RDIS) {        /* ^R, redisplay */
  1002.         *bp = NUL;
  1003.         printf("\n%s%s",cmprom,cmdbuf);
  1004.         continue;
  1005.         }
  1006.         }
  1007.     if (echof) putchar(c);      /* If tty input, echo. */
  1008.     inword = 1;         /* Flag we're in a word. */
  1009.     if (quote == 0 || c != NL) *bp++ = c;   /* And deposit it. */
  1010.     quote = 0;          /* Turn off quote. */
  1011.     }                   /* end of big while */
  1012.     beep();                 /* Get here if... */
  1013.     printf("\n?Buffer full\n");
  1014.     return(cmflgs = -2);
  1015. }
  1016.  
  1017. /* Utility functions */
  1018.  
  1019. /* A D D B U F  -- Add the string pointed to by cp to the command buffer  */
  1020.  
  1021. #ifdef ANSI
  1022. int
  1023. #endif
  1024. addbuf(cp) char *cp; {
  1025.     int len = 0;
  1026.     while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) {
  1027.         *bp++ = *cp++;          /* Copy and */
  1028.         len++;              /* count the characters. */
  1029.     }   
  1030.     *bp++ = SP;             /* Put a space at the end */
  1031.     *bp = NUL;              /* Terminate with a null */
  1032.     np = bp;                /* Update the next-field pointer */
  1033.     return(len);            /* Return the length */
  1034. }
  1035.  
  1036. /*  S E T A T M  --  Deposit a string in the atom buffer  */
  1037.  
  1038. #ifdef ANSI
  1039. int
  1040. #endif
  1041. setatm(cp) char *cp; {
  1042.     char *ap;
  1043.     cc = 0;
  1044.     ap = atmbuf;
  1045.     *ap = NUL;
  1046.     while (*cp == SP) cp++;
  1047.     while ((*cp != SP) && (*cp != NL) && (*cp != NUL) && (*cp != CR)) {
  1048.     *ap++ = *cp++;
  1049.     cc++;
  1050.     }
  1051.     *ap++ = NUL;
  1052.     return(cc);             /* Return length */
  1053. }
  1054.  
  1055. /*  D I G I T S  -- Verify that all the characters in line are digits  */
  1056.  
  1057. #ifdef ANSI
  1058. int
  1059. #endif
  1060. digits(s) char *s; {
  1061.     while (*s) {
  1062.         if (!isdigit(*s)) return(0);
  1063.         s++;
  1064.     }
  1065.     return(1);
  1066. }
  1067.  
  1068. /*  L O W E R  --  Lowercase a string  */
  1069.  
  1070. #ifdef ANSI
  1071. int
  1072. #endif
  1073. lower(s) char *s; {
  1074.     int n = 0;
  1075.     while (*s) {
  1076.     if (isupper(*s)) *s = tolower(*s);
  1077.     s++, n++;
  1078.     }
  1079.     return(n);
  1080. }
  1081.  
  1082. /*  T E S T  --  Bit test  */
  1083.  
  1084. #ifdef ANSI
  1085. int
  1086. #endif
  1087. test(x,m) int x, m; { /*  Returns 1 if any bits from m are on in x, else 0  */
  1088.     return((x & m) ? 1 : 0);
  1089. }
  1090.