3 * TOPS-20 style command parser
9 #include <sys/signal.h>
10 #include <X11/Intrinsic.h>
15 /* trivial absolute-value macro */
16 #define abs(i) ((i) < 0 ? -(i) : (i))
18 /* message used when end of parse tree is reached */
19 char eolmsg[] = "return to execute command";
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.
28 int parser(prompt, nd)
30 struct parse_node *nd;
38 write(1, prompt, strlen(prompt));
39 for (c = (getchar() & 0x7F); 1; c = (getchar() & 0x7F)) {
49 if (*(--p) == 'I' - '@') {
51 printf("\r%s%s", prompt, line);
60 write(1, " \007ABORT\r\n", 9);
67 printf("\b^%c", c + '@');
76 while ((p >= &line[0]) && isspace(*p)) {
80 while ((p >= &line[0]) && !isspace(*p)) {
94 do_help(&line[0], nd, prompt);
95 p = &line[strlen(line)];
96 printf("\n\r%s%s", prompt, line);
102 do_complete(&line[0], nd, prompt);
103 p = &line[strlen(line)];
108 kill(getpid(), SIGSTOP);
110 /* when continued, fall through to */
113 printf("\r\n%s%s", prompt, line);
117 while (p-- > &line[0])
118 write(1, "\b \b", 3);
120 printf("\r%s", prompt);
125 if ((val = do_parse(line, nd, prompt)) != ERROR) {
129 p = &line[strlen(line)];
131 printf("\r\n%s%s", prompt, line);
143 /* called when a ? is typed. This parses the line as far as possible, then
144 * displays possible completions and help strings.
147 do_help(line, nod, prompt)
149 struct parse_node *nod;
153 struct parse_node *n, *nd, *last;
154 int kw, state, nomatch;
155 struct parse_node *best, *ambig;
159 for (ln = line; n && state == MATCH; best && (n = best->p_next)) {
161 state = single_parse(&ln, n, &best, &ambig, &nomatch);
163 if (*ln && ((best && best->p_menu) || n == NULNODE))
168 if (last && last->p_menu && *line)
169 printf("Carriage return, or ");
174 write(1, "\r\nNOT a valid command line", 26);
177 write(1, "one of the following:\r\n ", 26);
178 for (; ambig; ambig = ambig->p_link)
179 printf("%s ", ambig->p_word);
183 printf("one of the following:\r\n %s", best->p_word);
187 write(1, eolmsg, strlen(eolmsg));
191 for (nd = n; nd; nd = nd->p_peer) {
193 write(1, "one of the following:\r\n ", 26);
196 if (kw + strlen(nd->p_word) > 72) {
197 printf("\r\n %s ", nd->p_word);
199 kw = 4 + strlen(nd->p_word);
201 printf("%s ", nd->p_word);
203 kw += 1 + strlen(nd->p_word);
209 /* Do escape processing. If a unique completion exists, use it. Otherwise,
213 do_complete(line, nod, prompt)
215 struct parse_node *nod;
218 struct parse_node *n, *nd;
222 struct parse_node *best, *ambig;
227 while (state == MATCH) {
229 state = single_parse(&ln, nd, &best, &ambig, &nomatch);
235 /* printf("ln %X, tmp %X, ln - tmp %d, nomatch %d\r\n", ln, tmp, ln-tmp, nomatch);
236 if (ln - tmp < nomatch) {
237 printf("attempting partial complete\r\n");
240 while ((ln > line) && !isspace(*ln))
249 /* fall through to: */
251 if (!(nd) || (nd->p_peer)) {
253 do_help(line, nod, prompt);
254 printf("\r\n%s%s", prompt, line);
260 /* fall through to incomplete case */
269 } while (best->p_next && !best->p_next->p_peer &&
270 !best->p_menu && (best = best->p_next));
271 printf("\r%s%s", prompt, line);
275 write(1, "We shouldn't get here (parser error)\r\n", 38);
280 /* Single parse parses through a single level of the parse tree.
281 * There are 4 possible outcomes:
282 * an exact match is made: the matching node is returned, ambig = 0
283 * an incomplete match: the matching node is returned, ambig = node
284 * ambiguous: nothing is returned, ambig = list of completions
285 * no matches: nothing is returned, ambig = 0
288 int single_parse(line, nd, best, ambig, nomatch)
290 struct parse_node *nd;
291 struct parse_node **best;
292 struct parse_node **ambig;
296 char c; /* char we're working on (from line) */
297 struct parse_node *n; /* node loop counter */
298 struct parse_node *tail; /* tmp used to build chains */
299 int i; /* loop counter */
300 int match; /* how many chars have we matched? */
301 int len; /* length of this keyword */
304 printf("single_parse(\"%s\") -> ", *line);
306 *ambig = tail = *best = NULNODE;
307 match = *nomatch = 0;
308 /* skip leading whitespace */
309 while (isspace(**line))
311 /* step through each node */
312 for (n = nd; n; n = n->p_peer) {
313 len = strlen(n->p_word);
314 /* step through each character in line */
315 for (i = 0; 1; i++) {
316 /* if at end of word on line */
317 if (isspace((*line)[i]) || (*line)[i] == 0) {
318 /* another ambiguous match */
319 if (i == match && i) {
327 *best = tail = *ambig = n;
332 if (isupper(c = (*line)[i]))
334 if (c != n->p_word[i]) {
343 if (tail != *ambig) {
346 if (isspace(**line)) {
358 if (isspace(**line)) {
360 while (isspace(**line))
372 *ambig = tail = *best = NULNODE;
380 /* execute the line. First check to see that the line is legal. If not,
381 * do_help the line & return ERROR. If so, execute each node passed through,
382 * and return OK (or EXIT if an EXIT node was encountered).
385 int do_parse(line, nod, prompt)
387 struct parse_node *nod;
390 struct parse_node *n, *nd, *last;
393 struct parse_node *best, *ambig;
401 while (n && ((state == MATCH) || (state == INCOMP))) {
403 state = single_parse(&ln, n, &best, &ambig, &nomatch);
405 printf("best = %s, best->next = 0x%x\r\n", best ? best->p_word : "",
406 best ? best->p_next : 0);
408 if ((state == NOMATCH) && (!*ln) && (n == nod))
410 if ((state == MATCH) || (state == INCOMP)) {
414 if (((state == AMBIG) || (state == NOMATCH)) && !*ln)
415 for (; n; n = n->p_peer)
416 if (last && last->p_menu) {
421 if (state == NOMATCH && !*ln) {
422 while (last && last->p_next && !last->p_next->p_peer && !last->p_menu)
424 if (last && last->p_menu) {
430 if ((state == NOMATCH) || (state == AMBIG)) {
431 write(1, " BAD command, ", 16);
432 do_help(line, nod, prompt);
439 for (i = 0; line[i] && !isspace(line[i]); i++);
440 if (!strncmp("help", line, i))
441 help(best->p_menu->form);
443 MoiraMenuRequest(best->p_menu);