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