1 /* handle_commands.c -- what to do when a command name is first read */
2 /* Copyright 2010-2020 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <config.h>
18 #include <stdlib.h>
19 #include <string.h>
20 
21 #include "parser.h"
22 #include "input.h"
23 #include "text.h"
24 
25 /* Return a containing @itemize or @enumerate if inside it. */
26 ELEMENT *
item_container_parent(ELEMENT * current)27 item_container_parent (ELEMENT *current)
28 {
29   if ((current->cmd == CM_item
30        || current->type == ET_before_item)
31       && current->parent
32       && ((current->parent->cmd == CM_itemize
33            || current->parent->cmd == CM_enumerate)))
34     {
35       return current->parent;
36     }
37   return 0;
38 }
39 
40 /* Check that there are no text holding environments (currently
41    checking only paragraphs and preformatted) in contents. */
42 int
check_no_text(ELEMENT * current)43 check_no_text (ELEMENT *current)
44 {
45   int after_paragraph = 0;
46   int i, j;
47   for (i = 0; i < current->contents.number; i++)
48     {
49       enum element_type t;
50       ELEMENT *f;
51       f = current->contents.list[i];
52       t = f->type;
53       if (t == ET_paragraph)
54         {
55           after_paragraph = 1;
56           break;
57         }
58       else if (t == ET_preformatted
59                || t == ET_rawpreformatted)
60         {
61           for (j = 0; j < f->contents.number; j++)
62             {
63               ELEMENT *g = f->contents.list[j];
64               if ((g->text.end > 0
65                    && g->text.text[strspn (g->text.text, whitespace_chars)])
66                   || (g->cmd && g->cmd != CM_c
67                       && g->cmd != CM_comment
68                       && g->type != ET_index_entry_command))
69                 {
70                   after_paragraph = 1;
71                   break;
72                 }
73             }
74           if (after_paragraph)
75             break;
76         }
77     }
78   return after_paragraph;
79 }
80 
81 /* noarg skipspace */
82 ELEMENT *
handle_other_command(ELEMENT * current,char ** line_inout,enum command_id cmd,int * status)83 handle_other_command (ELEMENT *current, char **line_inout,
84                      enum command_id cmd, int *status)
85 {
86   ELEMENT *misc = 0;
87   char *line = *line_inout;
88   int arg_spec;
89 
90   *status = STILL_MORE_TO_PROCESS;
91 
92   arg_spec = command_data(cmd).data;
93   if (arg_spec == OTHER_noarg)
94     {
95       if (command_data(cmd).flags & CF_in_heading)
96         {
97           line_error ("@%s should only appear in heading or footing",
98                       command_name(cmd));
99         }
100 
101       misc = new_element (ET_NONE);
102       misc->cmd = cmd;
103       add_to_element_contents (current, misc);
104       register_global_command (misc);
105       if (close_preformatted_command(cmd))
106         current = begin_preformatted (current);
107     }
108   else
109     {
110       /* @item can occur in several contents: in an @itemize, a @table, or
111          a @multitable. */
112       if (cmd == CM_item || cmd == CM_headitem || cmd == CM_tab)
113         {
114           ELEMENT *parent;
115 
116           /* @itemize or @enumerate */
117           if ((parent = item_container_parent (current)))
118             {
119               if (cmd == CM_item)
120                 {
121                   debug ("ITEM CONTAINER");
122                   counter_inc (&count_items);
123                   misc = new_element (ET_NONE);
124                   misc->cmd = CM_item;
125 
126                   add_extra_integer (misc, "item_number",
127                                      counter_value (&count_items, parent));
128 
129                   add_to_element_contents (parent, misc);
130                   current = misc;
131                 }
132               else
133                 {
134                   line_error ("@%s not meaningful inside `@%s' block",
135                               command_name(cmd),
136                               command_name(parent->cmd));
137                 }
138               current = begin_preformatted (current);
139             }
140           /* @table, @vtable, @ftable */
141           else if ((parent = item_line_parent (current)))
142             {
143               line_error ("@%s not meaningful inside `@%s' block",
144                           command_name(cmd),
145                           command_name(parent->cmd));
146               current = begin_preformatted (current);
147             }
148           /* In a @multitable */
149           else if ((parent = item_multitable_parent (current)))
150             {
151               if (cmd != CM_item && cmd != CM_headitem
152                   && cmd != CM_tab)
153                 {
154                   line_error ("@%s not meaningful inside @%s block",
155                               command_name(cmd),
156                               command_name(parent->cmd));
157                 }
158               else
159                 {
160                   int max_columns = 0;
161                   KEY_PAIR *prototypes;
162 
163                   prototypes = lookup_extra  (parent, "prototypes");
164                   if (prototypes)
165                     max_columns = prototypes->value->contents.number;
166                   else
167                     {
168                       prototypes = lookup_extra(parent, "columnfractions");
169                       if (prototypes)
170                         {
171                           prototypes = lookup_extra((ELEMENT *) prototypes->value,
172                                                     "misc_args");
173                           if (prototypes)
174                             max_columns = prototypes->value->contents.number;
175                         }
176                     }
177 
178                   if (max_columns == 0)
179                     {
180                       line_warn ("@%s in empty multitable",
181                                  command_name(cmd));
182                     }
183                   else if (cmd == CM_tab)
184                     {
185                       ELEMENT *row;
186                       row = last_contents_child (parent);
187                       if (row->type == ET_before_item)
188                         line_error ("@tab before @item");
189                       else if (counter_value (&count_cells, row)
190                                >= max_columns)
191                         {
192                           line_error ("too many columns in multitable item"
193                                       " (max %d)", max_columns);
194                         }
195                       else
196                         {
197                           counter_inc (&count_cells);
198                           misc = new_element (ET_NONE);
199                           misc->cmd = cmd;
200                           add_to_element_contents (row, misc);
201                           current = misc;
202                           debug ("TAB");
203 
204                           add_extra_integer (current, "cell_number",
205                                              counter_value (&count_cells, row));
206                         }
207                     }
208                   else /* @item or @headitem */
209                     {
210                       ELEMENT *row;
211 
212                       debug ("ROW");
213                       row = new_element (ET_row);
214                       add_to_element_contents (parent, row);
215 
216                       /* Note that the "row_number" extra value,
217                          isn't actually used anywhere at present. */
218                       add_extra_integer (row, "row_number",
219                                          parent->contents.number - 1);
220 
221                       misc = new_element (ET_NONE);
222                       misc->cmd = cmd;
223                       add_to_element_contents (row, misc);
224                       current = misc;
225 
226                       if (counter_value (&count_cells, parent) != -1)
227                         counter_pop (&count_cells);
228                       counter_push (&count_cells, row, 1);
229                       add_extra_integer (current, "cell_number",
230                                          counter_value (&count_cells, row));
231                     }
232                 }
233               current = begin_preformatted (current);
234             } /* In @multitable */
235           else if (cmd == CM_tab)
236             {
237               line_error ("ignoring @tab outside of multitable");
238               current = begin_preformatted (current);
239             }
240           else
241             {
242               line_error ("@%s outside of table or list",
243                           command_name(cmd));
244               current = begin_preformatted (current);
245             }
246           if (misc)
247             misc->line_nr = line_nr;
248         }
249       else
250         {
251           misc = new_element (ET_NONE);
252           misc->cmd = cmd;
253           misc->line_nr = line_nr;
254           add_to_element_contents (current, misc);
255         }
256       start_empty_line_after_command (current, &line, misc);
257       if (cmd == CM_indent || cmd == CM_noindent)
258         {
259           /* Start a new paragraph if not in one already. */
260           int spaces;
261           ELEMENT *paragraph;
262 
263           /* Check if if we should change an ET_empty_line_after_command
264              element to ET_empty_spaces_after_command by looking ahead
265              to see what comes next. */
266           if (!strchr (line, '\n'))
267             {
268               char *line2;
269               input_push_text (strdup (line), 0);
270               line2 = new_line ();
271               if (line2)
272                 line = line2;
273             }
274           spaces = strspn (line, whitespace_chars);
275           if (spaces > 0)
276             {
277               char saved = line[spaces];
278               line[spaces] = '\0';
279               current = merge_text (current, line);
280               line[spaces] = saved;
281               line += spaces;
282             }
283           if (*line
284               && last_contents_child(current)->type
285               == ET_empty_line_after_command)
286             {
287               last_contents_child(current)->type
288                 = ET_empty_spaces_after_command;
289             }
290           paragraph = begin_paragraph (current);
291           if (paragraph)
292             current = paragraph;
293           if (!*line)
294             {
295               *status = GET_A_NEW_LINE;
296               goto funexit;
297             }
298         }
299     }
300 
301 funexit:
302   *line_inout = line;
303   return current;
304 }
305 
306 /* STATUS is set to GET_A_NEW_LINE if we should get a new line after this,
307    to FINISHED_TOTALLY if we should stop processing completely. */
308 ELEMENT *
handle_line_command(ELEMENT * current,char ** line_inout,enum command_id cmd,int * status)309 handle_line_command (ELEMENT *current, char **line_inout,
310                      enum command_id cmd, int *status)
311 {
312   ELEMENT *misc = 0;
313   char *line = *line_inout;
314   int arg_spec;
315 
316   *status = STILL_MORE_TO_PROCESS;
317 
318   /* Root commands (like @node) and @bye */
319   if (command_data(cmd).flags & CF_root || cmd == CM_bye)
320     {
321       ELEMENT *closed_elt; /* Not used */
322       current = close_commands (current, 0, &closed_elt, cmd);
323       if (current->type == ET_text_root)
324         {
325           if (cmd != CM_bye)
326             {
327               /* Use document_root when we have nodes or sections. */
328               ELEMENT *new_root = new_element (ET_document_root);
329               add_to_element_contents (new_root, current);
330               current = new_root;
331             }
332         }
333       else
334         {
335           current = current->parent;
336           if (!current)
337             fatal ("no parent element");
338         }
339     }
340 
341   /* Look up information about this command ( skipline text
342      line lineraw (a number) ). */
343   arg_spec = command_data(cmd).data;
344 
345   /* All the cases using the raw line.
346      TODO: I don't understand what the difference is between these.
347      LINE_skipline is used where the command takes no argument at all. */
348   if (arg_spec == LINE_skipline || arg_spec == LINE_lineraw
349            || arg_spec == LINE_special)
350     {
351       ELEMENT *args = 0;
352       enum command_id equivalent_cmd = 0;
353       int has_comment = 0;
354       int ignored = 0;
355 
356       if (cmd == CM_insertcopying)
357         {
358           ELEMENT *p = current;
359           while (p)
360             {
361               if (p->cmd == CM_copying)
362                 {
363                   line_error ("@%s not allowed inside `@copying' block",
364                               command_name(cmd));
365                   ignored = 1;
366                   break;
367                 }
368               p = p->parent;
369             }
370         }
371 
372       /* If the current input is the result of a macro expansion,
373          it may not be a complete line.  Check for this and acquire the rest
374          of the line if necessary. */
375       if (!strchr (line, '\n'))
376         {
377           char *line2;
378           LINE_NR save_ln;
379 
380           input_push_text (strdup (line), 0);
381 
382           save_ln = line_nr;
383           line2 = new_line ();
384           if (line2)
385             {
386               line = line2;
387               line_nr = save_ln;
388             }
389         }
390 
391       misc = new_element (ET_NONE);
392       misc->cmd = cmd;
393 
394       if (arg_spec == LINE_skipline || arg_spec == LINE_lineraw)
395         {
396           ELEMENT *arg;
397           args = new_element (ET_NONE);
398           arg = new_element (ET_NONE);
399           add_to_element_contents (args, arg);
400           text_append (&arg->text, line);
401         }
402       else /* arg_spec == LINE_special */
403         {
404           args = parse_special_misc_command (line, cmd, &has_comment);
405           add_extra_string (misc, "arg_line", strdup (line));
406         }
407 
408       /* Handle @set txicodequoteundirected as an
409          alternative to @codequoteundirected. */
410       if (cmd == CM_set || cmd == CM_clear)
411         {
412           if (args->contents.number > 0
413               && args->contents.list[0]->text.end > 0)
414             {
415               if (!strcmp (args->contents.list[0]->text.text,
416                            "txicodequoteundirected"))
417                 equivalent_cmd = CM_codequoteundirected;
418               else if (!strcmp (args->contents.list[0]->text.text,
419                                 "txicodequotebacktick"))
420                 equivalent_cmd = CM_codequotebacktick;
421             }
422         }
423       if (equivalent_cmd)
424         {
425           char *arg = 0;
426           ELEMENT *line_args;
427           ELEMENT *e;
428 
429           if (cmd == CM_set)
430             arg = "on";
431           else
432             arg = "off";
433 
434           /* Now manufacture the parse tree for the equivalent
435              command and add it to the tree. */
436 
437           destroy_element_and_children (args);
438           args = new_element (ET_NONE);
439           e = new_element (ET_NONE);
440           text_append (&e->text, arg);
441           add_to_element_contents (args, e);
442 
443           destroy_element_and_children (misc);
444           misc = new_element (ET_NONE);
445           misc->cmd = equivalent_cmd;
446           misc->line_nr = line_nr;
447 
448           line_args = new_element (ET_line_arg);
449           add_to_element_args (misc, line_args);
450           add_extra_misc_args (misc, "misc_args", args);
451 
452           add_extra_string_dup (misc, "spaces_before_argument", " ");
453 
454           e = new_element (ET_NONE);
455           text_append (&e->text, arg);
456           add_to_element_contents (line_args, e);
457 
458           e = new_element (ET_spaces_at_end);
459           text_append_n (&e->text, "\n", 1);
460           add_to_element_contents (line_args, e);
461 
462           add_to_element_contents (current, misc);
463         }
464       else
465         {
466           int i;
467           if (!ignored)
468             {
469               add_to_element_contents (current, misc);
470 
471               for (i = 0; i < args->contents.number; i++)
472                 {
473                   ELEMENT *misc_arg = new_element (ET_misc_arg);
474                   text_append_n (&misc_arg->text,
475                                  args->contents.list[i]->text.text,
476                                  args->contents.list[i]->text.end);
477                   add_to_element_args (misc, misc_arg);
478                 }
479               /* TODO: Could we have just set misc->args directly as args? */
480               if (args->contents.number > 0 && arg_spec != LINE_skipline)
481                 add_extra_misc_args (misc, "misc_args", args);
482               else
483                 destroy_element_and_children (args);
484             }
485           else
486             {
487               destroy_element_and_children (misc);
488               destroy_element_and_children (args);
489               misc = 0;
490             }
491         }
492 
493       if (cmd == CM_raisesections)
494         {
495           global_info.sections_level++;
496         }
497       else if (cmd == CM_lowersections)
498         {
499           global_info.sections_level--;
500         }
501       else if (cmd == CM_novalidate)
502         {
503           global_info.novalidate = 1;
504         }
505 
506       if (misc)
507         register_global_command (misc);
508 
509       if (arg_spec != LINE_special || !has_comment)
510         current = end_line (current);
511 
512       if (cmd == CM_bye)
513         {
514           *status = FINISHED_TOTALLY;
515           goto funexit;
516         }
517 
518       if (close_preformatted_command(cmd))
519         current = begin_preformatted (current);
520 
521       *status = GET_A_NEW_LINE;
522       goto funexit;
523     }
524   else
525     {
526       ELEMENT *arg;
527 
528       /* text, line, or a number.
529          (This includes handling of "@end", which is LINE_text.) */
530       if (cmd == CM_item_LINE || cmd == CM_itemx)
531         {
532           ELEMENT *parent;
533           if ((parent = item_line_parent (current)))
534             {
535               debug ("ITEM_LINE");
536               current = parent;
537               gather_previous_item (current, cmd);
538             }
539           else
540             {
541               line_error ("@%s outside of table or list",
542                           cmd == CM_item_LINE ? "item" : "itemx");
543               current = begin_preformatted (current);
544             }
545           misc = new_element (ET_NONE);
546           misc->cmd = (cmd == CM_item_LINE) ? CM_item : CM_itemx;
547           misc->line_nr = line_nr;
548           add_to_element_contents (current, misc);
549         }
550       else
551         {
552           /* Add to contents */
553           misc = new_element (ET_NONE);
554           misc->cmd = cmd;
555           misc->line_nr = line_nr;
556 
557           if (cmd == CM_subentry)
558             {
559               long level = 1;
560               ELEMENT *parent = current->parent;
561 
562               if (!(command_flags(parent) & CF_index_entry_command)
563                   && parent->cmd != CM_subentry)
564                 {
565                   line_warn ("@subentry should only occur in an index entry");
566                 }
567 
568               add_extra_element (parent, "subentry", misc);
569 
570               if (parent->cmd == CM_subentry)
571                 {
572                   KEY_PAIR *k = lookup_extra (parent, "level");
573                   if (k && k->value)
574                     level = (long) k->value + 1;
575                 }
576               add_extra_integer (misc, "level", level);
577               if (level > 2)
578                 {
579                   line_error
580                     ("no more than two levels of index subentry are allowed");
581                 }
582 
583               /* Do not make the @subentry element a child of the index
584                  command.  This means that spaces are preserved properly
585                  when converting back to Texinfo. */
586               current = end_line (current);
587             }
588 
589           add_to_element_contents (current, misc);
590 
591           if (command_data(cmd).flags & CF_sectioning)
592             {
593               if (global_info.sections_level)
594                 {
595                   add_extra_integer (misc, "sections_level",
596                                      global_info.sections_level);
597                 }
598             }
599 
600           /* @def*x */
601           if (command_data(cmd).flags & CF_def)
602             {
603               enum command_id base_command;
604               char *base_name;
605               int base_len;
606               int after_paragraph;
607 
608               /* Find the command with "x" stripped from the end, e.g.
609                  deffnx -> deffn. */
610               base_name = command_name(cmd);
611               add_extra_string_dup (misc, "original_def_cmdname", base_name);
612 
613               base_name = strdup (base_name);
614               base_len = strlen (base_name);
615               if (base_name[base_len - 1] != 'x')
616                 fatal ("no x at end of def command name");
617               base_name[base_len - 1] = '\0';
618               base_command = lookup_command (base_name);
619               if (base_command == CM_NONE)
620                 fatal ("no def base command");
621               add_extra_string (misc, "def_command", base_name);
622 
623               after_paragraph = check_no_text (current);
624               push_context (ct_def);
625               misc->type = ET_def_line;
626               if (current->cmd == base_command)
627                 {
628                   ELEMENT *e = pop_element_from_contents (current);
629                   /* e should be the same as misc */
630                   /* Gather an "inter_def_item" element. */
631                   gather_def_item (current, cmd);
632                   add_to_element_contents (current, e);
633                 }
634               if (current->cmd != base_command || after_paragraph)
635                 {
636                   // error - deffnx not after deffn
637                   line_error ("must be after `@%s' to use `@%s'",
638                                command_name(base_command),
639                                command_name(cmd));
640                   add_extra_integer (misc, "not_after_command", 1);
641                 }
642             }
643         }
644 
645       /* change 'current' to its last child.  This is ELEMENT *misc above.  */
646       current = last_contents_child (current);
647       arg = new_element (ET_line_arg);
648       add_to_element_args (current, arg);
649 
650       if (cmd == CM_node)
651         {
652           /* At most three comma-separated arguments to @node.  This
653              is the only (non-block) line command taking comma-separated
654              arguments.  Its arguments will be gathered the same as
655              those of some block line commands and brace commands. */
656           counter_push (&count_remaining_args, current, 3);
657         }
658       else if (cmd == CM_author)
659         {
660           ELEMENT *parent = current;
661           int found = 0;
662           while (parent->parent)
663             {
664               parent = parent->parent;
665               if (parent->type == ET_brace_command_context)
666                 break;
667               if (parent->cmd == CM_titlepage)
668                 {
669                   add_extra_element (current, "titlepage", parent);
670                   found = 1; break;
671                 }
672               else if (parent->cmd == CM_quotation
673                        || parent->cmd == CM_smallquotation)
674                 {
675                   KEY_PAIR *k; ELEMENT *e;
676                   k = lookup_extra (parent, "authors");
677                   if (k)
678                     e = k->value;
679                   else
680                     {
681                       e = new_element (ET_NONE);
682                       add_extra_contents (parent, "authors", e);
683                     }
684                   add_to_contents_as_array (e, current);
685                   add_extra_element (current, "quotation", parent);
686                   found = 1; break;
687                 }
688             }
689           if (!found)
690             line_warn ("@author not meaningful outside "
691                        "`@titlepage' and `@quotation' environments");
692         }
693       else if (cmd == CM_dircategory && current_node)
694         line_warn ("@dircategory after first node");
695       else if (cmd == CM_printindex && current_node)
696         add_extra_integer (current_node, "isindex", 1);
697 
698       current = last_args_child (current);
699 
700       /* add 'line' to context_stack.  This will be the
701          case while we read the argument on this line. */
702       if (!(command_data(cmd).flags & CF_def))
703         push_context (ct_line);
704       start_empty_line_after_command (current, &line, misc);
705     }
706 
707   if (misc)
708     register_global_command (misc);
709   if (cmd == CM_dircategory)
710     add_to_contents_as_array (&global_info.dircategory_direntry, misc);
711 
712 funexit:
713   *line_inout = line;
714   return current;
715 }
716 
717 
718 struct expanded_format {
719     char *format;
720     int expandedp;
721 };
722 static struct expanded_format expanded_formats[] = {
723     "html", 0,
724     "docbook", 0,
725     "plaintext", 1,
726     "tex", 0,
727     "xml", 0,
728     "info", 1,
729 };
730 
731 void
clear_expanded_formats(void)732 clear_expanded_formats (void)
733 {
734   int i;
735   for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
736        i++)
737     {
738       expanded_formats[i].expandedp = 0;
739     }
740 }
741 
742 void
add_expanded_format(char * format)743 add_expanded_format (char *format)
744 {
745   int i;
746   for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
747        i++)
748     {
749       if (!strcmp (format, expanded_formats[i].format))
750         {
751           expanded_formats[i].expandedp = 1;
752           break;
753         }
754     }
755   if (!strcmp (format, "plaintext"))
756     add_expanded_format ("info");
757 }
758 
759 int
format_expanded_p(char * format)760 format_expanded_p (char *format)
761 {
762   int i;
763   for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
764        i++)
765     {
766       if (!strcmp (format, expanded_formats[i].format))
767         return expanded_formats[i].expandedp;
768     }
769   return 0;
770 }
771 
772 /* A command name has been read that starts a multiline block, which should
773    end in @end <command name>.  The block will be processed until
774    "end_line_misc_line" in end_line.c processes the @end command. */
775 ELEMENT *
handle_block_command(ELEMENT * current,char ** line_inout,enum command_id cmd,int * get_new_line)776 handle_block_command (ELEMENT *current, char **line_inout,
777                       enum command_id cmd, int *get_new_line)
778 {
779   char *line = *line_inout;
780   unsigned long flags = command_data(cmd).flags;
781 
782   /* New macro being defined. */
783   if (cmd == CM_macro || cmd == CM_rmacro)
784     {
785       ELEMENT *macro;
786       macro = parse_macro_command_line (cmd, &line, current);
787       add_to_element_contents (current, macro);
788       current = macro;
789 
790       /* A new line should be read immediately after this.  */
791       line = strchr (line, '\0');
792       *get_new_line = 1;
793       goto funexit;
794     }
795   else if (command_data(cmd).data == BLOCK_conditional)
796     {
797       int iftrue = 0; /* Whether the conditional is true. */
798       if (cmd == CM_ifclear || cmd == CM_ifset
799           || cmd == CM_ifcommanddefined || cmd == CM_ifcommandnotdefined)
800         {
801           char *p = line;
802           p = line + strspn (line, whitespace_chars);
803           if (!*p)
804             line_error ("@%s requires a name", command_name(cmd));
805           else
806             {
807               char *flag = read_flag_name (&p);
808               if (!flag)
809                 goto bad_value;
810               else
811                 {
812                   p += strspn (p, whitespace_chars);
813                   /* Check for a comment at the end of the line. */
814                   if (memcmp (p, "@c", 2) == 0)
815                     {
816                       p += 2;
817                       if (memcmp (p, "omment", 6) == 0)
818                         p += 7;
819                       if (*p && *p != '@' && !strchr (whitespace_chars, *p))
820                         goto bad_value; /* @c or @comment not terminated. */
821                     }
822                   else if (*p)
823                     goto bad_value; /* Trailing characters on line. */
824                 }
825               if (1)
826                 {
827                   if (cmd == CM_ifclear || cmd == CM_ifset)
828                     {
829                       char *val = fetch_value (flag);
830                       if (val)
831                         iftrue = 1;
832                       if (cmd == CM_ifclear)
833                         iftrue = !iftrue;
834                     }
835                   else /* cmd == CM_ifcommanddefined
836                           || cmd == CM_ifcommandnotdefined */
837                     {
838                       enum command_id c = lookup_command (flag);
839                       if (c)
840                         iftrue = 1;
841                       if (cmd == CM_ifcommandnotdefined)
842                         iftrue = !iftrue;
843                     }
844                 }
845               else if (0)
846                 {
847               bad_value:
848                   line_error ("bad name for @%s", command_name(cmd));
849                 }
850               free (flag);
851             }
852         }
853       else if (!memcmp (command_name(cmd), "if", 2)) /* e.g. @ifhtml */
854         {
855           int i; char *p;
856           /* Handle @if* and @ifnot* */
857 
858           p = command_name(cmd) + 2; /* After "if". */
859           if (!memcmp (p, "not", 3))
860             p += 3; /* After "not". */
861           for (i = 0; i < sizeof (expanded_formats)/sizeof (*expanded_formats);
862                i++)
863             {
864               if (!strcmp (p, expanded_formats[i].format))
865                 {
866                   iftrue = expanded_formats[i].expandedp;
867                   break;
868                 }
869             }
870           if (!memcmp (command_name(cmd), "ifnot", 5))
871             iftrue = !iftrue;
872         }
873       else
874         bug_message ("unknown conditional command @%s", command_name(cmd));
875 
876 
877       /* If conditional true, push onto conditional stack.  Otherwise
878          open a new element (which we shall later remove, in
879          process_remaining_on_line ("CLOSED conditional")). */
880 
881       debug ("CONDITIONAL %s %d", command_name(cmd), iftrue);
882       if (iftrue)
883         push_conditional_stack (cmd);
884       else
885         {
886           /* Ignored. */
887           ELEMENT *e;
888           e = new_element (ET_NONE);
889           e->cmd = cmd;
890           add_to_element_contents (current, e);
891           current = e;
892         }
893       line = strchr (line, '\0');
894       *get_new_line = 1;
895       goto funexit;
896     }
897   else
898     {
899       ELEMENT *block = 0;
900       if (flags & CF_menu
901           && (current->type == ET_menu_comment
902               || current->type == ET_menu_entry_description))
903         {
904           /* This is for @detailmenu within @menu */
905           ELEMENT *menu = current->parent;
906           if (current->contents.number == 0)
907             destroy_element (pop_element_from_contents (menu));
908 
909           if (pop_context () != ct_preformatted)
910             fatal ("preformatted context expected");
911           if (menu->type == ET_menu_entry)
912             menu = menu->parent;
913           current = menu;
914         }
915 
916       if (flags & CF_def)
917         {
918           ELEMENT *def_line;
919           push_context (ct_def);
920           block = new_element (ET_NONE);
921           block->cmd = cmd;
922           block->line_nr = line_nr;
923           add_to_element_contents (current, block);
924           current = block;
925           def_line = new_element (ET_def_line);
926           def_line->line_nr = line_nr;
927           add_to_element_contents (current, def_line);
928           current = def_line;
929           add_extra_string_dup (current, "def_command", command_name(cmd));
930           add_extra_string_dup (current, "original_def_cmdname",
931                                 command_name(cmd));
932         }
933       else
934         {
935           block = new_element (ET_NONE);
936 
937           block->cmd = cmd;
938           add_to_element_contents (current, block);
939           current = block;
940         }
941 
942       /* Check if 'block args command' */
943       if (command_data(cmd).data != BLOCK_raw)
944         {
945           if (command_data(cmd).flags & CF_preformatted)
946             push_context (ct_preformatted);
947           else if (cmd == CM_displaymath)
948             push_context (ct_math);
949           else if (command_data(cmd).flags & CF_format_raw)
950             {
951               push_context (ct_rawpreformatted);
952               if (!format_expanded_p (command_name(cmd)))
953                 {
954                   ELEMENT *e;
955                   enum command_id dummy;
956                   char *line_dummy;
957 
958                   e = new_element (ET_elided_block);
959                   add_to_element_contents (current, e);
960                   line_dummy = line;
961                   while (!is_end_current_command (current,
962                                                   &line_dummy, &dummy))
963                     {
964                       line = new_line ();
965                       if (!line)
966                         {
967                           line = "";
968                           break;
969                         }
970                       line_dummy = line;
971                     }
972                   e = new_element (ET_empty_line_after_command);
973                   text_append_n (&e->text, "\n", 1);
974                   add_to_element_contents (current, e);
975 
976                   e = new_element (ET_empty_line);
977                   text_append (&e->text, "");
978                   add_to_element_contents (current, e);
979                   goto funexit;
980                 }
981             }
982           else if (command_data(cmd).data == BLOCK_region)
983             {
984               if (current_region_cmd ())
985                 {
986                   line_error ("region %s inside region %s is not allowed",
987                               command_name(cmd),
988                               command_name(current_region_cmd ()));
989                 }
990               push_region (block);
991             }
992 
993           if (command_data(cmd).flags & CF_menu)
994             {
995               if (current_context () == ct_preformatted)
996                 push_context (ct_preformatted);
997               else
998                 push_context (ct_menu);
999 
1000               if (cmd == CM_direntry)
1001                 add_to_contents_as_array (&global_info.dircategory_direntry,
1002                                           block);
1003 
1004               if (current_node)
1005                 {
1006                   if (cmd == CM_direntry && conf.show_menu)
1007                     {
1008                       line_warn ("@direntry after first node");
1009                     }
1010                   else if (cmd == CM_menu)
1011                     {
1012                       if (!(command_flags(current->parent) & CF_root))
1013                         line_warn ("@menu in invalid context");
1014                       /* Add to array of menus for current node.  Currently
1015                          done in Perl code. */
1016                     }
1017                 }
1018             }
1019 
1020           if (cmd == CM_itemize || cmd == CM_enumerate)
1021             counter_push (&count_items, current, 0);
1022           /* Note that no equivalent thing is done in the Perl code, because
1023              'item_count' is assumed to start at 0. */
1024 
1025           {
1026             ELEMENT *bla = new_element (ET_block_line_arg);
1027             add_to_element_args (current, bla);
1028 
1029             if (command_data (current->cmd).data > 1)
1030               {
1031                 counter_push (&count_remaining_args,
1032                               current,
1033                               command_data (current->cmd).data - 1);
1034               }
1035             else if (command_data (current->cmd).data == BLOCK_variadic)
1036               {
1037                 /* Unlimited args */
1038                 counter_push (&count_remaining_args, current,
1039                               COUNTER_VARIADIC);
1040               }
1041 
1042             current = bla;
1043             if (!(command_data(cmd).flags & CF_def))
1044               push_context (ct_line);
1045 
1046             /* Note that an ET_empty_line_after_command gets reparented in the
1047                contents in 'end_line'. */
1048 
1049           }
1050         }
1051       block->line_nr = line_nr;
1052       register_global_command (block);
1053       start_empty_line_after_command (current, &line, block);
1054     }
1055 
1056 funexit:
1057   *line_inout = line;
1058   return current;
1059 }
1060 
1061 ELEMENT *
handle_brace_command(ELEMENT * current,char ** line_inout,enum command_id cmd)1062 handle_brace_command (ELEMENT *current, char **line_inout, enum command_id cmd)
1063 {
1064   char *line = *line_inout;
1065   ELEMENT *e;
1066 
1067   e = new_element (ET_NONE);
1068   e->cmd = cmd;
1069 
1070   /* The line number information is only ever used for brace commands
1071      if the command is given with braces, but it's easier just to always
1072      store the information. */
1073   e->line_nr = line_nr;
1074 
1075   add_to_element_contents (current, e);
1076 
1077   if (cmd == CM_sortas)
1078     {
1079       if (!(command_flags(current->parent) & CF_index_entry_command)
1080           && current->parent->cmd != CM_subentry)
1081         {
1082           line_warn ("@%s should only appear in an index entry",
1083                      command_name(cmd));
1084         }
1085     }
1086 
1087   current = e;
1088 
1089   if (cmd == CM_click)
1090     {
1091       add_extra_string_dup (e, "clickstyle", global_clickstyle);
1092     }
1093   else if (cmd == CM_kbd)
1094     {
1095       if (current_context () == ct_preformatted
1096           && global_kbdinputstyle != kbd_distinct
1097           || global_kbdinputstyle == kbd_code)
1098         {
1099           add_extra_integer (e, "code", 1);
1100         }
1101       else if (global_kbdinputstyle == kbd_example)
1102         {
1103           /* TODO: Understand what is going on here. */
1104           ELEMENT *tmp = current->parent;
1105           while (tmp->parent
1106                  && (command_flags(tmp->parent) & CF_brace)
1107                  && command_data(tmp->parent->cmd).data != BRACE_context)
1108             {
1109               if (command_flags(tmp->parent) & CF_code_style)
1110                 {
1111                   add_extra_integer (e, "code", 1);
1112                   break;
1113                 }
1114               tmp = tmp->parent->parent;
1115             }
1116         }
1117     }
1118   else if (command_data(cmd).flags & CF_INFOENCLOSE)
1119     {
1120       INFO_ENCLOSE *ie = lookup_infoenclose (cmd);
1121       if (ie)
1122         {
1123           add_extra_string_dup (e, "begin", ie->begin);
1124           add_extra_string_dup (e, "end", ie->end);
1125         }
1126       e->type = ET_definfoenclose_command;
1127     }
1128 
1129   *line_inout = line;
1130   return current;
1131 }
1132