1 /*************************************************************************
2 * TinyFugue - programmable mud client
3 * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
4 *
5 * TinyFugue (aka "tf") is protected under the terms of the GNU
6 * General Public License. See the file "COPYING" for details.
7 ************************************************************************/
8 static const char RCSid[] = "$Id: macro.c,v 35004.188 2007/01/13 23:12:39 kkeys Exp $";
9
10
11 /**********************************************
12 * Fugue macro package *
13 * *
14 * Macros, hooks, triggers, hilites and gags *
15 * are all processed here. *
16 **********************************************/
17
18 #include "tfconfig.h"
19 #include "port.h"
20 #include "tf.h"
21 #include "util.h"
22 #include "pattern.h"
23 #include "search.h"
24 #include "tfio.h"
25 #include "world.h"
26 #include "macro.h"
27 #include "keyboard.h" /* bind_key()... */
28 #include "expand.h"
29 #include "socket.h" /* xworld() */
30 #include "output.h" /* get_keycode() */
31 #include "attr.h"
32 #include "cmdlist.h"
33 #include "command.h"
34 #include "parse.h" /* valbool() for /def -E */
35 #include "variable.h" /* set_var_by_id() */
36
37 typedef struct {
38 cattr_t attr;
39 short subexp;
40 } subattr_t;
41
42 struct Macro {
43 const char *name;
44 struct ListEntry *numnode; /* node in maclist */
45 struct ListEntry *hashnode; /* node in macro_table hash bucket */
46 struct ListEntry *trignode; /* node in one of the triglists */
47 struct Macro *tnext; /* temp list ptr for collision/death */
48 conString *body, *expr;
49 Program *prog, *exprprog; /* compiled body, expr */
50 const char *bind, *keyname;
51 Pattern trig, hargs, wtype; /* trigger/hook/worldtype patterns */
52 hookvec_t hook; /* bit vector */
53 struct World *world; /* only trig on text from world */
54 int pri, num;
55 attr_t attr;
56 int nsubattr;
57 subattr_t *subattr;
58 short prob, shots, invis;
59 short flags;
60 signed char fallthru, quiet;
61 struct BuiltinCmd *builtin; /* builtin cmd with same name, if any */
62 int used[USED_N]; /* number of calls by each method */
63 };
64
65 typedef struct {
66 Pattern name, body, bind, keyname, expr;
67 } AuxPat;
68
69 typedef struct {
70 int shortflag;
71 int usedflag;
72 Cmp *cmp;
73 } ListOpts;
74
75 int invis_flag = 0;
76
77 static Macro *macro_spec(String *args, int offset, int *xmflag, ListOpts *opts);
78 static int macro_match(Macro *spec, Macro *macro, AuxPat *aux);
79 static int add_numbered_macro(Macro *macro, unsigned int hash, int num,
80 ListEntry *numnode);
81 static int complete_macro(Macro *spec, unsigned int hash, int num,
82 ListEntry *numnode);
83 static int init_aux_patterns(Macro *spec, int mflag, AuxPat *aux);
84 static Macro *match_exact(int hooknum, const char *str, attr_t attrs);
85 static int list_defs(TFILE *file, Macro *spec, int mflag, ListOpts *opts);
86 static void apply_attrs_of_match(Macro *macro, String *text, int hooknum,
87 String *line);
88 static int run_match(Macro *macro, String *text, int hooknum);
89 static const String *hook_name(const hookvec_t *hook) PURE;
90 static conString *print_def(TFILE *file, String *buffer, Macro *p);
91 static int rpricmp(const Macro *m1, const Macro *m2);
92 static void nuke_macro(Macro *macro);
93
94
95 #define HASH_SIZE 997 /* prime number */
96
97 #define MACRO_TEMP 0x01
98 #define MACRO_DEAD 0x02
99 #define MACRO_HOOK 0x08
100 #define MACRO_NOHOOK 0x10 /* -h0 */
101
102 #define INVALID_SUBEXP -3
103
104 static List maclist[1]; /* list of all (live) macros */
105 static List triglist[1]; /* list of macros by trigger */
106 static List hooklist[NUM_HOOKS]; /* lists of macros by hook */
107 static Macro *dead_macros; /* head of list of dead macros */
108 static HashTable macro_table[1]; /* macros hashed by name */
109 static World NoWorld, AnyWorld; /* explicit "no" and "any" */
110 static int mnum = 0; /* macro ID number */
111
112 typedef enum {
113 HT_TEXT = 0x00, /* normal text in fg world */
114 HT_ALERT = 0x01, /* alert */
115 HT_WORLD = 0x02, /* text in xsock->world, plus alert if xsock != fsock */
116 HT_XSOCK = 0x04 /* alert should be cleared if xsock is foregrounded */
117 } hooktype_t;
118
119 typedef struct hookrec {
120 const char *name;
121 hooktype_t hooktype;
122 } hookrec_t;
123
124 static const hookrec_t hook_table[] = {
125 #define gencode(id, type) { #id, type }
126 #include "hooklist.h"
127 #undef gencode
128 };
129
130 #define NONNULL(str) ((str) ? (str) : "")
131
132 /* These macros allow easy sharing of trigger and hook code. */
133 #define MAC(Node) ((Macro *)((Node)->datum))
134
135
init_macros(void)136 void init_macros(void)
137 {
138 int i;
139 init_hashtable(macro_table, HASH_SIZE, cstrstructcmp);
140 init_list(maclist);
141 init_list(triglist);
142 for (i = 0; i < (int)NUM_HOOKS; i++)
143 init_list(&hooklist[i]);
144 }
145
146 /***************************************
147 * Routines for parsing macro commands *
148 ***************************************/
149
hookname2int(const char * name)150 int hookname2int(const char *name)
151 {
152 const hookrec_t *hookrec;
153 hookrec = bsearch((void*)(name), (void *)hook_table,
154 NUM_HOOKS, sizeof(hookrec_t), cstrstructcmp);
155 if (hookrec)
156 return hookrec - hook_table;
157 if (cstrcmp(name, "BACKGROUND") == 0) /* backward compatability */
158 return H_BGTRIG;
159 if (cstrcmp(name, "CONNETFAIL") == 0) /* backward compatability */
160 eprintf("invalid hook event \"%s\"; see \"/help /connect\"", name);
161 else
162 eprintf("invalid hook event \"%s\"", name);
163 return -1;
164 }
165
166 /* Convert hook string to bit vector; return 0 on error, 1 on success. */
parse_hook(char ** argp,hookvec_t * hookvec)167 static int parse_hook(char **argp, hookvec_t *hookvec)
168 {
169 char *in, state;
170
171 VEC_ZERO(hookvec);
172 if (!**argp) {
173 memset(hookvec, 0xFF, sizeof(*hookvec));
174 return 1;
175 }
176 for (state = '|'; state == '|'; *argp = in) {
177 for (in = *argp; *in && !is_space(*in) && *in != '|'; ++in);
178 state = *in;
179 *in++ = '\0';
180 if (strcmp(*argp, "*") == 0) {
181 memset(hookvec, 0xFF, sizeof(*hookvec));
182 } else {
183 int i;
184 if ((i = hookname2int(*argp)) < 0)
185 return 0;
186 VEC_SET(i, hookvec);
187 }
188 }
189 if (!state) *argp = NULL;
190 return 1;
191 }
192
193 /* macro_spec
194 * Converts a macro description string to a more useful Macro structure.
195 * Omitted fields are set to a value that means "don't care".
196 * In /def, don't care fields are set to their default values;
197 * in macro_match(), they are not used in the comparison. Don't care
198 * values for numeric fields are -1 or 0, depending on the field; for
199 * strings, NULL; for hooks, neither MACRO_HOOK nor MACRO_NOHOOK will
200 * be set.
201 */
macro_spec(String * args,int offset,int * xmflag,ListOpts * listopts)202 static Macro *macro_spec(String *args, int offset, int *xmflag, ListOpts *listopts)
203 {
204 Macro *spec;
205 char opt;
206 const char *ptr, *s, *name = NULL, *body = NULL, *nameend, *bodyend;
207 int i, n, mflag = -1, error = 0;
208 ValueUnion uval;
209 attr_t attrs;
210
211 if (!(spec = (Macro *)MALLOC(sizeof(struct Macro)))) {
212 eprintf("macro_spec: not enough memory");
213 return NULL;
214 }
215 spec->num = 0;
216 spec->body = spec->expr = NULL;
217 spec->prog = spec->exprprog = NULL;
218 spec->name = spec->bind = spec->keyname = NULL;
219 spec->numnode = spec->trignode = spec->hashnode = NULL;
220 init_pattern_str(&spec->trig, NULL);
221 init_pattern_str(&spec->hargs, NULL);
222 init_pattern_str(&spec->wtype, NULL);
223 spec->world = NULL;
224 spec->pri = spec->prob = spec->shots = spec->fallthru = spec->quiet = -1;
225 VEC_ZERO(&spec->hook);
226 spec->invis = 0;
227 spec->attr = 0;
228 spec->nsubattr = 0;
229 spec->subattr = NULL;
230 spec->flags = MACRO_TEMP;
231 spec->builtin = NULL;
232 spec->used[USED_NAME] = spec->used[USED_TRIG] =
233 spec->used[USED_HOOK] = spec->used[USED_KEY] = 0;
234
235 startopt(CS(args), "usSp#c#b:B:E:t:w:h:a:f:P:T:FiIn#1m:q" +
236 (listopts ? 0 : 3));
237 while (!error && (opt = nextopt(&ptr, &uval, NULL, &offset))) {
238 switch (opt) {
239 case 'u':
240 listopts->usedflag = 1;
241 break;
242 case 's':
243 listopts->shortflag = 1;
244 break;
245 case 'S':
246 listopts->cmp = cstrpppcmp;
247 break;
248 case 'm':
249 if (!(error = ((i = enum2int(ptr, 0, enum_match, "-m")) < 0))) {
250 if ((error = (mflag >= 0 && mflag != i)))
251 eprintf("-m option conflicts with earlier -m or -P");
252 mflag = i;
253 }
254 break;
255 case 'p':
256 spec->pri = uval.ival;
257 break;
258 case 'c':
259 spec->prob = uval.ival;
260 break;
261 case 'F':
262 spec->fallthru = 1;
263 break;
264 case 'i':
265 spec->invis = 1;
266 break;
267 case 'I':
268 spec->invis = 2;
269 break;
270 case 'b':
271 if (spec->keyname) FREE(spec->keyname);
272 if (spec->bind) FREE(spec->bind);
273 ptr = print_to_ascii(NULL, ptr)->data;
274 spec->bind = STRDUP(ptr);
275 break;
276 case 'B':
277 if (warn_def_B)
278 wprintf("/def -B is deprecated. See /help keys.");
279 if (spec->keyname) FREE(spec->keyname);
280 if (spec->bind) FREE(spec->bind);
281 spec->keyname = STRDUP(ptr);
282 break;
283 case 'E':
284 if (spec->expr) conStringfree(spec->expr);
285 (spec->expr = CS(Stringnew(ptr, -1, 0)))->links++;
286 break;
287 case 't':
288 free_pattern(&spec->trig);
289 error += !init_pattern_str(&spec->trig, ptr);
290 break;
291 case 'T':
292 free_pattern(&spec->wtype);
293 error += !init_pattern_str(&spec->wtype, ptr);
294 break;
295 case 'w':
296 if (!*ptr || strcmp(ptr, "+") == 0)
297 spec->world = &AnyWorld;
298 else if (strcmp(ptr, "-") == 0)
299 spec->world = &NoWorld;
300 else if ((error = !(spec->world = find_world(ptr))))
301 eprintf("No world %s", ptr);
302 break;
303 case 'h':
304 if (strcmp(ptr, "0") == 0) {
305 spec->flags |= MACRO_NOHOOK;
306 } else {
307 char *buf, *p;
308 p = buf = STRDUP(ptr); /* XXX optimize */
309 if (!(error = !parse_hook(&p, &spec->hook))) {
310 free_pattern(&spec->hargs);
311 error += !init_pattern_str(&spec->hargs, p);
312 spec->flags |= MACRO_HOOK;
313 }
314 FREE(buf);
315 }
316 break;
317 case 'a': case 'f':
318 error = !parse_attrs(ptr, &attrs, 0);
319 spec->attr = adj_attr(spec->attr, attrs);
320 break;
321 case 'P':
322 if ((error = (spec->nsubattr > 0))) {
323 eprintf("-P can be given only once per macro.");
324 break;
325 } else if ((error = (mflag >= 0 && mflag != MATCH_REGEXP))) {
326 eprintf("\"-P\" requires \"-mregexp -t<pattern>\"");
327 break;
328 }
329 for (n = 0, s = ptr; *s; s++) {
330 if (*s == ';') continue; /* don't count empties */
331 n++;
332 s = strchr(s, ';');
333 if (!s) break;
334 }
335 spec->subattr = MALLOC(n * sizeof(subattr_t));
336 if (!spec->subattr) {
337 eprintf("-P: out of memory");
338 error++;
339 break;
340 }
341 spec->nsubattr = n;
342
343 {
344 char *start, *end, *buf = STRDUP(ptr); /* XXX optimize */
345 for (i = 0, start = buf; !error && i < n; i++, start = end+1) {
346 attr_t attr;
347 if ((end = strchr(start, ';')))
348 *end = '\0';
349 if (end == start) { /* skip empty */
350 i--;
351 continue;
352 }
353 if (*start == 'L') {
354 spec->subattr[i].subexp = -1;
355 start++;
356 } else if (*start == 'R') {
357 spec->subattr[i].subexp = -2;
358 start++;
359 } else {
360 spec->subattr[i].subexp = strtoint(start, &start);
361 if ((error = (spec->subattr[i].subexp < 0))) {
362 eprintf("-P: number must be non-negative");
363 break;
364 }
365 }
366 error = !parse_attrs(start, &attr, 0);
367 spec->subattr[i].attr = attr;
368 }
369 FREE(buf);
370 }
371 mflag = MATCH_REGEXP;
372 break;
373 case 'n':
374 spec->shots = uval.ival;
375 break;
376 case 'q':
377 spec->quiet = TRUE;
378 break;
379 case '1':
380 spec->shots = 1;
381 break;
382 default:
383 error = TRUE;
384 }
385 }
386
387 if (error) {
388 nuke_macro(spec);
389 return NULL;
390 }
391 if (mflag < 0)
392 mflag = matching;
393 if (xmflag) *xmflag = mflag;
394 if (!init_pattern_mflag(&spec->trig, mflag, 't') ||
395 !init_pattern_mflag(&spec->hargs, mflag, 'h') ||
396 !init_pattern_mflag(&spec->wtype, mflag, 'w'))
397 {
398 nuke_macro(spec);
399 return NULL;
400 }
401
402 name = args->data + offset;
403 while (is_space(*name)) ++name;
404 if (!*name) return spec;
405 nameend = name + strcspn(name, "=");
406
407 if (*nameend == '=') {
408 body = nameend + 1;
409 while (is_space(*body)) ++body;
410 bodyend = args->data + args->len;
411 while (bodyend > body && is_space(bodyend[-1])) bodyend--;
412 if (bodyend > body)
413 (spec->body = CS(Stringodup(CS(args), body - args->data)))->links++;
414 }
415 while (nameend > name && is_space(nameend[-1])) nameend--;
416 if (nameend > name) {
417 char *buf = strncpy(XMALLOC(nameend - name + 1), name, nameend - name);
418 buf[nameend - name] = '\0';
419 spec->name = buf;
420 }
421
422 return spec;
423 }
424
425
426 /* init_aux_patterns
427 * Macro_match() needs to compare some string fields that aren't normally
428 * patterns. This function initializes patterns for those fields.
429 */
init_aux_patterns(Macro * spec,int mflag,AuxPat * aux)430 static int init_aux_patterns(Macro *spec, int mflag, AuxPat *aux)
431 {
432 init_pattern_str(&aux->name, spec->name);
433 init_pattern_str(&aux->body, spec->body ? spec->body->data : NULL);
434 init_pattern_str(&aux->expr, spec->expr ? spec->expr->data : NULL);
435 init_pattern_str(&aux->bind, spec->bind);
436 init_pattern_str(&aux->keyname, spec->keyname);
437 if (mflag < 0) mflag = matching;
438
439 return init_pattern_mflag(&aux->name, mflag, 0) &&
440 init_pattern_mflag(&aux->body, mflag, 0) &&
441 init_pattern_mflag(&aux->expr, mflag, 'E') &&
442 init_pattern_mflag(&aux->bind, mflag, 'b') &&
443 init_pattern_mflag(&aux->keyname, mflag, 'B');
444 }
445
free_aux_patterns(AuxPat * aux)446 static void free_aux_patterns(AuxPat *aux)
447 {
448 free_pattern(&aux->name);
449 free_pattern(&aux->body);
450 free_pattern(&aux->bind);
451 free_pattern(&aux->keyname);
452 free_pattern(&aux->expr);
453 }
454
455 /* return 1 if attr contains any of the attributes listed in sattr */
attr_match(attr_t sattr,attr_t attr)456 static int attr_match(attr_t sattr, attr_t attr)
457 {
458 if (sattr == F_NONE) {
459 return !attr;
460 } else {
461 if ((sattr & ~F_COLORS & attr) != 0)
462 return 1;
463 if ((sattr & F_FGCOLOR) && (sattr & F_FGCOLORS) == (attr & F_FGCOLORS))
464 return 1;
465 if ((sattr & F_BGCOLOR) && (sattr & F_BGCOLORS) == (attr & F_BGCOLORS))
466 return 1;
467 return 0;
468 }
469 }
470
471 /* macro_match
472 * Compares spec to macro. aux contains patterns for string fields that
473 * aren't normally patterns. Returns 1 for match, 0 for nonmatch.
474 */
macro_match(Macro * spec,Macro * macro,AuxPat * aux)475 static int macro_match(Macro *spec, Macro *macro, AuxPat *aux)
476 {
477 if (!spec->invis && macro->invis) return 0;
478 if (spec->invis == 2 && !macro->invis) return 0;
479 if (spec->shots >= 0 && spec->shots != macro->shots) return 0;
480 if (spec->fallthru >= 0 && spec->fallthru != macro->fallthru) return 0;
481 if (spec->prob >= 0 && spec->prob != macro->prob) return 0;
482 if (spec->pri >= 0 && spec->pri != macro->pri) return 0;
483 if (spec->attr && !attr_match(spec->attr, macro->attr)) return 0;
484 if (spec->subattr) {
485 int i, j;
486 for (i = 0; i < spec->nsubattr; i++) {
487 for (j = 0; ; j++) {
488 if (j >= macro->nsubattr) return 0;
489 if (macro->subattr[j].subexp == spec->subattr[i].subexp) break;
490 }
491 if (!attr_match(spec->subattr[i].attr, macro->subattr[j].attr))
492 return 0;
493 }
494 }
495 if (spec->world) {
496 if (spec->world == &NoWorld) {
497 if (macro->world) return 0;
498 } else if (spec->world == &AnyWorld) {
499 if (!macro->world) return 0;
500 } else if (spec->world != macro->world) return 0;
501 }
502
503 if (spec->keyname) {
504 if (!*spec->keyname) {
505 if (!*macro->keyname) return 0;
506 } else {
507 if (!patmatch(&aux->keyname, NULL, macro->keyname)) return 0;
508 }
509 }
510
511 if (spec->bind) {
512 if (!*spec->bind) {
513 if (!*macro->bind) return 0;
514 } else {
515 if (!patmatch(&aux->bind, NULL, macro->bind)) return 0;
516 }
517 }
518
519 if (spec->expr) {
520 if (!spec->expr->len) {
521 if (!macro->expr) return 0;
522 } else {
523 if (!macro->expr) return 0;
524 if (!patmatch(&aux->expr, NULL, macro->expr->data)) return 0;
525 }
526 }
527
528 if (spec->flags & MACRO_NOHOOK) {
529 /* -h0 */
530 if (macro->flags & MACRO_HOOK) return 0;
531 } else if (spec->flags & MACRO_HOOK) {
532 int i, hit = 0;
533 if (!(macro->flags & MACRO_HOOK)) return 0;
534 for (i = 0; i < sizeof(spec->hook)/sizeof(long); i++) {
535 if ((hit = (spec->hook.bits[i] & macro->hook.bits[i])))
536 break;
537 }
538 if (!hit) return 0;
539 if (spec->hargs.str && *spec->hargs.str) {
540 if (!patmatch(&spec->hargs, NULL, NONNULL(macro->hargs.str)))
541 return 0;
542 }
543 }
544
545 if (spec->trig.str) {
546 if (!*spec->trig.str) {
547 if (!macro->trig.str) return 0;
548 } else {
549 if (!patmatch(&spec->trig, NULL, NONNULL(macro->trig.str)))
550 return 0;
551 }
552 }
553 if (spec->wtype.str) {
554 if (!*spec->wtype.str) {
555 if (!macro->wtype.str) return 0;
556 } else {
557 if (!patmatch(&spec->wtype, NULL, NONNULL(macro->wtype.str)))
558 return 0;
559 }
560 }
561 if (spec->num && macro->num != spec->num)
562 return 0;
563 if (spec->name && !patmatch(&aux->name, NULL, macro->name))
564 return 0;
565 if (spec->body && !patmatch(&aux->body, macro->body, NULL))
566 return 0;
567 return 1;
568 }
569
570 /* macro_equal
571 * Returns true if m1 and m2 are exactly equivalent (except for num).
572 */
macro_equal(Macro * m1,Macro * m2)573 int macro_equal(Macro *m1, Macro *m2)
574 {
575 if (m1->invis != m2->invis) return 0;
576 if (m1->shots != m2->shots) return 0;
577 if (m1->fallthru != m2->fallthru) return 0;
578 if (m1->quiet != m2->quiet) return 0;
579 if (m1->prob != m2->prob) return 0;
580 if (m1->pri != m2->pri) return 0;
581 if (m1->attr != m2->attr) return 0;
582 if (m1->nsubattr != m2->nsubattr) return 0;
583 if (m1->subattr) {
584 int i, j;
585 for (i = 0; i < m1->nsubattr; i++) {
586 for (j = 0; ; j++) {
587 if (j >= m2->nsubattr) return 0;
588 if (m2->subattr[j].subexp == m1->subattr[i].subexp)
589 break;
590 }
591 if (m2->subattr[j].attr != m1->subattr[i].attr)
592 return 0;
593 }
594 }
595 if (m1->world != m2->world) return 0;
596
597 if ((m1->flags ^ m2->flags) &~ MACRO_TEMP) return 0;
598 if (m1->flags & MACRO_HOOK)
599 if (memcmp(&m1->hook, &m2->hook, sizeof(m1->hook)) != 0) return 0;
600
601 if (nullstrcmp(m1->keyname, m2->keyname) != 0) return 0;
602 if (nullstrcmp(m1->bind, m2->bind) != 0) return 0;
603 if (nullstrcmp(m1->trig.str, m2->trig.str) != 0) return 0;
604 if (nullstrcmp(m1->hargs.str, m2->hargs.str) != 0) return 0;
605 if (nullstrcmp(m1->wtype.str, m2->wtype.str) != 0) return 0;
606 if (nullstrcmp(m1->name, m2->name) != 0) return 0;
607 if (Stringcmp(m1->body, m2->body) != 0) return 0;
608 if (Stringcmp(m1->expr, m2->expr) != 0) return 0;
609 return 1;
610 }
611
612 /* find Macro by name */
find_hashed_macro(const char * name,unsigned int hash)613 Macro *find_hashed_macro(const char *name, unsigned int hash)
614 {
615 if (!*name) return NULL;
616 if (*name == '#') return find_num_macro(hash);
617 return (Macro *)hashed_find(name, hash, macro_table);
618 }
619
620 /* find single exact match */
match_exact(int hooknum,const char * str,attr_t attrs)621 static Macro *match_exact(int hooknum, const char *str, attr_t attrs)
622 {
623 ListEntry *node;
624
625 if (hooknum < 0 && !*str) return NULL;
626 for (node = hooknum>=0 ? hooklist[hooknum].head : triglist->head; node;
627 node = node->next)
628 {
629 Macro *macro = MAC(node);
630 if (macro->flags & MACRO_DEAD) continue;
631 if (hooknum>=0) {
632 if (!VEC_ISSET(hooknum, ¯o->hook)) continue;
633 if (!macro->hargs.str || cstrcmp(macro->hargs.str, str) == 0)
634 return macro;
635 } else {
636 if (!(macro->attr & attrs)) continue;
637 if (!macro->trig.str || cstrcmp(macro->trig.str, str) == 0)
638 return macro;
639 }
640 }
641 eprintf("%s on \"%s\" was not defined.", hooknum>=0 ? "Hook" : "Trigger",
642 str);
643 return NULL;
644 }
645
646 /**************************
647 * Routines to add macros *
648 **************************/
649
650 /* create a Macro */
add_new_macro(const char * trig,const char * bind,const hookvec_t * hook,const char * hargs,const char * body,int pri,int prob,attr_t attr,int invis,int mflag)651 int add_new_macro(const char *trig, const char *bind, const hookvec_t *hook,
652 const char *hargs, const char *body, int pri, int prob, attr_t attr,
653 int invis, int mflag)
654 {
655 Macro *new;
656 int error = 0;
657
658 if (!(new = (Macro *) MALLOC(sizeof(struct Macro)))) {
659 eprintf("add_new_macro: not enough memory");
660 return 0;
661 }
662 new->numnode = new->trignode = new->hashnode = NULL;
663 new->flags = MACRO_TEMP;
664 new->prog = new->exprprog = NULL;
665 new->name = STRDUP("");
666 (new->body = CS(Stringnew(body, -1, 0)))->links++;
667 new->expr = NULL;
668 new->bind = STRDUP(bind);
669 new->keyname = STRDUP("");
670 if (hook) {
671 new->flags |= MACRO_HOOK;
672 new->hook = *hook;
673 } else {
674 VEC_ZERO(&new->hook);
675 }
676 error += !init_pattern(&new->trig, trig, mflag);
677 error += !init_pattern(&new->hargs, hargs, mflag);
678 init_pattern_str(&new->wtype, NULL);
679 new->world = NULL;
680 new->pri = pri;
681 new->prob = prob;
682 new->attr = attr;
683 new->nsubattr = 0;
684 new->subattr = NULL;
685 new->shots = 0;
686 new->invis = invis;
687 new->fallthru = FALSE;
688 new->quiet = FALSE;
689 new->builtin = NULL;
690 new->used[USED_NAME] = new->used[USED_TRIG] =
691 new->used[USED_HOOK] = new->used[USED_KEY] = 0;
692
693 if (!error)
694 return add_numbered_macro(new, 0, 0, NULL);
695 nuke_macro(new);
696 return 0;
697 }
698
bind_key_macro(Macro * spec)699 static int bind_key_macro(Macro *spec)
700 {
701 Macro *orig;
702
703 if ((orig = find_key(spec->bind))) {
704 if (macro_equal(orig, spec)) {
705 return -1; /* leave orig in place, don't insert spec */
706 } else if (redef) {
707 const char *obody = orig->body->data;
708 const char *sbody = spec->body->data;
709 if (strncmp(sbody, "/key_", 5) != 0 && orig->invis &&
710 strncmp(obody, "/key_", 5) == 0 && !strchr(obody, ' '))
711 {
712 conString *buf = print_def(NULL, NULL, orig);
713 eprintf("%ARecommendation: leave \"%S\" defined and "
714 "define a macro named \"%s\" instead.",
715 warning_attr, buf, obody+1);
716 conStringfree(buf);
717 }
718 kill_macro(orig); /* this guarantees intrie will succeed */
719 do_hook(H_REDEF, "!Redefined %s %S", "%s %S",
720 "binding", ascii_to_print(spec->bind));
721 } else {
722 eprintf("Binding %S already exists.", ascii_to_print(spec->bind));
723 return 0;
724 }
725 }
726
727 return bind_key(spec, spec->bind);
728 }
729
730 /* add_macro
731 * Install a permanent Macro in appropriate structures.
732 * Only the keybinding is checked for conflicts; everything else is assumed
733 * assumed to be error- and conflict-free. If the bind_key_macro fails, the
734 * macro will be nuked.
735 */
add_numbered_macro(Macro * macro,unsigned int hash,int num,ListEntry * numnode)736 static int add_numbered_macro(Macro *macro, unsigned int hash, int num,
737 ListEntry *numnode)
738 {
739 if (!macro) return 0;
740
741 if (*macro->bind) {
742 int result = bind_key_macro(macro);
743 if (result <= 0) {
744 nuke_macro(macro);
745 return -result;
746 }
747 }
748 macro->num = num ? num : ++mnum;
749 macro->numnode = inlist((void *)macro, maclist, numnode);
750
751 if (*macro->name) {
752 macro->hashnode = hashed_insert((void *)macro, hash, macro_table);
753 if (macro->builtin) { /* macro->builtin was set in complete_macro() */
754 macro->builtin->macro = macro;
755 }
756 }
757 if (macro->trig.str) {
758 macro->trignode = sinsert((void *)macro,
759 macro->world ? macro->world->triglist : triglist, (Cmp *)rpricmp);
760 }
761 if (macro->flags & MACRO_HOOK) {
762 int i;
763 for (i = 0; i < (int)NUM_HOOKS; i++) {
764 if (VEC_ISSET(i, ¯o->hook))
765 sinsert((void *)macro, &hooklist[i], (Cmp *)rpricmp);
766 }
767 }
768 macro->flags &= ~MACRO_TEMP;
769 if (!*macro->name && (macro->trig.str || macro->flags & MACRO_HOOK) &&
770 macro->shots == 0 && pedantic)
771 {
772 wprintf("new macro (#%d) does not have a name.", macro->num);
773 }
774 return macro->num;
775 }
776
777 /* rebind_key_macros
778 * Unbinds macros with keynames, and attempts to rebind them.
779 */
rebind_key_macros(void)780 void rebind_key_macros(void)
781 {
782 Macro *p;
783 ListEntry *node;
784 const char *code;
785
786 for (node = maclist->tail; node; node = node->prev) {
787 p = MAC(node);
788 if (!*p->keyname) continue;
789 code = get_keycode(p->keyname);
790 if (strcmp(code, p->bind) == 0) {
791 /* same code, don't need to rebind */
792 } else {
793 if (*p->bind) unbind_key(p->bind);
794 FREE(p->bind);
795 p->bind = STRDUP(code);
796 if (!*code) {
797 wprintf("no code for key \"%s\"", p->keyname);
798 } else if (bind_key_macro(p)) {
799 /* bind_key_macro can't return -1 here */
800 do_hook(H_REDEF, "!Redefined %s %s", "%s %s",
801 "key", p->keyname);
802 } else {
803 kill_macro(p);
804 }
805 }
806 }
807 }
808
809 /* compares m1 and m2 based on reverse priority and fallthru */
rpricmp(const Macro * m1,const Macro * m2)810 static int rpricmp(const Macro *m1, const Macro *m2)
811 {
812 if (m2->pri != m1->pri) return m2->pri - m1->pri;
813 else return m2->fallthru - m1->fallthru;
814 }
815
handle_def_command(String * args,int offset)816 struct Value *handle_def_command(String *args, int offset)
817 {
818 Macro *spec;
819
820 if (!(args->len - offset) || !(spec = macro_spec(args, offset, NULL, NULL)))
821 return shareval(val_zero);
822 return newint(complete_macro(spec, macro_hash(spec->name), 0, NULL));
823 }
824
825 /* Fill in "don't care" fields with default values, and add_numbered_macro().
826 * If error checking fails, spec will be nuked.
827 */
complete_macro(Macro * spec,unsigned int hash,int num,ListEntry * numnode)828 static int complete_macro(Macro *spec, unsigned int hash, int num,
829 ListEntry *numnode)
830 {
831 Macro *orig = NULL;
832 int i;
833
834 if (spec->name && *spec->name) {
835 if (strchr("#@!/", *spec->name) || strchr(spec->name, ' ')) {
836 eprintf("illegal macro name \"%s\".", spec->name);
837 nuke_macro(spec);
838 return 0;
839 }
840 if (keyword(spec->name) ||
841 ((spec->builtin = find_builtin_cmd(spec->name)) &&
842 (spec->builtin->reserved)))
843 {
844 eprintf("\"%s\" is a reserved word.", spec->name);
845 nuke_macro(spec);
846 return 0;
847 }
848
849 if (spec->builtin) {
850 do_hook(H_CONFLICT,
851 "!warning: macro \"%s\" conflicts with the builtin command.",
852 "%s", spec->name);
853 }
854 }
855
856 if (spec->expr) {
857 /* Compiling expr at /def time allows us to report errors at /def time
858 * (and means we don't have to test at runtime) */
859 extern char current_opt;
860 current_opt = 'E';
861 spec->exprprog = compile_tf(spec->expr, 0, -1, 1, 2);
862 current_opt = '\0';
863 if (!spec->exprprog) {
864 nuke_macro(spec);
865 return 0;
866 }
867 }
868
869 if (spec->body && defcompile) {
870 spec->prog = compile_tf(spec->body, 0, SUB_MACRO, 0,
871 !spec->shots ? 2 : spec->shots > 10 ? 1 : 0);
872 if (!spec->prog) {
873 nuke_macro(spec);
874 return 0;
875 }
876 }
877
878 if (spec->world == &AnyWorld) spec->world = xworld();
879 if (spec->pri < 0) spec->pri = 1;
880 if (spec->prob < 0) spec->prob = 100;
881 if (spec->shots < 0) spec->shots = 0;
882 if (spec->invis) spec->invis = 1;
883 if (spec->fallthru < 0) spec->fallthru = 0;
884 if (spec->quiet < 0) spec->quiet = 0;
885 if (!spec->name) spec->name = STRNDUP("", 0);
886 if (!spec->body) (spec->body = blankline)->links++;
887 /*if (!spec->expr) (spec->expr = blankline)->links++;*/
888
889 if (spec->nsubattr > 0 && spec->trig.mflag != MATCH_REGEXP) {
890 eprintf("\"-P\" requires \"-mregexp -t<pattern>\"");
891 nuke_macro(spec);
892 return 0;
893 }
894 spec->attr &= ~F_NONE;
895 if (spec->nsubattr) {
896 int n;
897 pcre_fullinfo(spec->trig.ri->re, NULL, PCRE_INFO_CAPTURECOUNT, &n);
898 for (i = 0; i < spec->nsubattr; i++) {
899 spec->subattr[i].attr &= ~F_NONE;
900 if (spec->subattr[i].subexp > n) {
901 eprintf("-P%d: trigger has only %d subexpressions",
902 spec->subattr[i].subexp, n);
903 nuke_macro(spec);
904 return 0;
905 }
906 }
907 }
908
909 if (!spec->keyname) spec->keyname = STRNDUP("", 0);
910 else if (*spec->keyname) {
911 if (spec->bind) FREE(spec->bind);
912 spec->bind = get_keycode(spec->keyname);
913 if (!spec->bind) {
914 eprintf("unknown key name \"%s\".", spec->keyname);
915 nuke_macro(spec);
916 return 0;
917 }
918 spec->bind = STRDUP(spec->bind);
919 if (!*spec->bind)
920 wprintf("no code for key \"%s\".", spec->keyname);
921 }
922
923 if (!spec->bind) spec->bind = STRNDUP("", 0);
924
925 if (*spec->name &&
926 (orig = (Macro *)hashed_find(spec->name, hash, macro_table)))
927 {
928 if (macro_equal(orig, spec)) {
929 /* identical redefinition has no effect */
930 nuke_macro(spec);
931 return orig->num;
932 } else if (!redef) {
933 eprintf("macro %s already exists", spec->name);
934 nuke_macro(spec);
935 return 0;
936 }
937 }
938 if (!add_numbered_macro(spec, hash, num, numnode)) return 0;
939 if (orig) {
940 do_hook(H_REDEF, "!Redefined %s %s", "%s %s", "macro", orig->name);
941 kill_macro(orig);
942 }
943 return spec->num;
944 }
945
946 /* define a new Macro with hook */
add_hook(char * args,const char * body)947 int add_hook(char *args, const char *body)
948 {
949 hookvec_t hook;
950
951 VEC_ZERO(&hook);
952 if (!parse_hook(&args, &hook)) return 0;
953 if (args && !*args) args = NULL;
954 return add_new_macro(NULL, "", &hook, args, body, 0, 100, 0, 0, matching);
955 }
956
957 /* /edit: Edit an existing macro.
958 * Actually editing the macro in place is quite hairy, so instead we
959 * remove the old one, create a replacement and add it. If the replacement
960 * fails, we re-add the original. Either way, the number and position in
961 * maclist are unchanged.
962 */
handle_edit_command(String * args,int offset)963 struct Value *handle_edit_command(String *args, int offset)
964 {
965 Macro *spec, *macro = NULL;
966 int error = 0;
967 int num;
968 unsigned int hash = 0;
969 ListEntry *numnode;
970
971 if (!(args->len - offset) || !(spec = macro_spec(args, offset, NULL, NULL))) {
972 return shareval(val_zero);
973 } else if (!spec->name) {
974 eprintf("You must specify a macro.");
975 } else if (spec->name[0] == '$') {
976 macro = match_exact(0, spec->name + 1, F_ATTR);
977 if (macro) hash = macro_hash(macro->name);
978 } else {
979 hash = macro_hash(spec->name); /* used for lookup and insertion */
980 if (!(macro = find_hashed_macro(spec->name, hash)))
981 eprintf("macro %s does not exist", spec->name);
982 }
983
984 if (!macro) {
985 nuke_macro(spec);
986 return shareval(val_zero);
987 }
988
989 num = macro->num;
990 numnode = macro->numnode->prev;
991 kill_macro(macro);
992
993 FREE(spec->name);
994 spec->name = STRDUP(macro->name);
995
996 if (!spec->body && macro->body) (spec->body = macro->body)->links++;
997 if (!spec->expr && macro->expr) (spec->expr = macro->expr)->links++;
998 if (!spec->bind && macro->bind) spec->bind = STRDUP(macro->bind);
999 if (!spec->keyname && macro->keyname) spec->keyname =STRDUP(macro->keyname);
1000 if (!spec->wtype.str && macro->wtype.str)
1001 error += !copy_pattern(&spec->wtype, ¯o->wtype);
1002 if (!spec->trig.str && macro->trig.str)
1003 error += !copy_pattern(&spec->trig, ¯o->trig);
1004 if (!(spec->flags & (MACRO_HOOK | MACRO_NOHOOK))) {
1005 spec->flags |= MACRO_HOOK;
1006 spec->hook = macro->hook;
1007 if (macro->hargs.str)
1008 error += !copy_pattern(&spec->hargs, ¯o->hargs);
1009 }
1010 if (!spec->world) spec->world = macro->world;
1011 else if (spec->world == &AnyWorld) spec->world = xworld();
1012 if (spec->pri < 0) spec->pri = macro->pri;
1013 if (spec->prob < 0) spec->prob = macro->prob;
1014 if (spec->shots < 0) spec->shots = macro->shots;
1015 if (spec->fallthru < 0) spec->fallthru = macro->fallthru;
1016 if (spec->quiet < 0) spec->quiet = macro->quiet;
1017 if (spec->attr == 0) spec->attr = macro->attr;
1018 if (spec->nsubattr == 0 && macro->nsubattr > 0) {
1019 spec->nsubattr = macro->nsubattr;
1020 spec->subattr = MALLOC(spec->nsubattr * sizeof(subattr_t));
1021 memcpy(spec->subattr, macro->subattr,
1022 spec->nsubattr * sizeof(subattr_t));
1023 }
1024
1025 spec->used[USED_NAME] = macro->used[USED_NAME];
1026 spec->used[USED_TRIG] = macro->used[USED_TRIG];
1027 spec->used[USED_HOOK] = macro->used[USED_HOOK];
1028 spec->used[USED_KEY] = macro->used[USED_KEY];
1029
1030 if (!error) {
1031 complete_macro(spec, hash, num, numnode);
1032 return newint(spec->num);
1033 }
1034
1035 /* Edit failed. Resurrect original macro. */
1036 macro = dead_macros;
1037 macro->flags &= ~MACRO_DEAD;
1038 dead_macros = macro->tnext;
1039 add_numbered_macro(macro, hash, num, numnode);
1040 return shareval(val_zero);
1041 }
1042
1043
1044 /********************************
1045 * Routines for removing macros *
1046 ********************************/
1047
kill_macro(Macro * macro)1048 void kill_macro(Macro *macro)
1049 {
1050 /* Remove macro from maclist, macro_table, and key_trie, and put it on
1051 * the dead_macros list. When called from find_and_run_matches(), this
1052 * allows a new macro to be defined without conflicting with the name
1053 * or binding of this macro.
1054 * The macro must NOT be removed from triglist and hooklist, so
1055 * find_and_run_matches() can work correctly when a macro kills itself,
1056 * is a one-shot, or defines another macro that is inserted immediately
1057 * after it in triglist/hooklist. macro will be removed from triglist
1058 * and hooklist in nuke_macro().
1059 */
1060
1061 if (macro->flags & MACRO_DEAD) return;
1062 macro->flags |= MACRO_DEAD;
1063 macro->tnext = dead_macros;
1064 dead_macros = macro;
1065 unlist(macro->numnode, maclist);
1066 if (*macro->name) hash_remove(macro->hashnode, macro_table);
1067 if (*macro->bind) unbind_key(macro->bind);
1068 }
1069
nuke_dead_macros(void)1070 void nuke_dead_macros(void)
1071 {
1072 Macro *macro;
1073
1074 while ((macro = dead_macros)) {
1075 dead_macros = dead_macros->tnext;
1076 nuke_macro(macro);
1077 }
1078 }
1079
1080 /* free macro structure */
nuke_macro(Macro * m)1081 static void nuke_macro(Macro *m)
1082 {
1083 if (!(m->flags & MACRO_DEAD) && !(m->flags & MACRO_TEMP)) {
1084 kill_macro(m);
1085 }
1086 if (m->trignode)
1087 unlist(m->trignode, m->world ? m->world->triglist : triglist);
1088 if (m->flags & MACRO_HOOK) {
1089 int i;
1090 ListEntry *node;
1091 for (i = 0; i < (int)NUM_HOOKS; i++) {
1092 if (!VEC_ISSET(i, &m->hook)) continue;
1093 for (node = hooklist[i].head; node; node = node->next) {
1094 if (MAC(node) == m) {
1095 unlist(node, &hooklist[i]);
1096 break; /* macro can only be in hooklist[i] once */
1097 }
1098 }
1099 }
1100 }
1101
1102 if (m->body) conStringfree(m->body);
1103 if (m->expr) conStringfree(m->expr);
1104 if (m->bind) FREE(m->bind);
1105 if (m->keyname) FREE(m->keyname);
1106 if (m->subattr) FREE(m->subattr);
1107 if (m->prog) prog_free(m->prog);
1108 if (m->exprprog) prog_free(m->exprprog);
1109 if (m->builtin && m->builtin->macro == m)
1110 m->builtin->macro = NULL;
1111 free_pattern(&m->trig);
1112 free_pattern(&m->hargs);
1113 free_pattern(&m->wtype);
1114 if (m->name) FREE(m->name);
1115 FREE(m);
1116 }
1117
1118 /* delete a macro */
remove_macro_by_name(const char * str)1119 int remove_macro_by_name(const char *str)
1120 {
1121 Macro *macro;
1122
1123 if (!(macro = find_macro(str))) {
1124 eprintf("Macro \"%s\" was not defined.", str);
1125 return 0;
1126 }
1127 kill_macro(macro);
1128 return 1;
1129 }
1130
1131 /* delete specified macros */
handle_purge_command(String * args,int offset)1132 struct Value *handle_purge_command(String *args, int offset)
1133 {
1134 Macro *spec;
1135 ListEntry *node, *next;
1136 int result = 0;
1137 int mflag;
1138 AuxPat aux;
1139
1140 if (!(spec = macro_spec(args, offset, &mflag, NULL)))
1141 return shareval(val_zero);
1142 if (spec->name && *spec->name == '#') {
1143 spec->num = atoi(spec->name + 1);
1144 FREE(spec->name);
1145 spec->name = NULL;
1146 }
1147 if (!(init_aux_patterns(spec, mflag, &aux)))
1148 goto error;
1149 for (node = maclist->head; node; node = next) {
1150 next = node->next;
1151 if (macro_match(spec, MAC(node), &aux)) {
1152 kill_macro(MAC(node));
1153 result++;
1154 }
1155 }
1156 /* regrelease(); */
1157 error:
1158 free_aux_patterns(&aux);
1159 nuke_macro(spec);
1160 return newint(result);
1161 }
1162
1163 /* delete macro by number */
handle_undefn_command(String * args,int offset)1164 struct Value *handle_undefn_command(String *args, int offset)
1165 {
1166 int num, result = 0;
1167 Macro *macro;
1168 const char *ptr = args->data + offset;
1169
1170 while (*ptr) {
1171 if ((num = numarg(&ptr)) >= 0 && (macro = find_num_macro(num))) {
1172 kill_macro(macro);
1173 result++;
1174 }
1175 }
1176 return newint(result);
1177 }
1178
find_num_macro(int num)1179 Macro *find_num_macro(int num)
1180 {
1181 if (maclist->tail && num >= MAC(maclist->tail)->num) {
1182 /* search from high end, assuming high macros are more likely;
1183 * stop when past */
1184 ListEntry *node = maclist->head;
1185 for ( ; node && MAC(node)->num >= num; node = node->next)
1186 if (MAC(node)->num == num) return MAC(node);
1187 }
1188 eprintf("no macro with number %d", num);
1189 return NULL;
1190 }
1191
remove_world_macros(World * w)1192 void remove_world_macros(World *w)
1193 {
1194 ListEntry *node, *next;
1195
1196 /* This could be more efficient by using w->triglist and
1197 * the hooklists, but this is not used often. */
1198 for (node = maclist->head; node; node = next) {
1199 next = node->next;
1200 if (MAC(node)->world == w)
1201 kill_macro(MAC(node));
1202 }
1203 }
1204
1205
1206 /**************************
1207 * Routine to list macros *
1208 **************************/
1209
1210 /* convert hook vector to string */
hook_name(const hookvec_t * hook)1211 static const String *hook_name(const hookvec_t *hook)
1212 {
1213 int i;
1214 STATIC_BUFFER(buf);
1215
1216 Stringtrunc(buf, 0);
1217 for (i = 0; i < (int)NUM_HOOKS; i++) {
1218 /* ^^^^^ Some brain dead compilers need that cast */
1219 if (!VEC_ISSET(i, hook)) continue;
1220 if (buf->len) Stringadd(buf, '|');
1221 Stringcat(buf, hook_table[i].name);
1222 }
1223 return buf;
1224 }
1225
print_def(TFILE * file,String * buffer,Macro * p)1226 static conString *print_def(TFILE *file, String *buffer, Macro *p)
1227 {
1228 int mflag = -1;
1229
1230 if (!buffer)
1231 buffer = Stringnew(NULL, 0, 0);
1232 buffer->links++;
1233
1234 if (file && file == tfout)
1235 Sprintf(buffer, "%% %d: /def ", p->num);
1236 else Stringcpy(buffer, "/def ");
1237 if (p->invis) Stringcat(buffer, "-i ");
1238 if (p->trig.str || (p->flags & MACRO_HOOK))
1239 Sappendf(buffer, "-%sp%d ", p->fallthru ? "F" : "", p->pri);
1240 if (p->prob != 100)
1241 Sappendf(buffer, "-c%d ", p->prob);
1242 if (p->attr) {
1243 Stringcat(attr2str(Stringcat(buffer, "-a"), p->attr), " ");
1244 }
1245 if (p->nsubattr > 0) {
1246 int i;
1247 mflag = MATCH_REGEXP;
1248 Stringcat(buffer, "-P");
1249 for (i = 0; i < p->nsubattr; i++) {
1250 if (i > 0) Stringadd(buffer, ';');
1251 if (p->subattr[i].subexp == -1)
1252 Stringadd(buffer, 'L');
1253 else if (p->subattr[i].subexp == -2)
1254 Stringadd(buffer, 'R');
1255 else
1256 Sappendf(buffer, "%d", (int)p->subattr[i].subexp);
1257 attr2str(buffer, p->subattr[i].attr);
1258 }
1259 Stringadd(buffer, ' ');
1260 }
1261 if (p->shots)
1262 Sappendf(buffer, "-n%d ", p->shots);
1263 if (p->world)
1264 Sappendf(buffer, "-w'%q' ", '\'', p->world->name);
1265 if (p->wtype.str) {
1266 if (p->wtype.mflag != mflag)
1267 Sappendf(buffer, "-m%S ", &enum_match[p->wtype.mflag]);
1268 mflag = p->wtype.mflag;
1269 Sappendf(buffer, "-T'%q' ", '\'', p->wtype.str);
1270 }
1271
1272 if (p->expr)
1273 Sappendf(buffer, "-E'%q' ", '\'', p->expr->data);
1274
1275 if (p->trig.str) {
1276 if (p->trig.mflag != mflag)
1277 Sappendf(buffer, "-m%S ", &enum_match[p->trig.mflag]);
1278 mflag = p->trig.mflag;
1279 Sappendf(buffer, "-t'%q' ", '\'', p->trig.str);
1280 }
1281 if (p->flags & MACRO_HOOK) {
1282 if (p->hargs.str && *p->hargs.str) {
1283 if (p->hargs.mflag != mflag)
1284 Sappendf(buffer, "-m%S ", &enum_match[mflag = p->hargs.mflag]);
1285 Sappendf(buffer, "-h'%S %q' ",
1286 hook_name(&p->hook), '\'', p->hargs.str);
1287 } else {
1288 Sappendf(buffer, "-h%S ", hook_name(&p->hook));
1289 }
1290 }
1291
1292 #if 0 /* obsolete */
1293 if (*p->keyname)
1294 Sappendf(buffer, "-B'%s' ", p->keyname);
1295 else
1296 #endif
1297 if (*p->bind)
1298 Sappendf(buffer, "-b'%q' ", '\'', ascii_to_print(p->bind)->data);
1299
1300 if (p->quiet) Stringcat(buffer, "-q ");
1301 if (*p->name == '-') Stringcat(buffer, "- ");
1302 if (*p->name) Sappendf(buffer, "%s ", p->name);
1303 if (p->body && p->body->len) Sappendf(buffer, "= %S", p->body);
1304
1305 if (file) {
1306 tfputline(CS(buffer), file);
1307 Stringfree(buffer);
1308 return NULL;
1309 } else {
1310 return CS(buffer);
1311 }
1312 }
1313
1314 /* list all specified macros */
list_defs(TFILE * file,Macro * spec,int mflag,ListOpts * listopts)1315 static int list_defs(TFILE *file, Macro *spec, int mflag, ListOpts *listopts)
1316 {
1317 Macro *p;
1318 ListEntry *node;
1319 AuxPat aux;
1320 String *buffer = NULL;
1321 int result = 0, i;
1322 Vector macs = vector_init(1024);
1323
1324 if (!(init_aux_patterns(spec, mflag, &aux))) goto error;
1325 if (spec->name && *spec->name == '#') {
1326 spec->num = atoi(spec->name + 1);
1327 FREE(spec->name);
1328 spec->name = NULL;
1329 }
1330
1331 /* maclist is in reverse numeric order, so we start from tail */
1332 for (node = maclist->tail; node; node = node->prev) {
1333 p = MAC(node);
1334 if (!macro_match(spec, p, &aux)) continue;
1335 vector_add(&macs, p);
1336 }
1337
1338 if (listopts && listopts->cmp)
1339 vector_sort(&macs, listopts->cmp);
1340
1341 for (i = 0; i < macs.size; i++) {
1342 p = macs.ptrs[i];
1343 result = p->num;
1344
1345 if (!buffer)
1346 (buffer = Stringnew(NULL, 0, 0))->links++;
1347
1348 if (listopts && listopts->shortflag) {
1349 Sprintf(buffer, "%% %d: ", p->num);
1350 if (p->attr & F_NOHISTORY) Stringcat(buffer, "(nohistory) ");
1351 if (p->attr & F_NOLOG) Stringcat(buffer, "(nolog) ");
1352 if (p->attr & F_GAG) Stringcat(buffer, "(gag) ");
1353 else if (p->attr & (F_HWRITE | F_EXCLUSIVE)) {
1354 if (p->attr & F_UNDERLINE) Stringcat(buffer, "(underline) ");
1355 if (p->attr & F_REVERSE) Stringcat(buffer, "(reverse) ");
1356 if (p->attr & F_FLASH) Stringcat(buffer, "(flash) ");
1357 if (p->attr & F_DIM) Stringcat(buffer, "(dim) ");
1358 if (p->attr & F_BOLD) Stringcat(buffer, "(bold) ");
1359 if (p->attr & F_BELL) Stringcat(buffer, "(bell) ");
1360 if (p->attr & F_HILITE) Stringcat(buffer, "(hilite) ");
1361 if (p->attr & F_FGCOLOR)
1362 Sappendf(buffer, "(%S) ",
1363 &enum_color[attr2fgcolor(p->attr)]);
1364 if (p->attr & F_BGCOLOR)
1365 Sappendf(buffer, "(bg%S) ",
1366 &enum_color[attr2bgcolor(p->attr)]);
1367 } else if (p->nsubattr > 0) {
1368 Stringcat(buffer, "(partial) ");
1369 } else if (p->trig.str) {
1370 Stringcat(buffer, "(trig");
1371 if (listopts && listopts->usedflag)
1372 Sappendf(buffer, " %d", p->used[USED_TRIG]);
1373 Stringcat(buffer, ") ");
1374 }
1375 if (p->trig.str)
1376 Sappendf(buffer, "'%q' ", '\'', p->trig.str);
1377 #if 0 /* obsolete */
1378 if (*p->keyname) {
1379 Stringcat(buffer, "(key");
1380 if (listopts && listopts->usedflag)
1381 Sappendf(buffer, " %d", p->used[USED_KEY]);
1382 Sappendf(buffer, ") '%s' ", p->keyname);
1383 } else
1384 #endif
1385 if (*p->bind) {
1386 Stringcat(buffer, "(bind");
1387 if (listopts && listopts->usedflag)
1388 Sappendf(buffer, " %d", p->used[USED_KEY]);
1389 Sappendf(buffer, ") '%q' ", '\'',
1390 ascii_to_print(p->bind)->data);
1391 if (p->keyname && *p->keyname)
1392 Sappendf(buffer, "(%s) ", p->keyname);
1393 }
1394 if (p->flags & MACRO_HOOK) {
1395 Stringcat(buffer, "(hook");
1396 if (listopts && listopts->usedflag)
1397 Sappendf(buffer, " %d", p->used[USED_HOOK]);
1398 Sappendf(buffer, ") %S ", hook_name(&p->hook));
1399 }
1400 if (*p->name) {
1401 Sappendf(buffer, "%s ", p->name);
1402 if (listopts && listopts->usedflag)
1403 Sappendf(buffer, "(%d) ", p->used[USED_NAME]);
1404 }
1405 tfputline(CS(buffer), file ? file : tfout);
1406
1407 } else {
1408 print_def(file ? file : tfout, buffer, p);
1409 }
1410
1411 /* If something is sharing buffer, we can't reuse it in next loop. */
1412 if (buffer->links > 1) {
1413 Stringfree(buffer);
1414 buffer = NULL;
1415 }
1416 }
1417 /* regrelease(); */
1418 vector_free(&macs);
1419 error:
1420 free_aux_patterns(&aux);
1421 if (buffer) {
1422 Stringfree(buffer);
1423 buffer = NULL;
1424 }
1425 return result;
1426 }
1427
1428 /* write specified macros to file */
save_macros(String * args,int offset)1429 int save_macros(String *args, int offset)
1430 {
1431 Macro *spec;
1432 TFILE *file = NULL;
1433 int result = 1;
1434 const char *name, *mode = "w";
1435 char opt, *next;
1436 int mflag;
1437
1438 startopt(CS(args), "a");
1439 while ((opt = nextopt(NULL, NULL, NULL, &offset))) {
1440 if (opt != 'a') return 0;
1441 mode = "a";
1442 }
1443
1444 next = args->data + offset;
1445 name = stringarg(&next, NULL);
1446 offset = next - args->data;
1447 if (!(spec = macro_spec(args, offset, &mflag, NULL))) result = 0;
1448 if (result && !(file = tfopen(expand_filename(name), mode))) {
1449 operror(name);
1450 result = 0;
1451 }
1452
1453 if (result) {
1454 oprintf("%% %sing macros to %s", *mode=='w' ? "Writ" : "Append",
1455 file->name);
1456 result = list_defs(file, spec, mflag, NULL);
1457 }
1458 if (file) tfclose(file);
1459 if (spec) nuke_macro(spec);
1460 return result;
1461 }
1462
1463 /* list macros on screen */
handle_list_command(String * args,int offset)1464 struct Value *handle_list_command(String *args, int offset)
1465 {
1466 Macro *spec;
1467 int result = 1;
1468 int mflag;
1469 ListOpts opts = { 0, 0, NULL };
1470
1471 if (!(spec = macro_spec(args, offset, &mflag, &opts))) result = 0;
1472 if (result) result = list_defs(NULL, spec, mflag, &opts);
1473 if (spec) nuke_macro(spec);
1474 return newint(result);
1475 }
1476
1477
1478 /**************************
1479 * Routines to use macros *
1480 **************************/
1481
1482 /* Do a macro! */
do_macro(Macro * macro,String * args,int offset,int used_type,int kbnumlocal)1483 int do_macro(Macro *macro, String *args, int offset, int used_type,
1484 int kbnumlocal)
1485 {
1486 int result, old_invis_flag, oldblock;
1487 const char *command;
1488 char numbuf[16];
1489
1490 if (*macro->name) {
1491 command = macro->name;
1492 } else {
1493 sprintf(numbuf, "#%d", macro->num);
1494 command = numbuf;
1495 }
1496 if (used_type >= 0) {
1497 macro->used[used_type]++;
1498 }
1499 old_invis_flag = invis_flag;
1500 invis_flag = macro->invis;
1501 oldblock = block; /* XXX ? */
1502 block = 0; /* XXX ? */
1503 if (!macro->prog) {
1504 const char *old_command = current_command;
1505 current_command = command; /* for macro compiler errors */
1506 macro->prog = compile_tf(macro->body, 0, SUB_MACRO, 0,
1507 !macro->shots ? 2 : macro->shots > 10 ? 1 : 0);
1508 current_command = old_command;
1509 }
1510 if (!macro->prog)
1511 result = 0;
1512 else
1513 result = prog_run(macro->prog, args, offset, command, kbnumlocal);
1514 invis_flag = old_invis_flag;
1515 block = oldblock; /* XXX ? */
1516 return result;
1517 }
1518
1519 /* get body of macro */
macro_body(const char * name)1520 const char *macro_body(const char *name)
1521 {
1522 Macro *m;
1523 const char *body;
1524
1525 if (!name) return NULL;
1526 if (strncmp("world_", name, 6) == 0 && (body = world_info(NULL, name + 6)))
1527 return body;
1528 if (!(m = find_macro(name))) return NULL;
1529 return m->body ? m->body->data : "";
1530 }
1531
1532
1533 /****************************************
1534 * Routines to check triggers and hooks *
1535 ****************************************/
1536
1537 /* do_hook
1538 * Call macros that match <hooknum> and optionally the filled-in <argfmt>, and
1539 * prints the message in <fmt>. Returns the number of matches that were run.
1540 * A leading '!' in <fmt> is replaced with "% ", file name, and line number.
1541 * Note that calling do_hook() makes the caller non-atomic; be careful.
1542 */
do_hook(int hooknum,const char * fmt,const char * argfmt,...)1543 int do_hook(int hooknum, const char *fmt, const char *argfmt, ...)
1544 {
1545 va_list ap;
1546 int ran = 0;
1547 String *line = NULL;
1548 String *args = NULL;
1549 /* do_hook is re-entrant, so we can't use static buffer. macro regexps
1550 * may save a pointer to args, so we can't even use an auto buffer. */
1551
1552 va_start(ap, argfmt);
1553 if (hookflag || hilite || gag) {
1554 (args = Stringnew(NULL, 96, 0))->links++;
1555 vSprintf(args, 0, argfmt, ap);
1556 }
1557 va_end(ap);
1558
1559 if (fmt) {
1560 (line = Stringnew(NULL, 96, 0))->links++;
1561 if (*fmt == '!') {
1562 eprefix(line);
1563 fmt++;
1564 }
1565 va_start(ap, argfmt);
1566 vSprintf(line, SP_APPEND, fmt, ap);
1567 va_end(ap);
1568 }
1569
1570 if (hookflag || hilite || gag) {
1571 ran = find_and_run_matches(args, hooknum, &line, xworld(), TRUE, 0);
1572 Stringfree(args);
1573 }
1574
1575 if (line) {
1576 Stringfree(line);
1577 }
1578 return ran;
1579 }
1580
1581 /* Find and run one or more matches for a hook or trig.
1582 * text is text to be matched; if NULL, *linep is used.
1583 * If %Pn subs are to be allowed, text should be NULL.
1584 * If <hooknum> is <0, this looks for a trigger;
1585 * if <hooknum> is >=0 it is a hook number. If <linep> is non-NULL,
1586 * attributes of matching macros will be applied to *<linep>.
1587 */
find_and_run_matches(String * text,int hooknum,String ** linep,World * world,int globalflag,int exec_list_long)1588 int find_and_run_matches(String *text, int hooknum, String **linep,
1589 World *world, int globalflag, int exec_list_long)
1590 {
1591 Queue runq[1]; /* queue of macros to run */
1592 Macro *nonfallthru = NULL; /* list of non fall-thrus */
1593 int num = 0; /* # of non-fall-thrus */
1594 int ran = 0; /* # of executed macros */
1595 int lowerlimit = -1; /* lowest priority that can match */
1596 int header = 0; /* which headers have we printed? */
1597 ListEntry *gnode, *wnode, **nodep;
1598 Pattern *pattern;
1599 Macro *macro;
1600 const char *worldtype = NULL;
1601
1602 /* Macros are sorted by decreasing priority, with fall-thrus first. So,
1603 * we search the global and world lists in parallel. For each matching
1604 * fall-thru, we apply its attributes; if it's a hook, we add it to a
1605 * queue, if it's a trigger, we execute it immediately. When we find a
1606 * matching non-fall-thru, we collect a list of other non-fall-thru
1607 * matches of the same priority and select one, and apply its attributes;
1608 * again, if it's a hook, we add it to queue, if it's a trigger, we
1609 * execute.
1610 * Then, we print the line.
1611 * Then, if macros were queued (because this is a hook), we run all the
1612 * queued macros.
1613 * The point of the queue is so the line can be printed before any output
1614 * generated by the macros. We would like to do this for triggers as well
1615 * as hooks, but then /substitute wouldn't work.
1616 */
1617 /* Note: kill_macro() does not remove macros from any lists, so this will
1618 * work correctly when a macro kills itself, or inserts a new macro just
1619 * after itself in a list.
1620 */
1621
1622 if (world)
1623 worldtype = world_type(world);
1624 if (!worldtype)
1625 worldtype = "";
1626 if (!text)
1627 text = *linep;
1628 text->links++; /* in case substitute() frees text */
1629 recur_count++;
1630 if (hooknum>=0) {
1631 gnode = hooklist[hooknum].head;
1632 wnode = NULL;
1633 } else {
1634 gnode = triglist->head;
1635 wnode = world ? world->triglist->head : NULL;
1636 }
1637
1638 if (exec_list_long == 0) {
1639 init_queue(runq);
1640 }
1641
1642 while (gnode || wnode) {
1643 nodep = (!wnode) ? &gnode : (!gnode) ? &wnode :
1644 (rpricmp(MAC(wnode), MAC(gnode)) > 0) ? &gnode : &wnode;
1645 macro = MAC(*nodep);
1646 *nodep = (*nodep)->next;
1647
1648 if (macro->pri < lowerlimit && exec_list_long == 0)
1649 break;
1650 if (macro->flags & MACRO_DEAD) continue;
1651 if (!(
1652 (hooknum<0 && (
1653 (borg && macro->body && (macro->prob > 0)) ||
1654 (hilite && ((macro->attr & F_HWRITE) || macro->nsubattr)) ||
1655 (gag && (macro->attr & F_GAG)))) ||
1656 (hooknum>=0 && VEC_ISSET(hooknum, ¯o->hook))))
1657 {
1658 continue;
1659 }
1660
1661 /* triggers are listed by world, but hooks are not, so we must check */
1662 if (macro->world && macro->world != world) continue;
1663
1664 if (!globalflag && !macro->world) continue;
1665 if (macro->wtype.str) {
1666 if (!world) continue;
1667 if (!patmatch(¯o->wtype, NULL, worldtype))
1668 continue;
1669 }
1670 if (macro->exprprog) {
1671 struct Value *result = NULL;
1672 int expr_condition;
1673 result = expr_value_safe(macro->exprprog);
1674 expr_condition = valbool(result);
1675 freeval(result);
1676 if (!expr_condition) continue;
1677 }
1678 pattern = hooknum>=0 ? ¯o->hargs : ¯o->trig;
1679 if ((hooknum>=0 && !macro->hargs.str) || patmatch(pattern, CS(text), NULL))
1680 {
1681 if (exec_list_long == 0) {
1682 if (macro->fallthru) {
1683 if (linep && *linep)
1684 apply_attrs_of_match(macro, text, hooknum, *linep);
1685 if (hooknum>=0) {
1686 enqueue(runq, macro);
1687 } else {
1688 ran += run_match(macro, text, hooknum);
1689 if (linep && hooknum<0) {
1690 /* in case of /substitute */ /* XXX */
1691 Stringfree(text);
1692 text = *linep;
1693 text->links++;
1694 }
1695 }
1696 } else {
1697 /* collect list of non-fall-thru matches */
1698 lowerlimit = macro->pri;
1699 num++;
1700 macro->tnext = nonfallthru;
1701 nonfallthru = macro;
1702 }
1703 } else {
1704 ran += (lowerlimit < 0);
1705 if (header < 3 && macro->pri < lowerlimit) {
1706 oputs("% The following matching macros would not be "
1707 "applied:");
1708 header = 3;
1709 }
1710 if (header < 2 && !macro->fallthru) {
1711 oprintf("%% One of the following macros would %sbe "
1712 "applied:", ran > 1 ? "also " : "");
1713 lowerlimit = macro->pri;
1714 header = 2;
1715 }
1716 if (header < 1) {
1717 oputs("% All of the following macros would be applied:");
1718 header = 1;
1719 }
1720 if (exec_list_long > 1)
1721 print_def(tfout, NULL, macro);
1722 else if (macro->name)
1723 oprintf("%% %c %10d %s", macro->fallthru ? 'F' : ' ',
1724 macro->pri, macro->name);
1725 else
1726 oprintf("%% %c %10d #%d", macro->fallthru ? 'F' :' ',
1727 macro->pri, macro->num);
1728 }
1729 }
1730 }
1731
1732 if (exec_list_long == 0) {
1733 /* select exactly one of the non fall-thrus. */
1734 if (num > 0) {
1735 for (macro = nonfallthru, num = RRAND(0, num-1); num; num--)
1736 macro = macro->tnext;
1737 if (linep && *linep)
1738 apply_attrs_of_match(macro, text, hooknum, *linep);
1739 if (hooknum>=0) {
1740 enqueue(runq, macro);
1741 } else {
1742 ran += run_match(macro, text, hooknum);
1743 }
1744 }
1745
1746 /* print the line! */
1747 if (hooknum>=0 && linep && *linep) {
1748 if (hook_table[hooknum].hooktype & HT_ALERT) {
1749 alert(CS(*linep));
1750 } else if (hook_table[hooknum].hooktype & HT_WORLD) {
1751 /* Note: world_output() must come before alert(), otherwise
1752 * world_output() could cause an activity hook that would
1753 * clobber the alert */
1754 if (xworld())
1755 world_output(xworld(), CS(*linep));
1756 if (!xsock_is_fg()) {
1757 alert(CS(*linep));
1758 }
1759 } else {
1760 tfputline(CS(*linep), tferr); /* XXX conStr bug??? */
1761 }
1762 if (hook_table[hooknum].hooktype & HT_XSOCK) {
1763 xsock_alert_id(); /* alert should clear when xsock is fg'd */
1764 }
1765 }
1766
1767 /* run all of the queued macros */
1768 while ((macro = (Macro*)dequeue(runq))) {
1769 ran += run_match(macro, text, hooknum);
1770 }
1771 } else {
1772 oprintf("%% %s would have %s %d macro%s.",
1773 hooknum>=0 ? "Event" : "Text", hooknum>=0 ? "hooked" : "triggered",
1774 ran, (ran != 1) ? "s" : "");
1775 }
1776
1777 recur_count--;
1778 Stringfree(text);
1779 return ran;
1780 }
1781
1782
1783 /* apply attributes of a macro that has been selected by a trigger or hook */
apply_attrs_of_match(Macro * macro,String * text,int hooknum,String * line)1784 static void apply_attrs_of_match(
1785 Macro *macro, /* macro to apply */
1786 String *text, /* argument text that matched trigger/hook */
1787 int hooknum, /* hook number */
1788 String *line) /* line to which attributes are applied */
1789 {
1790 RegInfo *old, *ri;
1791
1792 if (!hilite && !gag) return;
1793
1794 /* Apply attributes (full and partial) to line. */
1795 if (!hilite)
1796 line->attrs = adj_attr(line->attrs, macro->attr & F_GAG);
1797 else if (!gag)
1798 line->attrs = adj_attr(line->attrs, macro->attr & ~F_GAG);
1799 else
1800 line->attrs = adj_attr(line->attrs, macro->attr);
1801
1802 ri = (hooknum>=0 ? macro->hargs : macro->trig).ri;
1803
1804 if (hooknum<0 && macro->trig.mflag == MATCH_REGEXP && line->len && hilite
1805 && macro->nsubattr)
1806 {
1807 int i, x, offset = 0;
1808 int start, end;
1809 int *saved_ovector = NULL;
1810
1811 if (text)
1812 old = new_reg_scope(ri, text);
1813
1814 check_charattrs(line, line->len, 0, __FILE__, __LINE__);
1815 do {
1816 for (x = 0; x < macro->nsubattr; x++) {
1817 if (macro->subattr[x].subexp == -1) {
1818 start = 0;
1819 end = ri->ovector[0];
1820 } else if (macro->subattr[x].subexp == -2) {
1821 start = ri->ovector[1];
1822 end = line->len;
1823 } else {
1824 start = ri->ovector[macro->subattr[x].subexp * 2];
1825 end = ri->ovector[macro->subattr[x].subexp * 2 + 1];
1826 }
1827 for (i = start; i < end; ++i)
1828 line->charattrs[i] =
1829 adj_attr(line->charattrs[i], macro->subattr[x].attr);
1830 }
1831 if (offset == ri->ovector[1]) break; /* offset wouldn't move */
1832 offset = ri->ovector[1];
1833 if (!saved_ovector) {
1834 saved_ovector = ri->ovector;
1835 ri->ovector = NULL;
1836 }
1837 } while (offset < line->len &&
1838 tf_reg_exec(ri, CS(text), NULL, offset) > 0);
1839 /* restore original startp/endp */
1840 if (saved_ovector) {
1841 if (ri->ovector) FREE(ri->ovector);
1842 ri->ovector = saved_ovector;
1843 }
1844 (ri->Str = CS(line))->links++;
1845
1846 if (text)
1847 restore_reg_scope(old);
1848 }
1849 }
1850
1851 typedef struct max_ctr {
1852 int bucket[2];
1853 int count[2];
1854 const int maxid, flagid;
1855 const char *label;
1856 } max_ctr_t;
1857
1858 static max_ctr_t trig_ctr = {{0,0}, {0,0}, VAR_max_trig, VAR_borg, "Trigger"};
1859 static max_ctr_t hook_ctr = {{0,0}, {0,0}, VAR_max_hook, VAR_hook, "Hook"};
1860
test_max_counter(max_ctr_t * c)1861 static int test_max_counter(max_ctr_t *c)
1862 {
1863 static int bucketsize = 5;
1864 int now = time(NULL) / bucketsize;
1865 if (now != c->bucket[1]) {
1866 c->bucket[0] = now - 1;
1867 c->bucket[1] = now;
1868 c->count[0] = (now == c->bucket[1] + 1) ? c->count[1] : 1;
1869 c->count[1] = 0;
1870 }
1871 if (c->count[0] + ++c->count[1] > special_var[c->maxid].val.u.ival) {
1872 set_var_by_id(c->flagid, 0);
1873 eprintf("%s rate exceeded %%%s (%d) in less than %ds. "
1874 "Setting %%%s off.", c->label,
1875 special_var[c->maxid].val.name, special_var[c->maxid].val.u.ival,
1876 2*bucketsize, special_var[c->flagid].val.name);
1877 return 0;
1878 }
1879 return 1;
1880 }
1881
1882 /* run a macro that has been selected by a trigger or hook */
run_match(Macro * macro,String * text,int hooknum)1883 static int run_match(
1884 Macro *macro, /* macro to run */
1885 String *text, /* argument text that matched trigger/hook */
1886 int hooknum) /* hook number */
1887 {
1888 int ran = 0;
1889 struct Sock *callingsock = xsock;
1890 RegInfo *old;
1891
1892 if (hooknum < 0) { /* trigger */
1893 if (!borg) return 0;
1894 if (text && max_trig > 0 && !test_max_counter(&trig_ctr)) return 0;
1895 } else { /* hook */
1896 if (max_hook > 0 && !test_max_counter(&hook_ctr)) return 0;
1897 }
1898
1899 if (text)
1900 old = new_reg_scope(hooknum>=0 ? macro->hargs.ri : macro->trig.ri, text);
1901
1902 /* Execute the macro. */
1903 if ((hooknum>=0 && hookflag) || (hooknum<0 && borg)) {
1904 callingsock = xsock;
1905 if (macro->prob == 100 || RRAND(0, 99) < macro->prob) {
1906 if (macro->shots && !--macro->shots) kill_macro(macro);
1907 if (mecho > macro->invis) {
1908 char numbuf[16];
1909 if (!*macro->name) sprintf(numbuf, "#%d", macro->num);
1910 tfprintf(tferr, "%S%s%s: /%s %S%A", do_mprefix(),
1911 hooknum>=0 ? hook_table[hooknum].name : "",
1912 hooknum>=0 ? " HOOK" : "TRIGGER",
1913 *macro->name ? macro->name : numbuf,
1914 text, mecho_attr);
1915 }
1916 if (macro->body && macro->body->len) {
1917 do_macro(macro, text, 0, hooknum>=0 ? USED_HOOK : USED_TRIG, 0);
1918 ran += !macro->quiet;
1919 }
1920 }
1921
1922 /* Restore xsock, in case macro called fg_sock(). main_loop() will
1923 * set xsock=fsock, so any fg_sock() will effect xsock after the
1924 * find_and_run_matches() loop is complete.
1925 */
1926 xsock = callingsock;
1927 }
1928
1929 if (text)
1930 restore_reg_scope(old);
1931 return ran;
1932 }
1933
1934 #if USE_DMALLOC
free_macros(void)1935 void free_macros(void)
1936 {
1937 while (maclist->head) nuke_macro((Macro *)maclist->head->datum);
1938 free_hash(macro_table);
1939 }
1940 #endif
1941
1942