Commit | Line | Data |
---|---|---|
044a2feb C |
1 | int cmdmsk = 127; /* Command-terminal-to-C-Kermit character mask */ |
2 | ||
3 | #include "ckcdeb.h" /* Formats for debug(), etc. */ | |
4 | _PROTOTYP( int unhex, (char) ); | |
5 | ||
6 | #ifndef NOICP /* The rest only if interactive command parsing selected */ | |
7 | ||
8 | char *cmdv = "Command package 5A(053), 21 Nov 92"; | |
9 | ||
10 | /* C K U C M D -- Interactive command package for Unix */ | |
11 | ||
12 | /* | |
13 | Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET), | |
14 | Columbia University Center for Computing Activities. | |
15 | First released January 1985. | |
16 | Copyright (C) 1985, 1992, Trustees of Columbia University in the City of New | |
17 | York. Permission is granted to any individual or institution to use this | |
18 | software as long as it is not sold for profit. This copyright notice must be | |
19 | retained. This software may not be included in commercial products without | |
20 | written permission of Columbia University. | |
21 | */ | |
22 | ||
23 | /* | |
24 | Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP. | |
25 | Features: | |
26 | . parses and verifies keywords, filenames, text strings, numbers, other data | |
27 | . displays appropriate menu or help message when user types "?" | |
28 | . does keyword and filename completion when user types ESC or TAB | |
29 | . does partial filename completion | |
30 | . accepts any unique abbreviation for a keyword | |
31 | . allows keywords to have attributes, like "invisible" and "abbreviation" | |
32 | . can supply defaults for fields omitted by user | |
33 | . provides command line editing (character, word, and line deletion) | |
34 | . accepts input from keyboard, command files, or redirected stdin | |
35 | . allows for full or half duplex operation, character or line input | |
36 | . settable prompt, protected from deletion | |
37 | ||
38 | Functions: | |
39 | cmsetp - Set prompt (cmprom is prompt string) | |
40 | cmsavp - Save current prompt | |
41 | prompt - Issue prompt | |
42 | cmini - Clear the command buffer (before parsing a new command) | |
43 | cmres - Reset command buffer pointers (before reparsing) | |
44 | cmkey - Parse a keyword | |
45 | cmnum - Parse a number | |
46 | cmifi - Parse an input file name | |
47 | cmofi - Parse an output file name | |
48 | cmdir - Parse a directory name (UNIX only) | |
49 | cmfld - Parse an arbitrary field | |
50 | cmtxt - Parse a text string | |
51 | cmcfm - Parse command confirmation (end of line) | |
52 | ||
53 | Return codes: | |
54 | -3: no input provided when required | |
55 | -2: input was invalid (e.g. not a number when a number was required) | |
56 | -1: reparse required (user deleted into a preceding field) | |
57 | 0 or greater: success | |
58 | See individual functions for greater detail. | |
59 | ||
60 | Before using these routines, the caller should #include ckucmd.h, and set the | |
61 | program's prompt by calling cmsetp(). If the file parsing functions cmifi, | |
62 | cmofi, or cmdir are to be used, this module must be linked with a ck?fio file | |
63 | system support module for the appropriate system, e.g. ckufio for Unix. If | |
64 | the caller puts the terminal in character wakeup ("cbreak") mode with no echo, | |
65 | then these functions will provide line editing -- character, word, and line | |
66 | deletion, as well as keyword and filename completion upon ESC and help | |
67 | strings, keyword, or file menus upon '?'. If the caller puts the terminal | |
68 | into character wakeup/noecho mode, care should be taken to restore it before | |
69 | exit from or interruption of the program. If the character wakeup mode is not | |
70 | set, the system's own line editor may be used. | |
71 | ||
72 | NOTE: Contrary to expectations, many #ifdef's have been added to this module. | |
73 | Any operation requiring an #ifdef (like clear screen, get character from | |
74 | keyboard, erase character from screen, etc) should eventually be turned into a | |
75 | call to a function that is defined in ck?tio.c, but then all the ck?tio.c | |
76 | modules would have to be changed... | |
77 | */ | |
78 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | \f | |
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 | } |