]> andersk Git - moira.git/blame - clients/mmoira/parser.c
fix duplicated accelerators
[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");
107 kill(getpid(), SIGSTOP);
108 /* when continued, fall through to */
109 case 'R' - '@':
110 *p = 0;
111 printf("\r\n%s%s", prompt, line);
112 fflush(stdout);
113 break;
114 case 'U' - '@':
115 while (p-- > &line[0])
116 write(1, "\b \b", 3);
117 *(++p) = 0;
118 printf("\r%s", prompt);
119 fflush(stdout);
120 break;
121 case '\n':
122 case '\r':
123 if ((val = do_parse(line, nd, prompt)) != ERROR) {
124 write(1, "\r\n", 2);
125 return(val);
126 }
127 p = &line[strlen(line)];
128 *p = 0;
129 printf("\r\n%s%s", prompt, line);
130 fflush(stdout);
131 break;
132 default:
133 putchar(c);
134 *p++ = c;
135 *p = 0;
136 }
137 }
138}
139
140
141/* called when a ? is typed. This parses the line as far as possible, then
142 * displays possible completions and help strings.
143 */
144
145do_help(line, nod, prompt)
146char *line;
147struct parse_node *nod;
148char *prompt;
149{
150 char *ln;
151 struct parse_node *n, *nd, *last;
152 int kw, state, nomatch;
153 struct parse_node *best, *ambig;
154
155 n = nod;
156 state = MATCH;
157 for (ln = line; n && state == MATCH; best && (n = best->p_next)) {
158 last = best;
159 state = single_parse(&ln, n, &best, &ambig, &nomatch);
160 }
161 if (*ln && ((best && best->p_menu) || n == NULNODE))
162 state = NOMATCH;
163 switch (state) {
164 case NOMATCH:
165 if (!*ln) {
166 if (last && last->p_menu && *line)
167 printf("Carriage return, or ");
168 break;
169 }
170 ln += nomatch;
171 *ln = 0;
172 write(1, "\r\nNOT a valid command line", 26);
173 return;
174 case AMBIG:
175 write(1, "one of the following:\r\n ", 26);
176 for (; ambig; ambig = ambig->p_link)
177 printf("%s ", ambig->p_word);
178 fflush(stdout);
179 return;
180 case INCOMP:
181 printf("one of the following:\r\n %s", best->p_word);
182 fflush(stdout);
183 return;
184 default:
185 write(1, eolmsg, strlen(eolmsg));
186 return;
187 }
188 kw = 0;
189 for (nd = n; nd; nd = nd->p_peer) {
190 if (!kw) {
191 write(1, "one of the following:\r\n ", 26);
192 kw = 3;
193 }
194 if (kw + strlen(nd->p_word) > 72) {
195 printf("\r\n %s ", nd->p_word);
196 fflush(stdout);
197 kw = 4 + strlen(nd->p_word);
198 } else {
199 printf("%s ", nd->p_word);
200 fflush(stdout);
201 kw += 1 + strlen(nd->p_word);
202 }
203 }
204}
205
206
207/* Do escape processing. If a unique completion exists, use it. Otherwise,
208 * do the same as ?.
209 */
210
211do_complete(line, nod, prompt)
212char *line;
213struct parse_node *nod;
214char *prompt;
215{
216 struct parse_node *n, *nd;
217 char *ln;
218 char *tmp;
219 int state, nomatch;
220 struct parse_node *best, *ambig;
221
222 ln = line;
223 state = MATCH;
224 nd = nod;
225 while (state == MATCH) {
226 tmp = ln;
227 state = single_parse(&ln, nd, &best, &ambig, &nomatch);
228 if (state == MATCH)
229 nd = best->p_next;
230 }
231 switch (state) {
232 case AMBIG:
233/* printf("ln %X, tmp %X, ln - tmp %d, nomatch %d\r\n", ln, tmp, ln-tmp, nomatch);
234 if (ln - tmp < nomatch) {
235 printf("attempting partial complete\r\n");
236 fflush(stdout);
237 sleep(1);
238 while ((ln > line) && !isspace(*ln))
239 ln--;
240 tmp = ambig->p_word;
241 while (nomatch--)
242 *ln++ = *tmp++;
243 *ln = 0;
244 putchar(7);
245 return;
246 } */
247 /* fall through to: */
248 case NOMATCH:
249 if (!(nd) || (nd->p_peer)) {
250 write(1, " ", 2);
251 do_help(line, nod, prompt);
252 printf("\r\n%s%s", prompt, line);
253 fflush(stdout);
254 return;
255 }
256 best = nd;
257 *ln++ = 'x';
258 /* fall through to incomplete case */
259 case INCOMP:
260 ln = tmp;
261 do {
262 tmp = best->p_word;
263 while (*tmp)
264 *ln++ = *tmp++;
265 *ln++ = ' ';
266 *ln = 0;
267 } while (best->p_next && !best->p_next->p_peer &&
268 !best->p_menu && (best = best->p_next));
269 printf("\r%s%s", prompt, line);
270 fflush(stdout);
271 break;
272 default:
273 write(1, "We shouldn't get here (parser error)\r\n", 38);
274 }
275}
276
277
278/* Single parse parses through a single level of the parse tree.
279 * There are 4 possible outcomes:
280 * an exact match is made: the matching node is returned, ambig = 0
281 * an incomplete match: the matching node is returned, ambig = node
282 * ambiguous: nothing is returned, ambig = list of completions
283 * no matches: nothing is returned, ambig = 0
284 */
285
286int single_parse(line, nd, best, ambig, nomatch)
287char **line;
288struct parse_node *nd;
289struct parse_node **best;
290struct parse_node **ambig;
291int *nomatch;
292{
293 char *p;
294 char c; /* char we're working on (from line) */
295 struct parse_node *n; /* node loop counter */
296 struct parse_node *tail; /* tmp used to build chains */
297 int i; /* loop counter */
298 int match; /* how many chars have we matched? */
299 int len; /* length of this keyword */
300
301#ifdef DEBUG
302 printf("single_parse(\"%s\") -> ", *line);
303#endif /* DEBUG */
304 *ambig = tail = *best = NULNODE;
305 match = *nomatch = 0;
306 /* skip leading whitespace */
307 while (isspace(**line))
308 (*line)++;
309 /* step through each node */
310 for (n = nd; n; n = n->p_peer) {
311 len = strlen(n->p_word);
312 /* step through each character in line */
313 for (i = 0; 1; i++) {
314 /* if at end of word on line */
315 if (isspace((*line)[i]) || (*line)[i] == 0) {
316 /* another ambiguous match */
317 if (i == match && i) {
318 tail->p_link = n;
319 tail = n;
320 n->p_link = NULNODE;
321 }
322 /* a better match */
323 if (i > match) {
324 match = i;
325 *best = tail = *ambig = n;
326 n->p_link = NULNODE;
327 }
328 break;
329 }
330 if (isupper(c = (*line)[i]))
331 c = tolower(c);
332 if (c != n->p_word[i]) {
333 if (i > *nomatch)
334 *nomatch = i;
335 break;
336 }
337 }
338 }
339 if (match > 0) {
340 (*line) += match;
341 if (tail != *ambig) {
342 *best = NULNODE;
343 *nomatch = match;
344 if (isspace(**line)) {
345#ifdef DEBUG
346 printf("NOMATCH\n");
347#endif /* DEBUG */
348 return(NOMATCH);
349 } else {
350#ifdef DEBUG
351 printf("AMBIG\n");
352#endif /* DEBUG */
353 return(AMBIG);
354 }
355 }
356 if (isspace(**line)) {
357 *ambig = NULNODE;
358 while (isspace(**line))
359 (*line)++;
360#ifdef DEBUG
361 printf("MATCH\n");
362#endif /* DEBUG */
363 return(MATCH);
364 }
365#ifdef DEBUG
366 printf("INCOMP\n");
367#endif /* DEBUG */
368 return(INCOMP);
369 }
370 *ambig = tail = *best = NULNODE;
371#ifdef DEBUG
372 printf("NOMATCH\n");
373#endif /* DEBUG */
374 return(NOMATCH);
375}
376
377
378/* execute the line. First check to see that the line is legal. If not,
379 * do_help the line & return ERROR. If so, execute each node passed through,
380 * and return OK (or EXIT if an EXIT node was encountered).
381 */
382
383int do_parse(line, nod, prompt)
384char *line;
385struct parse_node *nod;
386char *prompt;
387{
388 struct parse_node *n, *nd, *last;
389 char *ln, *tmp;
390 int state, i;
391 struct parse_node *best, *ambig;
392 int nomatch;
393 EntryForm *f;
394
395 ln = line;
396 n = nod;
397 state = MATCH;
398 best = NULNODE;
399 while (n && ((state == MATCH) || (state == INCOMP))) {
400 last = best;
401 state = single_parse(&ln, n, &best, &ambig, &nomatch);
402#ifdef DEBUG
403 printf("best = %s, best->next = 0x%x\r\n", best ? best->p_word : "",
404 best ? best->p_next : 0);
405#endif /* DEBUG */
406 if ((state == NOMATCH) && (!*ln) && (n == nod))
407 return(OK);
408 if ((state == MATCH) || (state == INCOMP)) {
409 n = best->p_next;
410 }
411 }
412 if (((state == AMBIG) || (state == NOMATCH)) && !*ln)
413 for (; n; n = n->p_peer)
414 if (last && last->p_menu) {
415 n = last;
416 state = MATCH;
417 break;
418 }
419 if (state == NOMATCH && !*ln) {
420 while (last && last->p_next && !last->p_next->p_peer && !last->p_menu)
421 last = last->p_next;
422 if (last && last->p_menu) {
423 state = MATCH;
424 best = last;
425 }
426 }
427
428 if ((state == NOMATCH) || (state == AMBIG)) {
429 write(1, " BAD command, ", 16);
430 do_help(line, nod, prompt);
431 return(ERROR);
432 }
433 if (!best)
434 best = n;
435 write(1, "\r\n", 2);
436 cooked_mode();
437 for (i = 0; line[i] && !isspace(line[i]); i++);
438 if (!strncmp("help", line, i))
439 help(best->p_menu->form);
440 else
441 MoiraMenuRequest(best->p_menu);
442 raw_mode();
443 return(OK);
444}
This page took 0.122025 seconds and 5 git commands to generate.