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