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: }