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

Defined functions

addbuf defined in line 984; used 6 times
addhlp defined in line 731; used 2 times
chkwld defined in line 403; used 4 times
clrhlp defined in line 723; used 3 times
cmfld defined in line 421; used 2 times
cmifi defined in line 250; used 2 times
cmini defined in line 146; used 5 times
cmnum defined in line 175; used 9 times
cmofi defined in line 209; used 6 times
cmres defined in line 132; used 5 times
cmsavp defined in line 116; used 1 times
cmsetp defined in line 105; used 9 times
cmtxt defined in line 483; used 20 times
digits defined in line 1014; used 1 times
dmphlp defined in line 756; used 3 times
getwd defined in line 837; used 9 times
lookup defined in line 788; used 3 times
lower defined in line 1024; used 1 times
prompt defined in line 124; used 3 times
setatm defined in line 998; used 12 times
stripq defined in line 153; used 3 times
test defined in line 1035; used 1 times

Defined variables

atmbuf defined in line 91; used 34 times
bp defined in line 96; used 39 times
cmdbuf defined in line 89; used 32 times
cmdv defined in line 1; used 1 times
cmerrp defined in line 85; used 2 times
cmflgs defined in line 87; used 25 times
cmprom defined in line 82; used 12 times
dfprom defined in line 83; used 1 times
filbuf defined in line 92; used 4 times
hlpbuf defined in line 90; used 7 times
hw defined in line 75; used 1 times
np defined in line 98; used 5 times
pp defined in line 97; used 11 times
psetf defined in line 71; used 2 times

Defined macros

PROML defined in line 80; used 4 times
Last modified: 1985-08-14
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 2910
Valid CSS Valid XHTML 1.0 Strict