home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume22 / nn6.4 / part14 / macro.c < prev    next >
C/C++ Source or Header  |  1990-06-07  |  14KB  |  772 lines

  1. /*
  2.  *    (c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
  3.  *
  4.  *    Macro parsing and execution.
  5.  */
  6.  
  7. #include "config.h"
  8. #include "keymap.h"
  9. #include "term.h"
  10.  
  11. export int in_menu_mode = 0;
  12. export int get_from_macro = 0;
  13. export int macro_debug = 0;
  14. export char *dflt_enter_macro = NULL;
  15.  
  16. #define M_DUMMY        0    /* do nothing (end of branch)     */
  17.  
  18. #define    M_COMMAND    1    /* a command (get_c())          */
  19. #define M_KEY        2    /* a key stroke (get_c())      */
  20. #define M_STRING    3    /* a string (get_s())         */
  21.  
  22. #define M_INPUT        4    /* take input from keyboard (get_c/get_s) */
  23.  
  24. #define M_YES        5    /* answer yes to confirmation     */
  25. #define M_NO        6    /* answer no to confirmation and break */
  26.                 /* -- if neither are present, take input */
  27.  
  28. #define M_PUTS        7    /* puts "..."             */
  29. #define M_PROMPT    8    /* prompt(...)              */
  30. #define M_ECHO        9    /* msg(...)             */
  31.  
  32. #define M_IS_MENU    10    /* in menu mode ?         */
  33. #define M_IS_SHOW    11    /* in reading mode ?         */
  34. #define M_IS_GROUP    12    /* are we in a news group ?     */
  35. #define M_IS_FOLDER    13    /* are we in a folder ?         */
  36. #define M_CONFIRM    14    /* ask for confirmation to procede     */
  37. #define M_REJECT    15    /* ask for !confirmation to procede     */
  38. #define M_VARTEST    16    /* test value of variable     */
  39. #define M_BREAK        17    /* exit from macroes         */
  40. #define    M_RETURN    18    /* return from this macro     */
  41.  
  42. #define    M_SET_COMMAND    19    /* set/unset command         */
  43.  
  44.  
  45. struct macro {
  46.     int            m_type;        /* entry type */
  47.     union {
  48.     int         mu_int;        /* command or char */
  49.     char         *mu_string;    /* string for get_s */
  50.     struct macro     *mu_branch;    /* false conditional */
  51.     } m_value;
  52.     struct macro *m_next;        /* next macro element */
  53. };
  54.  
  55. #define m_int        m_value.mu_int
  56. #define m_string    m_value.mu_string
  57. #define m_branch    m_value.mu_branch
  58.  
  59. #define NUM_MACRO 101        /* numbered macros */
  60. #define ANON_MACRO 100        /* anonymous macroes */
  61. #define NMACRO (NUM_MACRO + ANON_MACRO)
  62.  
  63. #define MSTACK 5        /* max nesting level */
  64.  
  65. static struct macro *macro[NMACRO + 1];     /* macro table */
  66.                 /* the extra slot is for entry macroes */
  67.  
  68. static struct macro *mstack[MSTACK];    /* macro stack */
  69. static int cstack[MSTACK + 1];
  70. static int m_level = 0;
  71.  
  72. static struct macro *m = NULL;        /* current macro */
  73. static int no_advance = 0;
  74. static int changed_prompt = 0;
  75.  
  76. static int cur_m;
  77.  
  78. #define MERROR ((struct macro *)1)
  79.  
  80. static m_error(fmt, arg)
  81. char *fmt, *arg;
  82. {
  83.     char buf[80];
  84.  
  85.    if (arg) {
  86.        sprintf(buf, fmt, arg);
  87.        fmt = buf;
  88.    }
  89.  
  90.    init_message("Error in macro %d: %s", cur_m, fmt);
  91. }
  92.  
  93. init_macro()
  94. {
  95.     int n;
  96.  
  97.     for (n = 0; n <= NMACRO; n++)
  98.     macro[n] = NULL;
  99. }
  100.  
  101. static m_new(t)
  102. int t;
  103. {
  104.     struct macro *m1;
  105.  
  106.     m1 = newobj(struct macro, 1);
  107.  
  108.     if (m == NULL)
  109.     m = macro[cur_m] = m1;
  110.     else {
  111.     m->m_next = m1;
  112.     m = m1;
  113.     }
  114.     m->m_type = t;
  115.     m->m_next = NULL;
  116. }
  117.  
  118.  
  119. /*
  120.  *    Define macro "id" reading from file f until "end"
  121.  *
  122.  *    Macro definition syntax:
  123.  *        define <id>
  124.  *           <body>
  125.  *        end
  126.  *
  127.  *    Id string interpretation:
  128.  *    NULL         use next free numbered macro
  129.  *            Return: pointer to macro
  130.  *    "nnn" nnn>=0    Numbered macro nnn
  131.  *            Return: pointer to macro
  132.  *    "-1"        entry macro
  133.  *            Return: pointer to macro
  134.  *    "-2"        anonymous macro
  135.  *            Return: K_MACRO code or K_UNBOUND on error
  136.  */
  137.  
  138. static int initial_set_commands;
  139.  
  140. static parse_word(w)
  141. char *w;
  142. {
  143.     int cmd;
  144.     register struct macro *m1;
  145.  
  146.     if (m && m->m_type == M_COMMAND && m->m_int == (GETC_COMMAND | K_MACRO)) {
  147.     if (isdigit(*w)) {
  148.         m->m_int |= atoi(w);
  149.         goto ok;
  150.     }
  151.     m_error("macro number missing", (char *)NULL);
  152.     return 1;
  153.     }
  154.  
  155.     if (*w == '"') {
  156.     if (m == NULL || (m->m_type != M_PROMPT && m->m_type != M_ECHO && m->m_type != M_PUTS))
  157.         m_new(M_STRING);
  158.     m->m_string = copy_str(w + 1);
  159.     goto ok;
  160.     }
  161.  
  162.     if (*w == '\'') {
  163.     m_new(M_KEY);
  164.     m->m_int = parse_key(w + 1);
  165.     goto ok;
  166.     }
  167.  
  168.     if (*w == '?') {
  169.     if (strchr(w, '=')) {
  170.         m->m_type = M_VARTEST;
  171.         m1 = m;
  172.         m_new(M_DUMMY);
  173.         m->m_branch = m1->m_branch;
  174.         m1->m_string = copy_str(w + 1);
  175.         goto ok;
  176.     }
  177.  
  178.     switch (w[1]) {
  179.      case 'f': /* ?folder */
  180.         cmd = M_IS_FOLDER;
  181.         break;
  182.      case 'g': /* ?group */
  183.         cmd = M_IS_GROUP;
  184.         break;
  185.      case 'm': /* ?menu */
  186.         cmd = M_IS_MENU;
  187.         break;
  188.      case 'n': /* ?no */
  189.         cmd = M_REJECT;
  190.         break;
  191.      case 's': /* ?show */
  192.         cmd = M_IS_SHOW;
  193.         break;
  194.      case 'y': /* ?yes */
  195.         cmd = M_CONFIRM;
  196.         break;
  197.      default:
  198.         m_error("unknown conditional %s", w - 1);
  199.         return 1;
  200.     }
  201.     m->m_type = cmd;
  202.     goto ok;
  203.     }
  204.  
  205.     if ((cmd = lookup_command(w, (K_ONLY_MENU | K_ONLY_MORE))) != K_INVALID) {
  206.     m_new(M_COMMAND);
  207.     m->m_int = GETC_COMMAND | cmd;
  208.     goto ok;
  209.     }
  210.  
  211.     if (strcmp(w, "prompt") == 0) {
  212.     m_new(M_PROMPT);
  213.     m->m_string = "?";
  214.     goto ok;
  215.     }
  216.     if (strcmp(w, "echo") == 0) {
  217.     m_new(M_ECHO);
  218.     m->m_string = "ups";
  219.     goto ok;
  220.     }
  221.     if (strcmp(w, "puts") == 0) {
  222.     m_new(M_PUTS);
  223.     m->m_string = "";
  224.     goto ok;
  225.     }
  226.     if (strcmp(w, "input") == 0) {
  227.     m_new(M_INPUT);
  228.     goto ok;
  229.     }
  230.     if (strcmp(w, "yes") == 0) {
  231.     m_new(M_YES);
  232.     goto ok;
  233.     }
  234.     if (strcmp(w, "no") == 0) {
  235.     m_new(M_NO);
  236.     goto ok;
  237.     }
  238.     if (strcmp(w, "break") == 0) {
  239.     m_new(M_BREAK);
  240.     goto ok;
  241.     }
  242.     if (strcmp(w, "return") == 0) {
  243.     m_new(M_RETURN);
  244.     goto ok;
  245.     }
  246.  
  247.     m_error("Unknown word >>%s<<", w);
  248.     return 1;
  249.  
  250.  ok:
  251.     return 0;
  252. }
  253.  
  254. static parse_line(lp)
  255. char *lp;
  256. {
  257.     char *word;
  258.     struct macro *m1, *branch = NULL;
  259.  
  260.     while (*lp) {
  261.     if (*lp == '#') break;
  262.  
  263.     if (*lp == ':') {
  264.         lp++;
  265.         if (initial_set_commands) {
  266.         if (strncmp(lp, "local",  5) == 0 ||
  267.             strncmp(lp, "set",    3) == 0 ||
  268.             strncmp(lp, "unset",  5) == 0) {
  269.             m_new(M_SET_COMMAND);
  270.             m->m_string = copy_str(lp);
  271.             break;
  272.         }
  273.         initial_set_commands = 0;
  274.         }
  275.         m_new(M_COMMAND);
  276.         m->m_int = GETC_COMMAND | K_EXTENDED_CMD;
  277.         m_new(M_STRING);
  278.         m->m_string = copy_str(lp);
  279.         break;
  280.     }
  281.     initial_set_commands = 0;
  282.  
  283.     if (*lp == '?') {
  284.         m_new(M_IS_MENU);
  285.         if (branch == NULL) {
  286.         m1 = m;
  287.         m_new(M_DUMMY);
  288.         branch = m;
  289.         m = m1;
  290.         }
  291.         m->m_branch = branch;
  292.     }
  293.  
  294.     word = lp;
  295.     if (*lp == '"')
  296.         do lp++;
  297.         while (*lp && *lp != '"');
  298.     else
  299.     if (*lp == '\'')
  300.         do lp++;
  301.         while (*lp && *lp != '\'');
  302.     else
  303.         while (*lp && !isspace(*lp)) lp++;
  304.     if (*lp) {
  305.         *lp++ = NUL;
  306.         while (*lp && isspace(*lp)) lp++;
  307.     }
  308.     if (parse_word(word)) return 1;
  309.     }
  310.  
  311.     if (branch) {
  312.     m->m_next = branch;
  313.     m = branch;
  314.     }
  315.     return 0;
  316. }
  317.  
  318. char *m_define(id, f)
  319. char *id;
  320. FILE *f;
  321. {
  322.     char line[256], *lp, skip;
  323.     int type = 0;
  324.  
  325.     if (id) {
  326.     cur_m = atoi(id);
  327.     if (cur_m == -1) {
  328.         cur_m = NMACRO;    /* special slot for this purpose */
  329.     } else if (cur_m == -2) {
  330.         for (cur_m = NUM_MACRO; cur_m < NMACRO; cur_m++)
  331.         if (macro[cur_m] == NULL) break;
  332.         if (cur_m == NMACRO) {
  333.         init_message("No unused macro slots");
  334.         return (char *)K_UNBOUND;
  335.         }
  336.         type = 1;
  337.     } else if (cur_m < 0 || cur_m >= NUM_MACRO) {
  338.         m_error("macro number out of range\n", id);
  339.         return (char *)0;
  340.     }
  341.     } else {
  342.     for (cur_m = 0; cur_m < NUM_MACRO; cur_m++)
  343.         if (macro[cur_m] == NULL) break;
  344.     if (cur_m == NUM_MACRO) {
  345.         init_message("No unused macro numbers");
  346.         return (char *)0;
  347.     }
  348.     }
  349.  
  350.     if (f == NULL) {
  351.     clrdisp();
  352.     printf("DEFINE %sMACRO %d -- END WITH 'end'\n\n\r",
  353.            cur_m >= NUM_MACRO ? "ANONYMOUS " : "",
  354.            cur_m >= NUM_MACRO ? cur_m - NUM_MACRO : cur_m);
  355.     unset_raw();
  356.     f = stdin;
  357.     }
  358.  
  359.     m = NULL;
  360.     skip = 0;
  361.     initial_set_commands = (cur_m == NMACRO);
  362.  
  363.     while (fgets(line, 256, f)) {
  364.     for (lp = line; *lp && isspace(*lp); lp++);
  365.     if (*lp == NUL) continue;
  366.     if (*lp == ')' || strncmp(lp, "end", 3) == 0) goto out;
  367.     if (!skip && parse_line(lp)) {
  368.         macro[cur_m] = NULL;
  369.         skip++;
  370.     }
  371.     }
  372.  
  373.     if (f != stdin)
  374.     m_error("end missing", (char *)NULL);
  375.  
  376.  out:
  377.     if (f == stdin) raw();
  378.     m = NULL;
  379.     return type == 0 ? (char *)macro[cur_m] : (char *)(K_MACRO | cur_m);
  380. }
  381.  
  382. char *m_get_macro(id)
  383. char *id;
  384. {
  385.     if (id) {
  386.     cur_m = atoi(id);
  387.     if (cur_m < 0 || cur_m >= NMACRO) {
  388.         m_error("macro number out of range\n", id);
  389.         return (char *)0;
  390.     }
  391.     }
  392.     return (char *)macro[cur_m];
  393. }
  394.  
  395. char *parse_enter_macro(f, c)
  396. FILE *f;
  397. register int c;
  398. {
  399.     register char *gp;
  400.     char other[FILENAME];
  401.     group_header *gh;
  402.     static char *last_defined = NULL;
  403.  
  404.     while (c != EOF && c != NL && (!isascii(c) || isspace(c))) c = getc(f);
  405.  
  406.     if (c == ')') return last_defined;
  407.  
  408.     if (c == EOF) return (char *)NULL;
  409.  
  410.     if (c == NL) return last_defined = m_define("-1", f);
  411.  
  412.     gp = other;
  413.     do {
  414.     *gp++ = c;
  415.     c = getc(f);
  416.     } while (c != EOF && c != ')' && isascii(c) && !isspace(c));
  417.  
  418.     *gp = NUL;
  419.     if (gh = lookup(other)) return gh->enter_macro;
  420.  
  421.     return m_get_macro(other);
  422. }
  423.  
  424. /*
  425.  *    Invoke macro # N
  426.  */
  427.  
  428. m_invoke(n)
  429. int n;
  430. {
  431.     if (n < 0) {
  432.     n = NMACRO;
  433.     if ((macro[n] = (struct macro *)(current_group->enter_macro)) == NULL)
  434.         if ((macro[n] = (struct macro *)dflt_enter_macro) == NULL)
  435.         return;
  436.     } else
  437.     if (n >= NMACRO || macro[n] == NULL) {
  438.     msg("undefined macro %d", n);
  439.     return;
  440.     }
  441.  
  442.     if (m_level == 0)
  443.     no_advance = 0;
  444.     else {
  445.     if (m_level > MSTACK) {
  446.         msg("Macro stack overflow");
  447.         m_break();
  448.         return;
  449.     }
  450.     mstack[m_level] = m;
  451.     cstack[m_level] = cur_m;
  452.     }
  453.     m_level++;
  454.  
  455.     cur_m = n;
  456.     m = macro[cur_m];
  457.     while (m && m->m_type == M_SET_COMMAND) {
  458.     char buffer[128];
  459.     strcpy(buffer, m->m_string);
  460.     if (macro_debug) { msg(":%s", buffer); user_delay(1); }
  461.     parse_command(buffer, 0, (FILE *)NULL);
  462.     m = m->m_next;
  463.     }
  464. }
  465.  
  466. m_startinput()
  467. {
  468.     no_advance = 1;
  469. }
  470.  
  471. m_endinput()
  472. {
  473.     if (no_advance) {
  474.     no_advance = 0;
  475.     if (m && m->m_type == M_INPUT)
  476.         m = m->m_next;
  477.     }
  478. }
  479.  
  480. m_advinput()
  481. {
  482.     if (m && m->m_type == M_INPUT)
  483.     m = m->m_next;
  484. }
  485.  
  486. static struct macro *m_call(who)
  487. int who;
  488. {
  489.     struct macro *m1;
  490.  
  491.     for (;;) {
  492.     while (m == NULL && m_level > 1) {
  493.         m_level--;
  494.         m = mstack[m_level];
  495.         cur_m = cstack[m_level];
  496.     }
  497.     if (m == NULL) {
  498.         if (macro_debug) msg("end");
  499.         m_break();
  500.         return NULL;
  501.     }
  502.  
  503.     if (macro_debug)
  504.         macro_dbg();
  505.  
  506.     if (who == 3) {
  507.         if (m->m_type == M_YES || m->m_type == M_NO) goto out;
  508.         return NULL;
  509.     }
  510.  
  511.     switch (m->m_type) {
  512.      case M_COMMAND:
  513.         if (m->m_int == (GETC_COMMAND | K_REDRAW))
  514.         changed_prompt = 0;
  515.      case M_KEY:
  516.         if (who == 1) goto out;
  517.         goto err;
  518.  
  519.      case M_STRING:
  520.         if (who == 2) goto out;
  521.         goto err;
  522.  
  523.      case M_INPUT:
  524.         if (no_advance) return m;
  525.         goto out;
  526.  
  527.      case M_YES:
  528.      case M_NO:
  529.      case M_DUMMY:
  530.         break;
  531.  
  532.      case M_PUTS:
  533.         fputs(m->m_string, stdout); fl;
  534.         break;
  535.  
  536.      case M_PROMPT:
  537.         if (m->m_string[0] == NUL) {
  538.         changed_prompt = 0;
  539.         break;
  540.         }
  541.         if (!changed_prompt) prompt(P_SAVE);
  542.         changed_prompt = 1;
  543.         prompt("\1%s\1 ", m->m_string);
  544.         break;
  545.  
  546.      case M_ECHO:
  547.         msg(m->m_string);
  548.         restore_xy();
  549.         break;
  550.  
  551.      case M_IS_MENU:
  552.         if (!in_menu_mode) m = m->m_branch;
  553.         break;
  554.      case M_IS_SHOW:
  555.         if (in_menu_mode) m = m->m_branch;
  556.         break;
  557.      case M_IS_GROUP:
  558.         if (current_group->group_flag & G_FOLDER) m = m->m_branch;
  559.         break;
  560.      case M_IS_FOLDER:
  561.         if ((current_group->group_flag & G_FOLDER) == 0) m = m->m_branch;
  562.         break;
  563.      case M_CONFIRM:
  564.         if (yes(0) == 0) m = m->m_branch;
  565.         break;
  566.      case M_REJECT:
  567.         if (yes(0) == 1) m = m->m_branch;
  568.         break;
  569.  
  570.      case M_VARTEST:
  571.         m1 = m;
  572.         m = m->m_next;
  573.  
  574.         switch (test_variable(m1->m_string)) {
  575.          case 0:
  576.         m = m->m_branch;
  577.         break;
  578.          case -1:
  579.         goto err1;
  580.         }
  581.         break;
  582.  
  583.      case M_RETURN:
  584.         m = NULL;
  585.         continue;
  586.  
  587.      case M_BREAK:
  588.         goto term;
  589.     }
  590.  
  591.     if (m) m = m->m_next;
  592.     }
  593.  
  594.  out:
  595.     m1 = m;
  596.     m = m->m_next;
  597.     no_advance = 0;
  598.     return m1;
  599.  
  600.  err:
  601.     msg("Error in macro %d", cur_m);
  602.  err1:
  603.     user_delay(1);
  604.     m_break();
  605.     return MERROR;
  606.  
  607.  term:
  608.     m_break();
  609.     return NULL;
  610. }
  611.  
  612. m_break_entry()
  613. {
  614.     if (current_group->enter_macro || dflt_enter_macro)
  615.     m = NULL;
  616. }
  617.  
  618. m_break()
  619. {
  620.     if (changed_prompt) prompt(P_RESTORE);
  621.     changed_prompt = 0;
  622.     m = NULL;
  623.     m_level = 0;
  624. }
  625.  
  626. macro_dbg()
  627. {
  628.     extern char *command_name();
  629.     char *name;
  630.  
  631.     switch (m->m_type) {
  632.      case M_COMMAND:
  633.     msg("COMMAND: %s", command_name(m->m_int));
  634.     goto delay;
  635.  
  636.      case M_KEY:
  637.     msg("KEY: %s", key_name((key_type)(m->m_int)));
  638.     goto delay;
  639.  
  640.      case M_STRING:
  641.     msg("STRING: %s", m->m_string);
  642.     goto delay;
  643.  
  644.      case M_INPUT:
  645.     name = "input";
  646.     break;
  647.  
  648.      case M_YES:
  649.     name = "yes";
  650.     break;
  651.  
  652.      case M_NO:
  653.     name = "no";
  654.     break;
  655.  
  656.      case M_DUMMY:
  657.     name = "dummy";
  658.     break;
  659.  
  660.      case M_PROMPT:
  661.     msg("PROMPT: %s", m->m_string);
  662.     goto delay;
  663.  
  664.      case M_ECHO:
  665.     msg("ECHO: %s", m->m_string);
  666.     goto delay;
  667.  
  668.      case M_IS_MENU:
  669.     msg("?menu => %d", in_menu_mode);
  670.     goto delay;
  671.  
  672.      case M_IS_SHOW:
  673.     msg("?show => %d", !in_menu_mode);
  674.     goto delay;
  675.  
  676.      case M_IS_GROUP:
  677.     msg("?group => %d", (current_group->group_flag & G_FOLDER) == 0);
  678.     goto delay;
  679.  
  680.      case M_IS_FOLDER:
  681.     msg("?group => %d", (current_group->group_flag & G_FOLDER));
  682.     goto delay;
  683.  
  684.      case M_CONFIRM:
  685.     name = "?yes";
  686.     break;
  687.  
  688.      case M_REJECT:
  689.     name = "?no";
  690.     break;
  691.  
  692.      case M_VARTEST:
  693.     msg("?%s => %d", m->m_string, test_variable(m->m_string));
  694.     goto delay;
  695.  
  696.      case M_RETURN:
  697.     name = "return";
  698.     break;
  699.  
  700.      case M_BREAK:
  701.     name = "break";
  702.     break;
  703.     }
  704.     msg(name);
  705.  
  706.  delay:
  707.     user_delay(1);
  708. }
  709.  
  710. /*
  711.  *    Macro processing for get_c()
  712.  */
  713.  
  714. m_getc(cp)
  715. int *cp;
  716. {
  717.     struct macro *m1;
  718.  
  719.     get_from_macro = 0;
  720.     if (m_level && (m1 = m_call(1))) {
  721.     if (m1 == MERROR) return 2;
  722.     if (m1->m_type == M_INPUT) return 0;
  723.     *cp = m1->m_int;
  724.     get_from_macro = 1;
  725.     return 1;
  726.     }
  727.     return 0;
  728. }
  729.  
  730. /*
  731.  *    Macro processing for get_s()
  732.  */
  733.  
  734. m_gets(s)
  735. char *s;
  736. {
  737.     struct macro *m1;
  738.  
  739.     get_from_macro = 0;
  740.     if (m_level && (m1 = m_call(2))) {
  741.     if (m1 == MERROR) return 2;
  742.     if (m1->m_type == M_INPUT) return 0;
  743.     strcpy(s, m1->m_string);
  744.     get_from_macro = 1;
  745.     return 1;
  746.     }
  747.     return 0;
  748. }
  749.  
  750. /*
  751.  *    Macro processing for yes()
  752.  */
  753.  
  754. m_yes()
  755. {
  756.     struct macro *m1;
  757.  
  758.     if (m)
  759.     if (m->m_type == M_CONFIRM || m->m_type == M_REJECT) return 3;
  760.  
  761.     if (m_level) {
  762.     if (m1 = m_call(3))
  763.         if (m1->m_type == M_NO)
  764.         return 1;
  765.         else
  766.         return 2;
  767.     else
  768.         return 3;
  769.     }
  770.     return 0;
  771. }
  772.