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