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