1: int cmdmsk = 127; /* Command-terminal-to-C-Kermit character mask */
   2: 
   3: #include "ckcdeb.h"                     /* Formats for debug(), etc. */
   4: _PROTOTYP( int unhex, (char) );
   5: 
   6: #ifndef NOICP     /* The rest only if interactive command parsing selected */
   7: 
   8: char *cmdv = "Command package 5A(053), 21 Nov 92";
   9: 
  10: /*  C K U C M D  --  Interactive command package for Unix  */
  11: 
  12: /*
  13:   Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  14:   Columbia University Center for Computing Activities.
  15:   First released January 1985.
  16:   Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New
  17:   York.  Permission is granted to any individual or institution to use this
  18:   software as long as it is not sold for profit.  This copyright notice must be
  19:   retained.  This software may not be included in commercial products without
  20:   written permission of Columbia University.
  21: */
  22: 
  23: /*
  24: Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP.
  25: Features:
  26: . parses and verifies keywords, filenames, text strings, numbers, other data
  27: . displays appropriate menu or help message when user types "?"
  28: . does keyword and filename completion when user types ESC or TAB
  29: . does partial filename completion
  30: . accepts any unique abbreviation for a keyword
  31: . allows keywords to have attributes, like "invisible" and "abbreviation"
  32: . can supply defaults for fields omitted by user
  33: . provides command line editing (character, word, and line deletion)
  34: . accepts input from keyboard, command files, or redirected stdin
  35: . allows for full or half duplex operation, character or line input
  36: . settable prompt, protected from deletion
  37: 
  38: Functions:
  39:  cmsetp - Set prompt (cmprom is prompt string)
  40:  cmsavp - Save current prompt
  41:  prompt - Issue prompt
  42:  cmini - Clear the command buffer (before parsing a new command)
  43:  cmres - Reset command buffer pointers (before reparsing)
  44:  cmkey - Parse a keyword
  45:  cmnum - Parse a number
  46:  cmifi - Parse an input file name
  47:  cmofi - Parse an output file name
  48:  cmdir - Parse a directory name (UNIX only)
  49:  cmfld - Parse an arbitrary field
  50:  cmtxt - Parse a text string
  51:  cmcfm - Parse command confirmation (end of line)
  52: 
  53: Return codes:
  54:  -3: no input provided when required
  55:  -2: input was invalid (e.g. not a number when a number was required)
  56:  -1: reparse required (user deleted into a preceding field)
  57:   0 or greater: success
  58: See individual functions for greater detail.
  59: 
  60: Before using these routines, the caller should #include ckucmd.h, and set the
  61: program's prompt by calling cmsetp().  If the file parsing functions cmifi,
  62: cmofi, or cmdir are to be used, this module must be linked with a ck?fio file
  63: system support module for the appropriate system, e.g. ckufio for Unix.  If
  64: the caller puts the terminal in character wakeup ("cbreak") mode with no echo,
  65: then these functions will provide line editing -- character, word, and line
  66: deletion, as well as keyword and filename completion upon ESC and help
  67: strings, keyword, or file menus upon '?'.  If the caller puts the terminal
  68: into character wakeup/noecho mode, care should be taken to restore it before
  69: exit from or interruption of the program.  If the character wakeup mode is not
  70: set, the system's own line editor may be used.
  71: 
  72: NOTE: Contrary to expectations, many #ifdef's have been added to this module.
  73: Any operation requiring an #ifdef (like clear screen, get character from
  74: keyboard, erase character from screen, etc) should eventually be turned into a
  75: call to a function that is defined in ck?tio.c, but then all the ck?tio.c
  76: modules would have to be changed...
  77: */
  78: 
  79: /* Includes */
  80: 
  81: #include "ckcker.h"         /* (===OS2 addition===) */
  82: #include "ckcasc.h"         /* ASCII character symbols */
  83: #include "ckucmd.h"                     /* Command parsing definitions */
  84: #include <errno.h>          /* Error number symbols */
  85: 
  86: #ifdef OSK
  87: #define cc ccount           /* OS-9/68K compiler bug */
  88: #endif /* OSK */
  89: 
  90: #ifdef GEMDOS               /* Atari ST */
  91: #ifdef putchar
  92: #undef putchar
  93: #endif /* putchar */
  94: #define putchar(x) conoc(x)     /* Why doesn't everyone do this? */
  95: #endif /* GEMDOS */
  96: 
  97: /* Local variables */
  98: 
  99: static
 100: int psetf = 0,                          /* Flag that prompt has been set */
 101:     cc = 0,                             /* Character count */
 102:     dpx = 0,                            /* Duplex (0 = full) */
 103:     inword = 0;             /* In the middle of getting a word */
 104: 
 105: static
 106: int hw = HLPLW,                         /* Help line width */
 107:     hc = HLPCW,                         /* Help line column width */
 108:     hh,                                 /* Current help column number */
 109:     hx;                                 /* Current help line position */
 110: 
 111: #define PROML 160                       /* Maximum length for prompt */
 112: 
 113: char cmprom[PROML+1];                   /* Program's prompt */
 114: char cmprxx[PROML+1];                   /* Program's prompt, unevaluated */
 115: char *dfprom = "Command? ";             /* Default prompt */
 116: 
 117: int cmflgs;                             /* Command flags */
 118: int cmfsav;             /* A saved version of them */
 119: 
 120: #ifdef DCMDBUF
 121: char *cmdbuf;               /* Command buffer */
 122: char *savbuf;               /* Help string buffer */
 123: char *hlpbuf;               /* Atom buffer */
 124: char *atmbuf;               /* File name buffer */
 125: char *atxbuf;               /* For expanding the atom buffer */
 126: int atxn;               /* Length of expansion buffer */
 127: char *atybuf;               /* For copying atom buffer */
 128: char *filbuf;               /* Buffer to save copy of command */
 129: #else
 130: char cmdbuf[CMDBL+4];                   /* Command buffer */
 131: char hlpbuf[HLPBL+4];                   /* Help string buffer */
 132: char atmbuf[ATMBL+4];                   /* Atom buffer */
 133: char filbuf[ATMBL+4];                   /* File name buffer */
 134: char atxbuf[CMDBL+4];                   /* For expanding the atom buffer */
 135: int atxn;                           /* Length of expansion buffer */
 136: char atybuf[ATMBL+4];                   /* For copying atom buffer */
 137: char savbuf[CMDBL+4];                   /* Buffer to save copy of command */
 138: #endif /* DCMDBUF */
 139: 
 140: /* Command buffer pointers */
 141: 
 142: static char *bp,                        /* Current command buffer position */
 143:     *pp,                                /* Start of current field */
 144:     *np;                                /* Start of next field */
 145: 
 146: static int ungw;            /* For ungetting words */
 147: 
 148: _PROTOTYP( VOID addhlp, (char *) );
 149: _PROTOTYP( VOID clrhlp, (void) );
 150: _PROTOTYP( VOID dmphlp, (void) );
 151: _PROTOTYP( int gtword, (void) );
 152: _PROTOTYP( int addbuf, (char *) );
 153: _PROTOTYP( int setatm, (char *) );
 154: _PROTOTYP( int cmdgetc, (void) );
 155: _PROTOTYP( VOID cmdnewl, (char) );
 156: _PROTOTYP( VOID cmdchardel, (void) );
 157: _PROTOTYP( VOID cmdecho, (char, int) );
 158: _PROTOTYP( static int test, (int, int) );
 159: #ifdef GEMDOS
 160: _PROTOTYP( extern char *strchr, (char *, int) );
 161: #endif /* GEMDOS */
 162: 
 163: /*  T E S T  --  Bit test  */
 164: 
 165: static int
 166: test(x,m) int x, m; { /*  Returns 1 if any bits from m are on in x, else 0  */
 167:     return((x & m) ? 1 : 0);
 168: }
 169: 
 170: /*  C M S E T U P  --  Set up command buffers  */
 171: 
 172: #ifdef DCMDBUF
 173: int
 174: cmsetup() {
 175:     if (!(cmdbuf = malloc(CMDBL + 4))) return(-1);
 176:     if (!(savbuf = malloc(CMDBL + 4))) return(-1);
 177:     savbuf[0] = '\0';
 178:     if (!(hlpbuf = malloc(HLPBL + 4))) return(-1);
 179:     if (!(atmbuf = malloc(ATMBL + 4))) return(-1);
 180:     if (!(atxbuf = malloc(CMDBL + 4))) return(-1);
 181:     if (!(atybuf = malloc(ATMBL + 4))) return(-1);
 182:     if (!(filbuf = malloc(ATMBL + 4))) return(-1);
 183:     return(0);
 184: }
 185: #endif /* DCMDBUF */
 186: 
 187: /*  C M S E T P  --  Set the program prompt.  */
 188: 
 189: VOID
 190: cmsetp(s) char *s; {
 191:     strncpy(cmprxx,s,PROML - 1);
 192:     cmprxx[PROML] = NUL;
 193:     psetf = 1;                          /* Flag that prompt has been set. */
 194: }
 195: /*  C M S A V P  --  Save a copy of the current prompt.  */
 196: 
 197: VOID
 198: #ifdef CK_ANSIC
 199: cmsavp(char s[], int n)
 200: #else
 201: cmsavp(s,n) char s[]; int n;
 202: #endif /* CK_ANSIC */
 203: /* cmsavp */ {
 204:     strncpy(s,cmprxx,n-1);
 205:     s[n-1] = NUL;
 206: }
 207: 
 208: /*  P R O M P T  --  Issue the program prompt.  */
 209: 
 210: VOID
 211: prompt(f) xx_strp f; {
 212:     char *sx, *sy; int n;
 213: 
 214:     if (psetf == 0) cmsetp(dfprom);     /* If no prompt set, set default. */
 215: 
 216:     sx = cmprxx;            /* Unevaluated copy */
 217:     if (f) {                /* If conversion function given */
 218:     sy = cmprom;            /* Evaluate it */
 219:     n = PROML;
 220:     if ((*f)(sx,&sy,&n) < 0)    /* If evaluation failed */
 221:       sx = cmprxx;          /* revert to unevaluated copy */
 222:     else
 223:       sx = cmprom;
 224:     }
 225: #ifdef OSK
 226:     fputs(sx, stdout);
 227: #else
 228: #ifdef MAC
 229:     printf("%s", sx);
 230: #else
 231:     printf("\r%s",sx);          /* Print the prompt. */
 232:     fflush(stdout);         /* Now! */
 233: #endif /* MAC */
 234: #endif /* OSK */
 235: }
 236: 
 237: #ifndef NOSPL
 238: VOID
 239: pushcmd() {             /* For use with IF command. */
 240:     strcpy(savbuf,np);          /* Save the dependent clause,  */
 241:     cmres();                /* and clear the command buffer. */
 242:     debug(F110, "pushcmd: savbuf:", savbuf, 0);
 243: }
 244: #endif /* NOSPL */
 245: 
 246: #ifdef COMMENT
 247: /* no longer used... */
 248: VOID
 249: popcmd() {
 250:     strcpy(cmdbuf,savbuf);      /* Put back the saved material */
 251:     *savbuf = '\0';         /* and clear the save buffer */
 252:     cmres();
 253: }
 254: #endif /* COMMENT */
 255: 
 256: /*  C M R E S  --  Reset pointers to beginning of command buffer.  */
 257: 
 258: VOID
 259: cmres() {
 260:     inword = cc = 0;            /* Reset character counter. */
 261:     pp = np = bp = cmdbuf;              /* Point to command buffer. */
 262:     cmflgs = -5;                        /* Parse not yet started. */
 263:     ungw = 0;               /* Don't need to unget a word. */
 264: }
 265: 
 266: /*  C M I N I  --  Clear the command and atom buffers, reset pointers.  */
 267: 
 268: /*
 269: The argument specifies who is to echo the user's typein --
 270:   1 means the cmd package echoes
 271:   0 somebody else (system, front end, terminal) echoes
 272: */
 273: VOID
 274: cmini(d) int d; {
 275:     for (bp = cmdbuf; bp < cmdbuf+CMDBL; bp++) *bp = NUL;
 276:     *atmbuf = NUL;
 277:     dpx = d;
 278:     cmres();
 279: }
 280: 
 281: #ifndef NOSPL
 282: /* The following bits are to allow the command package to call itself */
 283: /* in the middle of a parse.  To do this, begin by calling cmpush, and */
 284: /* end by calling cmpop. */
 285: 
 286: #ifdef DCMDBUF
 287: struct cmp {
 288:     int i[5];               /* stack for integers */
 289:     char *c[3];             /* stack for pointers */
 290:     char *b[8];             /* stack for buffer contents */
 291: };
 292: struct cmp *cmp = 0;
 293: #else
 294: int cmp_i[CMDDEP+1][5];         /* Stack for integers */
 295: char *cmp_c[CMDDEP+1][5];       /* for misc pointers */
 296: char *cmp_b[CMDDEP+1][7];       /* for buffer contents pointers */
 297: #endif /* DCMDBUF */
 298: 
 299: int cmddep = -1;            /* Current stack depth */
 300: 
 301: int
 302: cmpush() {              /* Save the command environment */
 303:     char *cp;               /* Character pointer */
 304: 
 305:     if (cmddep >= CMDDEP)       /* Enter a new command depth */
 306:       return(-1);
 307:     cmddep++;
 308:     debug(F101,"&cmpush","",cmddep);
 309: 
 310: #ifdef DCMDBUF
 311:     /* allocate memory for cmp if not already done */
 312:     if (!cmp && !(cmp = (struct cmp *) malloc(sizeof(struct cmp)*(CMDDEP+1))))
 313:       fatal("cmpush: no memory for cmp");
 314:     cmp[cmddep].i[0] = cmflgs;      /* First do the global ints */
 315:     cmp[cmddep].i[1] = cmfsav;
 316:     cmp[cmddep].i[2] = atxn;
 317:     cmp[cmddep].i[3] = ungw;
 318: 
 319:     cmp[cmddep].c[0] = bp;      /* Then the global pointers */
 320:     cmp[cmddep].c[1] = pp;
 321:     cmp[cmddep].c[2] = np;
 322: #else
 323:     cmp_i[cmddep][0] = cmflgs;      /* First do the global ints */
 324:     cmp_i[cmddep][1] = cmfsav;
 325:     cmp_i[cmddep][2] = atxn;
 326:     cmp_i[cmddep][3] = ungw;
 327: 
 328:     cmp_c[cmddep][0] = bp;      /* Then the global pointers */
 329:     cmp_c[cmddep][1] = pp;
 330:     cmp_c[cmddep][2] = np;
 331: #endif /* DCMDBUF */
 332: 
 333:     /* Now the buffers themselves.  A lot of repititious code... */
 334: 
 335: #ifdef DCMDBUF
 336:     cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
 337:     if (cp) strcpy(cp,cmdbuf);
 338:     cmp[cmddep].b[0] = cp;
 339:     if (cp == NULL) return(-1);
 340: 
 341:     cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
 342:     if (cp) strcpy(cp,savbuf);
 343:     cmp[cmddep].b[1] = cp;
 344:     if (cp == NULL) return(-1);
 345: 
 346:     cp = malloc((int)strlen(hlpbuf)+1); /* 2: Help string buffer */
 347:     if (cp) strcpy(cp,hlpbuf);
 348:     cmp[cmddep].b[2] = cp;
 349:     if (cp == NULL) return(-1);
 350: 
 351:     cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
 352:     if (cp) strcpy(cp,atmbuf);
 353:     cmp[cmddep].b[3] = cp;
 354:     if (cp == NULL) return(-1);
 355: 
 356:     cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
 357:     if (cp) strcpy(cp,atxbuf);
 358:     cmp[cmddep].b[4] = cp;
 359:     if (cp == NULL) return(-1);
 360: 
 361:     cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
 362:     if (cp) strcpy(cp,atybuf);
 363:     cmp[cmddep].b[5] = cp;
 364:     if (cp == NULL) return(-1);
 365: 
 366:     cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
 367:     if (cp) strcpy(cp,filbuf);
 368:     cmp[cmddep].b[6] = cp;
 369:     if (cp == NULL) return(-1);
 370: #else
 371:     cp = malloc((int)strlen(cmdbuf)+1); /* 0: Command buffer */
 372:     if (cp) strcpy(cp,cmdbuf);
 373:     cmp_b[cmddep][0] = cp;
 374:     if (cp == NULL) return(-1);
 375: 
 376:     cp = malloc((int)strlen(savbuf)+1); /* 1: Save buffer */
 377:     if (cp) strcpy(cp,savbuf);
 378:     cmp_b[cmddep][1] = cp;
 379:     if (cp == NULL) return(-1);
 380: 
 381:     cp = malloc((int)strlen(hlpbuf)+1); /* 2: Help string buffer */
 382:     if (cp) strcpy(cp,hlpbuf);
 383:     cmp_b[cmddep][2] = cp;
 384:     if (cp == NULL) return(-1);
 385: 
 386:     cp = malloc((int)strlen(atmbuf)+1); /* 3: Atom buffer */
 387:     if (cp) strcpy(cp,atmbuf);
 388:     cmp_b[cmddep][3] = cp;
 389:     if (cp == NULL) return(-1);
 390: 
 391:     cp = malloc((int)strlen(atxbuf)+1); /* 4: Expansion buffer */
 392:     if (cp) strcpy(cp,atxbuf);
 393:     cmp_b[cmddep][4] = cp;
 394:     if (cp == NULL) return(-1);
 395: 
 396:     cp = malloc((int)strlen(atybuf)+1); /* 5: Atom buffer copy */
 397:     if (cp) strcpy(cp,atybuf);
 398:     cmp_b[cmddep][5] = cp;
 399:     if (cp == NULL) return(-1);
 400: 
 401:     cp = malloc((int)strlen(filbuf)+1); /* 6: File name buffer */
 402:     if (cp) strcpy(cp,filbuf);
 403:     cmp_b[cmddep][6] = cp;
 404:     if (cp == NULL) return(-1);
 405: #endif /* DCMDBUF */
 406: 
 407:     cmini(dpx);             /* Initize the command parser */
 408:     return(0);
 409: }
 410: 
 411: int
 412: cmpop() {               /* Restore the command environment */
 413:     debug(F101,"&cmpop","",cmddep);
 414:     if (cmddep < 0) return(-1);     /* Don't pop too much! */
 415: 
 416: #ifdef DCMDBUF
 417:     cmflgs = cmp[cmddep].i[0];      /* First do the global ints */
 418:     cmfsav = cmp[cmddep].i[1];
 419:     atxn = cmp[cmddep].i[2];
 420:     ungw = cmp[cmddep].i[3];
 421: 
 422:     bp = cmp[cmddep].c[0];      /* Then the global pointers */
 423:     pp = cmp[cmddep].c[1];
 424:     np = cmp[cmddep].c[2];
 425: #else
 426:     cmflgs = cmp_i[cmddep][0];      /* First do the global ints */
 427:     cmfsav = cmp_i[cmddep][1];
 428:     atxn = cmp_i[cmddep][2];
 429:     ungw = cmp_i[cmddep][3];
 430: 
 431:     bp = cmp_c[cmddep][0];      /* Then the global pointers */
 432:     pp = cmp_c[cmddep][1];
 433:     np = cmp_c[cmddep][2];
 434: #endif /* DCMDBUF */
 435: 
 436:     /* Now the buffers themselves. */
 437: 
 438: #ifdef DCMDBUF
 439:     if (cmp[cmddep].b[0]) {
 440:     strcpy(cmdbuf,cmp[cmddep].b[0]); /* 0: Command buffer */
 441:     free(cmp[cmddep].b[0]);
 442:     cmp[cmddep].b[0] = NULL;
 443:     }
 444:     if (cmp[cmddep].b[1]) {
 445:     strcpy(savbuf,cmp[cmddep].b[1]); /* 1: Save buffer */
 446:     free(cmp[cmddep].b[1]);
 447:     cmp[cmddep].b[1] = NULL;
 448:     }
 449:     if (cmp[cmddep].b[2]) {
 450:     strcpy(hlpbuf,cmp[cmddep].b[2]); /* 2: Help buffer */
 451:     free(cmp[cmddep].b[2]);
 452:     cmp[cmddep].b[2] = NULL;
 453:     }
 454:     if (cmp[cmddep].b[3]) {
 455:     strcpy(atmbuf,cmp[cmddep].b[3]); /* 3: Atomic buffer! */
 456:     free(cmp[cmddep].b[3]);
 457:     cmp[cmddep].b[3] = NULL;
 458:     }
 459:     if (cmp[cmddep].b[4]) {
 460:     strcpy(atxbuf,cmp[cmddep].b[4]); /* 4: eXpansion buffer */
 461:     free(cmp[cmddep].b[4]);
 462:     cmp[cmddep].b[4] = NULL;
 463:     }
 464:     if (cmp[cmddep].b[5]) {
 465:     strcpy(atybuf,cmp[cmddep].b[5]); /* 5: Atom buffer copY */
 466:     free(cmp[cmddep].b[5]);
 467:     cmp[cmddep].b[5] = NULL;
 468:     }
 469:     if (cmp[cmddep].b[6]) {
 470:     strcpy(filbuf,cmp[cmddep].b[6]); /* 6: Filename buffer */
 471:     free(cmp[cmddep].b[6]);
 472:     cmp[cmddep].b[6] = NULL;
 473:     }
 474: #else
 475:     if (cmp_b[cmddep][0]) {
 476:     strcpy(cmdbuf,cmp_b[cmddep][0]); /* 0: Command buffer */
 477:     free(cmp_b[cmddep][0]);
 478:     cmp_b[cmddep][0] = NULL;
 479:     }
 480:     if (cmp_b[cmddep][1]) {
 481:     strcpy(savbuf,cmp_b[cmddep][1]); /* 1: Save buffer */
 482:     free(cmp_b[cmddep][1]);
 483:     cmp_b[cmddep][1] = NULL;
 484:     }
 485:     if (cmp_b[cmddep][2]) {
 486:     strcpy(hlpbuf,cmp_b[cmddep][2]); /* 2: Help buffer */
 487:     free(cmp_b[cmddep][2]);
 488:     cmp_b[cmddep][2] = NULL;
 489:     }
 490:     if (cmp_b[cmddep][3]) {
 491:     strcpy(atmbuf,cmp_b[cmddep][3]); /* 3: Atomic buffer! */
 492:     free(cmp_b[cmddep][3]);
 493:     cmp_b[cmddep][3] = NULL;
 494:     }
 495:     if (cmp_b[cmddep][4]) {
 496:     strcpy(atxbuf,cmp_b[cmddep][4]); /* 4: eXpansion buffer */
 497:     free(cmp_b[cmddep][4]);
 498:     cmp_b[cmddep][4] = NULL;
 499:     }
 500:     if (cmp_b[cmddep][5]) {
 501:     strcpy(atybuf,cmp_b[cmddep][5]); /* 5: Atom buffer copY */
 502:     free(cmp_b[cmddep][5]);
 503:     cmp_b[cmddep][5] = NULL;
 504:     }
 505:     if (cmp_b[cmddep][6]) {
 506:     strcpy(filbuf,cmp_b[cmddep][6]); /* 6: Filename buffer */
 507:     free(cmp_b[cmddep][6]);
 508:     cmp_b[cmddep][6] = NULL;
 509:     }
 510: #endif /* DCMDBUF */
 511: 
 512:     cmddep--;               /* Rise, rise */
 513:     debug(F101,"&cmpop","",cmddep);
 514:     return(cmddep);
 515: }
 516: #endif /* NOSPL */
 517: 
 518: #ifdef COMMENT
 519: VOID
 520: stripq(s) char *s; {                    /* Function to strip '\' quotes */
 521:     char *t;
 522:     while (*s) {
 523:         if (*s == CMDQ) {
 524:             for (t = s; *t != '\0'; t++) *t = *(t+1);
 525:         }
 526:         s++;
 527:     }
 528: }
 529: #endif /* COMMENT */
 530: 
 531: /* Convert tabs to spaces, one for one */
 532: VOID
 533: untab(s) char *s; {
 534:     while (*s) {
 535:     if (*s == HT) *s = SP;
 536:     s++;
 537:     }
 538: }
 539: 
 540: /*  C M N U M  --  Parse a number in the indicated radix  */
 541: 
 542: /*
 543:  The only radix allowed in unquoted numbers is 10.
 544:  Parses unquoted numeric strings in base 10.
 545:  Parses backslash-quoted numbers in the radix indicated by the quote:
 546:    \nnn = \dnnn = decimal, \onnn = octal, \xnn = Hexadecimal.
 547:  If these fail, then if a preprocessing function is supplied, that is applied
 548:  and then a second attempt is made to parse an unquoted decimal string.
 549: 
 550:  Returns:
 551:    -3 if no input present when required,
 552:    -2 if user typed an illegal number,
 553:    -1 if reparse needed,
 554:     0 otherwise, with argument n set to the number that was parsed
 555: */
 556: int
 557: cmnum(xhlp,xdef,radix,n,f) char *xhlp, *xdef; int radix, *n; xx_strp f; {
 558:     int x; char *s, *zp, *zq;
 559: 
 560:     if (radix != 10) {                  /* Just do base 10 */
 561:         printf("cmnum: illegal radix - %d\n",radix);
 562:         return(-1);
 563:     }
 564:     x = cmfld(xhlp,xdef,&s,(xx_strp)0);
 565:     debug(F101,"cmnum: cmfld","",x);
 566:     if (x < 0) return(x);       /* Parse a field */
 567:     zp = atmbuf;
 568: 
 569:     if (chknum(zp)) {           /* Check for decimal number */
 570:         *n = atoi(zp);          /* Got one, we're done. */
 571:     debug(F101,"cmnum 1st chknum ok","",*n);
 572:         return(0);
 573:     } else if ((x = xxesc(&zp)) > -1) { /* Check for backslash escape */
 574: 
 575: #ifndef OS2
 576:     *n = x;
 577: #else
 578:     *n = wideresult;
 579: #endif /* OS2 */
 580: 
 581:     debug(F101,"cmnum xxesc ok","",*n);
 582:     return(*zp ? -2 : 0);
 583:     } else if (f) {         /* If conversion function given */
 584:         zp = atmbuf;            /* Try that */
 585:     zq = atxbuf;
 586:     atxn = CMDBL;
 587:     (*f)(zp,&zq,&atxn);     /* Convert */
 588:     zp = atxbuf;
 589:     }
 590:     debug(F110,"cmnum zp",zp,0);
 591:     if (chknum(zp)) {           /* Check again for decimal number */
 592:         *n = atoi(zp);          /* Got one, we're done. */
 593:     debug(F101,"cmnum 2nd chknum ok","",*n);
 594:         return(0);
 595:     } else {                /* Not numeric */
 596:     return(-2);
 597:     }
 598: }
 599: 
 600: /*  C M O F I  --  Parse the name of an output file  */
 601: 
 602: /*
 603:  Depends on the external function zchko(); if zchko() not available, use
 604:  cmfld() to parse output file names.
 605: 
 606:  Returns
 607:    -3 if no input present when required,
 608:    -2 if permission would be denied to create the file,
 609:    -1 if reparse needed,
 610:     0 or 1 otherwise, with xp pointing to name.
 611: */
 612: int
 613: cmofi(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
 614:     int x; char *s, *zq;
 615: #ifdef DTILDE
 616:     _PROTOTYP( char * tilde_expand, (char *) );
 617:     char *dirp;
 618: #endif
 619: 
 620:     if (*xhlp == NUL) xhlp = "Output file";
 621:     *xp = "";
 622: 
 623:     if ((x = cmfld(xhlp,xdef,&s,(xx_strp)0)) < 0) return(x);
 624: 
 625:     if (f) {                /* If a conversion function is given */
 626:     zq = atxbuf;
 627:     atxn = CMDBL;
 628:     if ((x = (*f)(s,&zq,&atxn)) < 0) return(-2);
 629:     s = atxbuf;
 630:     }
 631: 
 632: #ifdef DTILDE
 633:     dirp = tilde_expand(s);     /* Expand tilde, if any, */
 634:     if (*dirp != '\0') setatm(dirp);    /* right in the atom buffer. */
 635:     s = atmbuf;
 636: #endif
 637: 
 638:     if (iswild(s)) {
 639:         printf("?Wildcards not allowed - %s\n",s);
 640:         return(-2);
 641:     }
 642:     if (strcmp(s,CTTNAM) && (zchko(s) < 0)) { /* ok to write to tty */
 643:         printf("?Write permission denied - %s\n",s);
 644:         return(-9);
 645:     } else {
 646:         *xp = s;
 647:         return(x);
 648:     }
 649: }
 650: 
 651: 
 652: /*  C M I F I  --  Parse the name of an existing file  */
 653: 
 654: /*
 655:  This function depends on the external functions:
 656:    zchki()  - Check if input file exists and is readable.
 657:    zxpand() - Expand a wild file specification into a list.
 658:    znext()  - Return next file name from list.
 659:  If these functions aren't available, then use cmfld() to parse filenames.
 660: */
 661: /*
 662:  Returns
 663:    -4 EOF
 664:    -3 if no input present when required,
 665:    -2 if file does not exist or is not readable,
 666:    -1 if reparse needed,
 667:     0 or 1 otherwise, with:
 668:         xp pointing to name,
 669:         wild = 1 if name contains '*' or '?', 0 otherwise.
 670: */
 671: int
 672: cmifi(xhlp,xdef,xp,wild,f) char *xhlp, *xdef, **xp; int *wild; xx_strp f; {
 673:     int i, x, xc; long y; char *sp, *zq, *sv;
 674: #ifdef DTILDE
 675:     char *tilde_expand(), *dirp;
 676: #endif /* DTILDE */
 677: 
 678: #ifndef NOPARTIAL
 679:     extern char *mtchs[];
 680: #endif /* NOPARTIAL */
 681: 
 682:     inword = cc = xc = 0;       /* Initialize counts & pointers */
 683:     *xp = "";
 684:     if ((x = cmflgs) != 1) {            /* Already confirmed? */
 685:         x = gtword();                   /* No, get a word */
 686:     } else {
 687:         setatm(xdef);           /* If so, use default, if any. */
 688:     }
 689: 
 690:     *xp = atmbuf;                       /* Point to result. */
 691: 
 692:     while (1) {
 693:         xc += cc;                       /* Count the characters. */
 694:         debug(F111,"cmifi gtword",atmbuf,xc);
 695:         switch (x) {
 696:             case -4:                    /* EOF */
 697:             case -2:                    /* Out of space. */
 698:             case -1:                    /* Reparse needed */
 699:                 return(x);
 700:             case 0:                     /* SP or NL */
 701:             case 1:
 702:                 if (xc == 0) *xp = xdef;     /* If no input, return default. */
 703:                 if (**xp == NUL) return(-3); /* If field empty, return -3. */
 704: 
 705:         if (f) {        /* If a conversion function is given */
 706:             zq = atxbuf;    /* ... */
 707:             atxn = CMDBL;
 708:             if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
 709:             *xp = atxbuf;
 710:         }
 711:         debug(F110,"cmifi atxbuf",atxbuf,0);
 712: #ifdef COMMENT
 713: /* don't need this stuff, zxpand does it now. */
 714: #ifdef DTILDE
 715: 
 716:         dirp = tilde_expand(*xp);    /* Expand tilde, if any, */
 717:         if (*dirp != '\0') setatm(dirp); /* right in atom buffer. */
 718:         *xp = atmbuf;
 719: #endif /* DTILDE */
 720: 
 721:                 /* If filespec is wild, see if there are any matches */
 722: 
 723:                 *wild = iswild(*xp);
 724:                 debug(F101,"cmifi wild","",*wild);
 725:                 if (*wild != 0) {
 726: #endif /* COMMENT */
 727:             sv = malloc((int)strlen(*xp)+1); /* Make a safe copy */
 728:             if (!sv) {
 729:             printf("?malloc error 73, cmifi\n");
 730:             return(-9);
 731:             }
 732:             strcpy(sv,*xp);
 733:             debug(F110,"cmifi sv",sv,0);
 734:                     y = zxpand(*xp);
 735:             *wild = (y > 1);
 736:             debug(F111,"cmifi sv wild",sv,*wild);
 737:                     if (y == 0) {
 738:                         printf("?No files match - %s\n",*xp);
 739:                         return(-9);
 740:                     } else if (y < 0) {
 741:                         printf("?Too many files match - %s\n",*xp);
 742:                         return(-9);
 743:             } else if (y > 1) return(x);
 744: #ifdef COMMENT
 745:                 }
 746: #endif
 747:                 /* If not wild, see if it exists and is readable. */
 748: 
 749:         debug(F111,"cmifi sv not wild",sv,*wild);
 750: 
 751:         znext(*xp);     /* Get first (only?) matching file */
 752:                 y = zchki(*xp);     /* Check its accessibility */
 753:         zxpand(sv);     /* Rewind so next znext() gets 1st */
 754:         free(sv);       /* done with this */
 755:                 if (y == -3) {
 756:                     printf("?Read permission denied - %s\n",*xp);
 757:                     return(-9);
 758:                 } else if (y == -2) {
 759:                     printf("?File not readable - %s\n",*xp);
 760:                     return(-9);
 761:                 } else if (y < 0) {
 762:                     printf("?File not found - %s\n",*xp);
 763:                     return(-9);
 764:                 }
 765:                 return(x);
 766: 
 767: #ifndef MAC
 768:             case 2:                     /* ESC */
 769:         debug(F101,"cmifi esc, xc","",xc);
 770:                 if (xc == 0) {
 771:                     if (*xdef != '\0') {
 772:                         printf("%s ",xdef); /* If at beginning of field, */
 773: #ifdef GEMDOS
 774:             fflush(stdout);
 775: #endif /* GEMDOS */
 776:             inword = cmflgs = 0;
 777:                         addbuf(xdef);   /* supply default. */
 778:                         setatm(xdef);
 779:                     } else {            /* No default */
 780:                         putchar(BEL);
 781:                     }
 782:                     break;
 783:                 }
 784:         if (f) {        /* If a conversion function is given */
 785:             zq = atxbuf;    /* ... */
 786:             atxn = CMDBL;
 787:             if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
 788:                     /* reduce cc by number of \\ consumed by conversion */
 789:             /* function (needed for OS/2, where \ is path separator) */
 790:                     cc -= (strlen(*xp) - strlen(atxbuf));
 791:             *xp = atxbuf;
 792:         }
 793: /* #ifdef COMMENT */
 794: #ifdef DTILDE
 795:         dirp = tilde_expand(*xp);    /* Expand tilde, if any, */
 796:         if (*dirp != '\0') setatm(dirp); /* in the atom buffer. */
 797:                 *xp = atmbuf;
 798: #endif /* DTILDE */
 799: /* #endif */
 800:                 sp = *xp + cc;
 801: #ifdef datageneral
 802:                 *sp++ = '+';        /* Data General AOS wildcard */
 803: #else
 804:                 *sp++ = '*';        /* Others */
 805: #endif /* datageneral */
 806:                 *sp-- = '\0';
 807: #ifdef GEMDOS
 808:         if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */
 809:           strcat(*xp, ".*");    /* abc -> abc*.* */
 810: #endif /* GEMDOS */
 811:                 y = zxpand(*xp);    /* Add wildcard and expand list. */
 812:         if (y > 0) strcpy(filbuf,mtchs[0]);
 813:         else *filbuf = '\0';
 814:                 *sp = '\0';             /* Remove wildcard. */
 815:         *wild = (y > 1);
 816:                 if (y == 0) {
 817:                     printf("?No files match - %s\n",atmbuf);
 818:                     return(-9);
 819:                 } else if (y < 0) {
 820:                     printf("?Too many files match - %s\n",atmbuf);
 821:                     return(-9);
 822:                 } else if (y > 1) {     /* Not unique. */
 823: #ifndef NOPARTIAL
 824: /* Partial filename completion */
 825:             int i, j, k; char c;
 826:             k = 0;
 827:             debug(F111,"cmifi partial",filbuf,cc);
 828:             for (i = cc; (c = filbuf[i]); i++) {
 829:             for (j = 1; j < y; j++)
 830:               if (mtchs[j][i] != c) break;
 831:             if (j == y) k++;
 832:             else filbuf[i] = filbuf[i+1] = NUL;
 833:             }
 834:             debug(F111,"cmifi partial k",filbuf,k);
 835:             if (k > 0) {    /* Got more characters */
 836:             sp = filbuf + cc; /* Point to new ones */
 837: #ifdef VMS
 838:             for (i = 0; i < cc; i++) {
 839:                 cmdchardel(); /* Back up over old partial spec */
 840:                 bp--;
 841:             }
 842:             sp = filbuf;    /* Point to new word start */
 843:             debug(F100,"cmifi vms erase ok","",0);
 844: #endif /* VMS */
 845:             cc = k;     /* How many new ones we just got */
 846:             printf("%s",sp);        /* Print them */
 847:             while (*bp++ = *sp++) ; /* Copy to command buffer */
 848:             bp--;                   /* Back up over NUL */
 849:             debug(F110,"cmifi partial cmdbuf",cmdbuf,0);
 850:             setatm(filbuf);
 851:             debug(F111,"cmifi partial atmbuf",atmbuf,cc);
 852:             *xp = atmbuf;
 853:             }
 854: #endif /* NOPARTIAL */
 855:             putchar(BEL);   /* Beep because not unique. */
 856:                 } else {                /* Unique, complete it.  */
 857:                     sp = filbuf + cc;   /* Point past what user typed. */
 858: #ifdef VMS
 859:             for (i = 0; i < cc; i++) {
 860:             cmdchardel();   /* Back up over old partial spec */
 861:             bp--;
 862:             }
 863:             sp = filbuf;    /* Point to new word start */
 864: #endif /* VMS */
 865:                     printf("%s ",sp);   /* Complete the name. */
 866: #ifdef GEMDOS
 867:             fflush(stdout);
 868: #endif /* GEMDOS */
 869:                     addbuf(sp);         /* Add the characters to cmdbuf. */
 870:                     setatm(filbuf); /* And to atmbuf. */
 871:             inword = cmflgs = 0;
 872:                     *xp = atmbuf;       /* Return pointer to atmbuf. */
 873:                     return(0);
 874:                 }
 875:                 break;
 876: 
 877:             case 3:                     /* Question mark */
 878:                 if (*xhlp == NUL)
 879:           printf(" Input file specification");
 880:                 else
 881:           printf(" %s",xhlp);
 882: #ifdef GEMDOS
 883:         fflush(stdout);
 884: #endif /* GEMDOS */
 885:                 if (xc > 0) {
 886:             if (f) {        /* If a conversion function is given */
 887:             zq = atxbuf;    /* ... */
 888:             atxn = CMDBL;
 889:             if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
 890:             *xp = atxbuf;
 891:             }
 892: #ifdef DTILDE
 893:             dirp = tilde_expand(*xp);    /* Expand tilde, if any */
 894:             if (*dirp != '\0') setatm(dirp);
 895:             *xp = atmbuf;
 896: #endif
 897:             debug(F111,"cmifi ? *xp, cc",*xp,cc);
 898:                     sp = *xp + cc;  /* Insert "*" at end */
 899: #ifdef datageneral
 900:                     *sp++ = '+';        /* Insert +, the DG wild card */
 901: #else
 902:                     *sp++ = '*';
 903: #endif /* datageneral */
 904:                     *sp-- = '\0';
 905: #ifdef GEMDOS
 906:             if (! strchr(*xp, '.')) /* abde.e -> abcde.e* */
 907:               strcat(*xp, ".*");    /* abc -> abc*.* */
 908: #endif /* GEMDOS */
 909:             debug(F110,"cmifi ? wild",*xp,0);
 910:                     y = zxpand(*xp);
 911:                     *sp = '\0';
 912:                     if (y == 0) {
 913:                         printf("?No files match - %s\n",atmbuf);
 914:                         return(-9);
 915:                     } else if (y < 0) {
 916:                         printf("?Too many files match - %s\n",atmbuf);
 917:                         return(-9);
 918:                     } else {
 919:                         printf(", one of the following:\n");
 920:                         clrhlp();
 921:                         for (i = 0; i < y; i++) {
 922:                             znext(filbuf);
 923: #ifdef VMS
 924:                 printf(" %s\n",filbuf); /* VMS names can be long */
 925: #else
 926:                             addhlp(filbuf);
 927: #endif /* VMS */
 928:                         }
 929:                         dmphlp();
 930:                     }
 931:                 } else printf("\n");
 932:                 printf("%s%s",cmprom,cmdbuf);
 933:         fflush(stdout);
 934:                 break;
 935: #endif /* MAC */
 936:         }
 937:     x = gtword();
 938:     *xp = atmbuf;
 939:     }
 940: }
 941: 
 942: /*  C M D I R  --  Parse a directory specification  */
 943: 
 944: /*
 945:  This function depends on the external functions:
 946:    zchki()  - Check if input file exists and is readable.
 947:  If these functions aren't available, then use cmfld() to parse dir names.
 948:  Note: this function quickly cobbled together, mainly by deleting lots of
 949:  lines from cmifi().  It seems to work, but various services are missing,
 950:  like completion, lists of matching directories on "?", etc.
 951: */
 952: /*
 953:  Returns
 954:    -4 EOF
 955:    -3 if no input present when required,
 956:    -2 if out of space or other internal error,
 957:    -1 if reparse needed,
 958:     0 or 1, with xp pointing to name, if directory specified,
 959:     2 if a wildcard was included.
 960: */
 961: int
 962: cmdir(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
 963:     int x, xc; char *zq;
 964: #ifdef DTILDE
 965:     char *tilde_expand(), *dirp;
 966: #endif /* DTILDE */
 967: 
 968:     inword = cc = xc = 0;       /* Initialize counts & pointers */
 969:     *xp = "";
 970:     if ((x = cmflgs) != 1) {            /* Already confirmed? */
 971:         x = gtword();                   /* No, get a word */
 972:     } else {
 973:         setatm(xdef);           /* If so, use default, if any. */
 974:     }
 975:     *xp = atmbuf;                       /* Point to result. */
 976:     while (1) {
 977:         xc += cc;                       /* Count the characters. */
 978:         debug(F111,"cmdir gtword",atmbuf,xc);
 979:         switch (x) {
 980:             case -4:                    /* EOF */
 981:             case -2:                    /* Out of space. */
 982:             case -1:                    /* Reparse needed */
 983:                 return(x);
 984:             case 0:                     /* SP or NL */
 985:             case 1:
 986:                 if (xc == 0) *xp = xdef;     /* If no input, return default. */
 987:         else *xp = atmbuf;
 988:                 if (**xp == NUL) return(-3); /* If field empty, return -3. */
 989: 
 990:         if (f) {        /* If a conversion function is given */
 991:             zq = atxbuf;    /* ... */
 992:             atxn = CMDBL;
 993:             if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
 994:             *xp = atxbuf;
 995:             cc = (int)strlen(atxbuf);
 996:         }
 997: #ifdef DTILDE
 998: /*
 999:   This is ugly, and for UNIX only.
1000:   Normally, we wouldn't call tilde_expand from a place like this anyway,
1001:   but rather let zxpand() take care of it.  But in this case we might want
1002:   a hybrid result -- a string with the tilde expanded, but with wildcards
1003:   left unexpanded.
1004: */
1005:         dirp = tilde_expand(*xp); /* Expand tilde, if any, */
1006:         if (*dirp == '~') { /* Still starts with tilde? */
1007:             char *tp;       /* Yes, convert to lowercase */
1008:             tp = *xp;       /* and try again. */
1009:             while (*tp) {
1010:             if (isupper(*tp)) *tp = tolower(*tp);
1011:             tp++;
1012:             }
1013:         }
1014:         dirp = tilde_expand(*xp); /* Expand tilde, if any, */
1015:         if (*dirp != '\0') setatm(dirp); /* in the atom buffer. */
1016:         *xp = atmbuf;
1017: #endif /* DTILDE */
1018:         if (iswild(*xp)) return(2);
1019:         else return(x);
1020: 
1021:             case 2:                     /* ESC */
1022:         putchar(BEL);
1023:         break;
1024: 
1025:             case 3:                     /* Question mark */
1026:                 if (*xhlp == NUL)
1027:                     printf(" Directory name");
1028:                 else
1029:                     printf(" %s",xhlp);
1030:                 printf("\n%s%s",cmprom,cmdbuf);
1031:         fflush(stdout);
1032:                 break;
1033:         }
1034:     x = gtword();
1035: /*  *xp = atmbuf;  */
1036:     }
1037: }
1038: 
1039: /*  C M F L D  --  Parse an arbitrary field  */
1040: /*
1041:  Returns
1042:    -3 if no input present when required,
1043:    -2 if field too big for buffer,
1044:    -1 if reparse needed,
1045:     0 otherwise, xp pointing to string result.
1046: */
1047: int
1048: cmfld(xhlp,xdef,xp,f) char *xhlp, *xdef, **xp; xx_strp f; {
1049:     int x, xc;
1050:     char *zq;
1051: 
1052:     inword = cc = xc = 0;       /* Initialize counts & pointers */
1053:     *xp = "";
1054:     if ((x = cmflgs) != 1) {            /* Already confirmed? */
1055:         x = gtword();                   /* No, get a word */
1056:     } else {
1057:         setatm(xdef);           /* If so, use default, if any. */
1058:     }
1059:     *xp = atmbuf;                       /* Point to result. */
1060: 
1061:     while (1) {
1062:         xc += cc;                       /* Count the characters. */
1063:         debug(F111,"cmfld: gtword",atmbuf,xc);
1064:         debug(F101,"cmfld x","",x);
1065:         switch (x) {
1066:             case -4:                    /* EOF */
1067:             case -2:                    /* Out of space. */
1068:             case -1:                    /* Reparse needed */
1069:                 return(x);
1070:             case 0:                     /* SP or NL */
1071:             case 1:
1072:                 if (xc == 0)        /* If no input, return default. */
1073:           setatm(xdef);
1074:         *xp = atmbuf;
1075:         if (f) {        /* If a conversion function is given */
1076:             zq = atxbuf;    /* ... */
1077:             atxn = CMDBL;
1078:             if ((*f)(*xp,&zq,&atxn) < 0) return(-2);
1079:             setatm(atxbuf);
1080:             *xp = atmbuf;
1081:         }
1082:                 if (**xp == NUL) {  /* If variable evaluates to null */
1083:             setatm(xdef);   /* Stick in the default again. */
1084:             if (**xp == NUL) x = -3; /* If still empty, return -3. */
1085:         }
1086: #ifdef COMMENT
1087: /* The following is apparently not necessary. */
1088: /* Remove it if nothing is broken, esp. TAKE file with trailing comments */
1089:         xx = *xp;
1090:         debug(F111,"cmfld before trim",*xp,x);
1091:         for (i = (int)strlen(xx) - 1; i > 0; i--)
1092:           if (xx[i] != SP)  /* Trim trailing blanks */
1093:             break;
1094:           else
1095:             xx[i] = NUL;
1096:         debug(F111,"cmfld returns",*xp,x);
1097: #endif /* COMMENT */
1098:         debug(F101,"cmfld: returns","",x);
1099:                 return(x);
1100:             case 2:                     /* ESC */
1101:                 if (xc == 0 && *xdef != NUL) {
1102:                     printf("%s ",xdef); /* If at beginning of field, */
1103: #ifdef GEMDOS
1104:             fflush(stdout);
1105: #endif /* GEMDOS */
1106:                     addbuf(xdef);       /* supply default. */
1107:             inword = cmflgs = 0;
1108:                     setatm(xdef);   /* Return as if whole field */
1109:                     return(0);          /* typed, followed by space. */
1110:                 } else {
1111:                     putchar(BEL);       /* Beep if already into field. */
1112:                 }
1113:                 break;
1114:             case 3:                     /* Question mark */
1115:                 if (*xhlp == NUL)
1116:                     printf(" Please complete this field");
1117:                 else
1118:                     printf(" %s",xhlp);
1119:                 printf("\n%s%s",cmprom,cmdbuf);
1120:         fflush(stdout);
1121:                 break;
1122:         }
1123:     x = gtword();
1124: /*  *xp = atmbuf; */
1125:     }
1126: }
1127: 
1128: 
1129: /*  C M T X T  --  Get a text string, including confirmation  */
1130: 
1131: /*
1132:   Print help message 'xhlp' if ? typed, supply default 'xdef' if null
1133:   string typed.  Returns
1134: 
1135:    -1 if reparse needed or buffer overflows.
1136:     1 otherwise.
1137: 
1138:   with cmflgs set to return code, and xp pointing to result string.
1139: */
1140: int
1141: cmtxt(xhlp,xdef,xp,f) char *xhlp; char *xdef; char **xp; xx_strp f; {
1142: 
1143:     int x, i;
1144:     char *xx, *zq;
1145:     static int xc;
1146: 
1147:     debug(F101,"cmtxt, cmflgs","",cmflgs);
1148:     inword = cc = 0;            /* Start atmbuf counter off at 0 */
1149:     if (cmflgs == -1) {                 /* If reparsing, */
1150:         xc = (int)strlen(*xp);      /* get back the total text length, */
1151:     } else {                            /* otherwise, */
1152:         *xp = "";                       /* start fresh. */
1153:         xc = 0;
1154:     }
1155:     *atmbuf = NUL;                      /* And empty the atom buffer. */
1156:     if ((x = cmflgs) != 1) {
1157:         x = gtword();                   /* Get first word. */
1158:         *xp = pp;                       /* Save pointer to it. */
1159:     }
1160:     debug(F101,"cmtxt (*f)","", f);
1161:     while (1) {             /* Loop for each word in text. */
1162:         xc += cc;                       /* Char count for all words. */
1163:         debug(F111,"cmtxt: gtword",atmbuf,xc);
1164:         debug(F101," x","",x);
1165:         switch (x) {
1166:       case -9:          /* Buffer overflow */
1167:       case -4:          /* EOF */
1168: #ifdef MAC
1169:       case -3:          /* Quit/Timeout */
1170: #endif /* MAC */
1171:       case -2:          /* Overflow */
1172:       case -1:          /* Deletion */
1173:         return(x);
1174:       case 0:           /* Space */
1175:         xc++;           /* Just count it */
1176:         break;
1177:       case 1:           /* CR or LF */
1178:         if (xc == 0) *xp = xdef;
1179:         if (f) {            /* If a conversion function is given */
1180:         zq = atxbuf;        /* Point to the expansion buffer */
1181:         atxn = CMDBL;       /* specify its length */
1182:         debug(F110,"cmtxt calling (*f)",*xp,0);
1183:         if ((x = (*f)(*xp,&zq,&atxn)) < 0) return(-2);
1184:         cc = (int)strlen(atxbuf);
1185:         *xp = atxbuf;       /* and return pointer to it. */
1186:         debug(F111,"cmtxt (*f) returns",*xp,cc);
1187:         }
1188:         xx = *xp;
1189:         for (i = (int)strlen(xx) - 1; i > 0; i--)
1190:           if (xx[i] != SP)      /* Trim trailing blanks */
1191:         break;
1192:           else
1193:         xx[i] = NUL;
1194:         return(x);
1195:       case 2:           /* ESC */
1196:         if (xc == 0) {
1197:         printf("%s ",xdef);
1198:         inword = cmflgs = 0;
1199: #ifdef GEMDOS
1200:         fflush(stdout);
1201: #endif /* GEMDOS */
1202:         cc = addbuf(xdef);
1203:         } else {
1204:         putchar(BEL);
1205:         }
1206:         break;
1207:       case 3:           /* Question Mark */
1208:         if (*xhlp == NUL)
1209:           printf(" Text string");
1210:         else
1211:           printf(" %s",xhlp);
1212:         printf("\n%s%s",cmprom,cmdbuf);
1213:         fflush(stdout);
1214:         break;
1215:       default:
1216:         printf("?Unexpected return code from gtword() - %d\n",x);
1217:         return(-2);
1218:         }
1219:         x = gtword();
1220:     }
1221: }
1222: 
1223: 
1224: /*  C M K E Y  --  Parse a keyword  */
1225: 
1226: /*
1227:  Call with:
1228:    table    --  keyword table, in 'struct keytab' format;
1229:    n        --  number of entries in table;
1230:    xhlp     --  pointer to help string;
1231:    xdef     --  pointer to default keyword;
1232: 
1233:  Returns:
1234:    -3       --  no input supplied and no default available
1235:    -2       --  input doesn't uniquely match a keyword in the table
1236:    -1       --  user deleted too much, command reparse required
1237:     n >= 0  --  value associated with keyword
1238: */
1239: int
1240: cmkey(table,n,xhlp,xdef,f)
1241: /* cmkey */  struct keytab table[]; int n; char *xhlp, *xdef; xx_strp f; {
1242:     return(cmkey2(table,n,xhlp,xdef,"",f));
1243: }
1244: int
1245: cmkey2(table,n,xhlp,xdef,tok,f)
1246:     struct keytab table[]; int n; char *xhlp, *xdef; char *tok; xx_strp f; {
1247: 
1248:     int i, tl, y, z, zz, xc;
1249:     char *xp, *zq;
1250: 
1251:     tl = (int)strlen(tok);
1252:     inword = xc = cc = 0;       /* Clear character counters. */
1253: 
1254:     if ((zz = cmflgs) == 1)             /* Command already entered? */
1255:       setatm(xdef);         /* Yes, copy default into atom buf */
1256:     else zz = gtword();         /* Otherwise get a command word */
1257: 
1258: debug(F101,"cmkey: table length","",n);
1259: debug(F101," cmflgs","",cmflgs);
1260: debug(F101," zz","",zz);
1261: while (1) {
1262:     xc += cc;
1263:     debug(F111,"cmkey: gtword",atmbuf,xc);
1264: 
1265:     switch(zz) {
1266:         case -4:                        /* EOF */
1267: #ifdef MAC
1268:     case -3:            /* Quit/Timeout */
1269: #endif /* MAC */
1270:         case -2:                        /* Buffer overflow */
1271:         case -1:                        /* Or user did some deleting. */
1272:             return(cmflgs = zz);
1273: 
1274:         case 0:                         /* User terminated word with space */
1275:         case 1:                         /* or newline */
1276:             if (cc == 0) setatm(xdef);  /* Supply default if user typed nada */
1277:         if (f) {            /* If a conversion function is given */
1278:         zq = atxbuf;        /* apply it */
1279:         atxn = CMDBL;
1280:         if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
1281:         debug(F110,"cmkey atxbuf after *f",atxbuf,0);
1282:         setatm(atxbuf);
1283:         }
1284:             y = lookup(table,atmbuf,n,&z); /* Look up the word in the table */
1285:             switch (y) {
1286:                 case -2:        /* Ambiguous */
1287:                     printf("?Ambiguous - %s\n",atmbuf);
1288:             cmflgs = -2;
1289:                     return(-9);
1290:                 case -1:        /* Not found at all */
1291:             if (tl) {
1292:             for (i = 0; i < tl; i++) /* Check for token */
1293:               if (tok[i] == *atmbuf) { /* Got one */
1294:                   ungword();  /* Put back the following word */
1295:                   return(-5); /* Special return code for token */
1296:               }
1297:             }
1298:             /* Kludge alert... only print error if */
1299:             /* we were called as cmkey2, but not cmkey... */
1300:             /* This doesn't seem to always work. */
1301:             if (tl == 0) {
1302:             printf("?No keywords match - %s\n",atmbuf); /* cmkey */
1303:             return(cmflgs = -9);
1304:             } else {
1305:             if (cmflgs == 1) return(cmflgs = -6); /* cmkey2 */
1306:             else return(cmflgs = -2);
1307:             /* The -6 code is to let caller try another table */
1308:             }
1309:                 default:
1310:                     break;
1311:         }
1312:             return(y);
1313: 
1314:         case 2:                         /* User terminated word with ESC */
1315:             if (cc == 0) {
1316:                 if (*xdef != NUL) {     /* Nothing in atmbuf */
1317:                     printf("%s ",xdef); /* Supply default if any */
1318: #ifdef GEMDOS
1319:             fflush(stdout);
1320: #endif /* GEMDOS */
1321:                     addbuf(xdef);
1322:                     setatm(xdef);
1323:             inword = cmflgs = 0;
1324:                     debug(F111,"cmkey: default",atmbuf,cc);
1325:                 } else {
1326:                     putchar(BEL);       /* No default, just beep */
1327:                     break;
1328:                 }
1329:             }
1330:         if (f) {            /* If a conversion function is given */
1331:         zq = atxbuf;        /* apply it */
1332:         atxn = CMDBL;
1333:         if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
1334:         setatm(atxbuf);
1335:         }
1336:             y = lookup(table,atmbuf,n,&z); /* Something in atmbuf */
1337:             debug(F111,"cmkey: esc",atmbuf,y);
1338:             if (y == -2) {      /* Ambiguous */
1339:                 putchar(BEL);
1340:                 break;
1341:             }
1342:             if (y == -1) {      /* Not found */
1343:                 /* if (tl == 0) */ printf("?No keywords match - %s\n",atmbuf);
1344:         cmflgs = -2;
1345:                 return(-9);
1346:             }
1347: /*
1348:   See if the keyword just found has the CM_ABR bit set in its flgs field, and
1349:   if so, search forwards in the table for a keyword that has the same kwval
1350:   but does not have CM_ABR (or CM_INV?) set, and then expand using the full
1351:   keyword.  WARNING: This assumes that (a) keywords are in alphabetical order,
1352:   and (b) the CM_ABR bit is set only if the the abbreviated keyword is a true
1353:   abbreviation (left substring) of the full keyword.
1354: */
1355:         if (test(table[z].flgs,CM_ABR)) {
1356:         int zz;
1357:         for (zz = z+1; zz < n; zz++)
1358:           if ((table[zz].kwval == table[z].kwval) &&
1359:               (!test(table[zz].flgs,CM_ABR))) {
1360:               z = zz;
1361:               break;
1362:           }
1363:             }
1364:             xp = table[z].kwd + cc;
1365:             printf("%s ",xp);
1366: #ifdef GEMDOS
1367:         fflush(stdout);
1368: #endif /* GEMDOS */
1369:             addbuf(xp);
1370:         inword = cmflgs = 0;
1371:             debug(F110,"cmkey: addbuf",cmdbuf,0);
1372:             return(y);
1373: 
1374:         case 3:                         /* User typed "?" */
1375:         if (f) {            /* If a conversion function is given */
1376:         zq = atxbuf;        /* do the conversion now. */
1377:         atxn = CMDBL;
1378:         if ((*f)(atmbuf,&zq,&atxn) < 0) return(-2);
1379:         setatm(atxbuf);
1380:         }
1381:             y = lookup(table,atmbuf,n,&z); /* Look up what we have so far. */
1382: 
1383:         if (y == -1) {
1384:                 /* if (tl == 0) */ printf(" No keywords match\n");
1385:         cmflgs = -2;
1386:                 return(-9);
1387:             }
1388:             if (*xhlp == NUL)
1389:                 printf(" One of the following:\n");
1390:             else
1391:                 printf(" %s, one of the following:\n",xhlp);
1392: 
1393:         if ((y > -1) &&
1394:         !test(table[z].flgs,CM_ABR) &&
1395:         ((z >= n-1) || strncmp(table[z].kwd,table[z+1].kwd,cc))
1396:          ) {
1397:         printf(" %s\n",table[z].kwd);
1398:         } else {
1399:         clrhlp();
1400:         for (i = 0; i < n; i++) {
1401:             if (!strncmp(table[i].kwd,atmbuf,cc)
1402:             && !test(table[i].flgs,CM_INV)
1403:             )
1404:               addhlp(table[i].kwd);
1405:         }
1406:         dmphlp();
1407:         }
1408:         if (*atmbuf == NUL) {
1409:         if (tl == 1)
1410:           printf("or the token '%c'\n",*tok);
1411:         else if (tl > 1) printf("or one of the tokens '%s'\n",tok);
1412:         }
1413:             printf("%s%s", cmprom, cmdbuf);
1414:         fflush(stdout);
1415:             break;
1416: 
1417:         default:
1418:             printf("\n%d - Unexpected return code from gtword\n",zz);
1419:             return(cmflgs = -2);
1420:         }
1421:         zz = gtword();
1422:     }
1423: }
1424: int
1425: chktok(tlist) char *tlist; {
1426:     char *p;
1427:     p = tlist;
1428:     while (*p != NUL && *p != *atmbuf) p++;
1429:     return((*p) ? (int) *p : 0);
1430: }
1431: 
1432: /*  C M C F M  --  Parse command confirmation (end of line)  */
1433: 
1434: /*
1435:  Returns
1436:    -2: User typed anything but whitespace or newline
1437:    -1: Reparse needed
1438:     0: Confirmation was received
1439: */
1440: int
1441: cmcfm() {
1442:     int x, xc;
1443: 
1444:     debug(F101,"cmcfm: cmflgs","",cmflgs);
1445:     debug(F110,"cmcfm: atmbuf",atmbuf,0);
1446:     inword = xc = cc = 0;
1447:     if (cmflgs == 1) return(0);
1448: 
1449:     setatm("");             /* (Probably unnecessary) */
1450: 
1451:     while (1) {
1452:         x = gtword();
1453:         xc += cc;
1454:         switch (x) {
1455:             case -4:                    /* EOF */
1456:             case -2:
1457:             case -1:
1458:                 return(x);
1459: 
1460:             case 1:                     /* End of line */
1461:                 if (xc > 0) {
1462:                     printf("?Not confirmed - %s\n",atmbuf);
1463:                     return(-9);
1464:                 } else return(0);
1465:             case 2:         /* ESC */
1466:         if (xc == 0) {
1467:             putchar(BEL);   /* beep & continue */
1468:             continue;       /* or fall thru. */
1469:         }
1470:             case 0:                     /* Space */
1471:         if (xc == 0)        /* If no chars typed, continue, */
1472:           continue;     /* else fall thru. */
1473:             case 3:         /* Question mark */
1474:                 if (xc > 0) {
1475:                     printf("?Not confirmed - %s\n",atmbuf);
1476:                     return(-9);
1477:                 }
1478:                 printf("\n Type a carriage return to confirm the command\n");
1479:                 printf("%s%s",cmprom,cmdbuf);
1480:         fflush(stdout);
1481:                 continue;
1482:         }
1483:     }
1484: }
1485: 
1486: /* Keyword help routines */
1487: 
1488: 
1489: /*  C L R H L P -- Initialize/Clear the help line buffer  */
1490: 
1491: VOID
1492: clrhlp() {                              /* Clear the help buffer */
1493:     hlpbuf[0] = NUL;
1494:     hh = hx = 0;
1495: }
1496: 
1497: 
1498: /*  A D D H L P  --  Add a string to the help line buffer  */
1499: 
1500: VOID
1501: addhlp(s) char *s; {                    /* Add a word to the help buffer */
1502:     int j;
1503: 
1504:     hh++;                               /* Count this column */
1505: 
1506:     for (j = 0; (j < hc) && (*s != NUL); j++) { /* Fill the column */
1507:         hlpbuf[hx++] = *s++;
1508:     }
1509:     if (*s != NUL)                      /* Still some chars left in string? */
1510:         hlpbuf[hx-1] = '+';             /* Mark as too long for column. */
1511: 
1512:     if (hh < (hw / hc)) {               /* Pad col with spaces if necessary */
1513:         for (; j < hc; j++) {
1514:             hlpbuf[hx++] = SP;
1515:         }
1516:     } else {                            /* If last column, */
1517:         hlpbuf[hx++] = NUL;             /* no spaces. */
1518:         dmphlp();                       /* Print it. */
1519:         return;
1520:     }
1521: }
1522: 
1523: 
1524: /*  D M P H L P  --  Dump the help line buffer  */
1525: 
1526: VOID
1527: dmphlp() {                              /* Print the help buffer */
1528:     hlpbuf[hx++] = NUL;
1529:     printf(" %s\n",hlpbuf);
1530:     clrhlp();
1531: }
1532: 
1533: 
1534: /*  G T W O R D  --  Gets a "word" from the command input stream  */
1535: 
1536: /*
1537: Usage: retcode = gtword();
1538: 
1539: Returns:
1540:  -4 if end of file (e.g. pipe broken)
1541:  -2 if command buffer overflows
1542:  -1 if user did some deleting
1543:   0 if word terminates with SP or tab
1544:   1 if ... CR
1545:   2 if ... ESC
1546:   3 if ... ? (question mark)
1547: 
1548: With:
1549:   pp pointing to beginning of word in buffer
1550:   bp pointing to after current position
1551:   atmbuf containing a copy of the word
1552:   cc containing the number of characters in the word copied to atmbuf
1553: */
1554: 
1555: int
1556: ungword() {             /* unget a word */
1557:     if (ungw) return(0);
1558:     cmfsav = cmflgs;
1559:     debug(F101,"ungword cmflgs","",cmflgs);
1560:     ungw = 1;
1561:     cmflgs = 0;
1562:     return(0);
1563: }
1564: 
1565: int
1566: gtword() {
1567:     int c;                              /* Current char */
1568:     int quote = 0;                      /* Flag for quote character */
1569:     int echof = 0;                      /* Flag for whether to echo */
1570:     int chsrc = 0;          /* Source of character, 1 = tty */
1571:     int comment = 0;            /* Flag for in comment */
1572:     char *cp = NULL;            /* Comment pointer */
1573: 
1574: #ifdef RTU
1575:     extern int rtu_bug;
1576: #endif /* RTU */
1577: 
1578: #ifdef datageneral
1579:     extern int termtype;                /* DG terminal type flag */
1580:     extern int con_reads_mt;            /* Console read asynch is active */
1581:     if (con_reads_mt) connoi_mt();      /* Task would interfere w/cons read */
1582: #endif /* datageneral */
1583: 
1584:     if (ungw) {             /* Have a word saved? */
1585:     debug(F110,"gtword ungetting from pp",pp,0);
1586:     while (*pp++ == SP) ;
1587:     setatm(pp);
1588:     ungw = 0;
1589:     cmflgs = cmfsav;
1590:     debug(F111,"gtword returning atmbuf",atmbuf,cmflgs);
1591:     return(cmflgs);
1592:     }
1593:     pp = np;                            /* Start of current field */
1594: 
1595:     debug(F111,"gtword: cmdbuf",cmdbuf,cmdbuf);
1596:     debug(F111," bp",bp,bp);
1597:     debug(F111," pp",pp,pp);
1598: 
1599:     while (bp < cmdbuf+CMDBL) {         /* Big get-a-character loop */
1600:     echof = 0;          /* Assume we don't echo because */
1601:     chsrc = 0;          /* character came from reparse buf. */
1602: 
1603:         if ((c = *bp) == NUL) {         /* If no char waiting in reparse buf */
1604:             if (dpx) echof = 1;         /* must get from tty, set echo flag. */
1605:         c = cmdgetc();      /* Read a character from the tty. */
1606:         chsrc = 1;          /* Remember character source is tty. */
1607: #ifdef MAC
1608:         if (c == -3)        /* If null command... */
1609:           return(-3);
1610: #endif /* MAC */
1611:             if (c == EOF) {     /* This can happen if stdin not tty. */
1612: #ifdef EINTR
1613:         if (errno == EINTR) /* This is for when bg'd process is */
1614:           continue;     /* fg'd again. */
1615: #endif /* EINTR */
1616:         return(-4);
1617:         }
1618:         c &= cmdmsk;        /* Strip any parity bit */
1619:     }               /* if desired. */
1620: #ifndef MAC
1621:     debug(F000,"gtword char","",c);
1622: #endif /* MAC */
1623: 
1624: /* Now we have the next character */
1625: 
1626:         if (quote == 0) {       /* If this is not a quoted character */
1627:             if (c == CMDQ) {        /* Got the quote character itself */
1628:         if (!comment) quote = 1; /* Flag it if not in a comment */
1629:             }
1630:         if (c == FF) {      /* Formfeed. */
1631:                 c = NL;                 /* Replace with newline */
1632: #ifdef COMMENT
1633: /* No more screen clearing... */
1634:         cmdclrscn();        /* Clear the screen */
1635: #endif /* COMMENT */
1636:             }
1637:         if (c == HT) {      /* Tab */
1638:         if (comment)        /* If in comment, */
1639:           c = SP;       /* substitute space */
1640:         else            /* otherwise */
1641:           c = ESC;      /* substitute ESC (for completion) */
1642:         }
1643:         if (c == ';' || c == '#') { /* Trailing comment */
1644:         if (inword == 0) {  /* If we're not in a word */
1645:             comment = 1;    /* start a comment. */
1646:             cp = bp;        /* remember where it starts. */
1647:         }
1648:         }
1649:         if (!comment && c == SP) {  /* Space */
1650:                 *bp++ = c;      /* deposit in buffer if not already */
1651:                 if (echof) putchar(c);  /* echo it. */
1652:                 if (inword == 0) {      /* If leading, gobble it. */
1653:                     pp++;
1654:                     continue;
1655:                 } else {                /* If terminating, return. */
1656:                     np = bp;
1657:                     setatm(pp);
1658:                     inword = cmflgs = 0;
1659:             return(0);
1660:                 }
1661:             }
1662:             if (c == NL || c == CR) {   /* CR or LF. */
1663:         if (echof) cmdnewl((char)c); /* Echo it. */
1664:         while (bp > pp && (*(bp-1) == SP || *(bp-1) == HT)) /* Trim */
1665:           bp--;         /* trailing */
1666:         *bp = NUL;      /* whitespace */
1667:         if (*(bp-1) == '-') {   /* Is this line continued? */
1668:             if (chsrc) {    /* If reading from tty, */
1669: #ifdef COMMENT
1670:             bp--, pp--; /* back up the buffer pointer, */
1671: #else
1672:             bp--;
1673: #endif /* COMMENT */
1674:             *bp = NUL;  /* erase the dash, */
1675:             continue;   /* and go back for next char now. */
1676:             }
1677:         } else {        /* No, a command has been entered. */
1678:             *bp = NUL;      /* Terminate the command string. */
1679:             if (comment) {  /* If we're in a comment, */
1680:             comment = 0;    /* Say we're not any more, */
1681:             *cp = NUL;  /* cut it off. */
1682:             }
1683:             np = bp;        /* Where to start next field. */
1684:             setatm(pp);     /* Copy this field to atom buffer. */
1685:             inword = 0;     /* Not in a word any more. */
1686:             return(cmflgs = 1);
1687:         }
1688:             }
1689:             if (!comment && echof && (c == '?')) { /* Question mark */
1690:                 putchar(c);
1691:                 *bp = NUL;
1692:                 setatm(pp);
1693:                 return(cmflgs = 3);
1694:             }
1695:             if (c == ESC) {     /* ESC */
1696:         if (!comment) {
1697:             *bp = NUL;
1698:             setatm(pp);
1699:             return(cmflgs = 2);
1700:         } else {
1701:             putchar(BEL);
1702:             continue;
1703:         }
1704:             }
1705:             if (c == BS || c == RUB) {  /* Character deletion */
1706:                 if (bp > cmdbuf) {      /* If still in buffer... */
1707:             cmdchardel();   /* erase it. */
1708:                     bp--;               /* point behind it, */
1709:                     if (*bp == SP) inword = 0; /* Flag if current field gone */
1710:                     *bp = NUL;          /* Erase character from buffer. */
1711:                 } else {                /* Otherwise, */
1712:                     putchar(BEL);       /* beep, */
1713:                     cmres();            /* and start parsing a new command. */
1714:             *bp = *atmbuf = NUL;
1715:                 }
1716:                 if (pp < bp) continue;
1717:                 else return(cmflgs = -1);
1718:             }
1719:             if (c == LDEL) {            /* ^U, line deletion */
1720:                 while ((bp--) > cmdbuf) {
1721:                     cmdchardel();
1722:                     *bp = NUL;
1723:                 }
1724:                 cmres();                /* Restart the command. */
1725:         *bp = *atmbuf = NUL;
1726:                 inword = 0;
1727:                 return(cmflgs = -1);
1728:             }
1729:             if (c == WDEL) {            /* ^W, word deletion */
1730:                 if (bp <= cmdbuf) {     /* Beep if nothing to delete */
1731:                     putchar(BEL);
1732:                     cmres();
1733:             *bp = *atmbuf = NUL;
1734:                     return(cmflgs = -1);
1735:                 }
1736:                 bp--;
1737:                 for ( ; (bp >= cmdbuf) && (*bp == SP) ; bp--) {
1738:                     cmdchardel();
1739:                     *bp = NUL;
1740:                 }
1741:                 for ( ; (bp >= cmdbuf) && (*bp != SP) ; bp--) {
1742:                     cmdchardel();
1743:                     *bp = NUL;
1744:                 }
1745:                 bp++;
1746:                 inword = 0;
1747:                 return(cmflgs = -1);
1748:             }
1749:             if (c == RDIS) {            /* ^R, redisplay */
1750: #ifdef COMMENT
1751:                 *bp = NUL;
1752:                 printf("\n%s%s",cmprom,cmdbuf);
1753: #else
1754:         char *cpx; char cx;
1755:                 *bp = NUL;
1756:                 printf("\n%s",cmprom);
1757:         cpx = cmdbuf;
1758:         while (cx = *cpx++) {
1759: #ifdef isprint
1760:             putchar(isprint(cx) ? cx : '^');
1761: #else
1762:             putchar((cx >= SP && cx < DEL) ? cx : '^');
1763: #endif /* isprint */
1764:         }
1765: #endif /* COMMENT */
1766:         fflush(stdout);
1767:                 continue;
1768:             }
1769:         if (c < SP && quote == 0) { /* Any other unquoted control char */
1770:         if (!chsrc) bp++;   /* If cmd file, point past it */
1771:         else putchar(BEL);  /* otherwise just beep and */
1772:         continue;       /* continue, don't put in buffer */
1773:         }
1774:         if (echof) cmdecho((char) c, 0); /* Echo what was typed. */
1775:         } else {            /* This character was quoted. */
1776:         int qf = 1;
1777:         quote = 0;          /* Unset the quote flag. */
1778:         /* Quote character at this level is only for SP, ?, and controls */
1779:             /* If anything else was quoted, leave quote in, and let */
1780:         /* the command-specific parsing routines handle it, e.g. \007 */
1781:         if (c > 32 && c != '?' && c != RUB && chsrc != 0) {
1782:         *bp++ = CMDQ;       /* Deposit \ if it came from tty */
1783:         qf = 0;         /* and don't erase it from screen */
1784:         }
1785:         if (echof) cmdecho((char) c, qf); /* Now echo quoted character */
1786:         debug(F000,"gtword quote",cmdbuf,c);
1787:     }
1788: #ifdef COMMENT
1789:         if (echof) cmdecho((char) c,quote); /* Echo what was typed. */
1790: #endif /* COMMENT */
1791:         if (!comment) inword = 1;   /* Flag we're in a word. */
1792:     if (quote) continue;        /* Don't deposit quote character. */
1793:         if (c != NL) *bp++ = c;     /* Deposit command character. */
1794:     }                                   /* End of big while */
1795:     putchar(BEL);                       /* Get here if... */
1796:     printf("?Command too long, maximum length: %d.\n",CMDBL);
1797:     cmflgs = -2;
1798:     return(-9);
1799: }
1800: 
1801: /* Utility functions */
1802: 
1803: /* A D D B U F  -- Add the string pointed to by cp to the command buffer  */
1804: 
1805: int
1806: addbuf(cp) char *cp; {
1807:     int len = 0;
1808:     while ((*cp != NUL) && (bp < cmdbuf+CMDBL)) {
1809:         *bp++ = *cp++;                  /* Copy and */
1810:         len++;                          /* count the characters. */
1811:     }
1812:     *bp++ = SP;                         /* Put a space at the end */
1813:     *bp = NUL;                          /* Terminate with a null */
1814:     np = bp;                            /* Update the next-field pointer */
1815:     return(len);                        /* Return the length */
1816: }
1817: 
1818: /*  S E T A T M  --  Deposit a token in the atom buffer.  */
1819: /*  Break on space, newline, carriage return, or null. */
1820: /*  Null-terminate the result. */
1821: /*  If the source pointer is the atom buffer itself, do nothing. */
1822: /*  Return length of token, and also set global "cc" to this length. */
1823: 
1824: int
1825: setatm(cp) char *cp; {
1826:     char *ap, *xp;
1827: 
1828:     cc = 0;             /* Character counter */
1829:     ap = atmbuf;            /* Address of atom buffer */
1830: 
1831:     if (cp == ap) {         /* In case source is atom buffer */
1832:     xp = atybuf;            /* make a copy */
1833:     strcpy(xp,ap);          /* so we can copy it back, edited. */
1834:     cp = xp;
1835:     }
1836:     *ap = NUL;              /* Zero the atom buffer */
1837: 
1838:     while (*cp == SP) cp++;     /* Trim leading spaces */
1839:     while ((*cp != SP) && (*cp != NL) && (*cp != NUL) && (*cp != CR)) {
1840:         *ap++ = *cp++;          /* Copy up to SP, NL, CR, or end */
1841:         cc++;               /* and count */
1842:     }
1843:     *ap = NUL;              /* Terminate the string. */
1844:     return(cc);                         /* Return length. */
1845: }
1846: 
1847: /*  R D I G I T S  -- Verify that all the characters in line ARE DIGITS  */
1848: 
1849: int
1850: rdigits(s) char *s; {
1851:     while (*s) {
1852:         if (!isdigit(*s)) return(0);
1853:         s++;
1854:     }
1855:     return(1);
1856: }
1857: 
1858: /* These functions attempt to hide system dependencies from the mainline */
1859: /* code in gtword().  Ultimately they should be moved to ck?tio.c, where */
1860: /* ? = each and every system supported by C-Kermit. */
1861: 
1862: int
1863: cmdgetc() {             /* Get a character from the tty. */
1864:     int c;
1865: 
1866: #ifdef datageneral
1867:     {
1868:     char ch;
1869:     c = dgncinb(0,&ch,1);       /* -1 is EOF, -2 TO,
1870:                                          * -c is AOS/VS error */
1871:     if (c == -2) {          /* timeout was enabled? */
1872:         resto(channel(0));      /* reset timeouts */
1873:         c = dgncinb(0,&ch,1);   /* retry this now! */
1874:     }
1875:     if (c < 0) return(-4);      /* EOF or some error */
1876:     else c = (int) ch & 0177;   /* Get char without parity */
1877: /*	echof = 1; */
1878:     }
1879: #else /* Not datageneral */
1880: #ifdef GEMDOS
1881:     c = isatty(0) ? coninc(0) : getchar();
1882: #else
1883: #ifdef OS2
1884:     c = isatty(0) ? coninc(0) : getchar();
1885:     if (c < 0) return(-4);
1886: #else /* Not OS2 */
1887:     c = getchar();          /* or from tty. */
1888: #ifdef RTU
1889:     if (rtu_bug) {
1890:     c = getchar();          /* RTU doesn't discard the ^Z */
1891:     rtu_bug = 0;
1892:     }
1893: #endif /* RTU */
1894: #endif /* OS2 */
1895: #endif /* GEMDOS */
1896: #endif /* datageneral */
1897:     return(c);              /* Return what we got */
1898: }
1899: 
1900: 
1901: #ifdef COMMENT
1902: /*
1903:   No more screen clearing.  If you wanna clear the screen, define a macro
1904:   to do it, like "define cls write screen \27[;H\27[2J".
1905: */
1906: cmdclrscn() {               /* Clear the screen */
1907: 
1908: #ifdef aegis
1909:     putchar(FF);
1910: #else
1911: #ifdef AMIGA
1912:     putchar(FF);
1913: #else
1914: #ifdef OSK
1915:     putchar(FF);
1916: #else
1917: #ifdef datageneral
1918:     putchar(FF);
1919: #else
1920: #ifdef OS2
1921:     zsystem("cls");
1922: #else
1923:     zsystem("clear");
1924: #endif /* OS2 */
1925: #endif /* datageneral */
1926: #endif /* OSK */
1927: #endif /* AMIGA */
1928: #endif /* aegis */
1929: }
1930: #endif /* COMMENT */
1931: 
1932: VOID                    /* What to echo at end of command */
1933: #ifdef CK_ANSIC
1934: cmdnewl(char c)
1935: #else
1936: cmdnewl(c) char c;
1937: #endif /* CK_ANSIC */
1938: /* cmdnewl */ {
1939:     putchar(c);             /* c is the terminating character */
1940: #ifdef WINTCP
1941:     if (c == CR) putchar(NL);
1942: #endif /* WINTCP */
1943: #ifdef OS2
1944:     if (c == CR) putchar(NL);
1945: #endif /* OS2 */
1946: #ifdef aegis
1947:     if (c == CR) putchar(NL);
1948: #endif /* aegis */
1949: #ifdef AMIGA
1950:     if (c == CR) putchar(NL);
1951: #endif /* AMIGA */
1952: #ifdef datageneral
1953:     if (c == CR) putchar(NL);
1954: #endif /* datageneral */
1955: #ifdef GEMDOS
1956:     if (c == CR) putchar(NL);
1957: #endif /* GEMDOS */
1958: }
1959: 
1960: VOID
1961: cmdchardel() {              /* Erase a character from the screen */
1962:     if (!dpx) return;
1963: #ifdef datageneral
1964:     /* DG '\b' is EM (^y or \031) */
1965:     if (termtype == 1)
1966:       /* Erase a character from non-DG screen, */
1967:       dgncoub(1,"\010 \010",3);
1968:     else
1969: #endif
1970:       printf("\b \b");
1971: #ifdef GEMDOS
1972:     fflush(stdout);
1973: #endif /* GEMDOS */
1974: }
1975: 
1976: VOID
1977: #ifdef CK_ANSIC
1978: cmdecho(char c, int quote)
1979: #else
1980: cmdecho(c,quote) char c; int quote;
1981: #endif /* CK_ANSIC */
1982: { /* cmdecho */
1983:     if (!dpx) return;
1984:     /* Echo tty input character c */
1985:     if (quote) {
1986:     putchar(BS); putchar(SP); putchar(BS);
1987: #ifdef isprint
1988:     putchar( isprint(c) ? c : '^' );
1989: #else
1990:     putchar((c >= SP && c < DEL) ? c : '^');
1991: #endif /* isprint */
1992:     } else putchar(c);
1993: #ifdef OS2
1994:     if (quote==1 && c==CR) putchar(NL);
1995: #endif /* OS2 */
1996: }
1997: 
1998: #endif /* NOICP */
1999: 
2000: #ifdef NOICP
2001: #include "ckcdeb.h"
2002: #include "ckucmd.h"
2003: #include "ckcasc.h"
2004: /*** #include <ctype.h> (ckcdeb.h already includes this) ***/
2005: #endif /* NOICP */
2006: 
2007: /*  X X E S C  --  Interprets backslash codes  */
2008: /*  Returns the int value of the backslash code if it is > -1 and < 256 */
2009: /*  and updates the string pointer to first character after backslash code. */
2010: /*  If the argument is invalid, leaves pointer unchanged and returns -1. */
2011: 
2012: int
2013: xxesc(s) char **s; {            /* Expand backslash escapes */
2014:     int x, y, brace, radix;     /* Returns the int value */
2015:     char hd = '9';          /* Highest digit in radix */
2016:     char *p;
2017: 
2018:     p = *s;             /* pointer to beginning */
2019:     if (!p) return(-1);         /* watch out for null pointer */
2020:     x = *p++;               /* character at beginning */
2021:     if (x != CMDQ) return(-1);      /* make sure it's a backslash code */
2022: 
2023:     x = *p;             /* it is, get the next character */
2024:     if (x == '{') {         /* bracketed quantity? */
2025:     p++;                /* begin past bracket */
2026:     x = *p;
2027:     brace = 1;
2028:     } else brace = 0;
2029:     switch (x) {            /* Start interpreting */
2030:       case 'd':             /* Decimal radix indicator */
2031:       case 'D':
2032:     p++;                /* Just point past it and fall thru */
2033:       case '0':             /* Starts with digit */
2034:       case '1':
2035:       case '2':  case '3':  case '4':  case '5':
2036:       case '6':  case '7':  case '8':  case '9':
2037:     radix = 10;         /* Decimal */
2038:     hd = '9';           /* highest valid digit */
2039:     break;
2040:       case 'o':             /* Starts with o or O */
2041:       case 'O':
2042:     radix = 8;          /* Octal */
2043:     hd = '7';           /* highest valid digit */
2044:     p++;                /* point past radix indicator */
2045:     break;
2046:       case 'x':             /* Starts with x or X */
2047:       case 'X':
2048:     radix = 16;         /* Hexadecimal */
2049:     p++;                /* point past radix indicator */
2050:     break;
2051:       default:              /* All others */
2052: #ifdef COMMENT
2053:     *s = p+1;           /* Treat as quote of next char */
2054:     return(*p);
2055: #else
2056:     return(-1);
2057: #endif /* COMMENT */
2058:     }
2059:     /* For OS/2, there are "wide" characters required for the keyboard
2060:      * binding, i.e \644 and similar codes larger than 255 (byte).
2061:      * For this purpose, give up checking for < 256. If someone means
2062:      * \266 should result in \26 followed by a "6" character, he should
2063:      * always write \{26}6 anyway.  Now, return only the lower byte of
2064:      * the result, i.e. 10, but eat up the whole \266 sequence and
2065:      * put the wide result 266 into a global variable.  Yes, that's not
2066:      * the most beautiful programming style but requires the least
2067:      * amount of changes to other routines.
2068:      */
2069:     if (radix <= 10) {          /* Number in radix 8 or 10 */
2070:     for ( x = y = 0;
2071:           (*p) && (*p >= '0') && (*p <= hd)
2072: #ifdef OS2
2073:                    && (y < 4) && (x*radix < 768);
2074:               /* the maximum needed value \767 is still only 3 digits long */
2075:               /* while as octal it requires \1377, i.e. 4 digits */
2076: #else
2077:                    && (y < 3) && (x*radix < 256);
2078: #endif /* OS2 */
2079:           p++,y++) {
2080:         x = x * radix + (int) *p - 48;
2081:     }
2082: #ifdef OS2
2083:         wideresult = x;         /* Remember wide result */
2084:         x &= 255;
2085: #endif /* OS2 */
2086:     if (y == 0 || x > 255) {    /* No valid digits? */
2087:         *s = p;         /* point after it */
2088:         return(-1);         /* return failure. */
2089:     }
2090:     } else if (radix == 16) {       /* Special case for hex */
2091:     if ((x = unhex(*p++)) < 0) { *s = p - 1; return(-1); }
2092:     if ((y = unhex(*p++)) < 0) { *s = p - 2; return(-1); }
2093:     x = ((x << 4) & 0xF0) | (y & 0x0F);
2094: #ifdef OS2
2095:         wideresult = x;
2096:         if ((y = unhex(*p)) >= 0) {
2097:            p++;
2098:        wideresult = ((x << 4) & 0xFF0) | (y & 0x0F);
2099:            x = wideresult & 255;
2100:         }
2101: #endif /* OS2 */
2102:     } else x = -1;
2103:     if (brace && *p == '}' && x > -1)   /* Point past closing brace, if any */
2104:       p++;
2105:     *s = p;             /* Point to next char after sequence */
2106:     return(x);              /* Return value of sequence */
2107: }
2108: 
2109: int                 /* Convert hex string to int */
2110: #ifdef CK_ANSIC
2111: unhex(char x)
2112: #else
2113: unhex(x) char x;
2114: #endif /* CK_ANSIC */
2115: /* unhex */ {
2116: 
2117:     if (x >= '0' && x <= '9')       /* 0-9 is offset by hex 30 */
2118:       return(x - 0x30);
2119:     else if (x >= 'A' && x <= 'F')  /* A-F offset by hex 37 */
2120:       return(x - 0x37);
2121:     else if (x >= 'a' && x <= 'f')  /* a-f offset by hex 57 */
2122:       return(x - 0x57);         /* (obviously ASCII dependent) */
2123:     else return(-1);
2124: }
2125: 
2126: /* See if argument string is numeric */
2127: /* Returns 1 if OK, zero if not OK */
2128: /* If OK, string should be acceptable to atoi() */
2129: /* Allows leading space, sign */
2130: 
2131: int
2132: chknum(s) char *s; {            /* Check Numeric String */
2133:     int x = 0;              /* Flag for past leading space */
2134:     int y = 0;              /* Flag for digit seen */
2135:     char c;
2136:     debug(F110,"chknum",s,0);
2137:     while (c = *s++) {          /* For each character in the string */
2138:     switch (c) {
2139:       case SP:          /* Allow leading spaces */
2140:       case HT:
2141:         if (x == 0) continue;
2142:         else return(0);
2143:       case '+':         /* Allow leading sign */
2144:       case '-':
2145:         if (x == 0) x = 1;
2146:         else return(0);
2147:         break;
2148:       default:          /* After that, only decimal digits */
2149:         if (c >= '0' && c <= '9') {
2150:         x = y = 1;
2151:         continue;
2152:         } else return(0);
2153:     }
2154:     }
2155:     return(y);
2156: }
2157: 
2158: /*  L O W E R  --  Lowercase a string  */
2159: 
2160: int
2161: lower(s) char *s; {
2162:     int n = 0;
2163:     while (*s) {
2164:         if (isupper(*s)) *s = tolower(*s);
2165:         s++, n++;
2166:     }
2167:     return(n);
2168: }
2169: 
2170: /*  L O O K U P  --  Lookup the string in the given array of strings  */
2171: 
2172: /*
2173:  Call this way:  v = lookup(table,word,n,&x);
2174: 
2175:    table - a 'struct keytab' table.
2176:    word  - the target string to look up in the table.
2177:    n     - the number of elements in the table.
2178:    x     - address of an integer for returning the table array index.
2179: 
2180:  The keyword table must be arranged in ascending alphabetical order, and
2181:  all letters must be lowercase.
2182: 
2183:  Returns the keyword's associated value ( zero or greater ) if found,
2184:  with the variable x set to the array index, or:
2185: 
2186:   -3 if nothing to look up (target was null),
2187:   -2 if ambiguous,
2188:   -1 if not found.
2189: 
2190:  A match is successful if the target matches a keyword exactly, or if
2191:  the target is a prefix of exactly one keyword.  It is ambiguous if the
2192:  target matches two or more keywords from the table.
2193: */
2194: 
2195: int
2196: lookup(table,cmd,n,x) char *cmd; struct keytab table[]; int n, *x; {
2197: 
2198:     int i, v, cmdlen;
2199: 
2200: /* Lowercase & get length of target, if it's null return code -3. */
2201: 
2202:     if ((((cmdlen = lower(cmd))) == 0) || (n < 1)) return(-3);
2203: 
2204: /* Not null, look it up */
2205: 
2206:     for (i = 0; i < n-1; i++) {
2207:         if (!strcmp(table[i].kwd,cmd) ||
2208:            ((v = !strncmp(table[i].kwd,cmd,cmdlen)) &&
2209:              strncmp(table[i+1].kwd,cmd,cmdlen))) {
2210:                 *x = i;
2211:                 return(table[i].kwval);
2212:              }
2213:         if (v) return(-2);
2214:     }
2215: 
2216: /* Last (or only) element */
2217: 
2218:     if (!strncmp(table[n-1].kwd,cmd,cmdlen)) {
2219:         *x = n-1;
2220:         return(table[n-1].kwval);
2221:     } else return(-1);
2222: }

Defined functions

addbuf defined in line 1805; used 7 times
addhlp defined in line 1500; used 3 times
clrhlp defined in line 1491; used 4 times
cmdchardel defined in line 1960; used 7 times
cmdclrscn defined in line 1906; used 1 times
cmdecho defined in line 1976; used 4 times
cmdgetc defined in line 1862; used 2 times
cmdnewl defined in line 1932; used 2 times
cmini defined in line 273; used 14 times
cmkey2 defined in line 1244; used 3 times
cmpush defined in line 301; used 1 times
cmsavp defined in line 197; used 2 times
cmsetup defined in line 173; used 1 times
dmphlp defined in line 1526; used 4 times
gtword defined in line 1565; used 12 times
lower defined in line 2160; used 6 times
popcmd defined in line 248; never used
pushcmd defined in line 238; used 4 times
rdigits defined in line 1849; used 2 times
setatm defined in line 1824; used 28 times
stripq defined in line 519; never used
test defined in line 165; used 5 times
ungword defined in line 1555; used 3 times
unhex defined in line 2109; used 4 times
untab defined in line 532; used 1 times

Defined variables

VOID defined in line 1932; never used
atmbuf defined in line 132; used 63 times
atxbuf defined in line 134; used 37 times
atxn defined in line 135; used 26 times
atybuf defined in line 136; used 8 times
bp defined in line 142; used 64 times
cmddep defined in line 299; used 106 times
cmdv defined in line 8; used 1 times
cmflgs defined in line 117; used 47 times
cmfsav defined in line 118; used 6 times
cmp defined in line 292; used 51 times
cmp_b defined in line 296; used 35 times
cmp_c defined in line 295; used 6 times
cmp_i defined in line 294; used 8 times
cmprom defined in line 113; used 10 times
cmprxx defined in line 114; used 5 times
dfprom defined in line 115; used 1 times
filbuf defined in line 133; used 23 times
hlpbuf defined in line 131; used 14 times
hw defined in line 106; used 1 times
np defined in line 144; used 10 times
pp defined in line 143; used 20 times
psetf defined in line 100; used 2 times
savbuf defined in line 137; used 18 times
ungw defined in line 146; used 9 times

Defined struct's

cmp defined in line 287; used 6 times

Defined macros

PROML defined in line 111; used 5 times
cc defined in line 87; used 39 times
putchar defined in line 94; used 37 times
Last modified: 1992-11-24
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 11486
Valid CSS Valid XHTML 1.0 Strict