]> andersk Git - moira.git/commitdiff
Initial revision
authormar <mar>
Thu, 10 Dec 1992 10:58:42 +0000 (10:58 +0000)
committermar <mar>
Thu, 10 Dec 1992 10:58:42 +0000 (10:58 +0000)
clients/mmoira/parser.c [new file with mode: 0644]
clients/mmoira/parser.h [new file with mode: 0644]

diff --git a/clients/mmoira/parser.c b/clients/mmoira/parser.c
new file mode 100644 (file)
index 0000000..234b191
--- /dev/null
@@ -0,0 +1,444 @@
+/* $Header$
+ *
+ * TOPS-20 style command parser
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/signal.h>
+#include <X11/Intrinsic.h>
+#include "mmoira.h"
+#include "parser.h"
+
+
+/* trivial absolute-value macro */
+#define abs(i) ((i) < 0 ? -(i) : (i))
+
+/* message used when end of parse tree is reached */
+char   eolmsg[] = "return to execute command";
+
+
+/* main entry point.  Takes the prompt and top node of the parse tree, and
+ * will collect user's input, providing help and completion.  When entry
+ * is complete (<return> is pressed), the actions called for by each node
+ * in the parse tree which is traversed will be executed.
+ */
+
+int    parser(prompt, nd)
+char   *prompt;
+struct parse_node      *nd;
+{
+    char line[BUFLEN];
+    char *p, c;
+    int        val;
+
+    p = &line[0];
+    *p = 0;
+    write(1, prompt, strlen(prompt));
+    for (c = (getchar() & 0x7F); 1; c = (getchar() & 0x7F)) {
+       if (c == 0)
+         continue;
+       switch (c) {
+       case 127:
+       case '\b':
+           if (p == &line[0]) {
+               putchar(7);
+               break;
+           }
+           if (*(--p) == 'I' - '@') {
+               *p = 0;
+               printf("\r%s%s", prompt, line);
+               fflush(stdout);
+               break;
+           }
+           *p = 0;
+           write(1, "\b \b", 3);
+           break;
+       case 'C' - '@':
+       case 'G' - '@':
+           write(1, " \007ABORT\r\n", 9);
+           return(ABORT);
+       case 'Q' - '@':
+       case 'V' - '@':
+           putchar('\\');
+           c = getchar();
+           if (c < ' ')
+             printf("\b^%c", c + '@');
+           else
+             printf("\b%c", c);
+           fflush(stdout);
+           *p++ = c;
+           break;
+       case 'W' - '@':
+           if (p > &line[0])
+             p--;
+           while ((p >= &line[0]) && isspace(*p)) {
+               write(1, "\b \b", 3);
+               p--;
+           }
+           while ((p >= &line[0]) && !isspace(*p)) {
+               write(1, "\b \b", 3);
+               p--;
+           }
+           if (p > &line[0]) {
+               p++;
+           } else {
+               p = &line[0];
+           }
+           *p = 0;
+           break;
+       case '?':
+           write(1, "?  ", 3);
+           *p = 0;
+           do_help(&line[0], nd, prompt);
+           p = &line[strlen(line)];
+           printf("\n\r%s%s", prompt, line);
+           fflush(stdout);
+           break;
+       case '[' - '@':
+       case '\t':
+           *p = 0;
+           do_complete(&line[0], nd, prompt);
+           p = &line[strlen(line)];
+           break;
+       case 'Z' - '@':
+           printf("\r\n");
+           kill(getpid(), SIGSTOP);
+           /* when continued, fall through to */
+       case 'R' - '@':
+           *p = 0;
+           printf("\r\n%s%s", prompt, line);
+           fflush(stdout);
+           break;
+       case 'U' - '@':
+           while (p-- > &line[0])
+             write(1, "\b \b", 3);
+           *(++p) = 0;
+           printf("\r%s", prompt);
+           fflush(stdout);
+           break;
+       case '\n':
+       case '\r':
+           if ((val = do_parse(line, nd, prompt)) != ERROR) {
+               write(1, "\r\n", 2);
+               return(val);
+           }
+           p = &line[strlen(line)];
+           *p = 0;
+           printf("\r\n%s%s", prompt, line);
+           fflush(stdout);
+           break;
+       default:
+           putchar(c);
+           *p++ = c;
+           *p = 0;
+       }
+    }
+}
+
+
+/* called when a ? is typed.  This parses the line as far as possible, then
+ * displays possible completions and help strings.
+ */
+
+do_help(line, nod, prompt)
+char *line;
+struct parse_node *nod;
+char *prompt;
+{
+    char *ln;
+    struct parse_node *n, *nd, *last;
+    int kw, state, nomatch;
+    struct parse_node *best, *ambig;
+
+    n = nod;
+    state = MATCH;
+    for (ln = line; n && state == MATCH; best && (n = best->p_next)) {
+       last = best;
+       state = single_parse(&ln, n, &best, &ambig, &nomatch);
+    }
+    if (*ln && ((best && best->p_menu) || n == NULNODE))
+      state = NOMATCH;
+    switch (state) {
+    case NOMATCH:
+       if (!*ln) {
+           if (last && last->p_menu  && *line)
+             printf("Carriage return, or ");
+           break;
+       }
+       ln += nomatch;
+       *ln = 0;
+       write(1, "\r\nNOT a valid command line", 26);
+       return;
+    case AMBIG:
+       write(1, "one of the following:\r\n   ", 26);
+       for (; ambig; ambig = ambig->p_link)
+         printf("%s ", ambig->p_word);
+       fflush(stdout);
+       return;
+    case INCOMP:
+       printf("one of the following:\r\n   %s", best->p_word);
+       fflush(stdout);
+       return;
+    default:
+       write(1, eolmsg, strlen(eolmsg));
+       return;
+    }
+    kw = 0;
+    for (nd = n; nd; nd = nd->p_peer) {
+       if (!kw) {
+           write(1, "one of the following:\r\n   ", 26);
+           kw = 3;
+       }
+       if (kw + strlen(nd->p_word) > 72) {
+           printf("\r\n   %s ", nd->p_word);
+           fflush(stdout);
+           kw = 4 + strlen(nd->p_word);
+       } else {
+           printf("%s ", nd->p_word);
+           fflush(stdout);
+           kw += 1 + strlen(nd->p_word);
+       }
+    }
+}
+
+
+/* Do escape processing.  If a unique completion exists, use it.  Otherwise,
+ * do the same as ?.
+ */
+
+do_complete(line, nod, prompt)
+char *line;
+struct parse_node *nod;
+char *prompt;
+{
+    struct parse_node *n, *nd;
+    char *ln;
+    char *tmp;
+    int        state, nomatch;
+    struct parse_node *best, *ambig;
+
+    ln = line;
+    state = MATCH;
+    nd = nod;
+    while (state == MATCH) {
+       tmp = ln;
+       state = single_parse(&ln, nd, &best, &ambig, &nomatch);
+       if (state == MATCH)
+         nd = best->p_next;
+    }
+    switch (state) {
+    case AMBIG:
+/*     printf("ln %X, tmp %X, ln - tmp %d, nomatch %d\r\n", ln, tmp, ln-tmp, nomatch);
+       if (ln - tmp < nomatch) {
+           printf("attempting partial complete\r\n");
+           fflush(stdout);
+           sleep(1);
+           while ((ln > line) && !isspace(*ln))
+             ln--;
+           tmp = ambig->p_word;
+           while (nomatch--)
+             *ln++ = *tmp++;
+           *ln = 0;
+           putchar(7);
+           return;
+       } */
+       /* fall through to: */
+    case NOMATCH:
+       if (!(nd) || (nd->p_peer)) {
+           write(1, "  ", 2);
+           do_help(line, nod, prompt);
+           printf("\r\n%s%s", prompt, line);
+           fflush(stdout);
+           return;
+       }
+       best = nd;
+       *ln++ = 'x';
+       /* fall through to incomplete case */
+    case INCOMP:
+       ln = tmp;
+       do {
+           tmp = best->p_word;
+           while (*tmp)
+             *ln++ = *tmp++;
+           *ln++ = ' ';
+           *ln = 0;
+       } while (best->p_next && !best->p_next->p_peer &&
+                !best->p_menu && (best = best->p_next));
+       printf("\r%s%s", prompt, line);
+       fflush(stdout);
+       break;
+    default:
+       write(1, "We shouldn't get here (parser error)\r\n", 38);
+    }
+}
+
+
+/* Single parse parses through a single level of the parse tree.
+ * There are 4 possible outcomes:
+ *     an exact match is made: the matching node is returned, ambig = 0
+ *     an incomplete match: the matching node is returned, ambig = node
+ *     ambiguous: nothing is returned, ambig = list of completions
+ *     no matches: nothing is returned, ambig = 0
+ */
+
+int single_parse(line, nd, best, ambig, nomatch)
+char **line;
+struct parse_node *nd;
+struct parse_node **best;
+struct parse_node **ambig;
+int *nomatch;
+{
+    char *p;
+    char c;                    /* char we're working on (from line) */
+    struct parse_node *n;      /* node loop counter */
+    struct parse_node *tail;   /* tmp used to build chains */
+    int i;                     /* loop counter */
+    int        match;                  /* how many chars have we matched? */
+    int len;                   /* length of this keyword */
+
+#ifdef DEBUG
+    printf("single_parse(\"%s\") -> ", *line);
+#endif /* DEBUG */
+    *ambig = tail = *best = NULNODE;
+    match = *nomatch = 0;
+    /* skip leading whitespace */
+    while (isspace(**line))
+      (*line)++;
+    /* step through each node */
+    for (n = nd; n; n = n->p_peer) {
+       len = strlen(n->p_word);
+       /* step through each character in line */
+       for (i = 0; 1; i++) {
+           /* if at end of word on line */
+           if (isspace((*line)[i]) || (*line)[i] == 0) {
+               /* another ambiguous match */
+               if (i == match && i) {
+                   tail->p_link = n;
+                   tail = n;
+                   n->p_link = NULNODE;
+               }
+               /* a better match */
+               if (i > match) {
+                   match = i;
+                   *best = tail = *ambig = n;
+                   n->p_link = NULNODE;
+               }
+               break;
+           }
+           if (isupper(c = (*line)[i]))
+             c = tolower(c);
+           if (c != n->p_word[i]) {
+               if (i > *nomatch)
+                 *nomatch = i;
+               break;
+           }
+       }
+    }
+    if (match > 0) {
+       (*line) += match;
+       if (tail != *ambig) {
+           *best = NULNODE;
+           *nomatch = match;
+           if (isspace(**line)) {
+#ifdef DEBUG
+               printf("NOMATCH\n");
+#endif /* DEBUG */
+               return(NOMATCH);
+           } else {
+#ifdef DEBUG
+               printf("AMBIG\n");
+#endif /* DEBUG */
+               return(AMBIG);
+           }
+       }
+       if (isspace(**line)) {
+           *ambig = NULNODE;
+           while (isspace(**line))
+             (*line)++;
+#ifdef DEBUG
+           printf("MATCH\n");
+#endif /* DEBUG */
+           return(MATCH);
+       }
+#ifdef DEBUG
+       printf("INCOMP\n");
+#endif /* DEBUG */
+       return(INCOMP);
+    }
+    *ambig = tail = *best = NULNODE;
+#ifdef DEBUG
+    printf("NOMATCH\n");
+#endif /* DEBUG */
+    return(NOMATCH);
+}
+
+
+/* execute the line.  First check to see that the line is legal.  If not,
+ * do_help the line & return ERROR.  If so, execute each node passed through,
+ * and return OK (or EXIT if an EXIT node was encountered).
+ */
+
+int do_parse(line, nod, prompt)
+char *line;
+struct parse_node *nod;
+char *prompt;
+{
+    struct parse_node *n, *nd, *last;
+    char *ln, *tmp;
+    int state, i;
+    struct parse_node *best, *ambig;
+    int        nomatch;
+    EntryForm *f;
+
+    ln = line;
+    n = nod;
+    state = MATCH;
+    best = NULNODE;
+    while (n && ((state == MATCH) || (state == INCOMP))) {
+       last = best;
+       state = single_parse(&ln, n, &best, &ambig, &nomatch);
+#ifdef DEBUG
+       printf("best = %s, best->next = 0x%x\r\n", best ? best->p_word : "",
+              best ? best->p_next : 0);
+#endif /* DEBUG */
+       if ((state == NOMATCH) && (!*ln) && (n == nod))
+         return(OK);
+       if ((state == MATCH) || (state == INCOMP)) {
+           n = best->p_next;
+       }
+    }
+    if (((state == AMBIG) || (state == NOMATCH)) && !*ln)
+      for (; n; n = n->p_peer)
+       if (last && last->p_menu) {
+           n = last;
+           state = MATCH;
+           break;
+       }
+    if (state == NOMATCH && !*ln) {
+       while (last && last->p_next && !last->p_next->p_peer && !last->p_menu)
+         last = last->p_next;
+       if (last && last->p_menu) {
+           state = MATCH;
+           best = last;
+       }
+    }
+
+    if ((state == NOMATCH) || (state == AMBIG)) {
+       write(1, "  BAD command,  ", 16);
+       do_help(line, nod, prompt);
+       return(ERROR);
+    }
+    if (!best)
+      best = n;
+    write(1, "\r\n", 2);
+    cooked_mode();
+    for (i = 0; line[i] && !isspace(line[i]); i++);
+    if (!strncmp("help", line, i))
+      help(best->p_menu->form);
+    else
+      MoiraMenuRequest(best->p_menu);
+    raw_mode();
+    return(OK);
+}
diff --git a/clients/mmoira/parser.h b/clients/mmoira/parser.h
new file mode 100644 (file)
index 0000000..afa88c9
--- /dev/null
@@ -0,0 +1,53 @@
+/* $Header$
+ * Constants and structures for TOPS-20 style parser
+ *
+ */
+
+/* type codes for nodes & return values in parse tree */
+#define NONE   0
+#define KEYWORD 1
+#define INTEGER 2
+#define STRING  3
+#define ADDRESS 4
+#define PROC   5
+#define ERROR  6
+#define EXIT   7
+#define ABORT  8
+
+
+/* return codes from single_parse */
+#define OK     0
+#define MATCH  1       /* exact match found */
+#define        INCOMP  2       /* incomplete field */
+#define        AMBIG   3       /* ambiguous response */
+#define NOMATCH        4       /* no match */
+
+
+/* maximum line length for parser */
+#define BUFLEN 128
+
+/* the nodes which make up the parse tree */
+
+struct parse_node {
+       char    *p_word;        /* keyword, data description */
+       MenuItem *p_menu;       /* MMoira menu entry for this command */
+       struct parse_node *p_peer;      /* next node this level */
+       struct parse_node *p_next;      /* next level nodes */
+       struct parse_node *p_link;      /* for temporary chains */
+};
+
+
+#define NULNODE ((struct parse_node *) 0)
+
+
+/* this structure is used for collecting data to pass to parse functions */
+
+#define pval   struct pvalst
+
+pval {
+    char *p_dat;       /* data value or pointer */
+    int p_free;                /* size of data to free */
+};
+
+/* how many return values are allowed in a single parse */
+#define PMAXDAT        8
This page took 0.076253 seconds and 5 git commands to generate.