]> andersk Git - moira.git/blame - clients/mmoira/parser.c
Used /bin/sh format instead of /bin/csh format, by accident.
[moira.git] / clients / mmoira / parser.c
CommitLineData
053a78b2 1/* $Header$
2 *
3 * TOPS-20 style command parser
4 *
5 */
6
7#include <stdio.h>
8#include <ctype.h>
9#include <sys/signal.h>
10#include <X11/Intrinsic.h>
11#include "mmoira.h"
12#include "parser.h"
13
14
15/* trivial absolute-value macro */
16#define abs(i) ((i) < 0 ? -(i) : (i))
17
18/* message used when end of parse tree is reached */
19char eolmsg[] = "return to execute command";
20
21
22/* main entry point. Takes the prompt and top node of the parse tree, and
23 * will collect user's input, providing help and completion. When entry
24 * is complete (<return> is pressed), the actions called for by each node
25 * in the parse tree which is traversed will be executed.
26 */
27
28int parser(prompt, nd)
29char *prompt;
30struct parse_node *nd;
31{
32 char line[BUFLEN];
33 char *p, c;
34 int val;
35
36 p = &line[0];
37 *p = 0;
38 write(1, prompt, strlen(prompt));
39 for (c = (getchar() & 0x7F); 1; c = (getchar() & 0x7F)) {
40 if (c == 0)
41 continue;
42 switch (c) {
43 case 127:
44 case '\b':
45 if (p == &line[0]) {
46 putchar(7);
47 break;
48 }
49 if (*(--p) == 'I' - '@') {
50 *p = 0;
51 printf("\r%s%s", prompt, line);
52 fflush(stdout);
53 break;
54 }
55 *p = 0;
56 write(1, "\b \b", 3);
57 break;
58 case 'C' - '@':
59 case 'G' - '@':
60 write(1, " \007ABORT\r\n", 9);
61 return(ABORT);
62 case 'Q' - '@':
63 case 'V' - '@':
64 putchar('\\');
65 c = getchar();
66 if (c < ' ')
67 printf("\b^%c", c + '@');
68 else
69 printf("\b%c", c);
70 fflush(stdout);
71 *p++ = c;
72 break;
73 case 'W' - '@':
74 if (p > &line[0])
75 p--;
76 while ((p >= &line[0]) && isspace(*p)) {
77 write(1, "\b \b", 3);
78 p--;
79 }
80 while ((p >= &line[0]) && !isspace(*p)) {
81 write(1, "\b \b", 3);
82 p--;
83 }
84 if (p > &line[0]) {
85 p++;
86 } else {
87 p = &line[0];
88 }
89 *p = 0;
90 break;
91 case '?':
92 write(1, "? ", 3);
93 *p = 0;
94 do_help(&line[0], nd, prompt);
95 p = &line[strlen(line)];
96 printf("\n\r%s%s", prompt, line);
97 fflush(stdout);
98 break;
99 case '[' - '@':
100 case '\t':
101 *p = 0;
102 do_complete(&line[0], nd, prompt);
103 p = &line[strlen(line)];
104 break;
105 case 'Z' - '@':
106 printf("\r\n");
f631c332 107 cooked_mode();
053a78b2 108 kill(getpid(), SIGSTOP);
f631c332 109 raw_mode();
053a78b2 110 /* when continued, fall through to */
111 case 'R' - '@':
112 *p = 0;
113 printf("\r\n%s%s", prompt, line);
114 fflush(stdout);
115 break;
116 case 'U' - '@':
117 while (p-- > &line[0])
118 write(1, "\b \b", 3);
119 *(++p) = 0;
120 printf("\r%s", prompt);
121 fflush(stdout);
122 break;
123 case '\n':
124 case '\r':
125 if ((val = do_parse(line, nd, prompt)) != ERROR) {
126 write(1, "\r\n", 2);
127 return(val);
128 }
129 p = &line[strlen(line)];
130 *p = 0;
131 printf("\r\n%s%s", prompt, line);
132 fflush(stdout);
133 break;
134 default:
135 putchar(c);
136 *p++ = c;
137 *p = 0;
138 }
139 }
140}
141
142
143/* called when a ? is typed. This parses the line as far as possible, then
144 * displays possible completions and help strings.
145 */
146
147do_help(line, nod, prompt)
148char *line;
149struct parse_node *nod;
150char *prompt;
151{
152 char *ln;
153 struct parse_node *n, *nd, *last;
154 int kw, state, nomatch;
155 struct parse_node *best, *ambig;
156
157 n = nod;
158 state = MATCH;
e8e78110 159 best = NULNODE;
053a78b2 160 for (ln = line; n && state == MATCH; best && (n = best->p_next)) {
161 last = best;
162 state = single_parse(&ln, n, &best, &ambig, &nomatch);
163 }
164 if (*ln && ((best && best->p_menu) || n == NULNODE))
165 state = NOMATCH;
166 switch (state) {
167 case NOMATCH:
168 if (!*ln) {
169 if (last && last->p_menu && *line)
170 printf("Carriage return, or ");
171 break;
172 }
173 ln += nomatch;
174 *ln = 0;
175 write(1, "\r\nNOT a valid command line", 26);
176 return;
177 case AMBIG:
178 write(1, "one of the following:\r\n ", 26);
179 for (; ambig; ambig = ambig->p_link)
180 printf("%s ", ambig->p_word);
181 fflush(stdout);
182 return;
183 case INCOMP:
184 printf("one of the following:\r\n %s", best->p_word);
185 fflush(stdout);
186 return;
187 default:
188 write(1, eolmsg, strlen(eolmsg));
189 return;
190 }
191 kw = 0;
192 for (nd = n; nd; nd = nd->p_peer) {
193 if (!kw) {
194 write(1, "one of the following:\r\n ", 26);
195 kw = 3;
196 }
197 if (kw + strlen(nd->p_word) > 72) {
198 printf("\r\n %s ", nd->p_word);
199 fflush(stdout);
200 kw = 4 + strlen(nd->p_word);
201 } else {
202 printf("%s ", nd->p_word);
203 fflush(stdout);
204 kw += 1 + strlen(nd->p_word);
205 }
206 }
207}
208
209
210/* Do escape processing. If a unique completion exists, use it. Otherwise,
211 * do the same as ?.
212 */
213
214do_complete(line, nod, prompt)
215char *line;
216struct parse_node *nod;
217char *prompt;
218{
219 struct parse_node *n, *nd;
220 char *ln;
221 char *tmp;
222 int state, nomatch;
223 struct parse_node *best, *ambig;
224
225 ln = line;
226 state = MATCH;
227 nd = nod;
228 while (state == MATCH) {
229 tmp = ln;
230 state = single_parse(&ln, nd, &best, &ambig, &nomatch);
231 if (state == MATCH)
232 nd = best->p_next;
233 }
234 switch (state) {
235 case AMBIG:
236/* printf("ln %X, tmp %X, ln - tmp %d, nomatch %d\r\n", ln, tmp, ln-tmp, nomatch);
237 if (ln - tmp < nomatch) {
238 printf("attempting partial complete\r\n");
239 fflush(stdout);
240 sleep(1);
241 while ((ln > line) && !isspace(*ln))
242 ln--;
243 tmp = ambig->p_word;
244 while (nomatch--)
245 *ln++ = *tmp++;
246 *ln = 0;
247 putchar(7);
248 return;
249 } */
250 /* fall through to: */
251 case NOMATCH:
252 if (!(nd) || (nd->p_peer)) {
253 write(1, " ", 2);
254 do_help(line, nod, prompt);
255 printf("\r\n%s%s", prompt, line);
256 fflush(stdout);
257 return;
258 }
259 best = nd;
260 *ln++ = 'x';
261 /* fall through to incomplete case */
262 case INCOMP:
263 ln = tmp;
264 do {
265 tmp = best->p_word;
266 while (*tmp)
267 *ln++ = *tmp++;
268 *ln++ = ' ';
269 *ln = 0;
270 } while (best->p_next && !best->p_next->p_peer &&
271 !best->p_menu && (best = best->p_next));
272 printf("\r%s%s", prompt, line);
273 fflush(stdout);
274 break;
275 default:
276 write(1, "We shouldn't get here (parser error)\r\n", 38);
277 }
278}
279
280
281/* Single parse parses through a single level of the parse tree.
282 * There are 4 possible outcomes:
283 * an exact match is made: the matching node is returned, ambig = 0
284 * an incomplete match: the matching node is returned, ambig = node
285 * ambiguous: nothing is returned, ambig = list of completions
286 * no matches: nothing is returned, ambig = 0
287 */
288
289int single_parse(line, nd, best, ambig, nomatch)
290char **line;
291struct parse_node *nd;
292struct parse_node **best;
293struct parse_node **ambig;
294int *nomatch;
295{
296 char *p;
297 char c; /* char we're working on (from line) */
298 struct parse_node *n; /* node loop counter */
299 struct parse_node *tail; /* tmp used to build chains */
300 int i; /* loop counter */
301 int match; /* how many chars have we matched? */
302 int len; /* length of this keyword */
303
304#ifdef DEBUG
305 printf("single_parse(\"%s\") -> ", *line);
306#endif /* DEBUG */
307 *ambig = tail = *best = NULNODE;
308 match = *nomatch = 0;
309 /* skip leading whitespace */
310 while (isspace(**line))
311 (*line)++;
312 /* step through each node */
313 for (n = nd; n; n = n->p_peer) {
314 len = strlen(n->p_word);
315 /* step through each character in line */
316 for (i = 0; 1; i++) {
317 /* if at end of word on line */
318 if (isspace((*line)[i]) || (*line)[i] == 0) {
319 /* another ambiguous match */
320 if (i == match && i) {
321 tail->p_link = n;
322 tail = n;
323 n->p_link = NULNODE;
324 }
325 /* a better match */
326 if (i > match) {
327 match = i;
328 *best = tail = *ambig = n;
329 n->p_link = NULNODE;
330 }
331 break;
332 }
333 if (isupper(c = (*line)[i]))
334 c = tolower(c);
335 if (c != n->p_word[i]) {
336 if (i > *nomatch)
337 *nomatch = i;
338 break;
339 }
340 }
341 }
342 if (match > 0) {
343 (*line) += match;
344 if (tail != *ambig) {
345 *best = NULNODE;
346 *nomatch = match;
347 if (isspace(**line)) {
348#ifdef DEBUG
349 printf("NOMATCH\n");
350#endif /* DEBUG */
351 return(NOMATCH);
352 } else {
353#ifdef DEBUG
354 printf("AMBIG\n");
355#endif /* DEBUG */
356 return(AMBIG);
357 }
358 }
359 if (isspace(**line)) {
360 *ambig = NULNODE;
361 while (isspace(**line))
362 (*line)++;
363#ifdef DEBUG
364 printf("MATCH\n");
365#endif /* DEBUG */
366 return(MATCH);
367 }
368#ifdef DEBUG
369 printf("INCOMP\n");
370#endif /* DEBUG */
371 return(INCOMP);
372 }
373 *ambig = tail = *best = NULNODE;
374#ifdef DEBUG
375 printf("NOMATCH\n");
376#endif /* DEBUG */
377 return(NOMATCH);
378}
379
380
381/* execute the line. First check to see that the line is legal. If not,
382 * do_help the line & return ERROR. If so, execute each node passed through,
383 * and return OK (or EXIT if an EXIT node was encountered).
384 */
385
386int do_parse(line, nod, prompt)
387char *line;
388struct parse_node *nod;
389char *prompt;
390{
391 struct parse_node *n, *nd, *last;
392 char *ln, *tmp;
393 int state, i;
394 struct parse_node *best, *ambig;
395 int nomatch;
396 EntryForm *f;
397
398 ln = line;
399 n = nod;
400 state = MATCH;
401 best = NULNODE;
402 while (n && ((state == MATCH) || (state == INCOMP))) {
403 last = best;
404 state = single_parse(&ln, n, &best, &ambig, &nomatch);
405#ifdef DEBUG
406 printf("best = %s, best->next = 0x%x\r\n", best ? best->p_word : "",
407 best ? best->p_next : 0);
408#endif /* DEBUG */
409 if ((state == NOMATCH) && (!*ln) && (n == nod))
410 return(OK);
411 if ((state == MATCH) || (state == INCOMP)) {
412 n = best->p_next;
413 }
414 }
415 if (((state == AMBIG) || (state == NOMATCH)) && !*ln)
416 for (; n; n = n->p_peer)
417 if (last && last->p_menu) {
418 n = last;
419 state = MATCH;
420 break;
421 }
422 if (state == NOMATCH && !*ln) {
423 while (last && last->p_next && !last->p_next->p_peer && !last->p_menu)
424 last = last->p_next;
425 if (last && last->p_menu) {
426 state = MATCH;
427 best = last;
428 }
429 }
430
431 if ((state == NOMATCH) || (state == AMBIG)) {
432 write(1, " BAD command, ", 16);
433 do_help(line, nod, prompt);
434 return(ERROR);
435 }
436 if (!best)
437 best = n;
438 write(1, "\r\n", 2);
439 cooked_mode();
440 for (i = 0; line[i] && !isspace(line[i]); i++);
441 if (!strncmp("help", line, i))
442 help(best->p_menu->form);
443 else
444 MoiraMenuRequest(best->p_menu);
445 raw_mode();
446 return(OK);
447}
This page took 0.119887 seconds and 5 git commands to generate.