home *** CD-ROM | disk | FTP | other *** search
- /*
- * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- *
- * Macro parsing and execution.
- */
-
- #include "config.h"
- #include "keymap.h"
- #include "term.h"
-
- export int in_menu_mode = 0;
- export int get_from_macro = 0;
- export int macro_debug = 0;
- export char *dflt_enter_macro = NULL;
-
- #define M_DUMMY 0 /* do nothing (end of branch) */
-
- #define M_COMMAND 1 /* a command (get_c()) */
- #define M_KEY 2 /* a key stroke (get_c()) */
- #define M_STRING 3 /* a string (get_s()) */
-
- #define M_INPUT 4 /* take input from keyboard (get_c/get_s) */
-
- #define M_YES 5 /* answer yes to confirmation */
- #define M_NO 6 /* answer no to confirmation and break */
- /* -- if neither are present, take input */
-
- #define M_PUTS 7 /* puts "..." */
- #define M_PROMPT 8 /* prompt(...) */
- #define M_ECHO 9 /* msg(...) */
-
- #define M_IS_MENU 10 /* in menu mode ? */
- #define M_IS_SHOW 11 /* in reading mode ? */
- #define M_IS_GROUP 12 /* are we in a news group ? */
- #define M_IS_FOLDER 13 /* are we in a folder ? */
- #define M_CONFIRM 14 /* ask for confirmation to procede */
- #define M_REJECT 15 /* ask for !confirmation to procede */
- #define M_VARTEST 16 /* test value of variable */
- #define M_BREAK 17 /* exit from macroes */
- #define M_RETURN 18 /* return from this macro */
-
- #define M_SET_COMMAND 19 /* set/unset command */
-
-
- struct macro {
- int m_type; /* entry type */
- union {
- int mu_int; /* command or char */
- char *mu_string; /* string for get_s */
- struct macro *mu_branch; /* false conditional */
- } m_value;
- struct macro *m_next; /* next macro element */
- };
-
- #define m_int m_value.mu_int
- #define m_string m_value.mu_string
- #define m_branch m_value.mu_branch
-
- #define NUM_MACRO 101 /* numbered macros */
- #define ANON_MACRO 100 /* anonymous macroes */
- #define NMACRO (NUM_MACRO + ANON_MACRO)
-
- #define MSTACK 5 /* max nesting level */
-
- static struct macro *macro[NMACRO + 1]; /* macro table */
- /* the extra slot is for entry macroes */
-
- static struct macro *mstack[MSTACK]; /* macro stack */
- static int cstack[MSTACK + 1];
- static int m_level = 0;
-
- static struct macro *m = NULL; /* current macro */
- static int no_advance = 0;
- static int changed_prompt = 0;
-
- static int cur_m;
-
- #define MERROR ((struct macro *)1)
-
- static m_error(fmt, arg)
- char *fmt, *arg;
- {
- char buf[80];
-
- if (arg) {
- sprintf(buf, fmt, arg);
- fmt = buf;
- }
-
- init_message("Error in macro %d: %s", cur_m, fmt);
- }
-
- init_macro()
- {
- int n;
-
- for (n = 0; n <= NMACRO; n++)
- macro[n] = NULL;
- }
-
- static m_new(t)
- int t;
- {
- struct macro *m1;
-
- m1 = newobj(struct macro, 1);
-
- if (m == NULL)
- m = macro[cur_m] = m1;
- else {
- m->m_next = m1;
- m = m1;
- }
- m->m_type = t;
- m->m_next = NULL;
- }
-
-
- /*
- * Define macro "id" reading from file f until "end"
- *
- * Macro definition syntax:
- * define <id>
- * <body>
- * end
- *
- * Id string interpretation:
- * NULL use next free numbered macro
- * Return: pointer to macro
- * "nnn" nnn>=0 Numbered macro nnn
- * Return: pointer to macro
- * "-1" entry macro
- * Return: pointer to macro
- * "-2" anonymous macro
- * Return: K_MACRO code or K_UNBOUND on error
- */
-
- static int initial_set_commands;
-
- static parse_word(w)
- char *w;
- {
- int cmd;
- register struct macro *m1;
-
- if (m && m->m_type == M_COMMAND && m->m_int == (GETC_COMMAND | K_MACRO)) {
- if (isdigit(*w)) {
- m->m_int |= atoi(w);
- goto ok;
- }
- m_error("macro number missing", (char *)NULL);
- return 1;
- }
-
- if (*w == '"') {
- if (m == NULL || (m->m_type != M_PROMPT && m->m_type != M_ECHO && m->m_type != M_PUTS))
- m_new(M_STRING);
- m->m_string = copy_str(w + 1);
- goto ok;
- }
-
- if (*w == '\'') {
- m_new(M_KEY);
- m->m_int = parse_key(w + 1);
- goto ok;
- }
-
- if (*w == '?') {
- if (strchr(w, '=')) {
- m->m_type = M_VARTEST;
- m1 = m;
- m_new(M_DUMMY);
- m->m_branch = m1->m_branch;
- m1->m_string = copy_str(w + 1);
- goto ok;
- }
-
- switch (w[1]) {
- case 'f': /* ?folder */
- cmd = M_IS_FOLDER;
- break;
- case 'g': /* ?group */
- cmd = M_IS_GROUP;
- break;
- case 'm': /* ?menu */
- cmd = M_IS_MENU;
- break;
- case 'n': /* ?no */
- cmd = M_REJECT;
- break;
- case 's': /* ?show */
- cmd = M_IS_SHOW;
- break;
- case 'y': /* ?yes */
- cmd = M_CONFIRM;
- break;
- default:
- m_error("unknown conditional %s", w - 1);
- return 1;
- }
- m->m_type = cmd;
- goto ok;
- }
-
- if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) != K_INVALID) {
- m_new(M_COMMAND);
- m->m_int = GETC_COMMAND | cmd;
- goto ok;
- }
-
- if (strcmp(w, "prompt") == 0) {
- m_new(M_PROMPT);
- m->m_string = "?";
- goto ok;
- }
- if (strcmp(w, "echo") == 0) {
- m_new(M_ECHO);
- m->m_string = "ups";
- goto ok;
- }
- if (strcmp(w, "puts") == 0) {
- m_new(M_PUTS);
- m->m_string = "";
- goto ok;
- }
- if (strcmp(w, "input") == 0) {
- m_new(M_INPUT);
- goto ok;
- }
- if (strcmp(w, "yes") == 0) {
- m_new(M_YES);
- goto ok;
- }
- if (strcmp(w, "no") == 0) {
- m_new(M_NO);
- goto ok;
- }
- if (strcmp(w, "break") == 0) {
- m_new(M_BREAK);
- goto ok;
- }
- if (strcmp(w, "return") == 0) {
- m_new(M_RETURN);
- goto ok;
- }
-
- m_error("Unknown word >>%s<<", w);
- return 1;
-
- ok:
- return 0;
- }
-
- static parse_line(lp)
- char *lp;
- {
- char *word;
- struct macro *m1, *branch = NULL;
-
- while (*lp) {
- if (*lp == '#') break;
-
- if (*lp == ':') {
- lp++;
- if (initial_set_commands) {
- if (strncmp(lp, "local", 5) == 0 ||
- strncmp(lp, "set", 3) == 0 ||
- strncmp(lp, "unset", 5) == 0) {
- m_new(M_SET_COMMAND);
- m->m_string = copy_str(lp);
- break;
- }
- initial_set_commands = 0;
- }
- m_new(M_COMMAND);
- m->m_int = GETC_COMMAND | K_EXTENDED_CMD;
- m_new(M_STRING);
- m->m_string = copy_str(lp);
- break;
- }
- initial_set_commands = 0;
-
- if (*lp == '?') {
- m_new(M_IS_MENU);
- if (branch == NULL) {
- m1 = m;
- m_new(M_DUMMY);
- branch = m;
- m = m1;
- }
- m->m_branch = branch;
- }
-
- word = lp;
- if (*lp == '"')
- do lp++;
- while (*lp && *lp != '"');
- else
- if (*lp == '\'')
- do lp++;
- while (*lp && *lp != '\'');
- else
- while (*lp && !isspace(*lp)) lp++;
- if (*lp) {
- *lp++ = NUL;
- while (*lp && isspace(*lp)) lp++;
- }
- if (parse_word(word)) return 1;
- }
-
- if (branch) {
- m->m_next = branch;
- m = branch;
- }
- return 0;
- }
-
- char *m_define(id, f)
- char *id;
- FILE *f;
- {
- char line[256], *lp, skip;
- int type = 0;
-
- if (id) {
- cur_m = atoi(id);
- if (cur_m == -1) {
- cur_m = NMACRO; /* special slot for this purpose */
- } else if (cur_m == -2) {
- for (cur_m = NUM_MACRO; cur_m < NMACRO; cur_m++)
- if (macro[cur_m] == NULL) break;
- if (cur_m == NMACRO) {
- init_message("No unused macro slots");
- return (char *)K_UNBOUND;
- }
- type = 1;
- } else if (cur_m < 0 || cur_m >= NUM_MACRO) {
- m_error("macro number out of range\n", id);
- return (char *)0;
- }
- } else {
- for (cur_m = 0; cur_m < NUM_MACRO; cur_m++)
- if (macro[cur_m] == NULL) break;
- if (cur_m == NUM_MACRO) {
- init_message("No unused macro numbers");
- return (char *)0;
- }
- }
-
- if (f == NULL) {
- clrdisp();
- printf("DEFINE %sMACRO %d -- END WITH 'end'\n\n\r",
- cur_m >= NUM_MACRO ? "ANONYMOUS " : "",
- cur_m >= NUM_MACRO ? cur_m - NUM_MACRO : cur_m);
- unset_raw();
- f = stdin;
- }
-
- m = NULL;
- skip = 0;
- initial_set_commands = (cur_m == NMACRO);
-
- while (fgets(line, 256, f)) {
- for (lp = line; *lp && isspace(*lp); lp++);
- if (*lp == NUL) continue;
- if (*lp == ')' || strncmp(lp, "end", 3) == 0) goto out;
- if (!skip && parse_line(lp)) {
- macro[cur_m] = NULL;
- skip++;
- }
- }
-
- if (f != stdin)
- m_error("end missing", (char *)NULL);
-
- out:
- if (f == stdin) raw();
- m = NULL;
- return type == 0 ? (char *)macro[cur_m] : (char *)(K_MACRO | cur_m);
- }
-
- char *m_get_macro(id)
- char *id;
- {
- if (id) {
- cur_m = atoi(id);
- if (cur_m < 0 || cur_m >= NMACRO) {
- m_error("macro number out of range\n", id);
- return (char *)0;
- }
- }
- return (char *)macro[cur_m];
- }
-
- char *parse_enter_macro(f, c)
- FILE *f;
- register int c;
- {
- register char *gp;
- char other[FILENAME];
- group_header *gh;
- static char *last_defined = NULL;
-
- while (c != EOF && c != NL && (!isascii(c) || isspace(c))) c = getc(f);
-
- if (c == ')') return last_defined;
-
- if (c == EOF) return (char *)NULL;
-
- if (c == NL) return last_defined = m_define("-1", f);
-
- gp = other;
- do {
- *gp++ = c;
- c = getc(f);
- } while (c != EOF && c != ')' && isascii(c) && !isspace(c));
-
- *gp = NUL;
- if (gh = lookup(other)) return gh->enter_macro;
-
- return m_get_macro(other);
- }
-
- /*
- * Invoke macro # N
- */
-
- m_invoke(n)
- int n;
- {
- if (n < 0) {
- n = NMACRO;
- if ((macro[n] = (struct macro *)(current_group->enter_macro)) == NULL)
- if ((macro[n] = (struct macro *)dflt_enter_macro) == NULL)
- return;
- } else
- if (n >= NMACRO || macro[n] == NULL) {
- msg("undefined macro %d", n);
- return;
- }
-
- if (m_level == 0)
- no_advance = 0;
- else {
- if (m_level > MSTACK) {
- msg("Macro stack overflow");
- m_break();
- return;
- }
- mstack[m_level] = m;
- cstack[m_level] = cur_m;
- }
- m_level++;
-
- cur_m = n;
- m = macro[cur_m];
- while (m && m->m_type == M_SET_COMMAND) {
- char buffer[128];
- strcpy(buffer, m->m_string);
- if (macro_debug) { msg(":%s", buffer); user_delay(1); }
- parse_command(buffer, 0, (FILE *)NULL);
- m = m->m_next;
- }
- }
-
- m_startinput()
- {
- no_advance = 1;
- }
-
- m_endinput()
- {
- if (no_advance) {
- no_advance = 0;
- if (m && m->m_type == M_INPUT)
- m = m->m_next;
- }
- }
-
- m_advinput()
- {
- if (m && m->m_type == M_INPUT)
- m = m->m_next;
- }
-
- static struct macro *m_call(who)
- int who;
- {
- struct macro *m1;
-
- for (;;) {
- while (m == NULL && m_level > 1) {
- m_level--;
- m = mstack[m_level];
- cur_m = cstack[m_level];
- }
- if (m == NULL) {
- if (macro_debug) msg("end");
- m_break();
- return NULL;
- }
-
- if (macro_debug)
- macro_dbg();
-
- if (who == 3) {
- if (m->m_type == M_YES || m->m_type == M_NO) goto out;
- return NULL;
- }
-
- switch (m->m_type) {
- case M_COMMAND:
- if (m->m_int == (GETC_COMMAND | K_REDRAW))
- changed_prompt = 0;
- case M_KEY:
- if (who == 1) goto out;
- goto err;
-
- case M_STRING:
- if (who == 2) goto out;
- goto err;
-
- case M_INPUT:
- if (no_advance) return m;
- goto out;
-
- case M_YES:
- case M_NO:
- case M_DUMMY:
- break;
-
- case M_PUTS:
- fputs(m->m_string, stdout); fl;
- break;
-
- case M_PROMPT:
- if (m->m_string[0] == NUL) {
- changed_prompt = 0;
- break;
- }
- if (!changed_prompt) prompt(P_SAVE);
- changed_prompt = 1;
- prompt("\1%s\1 ", m->m_string);
- break;
-
- case M_ECHO:
- msg(m->m_string);
- restore_xy();
- break;
-
- case M_IS_MENU:
- if (!in_menu_mode) m = m->m_branch;
- break;
- case M_IS_SHOW:
- if (in_menu_mode) m = m->m_branch;
- break;
- case M_IS_GROUP:
- if (current_group->group_flag & G_FOLDER) m = m->m_branch;
- break;
- case M_IS_FOLDER:
- if ((current_group->group_flag & G_FOLDER) == 0) m = m->m_branch;
- break;
- case M_CONFIRM:
- if (yes(0) == 0) m = m->m_branch;
- break;
- case M_REJECT:
- if (yes(0) == 1) m = m->m_branch;
- break;
-
- case M_VARTEST:
- m1 = m;
- m = m->m_next;
-
- switch (test_variable(m1->m_string)) {
- case 0:
- m = m->m_branch;
- break;
- case -1:
- goto err1;
- }
- break;
-
- case M_RETURN:
- m = NULL;
- continue;
-
- case M_BREAK:
- goto term;
- }
-
- if (m) m = m->m_next;
- }
-
- out:
- m1 = m;
- m = m->m_next;
- no_advance = 0;
- return m1;
-
- err:
- msg("Error in macro %d", cur_m);
- err1:
- user_delay(1);
- m_break();
- return MERROR;
-
- term:
- m_break();
- return NULL;
- }
-
- m_break_entry()
- {
- if (current_group->enter_macro || dflt_enter_macro)
- m = NULL;
- }
-
- m_break()
- {
- if (changed_prompt) prompt(P_RESTORE);
- changed_prompt = 0;
- m = NULL;
- m_level = 0;
- }
-
- macro_dbg()
- {
- extern char *command_name();
- char *name;
-
- switch (m->m_type) {
- case M_COMMAND:
- msg("COMMAND: %s", command_name(m->m_int));
- goto delay;
-
- case M_KEY:
- msg("KEY: %s", key_name((key_type)(m->m_int)));
- goto delay;
-
- case M_STRING:
- msg("STRING: %s", m->m_string);
- goto delay;
-
- case M_INPUT:
- name = "input";
- break;
-
- case M_YES:
- name = "yes";
- break;
-
- case M_NO:
- name = "no";
- break;
-
- case M_DUMMY:
- name = "dummy";
- break;
-
- case M_PROMPT:
- msg("PROMPT: %s", m->m_string);
- goto delay;
-
- case M_ECHO:
- msg("ECHO: %s", m->m_string);
- goto delay;
-
- case M_IS_MENU:
- msg("?menu => %d", in_menu_mode);
- goto delay;
-
- case M_IS_SHOW:
- msg("?show => %d", !in_menu_mode);
- goto delay;
-
- case M_IS_GROUP:
- msg("?group => %d", (current_group->group_flag & G_FOLDER) == 0);
- goto delay;
-
- case M_IS_FOLDER:
- msg("?group => %d", (current_group->group_flag & G_FOLDER));
- goto delay;
-
- case M_CONFIRM:
- name = "?yes";
- break;
-
- case M_REJECT:
- name = "?no";
- break;
-
- case M_VARTEST:
- msg("?%s => %d", m->m_string, test_variable(m->m_string));
- goto delay;
-
- case M_RETURN:
- name = "return";
- break;
-
- case M_BREAK:
- name = "break";
- break;
- }
- msg(name);
-
- delay:
- user_delay(1);
- }
-
- /*
- * Macro processing for get_c()
- */
-
- m_getc(cp)
- int *cp;
- {
- struct macro *m1;
-
- get_from_macro = 0;
- if (m_level && (m1 = m_call(1))) {
- if (m1 == MERROR) return 2;
- if (m1->m_type == M_INPUT) return 0;
- *cp = m1->m_int;
- get_from_macro = 1;
- return 1;
- }
- return 0;
- }
-
- /*
- * Macro processing for get_s()
- */
-
- m_gets(s)
- char *s;
- {
- struct macro *m1;
-
- get_from_macro = 0;
- if (m_level && (m1 = m_call(2))) {
- if (m1 == MERROR) return 2;
- if (m1->m_type == M_INPUT) return 0;
- strcpy(s, m1->m_string);
- get_from_macro = 1;
- return 1;
- }
- return 0;
- }
-
- /*
- * Macro processing for yes()
- */
-
- m_yes()
- {
- struct macro *m1;
-
- if (m)
- if (m->m_type == M_CONFIRM || m->m_type == M_REJECT) return 3;
-
- if (m_level) {
- if (m1 = m_call(3))
- if (m1->m_type == M_NO)
- return 1;
- else
- return 2;
- else
- return 3;
- }
- return 0;
- }
-