home *** CD-ROM | disk | FTP | other *** search
/ Informática Multimedia: Special Games (Alt) / INFESPGAMES.iso / os2 / backgam / source / macro.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-09-22  |  31.6 KB  |  1,027 lines

  1. /*************************************************************
  2.  *    ______                                                 *
  3.  *   /     /\  TinyFugue was derived from a client initially *
  4.  *  /   __/  \ written by Anton Rang (Tarrant) and later     *
  5.  *  |  / /\  | modified by Leo Plotkin (Grod).  The early    *
  6.  *  |  |/    | versions of TinyFugue written by Greg Hudson  *
  7.  *  |  X__/  | (Explorer_Bob).  The current version is       *
  8.  *  \ /      / written and maintained by Ken Keys (Hawkeye), *
  9.  *   \______/  who can be reached at kkeys@ucsd.edu.         *
  10.  *                                                           *
  11.  *             No copyright 1992, no rights reserved.        *
  12.  *             Fugue is in the public domain.                *
  13.  *************************************************************/
  14.  
  15. /**********************************************
  16.  * Fugue macro package                        *
  17.  *                                            *
  18.  * Macros, hooks, triggers, hilites and gags  *
  19.  * are all processed here.                    *
  20.  **********************************************/
  21.  
  22. #include <stdio.h>
  23. #include <ctype.h>
  24. #include "tf.h"
  25. #include "dstring.h"
  26. #include "util.h"
  27. #include "history.h"
  28. #include "world.h"
  29. #include "socket.h"
  30. #include "macro.h"
  31. #include "keyboard.h"
  32. #include "output.h"
  33. #include "expand.h"
  34. #include "command1.h"
  35.  
  36. #ifndef BSD
  37. # define RANDOM rand
  38. #else
  39. # define RANDOM random
  40. #endif
  41.  
  42. static Macro  *FDECL(find_macro,(char *name));
  43. static int     FDECL(macro_match,(Macro *spec, Macro *macro,
  44.                int FDECL((*cmp),(char *s1, char *s2))));
  45. static Macro  *FDECL(match_exact,(int hook, char *str, int flags));
  46. static Macro  *FDECL(find_match,(int hook, char *text));
  47. static void    FDECL(insert_by_priority,(Macro *macro, int hook));
  48. static void    FDECL(list_defs,(TFILE *file, Macro *spec, int abbrev));
  49. static String *FDECL(escape,(char *src));
  50. static int     FDECL(parse_hook,(char *args));
  51. static int     FDECL(find_hook,(char *name));
  52. static int     FDECL(hash_string,(char *str, int size));
  53. static void    FDECL(hash_insert,(Macro *macro));
  54. static void    FDECL(hash_remove,(Macro *macro));
  55. static int     FDECL(run_trig_or_hook,(Macro *macro, char *text));
  56.  
  57. void   NDECL(init_macros);
  58. Macro *FDECL(new_macro,(char *name, char *trig, char *bind, int hook,
  59.        char *hargs, char *body, World *world, int pri, int prob,
  60.        int attr, int shots, int invis));
  61. void   FDECL(add_macro,(Macro *macro));
  62. void   FDECL(add_hook,(char *proto, char *body));
  63. int    FDECL(install_bind,(Macro *spec));
  64. void   FDECL(do_add,(Macro *spec));
  65. void   FDECL(do_edit,(Macro *spec));
  66. void   FDECL(nuke_macro,(Macro *macro));
  67. void   FDECL(remove_macro,(char *args, int attr, int byhook));
  68. void   FDECL(purge_macro,(Macro *spec));
  69. void   FDECL(remove_by_number,(char *args));
  70. void   FDECL(save_macros,(char *args));
  71. void   FDECL(list_macros,(char *args));
  72. int    FDECL(world_subs,(char *src, Stringp dest));
  73. void   FDECL(do_macro,(char *name, char *args, Stringp dest, int toplevel));
  74. short  VDECL(do_hook,(int hook, char *fmt, char *argfmt, ...));
  75. void   FDECL(get_macro_body,(char *name, Stringp dest));
  76. short  FDECL(check_trigger,(char *text));
  77.  
  78. extern int redef;
  79.  
  80. #define HASH_SIZE 197
  81.  
  82. static Macro *head = NULL;             /* head of complete macro list */
  83. static Macro *tail = NULL;             /* tail of complete macro list */
  84. static Macro *thead = NULL;            /* head of trigger macro list */
  85. static Macro *hhead = NULL;            /* head of hook macro list */
  86. static Macro *hash_table[HASH_SIZE];   /* macros hashed by name */
  87. static World NoWorld, AnyWorld;        /* explicit "no" and "don't care" */
  88. static int mnum = 1;                   /* macro ID number */
  89.  
  90. /* It is IMPORTANT that these be in the same order as enum Hooks */
  91. static char *hook_table[] = {
  92.   "ACTIVITY",   
  93.   "BACKGROUND",
  94.   "BAMF",
  95.   "CONFAIL",
  96.   "CONNECT",
  97.   "DISCONNECT",
  98.   "HISTORY",
  99.   "KILL",
  100.   "LOAD",
  101.   "LOADFAIL",
  102.   "LOGIN",
  103.   "MAIL",
  104.   "MORE",
  105.   "PENDING",
  106.   "PROCESS",
  107.   "REDEF",
  108.   "RESIZE",
  109.   "RESUME",
  110.   "SEND",
  111.   "SHELL",
  112.   "WORLD"
  113. };
  114.  
  115. #define NUM_HOOKS (sizeof(hook_table) / sizeof(char*))
  116.  
  117. #define macmatch(spec, macro) macro_match((spec), (macro), smatch)
  118. #define maccmp(spec, macro)   macro_match((spec), (macro), cstrcmp)
  119.  
  120. #define Unlink(Mac, Head, Next, Prev)      /* delete Mac from linked list */ \
  121.     do {\
  122.         if (Mac->Next) Mac->Next->Prev = Mac->Prev;\
  123.         if (Mac->Prev) Mac->Prev->Next = Mac->Next;\
  124.         else Head = Mac->Next;\
  125.     } while (0)
  126.  
  127. #define Insert(Mac, Head, Next, Prev)      /* add Mac to beginning of list */ \
  128.     do {\
  129.         if (Head) Head->Prev = Mac;\
  130.         Mac->Next = Head;\
  131.         Mac->Prev = NULL;\
  132.         Head = Mac;\
  133.     } while (0)
  134.  
  135. /* These macros allow easy sharing of trigger and hook code. The address and
  136.  * dereference is necessary to allow the expression to be used portably as
  137.  * the Left Hand Side of '='.
  138.  */
  139. #define Head         (*(hook ? &hhead      : &thead))
  140. #define Next(mac)    (*(hook ? &mac->hnext : &mac->tnext))
  141. #define Prev(mac)    (*(hook ? &mac->hprev : &mac->tprev))
  142. #define Pattern(mac) (*(hook ? &mac->hargs : &mac->trig))
  143. #define Flag(mac)    ((hook ? mac->hook  : (int)mac->attr))  /* can't be lhs */
  144.  
  145.  
  146. void init_macros()
  147. {
  148.     int i;
  149.  
  150.     for (i = 0; i < HASH_SIZE; i++) hash_table[i] = NULL;
  151. }
  152.  
  153. /***************************************
  154.  * Routines for parsing macro commands *
  155.  ***************************************/
  156.  
  157. short parse_attrs(args)            /* convert attribute string to bitfields */
  158.     char *args;
  159. {
  160.     short attrs = 0;
  161.  
  162.     while (*args) {
  163.         switch(*args++) {
  164.         case 'n':  break;
  165.         case 'G':  attrs |= F_NORECORD;  break;
  166.         case 'g':  attrs |= F_GAG;       break;
  167.         case 'u':  attrs |= F_UNDERLINE; break;
  168.         case 'r':  attrs |= F_REVERSE;   break;
  169.         case 'f':  attrs |= F_FLASH;     break;
  170.         case 'd':  attrs |= F_DIM;       break;
  171.         case 'h':  attrs |= F_HILITE;    break;
  172.         case 'b':  attrs |= F_BELL;      break;
  173.         default:
  174.             oputs("% Invalid attribute in 'a' option");
  175.             return -1;
  176.         }
  177.     }
  178.     return attrs;
  179. }
  180.  
  181. static int parse_hook(args)            /* convert hook string to bitfields */
  182.     char *args;
  183. {
  184.     char *in;
  185.     int hook, result = 0, done;
  186.  
  187.     if (strcmp(args, "*") == 0) return AnyHook;
  188.     for (done = FALSE; !done; args = in) {
  189.         if ((in = strchr(args, '|')) == NULL) done = TRUE;
  190.         else *in++ = '\0';
  191.         if ((hook = find_hook(args)) == 0) return 0;
  192.         result |= hook;
  193.     }
  194.     return result;
  195. }
  196.  
  197. Macro *macro_spec(args)           /* convert user macro string to Macro */
  198.     char *args;
  199. {
  200.     Macro *spec;
  201.     char opt, *ptr, *trig = NULL, *bind = NULL, *hargs = NULL;
  202.     int num, error = FALSE;
  203.  
  204.     spec = (Macro *)MALLOC(sizeof(struct Macro));
  205.     spec->next = spec->tnext = spec->hnext = NULL;
  206.     spec->prev = spec->tprev = spec->hprev = NULL;
  207.     spec->name = spec->trig = spec->bind = spec->hargs = spec->body = NULL;
  208.     spec->hook = 0;
  209.     spec->func = NULL;
  210.     spec->world = NULL;
  211.     spec->pri = spec->prob = spec->shots = -1;
  212.     spec->invis = 0;
  213.     spec->attr = 0;
  214.     spec->temp = TRUE;
  215.  
  216.     startopt(args, "p#c#b:t:w:h:a:f:iIn#1:");
  217.     while (opt = nextopt(&ptr, &num)) {
  218.         switch (opt) {
  219.         case 'p':
  220.             spec->pri = num;
  221.             break;
  222.         case 'c':
  223.             spec->prob = num;
  224.             break;
  225.         case 'i':
  226.             spec->invis = 1;
  227.             break;
  228.         case 'I':
  229.             spec->invis = 2;
  230.             break;
  231.         case 'b':
  232.             if (bind) FREE(bind);
  233.             else bind = STRDUP(translate_keystring(ptr));
  234.             break;
  235.         case 't':
  236.             if (trig) FREE(trig);
  237.             if (!smatch_check(trig = STRDUP(ptr))) error = TRUE;
  238.             break;
  239.         case 'w':
  240.             if (!*ptr || strcmp(ptr, "+") == 0) spec->world = &AnyWorld;
  241.             else if (strcmp(ptr, "-") == 0) spec->world = &NoWorld;
  242.             else if ((spec->world = find_world(ptr)) == NULL) {
  243.                 oprintf("%% No world %s", ptr);
  244.                 error = TRUE;
  245.             }
  246.             break;
  247.         case 'h':
  248.             if (hargs) FREE(hargs);
  249.             if (!*ptr || strcmp(ptr, "+") == 0) spec->hook = AnyHook;
  250.             else if (strcmp(ptr, "-") == 0) spec->hook = 0;
  251.             else {
  252.                 if ((hargs = strchr(ptr, ' ')) != NULL) *hargs++ = '\0';
  253.                 if ((spec->hook = parse_hook(ptr)) == 0) error = TRUE;
  254.                 else hargs = STRDUP(hargs ? stripstr(hargs) : "");
  255.             }
  256.             break;
  257.         case 'a': case 'f':
  258.             if ((spec->attr |= parse_attrs(ptr)) < 0) error = TRUE;
  259.             break;
  260.         case 'n':
  261.             spec->shots = num;
  262.             break;
  263.         case '1':
  264.             if (*ptr && *(ptr + 1)) error = TRUE;
  265.             else if (!*ptr || *ptr == '+') spec->shots = 1;
  266.             else if (*ptr == '-') spec->shots = 0;
  267.             else error = TRUE;
  268.             if (error) oputs("% Invalid argument to 1 option");
  269.             break;
  270.         default:
  271.             error = TRUE;
  272.         }
  273.  
  274.         if (error) {
  275.             nuke_macro(spec);
  276.             return NULL;
  277.         }
  278.     }
  279.  
  280.     if (trig) spec->trig = trig;
  281.     if (bind) spec->bind = bind;
  282.     spec->hargs = (hargs) ? hargs : STRDUP("");
  283.     if (!*ptr) return spec;
  284.     spec->name = ptr;
  285.     if ((ptr = strchr(ptr, '=')) != NULL) {
  286.         *ptr = '\0';
  287.         spec->body = ptr + 1;
  288.         stripstr(spec->body);
  289.         if (*spec->body) {
  290.             spec->body = STRDUP(spec->body);
  291.             if (cstrncmp(spec->body, "/DOKEY ", 7) == 0)
  292.                 spec->func = find_efunc(stripstr(spec->body + 7));
  293.         } else spec->body = NULL;
  294.     }
  295.     stripstr(spec->name);
  296.     spec->name = *spec->name ? STRDUP(spec->name) : NULL;
  297.     return spec;
  298. }
  299.  
  300.  
  301. /********************************
  302.  * Routines for locating macros *
  303.  ********************************/
  304.  
  305. static int macro_match(spec, macro, cmp)     /* compare spec to macro */
  306.     Macro *spec, *macro;
  307.     int FDECL((*cmp),(char *s1, char *s2));  /* string comparison function */
  308. {
  309.     if (!spec->invis && macro->invis) return 1;
  310.     if (spec->invis == 2 && !macro->invis) return 1;
  311.     if (spec->shots != -1 && spec->shots != macro->shots) return 1;
  312.     if (spec->prob != -1 && spec->prob != macro->prob) return 1;
  313.     if (spec->pri != -1 && spec->pri != macro->pri) return 1;
  314.     if (spec->world && spec->world != macro->world &&
  315.         (spec->world != &AnyWorld || macro->world == &NoWorld)) return 1;
  316.     if (spec->hook) {
  317.         if ((spec->hook & macro->hook) == 0) return 1;
  318.         if (*spec->hargs && (*cmp)(spec->hargs, macro->hargs) != 0) return 1;
  319.     }
  320.     if (spec->attr && (spec->attr & macro->attr) == 0) return 1;
  321.     if (spec->trig) {
  322.         if (strcmp(spec->trig, "+") == 0 || strcmp(spec->trig, "") == 0) {
  323.             if (!*macro->trig) return 1;
  324.         } else if (strcmp(spec->trig, "-") == 0) {
  325.             if (*macro->trig) return 1;
  326.         } else if ((*cmp)(spec->trig, macro->trig) != 0) return 1;
  327.     }
  328.     if (spec->bind) {
  329.         if (strcmp(spec->bind, "+") == 0 || strcmp(spec->bind, "") == 0) {
  330.             if (!*macro->bind) return 1;
  331.         } else if (strcmp(spec->bind, "-") == 0) {
  332.             if (*macro->bind) return 1;
  333.         } else if ((*cmp)(spec->bind, macro->bind) != 0) return 1;
  334.     }
  335.     if (spec->name && (*cmp)(spec->name, macro->name) != 0) return 1;
  336.     if (spec->body && (*cmp)(spec->body, macro->body) != 0) return 1;
  337.     return 0;
  338. }
  339.  
  340. static Macro *find_macro(name)              /* find Macro by name */
  341.     char *name;
  342. {
  343.     Macro *p;
  344.  
  345.     if (!*name) return NULL;
  346.     for (p = hash_table[hash_string(name, HASH_SIZE)]; p; p = p->bnext) {
  347.         if (cstrcmp(name, p->name) == 0) return p;
  348.     }
  349.     return NULL;
  350. }
  351.  
  352. static int find_hook(name)                  /* convert hook name to int */
  353.     char *name;
  354. {
  355.     int bottom, top, mid, value;
  356.  
  357.     bottom = 0;
  358.     top = NUM_HOOKS - 1;
  359.     while (bottom <= top) {
  360.         mid = (top + bottom) / 2;
  361.         value = cstrcmp(name, hook_table[mid]);
  362.         if (value == 0) return (1 << mid);
  363.         else if (value < 0) top = mid - 1;
  364.         else bottom = mid + 1;
  365.     }
  366.     oprintf("%% No hook for \"%s\"", name);
  367.     return 0;
  368. }
  369.  
  370. static Macro *match_exact(hook, str, flags)   /* find single exact match */
  371.     int hook, flags;
  372.     char *str;
  373. {
  374.     Macro *macro;
  375.   
  376.     if ((hook && !flags) || (!hook && !*str)) return NULL;
  377.     for (macro = Head; macro; macro = Next(macro))
  378.         if ((Flag(macro) & flags) && !cstrcmp(Pattern(macro), str)) break;
  379.     return macro;
  380. }
  381.  
  382. static Macro *find_match(hook, text)       /* find a matching hook or trig */
  383.     char *text;
  384.     int hook;
  385. {
  386.     Macro *first = NULL, *macro;
  387.     int num = 0, pri = -1;
  388.  
  389.     for (macro = Head; macro && macro->pri >= pri; macro = Next(macro)) {
  390.         if (hook && !(macro->hook & hook)) continue;
  391.         if (macro->world && macro->world != xworld()) continue;
  392.         if ((hook && !*macro->hargs) || equalstr(Pattern(macro), text)) {
  393.             if (macro->pri > pri) {
  394.                 macro->pnext = NULL;
  395.                 num = 1;
  396.                 pri = macro->pri;
  397.             } else {
  398.                 macro->pnext = first;
  399.                 num++;
  400.             }
  401.             first = macro;
  402.         }
  403.     }
  404.     if (!first) return NULL;
  405.     for (num = (int)(RANDOM() % num); num--; first = first->pnext);
  406.     return first;
  407. }
  408.  
  409.  
  410. /**************************
  411.  * Routines to add macros *
  412.  **************************/
  413.  
  414. /* create a Macro */
  415. Macro *new_macro(name, trig, bind, hook, hargs, body, world, pri, prob,
  416.   attr, shots, invis)
  417.     char *name, *trig, *bind, *body, *hargs;
  418.     int hook, pri, prob, attr, shots, invis;
  419.     World *world;
  420. {
  421.     Macro *new;
  422.  
  423.     new = (Macro *) MALLOC(sizeof(struct Macro));
  424.     new->next = new->prev = new->bnext = new->bprev = NULL;
  425.     new->tnext = new->tprev = new->hnext = new->hprev = NULL;
  426.     new->name = STRDUP(name);
  427.     new->trig = STRDUP(trig);
  428.     new->bind = STRDUP(bind);
  429.     new->hook = hook;
  430.     new->hargs = STRDUP(hargs);
  431.     new->body = STRDUP(body);
  432.     new->world = world;
  433.     new->hook = hook;
  434.     new->pri = pri;
  435.     new->prob = prob;
  436.     new->attr = attr;
  437.     new->shots = shots;
  438.     new->invis = invis;
  439.     new->temp = TRUE;
  440.     if (cstrncmp(new->body, "/DOKEY ", 7) == 0)
  441.         new->func = find_efunc(stripstr(new->body + 7));
  442.     else new->func = NULL;
  443.  
  444.     return new;
  445. }
  446.  
  447. /* add permanent Macro to appropriate structures */
  448. /* add_macro() assumes there is no confict with existing names */
  449. void add_macro(macro)
  450.     Macro *macro;
  451. {
  452.     macro->num = mnum++;
  453.     Insert(macro, head, next, prev);
  454.     if (tail == NULL) tail = macro;
  455.     if (*macro->name) hash_insert(macro);
  456.     if (*macro->trig) insert_by_priority(macro, FALSE);
  457.     if (macro->hook) insert_by_priority(macro, TRUE);
  458.     macro->temp = FALSE;
  459. }
  460.  
  461. static void insert_by_priority(macro, hook)        /* insert trig or hook */
  462.     Macro *macro;
  463.     int hook;
  464. {
  465.     Macro *p;
  466.  
  467.     if (Head == NULL || macro->pri >= Head->pri) {
  468.         Prev(macro) = NULL;
  469.         Next(macro) = Head;
  470.         Head = macro;
  471.     } else {
  472.         for (p = Head; Next(p) && macro->pri < Next(p)->pri; p = Next(p));
  473.         Prev(macro) = p;
  474.         Next(macro) = Next(p);
  475.         Next(p) = macro;
  476.     }
  477.     if (Next(macro)) Prev(Next(macro)) = macro;
  478. }
  479.  
  480. int install_bind(spec)          /* install Macro's binding in key structures */
  481.     Macro *spec;
  482. {
  483.     Macro *macro;
  484.  
  485.     if (macro = find_key(spec->bind)) {
  486.         if (redef) {
  487.             do_hook(H_REDEF, "%% Redefined %s %s", "%s %s",
  488.                 "binding", keyname(spec->bind));
  489.             nuke_macro(macro);
  490.         } else return 0;
  491.     }
  492.     if (bind_key(spec)) return 1;  /* fails if is prefix or has prefix */
  493.     nuke_macro(spec);
  494.     return 0;
  495. }
  496.  
  497. void do_add(spec)                          /* define a new Macro (general) */
  498.     Macro *spec;
  499. {
  500.     Macro *macro = NULL;
  501.  
  502.     if (spec->name && find_command(spec->name)) {
  503.         oprintf("%% /%s is a builtin command", spec->name);
  504.         nuke_macro(spec);
  505.         return;
  506.     }
  507.  
  508.     if (spec->world == &AnyWorld) spec->world = NULL;
  509.     if (spec->pri == -1) spec->pri = 1;
  510.     if (spec->prob == -1) spec->prob = 100;
  511.     if (spec->shots == -1) spec->shots = 0;
  512.     if (spec->invis) spec->invis = 1;
  513.     if (spec->attr & F_NORM || !spec->attr) spec->attr = F_NORM;
  514.     if (!spec->trig) spec->trig = STRDUP("");
  515.     if (!spec->bind) spec->bind = STRDUP("");
  516.     if (!spec->name) spec->name = STRDUP("");
  517.     if (!spec->body) spec->body = STRDUP("");
  518.  
  519.     if (*spec->name && (macro = find_macro(spec->name)) && !redef) {
  520.         oprintf("%% Macro %s already exists", spec->name);
  521.         nuke_macro(spec);
  522.         return;
  523.     }
  524.     if (*spec->bind && !install_bind(spec)) return;
  525.     add_macro(spec);
  526.     if (macro) {
  527.         do_hook(H_REDEF, "%% Redefined %s %s", "%s %s", "macro", spec->name);
  528.         nuke_macro(macro);
  529.     }
  530. }
  531.  
  532. void add_hook(name, body)                  /* define a new Macro with hook */
  533.     char *name, *body;
  534. {
  535.     int hook;
  536.     char *hargs;
  537.  
  538.     if ((hargs = strchr(name, ' ')) != NULL) *hargs++ = '\0';
  539.     if (!(hook = parse_hook(name))) return;
  540.     hargs = STRDUP(hargs ? stripstr(hargs) : "");
  541.     add_macro(new_macro("", "", "", hook, hargs, body, NULL, 0, 100, 1, 0, 0));
  542. }
  543.  
  544. void do_edit(spec)                         /* edit an existing Macro */
  545.     Macro *spec;
  546. {
  547.     Macro *macro;
  548.     int number;
  549.  
  550.     if (spec->name == NULL) {
  551.         oputs("% You must specify a macro.");
  552.         nuke_macro(spec);
  553.         return;
  554.     } else if (spec->name[0] == '#') {
  555.         number = atoi(spec->name + 1);
  556.         for (macro = head; macro != NULL; macro = macro->next)
  557.             if (macro->num == number) break;
  558.         if (macro == NULL) {
  559.             oprintf("%% Macro #%d does not exist", number);
  560.             nuke_macro(spec);
  561.             return;
  562.         }
  563.     } else if (spec->name[0] == '$') {
  564.         if ((macro = match_exact(FALSE, spec->name + 1, F_ALL)) == NULL) {
  565.             oprintf("%% Trigger %s does not exist", spec->name + 1);
  566.             nuke_macro(spec);
  567.             return;
  568.         }
  569.     } else if ((macro = find_macro(spec->name)) == NULL) {
  570.         oprintf("%% Macro %s does not exist", spec->name);
  571.         nuke_macro(spec);
  572.         return;
  573.     }
  574.  
  575.     if (spec->body && *spec->body) {
  576.         FREE(macro->body);
  577.         macro->body = STRDUP(spec->body);
  578.     }
  579.     if (spec->trig && *spec->trig) {
  580.         FREE(macro->trig);
  581.         macro->trig = STRDUP(spec->trig);
  582.         if (spec->pri != -1) {                    /* re-link if pri changed */
  583.             Unlink(macro, thead, tnext, tprev);
  584.             insert_by_priority(macro, FALSE);
  585.         }
  586.     }
  587.     if (spec->hook) {
  588.         macro->hook = spec->hook;
  589.         if (*macro->hargs) FREE(macro->hargs);
  590.         macro->hargs = STRDUP((spec->hargs && *spec->hargs) ? spec->hargs : "");
  591.         if (spec->pri != -1) {                    /* re-link if pri changed */
  592.             Unlink(macro, hhead, hnext, hprev);
  593.             insert_by_priority(macro, TRUE);
  594.         }
  595.     }
  596.     if (spec->world && spec->world != &AnyWorld) macro->world = spec->world;
  597.     if (spec->pri != -1) macro->pri = spec->pri;
  598.     if (spec->prob != -1) macro->prob = spec->prob;
  599.     if (spec->shots != -1) macro->shots = spec->shots;
  600.     if (spec->attr & F_NORM) macro->attr = F_NORM;
  601.     else if (spec->attr & F_GAG) macro->attr = spec->attr & F_SUPERGAG;
  602.     else if (spec->attr) macro->attr = spec->attr;
  603.     macro->invis = (spec->invis) ? 1 : 0;
  604.     nuke_macro(spec);
  605. }
  606.  
  607. static int hash_string(str, size)          /* convert string to hash index */
  608.     char *str;
  609.     int size;
  610. {
  611.     unsigned int hashval;
  612.  
  613.     for (hashval = 0; *str; str++) hashval = lcase(*str) + 33 * hashval;
  614.     return (int)(hashval % (unsigned int)size);
  615. }
  616.  
  617. static void hash_insert(macro)             /* add Macro to hash table by name */
  618.     Macro *macro;
  619. {
  620.     int index;
  621.  
  622.     index = hash_string(macro->name, HASH_SIZE);
  623.     Insert(macro, hash_table[index], bnext, bprev);
  624. }
  625.  
  626.  
  627. /********************************
  628.  * Routines for removing macros *
  629.  ********************************/
  630.  
  631. void nuke_macro(macro)            /* remove Macro from all lists, and free it */
  632.     Macro *macro;
  633. {
  634.     if (!macro->temp) {
  635.         if (*macro->name) hash_remove(macro);
  636.         if (macro == tail) tail = macro->prev;
  637.         Unlink(macro, head, next, prev);
  638.         if (macro->trig && *macro->trig) Unlink(macro, thead, tnext, tprev);
  639.         if (macro->hook) Unlink(macro, hhead, hnext, hprev);
  640.         if (*macro->bind) unbind_key(macro);
  641.     }
  642.  
  643.     if (macro->name) FREE(macro->name);
  644.     if (macro->body) FREE(macro->body);
  645.     if (macro->trig) FREE(macro->trig);
  646.     if (macro->hargs) FREE(macro->hargs);
  647.     if (macro->bind) FREE(macro->bind);
  648.     FREE(macro);
  649. }
  650.  
  651. void remove_macro(str, flags, byhook)        /* delete a macro */
  652.     char *str;
  653.     int flags;
  654.     int byhook;
  655. {
  656.     Macro *macro = NULL;
  657.     char *args;
  658.  
  659.     if (byhook) {
  660.         if ((args = strchr(str, ' ')) != NULL) *args++ = '\0';
  661.         if ((flags = parse_hook(str)) == 0) return;
  662.         if ((macro = match_exact(byhook, args ? args : "", flags)) == NULL)
  663.             oprintf("%% Hook on \"%s\" was not defined.", str);
  664.     } else if (flags) {
  665.         if ((macro = match_exact(byhook, str, flags)) == NULL)
  666.             oprintf("%% Trigger on \"%s\" was not defined.", str);
  667.     } else {
  668.         if ((macro = find_macro(str)) == NULL)
  669.             oprintf("%% Macro \"%s\" was not defined.", str);
  670.     }
  671.     if (macro) nuke_macro(macro);
  672. }
  673.  
  674. void purge_macro(spec)                   /* delete all specified macros */
  675.     Macro *spec;
  676. {
  677.     Macro *macro, *next;
  678.  
  679.     if (!spec) return;
  680.     if (spec->name && !smatch_check(spec->name)) return;
  681.     if (spec->trig && !smatch_check(spec->trig)) return;
  682.     if (spec->hargs && !smatch_check(spec->hargs)) return;
  683.     if (spec->bind && !smatch_check(spec->bind)) return;
  684.     if (spec->body && !smatch_check(spec->body)) return;
  685.     for (macro = head; macro; macro = next) {
  686.         next = macro->next;
  687.         if (macmatch(spec, macro) == 0) nuke_macro(macro);
  688.     }
  689.     nuke_macro(spec);
  690. }
  691.  
  692. void remove_by_number(args)                 /* delete macro by number */
  693.     char *args;
  694. {
  695.     char *ptr;
  696.     int num;
  697.     Macro *p;
  698.  
  699.     ptr = args;
  700.     do {
  701.         while (isspace(*ptr)) ptr++;
  702.         num = atoi(ptr);
  703.         if (num > 0 && num < mnum) {
  704.             for (p = head; p != NULL; p = p->next) if (p->num == num) break;
  705.             if (p != NULL) nuke_macro(p);
  706.             else oprintf("%% No macro with number %d", num);
  707.         } else oprintf("%% Invalid number %d", num);
  708.         ptr = strchr(ptr, ' ');
  709.     } while (ptr != NULL);
  710. }
  711.  
  712. static void hash_remove(macro)              /* remove macro from hash table */
  713.     Macro *macro;
  714. {
  715.     int index;
  716.  
  717.     index = hash_string(macro->name, HASH_SIZE);
  718.     Unlink(macro, hash_table[index], bnext, bprev);
  719. }
  720.  
  721.  
  722. /**************************
  723.  * Routine to list macros *
  724.  **************************/
  725.  
  726. static String *hook_name(hook)        /* convert hook bitfield to string */
  727.     int hook;
  728. {
  729.     int n;
  730.     STATIC_BUFFER(buf)
  731.  
  732.     Stringterm(buf, 0);
  733.     for (n = 0; n < NUM_HOOKS; n++) {
  734.         if (!((1 << n) & hook)) continue;
  735.         if (buf->len) Stringadd(buf, '|');
  736.         Stringcat(buf, hook_table[n]);
  737.     }
  738.     return buf;
  739. }
  740.  
  741. static String *escape(src)                /* insert '\' before each '"' */
  742.     char *src;
  743. {
  744.     char *ptr;
  745.     STATIC_BUFFER(dest)
  746.  
  747.     for (Stringterm(dest, 0); *src; src = ptr) {
  748.         if (*src == '\"' || *src == '\\') Sprintf(dest, "\200\\%c", *src++);
  749.         for (ptr = src; *ptr && *ptr != '\"' && *ptr != '\\'; ptr++);
  750.         Stringncat(dest, src, ptr - src);
  751.     }
  752.     return dest;
  753. }
  754.  
  755. static void list_defs(file, spec, abbrev)    /* list all specified macros */
  756.     TFILE *file;
  757.     Macro *spec;
  758.     int abbrev;
  759. {
  760.     Macro *p;
  761.     STATIC_BUFFER(buffer)
  762.  
  763.     if (spec->name && !smatch_check(spec->name)) return;
  764.     if (spec->trig && !smatch_check(spec->trig)) return;
  765.     if (spec->hargs && !smatch_check(spec->hargs)) return;
  766.     if (spec->bind && !smatch_check(spec->bind)) return;
  767.     if (spec->body && !smatch_check(spec->body)) return;
  768.  
  769.     for (p = tail; p != NULL; p = p->prev) {
  770.         if (macmatch(spec, p) != 0) continue;
  771.  
  772.         if (abbrev) {
  773.             Sprintf(buffer, "%% %d: ", p->num);
  774.             if (p->attr & F_NORECORD) Stringcat(buffer, "(norecord) ");
  775.             if (p->attr & F_GAG) Stringcat(buffer, "(gag) ");
  776.             else if (p->attr & ~F_NORM) {
  777.                 if (p->attr & F_UNDERLINE) Stringcat(buffer, "(underline) ");
  778.                 if (p->attr & F_REVERSE)   Stringcat(buffer, "(reverse) ");
  779.                 if (p->attr & F_FLASH)     Stringcat(buffer, "(flash) ");
  780.                 if (p->attr & F_DIM)       Stringcat(buffer, "(dim) ");
  781.                 if (p->attr & F_HILITE)    Stringcat(buffer, "(hilite) ");
  782.                 if (p->attr & F_BELL)      Stringcat(buffer, "(bell) ");
  783.             } else if (*p->trig) Stringcat(buffer, "(trig) ");
  784.             if (*p->trig) Sprintf(buffer, "\200\"%S\" ", escape(p->trig));
  785.             if (*p->bind)
  786.                 Sprintf(buffer, "\200(bind) \"%S\" ", escape(keyname(p->bind)));
  787.             if (p->hook) Sprintf(buffer, "\200(hook) %S ", hook_name(p->hook));
  788.             if (*p->name) Sprintf(buffer, "\200%s ", p->name);
  789.  
  790.         } else {
  791.             if (!file) Sprintf(buffer, "%% %d: /def ", p->num);
  792.             else Stringcpy(buffer, "/def ");
  793.             if (p->invis) Stringcat(buffer, "-i ");
  794.             if ((*p->trig || p->hook) && p->pri)
  795.                 Sprintf(buffer, "\200-p%d ", p->pri);
  796.             if (*p->trig && p->prob != 100)
  797.                 Sprintf(buffer, "\200-c%d ", p->prob);
  798.             if (p->attr & F_GAG) {
  799.                 Stringcat(buffer, "-ag");
  800.                 if (p->attr & F_NORECORD)  Stringadd(buffer, 'G');
  801.                 Stringadd(buffer, ' ');
  802.             } else if (p->attr & ~F_NORM) {
  803.                 Stringcat(buffer, "-a");
  804.                 if (p->attr & F_NORECORD)  Stringadd(buffer, 'G');
  805.                 if (p->attr & F_UNDERLINE) Stringadd(buffer, 'u');
  806.                 if (p->attr & F_REVERSE)   Stringadd(buffer, 'r');
  807.                 if (p->attr & F_FLASH)     Stringadd(buffer, 'f');
  808.                 if (p->attr & F_DIM)       Stringadd(buffer, 'd');
  809.                 if (p->attr & F_HILITE)    Stringadd(buffer, 'h');
  810.                 if (p->attr & F_BELL)      Stringadd(buffer, 'b');
  811.                 Stringadd(buffer, ' ');
  812.             }
  813.             if (p->world) Sprintf(buffer, "\200-w%s ", p->world->name);
  814.             if (p->shots) Sprintf(buffer, "\200-n%d ", p->shots);
  815.             if (*p->trig) Sprintf(buffer, "\200-t\"%S\" ", escape(p->trig));
  816.             if (p->hook) {
  817.                 Sprintf(buffer, "\200-h\"%S", hook_name(p->hook));
  818.                 if (*p->hargs) Sprintf(buffer, "\200 %S", escape(p->hargs));
  819.                 Stringcat(buffer, "\" ");
  820.             }
  821.             if (*p->bind) 
  822.                 Sprintf(buffer, "\200-b\"%S\" ", escape(keyname(p->bind)));
  823.             if (*p->name) Sprintf(buffer, "\200%s ", p->name);
  824.             if (*p->body) Sprintf(buffer, "\200= %s", p->body);
  825.         }
  826.  
  827.         if (file) {
  828.             Stringadd(buffer, '\n');
  829.             tfputs(buffer->s, file);
  830.         } else oputs(buffer->s);
  831.     }
  832. }
  833.  
  834. void save_macros(args)              /* write specified macros to file */
  835.     char *args;
  836. {
  837.     char *p;
  838.     Macro *spec;
  839.     TFILE *file;
  840.  
  841.     for (p = args; *p && !isspace(*p); p++);
  842.     if (*p) *p++ = '\0';
  843.     if ((file = tfopen(args, NULL, "w")) == NULL) return;
  844.     oprintf("%% Saving macros to %s", file->name);
  845.     if ((spec = macro_spec(p)) == NULL) return;
  846.     list_defs(file, spec, FALSE);
  847.     tfclose(file);
  848.     nuke_macro(spec);
  849. }
  850.  
  851. void list_macros(args)                    /* list specified macros on screen */
  852.     char *args;
  853. {
  854.     Macro *spec;
  855.     char *p;
  856.     int abbrev = FALSE;
  857.  
  858.     for (p = args; isspace(*p); p++);
  859.     if (p[0] == '-' && p[1] == 's' && (p[2] == '\0' || isspace(p[2]))) {
  860.         args = p + 2;
  861.         abbrev = TRUE;
  862.     }
  863.     if ((spec = macro_spec(args)) == NULL) return;
  864.     list_defs(NULL, spec, abbrev);
  865.     nuke_macro(spec);
  866. }
  867.  
  868.  
  869. /**************************
  870.  * Routines to use macros *
  871.  **************************/
  872.  
  873. int world_subs(src, dest)            /* evaulate "world_*" subtitutions */
  874.     char *src;
  875.     Stringp dest;
  876. {
  877.     char *ptr = src + 6;
  878.     World *world, *def;
  879.  
  880.     if (cstrncmp("world_", src, 6) != 0) return 0;
  881.     if (!(world = xworld())) return 1;
  882.     def = get_default_world();
  883.  
  884.     if (!cstrcmp("name", ptr)) Stringcpy(dest, world->name);
  885.     else if (!cstrcmp("character", ptr)) {
  886.         if (*world->character) Stringcpy(dest, world->character);
  887.         else if (def != NULL) Stringcpy(dest, def->character);
  888.     } else if (!cstrcmp("password", ptr)) {
  889.         if (*world->pass) Stringcpy(dest, world->pass);
  890.         else if (def != NULL) Stringcpy(dest, def->pass);
  891.     } else if (!cstrcmp("host", ptr)) Stringcpy(dest,  world->address);
  892.     else if (!cstrcmp("port", ptr)) Stringcpy(dest, world->port);
  893.     else if (!cstrcmp("mfile", ptr)) {
  894.         if (*world->mfile) Stringcpy(dest, world->mfile);
  895.         else if (def != NULL) Stringcpy(dest, def->mfile);
  896.     }
  897.     return 1;
  898. }
  899.  
  900. void do_macro(name, args, dest, toplevel)       /* Do a macro! */
  901.     char *name, *args;
  902.     Stringp dest;
  903.     int toplevel;
  904. {
  905.     Macro *macro;
  906.  
  907.     if (world_subs(name, dest)) return;
  908.     if (!(macro = find_macro(name))) oprintf("%% Macro not defined: %s", name);
  909.     else if (macro->func) (*macro->func)();
  910.     else process_macro(macro->body, args, dest, toplevel);
  911. }
  912.  
  913. void get_macro_body(name, dest)             /* get body of macro, duh */
  914.     char *name;
  915.     Stringp dest;
  916. {
  917.     Macro *m;
  918.  
  919.     if (world_subs(name, dest)) return;
  920.     if ((m = find_macro(name)) == NULL)
  921.         oprintf("%% Macro not defined: %s", name);
  922.     else Stringcpy(dest, m->body);
  923. }
  924.  
  925.  
  926. /****************************************
  927.  * Routines to check triggers and hooks *
  928.  ****************************************/
  929.  
  930. #ifdef STANDARD_C_YES_SIREE
  931. short do_hook(int idx, char *fmt, char *argfmt, ...)     /* do a hook event */
  932. #else
  933. /* VARARGS */
  934. short do_hook(va_alist)
  935. va_dcl
  936. #endif
  937. {
  938. #ifndef STANDARD_C_YES_SIREE
  939.     int idx;
  940.     char *fmt, *argfmt;
  941. #endif
  942.     va_list ap;
  943.     Macro *macro;
  944.     short attr = 0;
  945.     static Stringp buf, args;
  946.     static int buffers_initted = FALSE;
  947.     extern int hookflag;
  948.  
  949.     if (!buffers_initted) {
  950.         Stringinit(buf);
  951.         Stringinit(args);
  952.         buffers_initted = TRUE;
  953.     }
  954.  
  955. #ifdef STANDARD_C_YES_SIREE
  956.     va_start(ap, argfmt);
  957. #else
  958.     va_start(ap);
  959.     idx = va_arg(ap, int);
  960.     fmt = va_arg(ap, char *);
  961.     argfmt = va_arg(ap, char *);
  962. #endif
  963.     Stringterm(args, 0);
  964.     vSprintf(args, argfmt, ap);
  965.     va_end(ap);
  966.  
  967.     if (macro = find_match((1<<idx), args->s)) attr = macro->attr;
  968.  
  969.     if (fmt) {
  970. #ifdef STANDARD_C_YES_SIREE
  971.         va_start(ap, argfmt);
  972. #else
  973.         va_start(ap);
  974.         idx = va_arg(ap, int);
  975.         fmt = va_arg(ap, char *);
  976.         argfmt = va_arg(ap, char *);
  977. #endif
  978.         vSprintf(buf, fmt, ap);
  979.         output(buf->s, attr | F_NEWLINE);
  980.     }
  981.     va_end(ap);
  982.  
  983.     if (hookflag && macro && !(RANDOM() % 100 > macro->prob))
  984.         run_trig_or_hook(macro, args->s);
  985.     return attr;
  986. }
  987.  
  988. short check_trigger(text)                 /* look for trigger, and call it */
  989.     char *text;
  990. {
  991.     extern int borg;
  992.     Macro *macro;
  993.     short attr;
  994.  
  995.     if ((macro = find_match(0, text)) == NULL) return 1;
  996.     attr = macro->attr;
  997.     if (borg && RANDOM() % 100 <= macro->prob && run_trig_or_hook(macro, text))
  998.         background_hook(text);
  999.     return attr;
  1000. }
  1001.  
  1002. static int run_trig_or_hook(macro, text)
  1003.     Macro *macro;
  1004.     char *text;
  1005. {
  1006.     int result = 0, need_dup;
  1007.     char *body;
  1008.     Stringp dst;
  1009.     void NDECL((*func));
  1010.  
  1011.     need_dup = (!(func = macro->func) && macro->shots == 1);
  1012.     body = need_dup ? STRDUP(macro->body) : macro->body;
  1013.     if (macro->shots && !--macro->shots) nuke_macro(macro);
  1014.     if (func) {
  1015.         result = TRUE;
  1016.         (*func)();
  1017.     } else if (*body) {
  1018.         result = TRUE;
  1019.         Stringinit(dst);
  1020.         process_macro(body, text, dst, 1);
  1021.         Stringfree(dst);
  1022.     }
  1023.     if (need_dup) FREE(body);
  1024.     return result;
  1025. }
  1026.  
  1027.