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 #include <config.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20
21 #include "parser.h"
22 #include "text.h"
23 #include "convert.h"
24 #include "input.h"
25 #include "labels.h"
26
27 ELEMENT *
handle_open_brace(ELEMENT * current,char ** line_inout)28 handle_open_brace (ELEMENT *current, char **line_inout)
29 {
30 char *line = *line_inout;
31
32 abort_empty_line (¤t, NULL);
33 if (command_flags(current) & CF_brace)
34 {
35 enum command_id command;
36 ELEMENT *arg;
37
38 command = current->cmd;
39 counter_push (&count_remaining_args, current,
40 command_data(current->cmd).data);
41 counter_dec (&count_remaining_args);
42
43 arg = new_element (ET_NONE);
44 add_to_element_args (current, arg);
45 current = arg;
46
47 if (command == CM_verb)
48 {
49 current->type = ET_brace_command_arg;
50 /* Save the deliminating character in 'type'. */
51 if (!*line || *line == '\n')
52 {
53 line_error ("@verb without associated character");
54 add_extra_string_dup (current->parent, "delimiter", "");
55 current->parent->type = 0;
56 }
57 else
58 {
59 static char c[2];
60 c[0] = *line++;
61 add_extra_string_dup (current->parent, "delimiter", c);
62 }
63 }
64 else if (command_data(command).data == BRACE_context)
65 {
66 if (command == CM_caption || command == CM_shortcaption)
67 {
68 #define float floatxx
69 ELEMENT *float;
70 if (!current->parent->parent
71 || current->parent->parent->cmd != CM_float)
72 {
73 float = current->parent;
74 while (float->parent && float->cmd != CM_float)
75 float = float->parent;
76 if (float->cmd != CM_float)
77 {
78 line_error ("@%s is not meaningful outside "
79 "`@float' environment",
80 command_name(command));
81 float = 0;
82 }
83 else
84 line_warn ("@%s should be right below `@float'",
85 command_name(command));
86 }
87 else
88 float = current->parent->parent;
89 if (float)
90 {
91 if (lookup_extra (float, command_name(command)))
92 line_warn ("ignoring multiple @%s",
93 command_name(command));
94 else
95 {
96 add_extra_element (current->parent, "float", float);
97 add_extra_element (float, command_name(command),
98 current->parent);
99 }
100 }
101 #undef float
102 }
103
104 /* Add to context stack. */
105 switch (command)
106 {
107 case CM_footnote:
108 push_context (ct_footnote);
109 break;
110 case CM_caption:
111 push_context (ct_caption);
112 break;
113 case CM_shortcaption:
114 push_context (ct_shortcaption);
115 break;
116 case CM_math:
117 push_context (ct_math);
118 break;
119 default:
120 fatal ("no context for command");
121 }
122
123 {
124 ELEMENT *e;
125 int n;
126 n = strspn (line, whitespace_chars_except_newline);
127 e = new_element (ET_empty_spaces_before_argument);
128 text_append_n (&e->text, line, n);
129 add_to_element_contents (current, e);
130 add_extra_element (e, "command", current->parent);
131 line += n;
132 }
133 current->type = ET_brace_command_context;
134 }
135 else /* not context brace */
136 {
137 current->type = ET_brace_command_arg;
138
139 /* Commands which are said to take a positive number of arguments
140 disregard leading and trailing whitespace. */
141 if (command_data(command).data > 0)
142 {
143 ELEMENT *e;
144 e = new_element (ET_empty_spaces_before_argument);
145 /* See comment in parser.c:merge_text */
146 text_append (&e->text, "");
147 add_to_element_contents (current, e);
148 add_extra_element (e, "command", current);
149
150 if (command == CM_inlineraw)
151 push_context (ct_inlineraw);
152 }
153 }
154 debug ("OPENED");
155 }
156 else if (current->parent && (current->parent->cmd == CM_multitable
157 || current->parent->type == ET_def_line))
158 {
159 ELEMENT *b, *e;
160 b = new_element (ET_bracketed);
161 add_to_element_contents (current, b);
162 current = b;
163
164 /* We need the line number here in case @ protects the
165 end of the line. */
166 if (current->parent->parent->type == ET_def_line)
167 current->line_nr = line_nr;
168
169 e = new_element (ET_empty_spaces_before_argument);
170 text_append (&e->text, ""); /* See comment in parser.c:merge_text */
171 add_to_element_contents (current, e);
172 debug ("BRACKETED in def/multitable");
173 add_extra_element (e, "command", current);
174 }
175 else if (current->type == ET_rawpreformatted)
176 {
177 ELEMENT *e = new_element (ET_NONE);
178 text_append (&e->text, "{");
179 add_to_element_contents (current, e);
180 }
181 else if (current_context() == ct_math
182 || current_context() == ct_rawpreformatted
183 || current_context() == ct_inlineraw)
184 {
185 ELEMENT *b = new_element (ET_bracketed);
186 b->line_nr = line_nr;
187 add_to_element_contents (current, b);
188 current = b;
189 debug ("BRACKETED in math");
190 }
191 else
192 {
193 line_error ("misplaced {");
194 if (current->contents.number > 0
195 && last_contents_child(current)->type
196 == ET_empty_spaces_before_argument)
197 {
198 /* FIXME: Is this right? */
199 remove_from_contents (current, 0);
200 }
201 }
202
203 *line_inout = line;
204 return current;
205 }
206
207 /* Return 1 if an element is all whitespace.
208 Note that this function isn't completely reliable because it
209 doesn't look deep into the element tree.
210 In the perl code it calls
211 Texinfo::Convert::NodeNameNormalization::normalize_node,
212 and checks that the result isn't all hyphens.
213 */
214 int
check_empty_expansion(ELEMENT * e)215 check_empty_expansion (ELEMENT *e)
216 {
217 int i;
218 for (i = 0; i < e->contents.number; i++)
219 {
220 ELEMENT *f = e->contents.list[i];
221 if (!(
222 f->cmd == CM_SPACE
223 || f->cmd == CM_TAB
224 || f->cmd == CM_NEWLINE
225 || f->cmd == CM_c
226 || f->cmd == CM_comment
227 || f->cmd == CM_COLON
228 || f->type == ET_empty_spaces_before_argument
229 || f->type == ET_spaces_at_end
230 || (!f->cmd && !f->type && f->text.end == 0)
231 || (f->text.end > 0
232 && !*(f->text.text + strspn (f->text.text, whitespace_chars)))
233 ))
234 {
235 return 0;
236 }
237 }
238 return 1;
239 }
240
241 ELEMENT *
handle_close_brace(ELEMENT * current,char ** line_inout)242 handle_close_brace (ELEMENT *current, char **line_inout)
243 {
244 char *line = *line_inout;
245
246 abort_empty_line (¤t, NULL);
247
248 if (current->type == ET_bracketed)
249 {
250 /* Used in @math */
251 current = current->parent;
252 goto funexit;
253 }
254 else if (command_flags(current->parent) & CF_brace)
255 {
256 enum command_id closed_command;
257 if (command_data(current->parent->cmd).data == BRACE_context)
258 {
259 (void) pop_context ();
260 /* The Perl code here checks that the popped context and the
261 parent command match as strings. */
262 }
263 else if (command_data(current->parent->cmd).data > 0)
264 {
265 /* @inline* always have end spaces considered as normal text */
266 if (!(command_flags(current->parent) & CF_inline))
267 isolate_last_space (current);
268 }
269
270 closed_command = current->parent->cmd;
271 debug ("CLOSING(brace) %s", command_data(closed_command).cmdname);
272 counter_pop (&count_remaining_args);
273
274 if (current->contents.number > 0
275 && command_data(closed_command).data == 0)
276 line_warn ("command @%s does not accept arguments",
277 command_name(closed_command));
278
279 if (closed_command == CM_anchor)
280 {
281 NODE_SPEC_EXTRA *parsed_anchor;
282 current->parent->line_nr = line_nr;
283 parsed_anchor = parse_node_manual (current);
284 if (check_node_label (parsed_anchor, CM_anchor))
285 {
286 register_label (current->parent, parsed_anchor->node_content);
287 if (current_region ())
288 add_extra_element (current, "region", current_region ());
289 if (parsed_anchor->manual_content)
290 destroy_element (parsed_anchor->manual_content);
291 }
292 free (parsed_anchor);
293 }
294 else if (command_data(closed_command).flags & CF_ref)
295 {
296 ELEMENT *ref = current->parent;
297 if (ref->args.number > 0)
298 {
299 if ((closed_command == CM_inforef
300 && (ref->args.number <= 0
301 || ref->args.list[0]->contents.number == 0)
302 && (ref->args.number <= 2
303 || ref->args.list[2]->contents.number == 0))
304 || (closed_command != CM_inforef
305 && (ref->args.number <= 0
306 || ref->args.list[0]->contents.number == 0)
307 && (ref->args.number <= 3
308 || ref->args.list[3]->contents.number == 0)
309 && (ref->args.number <= 4
310 || ref->args.list[4]->contents.number == 0)))
311 {
312 line_warn ("command @%s missing a node or external manual "
313 "argument", command_name(closed_command));
314 }
315 else
316 {
317 NODE_SPEC_EXTRA *nse;
318 nse = parse_node_manual (args_child_by_index (ref, 0));
319 if (nse && (nse->manual_content || nse->node_content))
320 add_extra_node_spec (ref, "node_argument", nse);
321 else
322 {
323 if (nse->manual_content)
324 destroy_element (nse->manual_content);
325 if (nse->node_content)
326 destroy_element (nse->node_content);
327 free (nse);
328 }
329 if (closed_command != CM_inforef
330 && (ref->args.number <= 3
331 || ref->args.number <= 4
332 && ref->args.list[3]->contents.number == 0
333 || (ref->args.list[3]->contents.number == 0
334 && ref->args.list[4]->contents.number == 0))
335 && !nse->manual_content)
336 {
337 remember_internal_xref (ref);
338 }
339 }
340
341 if (ref->args.number > 1
342 && ref->args.list[1]->contents.number > 0)
343 {
344 if (check_empty_expansion (ref->args.list[1]))
345 {
346 char *texi = 0;
347 if (ref->args.list[1])
348 texi = convert_to_texinfo (ref->args.list[1]);
349
350 line_warn ("in @%s empty cross reference name "
351 "after expansion `%s'",
352 command_name(closed_command),
353 ref->args.list[1] ? texi : "");
354 free (texi);
355 }
356 }
357
358 if (closed_command != CM_inforef
359 && ref->args.number > 2
360 && ref->args.list[2]->contents.number > 0)
361 {
362 if (check_empty_expansion (ref->args.list[2]))
363 {
364 char *texi = 0;
365 if (ref->args.list[2])
366 texi = convert_to_texinfo (ref->args.list[2]);
367
368 line_warn ("in @%s empty cross reference title "
369 "after expansion `%s'",
370 command_name(closed_command),
371 ref->args.list[2] ? texi : "");
372 free (texi);
373 }
374 }
375 }
376 }
377 else if (closed_command == CM_image)
378 {
379 ELEMENT *image = current->parent;
380 if (image->args.number == 0
381 || image->args.list[0]->contents.number == 0)
382 {
383 line_error ("@image missing filename argument");
384 }
385 if (global_info.input_perl_encoding)
386 add_extra_string_dup (image, "input_perl_encoding",
387 global_info.input_perl_encoding);
388 }
389 else if (closed_command == CM_dotless)
390 {
391 if (current->contents.number > 0)
392 {
393 char *text = current->contents.list[0]->text.text;
394 if (!text || (strcmp (text, "i") && strcmp (text, "j")))
395 {
396 line_error ("@dotless expects `i' or `j' as argument, "
397 "not `%s'", text);
398 }
399 }
400 }
401 else if ((command_data(closed_command).flags & CF_inline)
402 || closed_command == CM_abbr
403 || closed_command == CM_acronym)
404 {
405 if (current->parent->cmd == CM_inlineraw)
406 {
407 if (ct_inlineraw != pop_context ())
408 fatal ("expected inlineraw context");
409 }
410 if (current->parent->args.number == 0
411 || current->parent->args.list[0]->contents.number == 0)
412 {
413 line_warn ("@%s missing first argument",
414 command_name(current->parent->cmd));
415 }
416 }
417 else if (closed_command == CM_errormsg)
418 {
419 char *arg = current->contents.list[0]->text.text;
420 if (arg)
421 line_error (arg);
422 }
423 else if (closed_command == CM_U)
424 {
425 if (current->contents.number == 0)
426 {
427 line_warn ("no argument specified for @U");
428 }
429 else
430 {
431 char *arg = current->contents.list[0]->text.text;
432 int n = strspn (arg, "0123456789ABCDEFabcdef");
433 if (arg[n])
434 {
435 line_error ("non-hex digits in argument for @U: %s", arg);
436 }
437 else if (n < 4)
438 {
439 line_warn
440 ("fewer than four hex digits in argument for @U: %s", arg);
441 }
442 else
443 {
444 unsigned long int val;
445 int ret = sscanf (arg, "%lx", &val);
446 if (ret != 1)
447 {
448 debug ("hex sscanf failed %s", arg);
449 /* unknown error. possibly argument is too large
450 for an int. */
451 }
452 if (ret != 1 || val > 0x10FFFF)
453 {
454 line_error
455 ("argument for @U exceeds Unicode maximum 0x10FFFF: %s",
456 arg);
457 }
458 }
459
460 }
461 }
462 else if (command_with_command_as_argument (current->parent->parent)
463 && current->contents.number == 0)
464 {
465 debug ("FOR PARENT ... command_as_argument_braces ...");
466 if (!current->parent->type)
467 current->parent->type = ET_command_as_argument;
468 add_extra_element (current->parent->parent->parent,
469 "command_as_argument", current->parent);
470 }
471 else if (current->parent->cmd == CM_sortas
472 || current->parent->cmd == CM_seeentry
473 || current->parent->cmd == CM_seealso)
474 {
475 ELEMENT *index_elt;
476 if (current->parent->parent
477 && current->parent->parent->parent
478 && ((command_flags(current->parent->parent->parent)
479 & CF_index_entry_command)
480 || current->parent->parent->parent->cmd == CM_subentry))
481 {
482 index_elt = current->parent->parent->parent;
483 if (current->parent->cmd == CM_sortas)
484 {
485 int superfluous_arg;
486 char *arg = convert_to_text (current, &superfluous_arg);
487 if (arg && *arg)
488 {
489 add_extra_string (index_elt,
490 command_name(current->parent->cmd),
491 arg);
492 }
493 }
494 else
495 {
496 add_extra_element (index_elt,
497 command_name(current->parent->cmd),
498 current->parent);
499 }
500 }
501 }
502 register_global_command (current->parent);
503
504 if (current->parent->cmd == CM_anchor
505 || current->parent->cmd == CM_hyphenation
506 || current->parent->cmd == CM_caption
507 || current->parent->cmd == CM_shortcaption
508 || current->parent->cmd == CM_sortas
509 || current->parent->cmd == CM_seeentry
510 || current->parent->cmd == CM_seealso)
511 {
512 ELEMENT *e;
513 e = new_element (ET_empty_spaces_after_close_brace);
514 text_append (&e->text, "");
515 add_to_element_contents (current->parent->parent, e);
516 }
517
518 current = current->parent->parent;
519 if (close_preformatted_command(closed_command))
520 current = begin_preformatted (current);
521 } /* CF_brace */
522 else if (current->type == ET_rawpreformatted)
523 {
524 /* lone right braces are accepted in a rawpreformatted */
525 ELEMENT *e = new_element (ET_NONE);
526 text_append_n (&e->text, "}", 1);
527 add_to_element_contents (current, e);
528 goto funexit;
529 }
530 /* context brace command (e.g. @footnote) when there is a paragraph inside */
531 else if (current_context() == ct_footnote
532 || current_context() == ct_caption
533 || current_context() == ct_shortcaption
534 || current_context() == ct_math)
535 {
536 current = end_paragraph (current, 0, 0);
537 if (current->parent
538 && (command_flags(current->parent) & CF_brace)
539 && (command_data(current->parent->cmd).data == BRACE_context))
540 {
541 enum command_id closed_command;
542 (void) pop_context ();
543 debug ("CLOSING(context command)");
544 closed_command = current->parent->cmd;
545 counter_pop (&count_remaining_args);
546
547 register_global_command (current->parent);
548 current = current->parent->parent;
549 if (close_preformatted_command(closed_command))
550 current = begin_preformatted (current);
551 }
552 }
553 else
554 {
555 line_error ("misplaced }");
556 goto funexit;
557 }
558
559 funexit:
560 *line_inout = line;
561 return current;
562 }
563
564
565 /* Handle a comma separating arguments to a Texinfo command. */
566 ELEMENT *
handle_comma(ELEMENT * current,char ** line_inout)567 handle_comma (ELEMENT *current, char **line_inout)
568 {
569 char *line = *line_inout;
570 enum element_type type;
571 ELEMENT *new_arg, *e;
572
573 abort_empty_line (¤t, NULL);
574 isolate_last_space (current);
575
576 type = current->type;
577 current = current->parent;
578
579 if (command_flags(current) & CF_inline)
580 {
581 KEY_PAIR *k;
582 int expandp = 0;
583 debug ("THE INLINE PART");
584 k = lookup_extra (current, "format");
585 if (!k)
586 {
587 ELEMENT *arg = 0;
588 char *inline_type = 0;
589 if (current->args.number > 0
590 && current->args.list[0]->contents.number > 0
591 && (arg = current->args.list[0]->contents.list[0]))
592 {
593 if (arg->text.end > 0)
594 inline_type = arg->text.text;
595 }
596
597 debug ("INLINE <%s>", inline_type);
598 if (!inline_type)
599 {
600 /* Condition is missing */
601 debug ("INLINE COND MISSING");
602 }
603 else if (current->cmd == CM_inlineraw
604 || current->cmd == CM_inlinefmt
605 || current->cmd == CM_inlinefmtifelse)
606 {
607 if (format_expanded_p (inline_type))
608 {
609 expandp = 1;
610 add_extra_integer (current, "expand_index", 1);
611 }
612 else
613 expandp = 0;
614 }
615 else if (current->cmd == CM_inlineifset
616 || current->cmd == CM_inlineifclear)
617 {
618 expandp = 0;
619 if (fetch_value (inline_type))
620 expandp = 1;
621 if (current->cmd == CM_inlineifclear)
622 expandp = !expandp;
623 if (expandp)
624 add_extra_integer (current, "expand_index", 1);
625 }
626 else
627 expandp = 0;
628
629 if (inline_type)
630 add_extra_string_dup (current, "format", inline_type);
631 else
632 add_extra_string (current, "format", 0);
633
634 /* Skip first argument for a false @inlinefmtifelse */
635 if (!expandp && current->cmd == CM_inlinefmtifelse)
636 {
637 ELEMENT *e;
638 int brace_count = 1;
639
640 add_extra_integer (current, "expand_index", 2);
641
642 /* Add a dummy argument for the first argument. */
643 e = new_element (ET_elided);
644 add_to_element_args (current, e);
645
646 /* Scan forward to get the next argument. */
647 while (brace_count > 0)
648 {
649 line += strcspn (line, "{},");
650 switch (*line)
651 {
652 case ',':
653 if (brace_count == 1)
654 {
655 line++;
656 goto inlinefmtifelse_done;
657 }
658 break;
659 case '{':
660 brace_count++;
661 break;
662 case '}':
663 brace_count--;
664 break;
665 default:
666 line = next_text ();
667 if (!line)
668 goto funexit;
669 continue;
670 }
671 line++;
672 }
673 inlinefmtifelse_done:
674 /* Check if the second argument is missing. */
675 if (brace_count == 0)
676 {
677 line--; /* on '}' */
678 }
679
680 counter_dec (&count_remaining_args);
681 expandp = 1;
682 }
683 }
684 else if (current->cmd == CM_inlinefmtifelse)
685 {
686 /* Second art of @inlinefmtifelse when condition is true. Discard
687 second argument. */
688 expandp = 0;
689 }
690
691 /* If this command is not being expanded, add a dummy argument, and
692 scan forward to the closing brace. */
693 if (!expandp)
694 {
695 static char *alloc_line;
696 ELEMENT *e;
697 int brace_count = 1;
698 e = new_element (ET_elided);
699 add_to_element_args (current, e);
700 while (brace_count > 0)
701 {
702 line += strcspn (line, "{}");
703 switch (*line)
704 {
705 case '{':
706 brace_count++;
707 break;
708 case '}':
709 brace_count--;
710 break;
711 default:
712 free (alloc_line);
713 alloc_line = next_text ();
714 if (!alloc_line)
715 goto funexit;
716 line = alloc_line;
717 continue;
718 }
719 line++;
720 }
721 current = last_args_child (current);
722 line--; /* on '}' */
723 goto funexit;
724 }
725 }
726
727 if (counter_value (&count_remaining_args, current) != COUNTER_VARIADIC)
728 counter_dec (&count_remaining_args);
729 new_arg = new_element (type);
730 add_to_element_args (current, new_arg);
731 current = new_arg;
732 e = new_element (ET_empty_spaces_before_argument);
733 text_append (&e->text, ""); /* See comment in parser.c:merge_text */
734 add_to_element_contents (current, e);
735 add_extra_element (e, "command", current);
736
737 funexit:
738 *line_inout = line;
739 return current;
740 }
741
742 /* Actions to be taken when a special character appears in the input. */
743 ELEMENT *
handle_separator(ELEMENT * current,char separator,char ** line_inout)744 handle_separator (ELEMENT *current, char separator, char **line_inout)
745 {
746 char *line = *line_inout;
747
748 if (separator == '{')
749 {
750 current = handle_open_brace (current, &line);
751 }
752 else if (separator == '}')
753 {
754 current = handle_close_brace (current, &line);
755 }
756 /* If a comma is seen after all the arguments for the command have been
757 read, it is included in the last argument. */
758 else if (separator == ','
759 && counter_value (&count_remaining_args, current->parent) > 0)
760 {
761 current = handle_comma (current, &line);
762 }
763 else if (separator == ',' && current->type == ET_line_arg
764 && current->parent->cmd == CM_node)
765 {
766 line_warn ("superfluous arguments for node");
767 }
768 /* After a separator in a menu. */
769 else if ((separator == ','
770 || separator == '\t'
771 || separator == '.')
772 && current->type == ET_menu_entry_node
773 || separator == ':' && current->type == ET_menu_entry_name)
774 {
775 ELEMENT *e;
776
777 current = current->parent;
778 e = new_element (ET_menu_entry_separator);
779 text_append_n (&e->text, &separator, 1);
780 add_to_element_args (current, e);
781
782 /* Note in 'handle_menu' in menus.c, if a '.' is not followed by
783 whitespace, we revert was was done here. */
784 }
785 else if (separator == '\f' && current->type == ET_paragraph)
786 {
787 ELEMENT *e;
788
789 /* A form feed stops and restarts a paragraph. */
790 current = end_paragraph (current, 0, 0);
791 e = new_element (ET_empty_line);
792 text_append_n (&e->text, "\f", 1);
793 add_to_element_contents (current, e);
794 e = new_element (ET_empty_line);
795 add_to_element_contents (current, e);
796 }
797 else
798 {
799 /* Default - merge the character as usual. */
800 char t[2];
801 t[0] = separator;
802 t[1] = '\0';
803 current = merge_text (current, t);
804 }
805
806 *line_inout = line;
807 return current;
808 }
809