BSD 4_4_Lite2 development
[unix-history] / usr / src / contrib / kermit-5A.188 / ckucmd.c
CommitLineData
044a2feb
C
1int 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
8char *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/*
24Modeled after the DECSYSTEM-20 command parser (the COMND JSYS), RIP.
25Features:
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
38Functions:
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
53Return 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
58See individual functions for greater detail.
59
60Before using these routines, the caller should #include ckucmd.h, and set the
61program's prompt by calling cmsetp(). If the file parsing functions cmifi,
62cmofi, or cmdir are to be used, this module must be linked with a ck?fio file
63system support module for the appropriate system, e.g. ckufio for Unix. If
64the caller puts the terminal in character wakeup ("cbreak") mode with no echo,
65then these functions will provide line editing -- character, word, and line
66deletion, as well as keyword and filename completion upon ESC and help
67strings, keyword, or file menus upon '?'. If the caller puts the terminal
68into character wakeup/noecho mode, care should be taken to restore it before
69exit from or interruption of the program. If the character wakeup mode is not
70set, the system's own line editor may be used.
71
72NOTE: Contrary to expectations, many #ifdef's have been added to this module.
73Any operation requiring an #ifdef (like clear screen, get character from
74keyboard, erase character from screen, etc) should eventually be turned into a
75call to a function that is defined in ck?tio.c, but then all the ck?tio.c
76modules 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
99static
100int 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
105static
106int 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
113char cmprom[PROML+1]; /* Program's prompt */
114char cmprxx[PROML+1]; /* Program's prompt, unevaluated */
115char *dfprom = "Command? "; /* Default prompt */
116
117int cmflgs; /* Command flags */
118int cmfsav; /* A saved version of them */
119
120#ifdef DCMDBUF
121char *cmdbuf; /* Command buffer */
122char *savbuf; /* Help string buffer */
123char *hlpbuf; /* Atom buffer */
124char *atmbuf; /* File name buffer */
125char *atxbuf; /* For expanding the atom buffer */
126int atxn; /* Length of expansion buffer */
127char *atybuf; /* For copying atom buffer */
128char *filbuf; /* Buffer to save copy of command */
129#else
130char cmdbuf[CMDBL+4]; /* Command buffer */
131char hlpbuf[HLPBL+4]; /* Help string buffer */
132char atmbuf[ATMBL+4]; /* Atom buffer */
133char filbuf[ATMBL+4]; /* File name buffer */
134char atxbuf[CMDBL+4]; /* For expanding the atom buffer */
135int atxn; /* Length of expansion buffer */
136char atybuf[ATMBL+4]; /* For copying atom buffer */
137char savbuf[CMDBL+4]; /* Buffer to save copy of command */
138#endif /* DCMDBUF */
139
140/* Command buffer pointers */
141
142static char *bp, /* Current command buffer position */
143 *pp, /* Start of current field */
144 *np; /* Start of next field */
145
146static 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
165static int
166test(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
173int
174cmsetup() {
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
189VOID
190cmsetp(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
197VOID
198#ifdef CK_ANSIC
199cmsavp(char s[], int n)
200#else
201cmsavp(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
210VOID
211prompt(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
238VOID
239pushcmd() { /* 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... */
248VOID
249popcmd() {
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
258VOID
259cmres() {
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/*
269The 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*/
273VOID
274cmini(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
287struct cmp {
288 int i[5]; /* stack for integers */
289 char *c[3]; /* stack for pointers */
290 char *b[8]; /* stack for buffer contents */
291};
292struct cmp *cmp = 0;
293#else
294int cmp_i[CMDDEP+1][5]; /* Stack for integers */
295char *cmp_c[CMDDEP+1][5]; /* for misc pointers */
296char *cmp_b[CMDDEP+1][7]; /* for buffer contents pointers */
297#endif /* DCMDBUF */
298
299int cmddep = -1; /* Current stack depth */
300
301int
302cmpush() { /* 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
411int
412cmpop() { /* 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
519VOID
520stripq(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 */
532VOID
533untab(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*/
556int
557cmnum(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*/
612int
613cmofi(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*/
671int
672cmifi(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*/
961int
962cmdir(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*/
1047int
1048cmfld(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*/
1140int
1141cmtxt(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*/
1239int
1240cmkey(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}
1244int
1245cmkey2(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
1258debug(F101,"cmkey: table length","",n);
1259debug(F101," cmflgs","",cmflgs);
1260debug(F101," zz","",zz);
1261while (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}
1424int
1425chktok(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*/
1440int
1441cmcfm() {
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
1491VOID
1492clrhlp() { /* 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
1500VOID
1501addhlp(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
1526VOID
1527dmphlp() { /* 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/*
1537Usage: retcode = gtword();
1538
1539Returns:
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
1548With:
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
1555int
1556ungword() { /* 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
1565int
1566gtword() {
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
1805int
1806addbuf(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
1824int
1825setatm(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
1849int
1850rdigits(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
1862int
1863cmdgetc() { /* 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*/
1906cmdclrscn() { /* 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
1932VOID /* What to echo at end of command */
1933#ifdef CK_ANSIC
1934cmdnewl(char c)
1935#else
1936cmdnewl(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
1960VOID
1961cmdchardel() { /* 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
1976VOID
1977#ifdef CK_ANSIC
1978cmdecho(char c, int quote)
1979#else
1980cmdecho(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
2012int
2013xxesc(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
2109int /* Convert hex string to int */
2110#ifdef CK_ANSIC
2111unhex(char x)
2112#else
2113unhex(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
2131int
2132chknum(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
2160int
2161lower(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
2195int
2196lookup(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}