1 /* Copyright 2010-2019 Free Software Foundation, Inc.
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>. */
15
16 #define _GNU_SOURCE
17
18 #include <config.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22
23 #include "parser.h"
24 #include "tree.h"
25 #include "text.h"
26 #include "input.h"
27 #include "convert.h"
28
29 static MACRO *macro_list;
30 static size_t macro_number;
31 static size_t macro_space;
32
33
34 /* Macro definition. */
35
36 void
new_macro(char * name,ELEMENT * macro)37 new_macro (char *name, ELEMENT *macro)
38 {
39 enum command_id new;
40 MACRO *m = 0;
41 ELEMENT tmp;
42
43 /* Check for an existing definition first for us to overwrite. */
44 new = lookup_command (name);
45 if (new)
46 m = lookup_macro (new);
47 if (!m)
48 {
49 if (macro_number == macro_space)
50 {
51 macro_list = realloc (macro_list,
52 (macro_space += 5) * sizeof (MACRO));
53 if (!macro_list)
54 fatal ("realloc failed");
55 }
56 new = add_texinfo_command (name);
57 m = ¯o_list[macro_number];
58 m->cmd = new;
59 macro_number++;
60 new &= ~USER_COMMAND_BIT;
61 user_defined_command_data[new].flags |= CF_MACRO;
62 }
63 else
64 {
65 free (m->macro_name);
66 }
67
68 m->macro_name = strdup (name);
69 m->element = macro;
70
71 memset (&tmp, 0, sizeof (ELEMENT));
72 tmp.contents = macro->contents;
73 m->macrobody = convert_to_texinfo (&tmp);
74 }
75
76 /* CMD will be either CM_macro or CM_rmacro. Read the line defining a macro's
77 name and the arguments it takes, and return this information in a new
78 ELEMENT. */
79 ELEMENT *
parse_macro_command_line(enum command_id cmd,char ** line_inout,ELEMENT * parent)80 parse_macro_command_line (enum command_id cmd, char **line_inout,
81 ELEMENT *parent)
82 {
83 char *line = *line_inout;
84 ELEMENT *macro, *macro_name;
85 char *name, *args_ptr;
86 int index;
87
88 macro = new_element (ET_NONE);
89 macro->cmd = cmd;
90 macro->line_nr = line_nr;
91
92 add_extra_string (macro, "arg_line", strdup (line));
93 /* Note this extra value isn't used much, so it might be possible
94 to get rid of it. */
95
96 line += strspn (line, whitespace_chars);
97 name = read_command_name (&line);
98
99 if (*line && *line != '{' && !strchr (whitespace_chars, *line))
100 {
101 line_error ("bad name for @%s", command_name (cmd));
102 add_extra_integer (macro, "invalid_syntax", 1);
103 return macro;
104 }
105 else if (!name)
106 {
107 line_error ("@%s requires a name", command_name (cmd));
108 add_extra_integer (macro, "invalid_syntax", 1);
109 return macro;
110 }
111
112 macro_name = new_element (ET_macro_name);
113 text_append (¯o_name->text, name);
114 free (name);
115 add_to_element_args (macro, macro_name);
116
117 args_ptr = line;
118 args_ptr += strspn (args_ptr, whitespace_chars);
119
120 if (*args_ptr != '{')
121 {
122 /* Either error or no args. */
123 goto check_trailing;
124 }
125 args_ptr++;
126
127 index = 0;
128 while (1)
129 {
130 /* args_ptr is after a '{' or ','. INDEX holds the number of
131 the macro argument */
132
133 char *q, *q2;
134 ELEMENT *arg;
135
136 args_ptr += strspn (args_ptr, whitespace_chars);
137
138 /* Find end of current argument. */
139 q = args_ptr;
140 while (*q != '\0' && *q != ',' && *q != '}')
141 q++;
142
143 if (!*q)
144 {
145 /* End of string reached before closing brace. */
146 goto check_trailing;
147 }
148
149 /* Disregard trailing whitespace. */
150 q2 = q;
151 while (q2 > args_ptr && strchr (whitespace_chars, q2[-1]))
152 q2--;
153
154 if (q2 == args_ptr)
155 {
156 /* argument is completely whitespace */
157 if (*q == ',')
158 {
159 line_error ("bad or empty @%s formal argument: ",
160 command_name(cmd));
161 arg = new_element (ET_macro_arg);
162 add_to_element_args (macro, arg);
163 text_append_n (&arg->text, "", 0);
164 add_extra_integer (macro, "invalid_syntax", 1);
165 }
166 }
167 else
168 {
169 arg = new_element (ET_macro_arg);
170 text_append_n (&arg->text, args_ptr, q2 - args_ptr);
171 add_to_element_args (macro, arg);
172
173 /* Check the argument name. */
174 {
175 char *p;
176 for (p = args_ptr; p < q2; p++)
177 {
178 if (!isalnum (*p) && *p != '_' && *p != '-')
179 {
180 char c = *q2; *q2 = 0;
181 line_error ("bad or empty @%s formal argument: %s",
182 command_name(cmd), args_ptr);
183 *q2 = c;
184 add_extra_integer (macro, "invalid_syntax", 1);
185 break;
186 }
187 }
188 }
189 }
190
191 args_ptr = q + 1;
192
193 if (*q == '}')
194 break;
195
196 index++;
197 }
198
199 check_trailing:
200 line = args_ptr;
201 line += strspn (line, whitespace_chars);
202 if (*line && *line != '@')
203 {
204 line_error ("bad syntax for @%s argument: %s",
205 command_name(cmd), line);
206 add_extra_integer (macro, "invalid_syntax", 1);
207 }
208 //line += strlen (line); /* Discard rest of line. */
209
210 *line_inout = line;
211 return macro;
212 }
213
214
215 /* Macro use. */
216
217 /* Return index into given arguments to look for the value of NAME.
218 Return -1 if not found. */
219
220 int
lookup_macro_parameter(char * name,ELEMENT * macro)221 lookup_macro_parameter (char *name, ELEMENT *macro)
222 {
223 int i, pos;
224 ELEMENT **args;
225
226 /* Find 'arg' in MACRO parameters. */
227 args = macro->args.list;
228 pos = 0;
229 for (i = 0; i < macro->args.number; i++)
230 {
231 if (args[i]->type == ET_macro_arg)
232 {
233 if (!strcmp (args[i]->text.text, name))
234 return pos;
235 pos++;
236 }
237 }
238 return -1;
239 }
240
241 /* LINE points the first non-whitespace character after the opening brace in a
242 macro invocation. CMD is the command identifier of the macro command.
243 Return array of the arguments. Return value to be freed by caller. */
244 char **
expand_macro_arguments(ELEMENT * macro,char ** line_inout,enum command_id cmd)245 expand_macro_arguments (ELEMENT *macro, char **line_inout, enum command_id cmd)
246 {
247 char *line = *line_inout;
248 char *pline = line;
249 TEXT arg;
250 int braces_level = 1;
251 int args_total;
252
253 char **arg_list = 0;
254 size_t arg_number = 0;
255 size_t arg_space = 0;
256
257 arg_list = malloc (sizeof (char *));
258 args_total = macro->args.number - 1;
259
260 text_init (&arg);
261
262 while (braces_level > 0)
263 {
264 /* At the beginning of this loop pline is at the start
265 of an argument. */
266 char *sep;
267
268 sep = pline + strcspn (pline, "\\,{}");
269 if (!*sep)
270 {
271 debug ("MACRO ARG end of line");
272 text_append (&arg, pline);
273 line = new_line ();
274 if (!line)
275 {
276 line_error ("@%s missing closing brace", command_name(cmd));
277 line = "\n";
278 free (arg.text);
279 goto funexit;
280 }
281 pline = line;
282 continue;
283 }
284
285 text_append_n (&arg, pline, sep - pline);
286
287 switch (*sep)
288 {
289 case '\\':
290 if (!strchr ("\\{},", sep[1]))
291 text_append_n (&arg, sep, 1);
292 if (sep[1])
293 {
294 text_append_n (&arg, &sep[1], 1);
295 pline = sep + 2;
296 }
297 else
298 pline = sep + 1;
299 break;
300 case '{':
301 braces_level++;
302 text_append_n (&arg, sep, 1);
303 pline = sep + 1;
304 break;
305 case '}':
306 braces_level--;
307 if (braces_level > 0)
308 {
309 text_append_n (&arg, sep, 1);
310 pline = sep + 1;
311 break;
312 }
313
314 /* Fall through to add argument. */
315 case ',':
316 if (braces_level > 1)
317 {
318 text_append_n (&arg, sep, 1);
319 pline = sep + 1;
320 break;
321 }
322
323 // check for too many args
324 if (*sep == '}' || arg_number < args_total - 1)
325 {
326 /* Add the last argument read to the list. */
327 if (arg_number == arg_space)
328 {
329 arg_list = realloc (arg_list,
330 (1+(arg_space += 5)) * sizeof (char *));
331 /* Include space for terminating null element. */
332 if (!arg_list)
333 fatal ("realloc failed");
334 }
335 if (arg.space > 0)
336 arg_list[arg_number++] = arg.text;
337 else
338 arg_list[arg_number++] = strdup ("");
339 text_init (&arg);
340
341 debug ("MACRO NEW ARG");
342 pline = sep + 1;
343
344 if (*sep == ',')
345 pline += strspn (pline, whitespace_chars);
346 }
347 else
348 {
349 if (args_total != 1)
350 line_error ("macro `%s' called with too many args",
351 command_name(cmd));
352 text_append_n (&arg, ",", 1);
353 pline = sep + 1;
354 }
355 break;
356 }
357 }
358
359 debug ("END MACRO ARGS EXPANSION");
360 line = pline;
361
362 if (args_total == 0 && arg_number > 0
363 && arg_list[0] && *arg_list[0])
364 {
365 line_error
366 ("macro `%s' declared without argument called with an argument",
367 command_name(cmd));
368 }
369
370 funexit:
371 *line_inout = line;
372 arg_list[arg_number] = 0;
373 return arg_list;
374 }
375
376 /* ARGUMENTS are the arguments used in the macro invocation. EXPANDED gets the
377 result of the expansion. */
378 static void
expand_macro_body(MACRO * macro_record,char * arguments[],TEXT * expanded)379 expand_macro_body (MACRO *macro_record, char *arguments[], TEXT *expanded)
380 {
381 int pos; /* Index into arguments. */
382 ELEMENT *macro;
383 char *macrobody;
384 char *ptext;
385
386 macro = macro_record->element;
387
388 macrobody = macro_record->macrobody;
389
390 /* Initialize TEXT object. */
391 expanded->end = 0;
392
393 if (!macrobody)
394 return;
395
396 ptext = macrobody;
397 while (1)
398 {
399 /* At the start of this loop ptext is at the beginning or
400 just after the last backslash sequence. */
401
402 char *bs; /* Pointer to next backslash. */
403
404 bs = strchrnul (ptext, '\\');
405 text_append_n (expanded, ptext, bs - ptext);
406 if (!*bs)
407 break; /* End of line. */
408
409 ptext = bs + 1;
410 if (*ptext == '\\')
411 {
412 text_append_n (expanded, "\\", 1); /* Escaped backslash (\\). */
413 ptext++;
414 }
415 else
416 {
417 bs = strchr (ptext, '\\');
418 if (!bs)
419 {
420 /* malformed input - unpaired backslash */
421 return;
422 }
423
424 *bs = '\0';
425 pos = lookup_macro_parameter (ptext, macro);
426 if (pos == -1)
427 {
428 line_error ("\\ in @%s expansion followed `%s' instead of "
429 "parameter name or \\",
430 macro->args.list[0]->text.text,
431 ptext);
432 text_append (expanded, "\\");
433 text_append (expanded, ptext);
434 }
435 else
436 {
437 if (arguments && arguments[pos])
438 text_append (expanded, arguments[pos]);
439 }
440 *bs = '\\';
441 ptext = bs + 1;
442 }
443 }
444 }
445
446 MACRO *
lookup_macro(enum command_id cmd)447 lookup_macro (enum command_id cmd)
448 {
449 int i;
450
451 for (i = 0; i < macro_number; i++)
452 {
453 if (macro_list[i].cmd == cmd)
454 return ¯o_list[i];
455 }
456 return 0;
457 }
458
459 void
delete_macro(char * name)460 delete_macro (char *name)
461 {
462 enum command_id cmd;
463 MACRO *m;
464 cmd = lookup_command (name);
465 if (!cmd)
466 return;
467 m = lookup_macro (cmd);
468 if (!m)
469 return;
470 m->cmd = 0;
471 free (m->macro_name);
472 m->macro_name = strdup ("");
473 free (m->macrobody);
474 m->macrobody = 0;
475 m->element = 0;
476 remove_texinfo_command (cmd);
477 }
478
479 void
wipe_macros(void)480 wipe_macros (void)
481 {
482 int i;
483
484 for (i = 0; i < macro_number; i++)
485 {
486 free (macro_list[i].macro_name);
487 free (macro_list[i].macrobody);
488 }
489 macro_number = 0;
490 }
491
492 /* Handle macro expansion. CMD is the macro command. */
493 ELEMENT *
handle_macro(ELEMENT * current,char ** line_inout,enum command_id cmd)494 handle_macro (ELEMENT *current, char **line_inout, enum command_id cmd)
495 {
496 char *line, *p;
497 MACRO *macro_record;
498 ELEMENT *macro;
499 TEXT expanded;
500 char **arguments = 0;
501 int args_number;
502
503 line = *line_inout;
504 text_init (&expanded);
505
506 macro_record = lookup_macro (cmd);
507 if (!macro_record)
508 fatal ("no macro record");
509 macro = macro_record->element;
510
511 /* Get number of args. - 1 for the macro name. */
512 args_number = macro->args.number - 1;
513
514 p = line + strspn (line, whitespace_chars);
515 if (*p == '{')
516 {
517 line = p;
518 line++;
519 line += strspn (line, whitespace_chars);
520 arguments = expand_macro_arguments (macro, &line, cmd);
521 }
522 /* Warning depending on the number of arguments this macro
523 is supposed to take. */
524 else if (args_number != 1)
525 {
526 if (args_number > 1)
527 line_warn ("@%s defined with zero or more than one argument should "
528 "be invoked with {}", command_name(cmd));
529 /* As agreed on the bug-texinfo mailing list, no warn when zero
530 arg and not called with {}. */
531 }
532 else
533 {
534 char *p;
535 /* If it takes a single line of input, and we don't have a full line of
536 input already, call new_line. */
537 if (!strchr (line, '\n'))
538 {
539 line = new_line ();
540 if (!line)
541 line = "";
542 }
543 line += strspn (line, whitespace_chars_except_newline);
544
545 arguments = malloc (sizeof (char *) * 2);
546 arguments[0] = strdup (line);
547 arguments[1] = 0;
548
549 p = strchr (arguments[0], '\n');
550 if (p)
551 {
552 *p = '\0';
553 line = "\n";
554 }
555 }
556
557 expand_macro_body (macro_record, arguments, &expanded);
558 debug ("MACROBODY: %s||||||", expanded.text);
559
560 if (expanded.end > 0 && expanded.text[expanded.end - 1] == '\n')
561 expanded.text[--expanded.end] = '\0';
562
563 if (input_number >= 1000)
564 {
565 line_warn (
566 "macro call nested too deeply "
567 "(set MAX_NESTED_MACROS to override; current value %d)", 1000);
568 goto funexit;
569 /* TODO: actually check MAX_NESTED_MACROS? */
570 }
571
572 if (macro->cmd == CM_macro)
573 {
574 if (expanding_macro (command_name(cmd)))
575 {
576 line_error ("recursive call of macro %s is not allowed; "
577 "use @rmacro if needed", command_name(cmd));
578 expanded.text[0] = '\0';
579 expanded.end = 0;
580 }
581 }
582
583 /* Free arguments. */
584 if (arguments)
585 {
586 char **s = arguments;
587 while (*s)
588 {
589 free (*s);
590 s++;
591 }
592 free (arguments);
593 }
594
595 // 3958 Pop macro stack
596
597 /* Put expansion in front of the current line. */
598 input_push_text (strdup (line), 0);
599 line = strchr (line, '\0');
600 input_push_text (expanded.text, command_name(cmd));
601
602 funexit:
603 *line_inout = line;
604 return current;
605 }
606
607
608 /* @set and @value */
609
610 typedef struct {
611 char *name;
612 char *value;
613 } VALUE;
614
615 static VALUE *value_list;
616 static size_t value_number;
617 static size_t value_space;
618
619 void
wipe_values(void)620 wipe_values (void)
621 {
622 size_t i;
623 for (i = 0; i < value_number; i++)
624 {
625 free (value_list[i].name);
626 free (value_list[i].value);
627 }
628 value_number = 0;
629 }
630
631 void
store_value(char * name,char * value)632 store_value (char *name, char *value)
633 {
634 int i;
635 VALUE *v = 0;
636 int len;
637
638 len = strlen (name);
639
640 /* Check if already defined. */
641 for (i = 0; i < value_number; i++)
642 {
643 if (!strncmp (value_list[i].name, name, len) && !value_list[i].name[len])
644 {
645 v = &value_list[i];
646 free (v->name); free (v->value);
647 break;
648 }
649 }
650
651 if (!v)
652 {
653 if (value_number == value_space)
654 {
655 value_list = realloc (value_list, (value_space += 5) * sizeof (VALUE));
656 }
657 v = &value_list[value_number++];
658 }
659
660 v->name = strdup (name);
661 v->value = strdup (value);
662 }
663
664 void
clear_value(char * name)665 clear_value (char *name)
666 {
667 int i;
668 for (i = 0; i < value_number; i++)
669 {
670 if (!strcmp (value_list[i].name, name))
671 {
672 value_list[i].name[0] = '\0';
673 value_list[i].value[0] = '\0';
674 }
675 }
676 }
677
678 char *
fetch_value(char * name)679 fetch_value (char *name)
680 {
681 int i;
682 for (i = 0; i < value_number; i++)
683 {
684 if (!strcmp (value_list[i].name, name))
685 return value_list[i].value;
686 }
687
688 if (!strcmp (name, "txicommandconditionals"))
689 return "1";
690 return 0;
691 }
692
693
694 static INFO_ENCLOSE *infoencl_list;
695 static size_t infoencl_number;
696 static size_t infoencl_space;
697
698 INFO_ENCLOSE *
lookup_infoenclose(enum command_id cmd)699 lookup_infoenclose (enum command_id cmd)
700 {
701 int i;
702 for (i = 0; i < infoencl_number; i++)
703 {
704 if (infoencl_list[i].cmd == cmd)
705 return &infoencl_list[i];
706 }
707 return 0;
708 }
709
710 void
add_infoenclose(enum command_id cmd,char * begin,char * end)711 add_infoenclose (enum command_id cmd, char *begin, char *end)
712 {
713 int i;
714 INFO_ENCLOSE *ie = 0;
715
716 /* Check if already defined. */
717 for (i = 0; i < infoencl_number; i++)
718 {
719 if (infoencl_list[i].cmd == cmd)
720 {
721 ie = &infoencl_list[i];
722 free (ie->begin);
723 free (ie->end);
724 break;
725 }
726 }
727
728 if (!ie)
729 {
730 if (infoencl_number == infoencl_space)
731 {
732 infoencl_list = realloc (infoencl_list,
733 (infoencl_space += 5)
734 * sizeof (INFO_ENCLOSE));
735 }
736 ie = &infoencl_list[infoencl_number++];
737 }
738
739 ie->cmd = cmd;
740 ie->begin = strdup (begin);
741 ie->end = strdup (end);
742 }
743
744