BSD 4_4_Lite1 development
authorCSRG <csrg@ucbvax.Berkeley.EDU>
Wed, 25 Nov 1992 05:30:30 +0000 (21:30 -0800)
committerCSRG <csrg@ucbvax.Berkeley.EDU>
Wed, 25 Nov 1992 05:30:30 +0000 (21:30 -0800)
Work on file usr/src/contrib/kermit-5A.188/ckucmd.c
Work on file usr/src/contrib/kermit-5A.188/ckucmd.h

Synthesized-from: CSRG/cd2/4.4BSD-Lite1

usr/src/contrib/kermit-5A.188/ckucmd.c [new file with mode: 0644]
usr/src/contrib/kermit-5A.188/ckucmd.h [new file with mode: 0644]

diff --git a/usr/src/contrib/kermit-5A.188/ckucmd.c b/usr/src/contrib/kermit-5A.188/ckucmd.c
new file mode 100644 (file)
index 0000000..3117a34
--- /dev/null
@@ -0,0 +1,2222 @@
+int cmdmsk = 127; /* Command-terminal-to-C-Kermit character mask */
+
+#include "ckcdeb.h"                     /* Formats for debug(), etc. */
+_PROTOTYP( int unhex, (char) );
+
+#ifndef NOICP     /* The rest only if interactive command parsing selected */
+
+char *cmdv = "Command package 5A(053), 21 Nov 92";
+/*  C K U C M D  --  Interactive command package for Unix  */
+
+/*
+  Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
+  Columbia University Center for Computing Activities.
+  First released January 1985.
+  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
+  York.  Permission is granted to any individual or institution to use this
+  software as long as it is not sold for profit.  This copyright notice must be
+  retained.  This software may not be included in commercial products without
+  written permission of Columbia University.
+*/
+
+/*
+Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP.
+Features:
+. parses and verifies keywords, filenames, text strings, numbers, other data
+. displays appropriate menu or help message when user types "?"
+. does keyword and filename completion when user types ESC or TAB
+. does partial filename completion
+. accepts any unique abbreviation for a keyword
+. allows keywords to have attributes, like "invisible" and "abbreviation"
+. can supply defaults for fields omitted by user
+. provides command line editing (character, word, and line deletion)
+. accepts input from keyboard, command files, or redirected stdin
+. allows for full or half duplex operation, character or line input
+. settable prompt, protected from deletion
+
+Functions:
+ cmsetp - Set prompt (cmprom is prompt string)
+ cmsavp - Save current prompt
+ prompt - Issue prompt
+ cmini - Clear the command buffer (before parsing a new command)
+ cmres - Reset command buffer pointers (before reparsing)
+ cmkey - Parse a keyword
+ cmnum - Parse a number
+ cmifi - Parse an input file name
+ cmofi - Parse an output file name
+ cmdir - Parse a directory name (UNIX only)
+ cmfld - Parse an arbitrary field
+ cmtxt - Parse a text string
+ cmcfm - Parse command confirmation (end of line)
+
+Return codes:
+ -3: no input provided when required
+ -2: input was invalid (e.g. not a number when a number was required)
+ -1: reparse required (user deleted into a preceding field)
+  0 or greater: success
+See individual functions for greater detail.
+
+Before using these routines, the caller should #include ckucmd.h, and set the
+program's prompt by calling cmsetp().  If the file parsing functions cmifi,
+cmofi, or cmdir are to be used, this module must be linked with a ck?fio file
+system support module for the appropriate system, e.g. ckufio for Unix.  If
+the caller puts the terminal in character wakeup ("cbreak") mode with no echo,
+then these functions will provide line editing -- character, word, and line
+deletion, as well as keyword and filename completion upon ESC and help
+strings, keyword, or file menus upon '?'.  If the caller puts the terminal
+into character wakeup/noecho mode, care should be taken to restore it before
+exit from or interruption of the program.  If the character wakeup mode is not
+set, the system's own line editor may be used.
+
+NOTE: Contrary to expectations, many #ifdef's have been added to this module.
+Any operation requiring an #ifdef (like clear screen, get character from
+keyboard, erase character from screen, etc) should eventually be turned into a
+call to a function that is defined in ck?tio.c, but then all the ck?tio.c
+modules would have to be changed...
+*/
+\f
+/* Includes */
+
+#include "ckcker.h"                    /* (===OS2 addition===) */
+#include "ckcasc.h"                    /* ASCII character symbols */
+#include "ckucmd.h"                     /* Command parsing definitions */
+#include <errno.h>                     /* Error number symbols */
+
+#ifdef OSK
+#define cc ccount                      /* OS-9/68K compiler bug */
+#endif /* OSK */
+
+#ifdef GEMDOS                          /* Atari ST */
+#ifdef putchar
+#undef putchar
+#endif /* putchar */
+#define putchar(x) conoc(x)            /* Why doesn't everyone do this? */
+#endif /* GEMDOS */
+
+/* Local variables */
+static
+int psetf = 0,                          /* Flag that prompt has been set */
+    cc = 0,                             /* Character count */
+    dpx = 0,                            /* Duplex (0 = full) */
+    inword = 0;                                /* In the middle of getting a word */
+static
+int hw = HLPLW,                         /* Help line width */
+    hc = HLPCW,                         /* Help line column width */
+    hh,                                 /* Current help column number */
+    hx;                                 /* Current help line position */
+#define PROML 160                       /* Maximum length for prompt */
+char cmprom[PROML+1];                   /* Program's prompt */
+char cmprxx[PROML+1];                   /* Program's prompt, unevaluated */
+char *dfprom = "Command? ";             /* Default prompt */
+int cmflgs;                             /* Command flags */
+int cmfsav;                            /* A saved version of them */
+#ifdef DCMDBUF
+char *cmdbuf;                          /* Command buffer */                
+char *savbuf;                          /* Help string buffer */            
+char *hlpbuf;                          /* Atom buffer */                   
+char *atmbuf;                          /* File name buffer */              
+char *atxbuf;                          /* For expanding the atom buffer */ 
+int atxn;                              /* Length of expansion buffer */    
+char *atybuf;                          /* For copying atom buffer */       
+char *filbuf;                          /* Buffer to save copy of command */
+#else
+char cmdbuf[CMDBL+4];                   /* Command buffer */                
+char hlpbuf[HLPBL+4];                   /* Help string buffer */            
+char atmbuf[ATMBL+4];                   /* Atom buffer */                   
+char filbuf[ATMBL+4];                   /* File name buffer */              
+char atxbuf[CMDBL+4];                   /* For expanding the atom buffer */ 
+int atxn;                              /* Length of expansion buffer */    
+char atybuf[ATMBL+4];                   /* For copying atom buffer */       
+char savbuf[CMDBL+4];                   /* Buffer to save copy of command */
+#endif /* DCMDBUF */
+/* Command buffer pointers */
+static char *bp,                        /* Current command buffer position */
+    *pp,                                /* Start of current field */
+    *np;                                /* Start of next field */
+static int ungw;                       /* For ungetting words */
+
+_PROTOTYP( VOID addhlp, (char *) );
+_PROTOTYP( VOID clrhlp, (void) );
+_PROTOTYP( VOID dmphlp, (void) );
+_PROTOTYP( int gtword, (void) );
+_PROTOTYP( int addbuf, (char *) );
+_PROTOTYP( int setatm, (char *) );
+_PROTOTYP( int cmdgetc, (void) );
+_PROTOTYP( VOID cmdnewl, (char) );
+_PROTOTYP( VOID cmdchardel, (void) );
+_PROTOTYP( VOID cmdecho, (char, int) );
+_PROTOTYP( static int test, (int, int) );
+#ifdef GEMDOS
+_PROTOTYP( extern char *strchr, (char *, int) );
+#endif /* GEMDOS */
+
+/*  T E S T  --  Bit test  */
+static int
+test(x,m) int x, m; { /*  Returns 1 if any bits from m are on in x, else 0  */
+    return((x & m) ? 1 : 0);
+}
+\f
+/*  C M S E T U P  --  Set up command buffers  */
+
+#ifdef DCMDBUF
+int
+cmsetup() {
+    if (!(cmdbuf = malloc(CMDBL + 4))) return(-1);
+    if (!(savbuf = malloc(CMDBL + 4))) return(-1);
+    savbuf[0] = '\0';
+    if (!(hlpbuf = malloc(HLPBL + 4))) return(-1);
+    if (!(atmbuf = malloc(ATMBL + 4))) return(-1);
+    if (!(atxbuf = malloc(CMDBL + 4))) return(-1);
+    if (!(atybuf = malloc(ATMBL + 4))) return(-1);
+    if (!(filbuf = malloc(ATMBL + 4))) return(-1);
+    return(0);
+}
+#endif /* DCMDBUF */ 
+
+/*  C M S E T P  --  Set the program prompt.  */
+VOID
+cmsetp(s) char *s; {
+    strncpy(cmprxx,s,PROML - 1);
+    cmprxx[PROML] = NUL;
+    psetf = 1;                          /* Flag that prompt has been set. */
+}
+/*  C M S A V P  --  Save a copy of the current prompt.  */
+VOID
+#ifdef CK_ANSIC
+cmsavp(char s[], int n)
+#else
+cmsavp(s,n) char s[]; int n;
+#endif /* CK_ANSIC */
+/* cmsavp */ {
+    strncpy(s,cmprxx,n-1);
+    s[n-1] = NUL;
+}
+/*  P R O M P T  --  Issue the program prompt.  */
+VOID
+prompt(f) xx_strp f; {
+    char *sx, *sy; int n;
+
+    if (psetf == 0) cmsetp(dfprom);     /* If no prompt set, set default. */
+
+    sx = cmprxx;                       /* Unevaluated copy */
+    if (f) {                           /* If conversion function given */
+       sy = cmprom;                    /* Evaluate it */
+       n = PROML;
+       if ((*f)(sx,&sy,&n) < 0)        /* If evaluation failed */
+         sx = cmprxx;                  /* revert to unevaluated copy */
+       else
+         sx = cmprom;
+    }
+#ifdef OSK
+    fputs(sx, stdout);
+#else
+#ifdef MAC
+    printf("%s", sx);
+#else
+    printf("\r%s",sx);                 /* Print the prompt. */
+    fflush(stdout);                    /* Now! */
+#endif /* MAC */
+#endif /* OSK */
+}
+#ifndef NOSPL
+VOID
+pushcmd() {                            /* For use with IF command. */
+    strcpy(savbuf,np);                 /* Save the dependent clause,  */
+    cmres();                           /* and clear the command buffer. */
+    debug(F110, "pushcmd: savbuf:", savbuf, 0);
+}
+#endif /* NOSPL */
+
+#ifdef COMMENT
+/* no longer used... */
+VOID
+popcmd() {
+    strcpy(cmdbuf,savbuf);             /* Put back the saved material */
+    *savbuf = '\0';                    /* and clear the save buffer */
+    cmres();
+}
+#endif /* COMMENT */
+
+/*  C M R E S  --  Reset pointers to beginning of command buffer.  */
+VOID
+cmres() {  
+    inword = cc = 0;                   /* Reset character counter. */
+    pp = np = bp = cmdbuf;              /* Point to command buffer. */
+    cmflgs = -5;                        /* Parse not yet started. */
+    ungw = 0;                          /* Don't need to unget a word. */
+}
+/*  C M I N I  --  Clear the command and atom buffers, reset pointers.  */
+/*
+The argument specifies who is to echo the user's typein --
+  1 means the cmd package echoes
+  0 somebody else (system, front end, terminal) echoes
+*/
+VOID
+cmini(d) int d; {
+    for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
+    *atmbuf = NUL;
+    dpx = d;
+    cmres();
+}
+#ifndef NOSPL
+/* The following bits are to allow the command package to call itself */
+/* in the middle of a parse.  To do this, begin by calling cmpush, and */
+/* end by calling cmpop. */
+
+#ifdef DCMDBUF
+struct cmp {
+    int i[5];                          /* stack for integers */
+    char *c[3];                                /* stack for pointers */
+    char *b[8];                                /* stack for buffer contents */
+};
+struct cmp *cmp = 0;
+#else
+int cmp_i[CMDDEP+1][5];                        /* Stack for integers */
+char *cmp_c[CMDDEP+1][5];              /* for misc pointers */
+char *cmp_b[CMDDEP+1][7];              /* for buffer contents pointers */
+#endif /* DCMDBUF */
+
+int cmddep = -1;                       /* Current stack depth */
+
+int
+cmpush() {                             /* Save the command environment */
+    char *cp;                          /* Character pointer */
+
+    if (cmddep >= CMDDEP)              /* Enter a new command depth */
+      return(-1);
+    cmddep++;
+    debug(F101,"&cmpush","",cmddep);
+
+#ifdef DCMDBUF
+    /* allocate memory for cmp if not already done */
+    if (!cmp && !(cmp = (struct cmp *) malloc(sizeof(struct cmp)*(CMDDEP+1))))
+      fatal("cmpush: no memory for cmp");
+    cmp[cmddep].i[0] = cmflgs;         /* First do the global ints */
+    cmp[cmddep].i[1] = cmfsav;
+    cmp[cmddep].i[2] = atxn;
+    cmp[cmddep].i[3] = ungw;
+
+    cmp[cmddep].c[0] = bp;             /* Then the global pointers */
+    cmp[cmddep].c[1] = pp;
+    cmp[cmddep].c[2] = np; 
+#else
+    cmp_i[cmddep][0] = cmflgs;         /* First do the global ints */
+    cmp_i[cmddep][1] = cmfsav;
+    cmp_i[cmddep][2] = atxn;
+    cmp_i[cmddep][3] = ungw;
+
+    cmp_c[cmddep][0] = bp;             /* Then the global pointers */
+    cmp_c[cmddep][1] = pp;
+    cmp_c[cmddep][2] = np; 
+#endif /* DCMDBUF */
+
+    /* Now the buffers themselves.  A lot of repititious code... */
+
+#ifdef DCMDBUF
+    cp = malloc((int)strlen(cmdbuf)+1);        /* 0: Command buffer */
+    if (cp) strcpy(cp,cmdbuf);
+    cmp[cmddep].b[0] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(savbuf)+1);        /* 1: Save buffer */
+    if (cp) strcpy(cp,savbuf);
+    cmp[cmddep].b[1] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(hlpbuf)+1);        /* 2: Help string buffer */
+    if (cp) strcpy(cp,hlpbuf);
+    cmp[cmddep].b[2] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atmbuf)+1);        /* 3: Atom buffer */
+    if (cp) strcpy(cp,atmbuf);
+    cmp[cmddep].b[3] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atxbuf)+1);        /* 4: Expansion buffer */
+    if (cp) strcpy(cp,atxbuf);
+    cmp[cmddep].b[4] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atybuf)+1);        /* 5: Atom buffer copy */
+    if (cp) strcpy(cp,atybuf);
+    cmp[cmddep].b[5] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(filbuf)+1);        /* 6: File name buffer */
+    if (cp) strcpy(cp,filbuf);
+    cmp[cmddep].b[6] = cp;
+    if (cp == NULL) return(-1);
+#else
+    cp = malloc((int)strlen(cmdbuf)+1);        /* 0: Command buffer */
+    if (cp) strcpy(cp,cmdbuf);
+    cmp_b[cmddep][0] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(savbuf)+1);        /* 1: Save buffer */
+    if (cp) strcpy(cp,savbuf);
+    cmp_b[cmddep][1] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(hlpbuf)+1);        /* 2: Help string buffer */
+    if (cp) strcpy(cp,hlpbuf);
+    cmp_b[cmddep][2] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atmbuf)+1);        /* 3: Atom buffer */
+    if (cp) strcpy(cp,atmbuf);
+    cmp_b[cmddep][3] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atxbuf)+1);        /* 4: Expansion buffer */
+    if (cp) strcpy(cp,atxbuf);
+    cmp_b[cmddep][4] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(atybuf)+1);        /* 5: Atom buffer copy */
+    if (cp) strcpy(cp,atybuf);
+    cmp_b[cmddep][5] = cp;
+    if (cp == NULL) return(-1);
+
+    cp = malloc((int)strlen(filbuf)+1);        /* 6: File name buffer */
+    if (cp) strcpy(cp,filbuf);
+    cmp_b[cmddep][6] = cp;
+    if (cp == NULL) return(-1);
+#endif /* DCMDBUF */
+
+    cmini(dpx);                                /* Initize the command parser */
+    return(0);
+}
+
+int
+cmpop() {                              /* Restore the command environment */
+    debug(F101,"&cmpop","",cmddep);
+    if (cmddep < 0) return(-1);                /* Don't pop too much! */
+
+#ifdef DCMDBUF
+    cmflgs = cmp[cmddep].i[0];         /* First do the global ints */
+    cmfsav = cmp[cmddep].i[1];
+    atxn = cmp[cmddep].i[2];
+    ungw = cmp[cmddep].i[3];
+
+    bp = cmp[cmddep].c[0];             /* Then the global pointers */
+    pp = cmp[cmddep].c[1];
+    np = cmp[cmddep].c[2]; 
+#else
+    cmflgs = cmp_i[cmddep][0];         /* First do the global ints */
+    cmfsav = cmp_i[cmddep][1];
+    atxn = cmp_i[cmddep][2];
+    ungw = cmp_i[cmddep][3];
+
+    bp = cmp_c[cmddep][0];             /* Then the global pointers */
+    pp = cmp_c[cmddep][1];
+    np = cmp_c[cmddep][2]; 
+#endif /* DCMDBUF */
+
+    /* Now the buffers themselves. */
+
+#ifdef DCMDBUF
+    if (cmp[cmddep].b[0]) {
+       strcpy(cmdbuf,cmp[cmddep].b[0]); /* 0: Command buffer */
+       free(cmp[cmddep].b[0]);
+       cmp[cmddep].b[0] = NULL;
+    }
+    if (cmp[cmddep].b[1]) {
+       strcpy(savbuf,cmp[cmddep].b[1]); /* 1: Save buffer */
+       free(cmp[cmddep].b[1]);
+       cmp[cmddep].b[1] = NULL;
+    }
+    if (cmp[cmddep].b[2]) {
+       strcpy(hlpbuf,cmp[cmddep].b[2]); /* 2: Help buffer */
+       free(cmp[cmddep].b[2]);
+       cmp[cmddep].b[2] = NULL;
+    }
+    if (cmp[cmddep].b[3]) {
+       strcpy(atmbuf,cmp[cmddep].b[3]); /* 3: Atomic buffer! */
+       free(cmp[cmddep].b[3]);
+       cmp[cmddep].b[3] = NULL;
+    }
+    if (cmp[cmddep].b[4]) {
+       strcpy(atxbuf,cmp[cmddep].b[4]); /* 4: eXpansion buffer */
+       free(cmp[cmddep].b[4]);
+       cmp[cmddep].b[4] = NULL;
+    }
+    if (cmp[cmddep].b[5]) {
+       strcpy(atybuf,cmp[cmddep].b[5]); /* 5: Atom buffer copY */
+       free(cmp[cmddep].b[5]);
+       cmp[cmddep].b[5] = NULL;
+    }
+    if (cmp[cmddep].b[6]) {
+       strcpy(filbuf,cmp[cmddep].b[6]); /* 6: Filename buffer */
+       free(cmp[cmddep].b[6]);
+       cmp[cmddep].b[6] = NULL;
+    }
+#else
+    if (cmp_b[cmddep][0]) {
+       strcpy(cmdbuf,cmp_b[cmddep][0]); /* 0: Command buffer */
+       free(cmp_b[cmddep][0]);
+       cmp_b[cmddep][0] = NULL;
+    }
+    if (cmp_b[cmddep][1]) {
+       strcpy(savbuf,cmp_b[cmddep][1]); /* 1: Save buffer */
+       free(cmp_b[cmddep][1]);
+       cmp_b[cmddep][1] = NULL;
+    }
+    if (cmp_b[cmddep][2]) {
+       strcpy(hlpbuf,cmp_b[cmddep][2]); /* 2: Help buffer */
+       free(cmp_b[cmddep][2]);
+       cmp_b[cmddep][2] = NULL;
+    }
+    if (cmp_b[cmddep][3]) {
+       strcpy(atmbuf,cmp_b[cmddep][3]); /* 3: Atomic buffer! */
+       free(cmp_b[cmddep][3]);
+       cmp_b[cmddep][3] = NULL;
+    }
+    if (cmp_b[cmddep][4]) {
+       strcpy(atxbuf,cmp_b[cmddep][4]); /* 4: eXpansion buffer */
+       free(cmp_b[cmddep][4]);
+       cmp_b[cmddep][4] = NULL;
+    }
+    if (cmp_b[cmddep][5]) {
+       strcpy(atybuf,cmp_b[cmddep][5]); /* 5: Atom buffer copY */
+       free(cmp_b[cmddep][5]);
+       cmp_b[cmddep][5] = NULL;
+    }
+    if (cmp_b[cmddep][6]) {
+       strcpy(filbuf,cmp_b[cmddep][6]); /* 6: Filename buffer */
+       free(cmp_b[cmddep][6]);
+       cmp_b[cmddep][6] = NULL;
+    }
+#endif /* DCMDBUF */
+
+    cmddep--;                          /* Rise, rise */
+    debug(F101,"&cmpop","",cmddep);
+    return(cmddep);
+}
+#endif /* NOSPL */
+
+#ifdef COMMENT
+VOID
+stripq(s) char *s; {                    /* Function to strip '\' quotes */
+    char *t;
+    while (*s) {
+        if (*s == CMDQ) {
+            for (t = s; *t != '\0'; t++) *t = *(t+1);
+        }
+        s++;
+    }
+}
+#endif /* COMMENT */
+
+/* Convert tabs to spaces, one for one */
+VOID
+untab(s) char *s; {
+    while (*s) {
+       if (*s == HT) *s = SP;
+       s++;
+    }
+}
+\f
+/*  C M N U M  --  Parse a number in the indicated radix  */
+/* 
+ The only radix allowed in unquoted numbers is 10.
+ Parses unquoted numeric strings in base 10.
+ Parses backslash-quoted numbers in the radix indicated by the quote:
+   \nnn = \dnnn = decimal, \onnn = octal, \xnn = Hexadecimal.
+ If these fail, then if a preprocessing function is supplied, that is applied 
+ and then a second attempt is made to parse an unquoted decimal string. 
+
+ Returns:
+   -3 if no input present when required,
+   -2 if user typed an illegal number,
+   -1 if reparse needed,
+    0 otherwise, with argument n set to the number that was parsed
+*/
+int
+cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *n; xx_strp f; {
+    int x; char *s, *zp, *zq;
+    if (radix != 10) {                  /* Just do base 10 */
+        printf("cmnum: illegal radix - %d\n",radix);
+        return(-1);
+    }
+    x = cmfld(xhlp,xdef,&s,(xx_strp)0);
+    debug(F101,"cmnum: cmfld","",x);
+    if (x < 0) return(x);              /* Parse a field */
+    zp = atmbuf;
+
+    if (chknum(zp)) {                  /* Check for decimal number */
+        *n = atoi(zp);                 /* Got one, we're done. */
+       debug(F101,"cmnum 1st chknum ok","",*n);
+        return(0);
+    } else if ((x = xxesc(&zp)) > -1) {        /* Check for backslash escape */
+
+#ifndef OS2
+       *n = x;
+#else
+       *n = wideresult;
+#endif /* OS2 */
+
+       debug(F101,"cmnum xxesc ok","",*n);
+       return(*zp ? -2 : 0);
+    } else if (f) {                    /* If conversion function given */
+        zp = atmbuf;                   /* Try that */
+       zq = atxbuf;
+       atxn = CMDBL;
+       (*f)(zp,&zq,&atxn);             /* Convert */
+       zp = atxbuf;
+    }
+    debug(F110,"cmnum zp",zp,0);
+    if (chknum(zp)) {                  /* Check again for decimal number */
+        *n = atoi(zp);                 /* Got one, we're done. */
+       debug(F101,"cmnum 2nd chknum ok","",*n);
+        return(0);
+    } else {                           /* Not numeric */
+       return(-2);     
+    }
+}
+\f
+/*  C M O F I  --  Parse the name of an output file  */
+/*
+ Depends on the external function zchko(); if zchko() not available, use
+ cmfld() to parse output file names.
+ Returns
+   -3 if no input present when required,
+   -2 if permission would be denied to create the file,
+   -1 if reparse needed,
+    0 or 1 otherwise, with xp pointing to name.
+*/
+int
+cmofi(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
+    int x; char *s, *zq;
+#ifdef DTILDE
+    _PROTOTYP( char * tilde_expand, (char *) );
+    char *dirp;
+#endif 
+
+    if (*xhlp == NUL) xhlp = "Output file";
+    *xp = "";
+    if ((x = cmfld(xhlp,xdef,&s,(xx_strp)0)) < 0) return(x);
+
+    if (f) {                           /* If a conversion function is given */
+       zq = atxbuf;
+       atxn = CMDBL;
+       if ((x = (*f)(s,&zq,&atxn)) < 0) return(-2);
+       s = atxbuf;
+    }
+
+#ifdef DTILDE
+    dirp = tilde_expand(s);            /* Expand tilde, if any, */
+    if (*dirp != '\0') setatm(dirp);   /* right in the atom buffer. */
+    s = atmbuf;
+#endif
+
+    if (iswild(s)) {
+        printf("?Wildcards not allowed - %s\n",s);
+        return(-2);
+    }
+    if (strcmp(s,CTTNAM) && (zchko(s) < 0)) { /* ok to write to tty */
+        printf("?Write permission denied - %s\n",s);
+        return(-9);
+    } else {
+        *xp = s;
+        return(x);
+    }
+}
+\f
+/*  C M I F I  --  Parse the name of an existing file  */
+/*
+ This function depends on the external functions:
+   zchki()  - Check if input file exists and is readable.
+   zxpand() - Expand a wild file specification into a list.
+   znext()  - Return next file name from list.
+ If these functions aren't available, then use cmfld() to parse filenames.
+*/
+/*
+ Returns
+   -4 EOF
+   -3 if no input present when required,
+   -2 if file does not exist or is not readable,
+   -1 if reparse needed,
+    0 or 1 otherwise, with:
+        xp pointing to name,
+        wild = 1 if name contains '*' or '?', 0 otherwise.
+*/
+int
+cmifi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
+    int i, x, xc; long y; char *sp, *zq, *sv;
+#ifdef DTILDE
+    char *tilde_expand(), *dirp;
+#endif /* DTILDE */
+
+#ifndef NOPARTIAL
+    extern char *mtchs[];
+#endif /* NOPARTIAL */
+
+    inword = cc = xc = 0;              /* Initialize counts & pointers */
+    *xp = "";
+    if ((x = cmflgs) != 1) {            /* Already confirmed? */
+        x = gtword();                   /* No, get a word */
+    } else {
+        setatm(xdef);                  /* If so, use default, if any. */
+    }
+
+    *xp = atmbuf;                       /* Point to result. */
+    while (1) {
+        xc += cc;                       /* Count the characters. */
+        debug(F111,"cmifi gtword",atmbuf,xc);
+        switch (x) {
+            case -4:                    /* EOF */
+            case -2:                    /* Out of space. */
+            case -1:                    /* Reparse needed */
+                return(x);
+            case 0:                     /* SP or NL */
+            case 1:
+                if (xc == 0) *xp = xdef;     /* If no input, return default. */
+                if (**xp == NUL) return(-3); /* If field empty, return -3. */
+
+               if (f) {                /* If a conversion function is given */
+                   zq = atxbuf;        /* ... */
+                   atxn = CMDBL;
+                   if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
+                   *xp = atxbuf;
+               }
+               debug(F110,"cmifi atxbuf",atxbuf,0);
+#ifdef COMMENT
+/* don't need this stuff, zxpand does it now. */
+#ifdef DTILDE
+
+               dirp = tilde_expand(*xp);    /* Expand tilde, if any, */
+               if (*dirp != '\0') setatm(dirp); /* right in atom buffer. */
+               *xp = atmbuf;
+#endif /* DTILDE */
+
+                /* If filespec is wild, see if there are any matches */
+
+                *wild = iswild(*xp);
+                debug(F101,"cmifi wild","",*wild);
+                if (*wild != 0) {
+#endif /* COMMENT */
+                   sv = malloc((int)strlen(*xp)+1); /* Make a safe copy */
+                   if (!sv) {
+                       printf("?malloc error 73, cmifi\n");
+                       return(-9);
+                   }
+                   strcpy(sv,*xp);
+                   debug(F110,"cmifi sv",sv,0);
+                    y = zxpand(*xp);
+                   *wild = (y > 1);
+                   debug(F111,"cmifi sv wild",sv,*wild);
+                    if (y == 0) {
+                        printf("?No files match - %s\n",*xp);
+                        return(-9);
+                    } else if (y < 0) {
+                        printf("?Too many files match - %s\n",*xp);
+                        return(-9);
+                   } else if (y > 1) return(x);
+#ifdef COMMENT
+                }
+#endif
+                /* If not wild, see if it exists and is readable. */
+               debug(F111,"cmifi sv not wild",sv,*wild);
+
+               znext(*xp);             /* Get first (only?) matching file */
+                y = zchki(*xp);                /* Check its accessibility */
+               zxpand(sv);             /* Rewind so next znext() gets 1st */
+               free(sv);               /* done with this */
+                if (y == -3) {
+                    printf("?Read permission denied - %s\n",*xp);
+                    return(-9);
+                } else if (y == -2) {
+                    printf("?File not readable - %s\n",*xp);
+                    return(-9);
+                } else if (y < 0) {
+                    printf("?File not found - %s\n",*xp);
+                    return(-9);
+                }
+                return(x);
+
+#ifndef MAC
+            case 2:                     /* ESC */
+               debug(F101,"cmifi esc, xc","",xc);
+                if (xc == 0) {
+                    if (*xdef != '\0') {
+                        printf("%s ",xdef); /* If at beginning of field, */
+#ifdef GEMDOS
+                       fflush(stdout);
+#endif /* GEMDOS */
+                       inword = cmflgs = 0;
+                        addbuf(xdef);   /* supply default. */
+                        setatm(xdef);
+                    } else {            /* No default */
+                        putchar(BEL);
+                    }
+                    break;
+                } 
+               if (f) {                /* If a conversion function is given */
+                   zq = atxbuf;        /* ... */
+                   atxn = CMDBL;
+                   if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
+                    /* reduce cc by number of \\ consumed by conversion */
+                   /* function (needed for OS/2, where \ is path separator) */
+                    cc -= (strlen(*xp) - strlen(atxbuf));
+                   *xp = atxbuf;
+               }
+/* #ifdef COMMENT */
+#ifdef DTILDE
+               dirp = tilde_expand(*xp);    /* Expand tilde, if any, */
+               if (*dirp != '\0') setatm(dirp); /* in the atom buffer. */
+                *xp = atmbuf;
+#endif /* DTILDE */
+/* #endif */
+                sp = *xp + cc;
+#ifdef datageneral
+                *sp++ = '+';           /* Data General AOS wildcard */
+#else
+                *sp++ = '*';           /* Others */
+#endif /* datageneral */
+                *sp-- = '\0';
+#ifdef GEMDOS
+               if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */
+                 strcat(*xp, ".*");    /* abc -> abc*.* */
+#endif /* GEMDOS */
+                y = zxpand(*xp);       /* Add wildcard and expand list. */
+               if (y > 0) strcpy(filbuf,mtchs[0]);
+               else *filbuf = '\0';
+                *sp = '\0';             /* Remove wildcard. */
+               *wild = (y > 1);
+                if (y == 0) {
+                    printf("?No files match - %s\n",atmbuf);
+                    return(-9);
+                } else if (y < 0) {
+                    printf("?Too many files match - %s\n",atmbuf);
+                    return(-9);
+                } else if (y > 1) {     /* Not unique. */
+#ifndef NOPARTIAL
+/* Partial filename completion */
+                   int i, j, k; char c;
+                   k = 0;
+                   debug(F111,"cmifi partial",filbuf,cc);
+                   for (i = cc; (c = filbuf[i]); i++) {
+                       for (j = 1; j < y; j++)
+                         if (mtchs[j][i] != c) break;
+                       if (j == y) k++;
+                       else filbuf[i] = filbuf[i+1] = NUL;
+                   }
+                   debug(F111,"cmifi partial k",filbuf,k);
+                   if (k > 0) {        /* Got more characters */
+                       sp = filbuf + cc; /* Point to new ones */
+#ifdef VMS
+                       for (i = 0; i < cc; i++) {
+                           cmdchardel(); /* Back up over old partial spec */
+                           bp--;
+                       }
+                       sp = filbuf;    /* Point to new word start */
+                       debug(F100,"cmifi vms erase ok","",0);
+#endif /* VMS */
+                       cc = k;         /* How many new ones we just got */
+                       printf("%s",sp);        /* Print them */
+                       while (*bp++ = *sp++) ; /* Copy to command buffer */
+                       bp--;                   /* Back up over NUL */
+                       debug(F110,"cmifi partial cmdbuf",cmdbuf,0);
+                       setatm(filbuf);
+                       debug(F111,"cmifi partial atmbuf",atmbuf,cc);
+                       *xp = atmbuf;
+                   }
+#endif /* NOPARTIAL */
+                   putchar(BEL);       /* Beep because not unique. */
+                } else {                /* Unique, complete it.  */
+                    sp = filbuf + cc;   /* Point past what user typed. */
+#ifdef VMS
+                   for (i = 0; i < cc; i++) {
+                       cmdchardel();   /* Back up over old partial spec */
+                       bp--;
+                   }
+                   sp = filbuf;        /* Point to new word start */
+#endif /* VMS */
+                    printf("%s ",sp);   /* Complete the name. */
+#ifdef GEMDOS
+                   fflush(stdout);
+#endif /* GEMDOS */
+                    addbuf(sp);         /* Add the characters to cmdbuf. */
+                    setatm(filbuf);    /* And to atmbuf. */
+                   inword = cmflgs = 0;
+                    *xp = atmbuf;       /* Return pointer to atmbuf. */
+                    return(0);
+                }
+                break;
+            case 3:                     /* Question mark */
+                if (*xhlp == NUL)
+                 printf(" Input file specification");
+                else
+                 printf(" %s",xhlp);
+#ifdef GEMDOS
+               fflush(stdout);
+#endif /* GEMDOS */
+                if (xc > 0) {
+                   if (f) {            /* If a conversion function is given */
+                       zq = atxbuf;    /* ... */
+                       atxn = CMDBL;
+                       if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
+                       *xp = atxbuf;
+                   }
+#ifdef DTILDE
+                   dirp = tilde_expand(*xp);    /* Expand tilde, if any */
+                   if (*dirp != '\0') setatm(dirp);
+                   *xp = atmbuf;
+#endif
+                   debug(F111,"cmifi ? *xp, cc",*xp,cc);
+                    sp = *xp + cc;     /* Insert "*" at end */
+#ifdef datageneral
+                    *sp++ = '+';        /* Insert +, the DG wild card */
+#else
+                    *sp++ = '*';
+#endif /* datageneral */
+                    *sp-- = '\0';
+#ifdef GEMDOS
+                   if (! strchr(*xp, '.'))     /* abde.e -> abcde.e* */
+                     strcat(*xp, ".*");        /* abc -> abc*.* */
+#endif /* GEMDOS */
+                   debug(F110,"cmifi ? wild",*xp,0);
+                    y = zxpand(*xp);
+                    *sp = '\0';
+                    if (y == 0) {                   
+                        printf("?No files match - %s\n",atmbuf);
+                        return(-9);
+                    } else if (y < 0) {
+                        printf("?Too many files match - %s\n",atmbuf);
+                        return(-9);
+                    } else {
+                        printf(", one of the following:\n");
+                        clrhlp();
+                        for (i = 0; i < y; i++) {
+                            znext(filbuf);
+#ifdef VMS
+                           printf(" %s\n",filbuf); /* VMS names can be long */
+#else
+                            addhlp(filbuf);
+#endif /* VMS */
+                        }
+                        dmphlp();
+                    }
+                } else printf("\n");
+                printf("%s%s",cmprom,cmdbuf);
+               fflush(stdout);
+                break;
+#endif /* MAC */
+        }
+    x = gtword();
+    *xp = atmbuf;
+    }
+}
+\f
+/*  C M D I R  --  Parse a directory specification  */
+/*
+ This function depends on the external functions:
+   zchki()  - Check if input file exists and is readable.
+ If these functions aren't available, then use cmfld() to parse dir names.
+ Note: this function quickly cobbled together, mainly by deleting lots of
+ lines from cmifi().  It seems to work, but various services are missing,
+ like completion, lists of matching directories on "?", etc.
+*/
+/*
+ Returns
+   -4 EOF
+   -3 if no input present when required,
+   -2 if out of space or other internal error,
+   -1 if reparse needed,
+    0 or 1, with xp pointing to name, if directory specified,
+    2 if a wildcard was included.
+*/
+int
+cmdir(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
+    int x, xc; char *zq;
+#ifdef DTILDE
+    char *tilde_expand(), *dirp;
+#endif /* DTILDE */
+
+    inword = cc = xc = 0;              /* Initialize counts & pointers */
+    *xp = "";
+    if ((x = cmflgs) != 1) {            /* Already confirmed? */
+        x = gtword();                   /* No, get a word */
+    } else {
+        setatm(xdef);                  /* If so, use default, if any. */
+    }
+    *xp = atmbuf;                       /* Point to result. */
+    while (1) {
+        xc += cc;                       /* Count the characters. */
+        debug(F111,"cmdir gtword",atmbuf,xc);
+        switch (x) {
+            case -4:                    /* EOF */
+            case -2:                    /* Out of space. */
+            case -1:                    /* Reparse needed */
+                return(x);
+            case 0:                     /* SP or NL */
+            case 1:
+                if (xc == 0) *xp = xdef;     /* If no input, return default. */
+               else *xp = atmbuf;
+                if (**xp == NUL) return(-3); /* If field empty, return -3. */
+
+               if (f) {                /* If a conversion function is given */
+                   zq = atxbuf;        /* ... */
+                   atxn = CMDBL;
+                   if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
+                   *xp = atxbuf;
+                   cc = (int)strlen(atxbuf);
+               }
+#ifdef DTILDE
+/*
+  This is ugly, and for UNIX only.
+  Normally, we wouldn't call tilde_expand from a place like this anyway,
+  but rather let zxpand() take care of it.  But in this case we might want 
+  a hybrid result -- a string with the tilde expanded, but with wildcards
+  left unexpanded.
+*/
+               dirp = tilde_expand(*xp); /* Expand tilde, if any, */
+               if (*dirp == '~') {     /* Still starts with tilde? */
+                   char *tp;           /* Yes, convert to lowercase */
+                   tp = *xp;           /* and try again. */
+                   while (*tp) {
+                       if (isupper(*tp)) *tp = tolower(*tp);
+                       tp++;
+                   }
+               }
+               dirp = tilde_expand(*xp); /* Expand tilde, if any, */
+               if (*dirp != '\0') setatm(dirp); /* in the atom buffer. */
+               *xp = atmbuf;
+#endif /* DTILDE */
+               if (iswild(*xp)) return(2);
+               else return(x);
+
+            case 2:                     /* ESC */
+               putchar(BEL);
+               break;
+
+            case 3:                     /* Question mark */
+                if (*xhlp == NUL)
+                    printf(" Directory name");
+                else
+                    printf(" %s",xhlp);
+                printf("\n%s%s",cmprom,cmdbuf);
+               fflush(stdout);
+                break;
+        }
+    x = gtword();
+/*  *xp = atmbuf;  */
+    }
+}
+\f 
+/*  C M F L D  --  Parse an arbitrary field  */
+/*
+ Returns
+   -3 if no input present when required,
+   -2 if field too big for buffer,
+   -1 if reparse needed,
+    0 otherwise, xp pointing to string result.
+*/
+int
+cmfld(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
+    int x, xc;
+    char *zq;
+
+    inword = cc = xc = 0;              /* Initialize counts & pointers */
+    *xp = "";
+    if ((x = cmflgs) != 1) {            /* Already confirmed? */
+        x = gtword();                   /* No, get a word */
+    } else {
+        setatm(xdef);                  /* If so, use default, if any. */
+    }
+    *xp = atmbuf;                       /* Point to result. */
+    while (1) {
+        xc += cc;                       /* Count the characters. */
+        debug(F111,"cmfld: gtword",atmbuf,xc);
+        debug(F101,"cmfld x","",x);
+        switch (x) {
+            case -4:                    /* EOF */
+            case -2:                    /* Out of space. */
+            case -1:                    /* Reparse needed */
+                return(x);
+            case 0:                     /* SP or NL */
+            case 1:
+                if (xc == 0)           /* If no input, return default. */
+                 setatm(xdef);
+               *xp = atmbuf;
+               if (f) {                /* If a conversion function is given */
+                   zq = atxbuf;        /* ... */
+                   atxn = CMDBL;
+                   if ((*f)(*xp,&zq,&atxn) < 0) return(-2);
+                   setatm(atxbuf);
+                   *xp = atmbuf;
+               }
+                if (**xp == NUL) {     /* If variable evaluates to null */
+                   setatm(xdef);       /* Stick in the default again. */
+                   if (**xp == NUL) x = -3; /* If still empty, return -3. */
+               }
+#ifdef COMMENT
+/* The following is apparently not necessary. */
+/* Remove it if nothing is broken, esp. TAKE file with trailing comments */
+               xx = *xp;
+               debug(F111,"cmfld before trim",*xp,x);
+               for (i = (int)strlen(xx) - 1; i > 0; i--)
+                 if (xx[i] != SP)      /* Trim trailing blanks */
+                   break;
+                 else
+                   xx[i] = NUL;
+               debug(F111,"cmfld returns",*xp,x); 
+#endif /* COMMENT */
+               debug(F101,"cmfld: returns","",x);
+                return(x);
+            case 2:                     /* ESC */
+                if (xc == 0 && *xdef != NUL) {
+                    printf("%s ",xdef); /* If at beginning of field, */
+#ifdef GEMDOS
+                   fflush(stdout);
+#endif /* GEMDOS */
+                    addbuf(xdef);       /* supply default. */
+                   inword = cmflgs = 0;
+                    setatm(xdef);      /* Return as if whole field */
+                    return(0);          /* typed, followed by space. */
+                } else {
+                    putchar(BEL);       /* Beep if already into field. */
+                }                   
+                break;
+            case 3:                     /* Question mark */
+                if (*xhlp == NUL)
+                    printf(" Please complete this field");
+                else
+                    printf(" %s",xhlp);
+                printf("\n%s%s",cmprom,cmdbuf);
+               fflush(stdout);
+                break;
+        }
+    x = gtword();
+/*  *xp = atmbuf; */
+    }
+}
+\f
+/*  C M T X T  --  Get a text string, including confirmation  */
+/*
+  Print help message 'xhlp' if ? typed, supply default 'xdef' if null
+  string typed.  Returns
+   -1 if reparse needed or buffer overflows.
+    1 otherwise.
+  with cmflgs set to return code, and xp pointing to result string.
+*/
+int
+cmtxt(xhlp,xdef,xp,f) char *xhlp; char *xdef; char **xp; xx_strp f; {
+    int x, i;
+    char *xx, *zq;
+    static int xc;
+    debug(F101,"cmtxt, cmflgs","",cmflgs);
+    inword = cc = 0;                   /* Start atmbuf counter off at 0 */
+    if (cmflgs == -1) {                 /* If reparsing, */
+        xc = (int)strlen(*xp);         /* get back the total text length, */
+    } else {                            /* otherwise, */
+        *xp = "";                       /* start fresh. */
+        xc = 0;
+    }
+    *atmbuf = NUL;                      /* And empty the atom buffer. */
+    if ((x = cmflgs) != 1) {
+        x = gtword();                   /* Get first word. */
+        *xp = pp;                       /* Save pointer to it. */
+    }
+    debug(F101,"cmtxt (*f)","", f);
+    while (1) {                                /* Loop for each word in text. */
+        xc += cc;                       /* Char count for all words. */
+        debug(F111,"cmtxt: gtword",atmbuf,xc);
+        debug(F101," x","",x);
+        switch (x) {
+         case -9:                      /* Buffer overflow */
+         case -4:                      /* EOF */
+#ifdef MAC
+         case -3:                      /* Quit/Timeout */
+#endif /* MAC */
+         case -2:                      /* Overflow */
+         case -1:                      /* Deletion */
+           return(x);
+         case 0:                       /* Space */
+           xc++;                       /* Just count it */
+           break;
+         case 1:                       /* CR or LF */
+           if (xc == 0) *xp = xdef;
+           if (f) {                    /* If a conversion function is given */
+               zq = atxbuf;            /* Point to the expansion buffer */
+               atxn = CMDBL;           /* specify its length */
+               debug(F110,"cmtxt calling (*f)",*xp,0);
+               if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
+               cc = (int)strlen(atxbuf);
+               *xp = atxbuf;           /* and return pointer to it. */
+               debug(F111,"cmtxt (*f) returns",*xp,cc);
+           }
+           xx = *xp;
+           for (i = (int)strlen(xx) - 1; i > 0; i--)
+             if (xx[i] != SP)          /* Trim trailing blanks */
+               break;
+             else
+               xx[i] = NUL;
+           return(x);
+         case 2:                       /* ESC */
+           if (xc == 0) {
+               printf("%s ",xdef);
+               inword = cmflgs = 0;
+#ifdef GEMDOS
+               fflush(stdout);
+#endif /* GEMDOS */
+               cc = addbuf(xdef);
+           } else {
+               putchar(BEL);
+           }
+           break;
+         case 3:                       /* Question Mark */
+           if (*xhlp == NUL)
+             printf(" Text string");
+           else
+             printf(" %s",xhlp);
+           printf("\n%s%s",cmprom,cmdbuf);
+           fflush(stdout);
+           break;
+         default:
+           printf("?Unexpected return code from gtword() - %d\n",x);
+           return(-2);
+        }
+        x = gtword();
+    }
+}
+\f
+/*  C M K E Y  --  Parse a keyword  */
+/*
+ Call with:
+   table    --  keyword table, in 'struct keytab' format;
+   n        --  number of entries in table;
+   xhlp     --  pointer to help string;
+   xdef     --  pointer to default keyword;
+ Returns:
+   -3       --  no input supplied and no default available
+   -2       --  input doesn't uniquely match a keyword in the table
+   -1       --  user deleted too much, command reparse required
+    n >= 0  --  value associated with keyword
+*/
+int
+cmkey(table,n,xhlp,xdef,f)
+/* cmkey */  struct keytab table[]; int n; char *xhlp, *xdef; xx_strp f; {
+    return(cmkey2(table,n,xhlp,xdef,"",f));
+}
+int
+cmkey2(table,n,xhlp,xdef,tok,f)
+    struct keytab table[]; int n; char *xhlp, *xdef; char *tok; xx_strp f; {
+
+    int i, tl, y, z, zz, xc;
+    char *xp, *zq;
+    tl = (int)strlen(tok);
+    inword = xc = cc = 0;              /* Clear character counters. */
+    if ((zz = cmflgs) == 1)             /* Command already entered? */
+      setatm(xdef);                    /* Yes, copy default into atom buf */
+    else zz = gtword();                        /* Otherwise get a command word */
+debug(F101,"cmkey: table length","",n);
+debug(F101," cmflgs","",cmflgs);
+debug(F101," zz","",zz);
+while (1) {
+    xc += cc;
+    debug(F111,"cmkey: gtword",atmbuf,xc);
+    switch(zz) {
+        case -4:                        /* EOF */
+#ifdef MAC
+       case -3:                        /* Quit/Timeout */
+#endif /* MAC */
+        case -2:                        /* Buffer overflow */
+        case -1:                        /* Or user did some deleting. */
+            return(cmflgs = zz);
+        case 0:                         /* User terminated word with space */
+        case 1:                         /* or newline */
+            if (cc == 0) setatm(xdef); /* Supply default if user typed nada */
+           if (f) {                    /* If a conversion function is given */
+               zq = atxbuf;            /* apply it */
+               atxn = CMDBL;
+               if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
+               debug(F110,"cmkey atxbuf after *f",atxbuf,0);
+               setatm(atxbuf);
+           }
+            y = lookup(table,atmbuf,n,&z); /* Look up the word in the table */
+            switch (y) {
+                case -2:               /* Ambiguous */
+                    printf("?Ambiguous - %s\n",atmbuf);
+                   cmflgs = -2;
+                    return(-9);
+                case -1:               /* Not found at all */
+                   if (tl) {
+                       for (i = 0; i < tl; i++) /* Check for token */
+                         if (tok[i] == *atmbuf) { /* Got one */
+                             ungword();  /* Put back the following word */
+                             return(-5); /* Special return code for token */
+                         }
+                   }
+                   /* Kludge alert... only print error if */
+                   /* we were called as cmkey2, but not cmkey... */
+                   /* This doesn't seem to always work. */
+                   if (tl == 0) {
+                       printf("?No keywords match - %s\n",atmbuf); /* cmkey */
+                       return(cmflgs = -9);
+                   } else { 
+                       if (cmflgs == 1) return(cmflgs = -6); /* cmkey2 */
+                       else return(cmflgs = -2);
+                       /* The -6 code is to let caller try another table */
+                   }
+                default:
+                    break;
+               }
+            return(y);
+        case 2:                         /* User terminated word with ESC */
+            if (cc == 0) {
+                if (*xdef != NUL) {     /* Nothing in atmbuf */
+                    printf("%s ",xdef); /* Supply default if any */
+#ifdef GEMDOS
+                   fflush(stdout);
+#endif /* GEMDOS */
+                    addbuf(xdef);
+                    setatm(xdef);
+                   inword = cmflgs = 0;
+                    debug(F111,"cmkey: default",atmbuf,cc);
+                } else {
+                    putchar(BEL);       /* No default, just beep */
+                    break;
+                }
+            }
+           if (f) {                    /* If a conversion function is given */
+               zq = atxbuf;            /* apply it */
+               atxn = CMDBL;
+               if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
+               setatm(atxbuf);
+           }
+            y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */
+            debug(F111,"cmkey: esc",atmbuf,y);
+            if (y == -2) {             /* Ambiguous */
+                putchar(BEL);
+                break;
+            }
+            if (y == -1) {             /* Not found */
+                /* if (tl == 0) */ printf("?No keywords match - %s\n",atmbuf);
+               cmflgs = -2;
+                return(-9);
+            }
+/*
+  See if the keyword just found has the CM_ABR bit set in its flgs field, and
+  if so, search forwards in the table for a keyword that has the same kwval
+  but does not have CM_ABR (or CM_INV?) set, and then expand using the full
+  keyword.  WARNING: This assumes that (a) keywords are in alphabetical order,
+  and (b) the CM_ABR bit is set only if the the abbreviated keyword is a true
+  abbreviation (left substring) of the full keyword.
+*/
+           if (test(table[z].flgs,CM_ABR)) {
+               int zz;
+               for (zz = z+1; zz < n; zz++)
+                 if ((table[zz].kwval == table[z].kwval) &&
+                     (!test(table[zz].flgs,CM_ABR))) {
+                     z = zz;
+                     break;
+                 }
+            } 
+            xp = table[z].kwd + cc;
+            printf("%s ",xp);
+#ifdef GEMDOS
+           fflush(stdout);
+#endif /* GEMDOS */
+            addbuf(xp);
+           inword = cmflgs = 0;
+            debug(F110,"cmkey: addbuf",cmdbuf,0);
+            return(y);
+        case 3:                         /* User typed "?" */
+           if (f) {                    /* If a conversion function is given */
+               zq = atxbuf;            /* do the conversion now. */
+               atxn = CMDBL;
+               if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
+               setatm(atxbuf);
+           }
+            y = lookup(table,atmbuf,n,&z); /* Look up what we have so far. */
+
+           if (y == -1) {
+                /* if (tl == 0) */ printf(" No keywords match\n");
+               cmflgs = -2;
+                return(-9);
+            }
+            if (*xhlp == NUL)
+                printf(" One of the following:\n");
+            else
+                printf(" %s, one of the following:\n",xhlp);
+           if ((y > -1) &&
+               !test(table[z].flgs,CM_ABR) &&
+               ((z >= n-1) || strncmp(table[z].kwd,table[z+1].kwd,cc))
+                ) {
+               printf(" %s\n",table[z].kwd);
+           } else {
+               clrhlp();
+               for (i = 0; i < n; i++) {   
+                   if (!strncmp(table[i].kwd,atmbuf,cc)
+                       && !test(table[i].flgs,CM_INV)
+                       )
+                     addhlp(table[i].kwd);
+               }
+               dmphlp();
+           }
+           if (*atmbuf == NUL) {
+               if (tl == 1)
+                 printf("or the token '%c'\n",*tok);
+               else if (tl > 1) printf("or one of the tokens '%s'\n",tok);
+           }
+            printf("%s%s", cmprom, cmdbuf);
+           fflush(stdout);
+            break;
+        default:            
+            printf("\n%d - Unexpected return code from gtword\n",zz);
+            return(cmflgs = -2);
+        }
+        zz = gtword();
+    }
+}
+int
+chktok(tlist) char *tlist; {
+    char *p;
+    p = tlist;
+    while (*p != NUL && *p != *atmbuf) p++;
+    return((*p) ? (int) *p : 0);
+}
+
+/*  C M C F M  --  Parse command confirmation (end of line)  */
+/*
+ Returns
+   -2: User typed anything but whitespace or newline
+   -1: Reparse needed
+    0: Confirmation was received
+*/
+int
+cmcfm() {
+    int x, xc;
+    debug(F101,"cmcfm: cmflgs","",cmflgs);
+    debug(F110,"cmcfm: atmbuf",atmbuf,0);
+    inword = xc = cc = 0;
+    if (cmflgs == 1) return(0);
+
+    setatm("");                                /* (Probably unnecessary) */
+    while (1) {
+        x = gtword();
+        xc += cc;
+        switch (x) {
+            case -4:                    /* EOF */
+            case -2:
+            case -1:
+                return(x);
+            case 1:                     /* End of line */
+                if (xc > 0) {
+                    printf("?Not confirmed - %s\n",atmbuf);
+                    return(-9);
+                } else return(0);                   
+            case 2:                    /* ESC */
+               if (xc == 0) {
+                   putchar(BEL);       /* beep & continue */
+                   continue;           /* or fall thru. */
+               }
+            case 0:                     /* Space */
+               if (xc == 0)            /* If no chars typed, continue, */
+                 continue;             /* else fall thru. */
+            case 3:                    /* Question mark */
+                if (xc > 0) {
+                    printf("?Not confirmed - %s\n",atmbuf);
+                    return(-9);
+                }
+                printf("\n Type a carriage return to confirm the command\n");
+                printf("%s%s",cmprom,cmdbuf);
+               fflush(stdout);
+                continue;
+        }
+    }
+}
+\f
+/* Keyword help routines */
+/*  C L R H L P -- Initialize/Clear the help line buffer  */
+VOID
+clrhlp() {                              /* Clear the help buffer */
+    hlpbuf[0] = NUL;
+    hh = hx = 0;
+}
+/*  A D D H L P  --  Add a string to the help line buffer  */
+VOID
+addhlp(s) char *s; {                    /* Add a word to the help buffer */
+    int j;
+    hh++;                               /* Count this column */
+    for (j = 0; (j < hc) && (*s != NUL); j++) { /* Fill the column */
+        hlpbuf[hx++] = *s++;
+    }
+    if (*s != NUL)                      /* Still some chars left in string? */
+        hlpbuf[hx-1] = '+';             /* Mark as too long for column. */
+    if (hh < (hw / hc)) {               /* Pad col with spaces if necessary */
+        for (; j < hc; j++) {
+            hlpbuf[hx++] = SP;
+        }
+    } else {                            /* If last column, */
+        hlpbuf[hx++] = NUL;             /* no spaces. */
+        dmphlp();                       /* Print it. */
+        return;
+    }
+}
+/*  D M P H L P  --  Dump the help line buffer  */
+VOID
+dmphlp() {                              /* Print the help buffer */
+    hlpbuf[hx++] = NUL;
+    printf(" %s\n",hlpbuf);
+    clrhlp();
+}
+\f
+/*  G T W O R D  --  Gets a "word" from the command input stream  */
+/*
+Usage: retcode = gtword();
+Returns:
+ -4 if end of file (e.g. pipe broken)
+ -2 if command buffer overflows
+ -1 if user did some deleting
+  0 if word terminates with SP or tab
+  1 if ... CR
+  2 if ... ESC
+  3 if ... ? (question mark)
+With:
+  pp pointing to beginning of word in buffer
+  bp pointing to after current position
+  atmbuf containing a copy of the word
+  cc containing the number of characters in the word copied to atmbuf
+*/
+
+int
+ungword() {                            /* unget a word */
+    if (ungw) return(0);
+    cmfsav = cmflgs;
+    debug(F101,"ungword cmflgs","",cmflgs);
+    ungw = 1;
+    cmflgs = 0;
+    return(0);
+}
+
+int
+gtword() {
+    int c;                              /* Current char */
+    int quote = 0;                      /* Flag for quote character */
+    int echof = 0;                      /* Flag for whether to echo */
+    int chsrc = 0;                     /* Source of character, 1 = tty */
+    int comment = 0;                   /* Flag for in comment */
+    char *cp = NULL;                   /* Comment pointer */
+
+#ifdef RTU
+    extern int rtu_bug;
+#endif /* RTU */
+
+#ifdef datageneral
+    extern int termtype;                /* DG terminal type flag */
+    extern int con_reads_mt;            /* Console read asynch is active */
+    if (con_reads_mt) connoi_mt();      /* Task would interfere w/cons read */
+#endif /* datageneral */
+    if (ungw) {                                /* Have a word saved? */
+       debug(F110,"gtword ungetting from pp",pp,0);
+       while (*pp++ == SP) ;
+       setatm(pp);
+       ungw = 0;
+       cmflgs = cmfsav;
+       debug(F111,"gtword returning atmbuf",atmbuf,cmflgs);
+       return(cmflgs);
+    }
+    pp = np;                            /* Start of current field */
+
+    debug(F111,"gtword: cmdbuf",cmdbuf,cmdbuf);
+    debug(F111," bp",bp,bp);
+    debug(F111," pp",pp,pp);
+    while (bp < cmdbuf+CMDBL) {         /* Big get-a-character loop */
+       echof = 0;                      /* Assume we don't echo because */
+       chsrc = 0;                      /* character came from reparse buf. */
+
+        if ((c = *bp) == NUL) {         /* If no char waiting in reparse buf */
+            if (dpx) echof = 1;         /* must get from tty, set echo flag. */
+           c = cmdgetc();              /* Read a character from the tty. */
+           chsrc = 1;                  /* Remember character source is tty. */
+#ifdef MAC
+           if (c == -3)                /* If null command... */
+             return(-3);
+#endif /* MAC */
+            if (c == EOF) {            /* This can happen if stdin not tty. */
+#ifdef EINTR
+               if (errno == EINTR)     /* This is for when bg'd process is */
+                 continue;             /* fg'd again. */
+#endif /* EINTR */
+               return(-4);
+           }
+           c &= cmdmsk;                /* Strip any parity bit */
+       }                               /* if desired. */
+#ifndef MAC
+       debug(F000,"gtword char","",c);
+#endif /* MAC */
+
+/* Now we have the next character */
+
+        if (quote == 0) {              /* If this is not a quoted character */
+            if (c == CMDQ) {           /* Got the quote character itself */
+               if (!comment) quote = 1; /* Flag it if not in a comment */
+            }
+           if (c == FF) {              /* Formfeed. */
+                c = NL;                 /* Replace with newline */
+#ifdef COMMENT
+/* No more screen clearing... */
+               cmdclrscn();            /* Clear the screen */
+#endif /* COMMENT */
+            }
+           if (c == HT) {              /* Tab */
+               if (comment)            /* If in comment, */
+                 c = SP;               /* substitute space */
+               else                    /* otherwise */
+                 c = ESC;              /* substitute ESC (for completion) */
+           }
+           if (c == ';' || c == '#') { /* Trailing comment */
+               if (inword == 0) {      /* If we're not in a word */
+                   comment = 1;        /* start a comment. */
+                   cp = bp;            /* remember where it starts. */
+               }
+           }
+           if (!comment && c == SP) {  /* Space */
+                *bp++ = c;             /* deposit in buffer if not already */
+                if (echof) putchar(c);  /* echo it. */
+                if (inword == 0) {      /* If leading, gobble it. */
+                    pp++;
+                    continue;
+                } else {                /* If terminating, return. */
+                    np = bp;
+                    setatm(pp);
+                    inword = cmflgs = 0;
+                   return(0);
+                }
+            }
+            if (c == NL || c == CR) {   /* CR or LF. */
+               if (echof) cmdnewl((char)c); /* Echo it. */
+               while (bp > pp && (*(bp-1) == SP || *(bp-1) == HT)) /* Trim */
+                 bp--;                 /* trailing */
+               *bp = NUL;              /* whitespace */
+               if (*(bp-1) == '-') {   /* Is this line continued? */
+                   if (chsrc) {        /* If reading from tty, */
+#ifdef COMMENT
+                       bp--, pp--;     /* back up the buffer pointer, */
+#else
+                       bp--;
+#endif /* COMMENT */
+                       *bp = NUL;      /* erase the dash, */
+                       continue;       /* and go back for next char now. */
+                   }
+               } else {                /* No, a command has been entered. */
+                   *bp = NUL;          /* Terminate the command string. */
+                   if (comment) {      /* If we're in a comment, */
+                       comment = 0;    /* Say we're not any more, */
+                       *cp = NUL;      /* cut it off. */
+                   }
+                   np = bp;            /* Where to start next field. */
+                   setatm(pp);         /* Copy this field to atom buffer. */
+                   inword = 0;         /* Not in a word any more. */
+                   return(cmflgs = 1);
+               }
+            }
+            if (!comment && echof && (c == '?')) { /* Question mark */
+                putchar(c);
+                *bp = NUL;
+                setatm(pp);
+                return(cmflgs = 3);
+            }
+            if (c == ESC) {            /* ESC */
+               if (!comment) {
+                   *bp = NUL;
+                   setatm(pp);
+                   return(cmflgs = 2);
+               } else {
+                   putchar(BEL);
+                   continue;
+               }
+            }
+            if (c == BS || c == RUB) {  /* Character deletion */
+                if (bp > cmdbuf) {      /* If still in buffer... */
+                   cmdchardel();       /* erase it. */
+                    bp--;               /* point behind it, */
+                    if (*bp == SP) inword = 0; /* Flag if current field gone */
+                    *bp = NUL;          /* Erase character from buffer. */
+                } else {                /* Otherwise, */
+                    putchar(BEL);       /* beep, */
+                    cmres();            /* and start parsing a new command. */
+                   *bp = *atmbuf = NUL;
+                }
+                if (pp < bp) continue;
+                else return(cmflgs = -1);
+            }
+            if (c == LDEL) {            /* ^U, line deletion */
+                while ((bp--) > cmdbuf) {
+                    cmdchardel();
+                    *bp = NUL;
+                }
+                cmres();                /* Restart the command. */
+               *bp = *atmbuf = NUL;
+                inword = 0;
+                return(cmflgs = -1);
+            }
+            if (c == WDEL) {            /* ^W, word deletion */
+                if (bp <= cmdbuf) {     /* Beep if nothing to delete */
+                    putchar(BEL);
+                    cmres();
+                   *bp = *atmbuf = NUL;
+                    return(cmflgs = -1);
+                }
+                bp--;
+                for ( ; (bp >= cmdbuf) && (*bp == SP) ; bp--) {
+                    cmdchardel();
+                    *bp = NUL;
+                }
+                for ( ; (bp >= cmdbuf) && (*bp != SP) ; bp--) {
+                    cmdchardel();
+                    *bp = NUL;
+                }
+                bp++;
+                inword = 0;
+                return(cmflgs = -1);
+            }
+            if (c == RDIS) {            /* ^R, redisplay */
+#ifdef COMMENT
+                *bp = NUL;
+                printf("\n%s%s",cmprom,cmdbuf);
+#else
+               char *cpx; char cx;
+                *bp = NUL;
+                printf("\n%s",cmprom);
+               cpx = cmdbuf;
+               while (cx = *cpx++) {
+#ifdef isprint
+                   putchar(isprint(cx) ? cx : '^');
+#else
+                   putchar((cx >= SP && cx < DEL) ? cx : '^');
+#endif /* isprint */
+               }
+#endif /* COMMENT */
+               fflush(stdout);
+                continue;
+            }
+           if (c < SP && quote == 0) { /* Any other unquoted control char */
+               if (!chsrc) bp++;       /* If cmd file, point past it */
+               else putchar(BEL);      /* otherwise just beep and */
+               continue;               /* continue, don't put in buffer */
+           }
+           if (echof) cmdecho((char) c, 0); /* Echo what was typed. */
+        } else {                       /* This character was quoted. */
+           int qf = 1;
+           quote = 0;                  /* Unset the quote flag. */
+           /* Quote character at this level is only for SP, ?, and controls */
+            /* If anything else was quoted, leave quote in, and let */
+           /* the command-specific parsing routines handle it, e.g. \007 */
+           if (c > 32 && c != '?' && c != RUB && chsrc != 0) {
+               *bp++ = CMDQ;           /* Deposit \ if it came from tty */
+               qf = 0;                 /* and don't erase it from screen */
+           }
+           if (echof) cmdecho((char) c, qf); /* Now echo quoted character */
+           debug(F000,"gtword quote",cmdbuf,c);
+       }
+#ifdef COMMENT
+        if (echof) cmdecho((char) c,quote); /* Echo what was typed. */
+#endif /* COMMENT */
+        if (!comment) inword = 1;      /* Flag we're in a word. */
+       if (quote) continue;            /* Don't deposit quote character. */
+        if (c != NL) *bp++ = c;                /* Deposit command character. */
+    }                                   /* End of big while */
+    putchar(BEL);                       /* Get here if... */
+    printf("?Command too long, maximum length: %d.\n",CMDBL);
+    cmflgs = -2;
+    return(-9);
+}
+\f
+/* Utility functions */
+/* A D D B U F  -- Add the string pointed to by cp to the command buffer  */
+int
+addbuf(cp) char *cp; {
+    int len = 0;
+    while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) {
+        *bp++ = *cp++;                  /* Copy and */
+        len++;                          /* count the characters. */
+    }   
+    *bp++ = SP;                         /* Put a space at the end */
+    *bp = NUL;                          /* Terminate with a null */
+    np = bp;                            /* Update the next-field pointer */
+    return(len);                        /* Return the length */
+}
+/*  S E T A T M  --  Deposit a token in the atom buffer.  */
+/*  Break on space, newline, carriage return, or null. */
+/*  Null-terminate the result. */
+/*  If the source pointer is the atom buffer itself, do nothing. */
+/*  Return length of token, and also set global "cc" to this length. */
+
+int
+setatm(cp) char *cp; {
+    char *ap, *xp;
+
+    cc = 0;                            /* Character counter */
+    ap = atmbuf;                       /* Address of atom buffer */
+
+    if (cp == ap) {                    /* In case source is atom buffer */
+       xp = atybuf;                    /* make a copy */
+       strcpy(xp,ap);                  /* so we can copy it back, edited. */
+       cp = xp;
+    }
+    *ap = NUL;                         /* Zero the atom buffer */
+
+    while (*cp == SP) cp++;            /* Trim leading spaces */
+    while ((*cp != SP) && (*cp != NL) && (*cp != NUL) && (*cp != CR)) {
+        *ap++ = *cp++;                 /* Copy up to SP, NL, CR, or end */
+        cc++;                          /* and count */
+    }
+    *ap = NUL;                         /* Terminate the string. */
+    return(cc);                         /* Return length. */
+}
+/*  R D I G I T S  -- Verify that all the characters in line ARE DIGITS  */
+int
+rdigits(s) char *s; {
+    while (*s) {
+        if (!isdigit(*s)) return(0);
+        s++;
+    }
+    return(1);
+}
+\f
+/* These functions attempt to hide system dependencies from the mainline */
+/* code in gtword().  Ultimately they should be moved to ck?tio.c, where */
+/* ? = each and every system supported by C-Kermit. */
+
+int
+cmdgetc() {                            /* Get a character from the tty. */
+    int c;
+
+#ifdef datageneral
+    {
+       char ch;
+       c = dgncinb(0,&ch,1);           /* -1 is EOF, -2 TO, 
+                                         * -c is AOS/VS error */
+       if (c == -2) {                  /* timeout was enabled? */
+           resto(channel(0));          /* reset timeouts */
+           c = dgncinb(0,&ch,1);       /* retry this now! */
+       }
+       if (c < 0) return(-4);          /* EOF or some error */
+       else c = (int) ch & 0177;       /* Get char without parity */
+/*     echof = 1; */
+    }
+#else /* Not datageneral */
+#ifdef GEMDOS
+    c = isatty(0) ? coninc(0) : getchar();
+#else
+#ifdef OS2
+    c = isatty(0) ? coninc(0) : getchar();
+    if (c < 0) return(-4);
+#else /* Not OS2 */
+    c = getchar();                     /* or from tty. */
+#ifdef RTU
+    if (rtu_bug) {
+       c = getchar();                  /* RTU doesn't discard the ^Z */
+       rtu_bug = 0;
+    }
+#endif /* RTU */
+#endif /* OS2 */
+#endif /* GEMDOS */
+#endif /* datageneral */
+    return(c);                         /* Return what we got */
+}
+
+
+#ifdef COMMENT
+/*
+  No more screen clearing.  If you wanna clear the screen, define a macro
+  to do it, like "define cls write screen \27[;H\27[2J".
+*/
+cmdclrscn() {                          /* Clear the screen */
+
+#ifdef aegis
+    putchar(FF);
+#else
+#ifdef AMIGA
+    putchar(FF);
+#else
+#ifdef OSK
+    putchar(FF);
+#else
+#ifdef datageneral
+    putchar(FF);
+#else
+#ifdef OS2
+    zsystem("cls");
+#else
+    zsystem("clear");
+#endif /* OS2 */
+#endif /* datageneral */
+#endif /* OSK */
+#endif /* AMIGA */
+#endif /* aegis */
+}
+#endif /* COMMENT */
+
+VOID                                   /* What to echo at end of command */
+#ifdef CK_ANSIC
+cmdnewl(char c)
+#else
+cmdnewl(c) char c;
+#endif /* CK_ANSIC */
+/* cmdnewl */ {
+    putchar(c);                                /* c is the terminating character */
+#ifdef WINTCP
+    if (c == CR) putchar(NL);
+#endif /* WINTCP */
+#ifdef OS2
+    if (c == CR) putchar(NL);
+#endif /* OS2 */
+#ifdef aegis
+    if (c == CR) putchar(NL);
+#endif /* aegis */
+#ifdef AMIGA
+    if (c == CR) putchar(NL);
+#endif /* AMIGA */
+#ifdef datageneral
+    if (c == CR) putchar(NL);
+#endif /* datageneral */
+#ifdef GEMDOS
+    if (c == CR) putchar(NL);
+#endif /* GEMDOS */
+}
+
+VOID
+cmdchardel() {                         /* Erase a character from the screen */
+    if (!dpx) return;
+#ifdef datageneral
+    /* DG '\b' is EM (^y or \031) */
+    if (termtype == 1)
+      /* Erase a character from non-DG screen, */
+      dgncoub(1,"\010 \010",3);
+    else
+#endif
+      printf("\b \b");
+#ifdef GEMDOS
+    fflush(stdout);
+#endif /* GEMDOS */
+}
+
+VOID
+#ifdef CK_ANSIC
+cmdecho(char c, int quote)
+#else
+cmdecho(c,quote) char c; int quote;
+#endif /* CK_ANSIC */
+{ /* cmdecho */
+    if (!dpx) return;
+    /* Echo tty input character c */
+    if (quote) {
+       putchar(BS); putchar(SP); putchar(BS); 
+#ifdef isprint
+       putchar( isprint(c) ? c : '^' );
+#else
+       putchar((c >= SP && c < DEL) ? c : '^');
+#endif /* isprint */
+    } else putchar(c);
+#ifdef OS2
+    if (quote==1 && c==CR) putchar(NL);
+#endif /* OS2 */
+}
+
+#endif /* NOICP */
+
+#ifdef NOICP
+#include "ckcdeb.h"
+#include "ckucmd.h"
+#include "ckcasc.h"
+/*** #include <ctype.h> (ckcdeb.h already includes this) ***/
+#endif /* NOICP */
+
+/*  X X E S C  --  Interprets backslash codes  */
+/*  Returns the int value of the backslash code if it is > -1 and < 256 */
+/*  and updates the string pointer to first character after backslash code. */
+/*  If the argument is invalid, leaves pointer unchanged and returns -1. */
+
+int
+xxesc(s) char **s; {                   /* Expand backslash escapes */
+    int x, y, brace, radix;            /* Returns the int value */
+    char hd = '9';                     /* Highest digit in radix */
+    char *p;
+
+    p = *s;                            /* pointer to beginning */
+    if (!p) return(-1);                        /* watch out for null pointer */
+    x = *p++;                          /* character at beginning */
+    if (x != CMDQ) return(-1);         /* make sure it's a backslash code */
+
+    x = *p;                            /* it is, get the next character */
+    if (x == '{') {                    /* bracketed quantity? */
+       p++;                            /* begin past bracket */
+       x = *p;
+       brace = 1;
+    } else brace = 0;
+    switch (x) {                       /* Start interpreting */
+      case 'd':                                /* Decimal radix indicator */
+      case 'D':
+       p++;                            /* Just point past it and fall thru */
+      case '0':                                /* Starts with digit */
+      case '1':
+      case '2':  case '3':  case '4':  case '5':
+      case '6':  case '7':  case '8':  case '9':
+       radix = 10;                     /* Decimal */
+       hd = '9';                       /* highest valid digit */
+       break;
+      case 'o':                                /* Starts with o or O */
+      case 'O':
+       radix = 8;                      /* Octal */
+       hd = '7';                       /* highest valid digit */
+       p++;                            /* point past radix indicator */
+       break;
+      case 'x':                                /* Starts with x or X */
+      case 'X':
+       radix = 16;                     /* Hexadecimal */
+       p++;                            /* point past radix indicator */
+       break;
+      default:                         /* All others */
+#ifdef COMMENT
+       *s = p+1;                       /* Treat as quote of next char */
+       return(*p);
+#else
+       return(-1);
+#endif /* COMMENT */
+    }
+    /* For OS/2, there are "wide" characters required for the keyboard
+     * binding, i.e \644 and similar codes larger than 255 (byte).
+     * For this purpose, give up checking for < 256. If someone means
+     * \266 should result in \26 followed by a "6" character, he should
+     * always write \{26}6 anyway.  Now, return only the lower byte of
+     * the result, i.e. 10, but eat up the whole \266 sequence and
+     * put the wide result 266 into a global variable.  Yes, that's not
+     * the most beautiful programming style but requires the least
+     * amount of changes to other routines.
+     */
+    if (radix <= 10) {                 /* Number in radix 8 or 10 */
+       for ( x = y = 0;
+             (*p) && (*p >= '0') && (*p <= hd)
+#ifdef OS2
+                   && (y < 4) && (x*radix < 768);
+              /* the maximum needed value \767 is still only 3 digits long */
+              /* while as octal it requires \1377, i.e. 4 digits */
+#else
+                   && (y < 3) && (x*radix < 256);
+#endif /* OS2 */
+             p++,y++) {
+           x = x * radix + (int) *p - 48;
+       }
+#ifdef OS2
+        wideresult = x;                        /* Remember wide result */
+        x &= 255;
+#endif /* OS2 */
+       if (y == 0 || x > 255) {        /* No valid digits? */
+           *s = p;                     /* point after it */
+           return(-1);                 /* return failure. */
+       }
+    } else if (radix == 16) {          /* Special case for hex */
+       if ((x = unhex(*p++)) < 0) { *s = p - 1; return(-1); }
+       if ((y = unhex(*p++)) < 0) { *s = p - 2; return(-1); }
+       x = ((x << 4) & 0xF0) | (y & 0x0F);
+#ifdef OS2
+        wideresult = x;
+        if ((y = unhex(*p)) >= 0) {
+           p++;
+          wideresult = ((x << 4) & 0xFF0) | (y & 0x0F);
+           x = wideresult & 255;
+        }
+#endif /* OS2 */
+    } else x = -1;
+    if (brace && *p == '}' && x > -1)  /* Point past closing brace, if any */
+      p++;
+    *s = p;                            /* Point to next char after sequence */
+    return(x);                         /* Return value of sequence */
+}
+
+int                                    /* Convert hex string to int */
+#ifdef CK_ANSIC
+unhex(char x)
+#else
+unhex(x) char x;
+#endif /* CK_ANSIC */
+/* unhex */ {
+
+    if (x >= '0' && x <= '9')          /* 0-9 is offset by hex 30 */
+      return(x - 0x30);
+    else if (x >= 'A' && x <= 'F')     /* A-F offset by hex 37 */
+      return(x - 0x37);
+    else if (x >= 'a' && x <= 'f')     /* a-f offset by hex 57 */
+      return(x - 0x57);                        /* (obviously ASCII dependent) */
+    else return(-1);
+}
+
+/* See if argument string is numeric */
+/* Returns 1 if OK, zero if not OK */
+/* If OK, string should be acceptable to atoi() */
+/* Allows leading space, sign */
+
+int
+chknum(s) char *s; {                   /* Check Numeric String */
+    int x = 0;                         /* Flag for past leading space */
+    int y = 0;                         /* Flag for digit seen */
+    char c;
+    debug(F110,"chknum",s,0);
+    while (c = *s++) {                 /* For each character in the string */
+       switch (c) {
+         case SP:                      /* Allow leading spaces */
+         case HT:
+           if (x == 0) continue;
+           else return(0);
+         case '+':                     /* Allow leading sign */
+         case '-':
+           if (x == 0) x = 1;
+           else return(0);
+           break;
+         default:                      /* After that, only decimal digits */
+           if (c >= '0' && c <= '9') {
+               x = y = 1;
+               continue;
+           } else return(0);
+       }
+    }
+    return(y);
+}
+
+/*  L O W E R  --  Lowercase a string  */
+int
+lower(s) char *s; {
+    int n = 0;
+    while (*s) {
+        if (isupper(*s)) *s = tolower(*s);
+        s++, n++;
+    }
+    return(n);
+}
+
+/*  L O O K U P  --  Lookup the string in the given array of strings  */
+/*
+ Call this way:  v = lookup(table,word,n,&x);
+   table - a 'struct keytab' table.
+   word  - the target string to look up in the table.
+   n     - the number of elements in the table.
+   x     - address of an integer for returning the table array index.
+ The keyword table must be arranged in ascending alphabetical order, and
+ all letters must be lowercase.
+ Returns the keyword's associated value ( zero or greater ) if found,
+ with the variable x set to the array index, or:
+  -3 if nothing to look up (target was null),
+  -2 if ambiguous,
+  -1 if not found.
+ A match is successful if the target matches a keyword exactly, or if
+ the target is a prefix of exactly one keyword.  It is ambiguous if the
+ target matches two or more keywords from the table.
+*/
+int
+lookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
+    int i, v, cmdlen;
+/* Lowercase & get length of target, if it's null return code -3. */
+    if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
+/* Not null, look it up */
+    for (i = 0; i < n-1; i++) {
+        if (!strcmp(table[i].kwd,cmd) ||
+           ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
+             strncmp(table[i+1].kwd,cmd,cmdlen))) {
+                *x = i;
+                return(table[i].kwval);
+             }
+        if (v) return(-2);
+    }   
+/* Last (or only) element */
+    if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
+        *x = n-1;
+        return(table[n-1].kwval);
+    } else return(-1);
+}
diff --git a/usr/src/contrib/kermit-5A.188/ckucmd.h b/usr/src/contrib/kermit-5A.188/ckucmd.h
new file mode 100644 (file)
index 0000000..3eb6fdc
--- /dev/null
@@ -0,0 +1,126 @@
+/*  C K U C M D . H  --  Header file for Unix cmd package  */
+/*
+  Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
+  Columbia University Center for Computing Activities.
+  First released January 1985.
+  Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
+  York.  Permission is granted to any individual or institution to use this
+  software as long as it is not sold for profit.  This copyright notice must be
+  retained.  This software may not be included in commercial products without
+  written permission of Columbia University.
+*/
+#ifndef CKUCMD_H
+#define CKUCMD_H
+
+/* Special getchars... */
+#ifdef DYNAMIC                         /* Dynamic command buffers */
+#define DCMDBUF
+/*
+  Use malloc() to allocate the many command-related buffers in ckucmd.c.
+*/
+#endif /* DYNAMIC */
+
+#ifdef VMS
+#define getchar()   vms_getchar()
+#endif /* VMS */
+#ifdef aegis
+#undef getchar
+#define getchar()   coninc(0)
+#endif /* aegis */
+#ifdef AMIGA
+#undef getchar
+#define getchar() coninc(0)
+#endif /* AMIGA */
+
+/* Sizes of things */
+#ifndef CMDDEP
+#define CMDDEP  20                     /* Maximum command recursion depth */
+#endif /* CMDDEP */
+#define HLPLW   78                     /* Width of ?-help line */
+#define HLPCW   19                     /* Width of ?-help column */
+#define HLPBL  100                     /* Help string buffer length */
+#define ATMBL  256                     /* Command atom buffer length*/
+#ifdef NOSPL
+/* No script programming language, save some space */
+#define CMDBL 512                      /* Command buffer length */
+#else
+#define CMDBL 1024                     /* Command buffer length */
+#endif /* NOSPL */
+/* Special characters */
+#define RDIS 0022                      /* Redisplay   (^R) */
+#define LDEL 0025                      /* Delete line (^U) */
+#define WDEL 0027                      /* Delete word (^W) */
+/* Keyword table flags */
+#define CM_INV 1                       /* Invisible keyword */
+#define CM_ABR 2                       /* Abbreviation */
+/* Token flags */
+
+#define CMT_COM 0                      /* Comment (; or #) */
+#define CMT_SHE 1                      /* Shell escape (!) */
+#define CMT_LBL 2                      /* Label (:) */
+#define CMT_FIL 3                      /* Indirect filespec (@) */
+
+/* Keyword Table Template */
+struct keytab {                                /* Keyword table */
+    char *kwd;                         /* Pointer to keyword string */
+    int kwval;                         /* Associated value */
+    int flgs;                          /* Flags (as defined above) */
+};
+
+/* Function prototypes */
+
+#ifdef CK_ANSIC                                /* ANSI C */
+#ifdef M_SYSV                          /* SCO Microsoft C wants no args */
+typedef int (*xx_strp)();
+#else
+typedef int (*xx_strp)(char *, char **, int *);
+#endif /* M_SYSV */
+#else                                  /* Not ANSI C */
+typedef int (*xx_strp)();
+#endif /* CK_ANSIC */
+
+_PROTOTYP( int xxesc, (char **) );
+_PROTOTYP( VOID cmsetp, (char *) );
+_PROTOTYP( VOID cmsavp, (char [], int) );
+_PROTOTYP( VOID prompt, (xx_strp) );
+_PROTOTYP( VOID pushcmd, (void) );
+_PROTOTYP( VOID cmres, (void) );
+_PROTOTYP( VOID cmini, (int) );
+_PROTOTYP( int cmpush, (void) );
+_PROTOTYP( int cmpop, (void) );
+_PROTOTYP( VOID untab, (char *) );
+_PROTOTYP( int cmnum, (char *, char *, int, int *, xx_strp ) );
+_PROTOTYP( int cmofi, (char *, char *, char **, xx_strp ) );
+_PROTOTYP( int cmifi, (char *, char *, char **, int *, xx_strp ) );
+_PROTOTYP( int cmdir, (char *, char *, char **, xx_strp ) );
+_PROTOTYP( int cmfld, (char *, char *, char **, xx_strp ) );
+_PROTOTYP( int cmtxt, (char *, char *, char **, xx_strp ) );
+_PROTOTYP( int cmkey, (struct keytab [], int, char *, char *, xx_strp) );
+_PROTOTYP( int cmkey2,(struct keytab [], int, char *, char *, char *,xx_strp));
+_PROTOTYP( int chktok, (char *) );
+_PROTOTYP( int cmcfm, (void) );
+_PROTOTYP( int rdigits, (char *) );
+_PROTOTYP( int chknum, (char *) );
+_PROTOTYP( int lower, (char *) );
+_PROTOTYP( int lookup, (struct keytab [], char *, int, int *) );
+_PROTOTYP( int ungword, (void) );
+
+#ifdef DCMDBUF
+_PROTOTYP( int cmsetup, (void) );
+#endif /* DCMDBUF */
+
+#endif /* CKUCMD_H */
+
+/* End of ckucmd.h */