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