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