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