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