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 = &macro_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 (&macro_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 &macro_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