diff options
Diffstat (limited to 'macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi')
47 files changed, 895 insertions, 7747 deletions
diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parser-5556.pm b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parser-5556.pm deleted file mode 100644 index 5b14993722..0000000000 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parser-5556.pm +++ /dev/null @@ -1,7078 +0,0 @@ -# Parser.pm: parse texinfo code into a tree. -# -# Copyright 2010, 2011, 2012 Free Software Foundation, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, -# or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -# Original author: Patrice Dumas <pertusus@free.fr> -# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init. - -# The organization of the file is the following: -# module definitions. -# default parser state. With explanation of the internal structures. -# initializations, determination of command types. -# user visible subroutines. -# internal subroutines, doing the parsing. - -package Texinfo::Parser; - -# We need the unicode stuff. -use 5.006; -use strict; - -# debug -use Carp qw(cluck); - -use Data::Dumper; - -# to detect if an encoding may be used to open the files -use Encode; - -# for fileparse -use File::Basename; - -#use POSIX qw(setlocale LC_ALL LC_CTYPE LC_MESSAGES); - -# commands definitions -use Texinfo::Common; -# Error reporting and counting, translation of strings. -use Texinfo::Report; -# encoding_alias -use Texinfo::Encoding; - -# to expand file names in @include and similar @-commands -use Texinfo::Convert::Text; -# to normalize node name, anchor, float arg, listoffloats and first *ref argument. -use Texinfo::Convert::NodeNameNormalization; -# in error messages, and for macro body expansion -use Texinfo::Convert::Texinfo; - -require Exporter; -use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); -@ISA = qw(Exporter Texinfo::Report); - -# Items to export into callers namespace by default. Note: do not export -# names by default without a very good reason. Use EXPORT_OK instead. -# Do not simply export all your public functions/methods/constants. - -# This allows declaration use Texinfo::Parser ':all'; -# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK -# will save memory. -%EXPORT_TAGS = ( 'all' => [ qw( - parser - parse_texi_text - parse_texi_line - parse_texi_file - indices_information - floats_information - internal_references_information - labels_information - global_commands_information - global_informations -) ] ); - -@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); - -@EXPORT = qw( -); - -$VERSION = '6.7'; - -sub N__($) -{ - return $_[0]; -} - -#sub __($$) -#{ -# my $self = shift; -# return &{$self->{'gettext'}}(@_); -#} - -# Customization variables obeyed by the Parser, and the default values. -our %default_customization_values = ( - 'TEST' => 0, - 'DEBUG' => 0, # if >= 10, tree is printed in texi2any.pl after parsing. - # If >= 100 tree is printed every line. - 'SHOW_MENU' => 1, # if false no menu error related. - 'INLINE_INSERTCOPYING' => 0, - 'IGNORE_BEFORE_SETFILENAME' => 1, - 'MACRO_BODY_IGNORES_LEADING_SPACE' => 0, - 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME' => 1, - 'INPUT_PERL_ENCODING' => undef, # input perl encoding name, set from - # @documentencoding in the default case - 'INPUT_ENCODING_NAME' => undef, # encoding name normalized as preferred - # IANA, set from @documentencoding in the default - # case - 'CPP_LINE_DIRECTIVES' => 1, # handle cpp like synchronization lines - 'MAX_MACRO_CALL_NESTING' => 100000, # max number of nested macro calls - 'GLOBAL_COMMANDS' => [], # list of commands registered - # This is not used directly, but passed to Convert::Text through - # Texinfo::Common::_convert_text_options - 'ENABLE_ENCODING' => 1, # output accented and special characters - # based on @documentencoding - # following are used in Texinfo::Structuring - 'TOP_NODE_UP' => '(dir)', # up node of Top node - 'SIMPLE_MENU' => 0, # not used in the parser but in structuring - 'USE_UP_NODE_FOR_ELEMENT_UP' => 0, # Use node up for Up if there is no - # section up. -); - -my %parser_default_configuration = (%Texinfo::Common::default_parser_state_configuration, - %default_customization_values); - -# the other possible keys for the parser state are: -# -# expanded_formats_hash each key comes from expanded_formats value is 1 -# index_names a structure holding the link between index -# names, prefixes, merged indices, -# initial value is %index_names in Texinfo::Common. -# context_stack stack of the contexts, more recent on top. -# 'line' is added when on a line or -# block @-command line, -# 'def' is added instead if on a definition line. -# 'preformatted' is added in block commands -# where there is no paragraphs and spaces are kept -# (format, example, display...) -# 'rawpreformatted' is added in raw block commands -# (html, xml, docbook...) -# 'menu' is added in menu commands -# 'math', 'footnote', 'caption', 'shortcaption', -# 'inlineraw' are also added when in those commands -# conditionals_stack a stack of conditional commands that are expanded. -# raw_formats_stack a stack of 1 or 0 for raw formats (@html... or -# @inlineraw), is 0 if within a raw format that is -# not expanded. -# macro_stack stack of macros being expanded (more recent first) -# definfoenclose an hash, key is the command name, value is an array -# reference with 2 values, beginning and ending. -# input a stack, with last at bottom. Holds the opened files -# or text. Pending macro expansion or text expansion -# is also in that structure. -# misc_commands the same than %misc_commands in Texinfo::Common, -# but with index entry commands dynamically added -# close_paragraph_commands same than %close_paragraph_commands, but with -# insertcopying removed if INLINE_INSERTCOPYING -# close_preformatted_commands same than %close_preformatted_commands, but with -# insertcopying removed if INLINE_INSERTCOPYING -# no_paragraph_commands the same than %default_no_paragraph_commands -# below, with index -# entry commands dynamically added -# simple_text_commands the same than %simple_text_commands below, but -# with index entry commands dynamically added -# current_node last seen node. -# current_section last seen section. -# nodes list of nodes. -# command_index_prefix associate a command name to an index prefix. -# prefix_to_index_name associate an index prefix to the index name. -# floats key is the normalized float type, value is an array -# reference holding all the floats. -# internal_references an array holding all the internal references. - -# set points to the value set when initializing, for -# configuration items that are not to be overriden -# by @-commands. For example documentlanguage. - -# A line information is an hash reference with the keys: -# line_nr the line number -# file_name the file name -# macro if in a macro expansion, the name of the macro -# -# A text fragment information is a 2 element array reference, the first is the -# text fragment, the second is the line information. - -# The input structure is an array, the first is the most recently included -# file. The last element may be a file if the parsing is done on a file, -# with parse_texi_file, or simply pending text, if called as parse_texi_text. -# each element of the array is a hash reference. The key are: -# pending an array reference containing pending text fragments, either the -# text given as parse_texi_text or macro expansion text. -# name file name -# line_nr current line number in the file -# fh filehandle for the file - -# content is not copied but reference is copied when duplicating a parser. -my %tree_informations; -foreach my $tree_information ('values', 'macros', 'explained_commands', 'labels') { - $tree_informations{$tree_information} = 1; -} - -# The commands in initialization_overrides are not set in the document if -# set at the parser initialization. -my %initialization_overrides = ( - 'INPUT_ENCODING_NAME' => 1, - 'documentlanguage' => 1, -); - -my %no_brace_commands = %Texinfo::Common::no_brace_commands; -my %misc_commands = %Texinfo::Common::misc_commands; -my %brace_commands = %Texinfo::Common::brace_commands; -my %accent_commands = %Texinfo::Common::accent_commands; -my %context_brace_commands = %Texinfo::Common::context_brace_commands; -my %block_commands = %Texinfo::Common::block_commands; -my %block_item_commands = %Texinfo::Common::block_item_commands; -my %close_paragraph_commands = %Texinfo::Common::close_paragraph_commands; -my %def_map = %Texinfo::Common::def_map; -my %def_commands = %Texinfo::Common::def_commands; -my %def_aliases = %Texinfo::Common::def_aliases; -my %menu_commands = %Texinfo::Common::menu_commands; -my %preformatted_commands = %Texinfo::Common::preformatted_commands; -my %format_raw_commands = %Texinfo::Common::format_raw_commands; -my %item_container_commands = %Texinfo::Common::item_container_commands; -my %item_line_commands = %Texinfo::Common::item_line_commands; -my %deprecated_commands = %Texinfo::Common::deprecated_commands; -my %root_commands = %Texinfo::Common::root_commands; -my %sectioning_commands = %Texinfo::Common::sectioning_commands; -my %command_index_prefix = %Texinfo::Common::command_index_prefix; -my %command_structuring_level = %Texinfo::Common::command_structuring_level; -my %ref_commands = %Texinfo::Common::ref_commands; -my %region_commands = %Texinfo::Common::region_commands; -my %code_style_commands = %Texinfo::Common::code_style_commands; -my %in_heading_commands = %Texinfo::Common::in_heading_commands; -my %explained_commands = %Texinfo::Common::explained_commands; -my %inline_format_commands = %Texinfo::Common::inline_format_commands; -my %inline_commands = %Texinfo::Common::inline_commands; -my %inline_conditional_commands = %Texinfo::Common::inline_conditional_commands; -my %all_commands = %Texinfo::Common::all_commands; - -# equivalence between a @set flag and an @@-command -my %set_flag_command_equivalent = ( - 'txicodequoteundirected' => 'codequoteundirected', - 'txicodequotebacktick' => 'codequotebacktick', -# 'txideftypefnnl' => 'deftypefnnewline', -); - - -# keep line information for those commands. -my %keep_line_nr_brace_commands = %context_brace_commands; -foreach my $keep_line_nr_brace_command ('titlefont', 'anchor') { - $keep_line_nr_brace_commands{$keep_line_nr_brace_command} = 1; -} -foreach my $brace_command (keys (%brace_commands)) { - $keep_line_nr_brace_commands{$brace_command} = 1 - if ($brace_commands{$brace_command} > 1); -} - -my %type_with_paragraph; -foreach my $type ('before_item', 'text_root', 'document_root', - 'brace_command_context') { - $type_with_paragraph{$type} = 1; -} - -my %command_ignore_space_after; -foreach my $command ('anchor', 'hyphenation', 'caption', 'shortcaption') { - $command_ignore_space_after{$command} = 1; -} - -my %global_multiple_commands; -foreach my $global_multiple_command ( - 'author', 'footnote', 'hyphenation', 'insertcopying', 'printindex', - 'subtitle','titlefont', 'listoffloats', 'detailmenu', 'part', - keys(%Texinfo::Common::document_settable_at_commands), ) { - $global_multiple_commands{$global_multiple_command} = 1; -} - -my %global_unique_commands; -foreach my $global_unique_command ( - 'copying', 'settitle', - 'shorttitlepage', 'title', 'titlepage', 'top', - keys(%Texinfo::Common::document_settable_unique_at_commands), ) { - $global_unique_commands{$global_unique_command} = 1; -} - -my %index_names = %Texinfo::Common::index_names; - -# index names that cannot be set by the user. -my %forbidden_index_name = (); - -foreach my $name(keys(%index_names)) { - foreach my $prefix (@{$index_names{$name}->{'prefix'}}) { - $forbidden_index_name{$prefix} = 1; - } -} - -foreach my $other_forbidden_index_name ('info','ps','pdf','htm', - 'html', 'log','aux','dvi','texi','txi','texinfo','tex','bib') { - $forbidden_index_name{$other_forbidden_index_name} = 1; -} - -# @-commands that do not start a paragraph -my %default_no_paragraph_commands; -# @-commands that should be at a line beginning -my %begin_line_commands; - -foreach my $command ('node', 'end') { - $begin_line_commands{$command} = $command; -} - -foreach my $no_paragraph_command ('titlefont', 'caption', 'shortcaption', - 'image', '*', 'hyphenation', 'anchor', 'errormsg') { - $default_no_paragraph_commands{$no_paragraph_command} = 1; -} - -foreach my $no_paragraph_command (keys(%misc_commands)) { - $default_no_paragraph_commands{$no_paragraph_command} = 1; - $begin_line_commands{$no_paragraph_command} = 1; -} - -# verbatiminclude is not said to begin at the beginning of the line -# in the manual -foreach my $misc_not_begin_line ('comment', 'c', 'sp', 'refill', - 'noindent', 'indent', 'columnfractions', - 'tab', 'item', 'headitem', 'verbatiminclude', - 'set', 'clear', - 'vskip', keys(%in_heading_commands)) { - delete $begin_line_commands{$misc_not_begin_line}; -} - -my %block_arg_commands; -foreach my $block_command (keys(%block_commands)) { - $begin_line_commands{$block_command} = 1; - $default_no_paragraph_commands{$block_command} = 1; - $block_arg_commands{$block_command} = 1 - if ($block_commands{$block_command} ne 'raw'); -# and ! $format_raw_commands{$block_command}); -} - -my %close_preformatted_commands = %close_paragraph_commands; -foreach my $no_close_preformatted('sp') { - delete $close_preformatted_commands{$no_close_preformatted}; -} -# FIXME to close preformated or not to close? -#foreach my $format_raw_command(keys(%format_raw_commands)) { -# $close_preformatted_commands{$format_raw_command} = 1; -#} - -# commands that may appear in accents -my %in_accent_commands = %accent_commands; -foreach my $brace_command(keys(%brace_commands)) { - $in_accent_commands{$brace_command} = 1 if (!$brace_commands{$brace_command}); -} -foreach my $no_brace_command (keys(%no_brace_commands)) { - $in_accent_commands{$no_brace_command} = 1; -} -$in_accent_commands{'c'} = 1; -$in_accent_commands{'comment'} = 1; - -# commands that may appear in texts arguments -my %in_full_text_commands; -foreach my $command (keys(%brace_commands), keys(%no_brace_commands)) { - $in_full_text_commands{$command} = 1; -} -foreach my $misc_command_in_full_text('c', 'comment', 'refill', 'noindent', - 'indent', 'columnfractions', 'set', 'clear', 'end') { - $in_full_text_commands{$misc_command_in_full_text} = 1; -} - -foreach my $out_format (keys(%format_raw_commands)) { - $in_full_text_commands{$out_format} = 1; -} -delete $in_full_text_commands{'caption'}; -delete $in_full_text_commands{'shortcaption'}; -foreach my $block_command (keys(%block_commands)) { - $in_full_text_commands{$block_command} = 1 - if ($block_commands{$block_command} eq 'conditional'); -} - -# commands that may happen on lines where everything is -# permitted -my %in_full_line_commands = %in_full_text_commands; -foreach my $not_in_full_line_commands('noindent', 'indent') { - delete $in_full_line_commands{$not_in_full_line_commands}; -} - -# commands that may happen on sectioning commands -my %in_full_line_commands_no_refs = %in_full_line_commands; -foreach my $not_in_full_line_commands_no_refs ('titlefont', - 'anchor', 'footnote', 'verb') { - delete $in_full_line_commands_no_refs{$not_in_full_line_commands_no_refs}; -} - -# commands that may happen in simple text arguments -my %in_simple_text_commands = %in_full_line_commands_no_refs; -foreach my $not_in_simple_text_command('xref', 'ref', 'pxref', 'inforef') { - delete $in_simple_text_commands{$not_in_simple_text_command}; -} - -# commands that only accept simple text as argument in any context. -my %simple_text_commands; -foreach my $misc_command(keys(%misc_commands)) { - if ($misc_commands{$misc_command} =~ /^\d+$/ - or ($misc_commands{$misc_command} eq 'line' - and !($sectioning_commands{$misc_command} - or $def_commands{$misc_command})) - or $misc_commands{$misc_command} eq 'text') { - $simple_text_commands{$misc_command} = 1; - } -} - -my %full_line_commands_no_refs = (%sectioning_commands, - %def_commands); - -delete $simple_text_commands{'center'}; -delete $simple_text_commands{'exdent'}; -foreach my $command ('titlefont', 'anchor', 'xref','ref','pxref', - 'inforef', 'shortcaption', 'math', 'indicateurl', - 'email', 'uref', 'url', 'image', 'abbr', 'acronym', - 'dmn', 'ctrl', 'errormsg') { - $simple_text_commands{$command} = 1; -} - -# commands that accept full text, but no block or top-level commands -my %full_text_commands; -foreach my $brace_command (keys (%brace_commands)) { - if ($brace_commands{$brace_command} == 1 - and !$simple_text_commands{$brace_command} - and !$context_brace_commands{$brace_command} - and !$accent_commands{$brace_command}) { - $full_text_commands{$brace_command} = 1; - } -} - -# commands that accept almost the same than in full text, except -# what do not make sense on a line. -my %full_line_commands; -$full_line_commands{'center'} = 1; -$full_line_commands{'exdent'} = 1; -$full_line_commands{'item'} = 1; -$full_line_commands{'itemx'} = 1; - -# Fill the valid nestings hash. All commands not in that hash -# are considered to accept anything within. There are additional -# context tests, to make sure, for instance that we are testing -# @-commands on the block, misc or node @-command line and not -# in the content. -# index entry commands are dynamically set as in_simple_text_commands -my %default_valid_nestings; - -foreach my $command (keys(%accent_commands)) { - $default_valid_nestings{$command} = \%in_accent_commands; -} -foreach my $command (keys(%full_text_commands)) { - $default_valid_nestings{$command} = \%in_full_text_commands; -} -foreach my $command (keys(%simple_text_commands)) { - $default_valid_nestings{$command} = \%in_simple_text_commands; -} -foreach my $command (keys(%full_line_commands)) { - $default_valid_nestings{$command} = \%in_full_line_commands; -} -foreach my $command (keys(%full_line_commands_no_refs)) { - $default_valid_nestings{$command} = \%in_full_line_commands_no_refs; -} -# Only for block commands with line arguments -foreach my $command (keys(%block_commands)) { - if ($block_commands{$command} and $block_commands{$command} ne 'raw' - and $block_commands{$command} ne 'conditional' - and !$def_commands{$command}) { - $default_valid_nestings{$command} = \%in_simple_text_commands; - } -} - - -my @preformatted_contexts = ('preformatted', 'rawpreformatted'); -my %preformatted_contexts; -foreach my $preformatted_context (@preformatted_contexts) { - $preformatted_contexts{$preformatted_context} = 1; -} - -# contexts on the context_stack stack where empty line don't trigger -# paragraph -my %no_paragraph_contexts; -foreach my $no_paragraph_context ('math', 'menu', @preformatted_contexts, - 'def', 'inlineraw') { - $no_paragraph_contexts{$no_paragraph_context} = 1; -}; - - - -# Format a bug message -sub _bug_message($$;$$) -{ - my $self = shift; - my $message = shift; - my $line_number = shift; - my $current = shift; - - my $line_message = ''; - if ($line_number) { - my $file = $line_number->{'file_name'}; - $line_message - = "last location: $line_number->{'file_name'}:$line_number->{'line_nr'}"; - if ($line_number->{'macro'} ne '') { - $line_message .= " (possibly involving $line_number->{'macro'})"; - } - $line_message .= "\n"; - } - my $message_context_stack = "context_stack: (@{$self->{'context_stack'}})\n"; - my $current_element_message = ''; - if ($current) { - $current_element_message = "current: ". _print_current($current); - } - warn "You found a bug: $message\n\n". - "Additional informations:\n". - $line_message.$message_context_stack.$current_element_message; - -} - -# simple deep copy of a structure -sub _deep_copy($) -{ - my $struct = shift; - my $string = Data::Dumper->Dump([$struct], ['struct']); - eval $string; - return $struct; -} - -# return true if effect of global commands should be ignored. -sub _ignore_global_commands($) -{ - my $self = shift; - return !$self->{'expanded_formats_stack'}->[-1]; -} - -# enter all the commands associated with an index name using the prefix -# list -sub _register_index_commands($$) -{ - my $self = shift; - my $index_name = shift; - if (!$self->{'index_names'}->{$index_name}->{'prefix'}) { - $self->{'index_names'}->{$index_name}->{'prefix'} = [$index_name]; - } - if (!exists($self->{'index_names'}->{$index_name}->{'name'})) { - $self->{'index_names'}->{$index_name}->{'name'} = $index_name; - } - if (!exists($self->{'index_names'}->{$index_name}->{'contained_indices'})) { - $self->{'index_names'}->{$index_name}->{'contained_indices'}->{$index_name} = 1; - } - foreach my $prefix (@{$self->{'index_names'}->{$index_name}->{'prefix'}}) { - $self->{'misc_commands'}->{$prefix.'index'} = 'line'; - $self->{'no_paragraph_commands'}->{$prefix.'index'} = 1; - $self->{'valid_nestings'}->{$prefix.'index'} = \%in_simple_text_commands; - $self->{'command_index_prefix'}->{$prefix.'index'} = $prefix; - $self->{'prefix_to_index_name'}->{$prefix} = $index_name; - } -} - -# initialization entry point. Set up a parser. -# The last argument, optional, is a hash provided by the user to change -# the default values for what is present in %parser_default_configuration. -# The exact arguments of the function depend on how it was called, -# in a object oriented way or not. -sub parser(;$$) -{ - my $class = shift; - my $conf; - - my $parser = _deep_copy(\%parser_default_configuration); - # _deep_copy doesn't handle subs - $parser->{'gettext'} = $parser_default_configuration{'gettext'}; - $parser->{'pgettext'} = $parser_default_configuration{'pgettext'}; - - # called not object-oriented - if (ref($class) eq 'HASH') { - #print STDERR "Not oo\n" - $conf = $class; - bless $parser; - - } elsif (ref($class)) { - # called on an existing parser, interpreted as a duplication - my $old_parser = $class; - $class = ref($class); - foreach my $key (keys(%parser_default_configuration)) { - if ($tree_informations{$key}) { - if (defined($old_parser->{$key})) { - foreach my $info_key (keys(%{$old_parser->{$key}})) { - $parser->{$key}->{$info_key} - = $old_parser->{$key}->{$info_key}; - } - } - } else { - $parser->{$key} = _deep_copy($old_parser->{$key}); - } - } - #$parser = _deep_copy($old_parser); - $parser->{'gettext'} = $old_parser->{'gettext'}; - $parser->{'pgettext'} = $old_parser->{'pgettext'}; - bless $parser, $class; - $conf = shift; - - } elsif (defined($class)) { - bless $parser, $class; - $conf = shift; - } else { - bless $parser; - $conf = shift; - } - - if (defined($conf)) { - foreach my $key (keys(%$conf)) { - if (exists($parser_default_configuration{$key})) { - if (ref($conf->{$key}) ne 'CODE' and $key ne 'values') { - $parser->{$key} = _deep_copy($conf->{$key}); - } else { - $parser->{$key} = $conf->{$key}; - } - if ($initialization_overrides{$key}) { - $parser->{'set'}->{$key} = $parser->{$key}; - } - } else { - warn "$key not a possible customization in Texinfo::Parser::parser\n"; - } - } - } - #foreach my $value (keys %{$parser->{'values'}}) { - # print STDERR " -> $value $parser->{'values'}->{$value}\n"; - #} - # Now initialize command hash that are dynamically modified, notably - # those for index commands, and lists, based on defaults and user provided. - $parser->{'misc_commands'} = _deep_copy (\%misc_commands); - $parser->{'valid_nestings'} = _deep_copy (\%default_valid_nestings); - $parser->{'no_paragraph_commands'} = { %default_no_paragraph_commands }; - $parser->{'index_names'} = _deep_copy (\%index_names); - $parser->{'command_index_prefix'} = {%command_index_prefix}; - $parser->{'close_paragraph_commands'} = {%close_paragraph_commands}; - $parser->{'close_preformatted_commands'} = {%close_preformatted_commands}; - if ($parser->{'INLINE_INSERTCOPYING'}) { - delete $parser->{'close_paragraph_commands'}->{'insercopying'}; - delete $parser->{'close_preformatted_commands'}->{'insercopying'}; - } - # a hash is simply concatenated. It should be like %index_names. - if (ref($parser->{'indices'}) eq 'HASH') { - %{$parser->{'index_names'}} = (%{$parser->{'index_names'}}, - %{$parser->{'indices'}}); - } else { # an array holds index names defined with @defindex - foreach my $name (@{$parser->{'indices'}}) { - $parser->{'index_names'}->{$name} = {'in_code' => 0}; - } - } - foreach my $index (keys (%{$parser->{'index_names'}})) { - $parser->_register_index_commands($index); - } - if ($parser->{'merged_indices'}) { - foreach my $index_from (keys (%{$parser->{'merged_indices'}})) { - my $index_to = $parser->{'merged_indices'}->{$index_from}; - if (defined($parser->{'index_names'}->{$index_from}) - and defined($parser->{'index_names'}->{$index_to})) { - $parser->{'index_names'}->{$index_from}->{'merged_in'} = $index_to; - $parser->{'index_names'}->{$index_to}->{'contained_indices'}->{$index_from} = 1; - } - } - } - foreach my $explained_command(keys(%explained_commands)) { - if (!defined($parser->{'explained_commands'}->{$explained_command})) { - $parser->{'explained_commands'}->{$explained_command} = {}; - } - } - $parser->{'context_stack'} = [ $parser->{'context'} ]; - $parser->{'regions_stack'} = []; - $parser->{'macro_stack'} = []; - $parser->{'conditionals_stack'} = []; - $parser->{'expanded_formats_stack'} = [1]; - - # turn the array to a hash for speed. Not sure it really matters for such - # a small array. - foreach my $expanded_format(@{$parser->{'expanded_formats'}}) { - $parser->{'expanded_formats_hash'}->{$expanded_format} = 1; - } - - %{$parser->{'global_commands'}} = %global_multiple_commands; - - foreach my $global_command (@{$parser->{'GLOBAL_COMMANDS'}}) { - $parser->{'global_commands'}->{$global_command} = 1; - } - - $parser->Texinfo::Report::new; - - return $parser; -} - -sub get_conf($$) -{ - my $self = shift; - my $var = shift; - return $self->{$var}; -} - -# split a scalar text in an array lines. -sub _text_to_lines($) -{ - my $text = shift; - die if (!defined($text)); - my $had_final_end_line = chomp($text); - my $lines = [ map {$_."\n"} split (/\n/, $text, -1) ]; - $lines = [''] if (!@$lines); - chomp($lines->[-1]) unless ($had_final_end_line); - return $lines; -} - -# construct a text fragments array matching a lines array, based on information -# supplied. -# If $fixed_line_number is set the line number is not increased, otherwise -# it is increased, beginning at $first_line. -sub _complete_line_nr($$;$$$) -{ - my $lines = shift; - my $first_line = shift; - my $file = shift; - my $macro = shift; - my $fixed_line_number = shift; - - $macro = '' if (!defined($macro)); - $file = '' if (!defined($file)); - my $new_lines = []; - - if (defined($first_line)) { - my $line_index = $first_line; - foreach my $index(0..scalar(@$lines)-1) { - $line_index = $index+$first_line if (!$fixed_line_number); - $new_lines->[$index] = [ $lines->[$index], - { 'line_nr' => $line_index, - 'file_name' => $file, 'macro' => $macro } ]; - } - } else { - foreach my $line (@$lines) { - push @$new_lines, [ $line ]; - } - } - return $new_lines; -} - -# entry point for text fragments. -# Used in tests. -# Note that it has no associated root type a opposed to parse_texi_line -# and parse_texi_file. -sub parse_texi_text($$;$$$$) -{ - my $self = shift; - my $text = shift; - my $lines_nr = shift; - my $file = shift; - my $macro = shift; - my $fixed_line_number = shift; - - return undef if (!defined($text)); - - my $lines_array = []; - if (!ref($text)) { - $text = _text_to_lines($text); - } - $lines_nr = [] if (!defined($lines_nr)); - if (!ref($lines_nr)) { - #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'}); - $lines_array = _complete_line_nr($text, $lines_nr, $file, - $macro, $fixed_line_number); - } else { - while (@$text) { - my $line_nr = shift @$lines_nr; - my $line = shift @$text; - push @$lines_array, [$line, $line_nr]; - } - } - - $self = parser() if (!defined($self)); - $self->{'input'} = [{'pending' => $lines_array}]; - my $tree = $self->_parse_texi(); - return $tree; -} - -# Not used for now, as a @contents after the first sectioning command -# is correct if not using TeX. -sub _check_contents_location($$) -{ - my $self = shift; - my $tree = shift; - - my $commands = $self->global_commands_information(); - return unless ($commands); - # Find the last sectioning command - my $index = -1; - my %ending_root_commands; - my $found = 0; - while ($tree->{'contents'}->[$index]) { - if (defined($tree->{'contents'}->[$index]->{'cmdname'})) { - $ending_root_commands{$tree->{'contents'}->[$index]} = 1; - if ($sectioning_commands{$tree->{'contents'}->[$index]->{'cmdname'}}) { - $found = 1; - last; - } - } - $index--; - } - return if (!$found); - - #print STDERR "ending_root_commands ".join('|',keys(%ending_root_commands))."\n"; - #print STDERR "tree contents: ".join('|', @{$tree->{'contents'}})."\n"; - foreach my $command ('contents', 'shortcontents', 'summarycontents') { - if ($commands->{$command}) { - foreach my $current (@{$commands->{$command}}) { - my $root_command = $self->Texinfo::Common::find_parent_root_command($current); - #print STDERR "root_command for $current->{'cmdname'}: $root_command\n"; - if (defined($root_command) - and !$ending_root_commands{$root_command}) { - $self->line_warn(sprintf($self->__( - "\@%s should only appear at beginning or end of document"), - $current->{'cmdname'}), $current->{'line_nr'}); - } - } - } - } -} - -# parse a texi file -sub parse_texi_file($$) -{ - my $self = shift; - my $file_name = shift; - - my $filehandle = do { local *FH }; - if (! open($filehandle, $file_name)) { - $self->document_error(sprintf($self->__("could not open %s: %s"), - $file_name, $!)); - return undef; - } - my $line_nr = 0; - my $line; - my @first_lines; - - my $pending_first_texi_line; - # the first line not empty and not with \input is kept in - # $pending_first_texi_line and put in the pending lines just below - while ($line = <$filehandle>) { - $line_nr++; - if ($line =~ /^ *\\input/ or $line =~ /^\s*$/) { - $line =~ s/\x{7F}.*\s*//; - push @first_lines, $line; - } else { - $pending_first_texi_line = $line; - last; - } - } - my $root = { 'contents' => [], 'type' => 'text_root' }; - if (@first_lines) { - push @{$root->{'contents'}}, { 'type' => 'preamble', 'contents' => [] }; - foreach my $line (@first_lines) { - push @{$root->{'contents'}->[-1]->{'contents'}}, - { 'text' => $line, - 'type' => 'preamble_text' }; - } - } - my ($directories, $suffix); - ($file_name, $directories, $suffix) = fileparse($file_name) - if ($self->{'TEST'}); - $self = parser() if (!defined($self)); - $self->{'input'} = [{ - 'pending' => [[$pending_first_texi_line, {'line_nr' => $line_nr, - 'macro' => '', 'file_name' => $file_name}]], - 'name' => $file_name, - 'line_nr' => $line_nr, - 'fh' => $filehandle - }]; - $self->{'info'}->{'input_file_name'} = $file_name; - my $tree = $self->_parse_texi($root); - - # Find 'text_root', which contains everything before first node/section. - # if there are elements, 'text_root' is the first content, otherwise it - # is the root. - my $text_root; - if ($tree->{'type'} eq 'text_root') { - $text_root = $tree; - } elsif ($tree->{'contents'} and $tree->{'contents'}->[0] - and $tree->{'contents'}->[0]->{'type'} eq 'text_root') { - $text_root = $tree->{'contents'}->[0]; - } - - # Put everything before @setfilename in a special type. This allows to - # ignore everything before @setfilename. - if ($self->{'IGNORE_BEFORE_SETFILENAME'} and $text_root and - $self->{'extra'} and $self->{'extra'}->{'setfilename'} - and $self->{'extra'}->{'setfilename'}->{'parent'} eq $text_root) { - my $before_setfilename = {'type' => 'preamble_before_setfilename', - 'parent' => $text_root, - 'contents' => []}; - while ($text_root->{'contents'}->[0] ne $self->{'extra'}->{'setfilename'}) { - my $content = shift @{$text_root->{'contents'}}; - $content->{'parent'} = $before_setfilename; - push @{$before_setfilename->{'contents'}}, $content; - } - unshift (@{$text_root->{'contents'}}, $before_setfilename) - if (@{$before_setfilename->{'contents'}}); - } - #$self->_check_contents_location($tree); - - return $tree; -} - -sub parse_texi_line($$;$$$$) -{ - my $self = shift; - my $text = shift; - my $lines_nr = shift; - my $file = shift; - my $macro = shift; - my $fixed_line_number = shift; - - return undef if (!defined($text)); - - if (!ref($text)) { - $text = _text_to_lines($text); - } - #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'}); - my $lines_array = _complete_line_nr($text, $lines_nr, $file, - $macro, $fixed_line_number); - - $self = parser() if (!defined($self)); - $self->{'input'} = [{'pending' => $lines_array}]; - my $tree = $self->_parse_texi({'contents' => [], 'type' => 'root_line'}); - return $tree; -} - -# return indices informations -sub indices_information($) -{ - my $self = shift; - return ($self->{'index_names'}, $self->{'merged_indices'}); - #return ($self->{'index_names'}, $self->{'merged_indices'}, $self->{'index_entries'}); -} - -sub floats_information($) -{ - my $self = shift; - return $self->{'floats'}; -} - -sub internal_references_information($) -{ - my $self = shift; - return $self->{'internal_references'}; -} - -sub global_commands_information($) -{ - my $self = shift; - return $self->{'extra'}; -} - -# @ dircategory_direntry -# @ unassociated_menus -# perl_encoding -# input_encoding_name -# input_file_name -sub global_informations($) -{ - my $self = shift; - return $self->{'info'}; -} - -sub labels_information($) -{ - my $self = shift; - return $self->{'labels'}; -} - -# Following are the internal subroutines. The most important are -# _parse_texi: the main parser loop. -# _end_line: called at an end of line. Handling of @include lines is -# done here. -# _next_text: present the next text fragment, from pending text or line, -# as described above. - -# for debugging -sub _print_current($) -{ - my $current = shift; - return Texinfo::Common::_print_current($current); -} - -# for debugging -sub _print_command_args_texi($) -{ - my $current = shift; - return '' if (!$current->{'cmdname'}); - my $args = ''; - my $with_brace; - if ($current->{'args'} and @{$current->{'args'}}) { - $with_brace - = ($current->{'args'}->[0]->{'type'} eq 'brace_command_arg' - or $current->{'args'}->[0]->{'type'} eq 'brace_command_context'); - $args .= '{' if ($with_brace); - foreach my $arg (@{$current->{'args'}}) { - $args .= Texinfo::Convert::Texinfo::convert($arg).', '; - } - $args =~ s/, $//; - } - chomp($args); - if ($with_brace) { - $args .= '}'; - } - return '@'.$current->{'cmdname'} .$args."\n"; -} - -sub _print_current_keys($) -{ - my $current = shift; - my $string = _print_current($current); - foreach my $key (keys (%$current)) { - $string .= " $key: $current->{$key}\n"; - } - if ($current->{'extra'}) { - $string .= " EXTRA\n"; - foreach my $key (keys (%{$current->{'extra'}})) { - $string .= " $key: $current->{'extra'}->{$key}\n"; - } - } - return $string; -} - -# For debugging -my @kept_keys = ('contents', 'cmdname', 'type', 'text', 'args'); -my %kept_keys; -foreach my $key (@kept_keys) { - $kept_keys{$key} = 1; -} -sub _filter_print_keys { [grep {$kept_keys{$_}} ( sort keys %{$_[0]} )] }; -sub _print_tree($) -{ - my $tree = shift; - local $Data::Dumper::Sortkeys = \&_filter_print_keys; - local $Data::Dumper::Purity = 1; - local $Data::Dumper::Indent = 1; - - return Data::Dumper->Dump([$tree]); -} - -sub _register_global_command($$$$) -{ - my $self = shift; - my $command = shift; - my $current = shift; - my $line_nr = shift; - if ($command eq 'summarycontents' and !$self->{'global_commands'}->{$command}) { - $command = 'shortcontents'; - } - if ($self->{'global_commands'}->{$command} and $command ne 'author') { - push @{$self->{'extra'}->{$command}}, $current - unless (_ignore_global_commands($self)); - $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'}); - return 1; - } elsif ($global_unique_commands{$command}) { - # setfilename ignored in an included file - $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'}); - if ($command eq 'setfilename' - and scalar(@{$self->{'input'}}) > 1) { - } elsif (exists ($self->{'extra'}->{$current->{'cmdname'}})) { - $self->line_warn(sprintf($self->__('multiple @%s'), - $current->{'cmdname'}), $line_nr); - } else { - $self->{'extra'}->{$current->{'cmdname'}} = $current - unless (_ignore_global_commands($self)); - } - return 1; - } - return 0; -} - -# parse a @macro line -sub _parse_macro_command_line($$$$$;$) -{ - my $self = shift; - my $command = shift; - my $line = shift; - my $parent = shift; - my $line_nr = shift; - my $macro = { 'cmdname' => $command, 'parent' => $parent, 'contents' => [], - 'extra' => {'arg_line' => $line}, 'line_nr' => $line_nr }; - # REMACRO - if ($line =~ /^\s+([[:alnum:]][[:alnum:]-]*)\s*(.*)/) { - my $macro_name = $1; - my $args_def = $2; - my @args; - - if ($args_def =~ s/^\s*{\s*(.*?)\s*}\s*//) { - @args = split(/\s*,\s*/, $1); - } - - # accept an @-command after the arguments in case there is a @c or - # @comment - if ($args_def =~ /^\s*[^\@]/) { - $self->line_error(sprintf($self->__("bad syntax for \@%s argument: %s"), - $command, $args_def), - $line_nr); - $macro->{'extra'}->{'invalid_syntax'} = 1; - } - print STDERR "MACRO \@$command $macro_name\n" if ($self->{'DEBUG'}); - - $macro->{'args'} = [ - { 'type' => 'macro_name', 'text' => $macro_name, - 'parent' => $macro } ]; - my $index = 0; - foreach my $formal_arg (@args) { - push @{$macro->{'args'}}, - { 'type' => 'macro_arg', 'text' => $formal_arg, - 'parent' => $macro}; - if ($formal_arg !~ /^[\w\-]+$/) { - $self->line_error(sprintf($self->__("bad or empty \@%s formal argument: %s"), - $command, $formal_arg), $line_nr); - $macro->{'extra'}->{'invalid_syntax'} = 1; - } - $macro->{'extra'}->{'args_index'}->{$formal_arg} = $index; - $index++; - } - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - $macro->{'extra'}->{'invalid_syntax'} = 1; - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - $macro->{'extra'}->{'invalid_syntax'} = 1; - } - return $macro; -} - -# start a paragraph if in a context where paragraphs are to be started. -sub _begin_paragraph($$;$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - - # !$current->{'type'} is true for @-commands. In fact it is unclear - # that there may be cases of !$current->{'type'} and !$current->{'cmdname'} - if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}}) - and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) { - if (!defined($current->{'contents'})) { - $self->_bug_message("contents undef", $line_nr, $current); - die; - } - - # find whether an @indent precedes the paragraph - my $indent; - if (scalar(@{$current->{'contents'}})) { - my $index = scalar(@{$current->{'contents'}}) -1; - while ($index >= 0 - and !($current->{'contents'}->[$index]->{'type'} - and ($current->{'contents'}->[$index]->{'type'} eq 'empty_line' - or $current->{'contents'}->[$index]->{'type'} eq 'paragraph')) - and !($current->{'contents'}->[$index]->{'cmdname'} - and $self->{'close_paragraph_commands'}->{$current->{'contents'}->[$index]->{'cmdname'}})) { - if ($current->{'contents'}->[$index]->{'cmdname'} - and ($current->{'contents'}->[$index]->{'cmdname'} eq 'indent' - or $current->{'contents'}->[$index]->{'cmdname'} eq 'noindent')) { - $indent = $current->{'contents'}->[$index]->{'cmdname'}; - last; - } - $index--; - } - } - push @{$current->{'contents'}}, - { 'type' => 'paragraph', 'parent' => $current, 'contents' => [] }; - $current->{'contents'}->[-1]->{'extra'}->{$indent} = 1 if ($indent); - $current = $current->{'contents'}->[-1]; - print STDERR "PARAGRAPH\n" if ($self->{'DEBUG'}); - return $current; - } - return 0; -} - -sub _begin_preformatted($) -{ - my $self = shift; - my $current = shift; - if ($preformatted_contexts{$self->{'context_stack'}->[-1]}) { - push @{$current->{'contents'}}, - { 'type' => $self->{'context_stack'}->[-1], - 'parent' => $current, 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - print STDERR "PREFORMATTED $self->{'context_stack'}->[-1]\n" if ($self->{'DEBUG'}); - } - return $current; -} - -# wrapper around line_warn. Set line_nr to be the line_nr of the command, -# corresponding to the opening of the command. Call line_warn with -# sprintf if needed. -sub _command_warn($$$$;@) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $message = shift; - - if ($current->{'line_nr'}) { - $line_nr = $current->{'line_nr'}; - } - if (@_) { - $self->line_warn(sprintf($message, @_), $line_nr); - } else { - $self->line_warn($message, $line_nr); - } -} - -sub _command_error($$$$;@) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $message = shift; - - # use the beginning of the @-command for the error message - # line number if available. - # FIXME line_nr currently not registered for regular brace commands - if ($current->{'line_nr'}) { - $line_nr = $current->{'line_nr'}; - } - if (@_) { - $self->line_error(sprintf($message, @_), $line_nr); - } else { - $self->line_error($message, $line_nr); - } -} - -# currently doesn't do much more than -# return $_[1]->{'parent'} -sub _close_brace_command($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift; - - if ($current->{'cmdname'} ne 'verb' or $current->{'type'} eq '') { - if (defined($closed_command)) { - $self->_command_error($current, $line_nr, - $self->__("\@end %s seen before \@%s closing brace"), - $closed_command, $current->{'cmdname'}); - } elsif (defined($interrupting_command)) { - $self->_command_error($current, $line_nr, - $self->__("\@%s seen before \@%s closing brace"), - $interrupting_command, $current->{'cmdname'}); - - } else { - $self->_command_error($current, $line_nr, - $self->__("%c%s missing close brace"), ord('@'), $current->{'cmdname'}); - } - } else { - $self->_command_error($current, $line_nr, - $self->__("\@%s missing closing delimiter sequence: %s}"), - $current->{'cmdname'}, $current->{'type'}); - } - $current = $current->{'parent'}; - return $current; -} - -sub _in_code($$) -{ - my $self = shift; - my $current = shift; - - while ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and exists $brace_commands{$current->{'parent'}->{'cmdname'}} - and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) { - return 1 if ($code_style_commands{$current->{'parent'}->{'cmdname'}}); - $current = $current->{'parent'}->{'parent'}; - } - return 0; -} - -# close brace commands, that don't set a new context (ie @caption, @footnote) -sub _close_all_style_commands($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift; - - while ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and exists $brace_commands{$current->{'parent'}->{'cmdname'}} - and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) { - $current = _close_brace_command($self, $current->{'parent'}, $line_nr, - $closed_command, $interrupting_command); - } - return $current; -} - -# close brace commands except for @caption, @footnote then the paragraph -sub _end_paragraph($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift; - - $current = _close_all_style_commands($self, $current, $line_nr, - $closed_command, $interrupting_command); - if ($current->{'type'} and $current->{'type'} eq 'paragraph') { - print STDERR "CLOSE PARA\n" if ($self->{'DEBUG'}); - $current = $current->{'parent'}; - } - return $current; -} - -# close brace commands except for @caption, @footnote then the preformatted -sub _end_preformatted($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift; - - $current = _close_all_style_commands($self, $current, $line_nr, - $closed_command, $interrupting_command); - if ($current->{'type'} and $preformatted_contexts{$current->{'type'}}) { - print STDERR "CLOSE PREFORMATTED $current->{'type'}\n" if ($self->{'DEBUG'}); - # completly remove void preformatted contexts - if (!@{$current->{'contents'}}) { - my $removed = pop @{$current->{'parent'}->{'contents'}}; - print STDERR "popping $removed->{'type'}\n" if ($self->{'DEBUG'}); - } - $current = $current->{'parent'}; - } - return $current; -} - -# check that there are no text holding environment (currently -# checking only paragraphs and preformatted) in contents -sub _check_no_text($) -{ - my $current = shift; - my $after_paragraph = 0; - foreach my $content (@{$current->{'contents'}}) { - if ($content->{'type'} and $content->{'type'} eq 'paragraph') { - $after_paragraph = 1; - last; - } elsif ($content->{'type'} and $preformatted_contexts{$content->{'type'}}) { - foreach my $preformatted_content (@{$content->{'contents'}}) { - if ((defined($preformatted_content->{'text'}) - and $preformatted_content->{'text'} =~ /\S/) - or ($preformatted_content->{'cmdname'} - and ($preformatted_content->{'cmdname'} ne 'c' - and $preformatted_content->{'cmdname'} ne 'comment') - and !($preformatted_content->{'type'} - and $preformatted_content->{'type'} eq 'index_entry_command'))) { - $after_paragraph = 1; - last; - } - } - last if ($after_paragraph); - } - } - return $after_paragraph; -} - -# put everything after the last @item/@itemx in an item_table type container -# and distinguish table_term and table_entry. -sub _gather_previous_item($$;$$) -{ - my $self = shift; - my $current = shift; - my $next_command = shift; - my $line_nr = shift; - - # nothing to do in that case. - if ($current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'before_item') { - if ($next_command and $next_command eq 'itemx') { - $self->line_warn(sprintf($self->__("\@itemx should not begin \@%s"), - $current->{'cmdname'}), $line_nr); - } - return; - } - #print STDERR "GATHER "._print_current($current)."\n"; - my $type; - # if before an itemx, the type is different since there should not be - # real content, so it may be treated differently - if ($next_command and $next_command eq 'itemx') { - $type = 'inter_item'; - } else { - $type = 'table_item'; - } - my $table_gathered = {'type' => $type, - 'contents' => []}; - # remove everything that is not an @item/@items or before_item to - # put it in the table_item, starting from the end. - my $contents_count = scalar(@{$current->{'contents'}}); - for (my $i = 0; $i < $contents_count; $i++) { - #print STDERR "_gather_previous_item $i on $contents_count: "._print_current($current->{'contents'}->[-1])."\n"; - if ($current->{'contents'}->[-1]->{'cmdname'} - and ($current->{'contents'}->[-1]->{'cmdname'} eq 'item' - or ($current->{'contents'}->[-1]->{'cmdname'} eq 'itemx'))) { - last; - } else { - my $item_content = pop @{$current->{'contents'}}; - $item_content->{'parent'} = $table_gathered; - unshift @{$table_gathered->{'contents'}}, $item_content; - } - } - if ($type eq 'table_item') { - my $table_entry = {'type' => 'table_entry', - 'parent' => $current, - 'contents' => []}; - my $table_term = {'type' => 'table_term', - 'parent' => $table_entry, - 'contents' => []}; - push @{$table_entry->{'contents'}}, $table_term; - my $contents_count = scalar(@{$current->{'contents'}}); - for (my $i = 0; $i < $contents_count; $i++) { - if ($current->{'contents'}->[-1]->{'type'} - and ($current->{'contents'}->[-1]->{'type'} eq 'before_item' - or $current->{'contents'}->[-1]->{'type'} eq 'table_entry')) { - last; - } else { - my $item_content = pop @{$current->{'contents'}}; - $item_content->{'parent'} = $table_term; - unshift @{$table_term->{'contents'}}, $item_content; - # debug - if (! (($item_content->{'cmdname'} - and ($item_content->{'cmdname'} eq 'itemx' - or $item_content->{'cmdname'} eq 'item')) - or ($item_content->{'type'} - and $item_content->{'type'} eq 'inter_item'))) { - $self->_bug_message("wrong element in table term", $line_nr, - $item_content); - } - } - } - push @{$current->{'contents'}}, $table_entry; - if (scalar(@{$table_gathered->{'contents'}})) { - push @{$table_entry->{'contents'}}, $table_gathered; - $table_gathered->{'parent'} = $table_entry; - } - } else { - my $after_paragraph = _check_no_text($table_gathered); - if ($after_paragraph) { - $self->line_error($self->__("\@itemx must follow \@item"), $line_nr); - } - if (scalar(@{$table_gathered->{'contents'}})) { - push @{$current->{'contents'}}, $table_gathered; - $table_gathered->{'parent'} = $current; - } - } -} - -# Starting from the end, gather everything util the def_line to put in -# a def_item -sub _gather_def_item($;$) -{ - my $current = shift; - my $next_command = shift; - my $type; - # means that we are between a @def*x and a @def - if ($next_command) { - $type = 'inter_def_item'; - } else { - $type = 'def_item'; - } - - # This may happen for a construct like - # @deffnx a b @section - # but otherwise the end of line will lead to the command closing - return if (!$current->{'cmdname'} or $current->{'cmdname'} =~ /x$/); - #print STDERR "_gather_def_item($type) in "._print_current($current)."\n"; - my $def_item = {'type' => $type, - 'parent' => $current, - 'contents' => []}; - # remove everything that is not a def_line to put it in the def_item, - # starting from the end. - my $contents_count = scalar(@{$current->{'contents'}}); - for (my $i = 0; $i < $contents_count; $i++) { - #print STDERR "_gather_def_item $type ($i on $contents_count) "._print_current($current->{'contents'}->[-1])."\n"; - if ($current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'def_line') { - # and !$current->{'contents'}->[-1]->{'extra'}->{'not_after_command'}) { - last; - } else { - my $item_content = pop @{$current->{'contents'}}; - $item_content->{'parent'} = $def_item; - unshift @{$def_item->{'contents'}}, $item_content; - } - } - if (scalar(@{$def_item->{'contents'}})) { - push @{$current->{'contents'}}, $def_item; - } -} - -# close formats -sub _close_command_cleanup($$$) { - my $self = shift; - my $current = shift; - - return unless ($current->{'cmdname'}); - # remove the dynamic counters in multitable, they are not of use in the final - # tree. Also determine the multitable_body and multitable_head with - # @item or @headitem rows. - if ($current->{'cmdname'} eq 'multitable') { - my $in_head_or_rows; - my @contents = @{$current->{'contents'}}; - $current->{'contents'} = []; - foreach my $row (@contents) { - if ($row->{'type'} and $row->{'type'} eq 'row') { - delete $row->{'cells_count'}; - if ($row->{'contents'}->[0]->{'cmdname'} eq 'headitem') { - if (!$in_head_or_rows) { - push @{$current->{'contents'}}, {'type' => 'multitable_head', - 'parent' => $current}; - $in_head_or_rows = 1; - } - } elsif ($row->{'contents'}->[0]->{'cmdname'} eq 'item') { - if (!defined($in_head_or_rows) or $in_head_or_rows) { - push @{$current->{'contents'}}, {'type' => 'multitable_body', - 'parent' => $current}; - $in_head_or_rows = 0; - } - } - push @{$current->{'contents'}->[-1]->{'contents'}}, $row; - $row->{'parent'} = $current->{'contents'}->[-1]; - } else { - push @{$current->{'contents'}}, $row; - $in_head_or_rows = undef; - } - } - delete $current->{'rows_count'}; - } elsif ($item_container_commands{$current->{'cmdname'}}) { - delete $current->{'items_count'}; - } - - # put everything after the last @def*x command in a def_item type container. - if ($def_commands{$current->{'cmdname'}}) { - # At this point the end command hasn't been added to the command contents. - # so checks cannot be done at this point. - _gather_def_item($current); - } - - if ($item_line_commands{$current->{'cmdname'}}) { - # At this point the end command hasn't been added to the command contents. - # so checks cannot be done at this point. - if (@{$current->{'contents'}}) { - $self->_gather_previous_item($current); - } - } - - # put end out of before_item, and replace it at the end of the parent. - # remove empty before_item. - # warn if not empty before_item, but format is empty - if ($block_item_commands{$current->{'cmdname'}}) { - if (@{$current->{'contents'}}) { - my $leading_spaces = 0; - my $before_item; - if ($current->{'contents'}->[0]->{'type'} - and $current->{'contents'}->[0]->{'type'} eq 'empty_line_after_command' - and $current->{'contents'}->[1] - and $current->{'contents'}->[1]->{'type'} - and $current->{'contents'}->[1]->{'type'} eq 'before_item') { - $leading_spaces = 1; - $before_item = $current->{'contents'}->[1]; - } elsif ($current->{'contents'}->[0]->{'type'} - and $current->{'contents'}->[0]->{'type'} eq 'before_item') { - $before_item = $current->{'contents'}->[0]; - } - if ($before_item) { - if ($current->{'extra'}->{'end_command'} - and @{$before_item->{'contents'}} - and $before_item->{'contents'}->[-1] eq $current->{'extra'}->{'end_command'}) { - my $end = pop @{$before_item->{'contents'}}; - $end->{'parent'} = $current; - push @{$current->{'contents'}}, $end; - } - # remove empty before_items - if (!@{$before_item->{'contents'}}) { - if ($leading_spaces) { - my $space = shift @{$current->{'contents'}}; - shift @{$current->{'contents'}}; - unshift @{$current->{'contents'}}, $space; - } else { - shift @{$current->{'contents'}}; - } - } else { - # warn if not empty before_item, but format is empty - my $empty_before_item = 1; - foreach my $before_item_content (@{$before_item->{'contents'}}) { - if (!$before_item_content->{'cmdname'} or - ($before_item_content->{'cmdname'} ne 'c' - and $before_item_content->{'cmdname'} ne 'comment')) { - $empty_before_item = 0; - last; - } - } - if (!$empty_before_item) { - my $empty_format = 1; - foreach my $format_content (@{$current->{'contents'}}) { - next if ($format_content eq $before_item); - if (($format_content->{'cmdname'} and - ($format_content->{'cmdname'} ne 'c' - and $format_content->{'cmdname'} ne 'comment' - and $format_content->{'cmdname'} ne 'end')) - or ($format_content->{'type'} and - ($format_content->{'type'} ne 'empty_line_after_command'))) { - $empty_format = 0; - last; - } - } - if ($empty_format) { - $self->line_warn(sprintf($self->__("\@%s has text but no \@item"), - $current->{'cmdname'}), $current->{'line_nr'}); - } - } - } - } - } - } -} - -# close the current command, with error messages and give the parent. -# If the last argument is given it is the command being closed if -# hadn't there be an error, currently only block command, used for a -# better error message. -sub _close_current($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift; - - if ($current->{'cmdname'}) { - print STDERR "CLOSING(_close_current) \@$current->{'cmdname'}\n" if ($self->{'DEBUG'}); - if (exists($brace_commands{$current->{'cmdname'}})) { - pop @{$self->{'context_stack'}} - if (exists $context_brace_commands{$current->{'cmdname'}}); - $current = _close_brace_command($self, $current, $line_nr, - $closed_command, $interrupting_command); - } elsif (exists($block_commands{$current->{'cmdname'}})) { - if (defined($closed_command)) { - $self->line_error(sprintf($self->__("`\@end' expected `%s', but saw `%s'"), - $current->{'cmdname'}, $closed_command), $line_nr); - } elsif ($interrupting_command) { - $self->line_error(sprintf($self->__("\@%s seen before \@end %s"), - $interrupting_command, $current->{'cmdname'}), - $line_nr); - } else { - $self->line_error(sprintf($self->__("no matching `%cend %s'"), - ord('@'), $current->{'cmdname'}), $line_nr); - if ($block_commands{$current->{'cmdname'}} eq 'conditional') { - # in this case we are within an ignored conditional - my $conditional = pop @{$current->{'parent'}->{'contents'}}; - } - } - if ($preformatted_commands{$current->{'cmdname'}} - or $menu_commands{$current->{'cmdname'}} - or $format_raw_commands{$current->{'cmdname'}}) { - my $context = pop @{$self->{'context_stack'}}; - pop @{$self->{'expanded_formats_stack'}} - if ($format_raw_commands{$current->{'cmdname'}}); - } - pop @{$self->{'regions_stack'}} - if ($region_commands{$current->{'cmdname'}}); - $current = $current->{'parent'}; - } else { - # There @item and @tab commands are closed, and also line commands - # with invalid content - $current = $current->{'parent'}; - } - } elsif ($current->{'type'}) { - print STDERR "CLOSING type $current->{'type'}\n" if ($self->{'DEBUG'}); - if ($current->{'type'} eq 'bracketed') { - $self->_command_error($current, $line_nr, - $self->__("misplaced %c"), ord('{')); - } elsif ($current->{'type'} eq 'menu_comment' - or $current->{'type'} eq 'menu_entry_description') { - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted", - $line_nr, $current); - } - # close empty menu_comment - if (!@{$current->{'contents'}}) { - pop @{$current->{'parent'}->{'contents'}}; - } - } elsif ($current->{'type'} eq 'misc_line_arg' - or $current->{'type'} eq 'block_line_arg') { - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'line' and $context ne 'def') { - $self->_bug_message("context $context instead of line or def", - $line_nr, $current); - die; - } - } - $current = $current->{'parent'}; - } else { # Should never go here. - $current = $current->{'parent'} if ($current->{'parent'}); - $self->_bug_message("No type nor cmdname when closing", - $line_nr, $current); - } - return $current; -} - -# a closed_command arg means closing until that command is found. -# no command arg means closing until the root or a root_command -# is found. -sub _close_commands($$$;$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $closed_command = shift; - my $interrupting_command = shift;; - - $current = _end_paragraph($self, $current, $line_nr, $closed_command, - $interrupting_command); - $current = _end_preformatted($self, $current, $line_nr, $closed_command, - $interrupting_command); - - # stop if the command is found - while (!($closed_command and $current->{'cmdname'} - and $current->{'cmdname'} eq $closed_command) - # stop if at the root - and $current->{'parent'} - # stop if in a root command - # or in a context_brace_commands and searching for a specific - # end block command (with $closed_command set). - # This second condition means that a footnote is not closed when - # looking for the end of a block command, but is closed when - # completly closing the stack. - and !($current->{'cmdname'} - and ($root_commands{$current->{'cmdname'}} - or ($closed_command and $current->{'parent'}->{'cmdname'} - and $context_brace_commands{$current->{'parent'}->{'cmdname'}})))){ - $self->_close_command_cleanup($current); - $current = $self->_close_current($current, $line_nr, $closed_command, - $interrupting_command); - } - - my $closed_element; - if ($closed_command and $current->{'cmdname'} - and $current->{'cmdname'} eq $closed_command) { - if ($preformatted_commands{$current->{'cmdname'}}) { - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted for $closed_command", - $line_nr, $current); - } - } elsif ($format_raw_commands{$current->{'cmdname'}}) { - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'rawpreformatted') { - $self->_bug_message("context $context instead of rawpreformatted for $closed_command", - $line_nr, $current); - } - pop @{$self->{'expanded_formats_stack'}}; - } elsif ($menu_commands{$current->{'cmdname'}}) { - my $context = pop @{$self->{'context_stack'}}; - # may be in menu, but context is preformatted if in a preformatted too. - if ($context ne 'menu' and $context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted or menu for $closed_command", - $line_nr, $current); - } - } - #print STDERR "close context $context for $current->{'cmdname'}\n" - # if ($self->{'DEBUG'}); - pop @{$self->{'regions_stack'}} - if ($region_commands{$current->{'cmdname'}}); - $closed_element = $current; - #$self->_close_command_cleanup($current); - $current = $current->{'parent'}; - } elsif ($closed_command) { - $self->line_error(sprintf($self->__("unmatched `%c%s'"), - ord('@'), "end $closed_command"), $line_nr); - } - return ($closed_element, $current); -} - -# begin paragraph if needed. If not try to merge with the previous -# content if it is also some text. -sub _merge_text($$$) -{ - my $self = shift; - my $current = shift; - my $text = shift; - - my $paragraph; - - my $no_merge_with_following_text = 0; - if ($text =~ /\S/) { - my $leading_spaces; - if ($text =~ /^(\s+)/) { - $leading_spaces = $1; - } - if ($current->{'contents'} and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command' - or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument' - or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) { - $no_merge_with_following_text = 1; - } - if (_abort_empty_line($self, $current, $leading_spaces)) { - $text =~ s/^(\s+)//; - } - - $paragraph = _begin_paragraph($self, $current); - $current = $paragraph if ($paragraph); - } - - if (!defined($current->{'contents'})) { - $self->_bug_message("No contents in _merge_text", - undef, $current); - die; - } - - if (@{$current->{'contents'}} - and exists($current->{'contents'}->[-1]->{'text'}) - and $current->{'contents'}->[-1]->{'text'} !~ /\n/ - and !$no_merge_with_following_text) { - $current->{'contents'}->[-1]->{'text'} .= $text; - print STDERR "MERGED TEXT: $text|||\n" if ($self->{'DEBUG'}); - } else { - push @{$current->{'contents'}}, { 'text' => $text, 'parent' => $current }; - print STDERR "NEW TEXT: $text|||\n" if ($self->{'DEBUG'}); - } - return $current; -} - -# return the parent if in a item_container command, itemize or enumerate -sub _item_container_parent($) -{ - my $current = shift; - if ((($current->{'cmdname'} and $current->{'cmdname'} eq 'item') - or ($current->{'type'} and $current->{'type'} eq 'before_item')) - and ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and $item_container_commands{$current->{'parent'}->{'cmdname'}})) { - return ($current->{'parent'}); - } - return undef; -} - -# return the parent if in a item_line command, @*table -sub _item_line_parent($) -{ - my $current = shift; - if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'item' - or $current->{'cmdname'} eq 'itemx')) { - $current = $current->{'parent'}->{'parent'}; - } elsif ($current->{'type'} and $current->{'type'} eq 'before_item' - and $current->{'parent'}) { - $current = $current->{'parent'}; - } - return $current if ($current->{'cmdname'} - and $item_line_commands{$current->{'cmdname'}}); - return undef; -} - -# return the parent if in a multitable -sub _item_multitable_parent($) -{ - my $current = shift; - if (($current->{'cmdname'} and ($current->{'cmdname'} eq 'headitem' - or $current->{'cmdname'} eq 'item' or $current->{'cmdname'} eq 'tab')) - and $current->{'parent'} and $current->{'parent'}->{'parent'}) { - $current = $current->{'parent'}->{'parent'}; - } elsif ($current->{'type'} and $current->{'type'} eq 'before_item' - and $current->{'parent'}) { - $current = $current->{'parent'}; - } - return $current if ($current->{'cmdname'} - and $current->{'cmdname'} eq 'multitable'); - return undef; -} - -# returns next text fragment, be it pending from a macro expansion or -# text or file -sub _next_text($$$) -{ - my $self = shift; - my $line_nr = shift; - my $current = shift; - - while (@{$self->{'input'}}) { - my $input = $self->{'input'}->[0]; - if (@{$input->{'pending'}}) { - my $new_text = shift @{$input->{'pending'}}; - if ($new_text->[1] and $new_text->[1]->{'end_macro'}) { - delete $new_text->[1]->{'end_macro'}; - my $top_macro = shift @{$self->{'macro_stack'}}; - print STDERR "SHIFT MACRO_STACK(@{$self->{'macro_stack'}}): $top_macro->{'args'}->[0]->{'text'}\n" - if ($self->{'DEBUG'}); - } - return ($new_text->[0], $new_text->[1]); - } elsif ($input->{'fh'}) { - my $fh = $input->{'fh'}; - my $line = <$fh>; - while (defined($line)) { - # add an end of line if there is none at the end of file - if (eof($fh) and $line !~ /\n/) { - $line .= "\n"; - } - $line =~ s/\x{7F}.*\s*//; - if ($self->{'CPP_LINE_DIRECTIVES'} - # no cpp directives in ignored/macro/verbatim - and defined ($current) - and not - (($current->{'cmdname'} - and $block_commands{$current->{'cmdname'}} - and ($block_commands{$current->{'cmdname'}} eq 'raw' - or $block_commands{$current->{'cmdname'}} eq 'conditional')) - or - ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'verb') - ) - and $line =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/) { - $input->{'line_nr'} = $2; - if (defined($5)) { - $input->{'name'} = $5; - } - $line = <$fh>; - } else { - $input->{'line_nr'}++; - return ($line, {'line_nr' => $input->{'line_nr'}, - 'file_name' => $input->{'name'}, - 'macro' => ''}); - } - } - } - my $previous_input = shift(@{$self->{'input'}}); - # Don't close STDIN - if ($previous_input->{'fh'} and $previous_input->{'name'} ne '-') { - if (!close($previous_input->{'fh'})) { - $self->document_warn(sprintf($self->__("error on closing %s: %s"), - $previous_input->{'name'}, $!)); - - } - } - } - - return (undef, $line_nr); -} - -# collect text and line numbers until an end of line is found. -sub _new_line($$$) -{ - my $self = shift; - my $line_nr = shift; - my $current = shift; - my $new_line = ''; - - while (1) { - my $new_text; - ($new_text, $line_nr) = _next_text($self, $line_nr, $current); - if (!defined($new_text)) { - $new_line = undef if ($new_line eq ''); - last; - } - - $new_line .= $new_text; - - my $chomped_text = $new_text; - last if chomp($chomped_text); - } - return ($new_line, $line_nr); -} - -sub _expand_macro_arguments($$$$) -{ - my $self = shift; - my $macro = shift; - my $line = shift; - my $line_nr = shift; - my $braces_level = 1; - my $arguments = [ '' ]; - my $arg_nr = 0; - my $args_total = scalar(@{$macro->{'args'}}) -1; - my $name = $macro->{'args'}->[0]->{'text'}; - - my $line_nr_orig = $line_nr; - - while (1) { - if ($line =~ s/([^\\{},]*)([\\{},])//) { - my $separator = $2; - $arguments->[-1] .= $1; - if ($separator eq '\\') { - if ($line =~ s/^(.)//) { - my $protected_char = $1; - if ($protected_char !~ /[\\{},]/) { - $arguments->[-1] .= '\\'; - } - $arguments->[-1] .= $protected_char; - - print STDERR "MACRO ARG: $separator: $protected_char\n" if ($self->{'DEBUG'}); - } else { - $arguments->[-1] .= '\\'; - print STDERR "MACRO ARG: $separator\n" if ($self->{'DEBUG'}); - } - } elsif ($separator eq ',') { - if ($braces_level == 1) { - if (scalar(@$arguments) < $args_total) { - push @$arguments, ''; - $line =~ s/^[^\S\f]*//; - print STDERR "MACRO NEW ARG\n" if ($self->{'DEBUG'}); - } else { - # implicit quoting when there is one argument. - if ($args_total != 1) { - $self->line_error(sprintf($self->__( - "macro `%s' called with too many args"), - $name), $line_nr); - } - $arguments->[-1] .= ','; - } - } else { - $arguments->[-1] .= ','; - } - } elsif ($separator eq '}') { - $braces_level--; - last if ($braces_level == 0); - $arguments->[-1] .= $separator; - } elsif ($separator eq '{') { - $braces_level++; - $arguments->[-1] .= $separator; - } - } else { - print STDERR "MACRO ARG end of line\n" if ($self->{'DEBUG'}); - $arguments->[-1] .= $line; - - ($line, $line_nr) = _new_line($self, $line_nr, $macro); - if (!defined($line)) { - $self->line_error(sprintf($self->__("\@%s missing close brace"), - $name), $line_nr_orig); - return ($arguments, "\n", $line_nr); - } - } - } - if ($args_total == 0 and $arguments->[0] =~ /[\S\f]/) { - $self->line_error(sprintf($self->__( - "macro `%s' declared without argument called with an argument"), - $name), $line_nr); - } - print STDERR "END MACRO ARGS EXPANSION(".scalar(@$arguments)."): ". - join("|\n", @$arguments) ."|\n" if ($self->{'DEBUG'}); - return ($arguments, $line, $line_nr); -} - -sub _expand_macro_body($$$$) { - my $self = shift; - my $macro = shift; - my $args = shift; - my $line_nr = shift; - - my $macrobody = $macro->{'extra'}->{'macrobody'}; - my $args_total = scalar(@{$macro->{'args'}}) -1; - my $args_index = $macro->{'extra'}->{'args_index'}; - - my $i; - for ($i=0; $i<=$args_total; $i++) { - $args->[$i] = "" unless (defined($args->[$i])); - } - - my $result = ''; - while ($macrobody ne '') { - if ($macrobody =~ s/^([^\\]*)\\//o) { - $result .= $1; - if ($macrobody =~ s/^\\//) { - $result .= '\\'; - } elsif ($macrobody =~ s/^([^\\]*)\\//) { - my $arg = $1; - if (defined($args_index->{$arg})) { - $result .= $args->[$args_index->{$arg}]; - } else { - $self->line_error(sprintf($self->__( - "\\ in \@%s expansion followed `%s' instead of parameter name or \\"), - $macro->{'args'}->[0]->{'text'}, $arg), $line_nr); - $result .= '\\' . $arg; - } - } - next; - } - $result .= $macrobody; - last; - } - return $result; -} - -# each time a new line appeared, a container is opened to hold the text -# consisting only of spaces. This container is removed here, typically -# this is called when non-space happens on a line. -sub _abort_empty_line($$;$) -{ - my $self = shift; - my $current = shift; - my $additional_text = shift; - $additional_text = '' if (!defined($additional_text)); - if ($current->{'contents'} and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line' - or $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command' - or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument' - or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) { - print STDERR "ABORT EMPTY additional text |$additional_text|, current |$current->{'contents'}->[-1]->{'text'}|\n" if ($self->{'DEBUG'}); - $current->{'contents'}->[-1]->{'text'} .= $additional_text; - # remove empty 'empty*before'. - if ($current->{'contents'}->[-1]->{'text'} eq '') { - # as we remove 'empty_spaces_before_argument', 'spaces_before_argument' - # is removed from 'extra' too. - if ($current->{'extra'} - and $current->{'extra'}->{'spaces_before_argument'} - and $current->{'extra'}->{'spaces_before_argument'} - eq $current->{'contents'}->[-1]) { - delete ($current->{'extra'}->{'spaces_before_argument'}); - delete ($current->{'extra'}) if !(keys(%{$current->{'extra'}})); - } - pop @{$current->{'contents'}} - } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line') { - # exactly the same condition than to begin a paragraph - if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}}) - and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) { - $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_before_paragraph'; - } else { - delete $current->{'contents'}->[-1]->{'type'}; - } - } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command') { - $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_after_command'; - } - return 1; - } - return 0; -} - -# isolate last space in a command to help expansion disregard unuseful spaces. -sub _isolate_last_space($$;$) -{ - my $self = shift; - my $current = shift; - my $type = shift; - $type = 'spaces_at_end' if (!defined($type)); - if ($current->{'contents'} and @{$current->{'contents'}}) { - my $index = -1; - # we ignore space before a misc command that is last on line. - # This is primarily to tag spaces before comments, but this will - # also tag and, in most converter lead to removal of spaces - # before any misc command, which is not really problematic as - # in most cases, if it is not a comment, we are in an invalid - # nesting of misc command on another @-command line. - $index = -2 - if (scalar(@{$current->{'contents'}}) > 1 - and $current->{'contents'}->[-1]->{'cmdname'} - and $self->{'misc_commands'}->{$current->{'contents'}->[-1]->{'cmdname'}}); - if (defined($current->{'contents'}->[$index]->{'text'}) - and !$current->{'contents'}->[$index]->{'type'} - and $current->{'contents'}->[$index]->{'text'} =~ /\s+$/) { - if ($current->{'contents'}->[$index]->{'text'} !~ /\S/) { - $current->{'contents'}->[$index]->{'type'} = $type; - } else { - $current->{'contents'}->[$index]->{'text'} =~ s/(\s+)$//; - my $spaces = $1; - my $new_spaces = { 'text' => $spaces, 'parent' => $current, - 'type' => $type }; - if ($index == -1) { - push @{$current->{'contents'}}, $new_spaces; - } else { - splice (@{$current->{'contents'}}, $index+1, 0, $new_spaces); - } - } - } - } -} - -# used to put a node name in error messages. -sub _node_extra_to_texi($) -{ - my $node = shift; - my $result = ''; - if ($node->{'manual_content'}) { - $result = '('.Texinfo::Convert::Texinfo::convert({'contents' - => $node->{'manual_content'}}) .')'; - } - if ($node->{'node_content'}) { - $result .= Texinfo::Convert::Texinfo::convert ({'contents' - => $node->{'node_content'}}); - } - return $result; -} - -sub _find_end_brace($$) -{ - my $text = shift; - my $braces_count = shift; - - my $before = ''; - while ($braces_count > 0 and length($text)) { - if ($text =~ s/([^()]*)([()])//) { - $before .= $1.$2; - my $brace = $2; - if ($brace eq '(') { - $braces_count++; - } else { - $braces_count--; - if ($braces_count == 0) { - return ($before, $text, 0); - } - } - } else { - $before .= $text; - $text = ''; - } - } - return ($before, undef, $braces_count); -} - -# This only counts opening braces, and returns 0 once all the parentheses -# are closed -sub _count_opened_tree_braces($$); -sub _count_opened_tree_braces($$) -{ - my $current = shift; - my $braces_count = shift; - if (defined($current->{'text'})) { - my ($before, $after); - ($before, $after, $braces_count) = _find_end_brace($current->{'text'}, - $braces_count); - } - if ($current->{'args'}) { - foreach my $arg (@{$current->{'args'}}) { - $braces_count = _count_opened_tree_braces($arg, $braces_count); - return $braces_count if ($braces_count == 0); - } - } - if ($current->{'contents'}) { - foreach my $content (@{$current->{'contents'}}) { - $braces_count = _count_opened_tree_braces($content, $braces_count); - return $braces_count if ($braces_count == 0); - } - } - return $braces_count; -} - -# retrieve a leading manual name in parentheses, if there is one. -sub _parse_node_manual($) -{ - my $node = shift; - my @contents = @{$node->{'contents'}}; - _trim_spaces_comment_from_content(\@contents); - - my $manual; - my $result; -#print STDERR "RRR $contents[0] and $contents[0]->{'text'} \n"; - if ($contents[0] and $contents[0]->{'text'} and $contents[0]->{'text'} =~ /^\(/) { - my $braces_count = 1; - if ($contents[0]->{'text'} !~ /^\($/) { - my $brace = shift @contents; - my $brace_text = $brace->{'text'}; - $brace_text =~ s/^\(//; - unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'}, - 'parent' => $brace->{'parent'} } if $brace_text ne ''; - } else { - shift @contents; - } - while(@contents) { - my $content = shift @contents; - if (!defined($content->{'text'}) or $content->{'text'} !~ /\)/) { - push @$manual, $content; - $braces_count = _count_opened_tree_braces($content, $braces_count); - # This is an error, braces were closed in a command - if ($braces_count == 0) { - last; - } - } else { - my ($before, $after); - ($before, $after, $braces_count) = _find_end_brace($content->{'text'}, - $braces_count); - if ($braces_count == 0) { - $before =~ s/\)$//; - push @$manual, { 'text' => $before, 'parent' => $content->{'parent'} } - if ($before ne ''); - $after =~ s/^\s*//; - unshift @contents, { 'text' => $after, 'parent' => $content->{'parent'} } - if ($after ne ''); - last; - } else { - push @$manual, $content; - } - } - } - $result->{'manual_content'} = $manual if (defined($manual)); - } - if (@contents) { - $result->{'node_content'} = \@contents; - $result->{'normalized'} = - Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => \@contents}); - } - return $result; -} - -sub _parse_float_type($) -{ - my $current = shift; - if (@{$current->{'args'}}) { - my @type_contents = @{$current->{'args'}->[0]->{'contents'}}; - _trim_spaces_comment_from_content(\@type_contents); - if (@type_contents) { - my $normalized - = Texinfo::Convert::NodeNameNormalization::normalize_node( - {'contents' => \@type_contents}); - $current->{'extra'}->{'type'}->{'content'} = \@type_contents; - if ($normalized =~ /[^-]/) { - $current->{'extra'}->{'type'}->{'normalized'} = $normalized; - return 1; - } - } - } - $current->{'extra'}->{'type'}->{'normalized'} = ''; - return 0; -} - -# used for definition line parsing -sub _next_bracketed_or_word($$) -{ - my $self = shift; - my $contents = shift; - return undef if (!scalar(@{$contents})); - my $spaces; - $spaces = shift @{$contents} if (defined($contents->[0]->{'text'}) and - $contents->[0]->{'text'} !~ /\S/); - if (defined($spaces)) { - $spaces->{'type'} = 'spaces'; - chomp $spaces->{'text'}; - $spaces = undef if ($spaces->{'text'} eq ''); - } - return ($spaces, undef) if (!scalar(@{$contents})); - - #print STDERR "BEFORE PROCESSING ".Texinfo::Convert::Texinfo::convert({'contents' => $contents}); - if ($contents->[0]->{'type'} and $contents->[0]->{'type'} eq 'bracketed') { - #print STDERR "Return bracketed\n"; - my $bracketed = shift @{$contents}; - $self->_isolate_last_space($bracketed, 'empty_space_at_end_def_bracketed'); - my $bracketed_def_content = { 'contents' => $bracketed->{'contents'}, - 'parent' => $bracketed->{'parent'}, - 'type' => 'bracketed_def_content', }; - if ($bracketed->{'extra'} and $bracketed->{'extra'}->{'spaces_before_argument'}) { - $bracketed_def_content->{'extra'}->{'spaces_before_argument'} - = $bracketed->{'extra'}->{'spaces_before_argument'}; - } - return ($spaces, $bracketed_def_content); - } elsif ($contents->[0]->{'cmdname'}) { - #print STDERR "Return command $contents->[0]->{'cmdname'}\n"; - return ($spaces, shift @{$contents}); - } else { - #print STDERR "Process $contents->[0]->{'text'}\n"; - $contents->[0]->{'text'} =~ s/^(\s*)//; - my $space_text = $1; - $spaces = {'text' => $space_text, 'type' => 'spaces'} if ($space_text); - $contents->[0]->{'text'} =~ s/^(\S+)//; - shift @{$contents} if ($contents->[0]->{'text'} eq ''); - return ($spaces, {'text' => $1}); - } -} - -# definition line parsing -sub _parse_def($$$) -{ - my $self = shift; - my $command = shift; - my $contents = shift; - - my @contents = @$contents; - shift @contents if ($contents[0] and $contents[0]->{'type'} - and $contents[0]->{'type'} eq 'empty_spaces_after_command'); - if ($def_aliases{$command}) { - my $real_command = $def_aliases{$command}; - my $prepended = $def_map{$command}->{$real_command}; - my @prepended_content; - - my $text; - my $in_bracketed; - if ($prepended =~ /^\{/) { - $text = $prepended; - $text =~ s/\{([^\}]+)\}/$1/; - $in_bracketed = 1; - } else { - $text = $prepended; - } - my $tree = $self->gdt($text); - if ($in_bracketed or @{$tree->{'contents'}} > 1) { - my $bracketed = { 'type' => 'bracketed' }; - $bracketed->{'contents'} = $tree->{'contents'}; - foreach my $content (@{$tree->{'contents'}}) { - $content->{'parent'} = $bracketed; - } - @prepended_content = ($bracketed); - } else { - @prepended_content = (@{$tree->{'contents'}}); - } - push @prepended_content, { 'text' => ' ' }; - - unshift @contents, @prepended_content; - - $command = $def_aliases{$command}; - } - foreach (my $i = 0; $i < scalar(@contents); $i++) { - # copy, to avoid changing the original - $contents[$i] = {'text' => $contents[$i]->{'text'}} - if (defined($contents[$i]->{'text'})); - } - my @result; - my @args = @{$def_map{$command}}; - my $arg_type; - # Even when $arg_type is not set, that is for def* that is not documented - # to take args, everything is as is arg_type was set to arg. - $arg_type = pop @args if ($args[-1] eq 'arg' or $args[-1] eq 'argtype'); - foreach my $arg (@args) { - #print STDERR "$command $arg"._print_current($contents[0]); - #foreach my $content (@contents) {print STDERR " "._print_current($content)}; - #print STDERR " contents ->".Texinfo::Convert::Texinfo::convert ({'contents' => \@contents}); - my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents); - last if (!defined($next)); - #print STDERR "NEXT[$arg] ".Texinfo::Convert::Texinfo::convert($next)."\n"; - push @result, ['spaces', $spaces] if (defined($spaces)); - push @result, [$arg, $next]; - } - - my @args_results; - while (@contents) { - my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents); - push @args_results, ['spaces', $spaces] if (defined($spaces)); - last if (!defined($next)); - if (defined($next->{'text'})) { - while (1) { - if ($next->{'text'} =~ s/^([^\[\](),]+)//) { - push @args_results, ['arg', {'text' => $1}]; - } elsif ($next->{'text'} =~ s/^([\[\](),])//) { - push @args_results, ['delimiter', - {'text' => $1, 'type' => 'delimiter'}]; - } else { - last; - } - } - } else { - push @args_results, [ 'arg', $next ]; - } - } - if ($arg_type and $arg_type eq 'argtype') { - my $next_is_type = 1; - foreach my $arg(@args_results) { - if ($arg->[0] eq 'spaces') { - } elsif ($arg->[0] eq 'delimiter') { - $next_is_type = 1; - } elsif ($arg->[1]->{'cmdname'} and $arg->[1]->{'cmdname'} ne 'code') { - $next_is_type = 1; - } elsif ($next_is_type) { - $arg->[0] = 'typearg'; - $next_is_type = 0; - } else { - $next_is_type = 1; - } - } - } - return [@result, @args_results]; -} - -# register a label, that is something that may be the target of a reference -# and must be unique in the document. Corresponds with @node, @anchor and -# @float second arg. -sub _register_label($$$$) -{ - my $self = shift; - my $current = shift; - my $label = shift; - my $line_nr = shift; - my $normalized = $label->{'normalized'}; - if (_ignore_global_commands($self)) { - $current->{'extra'}->{'normalized'} = $normalized; - $current->{'extra'}->{'node_content'} = $label->{'node_content'}; - return 0; - } elsif ($self->{'labels'}->{$normalized}) { - $self->line_error(sprintf($self->__("\@%s `%s' previously defined"), - $current->{'cmdname'}, - Texinfo::Convert::Texinfo::convert({'contents' => - $label->{'node_content'}})), - $line_nr); - $self->line_error(sprintf($self->__("here is the previous definition as \@%s"), - $self->{'labels'}->{$normalized}->{'cmdname'}), - $self->{'labels'}->{$normalized}->{'line_nr'}, 1); - return 0; - } else { - $current->{'extra'}->{'normalized'} = $normalized; - $current->{'extra'}->{'node_content'} = $label->{'node_content'}; - $self->{'labels'}->{$normalized} = $current; - return 1; - } -} - -sub _non_bracketed_contents($) -{ - my $current = shift; - if ($current->{'type'} and $current->{'type'} eq 'bracketed') { - my $new = {}; - $new->{'contents'} = $current->{'contents'} if ($current->{'parent'}); - $new->{'parent'} = $current->{'parent'} if ($current->{'parent'}); - return $new; - } else { - return $current; - } -} - -# store an index entry. -# $current is the command element. -# $content holds the actual content. -# for index entries and v|ftable items, it is the index entry content, -# for def, it is the parsed arguments, based on the definition line -# arguments. -sub _enter_index_entry($$$$$$$) -{ - my $self = shift; - my $command_container = shift; - my $command = shift; - my $current = shift; - my $content = shift; - my $content_normalized = shift; - my $line_nr = shift; - - $content_normalized = $content if (!defined($content_normalized)); - - my $prefix = $self->{'command_index_prefix'}->{$command_container}; - my $index_name = $self->{'prefix_to_index_name'}->{$prefix}; - my $index = $self->{'index_names'}->{$index_name}; - - my $number; - unless (_ignore_global_commands($self)) { - $number = (defined($index->{'index_entries'}) - ? (scalar(@{$index->{'index_entries'}}) + 1) - : 1); - } - my $index_entry = { 'index_name' => $index_name, - 'index_at_command' => $command, - 'index_type_command' => $command_container, - 'index_prefix' => $prefix, - 'content' => $content, - 'content_normalized' => $content_normalized, - 'command' => $current, - 'number' => $number, - }; - if (@{$self->{'regions_stack'}}) { - $index_entry->{'region'} = $self->{'regions_stack'}->[-1]; - } elsif ($self->{'current_node'}) { - $index_entry->{'node'} = $self->{'current_node'}; - } elsif (!$self->{'current_section'}) { - $self->line_warn(sprintf($self->__("entry for index `%s' outside of any node"), - $index_name), $line_nr); - } - #print STDERR "INDEX ENTRY \@$command->{'cmdname'} $index_name($number)\n"; - unless (_ignore_global_commands($self)) { - push @{$index->{'index_entries'}}, $index_entry; - } - $current->{'extra'}->{'index_entry'} = $index_entry; -} - -# This is always called at command closing. -sub _remove_empty_content_arguments($) -{ - my $current = shift; - my $type; - if ($current->{'extra'}) { - if ($current->{'extra'}->{'block_command_line_contents'}) { - $type = 'block_command_line_contents'; - } elsif ($current->{'extra'}->{'brace_command_contents'}) { - $type = 'brace_command_contents'; - } - } - if ($type) { - while (@{$current->{'extra'}->{$type}} - and not defined($current->{'extra'}->{$type}->[-1])) { - pop @{$current->{'extra'}->{$type}}; - } - delete $current->{'extra'}->{$type} if (!@{$current->{'extra'}->{$type}}); - delete $current->{'extra'} if (!keys(%{$current->{'extra'}})); - } -} - -sub _strip_macrobody_leading_space($$) -{ - my $self = shift; - my $text = shift; - if ($self->{'MACRO_BODY_IGNORES_LEADING_SPACE'}) { - $text =~ s/^\s*//mg; - } - return $text; -} - -# close constructs and do stuff at end of line (or end of the document) -sub _end_line($$$); -sub _end_line($$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - - my $current_old = $current; - - my $included_file = 0; - - # a line consisting only of spaces. - if ($current->{'contents'} and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'empty_line') { - print STDERR "END EMPTY LINE\n" if ($self->{'DEBUG'}); - if ($current->{'type'} and $current->{'type'} eq 'paragraph') { - my $empty_line = pop @{$current->{'contents'}}; - $current = _end_paragraph($self, $current, $line_nr); - push @{$current->{'contents'}}, $empty_line; - $empty_line->{'parent'} = $current; - } elsif ($current->{'type'} - and $current->{'type'} eq 'preformatted' - and $current->{'parent'}->{'type'} - and $current->{'parent'}->{'type'} eq 'menu_entry_description') { - my $empty_line = pop @{$current->{'contents'}}; - if ($current->{'type'} eq 'preformatted') { - my $empty_preformatted = (!@{$current->{'contents'}}); - $current = $current->{'parent'}; - pop @{$current->{'contents'}} if ($empty_preformatted); - } - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted in empty line after menu_entry_description", - $line_nr, $current); - } - - # first parent is menu_entry - $current = $current->{'parent'}->{'parent'}; - - push @{$current->{'contents'}}, { 'type' => 'menu_comment', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - push @{$current->{'contents'}}, { 'type' => 'preformatted', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - push @{$current->{'contents'}}, { 'type' => 'after_description_line', - 'text' => $empty_line->{'text'}, - 'parent' => $current }; - push @{$self->{'context_stack'}}, 'preformatted'; - print STDERR "MENU: END DESCRIPTION, OPEN COMMENT\n" if ($self->{'DEBUG'}); - } elsif (!$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) { - $current = _end_paragraph($self, $current, $line_nr); - } - - # end of a menu line. - } elsif ($current->{'type'} - and ($current->{'type'} eq 'menu_entry_name' - or $current->{'type'} eq 'menu_entry_node')) { - my $empty_menu_entry_node = 0; - my $end_comment; - if ($current->{'type'} eq 'menu_entry_node') { - if (@{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'cmdname'} - and ($current->{'contents'}->[-1]->{'cmdname'} eq 'c' - or $current->{'contents'}->[-1]->{'cmdname'} eq 'comment')) { - $end_comment = pop @{$current->{'contents'}}; - } - if (!@{$current->{'contents'}} - # empty if only the end of line or spaces - or (@{$current->{'contents'}} == 1 - and defined($current->{'contents'}->[-1]->{'text'}) - and $current->{'contents'}->[-1]->{'text'} !~ /\S/)) { - $empty_menu_entry_node = 1; - push @{$current->{'contents'}}, $end_comment if ($end_comment); - } - } - # we abort the menu entry if there is no node name - if ($empty_menu_entry_node - or $current->{'type'} eq 'menu_entry_name') { - my $description_or_menu_comment; - print STDERR "FINALLY NOT MENU ENTRY\n" if ($self->{'DEBUG'}); - my $menu = $current->{'parent'}->{'parent'}; - my $menu_entry = pop @{$menu->{'contents'}}; - if (@{$menu->{'contents'}} and $menu->{'contents'}->[-1]->{'type'} - and $menu->{'contents'}->[-1]->{'type'} eq 'menu_entry') { - my $entry = $menu->{'contents'}->[-1]; - my $description; - foreach my $entry_element (reverse(@{$entry->{'args'}})) { - if ($entry_element->{'type'} eq 'menu_entry_description') { - $description = $entry_element; - last; - } - } - if ($description) { - $description_or_menu_comment = $description; - } else { - # Normally this cannot happen - $self->_bug_message("No description in menu_entry", - $line_nr, $current); - push @{$entry->{'args'}}, {'type' => 'menu_entry_description', - 'parent' => $entry, - 'contents' => [] }; - $description_or_menu_comment = $entry->{'args'}->[-1]; - } - } elsif (@{$menu->{'contents'}} and $menu->{'contents'}->[-1]->{'type'} - and $menu->{'contents'}->[-1]->{'type'} eq 'menu_comment') { - $description_or_menu_comment = $menu->{'contents'}->[-1]; - } - if ($description_or_menu_comment) { - $current = $description_or_menu_comment; - if ($current->{'contents'}->[-1] and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'preformatted') { - $current = $current->{'contents'}->[-1]; - } else { - # this should not happen - $self->_bug_message("description or menu comment not in preformatted", - $line_nr, $current); - push @{$current->{'contents'}}, {'type' => 'preformatted', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - } - push @{$self->{'context_stack'}}, 'preformatted'; - } else { - push @{$menu->{'contents'}}, {'type' => 'menu_comment', - 'parent' => $menu, - 'contents' => [] }; - $current = $menu->{'contents'}->[-1]; - push @{$current->{'contents'}}, {'type' => 'preformatted', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - push @{$self->{'context_stack'}}, 'preformatted'; - print STDERR "THEN MENU_COMMENT OPEN\n" if ($self->{'DEBUG'}); - } - while (@{$menu_entry->{'args'}}) { - my $arg = shift @{$menu_entry->{'args'}}; - if (defined($arg->{'text'})) { - $current = _merge_text($self, $current, $arg->{'text'}); - } else { - while (@{$arg->{'contents'}}) { - my $content = shift @{$arg->{'contents'}}; - if (defined($content->{'text'})) { - $current = _merge_text($self, $current, - $content->{'text'}); - $content = undef; - } else { - $content->{'parent'} = $current; - push @{$current->{'contents'}}, $content; - } - } - } - $arg = undef; - } - # MENU_COMMENT open - $menu_entry = undef; - } else { - print STDERR "MENU ENTRY END LINE\n" if ($self->{'DEBUG'}); - $current = $current->{'parent'}; - $current = _enter_menu_entry_node($self, $current, $line_nr); - if (defined($end_comment)) { - $end_comment->{'parent'} = $current; - push @{$current->{'contents'}}, $end_comment; - } - } - # def line - } elsif ($current->{'parent'} - and $current->{'parent'}->{'type'} - and $current->{'parent'}->{'type'} eq 'def_line') { - my $def_context = pop @{$self->{'context_stack'}}; - if ($def_context ne 'def') { - $self->_bug_message("context $def_context instead of def", - $line_nr, $current); - die; - } - my $def_command = $current->{'parent'}->{'extra'}->{'def_command'}; - my $arguments = $self->_parse_def($def_command, - $current->{'contents'}); - if (scalar(@$arguments)) { - $current->{'parent'}->{'extra'}->{'def_args'} = $arguments; - my $def_parsed_hash; - foreach my $arg (@$arguments) { - die if (!defined($arg->[0])); - last if ($arg->[0] eq 'arg' or $arg->[0] eq 'typearg' - or $arg->[0] eq 'delimiter'); - next if ($arg->[0] eq 'spaces'); - # change of type is done in _parse_def. - #if ($arg->[1]->{'type'} and $arg->[1]->{'type'} eq 'bracketed') { - # $def_parsed_hash->{$arg->[0]} = { 'contents' => $arg->[1]->{'contents'}, - # 'type' => 'bracketed_def_content', - # 'parent' => $arg->[1]->{'parent'}}; - #} else { - # $def_parsed_hash->{$arg->[0]} = $arg->[1]; - #} - $def_parsed_hash->{$arg->[0]} = $arg->[1]; - } - $current->{'parent'}->{'extra'}->{'def_parsed_hash'} = $def_parsed_hash; - # do an standard index entry tree - my $index_entry; - if (defined($def_parsed_hash->{'name'})) { - $index_entry = $def_parsed_hash->{'name'} - # empty bracketed - unless ($def_parsed_hash->{'name'}->{'type'} - and $def_parsed_hash->{'name'}->{'type'} eq 'bracketed_def_content' - and (!$def_parsed_hash->{'name'}->{'contents'} - or (!scalar(@{$def_parsed_hash->{'name'}->{'contents'}})) - or (scalar(@{$def_parsed_hash->{'name'}->{'contents'}}) == 1 - and defined($def_parsed_hash->{'name'}->{'contents'}->[0]->{'text'}) - and $def_parsed_hash->{'name'}->{'contents'}->[0]->{'text'} !~ /\S/))); - } - if (defined($index_entry)) { - my $index_contents_normalized; - if ($def_parsed_hash->{'class'}) { - if ($command_index_prefix{$def_command} eq 'f') { - $index_entry = $self->gdt('{name} on {class}', - {'name' => $def_parsed_hash->{'name'}, - 'class' => $def_parsed_hash->{'class'}}); - $index_contents_normalized - = [_non_bracketed_contents($def_parsed_hash->{'name'}), - { 'text' => ' on '}, - _non_bracketed_contents($def_parsed_hash->{'class'})]; - } elsif ($command_index_prefix{$def_command} eq 'v' - and $def_command ne 'defcv') { - $index_entry = $self->gdt('{name} of {class}', - {'name' => $def_parsed_hash->{'name'}, - 'class' => $def_parsed_hash->{'class'}}); - $index_contents_normalized - = [_non_bracketed_contents($def_parsed_hash->{'name'}), - { 'text' => ' of '}, - _non_bracketed_contents($def_parsed_hash->{'class'})]; - } - } - $index_contents_normalized = [$index_entry] - if (!defined($index_contents_normalized)); - my $index_contents; - # 'root_line' is the container returned by gdt. - if ($index_entry->{'type'} and $index_entry->{'type'} eq 'root_line') { - $index_contents = $index_entry->{'contents'}; - } else { - $index_contents = [$index_entry]; - } - _enter_index_entry($self, - $current->{'parent'}->{'extra'}->{'def_command'}, - $current->{'parent'}->{'extra'}->{'original_def_cmdname'}, - $current->{'parent'}, $index_contents, - $index_contents_normalized, $line_nr); - } else { - $self->_command_warn($current->{'parent'}, $line_nr, - $self->__('missing name for @%s'), - $current->{'parent'}->{'extra'}->{'original_def_cmdname'}); - } - } else { - $self->_command_warn($current->{'parent'}, $line_nr, - $self->__('missing category for @%s'), - $current->{'parent'}->{'extra'}->{'original_def_cmdname'}); - } - $current = $current->{'parent'}->{'parent'}; - $current = $self->_begin_preformatted($current); - - # other block command lines - } elsif ($current->{'type'} - and $current->{'type'} eq 'block_line_arg') { - my $empty_text; - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'line') { - $self->_bug_message("context $context instead of line in block_line_arg", - $line_nr, $current); - } - # @multitable args - if ($current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'multitable') { - # parse the prototypes and put them in a special arg - my @prototype_row; - # do the same but keeping spaces information - my @prototype_line; - foreach my $content (@{$current->{'contents'}}) { - if ($content->{'type'} and $content->{'type'} eq 'bracketed') { - push @prototype_row, { 'contents' => $content->{'contents'}, - 'parent' => $content->{'parent'}, - 'type' => 'bracketed_multitable_prototype'}; - push @prototype_line, $content; - } elsif ($content->{'text'}) { - if ($content->{'text'} =~ /\S/) { - foreach my $prototype (split /\s+/, $content->{'text'}) { - push @prototype_row, { 'text' => $prototype, - 'type' => 'row_prototype' } unless ($prototype eq ''); - } - } - # The regexp breaks between characters, with a non space followed - # by a space or a space followed by non space. It is like \b, but - # for \s \S, and not \w \W. - foreach my $prototype_or_space (split /(?<=\S)(?=\s)|(?=\S)(?<=\s)/, - $content->{'text'}) { - if ($prototype_or_space =~ /\S/) { - push @prototype_line, {'text' => $prototype_or_space, - 'type' => 'row_prototype' }; - } elsif ($prototype_or_space =~ /\s/) { - push @prototype_line, {'text' => $prototype_or_space, - 'type' => 'prototype_space' }; - } - } - } else { - # FIXME could this happen? Should be a debug message? - if (!$content->{'cmdname'}) { - $self->_command_warn($current, $line_nr, - $self->__("unexpected argument on \@%s line: %s"), - $current->{'cmdname'}, - Texinfo::Convert::Texinfo::convert({ $content->{'contents'} })); - } elsif ($content->{'cmdname'} eq 'c' - or $content->{'cmdname'} eq 'comment') { - } else { - push @prototype_row, $content; - push @prototype_line, $content; - } - } - } - - my $multitable = $current->{'parent'}; - $multitable->{'extra'}->{'max_columns'} = scalar(@prototype_row); - if (!scalar(@prototype_row)) { - $self->_command_warn($multitable, $line_nr, - $self->__("empty multitable")); - } - $multitable->{'extra'}->{'prototypes'} = \@prototype_row; - $multitable->{'extra'}->{'prototypes_line'} = \@prototype_line; - - } else { - $self->_isolate_last_space($current, 'space_at_end_block_command'); - $self->_register_command_arg($current, 'block_command_line_contents'); - } - # @float args - if ($current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'float') { - my $float = $current->{'parent'}; - $float->{'line_nr'} = $line_nr; - my $type = ''; - if (@{$float->{'args'}}) { - if ($float->{'args'}->[1]) { - my $float_label = _parse_node_manual($float->{'args'}->[1]); - _check_internal_node($self, $float_label, $line_nr); - if (defined($float_label) and $float_label->{'node_content'} - and $float_label->{'normalized'} =~ /[^-]/) { - _register_label($self, $float, $float_label, $line_nr); - } - } - _parse_float_type($float); - $type = $float->{'extra'}->{'type'}->{'normalized'}; - } - push @{$self->{'floats'}->{$type}}, $float - unless (_ignore_global_commands($self)); - $float->{'float_section'} = $self->{'current_section'} - if (defined($self->{'current_section'})); - } - $current = $current->{'parent'}; - delete $current->{'remaining_args'}; - # don't consider empty argument of block @-commands as argument, - # reparent them as contents - if ($current->{'args'}->[0]->{'contents'}->[0] - and $current->{'args'}->[0]->{'contents'}->[0]->{'type'} - and $current->{'args'}->[0]->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') - { - my $empty_text = $current->{'args'}->[0]->{'contents'}->[0]; - $empty_text->{'parent'} = $current; - unshift @{$current->{'contents'}}, $empty_text; - delete $current->{'args'}; - } - # Additionally, remove empty arguments as far as possible - _remove_empty_content_arguments($current); - - if ($current->{'cmdname'} - and $block_item_commands{$current->{'cmdname'}}) { - if ($current->{'cmdname'} eq 'enumerate') { - my $spec = 1; - if ($current->{'extra'}->{'block_command_line_contents'} - and defined($current->{'extra'}->{'block_command_line_contents'}->[0])) { - if (scalar(@{$current->{'extra'}->{'block_command_line_contents'}->[0]}) > 1) { - $self->_command_error($current, $line_nr, - $self->__("superfluous argument to \@%s"), - $current->{'cmdname'}); - } - my $arg = $current->{'extra'}->{'block_command_line_contents'}->[0]->[0]; - if (!defined($arg->{'text'}) or $arg->{'text'} !~ /^(([[:digit:]]+)|([[:alpha:]]+))$/) { - $self->_command_error($current, $line_nr, - $self->__("bad argument to \@%s"), - $current->{'cmdname'}); - } else { - $spec = $arg->{'text'}; - } - } - $current->{'extra'}->{'enumerate_specification'} = $spec; - } elsif ($item_line_commands{$current->{'cmdname'}}) { - if (!$current->{'extra'} - or !$current->{'extra'}->{'command_as_argument'}) { - $self->_command_error($current, $line_nr, - $self->__("%s requires an argument: the formatter for %citem"), - $current->{'cmdname'}, ord('@')); - } elsif (!$brace_commands{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}} - and !$self->{'definfoenclose'}->{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}}) { - $self->_command_error($current, $line_nr, - $self->__("command \@%s not accepting argument in brace should not be on \@%s line"), - $current->{'extra'}->{'command_as_argument'}->{'cmdname'}, - $current->{'cmdname'}); - delete $current->{'extra'}->{'command_as_argument'}; - } - } - # This code checks that the command_as_argument of the @itemize - # is alone on the line, otherwise it is not a command_as_argument. - if ($current->{'extra'} - and $current->{'extra'}->{'command_as_argument'} - and $current->{'cmdname'} eq 'itemize') { - my @args = @{$current->{'args'}->[0]->{'contents'}}; - while (@args) { - my $arg = shift @args; - last if ($arg eq $current->{'extra'}->{'command_as_argument'}); - } - while (@args) { - my $arg = shift @args; - if (!(($arg->{'cmdname'} - and ($arg->{'cmdname'} eq 'c' - or $arg->{'cmdname'} eq 'comment')) - or (defined($arg->{'text'}) and $arg->{'text'} !~ /\S/))) { - #print STDERR " -> stop at "._print_current($arg)."\n"; - delete $current->{'extra'}->{'command_as_argument'}->{'type'}; - delete $current->{'extra'}->{'command_as_argument'}; - last; - } - } - } - if ($current->{'extra'} - and $current->{'extra'}->{'command_as_argument'} - and $accent_commands{$current->{'extra'}->{'command_as_argument'}->{'cmdname'}} - and ($current->{'cmdname'} eq 'itemize' - or $item_line_commands{$current->{'cmdname'}})) { - $self->_command_warn($current, $line_nr, - $self->__("accent command `\@%s' not allowed as \@%s argument"), - $current->{'extra'}->{'command_as_argument'}->{'cmdname'}, - $current->{'cmdname'}); - delete $current->{'extra'}->{'command_as_argument'}; - delete $current->{'extra'}->{'block_command_line_contents'}; - } - if (!$current->{'extra'}->{'block_command_line_contents'} - and $current->{'cmdname'} eq 'itemize') { - $current->{'extra'}->{'block_command_line_contents'} = [ - [ { 'cmdname' => 'bullet', - 'type' => 'command_as_argument', - 'parent' => $current } - ] - ]; - $current->{'extra'}->{'command_as_argument'} = - $current->{'extra'}->{'block_command_line_contents'}->[0]->[0]; - } elsif ($item_line_commands{$current->{'cmdname'}} and - ! $current->{'extra'}->{'command_as_argument'}) { - $current->{'extra'}->{'block_command_line_contents'} = [ - [ { 'cmdname' => 'asis', - 'type' => 'command_as_argument', - 'parent' => $current } - ] - ]; - $current->{'extra'}->{'command_as_argument'} = - $current->{'extra'}->{'block_command_line_contents'}->[0]->[0]; - } - push @{$current->{'contents'}}, { 'type' => 'before_item', - 'contents' => [], 'parent', $current }; - $current = $current->{'contents'}->[-1]; - } - if ($current->{'cmdname'} and $menu_commands{$current->{'cmdname'}}) { - push @{$current->{'contents'}}, {'type' => 'menu_comment', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - print STDERR "MENU_COMMENT OPEN\n" if ($self->{'DEBUG'}); - push @{$self->{'context_stack'}}, 'preformatted'; - } - $current = $self->_begin_preformatted($current); - - # if we are after a @end verbatim, we must restart a preformatted if needed, - # since there is no @end command explicitly associated to raw commands - # it won't be done elsewhere. - } elsif ($current->{'contents'} - and $current->{'contents'}->[-1] - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command' - and $current->{'contents'}->[-1]->{'extra'} - and $current->{'contents'}->[-1]->{'extra'}->{'command'} - and $current->{'contents'}->[-1]->{'extra'}->{'command'}->{'cmdname'} eq 'verbatim') { - $current = $self->_begin_preformatted($current); - # misc command line arguments - # Never go here if skipline/noarg/... - } elsif ($current->{'type'} - and $current->{'type'} eq 'misc_line_arg') { - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'line') { - $self->_bug_message("context $context instead of line in misc_line_arg", - $line_nr, $current); - } - $self->_isolate_last_space($current); - - # first parent is the @command, second is the parent - $current = $current->{'parent'}; - my $misc_cmd = $current; - my $command = $current->{'cmdname'}; - my $end_command; - print STDERR "MISC END \@$command\n" if ($self->{'DEBUG'}); - if ($self->{'misc_commands'}->{$command} =~ /^\d$/) { - my $args = _parse_line_command_args($self, $current, $line_nr); - $current->{'extra'}->{'misc_args'} = $args if (defined($args)); - } elsif ($self->{'misc_commands'}->{$command} eq 'text') { - my $text = Texinfo::Convert::Text::convert($current->{'args'}->[0], - {'code' => 1, - Texinfo::Common::_convert_text_options($self)}); - if ($text eq '') { - $self->_command_warn($current, $line_nr, - $self->__("\@%s missing argument"), $command); - $current->{'extra'}->{'missing_argument'} = 1; - } else { - $current->{'extra'}->{'text_arg'} = $text; - if ($command eq 'end') { - # REMACRO - my $line = $text; - if ($line =~ s/^([[:alnum:]][[:alnum:]-]+)//) { - $end_command = $1; - - if (!exists $block_commands{$end_command}) { - $self->_command_warn($current, $line_nr, - $self->__("unknown \@end %s"), $end_command); - $end_command = undef; - } else { - print STDERR "END BLOCK $end_command\n" if ($self->{'DEBUG'}); - if ($block_commands{$end_command} eq 'conditional') { - if (@{$self->{'conditionals_stack'}} - and $self->{'conditionals_stack'}->[-1] eq $end_command) { - pop @{$self->{'conditionals_stack'}}; - } else { - $self->_command_error($current, $line_nr, - $self->__("unmatched `%c%s'"), ord('@'), 'end'); - $end_command = undef; - } - } - $current->{'extra'}->{'command_argument'} = $end_command - if (defined($end_command)); - } - if ($line =~ /\S/ and defined($end_command)) { - my $texi_line - = Texinfo::Convert::Texinfo::convert($current->{'args'}->[0]); - $texi_line =~ s/^\s*([[:alnum:]][[:alnum:]-]+)//; - $self->_command_error($current, $line_nr, - $self->__("superfluous argument to \@%s %s: %s"), - $command, $end_command, $texi_line); - } - } else { - $self->_command_error($current, $line_nr, - $self->__("bad argument to \@%s: %s"), - $command, $line); - } - } elsif ($command eq 'include') { - my $file = Texinfo::Common::locate_include_file($self, $text) ; - if (defined($file)) { - my $filehandle = do { local *FH }; - if (open ($filehandle, $file)) { - $included_file = 1; - binmode($filehandle, ":encoding($self->{'INPUT_PERL_ENCODING'})") - if (defined($self->{'INPUT_PERL_ENCODING'})); - print STDERR "Included $file($filehandle)\n" if ($self->{'DEBUG'}); - unshift @{$self->{'input'}}, { - 'name' => $file, - 'line_nr' => 0, - 'pending' => [], - 'fh' => $filehandle }; - } else { - $self->_command_error($current, $line_nr, - $self->__("\@%s: could not open %s: %s"), - $command, $text, $!); - } - } else { - $self->_command_error($current, $line_nr, - $self->__("\@%s: could not find %s"), - $command, $text); - } - } elsif ($command eq 'documentencoding') { - my ($texinfo_encoding, $perl_encoding, $input_encoding) - = Texinfo::Encoding::encoding_alias($text); - $self->_command_warn($current, $line_nr, - $self->__("encoding `%s' is not a canonical texinfo encoding"), - $text) - if (!$texinfo_encoding or $texinfo_encoding ne lc($text)); - if (! _ignore_global_commands($self)) { - if ($input_encoding) { - $current->{'extra'}->{'input_encoding_name'} = $input_encoding; - } - if (!$perl_encoding) { - $self->_command_warn($current, $line_nr, - $self->__("unrecognized encoding name `%s'"), $text); - } else { - $current->{'extra'}->{'input_perl_encoding'} = $perl_encoding; - - if ($input_encoding) { - if (!$self->{'set'}->{'INPUT_ENCODING_NAME'}) { - $self->{'INPUT_ENCODING_NAME'} = $input_encoding; - $self->{'info'}->{'input_encoding_name'} = $input_encoding; - } - } - - if (!$self->{'set'}->{'INPUT_PERL_ENCODING'}) { - $self->{'INPUT_PERL_ENCODING'} = $perl_encoding; - $self->{'info'}->{'input_perl_encoding'} = $perl_encoding; - foreach my $input (@{$self->{'input'}}) { - binmode($input->{'fh'}, ":encoding($perl_encoding)") if ($input->{'fh'}); - } - } - } - } - } elsif ($command eq 'documentlanguage') { - my @messages = Texinfo::Common::warn_unknown_language($text, - $self->{'gettext'}); - foreach my $message(@messages) { - $self->_command_warn($current, $line_nr, $message); - } - if (!$self->{'set'}->{'documentlanguage'} - and !_ignore_global_commands($self)) { - $self->{'documentlanguage'} = $text; - } - } - } - } elsif ($command eq 'node') { - foreach my $arg (@{$current->{'args'}}) { - my $node = _parse_node_manual($arg); - push @{$current->{'extra'}->{'nodes_manuals'}}, $node; - } - if (_check_node_label($self, $current->{'extra'}->{'nodes_manuals'}->[0], - $command, $line_nr)) { - if (_register_label($self, $current, - $current->{'extra'}->{'nodes_manuals'}->[0], $line_nr)) { - $self->{'current_node'} = $current; - push @{$self->{'nodes'}}, $current; - } - } - } elsif ($command eq 'listoffloats') { - # Empty listoffloats is allowed - _parse_float_type($current); - #if (!_parse_float_type($current)) { - # $self->line_error (sprintf($self->__("\@%s missing argument"), - # $command), $line_nr); - #} - # handle all the other 'line' commands. Here just check that they - # have an argument and prepare contents without spaces. - } else { - my @contents = @{$current->{'args'}->[0]->{'contents'}}; - _trim_spaces_comment_from_content(\@contents); - # empty @top is allowed - if (!scalar(@contents) and $command ne 'top') { - $self->_command_warn($current, $line_nr, - $self->__("\@%s missing argument"), $command); - $current->{'extra'}->{'missing_argument'} = 1; - } else { - $current->{'extra'}->{'misc_content'} = \@contents; - if (($command eq 'item' or $command eq 'itemx') - and $self->{'command_index_prefix'}->{$current->{'parent'}->{'cmdname'}}) { - _enter_index_entry($self, $current->{'parent'}->{'cmdname'}, - $command, $current, - $current->{'extra'}->{'misc_content'}, - undef, $line_nr); - } elsif ($self->{'command_index_prefix'}->{$current->{'cmdname'}}) { - _enter_index_entry($self, $current->{'cmdname'}, - $current->{'cmdname'}, $current, - $current->{'extra'}->{'misc_content'}, - undef, $line_nr); - $current->{'type'} = 'index_entry_command'; - } - } - if (defined($command_structuring_level{$command})) { - $current->{'level'} = $command_structuring_level{$command}; - } - } - $current = $current->{'parent'}; - if ($end_command) { - print STDERR "END COMMAND $end_command\n" if ($self->{'DEBUG'}); - my $end = pop @{$current->{'contents'}}; - if ($block_commands{$end_command} ne 'conditional') { - my $closed_command; - ($closed_command, $current) - = _close_commands($self, $current, $line_nr, $end_command); - my $inline_copying; - if ($closed_command) { - $misc_cmd->{'extra'}->{'command'} = $closed_command; - $closed_command->{'extra'}->{'end_command'} = $misc_cmd; - $self->_close_command_cleanup($closed_command); - $end->{'parent'} = $closed_command; - - # register @insertcopying as a macro if INLINE_INSERTCOPYING is set. - if ($end_command eq 'copying' and $self->{'INLINE_INSERTCOPYING'}) { - # remove the end of line following @copying. - my @contents = @{$closed_command->{'contents'}}; - shift @contents if ($contents[0] and $contents[0]->{'type'} - and ($contents[0]->{'type'} eq 'empty_line_after_command' - or $contents[0]->{'type'} eq 'empty_spaces_after_command')); - # the macrobody is the @copying content converted to Texinfo. - my $body = Texinfo::Convert::Texinfo::convert( - {'contents' => \@contents}); - - #chomp ($body); - $self->{'macros'}->{'insertcopying'} = { - 'args' => [{'text' => 'insertcopying', 'type' => 'macro_name'}], - 'cmdname' => 'macro', - 'extra' => {'macrobody' => - $self->_strip_macrobody_leading_space($body)} - }; - $inline_copying = 1; - print STDERR "INLINE_INSERTCOPYING as macro\n" if ($self->{'DEBUG'}); - } - push @{$closed_command->{'contents'}}, $end; - - # closing a menu command, but still in a menu. Open a menu_comment - if ($menu_commands{$closed_command->{'cmdname'}} - and $self->{'context_stack'}->[-1] eq 'menu') { - print STDERR "CLOSE MENU but still in menu context\n" - if ($self->{'DEBUG'}); - push @{$current->{'contents'}}, {'type' => 'menu_comment', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - push @{$self->{'context_stack'}}, 'preformatted'; - } - } else { - #print STDERR "LLLLLLLLLLLL Cannot be here...\n"; - } - $current = $self->_begin_preformatted($current) - if ($close_preformatted_commands{$end_command}); - } - } else { - $current = $self->_begin_preformatted($current) - if ($close_preformatted_commands{$command}); - } - # if a file was included, remove completly the include file command. - # Also ignore @setfilename in included file, as said in the manual. - if ($included_file or ($command eq 'setfilename' - and scalar(@{$self->{'input'}}) > 1)) { - # TODO keep the information with sourcemark - pop @{$current->{'contents'}}; - } elsif ($command eq 'setfilename' - and ($self->{'current_node'} or $self->{'current_section'})) { - $self->_command_warn($misc_cmd, $line_nr, - $self->__("\@%s after the first element"), $command); - # columnfractions - } elsif ($command eq 'columnfractions') { - # in a multitable, we are in a block_line_arg - if (!$current->{'parent'} or !$current->{'parent'}->{'cmdname'} - or $current->{'parent'}->{'cmdname'} ne 'multitable') { - $self->_command_error($current, $line_nr, - $self->__("\@%s only meaningful on a \@multitable line"), - $command); - } else { - # This is the multitable block_line_arg line context - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'line') { - $self->_bug_message("context $context instead of line for multitable", - $line_nr, $current); - } - $current = $current->{'parent'}; - $current->{'extra'}->{'max_columns'} = 0; - if (defined($misc_cmd->{'extra'}->{'misc_args'})) { - $current->{'extra'}->{'max_columns'} = - scalar(@{$misc_cmd->{'extra'}->{'misc_args'}}); - $current->{'extra'}->{'columnfractions'} = $misc_cmd->{'extra'}->{'misc_args'}; - } - push @{$current->{'contents'}}, { 'type' => 'before_item', - 'contents' => [], 'parent', $current }; - $current = $current->{'contents'}->[-1]; - } - } elsif ($root_commands{$command}) { - $current = $current->{'contents'}->[-1]; - delete $current->{'remaining_args'}; - $current->{'contents'} = []; - - # we never should be in a raw format bock, so we don't check for - # _ignore_global_commands($self) - # associate the section (not part) with the current node. - if ($command ne 'node' and $command ne 'part') { - if ($self->{'current_node'} - and !$self->{'current_node'}->{'extra'}->{'associated_section'}) { - $self->{'current_node'}->{'extra'}->{'associated_section'} = $current; - $current->{'extra'}->{'associated_node'} = $self->{'current_node'}; - } - if ($self->{'current_parts'}) { - $current->{'extra'}->{'associated_part'} = $self->{'current_parts'}->[-1]; - foreach my $part (@{$self->{'current_parts'}}) { - $part->{'extra'}->{'part_associated_section'} = $current; - if ($current->{'cmdname'} eq 'top') { - $self->line_warn(sprintf($self->__( - "\@%s should not be associated with \@top"), - $part->{'cmdname'}), $part->{'line_nr'}); - } - } - delete $self->{'current_parts'}; - } - $self->{'current_section'} = $current; - } elsif ($command eq 'part') { - push @{$self->{'current_parts'}}, $current; - if ($self->{'current_node'} - and !$self->{'current_node'}->{'extra'}->{'associated_section'}) { - $self->line_warn (sprintf($self->__( - "\@node precedes \@%s, but parts may not be associated with nodes"), - $command), $line_nr); - } - } - } - # do that last in order to have the line processed if one of the above - # case is also set. - } elsif ( - $current->{'contents'} - and (scalar(@{$current->{'contents'}}) == 1 - and (($current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command')) - or (scalar(@{$current->{'contents'}}) == 2 - and $current->{'contents'}->[-1]->{'cmdname'} - and ($current->{'contents'}->[-1]->{'cmdname'} eq 'c' - or $current->{'contents'}->[-1]->{'cmdname'} eq 'comment') - and $current->{'contents'}->[-2] - and $current->{'contents'}->[-2]->{'type'} - and $current->{'contents'}->[-2]->{'type'} eq 'empty_line_after_command'))) { - # empty line after a @menu or before a preformatted. Reparent to the menu - # or other format - if ($current->{'type'} - and $preformatted_contexts{$current->{'type'}}) { - my $parent = $current->{'parent'}; - if ($parent->{'type'} and $parent->{'type'} eq 'menu_comment' - and scalar(@{$parent->{'contents'}}) == 1) { - $parent = $parent->{'parent'}; - } - my $to_reparent = pop @{$parent->{'contents'}}; - print STDERR "LINE AFTER COMMAND IN PREFORMATTED ($to_reparent->{'type'})\n" if ($self->{'DEBUG'}); - while (@{$current->{'contents'}}) { - my $content = shift @{$current->{'contents'}}; - $content->{'parent'} = $parent; - push @{$parent->{'contents'}}, $content; - } - push @{$parent->{'contents'}}, $to_reparent; - } - } - - # this happens if there is a nesting of line @-commands on a line. - # they are reprocessed here. - if ($self->{'context_stack'}->[-1] eq 'line' - or $self->{'context_stack'}->[-1] eq 'def') { - print STDERR "Still opened line command $self->{'context_stack'}->[-1]:"._print_current($current) - if ($self->{'DEBUG'}); - if ($self->{'context_stack'}->[-1] eq 'def') { - while ($current->{'parent'} and !($current->{'parent'}->{'type'} - and $current->{'parent'}->{'type'} eq 'def_line')) { - $current = $self->_close_current($current, $line_nr); - } - } else { - while ($current->{'parent'} and !($current->{'type'} - and ($current->{'type'} eq 'block_line_arg' - or $current->{'type'} eq 'misc_line_arg'))) { - $current = $self->_close_current($current, $line_nr); - } - } - - # check for infinite loop bugs... - if ($current eq $current_old) { - my $indent = '- '; - my $tree_msg = $indent . _print_current($current); - while ($current->{'parent'}) { - $indent = '-'.$indent; - $current = $current->{'parent'}; - $tree_msg .= $indent . _print_current($current); - } - $self->_bug_message("Nothing closed while a line context remains\n$tree_msg", - $line_nr); - die; - } - - $current = $self->_end_line($current, $line_nr); - } - return $current; -} - -# $command may be undef if we are after a wrong misc command such as -# a buggy @tab. -sub _start_empty_line_after_command($$$) { - my $line = shift; - my $current = shift; - my $command = shift; - $line =~ s/^([^\S\r\n]*)//; - push @{$current->{'contents'}}, { 'type' => 'empty_line_after_command', - 'text' => $1, - 'parent' => $current, - }; - if (defined($command)) { - $current->{'contents'}->[-1]->{'extra'} = {'command' => $command}; - $command->{'extra'}->{'spaces_after_command'} = $current->{'contents'}->[-1]; - } - return $line; -} - -sub _check_empty_node($$$$) -{ - my $self = shift; - my $parsed_node = shift; - my $command = shift; - my $line_nr = shift; - if (!defined($parsed_node) or !$parsed_node->{'node_content'}) { - $self->line_error (sprintf($self->__("empty argument in \@%s"), - $command), $line_nr); - return 0; - } elsif ($parsed_node->{'normalized'} !~ /[^-]/) { - $self->line_error (sprintf($self->__("empty node name after expansion `%s'"), - Texinfo::Convert::Texinfo::convert({'contents' - => $parsed_node->{'node_content'}})), - $line_nr); - return 0; - } else { - return 1; - } -} - -sub _check_internal_node($$$) -{ - my $self = shift; - my $parsed_node = shift; - my $line_nr = shift; - if ($parsed_node and $parsed_node->{'manual_content'}) { - $self->line_error (sprintf($self->__("syntax for an external node used for `%s'"), - _node_extra_to_texi($parsed_node)), $line_nr) - } -} - -sub _check_node_label($$$$) -{ - my $self = shift; - my $parsed_node = shift; - my $command = shift; - my $line_nr = shift; - _check_internal_node($self, $parsed_node, $line_nr); - return _check_empty_node($self, $parsed_node, $command, $line_nr); -} - -sub _register_extra_menu_entry_information($$;$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - - foreach my $arg (@{$current->{'args'}}) { - if ($arg->{'type'} eq 'menu_entry_name') { - $current->{'extra'}->{'menu_entry_name'} = $arg; - my $normalized_menu_entry_name = - Texinfo::Convert::NodeNameNormalization::normalize_node($arg); - if ($normalized_menu_entry_name !~ /[^-]/) { - $self->line_warn(sprintf($self->__("empty menu entry name in `%s'"), - Texinfo::Convert::Texinfo::convert($current)), $line_nr); - } - } elsif ($arg->{'type'} eq 'menu_entry_node') { - $self->_isolate_last_space($arg, 'space_at_end_menu_node'); - my $parsed_entry_node = _parse_node_manual($arg); - if (! defined($parsed_entry_node)) { - if ($self->{'SHOW_MENU'}) { - $self->line_error ($self->__("empty node name in menu entry"), $line_nr); - } - } else { - $current->{'extra'}->{'menu_entry_node'} = $parsed_entry_node; - } - } elsif ($arg->{'type'} eq 'menu_entry_description') { - $current->{'extra'}->{'menu_entry_description'} = $arg; - } - } -} - -sub _enter_menu_entry_node($$$) -{ - my $self = shift; - my $current = shift; - my $line_nr = shift; - my $description = { 'type' => 'menu_entry_description', - 'contents' => [], - 'parent' => $current }; - push @{$current->{'args'}}, $description; - _register_extra_menu_entry_information($self, $current, $line_nr); - $current->{'line_nr'} = $line_nr; - $current = $description; - push @{$current->{'contents'}}, {'type' => 'preformatted', - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - push @{$self->{'context_stack'}}, 'preformatted'; - return $current; -} - -sub _register_command_arg($$$) -{ - my $self = shift; - my $current = shift; - my $type = shift; - my @contents = @{$current->{'contents'}}; - _trim_spaces_comment_from_content(\@contents); - if (scalar(@contents)) { - push @{$current->{'parent'}->{'extra'}->{$type}}, \@contents; - } else { - push @{$current->{'parent'}->{'extra'}->{$type}}, undef; - } -} - -sub _command_with_command_as_argument($) -{ - my $current = shift; - return ($current and $current->{'type'} - and $current->{'type'} eq 'block_line_arg' - and $current->{'parent'} - and $current->{'parent'}->{'cmdname'} and - ($current->{'parent'}->{'cmdname'} eq 'itemize' - or $item_line_commands{$current->{'parent'}->{'cmdname'}}) - and (scalar(@{$current->{'contents'}}) == 1 - or (scalar(@{$current->{'contents'}}) == 2 - and defined($current->{'contents'}->[0]->{'text'}) - and $current->{'contents'}->[0]->{'text'} - =~ /^[^\S\r\n]*/))) -} - -# $marked_as_invalid_command may be undef, if there is no -# tree element because the @-command construct is incorrect, for example -# wrong @tab. -sub _mark_and_warn_invalid($$$$$) -{ - my $self = shift; - my $command = shift; - my $invalid_parent = shift; - my $line_nr = shift; - my $marked_as_invalid_command = shift; - - if (defined($invalid_parent)) { - $self->line_warn(sprintf($self->__("\@%s should not appear in \@%s"), - $command, $invalid_parent), $line_nr); - $marked_as_invalid_command->{'extra'}->{'invalid_nesting'} = 1 - if (defined($marked_as_invalid_command)); - } -} -# the different types -#c 'menu_entry' -#c 'menu_entry' -# t 'menu_entry_leading_text' -# -#t 'macro_arg_name' -#t 'macro_arg_args' -# -#t 'raw' -# -#t 'misc_arg' -#c 'misc_line_arg' -# -#c 'block_line_arg' -# -#c 'brace_command_arg' -#c 'brace_command_context' -# -#c 'before_item' what comes after @*table, @itemize, @enumerate before -# an @item -# -#c 'paragraph' -# -#a 'def_line' -# -#special for @verb, type is the character - -# the main subroutine -sub _parse_texi($;$) -{ - my $self = shift; - my $root = shift; - - $root = { 'contents' => [], 'type' => 'text_root' } if (!defined($root)); - my $current = $root; - - my $line_nr; - - NEXT_LINE: - while (1) { - my $line; - ($line, $line_nr) = _next_text($self, $line_nr, $current); - last if (!defined($line)); - - if ($self->{'DEBUG'}) { - $current->{'HERE !!!!'} = 1; # marks where we are in the tree - if ($self->{'DEBUG'} >= 100) { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Purity = 1; - print STDERR "".Data::Dumper->Dump([$root], ['$root']); - } - my $line_text = ''; - $line_text = "$line_nr->{'line_nr'}.$line_nr->{'macro'}" if ($line_nr); - print STDERR "NEW LINE(".join('|', @{$self->{'context_stack'}}).":@{$self->{'conditionals_stack'}}:$line_text): $line"; - #print STDERR "CONTEXT_STACK ".join('|',@{$self->{'context_stack'}})."\n"; - delete $current->{'HERE !!!!'}; - } - - if (not - # raw format or verb - (($current->{'cmdname'} - and $block_commands{$current->{'cmdname'}} - and ($block_commands{$current->{'cmdname'}} eq 'raw' - or $block_commands{$current->{'cmdname'}} eq 'conditional')) - or - ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'verb') - ) - # not def line - and $self->{'context_stack'}->[-1] ne 'def') { - print STDERR "BEGIN LINE\n" if ($self->{'DEBUG'}); - $line =~ s/^([^\S\r\n]*)//; - push @{$current->{'contents'}}, { 'type' => 'empty_line', - 'text' => $1, - 'parent' => $current }; - } - - while (1) { - # in a raw or ignored conditional block command - if ($current->{'cmdname'} and - $block_commands{$current->{'cmdname'}} and - ($block_commands{$current->{'cmdname'}} eq 'raw' - or $block_commands{$current->{'cmdname'}} eq 'conditional')) { - # r?macro may be nested - if (($current->{'cmdname'} eq 'macro' - or $current->{'cmdname'} eq 'rmacro') - and $line =~ /^\s*\@r?macro\s+/) { - $line =~ s/\s*\@(r?macro)//; - push @{$current->{'contents'}}, { 'cmdname' => $1, - 'parent' => $current, - 'contents' => [], - 'extra' => {'arg_line' => $line }}; - $current = $current->{'contents'}->[-1]; - last; - # ifclear/ifset may be nested - } elsif (($current->{'cmdname'} eq 'ifclear' - or $current->{'cmdname'} eq 'ifset' - or $current->{'cmdname'} eq 'ifcommanddefined' - or $current->{'cmdname'} eq 'ifcommandnotdefined') - and $line =~ /^\s*\@$current->{'cmdname'}/) { - $line =~ s/\s*\@($current->{'cmdname'})//; - push @{$current->{'contents'}}, { 'cmdname' => $1, - 'parent' => $current, - 'contents' => [], - 'extra' => {'line' => $line }}; - $current = $current->{'contents'}->[-1]; - last; - } elsif ($line =~ /^(\s*?)\@end\s+([a-zA-Z][\w-]*)/ - and ($2 eq $current->{'cmdname'})) { - my $end_command = $2; - my $raw_command = $current; - $line =~ s/^(\s*?)(\@end\s+$current->{'cmdname'})//; - if ($1 eq '') { - # FIXME exclude other formats, like @macro, @ifset, @ignore? - if ($current->{'cmdname'} ne 'verbatim' - and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'raw') { - if ($current->{'contents'}->[-1]->{'text'} =~ s/(\n)//) { - push @{$current->{'contents'}}, {'type' => 'last_raw_newline', - 'text' => $1, 'parent' => $current}; - } - } - } else { - push @{$current->{'contents'}}, - { 'text' => $1, 'type' => 'raw', 'parent' => $current }; - $self->line_warn(sprintf($self->__("\@end %s should only appear at a line beginning"), - $end_command), $line_nr); - } - # if there is a user defined macro that expandes to spaces, there - # will be a spurious warning. - $self->line_warn(sprintf($self-> - __("superfluous argument to \@%s %s: %s"), 'end', $end_command, - $line), $line_nr) - if ($line =~ /\S/ and $line !~ /^\s*\@c(omment)?\b/); - # store toplevel macro specification - if (($end_command eq 'macro' or $end_command eq 'rmacro') - and (! $current->{'parent'} - or !$current->{'parent'}->{'cmdname'} - or ($current->{'parent'}->{'cmdname'} ne 'macro' - and $current->{'parent'}->{'cmdname'} ne 'rmacro'))) { - $current->{'extra'}->{'macrobody'} = - $self->_strip_macrobody_leading_space( - Texinfo::Convert::Texinfo::convert({ 'contents' - => $current->{'contents'} })); - if ($current->{'args'} and $current->{'args'}->[0] - and !_ignore_global_commands($self)) { - my $name = $current->{'args'}->[0]->{'text'}; - if (exists($self->{'macros'}->{$name})) { - $self->line_warn(sprintf($self->__("macro `%s' previously defined"), - $name), $current->{'line_nr'}); - $self->line_warn(sprintf($self->__( - "here is the previous definition of `%s'"), - $name), $self->{'macros'}->{$name}->{'line_nr'}); - } - if ($all_commands{$name}) { - $self->line_warn(sprintf($self->__( - "redefining Texinfo language command: \@%s"), - $name), $current->{'line_nr'}); - } - $self->{'macros'}->{$name} = $current - unless ($current->{'extra'}->{'invalid_syntax'}); - } - } - $current = $current->{'parent'}; - if ($block_commands{$end_command} eq 'conditional') { - # don't store ignored @if* - my $conditional = pop @{$current->{'contents'}}; - if (!defined($conditional->{'cmdname'} - or $conditional->{'cmdname'} ne $end_command)) { - $self->_bug_message("Ignored command is not the conditional $end_command", - $line_nr, $conditional); - die; - } - # Ignore until end of line - if ($line !~ /\n/) { - ($line, $line_nr) = _new_line($self, $line_nr, $conditional); - print STDERR "IGNORE CLOSE line: $line" if ($self->{'DEBUG'}); - } - print STDERR "CLOSED conditional $end_command\n" if ($self->{'DEBUG'}); - last; - } else { - print STDERR "CLOSED raw $end_command\n" if ($self->{'DEBUG'}); - $line = _start_empty_line_after_command($line, $current, $raw_command); - } - } else { - if (@{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command' - and $current->{'contents'}->[-1]->{'text'} !~ /\n/ - and $line !~ /\S/) { - $current->{'contents'}->[-1]->{'text'} .= $line; - } else { - push @{$current->{'contents'}}, - { 'text' => $line, 'type' => 'raw', 'parent' => $current }; - } - last; - } - # in @verb. type should be 'brace_command_arg' - } elsif ($current->{'parent'} and $current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'verb') { - # collect the first character if not already done - if (!defined($current->{'parent'}->{'type'})) { - if ($line =~ /^$/) { - $current->{'parent'}->{'type'} = ''; - $self->line_error(sprintf($self-> - __("\@%s without associated character"), 'verb'), $line_nr); - } else { - $line =~ s/^(.)//; - $current->{'parent'}->{'type'} = $1; - } - } - my $char = quotemeta($current->{'parent'}->{'type'}); - if ($line =~ s/^(.*?)$char\}/\}/) { - push @{$current->{'contents'}}, - { 'text' => $1, 'type' => 'raw', 'parent' => $current } - if ($1 ne ''); - print STDERR "END VERB\n" if ($self->{'DEBUG'}); - } else { - push @{$current->{'contents'}}, - { 'text' => $line, 'type' => 'raw', 'parent' => $current }; - print STDERR "LINE VERB: $line" if ($self->{'DEBUG'}); - last; - } - } - - # this mostly happens in the following cases: - # after expansion of user defined macro that doesn't end with EOL - # after a protection of @\n in @def* line - # at the end of an expanded Texinfo fragment - while ($line eq '') { - print STDERR "EMPTY TEXT\n" - if ($self->{'DEBUG'}); - ($line, $line_nr) = _next_text($self, $line_nr, $current); - if (!defined($line)) { - # end of the file or of a text fragment. - $current = _end_line ($self, $current, $line_nr); - # It may happen that there is an @include file on the line, it - # will be picked up at NEXT_LINE, beginning a new line - next NEXT_LINE; - } - } - - # handle user defined macros before anything else since - # their expansion may lead to changes in the line - # REMACRO - if ($line =~ /^\@([[:alnum:]][[:alnum:]-]*)/ - and ($self->{'macros'}->{$1} - or (exists $self->{'aliases'}->{$1} and - $self->{'macros'}->{$self->{'aliases'}->{$1}}))) { - $line =~ s/^\@([[:alnum:]][[:alnum:]-]*)//o; - my $command = $1; - my $alias_command; - if (exists($self->{'aliases'}->{$command})) { - $alias_command = $command; - $command = $self->{'aliases'}->{$command}; - } - - my $expanded_macro = $self->{'macros'}->{$command}; - my $args_number = scalar(@{$expanded_macro->{'args'}}) -1; - my $arguments = []; - if ($line =~ s/^\s*{[^\S\f]*//) { # macro with args - ($arguments, $line, $line_nr) = - _expand_macro_arguments($self, $expanded_macro, $line, $line_nr); - } elsif (($args_number >= 2) or ($args_number <1)) { - # as agreed on the bug-texinfo mailing list, no warn when zero - # arg and not called with {}. - $self->line_warn(sprintf($self->__( - "\@%s defined with zero or more than one argument should be invoked with {}"), - $command), $line_nr) - if ($args_number >= 2); - } else { - if ($line !~ /\n/) { - ($line, $line_nr) = _new_line($self, $line_nr, $expanded_macro); - $line = '' if (!defined($line)); - } - $line =~ s/^[^\S\f]*// if ($line =~ /[\S\f]/); - my $has_end_of_line = chomp $line; - $arguments = [$line]; - $line = "\n" if ($has_end_of_line); - } - my $expanded = _expand_macro_body ($self, $expanded_macro, - $arguments, $line_nr); - print STDERR "MACROBODY: $expanded".'||||||'."\n" - if ($self->{'DEBUG'}); - # empty result. It is ignored here. - if ($expanded eq '') { - next; - } - if ($self->{'MAX_MACRO_CALL_NESTING'} - and scalar(@{$self->{'macro_stack'}}) > $self->{'MAX_MACRO_CALL_NESTING'}) { - $self->line_warn(sprintf($self->__( -"macro call nested too deeply (set MAX_NESTED_MACROS to override; current value %d)"), - $self->{'MAX_MACRO_CALL_NESTING'}), $line_nr); - next; - } - if ($expanded_macro->{'cmdname'} eq 'macro') { - my $found = 0; - foreach my $macro (@{$self->{'macro_stack'}}) { - if ($macro->{'args'}->[0]->{'text'} eq $command) { - $self->line_error(sprintf($self->__( - "recursive call of macro %s is not allowed; use \@rmacro if needed"), - $command), $line_nr); - $found = 1; - last; - } - } - next if ($found); - } - - unshift @{$self->{'macro_stack'}}, $expanded_macro; - print STDERR "UNSHIFT MACRO_STACK: $expanded_macro->{'args'}->[0]->{'text'}\n" - if ($self->{'DEBUG'}); - my $expanded_lines = _text_to_lines($expanded); - chomp ($expanded_lines->[-1]); - pop @$expanded_lines if ($expanded_lines->[-1] eq ''); - print STDERR "MACRO EXPANSION LINES: ".join('|', @$expanded_lines) - ."|\nEND LINES MACRO EXPANSION\n" if ($self->{'DEBUG'}); - next if (!@$expanded_lines); - my $new_lines = _complete_line_nr($expanded_lines, - $line_nr->{'line_nr'}, $line_nr->{'file_name'}, - $expanded_macro->{'args'}->[0]->{'text'}, 1); - $line_nr->{'end_macro'} = 1; - unshift @{$self->{'input'}->[0]->{'pending'}}, [$line, $line_nr]; - my $new_text = shift @$new_lines; - ($line, $line_nr) = ($new_text->[0], $new_text->[1]); - unshift @{$self->{'input'}->[0]->{'pending'}}, @$new_lines; - - # Now handle all the cases that may lead to command closing - # or following character association with an @-command, especially - # accent command, that is handle @-command with braces that don't - # always need a brace. - - # The condition below is only caught right after command opening, - # otherwise we are in the 'args' and not right in the command container. - } elsif ($current->{'cmdname'} and - (defined($brace_commands{$current->{'cmdname'}}) or - $self->{'definfoenclose'}->{$current->{'cmdname'}}) - and $line !~ /^{/) { - # special case for @-command as argument of @itemize or @*table. - if (_command_with_command_as_argument($current->{'parent'})) { - delete $current->{'contents'}; - print STDERR "FOR PARENT \@$current->{'parent'}->{'parent'}->{'cmdname'} command_as_argument $current->{'cmdname'}\n" if ($self->{'DEBUG'}); - $current->{'type'} = 'command_as_argument' if (!$current->{'type'}); - $current->{'parent'}->{'parent'}->{'extra'}->{'command_as_argument'} - = $current; - $current = $current->{'parent'}; - # now accent commands - } elsif ($accent_commands{$current->{'cmdname'}}) { - if ($line =~ /^[^\S\r\n]/) { - if ($current->{'cmdname'} =~ /^[a-zA-Z]/) { - $line =~ s/^([^\S\r\n]+)//; - $current->{'extra'}->{'spaces'} = '' - if (!defined($current->{'extra'}->{'spaces'})); - $current->{'extra'}->{'spaces'} .= $1; - } else { - $self->line_warn(sprintf($self-> - __("accent command `\@%s' must not be followed by whitespace"), - $current->{'cmdname'}), $line_nr); - $current = $current->{'parent'}; - } - } elsif ($line =~ /^\@/) { - $self->line_error(sprintf($self-> - __("use braces to give a command as an argument to \@%s"), - $current->{'cmdname'}), $line_nr); - $current = $current->{'parent'}; - } elsif ($line =~ s/^(.)//o) { - print STDERR "ACCENT \@$current->{'cmdname'}\n" - if ($self->{'DEBUG'}); - my $following_arg = {'type' => 'following_arg', - 'parent' => $current}; - $following_arg->{'contents'} = [{ 'text' => $1, - 'parent' => $following_arg } ]; - $current->{'args'} = [ $following_arg ]; - if ($current->{'cmdname'} eq 'dotless' and $1 ne 'i' and $1 ne 'j') { - $self->line_error(sprintf($self-> - __("%c%s expects `i' or `j' as argument, not `%s'"), - ord('@'), $current->{'cmdname'}, $1), $line_nr); - } - if ($current->{'cmdname'} =~ /^[a-zA-Z]/) { - $current->{'args'}->[-1]->{'type'} = 'space_command_arg'; - } - delete $current->{'contents'}; - $current = $current->{'parent'}; - } else { # The accent is at end of line - # whitespace for commands with letter. - print STDERR "STRANGE ACC \@$current->{'cmdname'}\n" if ($self->{'DEBUG'}); - $self->line_warn(sprintf($self-> - __("accent command `\@%s' must not be followed by new line"), - $current->{'cmdname'}), $line_nr); - $current = $current->{'parent'}; - } - next; - } else { - # ignore space after a braced @-command like TeX does - if ($self->{'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME'} - and $line =~ s/^\s+//) { - next; - } - $self->line_error(sprintf($self->__("\@%s expected braces"), - $current->{'cmdname'}), $line_nr); - $current = $current->{'parent'}; - } - # maybe a menu entry beginning: a * at the beginning of a menu line - } elsif ($line =~ /^\*/ and $current->{'type'} - and $current->{'type'} eq 'preformatted' - and $current->{'parent'}->{'type'} - and ($current->{'parent'}->{'type'} eq 'menu_comment' - or $current->{'parent'}->{'type'} eq 'menu_entry_description') - and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'empty_line' - and $current->{'contents'}->[-1]->{'text'} eq '') { - print STDERR "MENU STAR\n" if ($self->{'DEBUG'}); - _abort_empty_line($self, $current); - $line =~ s/^\*//; - push @{$current->{'contents'}}, { 'type' => 'menu_star', - 'text' => '*' }; - # a space after a * at the beginning of a menu line - } elsif ($line =~ /^\s+/ and $current->{'contents'} - and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'menu_star') { - print STDERR "MENU ENTRY (certainly)\n" if ($self->{'DEBUG'}); - # this is the menu star collected previously - pop @{$current->{'contents'}}; - $line =~ s/^(\s+)//; - my $leading_text = '*' . $1; - if ($current->{'type'} eq 'preformatted' - and $current->{'parent'}->{'type'} - and $current->{'parent'}->{'type'} eq 'menu_comment') { - my $menu = $current->{'parent'}->{'parent'}; - if (!@{$current->{'contents'}}) { - pop @{$current->{'parent'}->{'contents'}}; - if (!scalar(@{$current->{'parent'}->{'contents'}})) { - pop @{$menu->{'contents'}}; - } - } - $current = $menu; - #print STDERR "Close MENU_COMMENT because new menu entry\n"; - } else { - # first parent preformatted, third is menu_entry - if ($current->{'type'} ne 'preformatted' - or $current->{'parent'}->{'type'} ne 'menu_entry_description' - or $current->{'parent'}->{'parent'}->{'type'} ne 'menu_entry' - or !$menu_commands{$current->{'parent'}->{'parent'}->{'parent'}->{'cmdname'}}) { - $self->_bug_message("Not in menu comment nor description", - $line_nr, $current); - } - $current = $current->{'parent'}->{'parent'}->{'parent'}; - } - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted after menu leading star", - $line_nr, $current); - } - push @{$current->{'contents'}}, { 'type' => 'menu_entry', - 'parent' => $current, - }; - $current = $current->{'contents'}->[-1]; - $current->{'args'} = [ { 'type' => 'menu_entry_leading_text', - 'text' => $leading_text, - 'parent' => $current }, - { 'type' => 'menu_entry_name', - 'contents' => [], - 'parent' => $current } ]; - $current = $current->{'args'}->[-1]; - # * followed by something else than a space. - } elsif ($current->{'contents'} and @{$current->{'contents'}} - and $current->{'contents'}->[-1]->{'type'} - and $current->{'contents'}->[-1]->{'type'} eq 'menu_star') { - print STDERR "ABORT MENU STAR ($line)\n" if ($self->{'DEBUG'}); - delete $current->{'contents'}->[-1]->{'type'}; - # after a separator in menu - } elsif ($current->{'args'} and @{$current->{'args'}} - and $current->{'args'}->[-1]->{'type'} - and $current->{'args'}->[-1]->{'type'} eq 'menu_entry_separator') { - my $separator = $current->{'args'}->[-1]->{'text'}; - # separator is ::, we concatenate and let the while restart - # in order to collect spaces below - if ($separator eq ':' and $line =~ s/^(:)//) { - $current->{'args'}->[-1]->{'text'} .= $1; - # a . not followed by a space. Not a separator. - } elsif ($separator eq '.' and $line =~ /^\S/) { - pop @{$current->{'args'}}; - $current = $current->{'args'}->[-1]; - $current = _merge_text($self, $current, $separator); - # here we collect spaces following separators. - } elsif ($line =~ s/^([^\S\r\n]+)//) { - # FIXME a trailing end of line could be considered to be part - # of the separator. Right now it is part of the description, - # since it is catched (in the next while) as one of the case below - $current->{'args'}->[-1]->{'text'} .= $1; - # now handle the menu part that was closed - } elsif ($separator =~ /^::/) { - print STDERR "MENU NODE no entry $separator\n" if ($self->{'DEBUG'}); - # it was previously registered as menu_entry_name, it is - # changed to node - $current->{'args'}->[-2]->{'type'} = 'menu_entry_node'; - $current = _enter_menu_entry_node($self, $current, $line_nr); - # end of the menu entry name - } elsif ($separator =~ /^:/) { - print STDERR "MENU ENTRY $separator\n" if ($self->{'DEBUG'}); - push @{$current->{'args'}}, { 'type' => 'menu_entry_node', - 'contents' => [], - 'parent' => $current }; - $current = $current->{'args'}->[-1]; - # anything else is the end of the menu node following a menu_entry_name - } else { - print STDERR "MENU NODE $separator\n" if ($self->{'DEBUG'}); - $current = _enter_menu_entry_node($self, $current, $line_nr); - } - # REMACRO - } elsif ($line =~ s/^\@(["'~\@\}\{,\.!\?\s\*\-\^`=:\|\/\\])//o - or $line =~ s/^\@([[:alnum:]][[:alnum:]-]*)//o) { - my $command = $1; - my $alias_command; - if (exists($self->{'aliases'}->{$command})) { - $alias_command = $command; - $command = $self->{'aliases'}->{$command}; - } - print STDERR "COMMAND $command\n" if ($self->{'DEBUG'}); - - - if ($command eq 'value') { - $line =~ s/^\s*// - if ($self->{'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME'}); - # REVALUE - if ($line =~ s/^{([\w\-][^\s{\\}~`\^+"<>|@]*)}//) { - my $value = $1; - if (exists($self->{'values'}->{$value})) { - if (!defined($self->{'values'}->{$value})) { - print STDERR "BUG? $value exists but not defined\n"; - } elsif (!ref($self->{'values'}->{$value})) { - $line = $self->{'values'}->{$value} . $line; - # the push @{$current->{'contents'}}, {}; prevents a trailing - # text to be merged, to avoid having the value tree modified. - } elsif (ref($self->{'values'}->{$value}) eq 'ARRAY') { - # we don't know for sure, but if we don't do it here it - # won't be done - _abort_empty_line($self, $current); - foreach my $content (@{$self->{'values'}->{$value}}) { - push @{$current->{'contents'}}, $content; - } - push @{$current->{'contents'}}, {}; - } elsif (ref($self->{'values'}->{$value}) eq 'HASH') { - # we don't know for sure, but if we don't do it here it - # won't be done - _abort_empty_line($self, $current); - my $content = $self->{'values'}->{$value}; - push @{$current->{'contents'}}, $content; - push @{$current->{'contents'}}, {}; - } - } else { - # caller should expand something along - # gdt('@{No value for `{value}\'@}', {'value' => $value}, {'keep_texi'=> 1}); - push @{$current->{'contents'}}, { 'cmdname' => 'value', - 'type' => $value }; - $self->line_warn( - sprintf($self->__("undefined flag: %s"), $value), $line_nr); - } - } else { - $self->line_error($self->__("bad syntax for \@value"), $line_nr); - } - next; - } - - if (defined($deprecated_commands{$command})) { - if ($deprecated_commands{$command} eq '') { - $self->line_warn(sprintf($self->__("%c%s is obsolete."), - ord('@'), $command), $line_nr); - } else { - $self->line_warn(sprintf($self->__("%c%s is obsolete; %s"), - ord('@'), $command, - $self->__($deprecated_commands{$command})), $line_nr); - } - } - - if (not _abort_empty_line($self, $current) - and $begin_line_commands{$command}) { - $self->line_warn( - sprintf($self->__("\@%s should only appear at a line beginning"), - $command), $line_nr); - } - - my $invalid_parent; - # error messages for forbidden constructs, like @node in @r, - # block command on line command, @xref in @anchor or node... - if ($current->{'parent'}) { - if ($current->{'parent'}->{'cmdname'}) { - if (defined($self->{'valid_nestings'}->{$current->{'parent'}->{'cmdname'}}) - and !$self->{'valid_nestings'}->{$current->{'parent'}->{'cmdname'}}->{$command} - # we make sure that we are on a root @-command line and - # not in contents - and (!$root_commands{$current->{'parent'}->{'cmdname'}} - or ($current->{'type'} - and $current->{'type'} eq 'misc_line_arg')) - # we make sure that we are on a block @-command line and - # not in contents - and (!($block_commands{$current->{'parent'}->{'cmdname'}}) - or ($current->{'type'} - and $current->{'type'} eq 'block_line_arg')) - # we make sure that we are on an @item/@itemx line and - # not in an @enumerate, @multitable or @itemize @item. - and (($current->{'parent'}->{'cmdname'} ne 'itemx' - and $current->{'parent'}->{'cmdname'} ne 'item') - or ($current->{'type'} - and $current->{'type'} eq 'misc_line_arg'))) { - $invalid_parent = $current->{'parent'}->{'cmdname'}; - } - } elsif ($self->{'context_stack'}->[-1] eq 'def' - # FIXME instead of hardcoding in_full_line_commands_no_refs - # it would be better to use the parent command valid_nesting. - and !$in_full_line_commands_no_refs{$command}) { - my $def_block = $current; - while ($def_block->{'parent'} and (!$def_block->{'parent'}->{'type'} - or $def_block->{'parent'}->{'type'} ne 'def_line')) { - $def_block = $def_block->{'parent'}; - } - - $invalid_parent = $def_block->{'parent'}->{'parent'}->{'cmdname'}; - } - } - - # special case with @ followed by a newline protecting end of lines - # in @def* - last if ($self->{'context_stack'}->[-1] eq 'def' and $command eq "\n"); - - unless ($self->{'no_paragraph_commands'}->{$command}) { - my $paragraph = _begin_paragraph($self, $current, $line_nr); - $current = $paragraph if ($paragraph); - } - - if ($self->{'close_paragraph_commands'}->{$command}) { - $current = _end_paragraph($self, $current, $line_nr); - } - if ($self->{'close_preformatted_commands'}->{$command}) { - $current = _end_preformatted($self, $current, $line_nr); - } - - # commands without braces and not block commands, ie no @end - if (defined($self->{'misc_commands'}->{$command})) { - if ($root_commands{$command} or $command eq 'bye') { - $current = _close_commands($self, $current, $line_nr, undef, - $command); - # root_level commands leads to setting a new root - # for the whole document and stuffing the preceding text - # as the first content, this is done only once. - if ($current->{'type'} and $current->{'type'} eq 'text_root') { - if ($command ne 'bye') { - $root = { 'type' => 'document_root', 'contents' => [$current] }; - $current->{'parent'} = $root; - $current = $root; - } - } else { - die if (!defined($current->{'parent'})); - $current = $current->{'parent'}; - } - } - - # noarg skipline skipspace text line lineraw /^\d$/ - my $arg_spec = $self->{'misc_commands'}->{$command}; - my $misc; - - if ($arg_spec eq 'noarg') { - my $ignored = 0; - my $only_in_headings = 0; - if ($command eq 'insertcopying') { - my $parent = $current; - while ($parent) { - if ($parent->{'cmdname'} and $parent->{'cmdname'} eq 'copying') { - $self->line_error( - sprintf($self->__("\@%s not allowed inside `\@%s' block"), - $command, $parent->{'cmdname'}), $line_nr); - $ignored = 1; - last; - } - $parent = $parent->{'parent'}; - } - } elsif ($in_heading_commands{$command}) { - $self->line_error( - sprintf($self->__("\@%s should only appear in heading or footing"), - $command), $line_nr); - $only_in_headings = 1; - } - if (!$ignored) { - $misc = {'cmdname' => $command, - 'parent' => $current}; - push @{$current->{'contents'}}, $misc; - # also sets invalid_nesting in that case - $misc->{'extra'}->{'invalid_nesting'} = 1 if ($only_in_headings); - $self->_register_global_command($command, $misc, $line_nr); - } - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $misc); - $current = $self->_begin_preformatted($current) - if ($close_preformatted_commands{$command}); - - # all the cases using the raw line - } elsif ($arg_spec eq 'skipline' or $arg_spec eq 'lineraw' - or $arg_spec eq 'special') { - # complete the line if there was a user macro expansion - if ($line !~ /\n/) { - my ($new_line, $new_line_nr) = _new_line($self, $line_nr, undef); - $line .= $new_line if (defined($new_line)); - } - $misc = {'cmdname' => $command, - 'parent' => $current}; - my $args = []; - my $has_comment; - if ($arg_spec eq 'lineraw' or $arg_spec eq 'skipline') { - $args = [ $line ]; - } elsif ($arg_spec eq 'special') { - ($args, $has_comment) - = $self->_parse_special_misc_command($line, $command, $line_nr); - $misc->{'extra'}->{'arg_line'} = $line; - } - - # if using the @set txi* instead of a proper @-command, replace - # by the tree obtained with the @-command. Even though - # _end_line is called below, as $current is not misc_line_arg - # there should not be anything done in addition than what is - # done for @clear or @set. - if (($command eq 'set' or $command eq 'clear') - and scalar(@$args) >= 1 - and $set_flag_command_equivalent{$args->[0]}) { - my $arg; - if ($command eq 'set') { - $arg = 'on'; - } else { - $arg = 'off'; - } - $command = $set_flag_command_equivalent{$args->[0]}; - $misc = {'cmdname' => $command, - 'parent' => $current, - 'line_nr' => $line_nr, - 'extra' => {'misc_args' => [$arg]}}; - my $misc_line_args = {'type' => 'misc_line_arg', - 'parent' => $misc}; - $misc->{'args'} = [$misc_line_args]; - my $spaces_after_command - = { 'type' => 'empty_spaces_after_command', - 'text' => ' ', - 'parent' => $misc_line_args, - 'extra' => {'command' => $misc} }; - $misc->{'extra'}->{'spaces_after_command'} - = $spaces_after_command; - $misc_line_args->{'contents'} = [ $spaces_after_command, - { 'text' => $arg, - 'parent' => $misc_line_args, }, - { 'text' => "\n", - 'parent' => $misc_line_args, - 'type' => 'spaces_at_end', } ]; - push @{$current->{'contents'}}, $misc; - } else { - push @{$current->{'contents'}}, $misc; - foreach my $arg (@$args) { - push @{$misc->{'args'}}, - { 'type' => 'misc_arg', 'text' => $arg, - 'parent' => $current->{'contents'}->[-1] }; - } - $misc->{'extra'}->{'misc_args'} = $args - if (scalar(@$args) and $arg_spec ne 'skipline'); - } - if (! _ignore_global_commands($self)) { - if ($command eq 'raisesections') { - $self->{'sections_level'}++; - } elsif ($command eq 'lowersections') { - $self->{'sections_level'}--; - } elsif ($command eq 'novalidate') { - $self->{'novalidate'} = 1; - } - } - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $misc); - $self->_register_global_command($command, $misc, $line_nr); - # the end of line is ignored for special commands - if ($arg_spec ne 'special' or !$has_comment) { - $current = _end_line($self, $current, $line_nr); - } - - last NEXT_LINE if ($command eq 'bye'); - # Even if _end_line is called, it is not done since there is - # no misc_line_arg - $current = $self->_begin_preformatted($current) - if ($close_preformatted_commands{$command}); - last; - } else { - # $arg_spec is text, line, skipspace or a number - my $line_arg = 0; - $line_arg = 1 if ($arg_spec ne 'skipspace'); - if ($command eq 'item' or $command eq 'itemx' - or $command eq 'headitem' or $command eq 'tab') { - my $parent; - # itemize or enumerate - if ($parent = _item_container_parent($current)) { - if ($command eq 'item') { - print STDERR "ITEM_CONTAINER\n" if ($self->{'DEBUG'}); - $parent->{'items_count'}++; - $misc = { 'cmdname' => $command, 'parent' => $parent, - 'contents' => [], - 'extra' => - {'item_number' => $parent->{'items_count'}} }; - push @{$parent->{'contents'}}, $misc; - $current = $parent->{'contents'}->[-1]; - } else { - $self->line_error(sprintf($self->__( - "\@%s not meaningful inside `\@%s' block"), - $command, $parent->{'cmdname'}), $line_nr); - } - $current = $self->_begin_preformatted($current); - # *table - } elsif ($parent = _item_line_parent($current)) { - if ($command eq 'item' or $command eq 'itemx') { - print STDERR "ITEM_LINE\n" if ($self->{'DEBUG'}); - $current = $parent; - $self->_gather_previous_item($current, $command, $line_nr); - $misc = { 'cmdname' => $command, 'parent' => $current }; - push @{$current->{'contents'}}, $misc; - # since in the %misc_commands hash the entry for those - # commands is 'skipspace' we set $line_arg here. - $line_arg = 1; - } else { - $self->line_error(sprintf($self->__( - "\@%s not meaningful inside `\@%s' block"), - $command, $parent->{'cmdname'}), $line_nr); - $current = $self->_begin_preformatted($current); - } - # multitable - } elsif ($parent = _item_multitable_parent($current)) { - if ($command eq 'item' or $command eq 'headitem' - or $command eq 'tab') { - if (!$parent->{'extra'}->{'max_columns'}) { - $self->line_warn( - sprintf($self->__("\@%s in empty multitable"), - $command), $line_nr); - } elsif ($command eq 'tab') { - my $row = $parent->{'contents'}->[-1]; - die if (!$row->{'type'}); - if ($row->{'type'} eq 'before_item') { - $self->line_error($self->__("\@tab before \@item"), $line_nr); - } elsif ($row->{'cells_count'} >= $parent->{'extra'}->{'max_columns'}) { - $self->line_error(sprintf($self->__( - "too many columns in multitable item (max %d)"), - $parent->{'extra'}->{'max_columns'}), $line_nr); - } else { - $row->{'cells_count'}++; - $misc = { 'cmdname' => $command, - 'parent' => $row, - 'contents' => [], - 'extra' => - {'cell_number' => $row->{'cells_count'}} }; - push @{$row->{'contents'}}, $misc; - $current = $row->{'contents'}->[-1]; - #$current = $self->_begin_preformatted($current); - print STDERR "TAB\n" if ($self->{'DEBUG'}); - } - } else { - print STDERR "ROW\n" if ($self->{'DEBUG'}); - $parent->{'rows_count'}++; - my $row = { 'type' => 'row', 'contents' => [], - 'cells_count' => 1, - 'extra' => {'row_number' => $parent->{'rows_count'} }, - 'parent' => $parent }; - push @{$parent->{'contents'}}, $row; - $misc = { 'cmdname' => $command, - 'parent' => $row, - 'contents' => [], - 'extra' => {'cell_number' => 1}}; - push @{$row->{'contents'}}, $misc; - $current = $row->{'contents'}->[-1]; - } - } else { - $self->line_error(sprintf($self->__( - "\@%s not meaningful inside `\@%s' block"), - $command, $parent->{'cmdname'}), $line_nr); - } - $current = $self->_begin_preformatted($current); - } elsif ($command eq 'tab') { - $self->line_error($self->__( - "ignoring \@tab outside of multitable"), $line_nr); - $current = $self->_begin_preformatted($current); - } else { - $self->line_error (sprintf($self->__( - "\@%s outside of table or list"), $command), $line_nr); - $current = $self->_begin_preformatted($current); - } - $misc->{'line_nr'} = $line_nr if (defined($misc)); - } else { - $misc = { 'cmdname' => $command, 'parent' => $current, - 'line_nr' => $line_nr }; - push @{$current->{'contents'}}, $misc; - if ($self->{'sections_level'} and $root_commands{$command} - and $command ne 'node' and $command ne 'part') { - $current->{'contents'}->[-1]->{'extra'}->{'sections_level'} - = $self->{'sections_level'}; - } - # def*x - if ($def_commands{$command}) { - my $base_command = $command; - $base_command =~ s/x$//; - # check that the def*x is first after @def*, no paragraph - # in-between. - my $after_paragraph = _check_no_text($current); - push @{$self->{'context_stack'}}, 'def'; - $current->{'contents'}->[-1]->{'type'} = 'def_line'; - $current->{'contents'}->[-1]->{'extra'} = - {'def_command' => $base_command, - 'original_def_cmdname' => $command}; - if ($current->{'cmdname'} - and $current->{'cmdname'} eq $base_command) { - pop @{$current->{'contents'}}; - _gather_def_item($current, $command); - push @{$current->{'contents'}}, $misc; - } - if (!$current->{'cmdname'} - or $current->{'cmdname'} ne $base_command - or $after_paragraph) { - $self->line_error(sprintf($self->__( - "must be after `\@%s' to use `\@%s'"), - $base_command, $command), $line_nr); - $current->{'contents'}->[-1]->{'extra'}->{'not_after_command'} = 1; - } - } - } - # a container for what is on the @-command line, considered to - # be the @-command argument - if ($line_arg) { - $current = $current->{'contents'}->[-1]; - $current->{'args'} = [{ 'type' => 'misc_line_arg', - 'contents' => [], - 'parent' => $current }]; - # @node is the only misc command with args separated with comma - # FIXME a 3 lingering here deep into the code may not - # be very wise... However having a hash only for one @-command - # is not very appealing either... - if ($command eq 'node') { - $current->{'remaining_args'} = 3; - } elsif ($command eq 'author') { - my $parent = $current; - my $found; - while ($parent->{'parent'}) { - $parent = $parent->{'parent'}; - last if ($parent->{'type'} - and $parent->{'type'} eq 'brace_command_context'); - if ($parent->{'cmdname'}) { - if ($parent->{'cmdname'} eq 'titlepage') { - push @{$self->{'extra'}->{'author'}}, $current; - $current->{'extra'}->{'titlepage'} = $parent; - $found = 1; - } elsif ($parent->{'cmdname'} eq 'quotation' or - $parent->{'cmdname'} eq 'smallquotation') { - push @{$parent->{'extra'}->{'authors'}}, $current; - $current->{'extra'}->{'quotation'} = $parent; - $found = 1; - } - last if ($found); - } - } - if (!$found) { - $self->line_warn(sprintf($self->__( - "\@%s not meaningful outside `\@titlepage' and `\@quotation' environments"), - $command), $current->{'line_nr'}); - } - } elsif ($command eq 'dircategory' and $self->{'current_node'}) { - $self->line_warn($self->__("\@dircategory after first node"), - $line_nr); - } - - $current = $current->{'args'}->[-1]; - push @{$self->{'context_stack'}}, 'line' - unless ($def_commands{$command}); - } - $line = _start_empty_line_after_command($line, $current, $misc); - } - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $misc); - - $self->_register_global_command($command, $misc, $line_nr); - if ($command eq 'dircategory' - and ! _ignore_global_commands($self)) { - push @{$self->{'info'}->{'dircategory_direntry'}}, $misc; - } - # @-command with matching @end opening - } elsif (exists($block_commands{$command})) { - if ($command eq 'macro' or $command eq 'rmacro') { - my $macro = _parse_macro_command_line($self, $command, $line, - $current, $line_nr); - push @{$current->{'contents'}}, $macro; - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $current->{'contents'}->[-1]); - $current = $current->{'contents'}->[-1]; - last; - } elsif ($block_commands{$command} eq 'conditional') { - my $ifvalue_true = 0; - if ($command eq 'ifclear' or $command eq 'ifset') { - # REVALUE - if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) { - my $name = $1; - if ((exists($self->{'values'}->{$name}) and $command eq 'ifset') - or (!exists($self->{'values'}->{$name}) - and $command eq 'ifclear')) { - $ifvalue_true = 1; - } - print STDERR "CONDITIONAL \@$command $name: $ifvalue_true\n" if ($self->{'DEBUG'}); - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - } - } elsif ($command eq 'ifcommanddefined' - or $command eq 'ifcommandnotdefined') { - # REMACRO - if ($line =~ /^\s+([[:alnum:]][[:alnum:]\-]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) { - my $name = $1; - my $command_is_defined = ( - exists($Texinfo::Common::all_commands{$name}) - or $self->{'macros'}->{$name} - or $self->{'definfoenclose'}->{$name} - or $self->{'aliases'}->{$name} - or $self->{'command_index_prefix'}->{$name} - ); - if (($command_is_defined - and $command eq 'ifcommanddefined') - or (! $command_is_defined - and $command eq 'ifcommandnotdefined')) { - $ifvalue_true = 1; - } - print STDERR "CONDITIONAL \@$command $name: $ifvalue_true\n" if ($self->{'DEBUG'}); - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - } - } elsif ($command =~ /^ifnot(.*)/) { - $ifvalue_true = 1 if !($self->{'expanded_formats_hash'}->{$1} - # exception as explained in the texinfo manual - or ($1 eq 'info' - and $self->{'expanded_formats_hash'}->{'plaintext'})); - print STDERR "CONDITIONAL \@$command format $1: $ifvalue_true\n" if ($self->{'DEBUG'}); - } else { - die unless ($command =~ /^if(.*)/); - $ifvalue_true = 1 if ($self->{'expanded_formats_hash'}->{$1} - or ($1 eq 'info' - and $self->{'expanded_formats_hash'}->{'plaintext'})); - print STDERR "CONDITIONAL \@$command format $1: $ifvalue_true\n" if ($self->{'DEBUG'}); - } - if ($ifvalue_true) { - push @{$self->{'conditionals_stack'}}, $command; - } else { - push @{$current->{'contents'}}, { 'cmdname' => $command, - 'parent' => $current, - 'contents' => [] }; - $current = $current->{'contents'}->[-1]; - } - # FIXME(Karl) ignore what is remaining on the line, to eat - # the end of line? - last; - } else { - my $block; - # a menu command closes a menu_comment, but not the other - # block commands. This won't catch menu commands buried in - # other formats (that are incorrect anyway). - if ($menu_commands{$command} and $current->{'type'} - and ($current->{'type'} eq 'menu_comment' - or $current->{'type'} eq 'menu_entry_description')) { - - my $menu; - - $menu = $current->{'parent'}; - pop @{$menu->{'contents'}} - if (!@{$current->{'contents'}}); - - my $context = pop @{$self->{'context_stack'}}; - if ($context ne 'preformatted') { - $self->_bug_message("context $context instead of preformatted in new menu", - $line_nr, $current); - } - - if ($menu->{'type'} and $menu->{'type'} eq 'menu_entry') { - $menu = $menu->{'parent'}; - } - - $current = $menu; - } - # the def command holds a line_def* which corresponds with the - # definition line. This allows to have a treatement similar - # with def*x. - if ($def_commands{$command}) { - push @{$self->{'context_stack'}}, 'def'; - $block = { 'parent' => $current, - 'cmdname' => $command, - 'contents' => [] }; - push @{$current->{'contents'}}, $block; - $current = $current->{'contents'}->[-1]; - push @{$current->{'contents'}}, { - 'type' => 'def_line', - 'parent' => $current, - 'line_nr' => $line_nr, - 'extra' => - {'def_command' => $command, - 'original_def_cmdname' => $command} - }; - } else { - $block = { 'cmdname' => $command, - 'parent' => $current, - 'contents' => [] }; - push @{$current->{'contents'}}, $block; - } - $current = $current->{'contents'}->[-1]; - - if ($block_arg_commands{$command}) { - if ($preformatted_commands{$command}) { - push @{$self->{'context_stack'}}, 'preformatted'; - } elsif ($format_raw_commands{$command}) { - push @{$self->{'context_stack'}}, 'rawpreformatted'; - if ($self->{'expanded_formats_hash'}->{$command} - and $self->{'expanded_formats_stack'}->[-1]) { - push @{$self->{'expanded_formats_stack'}}, $command; - } else { - push @{$self->{'expanded_formats_stack'}}, 0; - } - } - if ($region_commands{$command}) { - if (@{$self->{'regions_stack'}}) { - $self->line_error( - sprintf($self->__("region %s inside region %s is not allowed"), - $command, $self->{'regions_stack'}->[-1]->{'cmdname'}), - $line_nr); - } - push @{$self->{'regions_stack'}}, $block; - } - if ($menu_commands{$command}) { - if ($self->{'context_stack'}->[-1] eq 'preformatted') { - push @{$self->{'context_stack'}}, 'preformatted'; - } else { - push @{$self->{'context_stack'}}, 'menu'; - } - if (! _ignore_global_commands($self)) { - push @{$self->{'info'}->{'dircategory_direntry'}}, $block - if ($command eq 'direntry'); - if ($self->{'current_node'}) { - if ($command eq 'direntry') { - if ($self->{'SHOW_MENU'}) { - $self->line_warn($self->__("\@direntry after first node"), - $line_nr); - } - } elsif ($command eq 'menu') { - push @{$self->{'current_node'}->{'menus'}}, $current; - } - } elsif ($command ne 'direntry') { - if ($self->{'SHOW_MENU'}) { - $self->line_error(sprintf($self->__("\@%s seen before first \@node"), - $command), $line_nr); - $self->line_error($self->__( - "perhaps your \@top node should be wrapped in \@ifnottex rather than \@ifinfo?"), - $line_nr, 1); - } - if ($command eq 'menu') { - push @{$self->{'info'}->{'unassociated_menus'}}, $current; - } - } - } - } - $current->{'args'} = [ { - 'type' => 'block_line_arg', - 'contents' => [], - 'parent' => $current } ]; - - $current->{'remaining_args'} = $block_commands{$command} -1 - if ($block_commands{$command} =~ /^\d+$/ - and $block_commands{$command} -1 > 0); - $current = $current->{'args'}->[-1]; - push @{$self->{'context_stack'}}, 'line' - unless ($def_commands{$command}); - } - $block->{'line_nr'} = $line_nr; - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $block); - $self->_register_global_command($command, $block, $line_nr); - - $line = _start_empty_line_after_command($line, $current, $block); - } - } elsif (defined($brace_commands{$command}) - or defined($self->{'definfoenclose'}->{$command})) { - - push @{$current->{'contents'}}, { 'cmdname' => $command, - 'parent' => $current, - 'contents' => [] }; - $current->{'contents'}->[-1]->{'line_nr'} = $line_nr - if ($keep_line_nr_brace_commands{$command}); - $self->_mark_and_warn_invalid($command, $invalid_parent, - $line_nr, $current->{'contents'}->[-1]); - $current = $current->{'contents'}->[-1]; - if ($command eq 'click') { - $current->{'extra'}->{'clickstyle'} = $self->{'clickstyle'}; - } elsif ($command eq 'kbd') { - if ($self->{'context_stack'}->[-1] eq 'preformatted' - and $self->{'kbdinputstyle'} ne 'distinct') { - $current->{'extra'}->{'code'} = 1; - } elsif ($self->{'kbdinputstyle'} eq 'code' - or ($self->{'kbdinputstyle'} eq 'example' - and $self->_in_code($current->{'parent'}))) { - $current->{'extra'}->{'code'} = 1; - } - } - if ($self->{'definfoenclose'}->{$command}) { - $current->{'type'} = 'definfoenclose_command'; - $current->{'extra'} = { - 'begin' => $self->{'definfoenclose'}->{$command}->[0], - 'end' => $self->{'definfoenclose'}->{$command}->[1] }; - } - } elsif (exists ($no_brace_commands{$command})) { - push @{$current->{'contents'}}, - { 'cmdname' => $command, 'parent' => $current }; - # FIXME generalize? - if ($command eq '\\' and $self->{'context_stack'}->[-1] ne 'math') { - $self->line_warn(sprintf($self->__("\@%s should only appear in math context"), - $command), $line_nr); - } - if ($command eq "\n") { - $current = _end_line($self, $current, $line_nr); - last; - } - } else { - $self->line_error(sprintf($self->__("unknown command `%s'"), - $command), $line_nr); - } - - } elsif ($line =~ s/^([{}@,:\t.\f])//) { - my $separator = $1; - print STDERR "SEPARATOR: $separator\n" if ($self->{'DEBUG'}); - if ($separator eq '@') { - # this may happen with a @ at the very end of a file, therefore - # not followed by anything. - $self->line_error($self->__("unexpected \@"), $line_nr); - } elsif ($separator eq '{') { - _abort_empty_line($self, $current); - if ($current->{'cmdname'} - and (defined($brace_commands{$current->{'cmdname'}}) - or $self->{'definfoenclose'}->{$current->{'cmdname'}})) { - my $command = $current->{'cmdname'}; - $current->{'args'} = [ { 'parent' => $current, - 'contents' => [] } ]; - $current->{'remaining_args'} = $brace_commands{$command} -1 - if ($brace_commands{$command} and $brace_commands{$command} -1); - $current->{'line_nr'} = $line_nr if ($brace_commands{$command}); - if ($self->{'definfoenclose'}->{$command}) { - $current->{'remaining_args'} = 0; - } - $current = $current->{'args'}->[-1]; - if ($context_brace_commands{$command}) { - if ($command eq 'caption' or $command eq 'shortcaption') { - my $float; - if (!$current->{'parent'}->{'parent'} - or !$current->{'parent'}->{'parent'}->{'cmdname'} - or $current->{'parent'}->{'parent'}->{'cmdname'} ne 'float') { - $float = $current->{'parent'}; - while ($float->{'parent'} and !($float->{'cmdname'} - and $float->{'cmdname'} eq 'float')) { - $float = $float->{'parent'}; - } - if (!($float->{'cmdname'} and $float->{'cmdname'} eq 'float')) { - $self->line_error(sprintf($self->__( - "\@%s is not meaningful outside `\@float' environment"), - $command), $line_nr); - $float = undef; - } else { - $self->line_warn(sprintf($self->__( - "\@%s should be right below `\@float'"), - $command), $line_nr); - } - } else { - $float = $current->{'parent'}->{'parent'}; - } - if ($float) { - if ($float->{'extra'}->{$command}) { - $self->line_warn(sprintf($self->__("ignoring multiple \@%s"), - $command), $line_nr); - } else { - $current->{'parent'}->{'extra'}->{'float'} = $float; - $float->{'extra'}->{$command} = $current->{'parent'}; - } - } - } - push @{$self->{'context_stack'}}, $command; - $line =~ s/([^\S\f\n]*)//; - $current->{'type'} = 'brace_command_context'; - push @{$current->{'contents'}}, { 'type' => 'empty_spaces_before_argument', - 'text' => $1, - 'parent' => $current }; - $current->{'parent'}->{'extra'}->{'spaces_before_argument'} - = $current->{'contents'}->[-1]; - } else { - $current->{'type'} = 'brace_command_arg'; - if ($brace_commands{$command} - and ($brace_commands{$command} > 1 - or $simple_text_commands{$command})) { - push @{$current->{'contents'}}, - {'type' => 'empty_spaces_before_argument', - 'text' => '' }; - $current->{'parent'}->{'extra'}->{'spaces_before_argument'} - = $current->{'contents'}->[-1]; - } - if ($inline_commands{$command}) { - # this is changed when the first argument is known. - push @{$self->{'expanded_formats_stack'}}, 0; - push @{$self->{'context_stack'}}, $command - if ($command eq 'inlineraw'); - } - } - print STDERR "OPENED \@$current->{'parent'}->{'cmdname'}, remaining: " - .(defined($current->{'parent'}->{'remaining_args'}) ? "remaining: $current->{'parent'}->{'remaining_args'}, " : '') - .($current->{'type'} ? "type: $current->{'type'}" : '')."\n" - if ($self->{'DEBUG'}); - } elsif ($current->{'parent'} - and (($current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'multitable') - or ($current->{'parent'}->{'type'} - and $current->{'parent'}->{'type'} eq 'def_line'))) { - push @{$current->{'contents'}}, - { 'type' => 'bracketed', 'contents' => [], - 'parent' => $current }; - $current = $current->{'contents'}->[-1]; - # we need the line number here in case @ protects end of line - $current->{'line_nr'} = $line_nr - if ($current->{'parent'}->{'parent'}->{'type'} - and $current->{'parent'}->{'parent'}->{'type'} eq 'def_line'); - push @{$current->{'contents'}}, - {'type' => 'empty_spaces_before_argument', - 'text' => '' }; - print STDERR "BRACKETED in def/multitable\n" if ($self->{'DEBUG'}); - $current->{'extra'}->{'spaces_before_argument'} - = $current->{'contents'}->[-1]; - - # lone braces accepted right in a rawpreformatted - } elsif ($current->{'type'} - and $current->{'type'} eq 'rawpreformatted') { - push @{$current->{'contents'}}, {'text' => '{' }; - # matching braces accepted in a rawpreformatted or math or ignored - # code - } elsif ($self->{'context_stack'}->[-1] eq 'math' - or $self->{'context_stack'}->[-1] eq 'rawpreformatted' - or $self->{'context_stack'}->[-1] eq 'inlineraw' - or $self->_ignore_global_commands()) { - push @{$current->{'contents'}}, - { 'type' => 'bracketed', 'contents' => [], - 'parent' => $current, 'line_nr' => $line_nr }; - $current = $current->{'contents'}->[-1]; - print STDERR "BRACKETED in math\n" if ($self->{'DEBUG'}); - } else { - $self->line_error(sprintf($self->__("misplaced %c"), - ord('{')), $line_nr); - } - - } elsif ($separator eq '}') { - _abort_empty_line($self, $current); - #print STDERR "GGGGG". _print_current ($current); - if ($current->{'type'} and ($current->{'type'} eq 'bracketed')) { - $current = $current->{'parent'}; - # the following will not happen for footnote if there is - # a paragraph withing the footnote - } elsif ($current->{'parent'} - and $current->{'parent'}->{'cmdname'} - and (exists $brace_commands{$current->{'parent'}->{'cmdname'}} - or $self->{'definfoenclose'}->{$current->{'parent'}->{'cmdname'}})) { - # for math and footnote out of paragraph - if ($context_brace_commands{$current->{'parent'}->{'cmdname'}}) { - my $context_command = pop @{$self->{'context_stack'}}; - if ($context_command ne $current->{'parent'}->{'cmdname'}) { - $self->_bug_message("context $context_command instead of brace command $current->{'parent'}->{'cmdname'}", - $line_nr, $current); - die; - } - } - # first is the arg. - - if ($brace_commands{$current->{'parent'}->{'cmdname'}} - and ($brace_commands{$current->{'parent'}->{'cmdname'}} > 1 - or $simple_text_commands{$current->{'parent'}->{'cmdname'}}) - and $current->{'parent'}->{'cmdname'} ne 'math') { - # @inline* always have end spaces considered as normal text - $self->_isolate_last_space($current) - unless ($inline_commands{$current->{'parent'}->{'cmdname'}}); - $self->_register_command_arg($current, 'brace_command_contents'); - # Remove empty arguments, as far as possible - _remove_empty_content_arguments($current); - } - my $closed_command = $current->{'parent'}->{'cmdname'}; - print STDERR "CLOSING(brace) \@$current->{'parent'}->{'cmdname'}\n" - if ($self->{'DEBUG'}); - delete $current->{'parent'}->{'remaining_args'}; - if (defined($brace_commands{$closed_command}) - and $brace_commands{$closed_command} == 0 - and @{$current->{'contents'}}) { - $self->line_warn(sprintf($self->__( - "command \@%s does not accept arguments"), - $closed_command), $line_nr); - } - if ($current->{'parent'}->{'cmdname'} eq 'anchor') { - $current->{'parent'}->{'line_nr'} = $line_nr; - my $parsed_anchor = _parse_node_manual($current); - if (_check_node_label($self, $parsed_anchor, - $current->{'parent'}->{'cmdname'}, $line_nr)) { - _register_label($self, $current->{'parent'}, - $parsed_anchor, $line_nr); - if (@{$self->{'regions_stack'}}) { - $current->{'extra'}->{'region'} = $self->{'regions_stack'}->[-1]; - } - } - } elsif ($ref_commands{$current->{'parent'}->{'cmdname'}}) { - my $ref = $current->{'parent'}; - if (@{$ref->{'args'}}) { - my @args = @{$ref->{'extra'}->{'brace_command_contents'}}; - if (($closed_command eq 'inforef' - and !defined($args[0]) and !defined($args[2])) - or ($closed_command ne 'inforef' - and !defined($args[0]) and !defined($args[3]) - and !defined($args[4]))) { - $self->line_warn(sprintf($self->__( - "command \@%s missing a node or external manual argument"), - $closed_command), $line_nr); - } else { - my $parsed_ref_node = _parse_node_manual($ref->{'args'}->[0]); - $ref->{'extra'}->{'node_argument'} = $parsed_ref_node - if (defined($parsed_ref_node)); - if ($closed_command ne 'inforef' - and !defined($args[3]) and !defined($args[4]) - and !$parsed_ref_node->{'manual_content'} - and ! _ignore_global_commands($self)) { - push @{$self->{'internal_references'}}, $ref; - } - } - if (defined($args[1])) { - my $normalized_cross_ref_name = - Texinfo::Convert::NodeNameNormalization::normalize_node( - {'contents' => $args[1]}); - if ($normalized_cross_ref_name !~ /[^-]/) { - $self->line_warn(sprintf($self->__( - "in \@%s empty cross reference name after expansion `%s'"), - $closed_command, - Texinfo::Convert::Texinfo::convert({'contents' => $args[1]})), - $line_nr); - } - } - if ($closed_command ne 'inforef' and defined($args[2])) { - my $normalized_cross_ref_title = - Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => $args[2]}); - if ($normalized_cross_ref_title !~ /[^-]/) { - $self->line_warn(sprintf($self->__( - "in \@%s empty cross reference title after expansion `%s'"), - $closed_command, - Texinfo::Convert::Texinfo::convert({'contents' => $args[2]})), - $line_nr); - } - } - } - } elsif ($current->{'parent'}->{'cmdname'} eq 'image') { - my $image = $current->{'parent'}; - if (!@{$image->{'args'}} - or !@{$image->{'extra'}->{'brace_command_contents'}} - or !defined($image->{'extra'}->{'brace_command_contents'}->[0])) { - $self->line_error( - $self->__("\@image missing filename argument"), $line_nr); - } - } elsif($current->{'parent'}->{'cmdname'} eq 'dotless') { - my $dotless = $current->{'parent'}; - if (@{$current->{'contents'}}) { - my $text = $current->{'contents'}->[0]->{'text'}; - if (!defined ($text) - or ($text ne 'i' and $text ne 'j')) { - $self->line_error(sprintf($self-> - __("%c%s expects `i' or `j' as argument, not `%s'"), - ord('@'), $dotless->{'cmdname'}, - Texinfo::Convert::Texinfo::convert($current)), $line_nr); - } - } - } elsif ($explained_commands{$current->{'parent'}->{'cmdname'}} - or $inline_commands{$current->{'parent'}->{'cmdname'}}) { - my $current_command = $current->{'parent'}; - if ($inline_commands{$current_command->{'cmdname'}}) { - if ($current_command->{'cmdname'} eq 'inlineraw') { - my $context_command = pop @{$self->{'context_stack'}}; - if ($context_command ne $current_command->{'cmdname'}) { - $self->_bug_message("context $context_command instead of inlineraw $current_command->{'cmdname'}", - $line_nr, $current); - die; - } - } - pop @{$self->{'expanded_formats_stack'}}; - } - if (!@{$current_command->{'args'}} - or !@{$current_command->{'extra'}->{'brace_command_contents'}} - or !defined($current_command->{'extra'}->{'brace_command_contents'}->[0])) { - $self->line_warn( - sprintf($self->__("\@%s missing first argument"), - $current_command->{'cmdname'}), $line_nr); - } else { - if ($explained_commands{$current_command->{'cmdname'}}) { - my $normalized_type - = Texinfo::Convert::NodeNameNormalization::normalize_node( - {'contents' => - $current_command->{'extra'}->{'brace_command_contents'}->[0]}); - $current_command->{'extra'}->{'normalized'} = $normalized_type; - if (!$current_command->{'extra'}->{'brace_command_contents'}->[1]) { - if ($self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type}) { - $current_command->{'extra'}->{'explanation_contents'} - = $self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type}; - } - } elsif (! _ignore_global_commands($self)) { - $self->{'explained_commands'}->{$current_command->{'cmdname'}}->{$normalized_type} - = $current_command->{'extra'}->{'brace_command_contents'}->[1]; - } - }# else { - # my $argument - # = Texinfo::Convert::Text::convert({'contents' => - # $current_command->{'extra'}->{'brace_command_contents'}->[0]}, - # {Texinfo::Common::_convert_text_options($self)}); - # $current_command->{'extra'}->{'format'} = $argument; - #} - } - } elsif ($current->{'parent'}->{'cmdname'} eq 'errormsg') { - if (! _ignore_global_commands($self)) { - my $error_message_text - = Texinfo::Convert::Text::convert($current, - {Texinfo::Common::_convert_text_options($self)}); - $self->line_error($error_message_text, $line_nr); - } - } elsif (_command_with_command_as_argument($current->{'parent'}->{'parent'}) - and scalar(@{$current->{'contents'}}) == 0) { - print STDERR "FOR PARENT \@$current->{'parent'}->{'parent'}->{'parent'}->{'cmdname'} command_as_argument braces $current->{'cmdname'}\n" if ($self->{'DEBUG'}); - $current->{'parent'}->{'type'} = 'command_as_argument' - if (!$current->{'parent'}->{'type'}); - $current->{'parent'}->{'parent'}->{'parent'}->{'extra'}->{'command_as_argument'} - = $current->{'parent'}; - } - $self->_register_global_command($current->{'parent'}->{'cmdname'}, - $current->{'parent'}, $line_nr); - if ($command_ignore_space_after{$current->{'parent'}->{'cmdname'}}) { - push @{$current->{'parent'}->{'parent'}->{'contents'}}, - {'type' => 'empty_spaces_after_close_brace', - 'text' => '' }; - } - $current = $current->{'parent'}->{'parent'}; - $current = $self->_begin_preformatted ($current) - if ($close_preformatted_commands{$closed_command}); - # lone braces accepted right in a rawpreformatted - } elsif ($current->{'type'} - and $current->{'type'} eq 'rawpreformatted') { - push @{$current->{'contents'}}, {'text' => '}' }; - # footnote caption closing, when there is a paragraph inside. - } elsif ($context_brace_commands{$self->{'context_stack'}->[-1]}) { - # closing the context under broader situations - $current = _end_paragraph($self, $current, $line_nr); - if ($current->{'parent'} - and $current->{'parent'}->{'cmdname'} - and $context_brace_commands{$current->{'parent'}->{'cmdname'}} - and $current->{'parent'}->{'cmdname'} eq $self->{'context_stack'}->[-1]) { - my $context_command = pop @{$self->{'context_stack'}}; - if ($context_command ne $current->{'parent'}->{'cmdname'}) { - $self->_bug_message("context $context_command instead of brace isolated $current->{'parent'}->{'cmdname'}", - $line_nr, $current); - die; - } - print STDERR "CLOSING(context command) \@$current->{'parent'}->{'cmdname'}\n" if ($self->{'DEBUG'}); - my $closed_command = $current->{'parent'}->{'cmdname'}; - $self->_register_global_command($current->{'parent'}->{'cmdname'}, - $current->{'parent'}, $line_nr); - $current = $current->{'parent'}->{'parent'}; - $current = $self->_begin_preformatted ($current) - if ($close_preformatted_commands{$closed_command}); - } - } else { - $self->line_error(sprintf($self->__("misplaced %c"), - ord('}')), $line_nr); - } - } elsif ($separator eq ',' - and $current->{'parent'}->{'remaining_args'}) { - _abort_empty_line ($self, $current); - if ($brace_commands{$current->{'parent'}->{'cmdname'}} - and ($brace_commands{$current->{'parent'}->{'cmdname'}} > 1 - or $simple_text_commands{$current->{'parent'}->{'cmdname'}})) { - $self->_isolate_last_space($current); - $self->_register_command_arg($current, 'brace_command_contents'); - } else { - $self->_isolate_last_space($current); - if (exists $block_commands{$current->{'parent'}->{'cmdname'}}) { - $self->_register_command_arg($current, 'block_command_line_contents'); - } - } - my $type = $current->{'type'}; - $current = $current->{'parent'}; - if ($inline_commands{$current->{'cmdname'}}) { - if (! $current->{'extra'}->{'format'}) { - # change the top of the raw_formats_stack now that we know the - # first arg of the inlineraw - my $inline_type - = Texinfo::Convert::Text::convert({'contents' => - $current->{'extra'}->{'brace_command_contents'}->[0]}, - {Texinfo::Common::_convert_text_options($self)}); - if ($self->{'expanded_formats_stack'}->[-2]) { - if ($inline_format_commands{$current->{'cmdname'}}) { - if ($self->{'expanded_formats_hash'}->{$inline_type}) { - $self->{'expanded_formats_stack'}->[-1] = $inline_type; - $current->{'extra'}->{'expand_index'} = 1; - } else { - $self->{'expanded_formats_stack'}->[-1] = 0; - } - } elsif (($current->{'cmdname'} eq 'inlineifset' - and exists($self->{'values'}->{$inline_type})) - or ($current->{'cmdname'} eq 'inlineifclear' - and ! exists($self->{'values'}->{$inline_type}))) { - $self->{'expanded_formats_stack'}->[-1] - = "$current->{'cmdname'} $inline_type"; - $current->{'extra'}->{'expand_index'} = 1; - } else { - $self->{'expanded_formats_stack'}->[-1] = 0; - } - } else { - $self->{'expanded_formats_stack'}->[-1] = 0; - } - $current->{'extra'}->{'format'} = $inline_type; - } else { - # happens for the second arg of inlinefmtifelse - my $inline_type = $current->{'extra'}->{'format'}; - if ($self->{'expanded_formats_stack'}->[-2] - and ! ($self->{'expanded_formats_hash'}->{$inline_type})) { - $self->{'expanded_formats_stack'}->[-1] = $inline_type; - $current->{'extra'}->{'expand_index'} = 2; - } else { - $self->{'expanded_formats_stack'}->[-1] = 0; - } - } - } - $current->{'remaining_args'}--; - push @{$current->{'args'}}, - { 'type' => $type, 'parent' => $current, 'contents' => [] }; - #if ($inline_commands{$current->{'cmdname'}} - # and ! $self->{'expanded_formats_stack'}->[-1]) { - # $current->{'args'}->[-1]->{'extra'}->{'ignore'} = 1; - #} - $current = $current->{'args'}->[-1]; - push @{$current->{'contents'}}, - {'type' => 'empty_spaces_before_argument', - 'text' => '' }; - } elsif ($separator eq ',' and $current->{'type'} - and $current->{'type'} eq 'misc_line_arg' - and $current->{'parent'}->{'cmdname'} - and $current->{'parent'}->{'cmdname'} eq 'node') { - $self->line_warn($self->__("superfluous arguments for node"), $line_nr); - # end of menu node (. must be followed by a space to stop the node). - } elsif (($separator =~ /[,\t.]/ and $current->{'type'} - and $current->{'type'} eq 'menu_entry_node') - or ($separator eq ':' and $current->{'type'} - and $current->{'type'} eq 'menu_entry_name')) { - $current = $current->{'parent'}; - push @{$current->{'args'}}, { 'type' => 'menu_entry_separator', - 'text' => $separator, - 'parent' => $current }; - } elsif ($separator eq "\f" and $current->{'type'} - and $current->{'type'} eq 'paragraph') { - # form feed stops and restart a paragraph. - $current = $self->_end_paragraph($current); - push @{$current->{'contents'}}, {'text' => $separator, - 'type' => 'empty_line', - 'parent' => $current }; - push @{$current->{'contents'}}, { 'type' => 'empty_line', - 'text' => '', - 'parent' => $current }; - - } else { - $current = _merge_text($self, $current, $separator); - } - # Misc text except end of line - } elsif ($line =~ s/^([^{}@,:\t.\n\f]+)//) { - my $new_text = $1; - $current = _merge_text($self, $current, $new_text); - # end of line - } else { - if ($self->{'DEBUG'}) { - print STDERR "END LINE: ". _print_current($current)."\n"; - } - if ($line =~ s/^(\n)//) { - $current = _merge_text($self, $current, $1); - } else { - if (scalar(@{$self->{'input'}})) { - $self->_bug_message("Text remaining without normal text but `$line'", - $line_nr, $current); - die; - } - } - #print STDERR "END LINE AFTER MERGE END OF LINE: ". _print_current($current)."\n"; - $current = _end_line($self, $current, $line_nr); - last; - } - } - } - while (@{$self->{'conditionals_stack'}}) { - my $end_conditional = pop @{$self->{'conditionals_stack'}}; - $self->line_error(sprintf($self->__("expected \@end %s"), $end_conditional), - $line_nr); - } - $current = _close_commands($self, $current, $line_nr); - - if (@{$self->{'context_stack'}} != 1) { - # This happens in 2 cases in the tests: - # @verb not closed on misc commands line - # def line escaped with @ ending the file - if ($self->{'DEBUG'}) { - print STDERR "CONTEXT_STACK no empty end _parse_texi: ".join('|', @{$self->{'context_stack'}})."\n"; - } - @{$self->{'context_stack'}} = ($self->{'context'}); - } - if (@{$self->{'expanded_formats_stack'}} != 1) { - if ($self->{'DEBUG'}) { - print STDERR "EXPANDED_FORMATS_STACK no empty end _parse_texi: ".join('|', @{$self->{'expanded_formats_stack'}})."\n"; - } - @{$self->{'expanded_formats_stack'}} = ($self->{'expanded_formats_stack'}->[0]); - } - return $root; -} - - -# parse special line @-commands, unmacro, set, clear, clickstyle. -# Also remove spaces or ignore text, as specified in the misc_commands hash. -sub _parse_special_misc_command($$$$) -{ - my $self = shift; - my $line = shift; - my $command = shift; - my $line_nr = shift; - my $args = []; - - my $has_comment = 0; - my $remaining; - if ($command eq 'set') { - # REVALUE - #if ($line =~ s/^\s+([\w\-]+)(\s+(.*?))\s*$//) { - if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)(\@(c|comment)((\@|\s+).*)?|[^\S\f]+(.*?))?[^\S\f]*$/) { - if ($line =~ s/\@(c|comment)((\@|\s+).*)?$//) { - $has_comment = 1; - } - $line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)([^\S\f]+(.*?))?[^\S\f]*$/; - my $name = $1; - my $arg = $3; - $arg = '' if (!defined($arg)); - $args = [$name, $arg]; - $self->{'values'}->{$name} = $arg - unless(_ignore_global_commands($self)); - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - } - } elsif ($command eq 'clear') { - # REVALUE - if ($line =~ /^\s+([\w\-][^\s{\\}~`\^+"<>|@]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) { - $args = [$1]; - delete $self->{'values'}->{$1} - unless(_ignore_global_commands($self)); - $has_comment = 1 if (defined($3)); - #$remaining = $line; - #$remaining =~ s/^\s+([\w\-]+)\s*(\@(c|comment)((\@|\s+).*)?)?//; - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - } - } elsif ($command eq 'unmacro') { - # REMACRO - if ($line =~ /^\s+([[:alnum:]][[:alnum:]\-]*)\s*(\@(c|comment)((\@|\s+).*)?)?$/) { - $args = [$1]; - delete $self->{'macros'}->{$1} - unless(_ignore_global_commands($self)); - $has_comment = 1 if (defined($3)); - print STDERR "UNMACRO $1\n" if ($self->{'DEBUG'}); - } elsif ($line !~ /\S/) { - $self->line_error(sprintf($self-> - __("%c%s requires a name"), ord('@'), $command), $line_nr); - } else { - $self->line_error(sprintf($self-> - __("bad name for \@%s"), $command), $line_nr); - } - } elsif ($command eq 'clickstyle') { - # REMACRO - if ($line =~ /^\s+@([[:alnum:]][[:alnum:]\-]*)({})?\s*/) { - $args = ['@'.$1]; - $self->{'clickstyle'} = $1 - unless(_ignore_global_commands($self)); - $remaining = $line; - $remaining =~ s/^\s+@([[:alnum:]][[:alnum:]\-]*)({})?\s*(\@(c|comment)((\@|\s+).*)?)?//; - $has_comment = 1 if (defined($4)); - } else { - $self->line_error (sprintf($self->__( - "\@%s should only accept a \@-command as argument, not `%s'"), - $command, $line), $line_nr); - } - } else { - die $self->_bug_message("Unknown special command $command", $line_nr); - } - if (defined($remaining)) { - chomp($remaining); - if ($remaining ne '') { - $self->line_warn(sprintf($self->__( - "remaining argument on \@%s line: %s"), - $command, $remaining), $line_nr); - } - } - return ($args, $has_comment); -} - -sub _trim_spaces_comment_from_content($) -{ - Texinfo::Common::trim_spaces_comment_from_content($_[0]); -} - -# at the end of a @-command line with arguments, parse the resulting -# text, to collect aliases, definfoenclose and collect errors on -# wrong arguments. -sub _parse_line_command_args($$$) -{ - my $self = shift; - my $line_command = shift; - my $line_nr = shift; - - my $args; - - my $command = $line_command->{'cmdname'}; - my $arg = $line_command->{'args'}->[0]; - - if ($self->{'DEBUG'}) { - print STDERR "MISC ARGS \@$command\n"; - if (@{$arg->{'contents'}}) { - my $idx = 0; - foreach my $content (@{$arg->{'contents'}}) { - my $name = ''; - $name = '@' . $content->{'cmdname'} if ($content->{'cmdname'}); - my $type = ', t: '; - $type .= $content->{'type'} if ($content->{'type'}); - my $text = ', '; - $type .= $content->{'text'} if ($content->{'text'}); - print STDERR " -> $idx $name $type $text\n"; - $idx++; - } - } - } - - my @contents = @{$arg->{'contents'}}; - - _trim_spaces_comment_from_content(\@contents); - - if (! @contents) { - $self->_command_error($line_command, $line_nr, - $self->__("\@%s missing argument"), $command); - $line_command->{'extra'}->{'missing_argument'} = 1; - return undef; - } - - if (@contents > 1 - or (!defined($contents[0]->{'text'}))) { - $self->line_error (sprintf($self->__("superfluous argument to \@%s"), - $command), $line_nr); - } - return undef if (!defined($contents[0]->{'text'})); - - my $line = $contents[0]->{'text'}; - - if ($command eq 'alias') { - # REMACRO - if ($line =~ s/^([[:alnum:]][[:alnum:]-]*)(\s*=\s*)([[:alnum:]][[:alnum:]-]*)$//) { - my $new_command = $1; - my $existing_command = $3; - $args = [$1, $3]; - $self->{'aliases'}->{$new_command} = $existing_command - unless (_ignore_global_commands($self)); - if (exists($block_commands{$existing_command})) { - $self->line_warn(sprintf($self-> - __("environment command %s as argument to \@%s"), - $existing_command, $command), $line_nr); - } - } else { - $self->line_error(sprintf($self-> - __("bad argument to \@%s"), $command), $line_nr); - } - - } elsif ($command eq 'definfoenclose') { - # REMACRO - if ($line =~ s/^([[:alnum:]][[:alnum:]\-]*)\s*,\s*([^\s,]*)\s*,\s*([^\s,]*)$//) { - $args = [$1, $2, $3 ]; - $self->{'definfoenclose'}->{$1} = [ $2, $3 ] - unless (_ignore_global_commands($self)); - print STDERR "DEFINFOENCLOSE \@$1: $2, $3\n" if ($self->{'DEBUG'}); - } else { - $self->line_error(sprintf($self-> - __("bad argument to \@%s"), $command), $line_nr); - } - } elsif ($command eq 'columnfractions') { - my @possible_fractions = split (/\s+/, $line); - if (!@possible_fractions) { - $self->line_error (sprintf($self->__("empty \@%s"), $command), - $line_nr); - } else { - foreach my $fraction (@possible_fractions) { - if ($fraction =~ /^(\d*\.\d+)|(\d+)\.?$/) { - push @$args, $fraction; - } else { - $self->line_error (sprintf($self-> - __("column fraction not a number: %s"), - $fraction), $line_nr); - } - } - } - } elsif ($command eq 'sp') { - if ($line =~ /^([0-9]+)$/) { - $args = [$1]; - } else { - $self->line_error(sprintf($self->__("\@sp arg must be numeric, not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'defindex' || $command eq 'defcodeindex') { - # REMACRO - if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)$/) { - my $name = $1; - if ($forbidden_index_name{$name}) { - $self->line_error(sprintf($self-> - __("reserved index name %s"),$name), $line_nr); - } else { - my $in_code = 0; - $in_code = 1 if ($command eq 'defcodeindex'); - $args = [$name]; - if (! _ignore_global_commands($self)) { - $self->{'index_names'}->{$name} = {'in_code' => $in_code}; - $self->_register_index_commands($name); - } - } - } else { - $self->line_error(sprintf($self-> - __("bad argument to \@%s: %s"), $command, $line), $line_nr); - } - } elsif ($command eq 'synindex' || $command eq 'syncodeindex') { - # REMACRO - if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)\s+([[:alnum:]][[:alnum:]\-]*)$/) { - my $index_from = $1; - my $index_to = $2; - $self->line_error(sprintf($self->__("unknown source index in \@%s: %s"), - $command, $index_from), $line_nr) - unless $self->{'index_names'}->{$index_from}; - $self->line_error(sprintf($self->__("unknown destination index in \@%s: %s"), - $command, $index_to), $line_nr) - unless $self->{'index_names'}->{$index_to}; - if ($self->{'index_names'}->{$index_from} - and $self->{'index_names'}->{$index_to}) { - my $current_to = $index_to; - # find the merged indices recursively avoiding loops - while ($current_to ne $index_from - and $self->{'merged_indices'}->{$current_to}) { - $current_to = $self->{'merged_indices'}->{$current_to}; - } - if ($current_to ne $index_from) { - my $index_from_info = $self->{'index_names'}->{$index_from}; - my $index_to_info = $self->{'index_names'}->{$current_to}; - - my $in_code = 0; - $in_code = 1 if ($command eq 'syncodeindex'); - if (! _ignore_global_commands($self)) { - $self->{'merged_indices'}->{$index_from} = $current_to; - $index_from_info->{'in_code'} = $in_code; - foreach my $contained_index (keys %{$index_from_info->{'contained_indices'}}) { - $index_to_info->{'contained_indices'}->{$contained_index} = 1; - $self->{'index_names'}->{$contained_index}->{'merged_in'} = $current_to; - } - $index_from_info->{'merged_in'} = $current_to; - $index_to_info->{'contained_indices'}->{$index_from} = 1; - } - $args = [$index_from, $index_to]; - } else { - $self->line_warn(sprintf($self->__( - "\@%s leads to a merging of %s in itself, ignoring"), - $command, $index_from), $line_nr); - } - } - } else { - $self->line_error(sprintf($self->__("bad argument to \@%s: %s"), - $command, $line), $line_nr); - } - } elsif ($command eq 'printindex') { - # REMACRO - if ($line =~ /^([[:alnum:]][[:alnum:]\-]*)$/) { - my $name = $1; - if (!exists($self->{'index_names'}->{$name})) { - $self->line_error(sprintf($self->__("unknown index `%s' in \@printindex"), - $name), $line_nr); - - } else { - if ($self->{'merged_indices'}->{$name}) { - $self->line_warn(sprintf($self->__( - "printing an index `%s' merged in another one `%s'"), - $name, $self->{'merged_indices'}->{$name}), - $line_nr); - } - if (!defined($self->{'current_node'}) - and !defined($self->{'current_section'}) - and !scalar(@{$self->{'regions_stack'}})) { - $self->line_warn(sprintf($self->__( - "printindex before document beginning: \@printindex %s"), - $name), $line_nr); - } - $args = [$name]; - } - } else { - $self->line_error(sprintf($self-> - __("bad argument to \@%s: %s"), $command, $line), $line_nr); - } - } elsif (grep {$_ eq $command} ('everyheadingmarks', 'everyfootingmarks', - 'evenheadingmarks', 'oddheadingmarks', - 'evenfootingmarks', 'oddfootingmarks')) { - if ($line eq 'top' or $line eq 'bottom') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@%s arg must be `top' or `bottom', not `%s'"), - $command, $line), $line_nr); - } - } elsif ($command eq 'fonttextsize') { - if ($line eq '10' or $line eq '11') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "Only \@%s 10 or 11 is supported, not `%s'"), - $command, $line), $line_nr); - } - } elsif ($command eq 'footnotestyle') { - if ($line eq 'separate' or $line eq 'end') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@%s arg must be `separate' or `end', not `%s'"), - $command, $line), $line_nr); - } - } elsif ($command eq 'setchapternewpage') { - if ($line eq 'on' or $line eq 'off' or $line eq 'odd') { - $args = [$1]; - } else { - $self->line_error(sprintf($self->__( - "\@%s arg must be `on', `off' or `odd', not `%s'"), - $command, $line), $line_nr); - } - } elsif ($command eq 'need') { # only a warning - if (($line =~ /^([0-9]+(\.[0-9]*)?)$/) or - ($line =~ /^(\.[0-9]+)$/)) { - $args = [$1]; - } else { - $self->line_error(sprintf($self->__("bad argument to \@%s: %s"), - $command, $line), $line_nr); - } - } elsif ($command eq 'paragraphindent') { - if ($line =~ /^([\w\-]+)$/) { - my $value = $1; - if ($value =~ /^([0-9]+)$/ or $value eq 'none' or $value eq 'asis') { - $args = [$1]; - } else { - $self->line_error(sprintf($self->__( - "\@paragraphindent arg must be numeric/`none'/`asis', not `%s'"), - $value), $line_nr); - } - } else { - $self->line_error(sprintf($self->__( - "\@paragraphindent arg must be numeric/`none'/`asis', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'firstparagraphindent') { - if ($line eq 'none' or $line eq 'insert') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@firstparagraphindent arg must be `none' or `insert', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'exampleindent') { - if ($line =~ /^([0-9]+)/) { - $args = [$1]; - } elsif ($line =~ /^(asis)$/) { - $args = [$1]; - } else { - $self->line_error(sprintf($self->__( - "\@exampleindent arg must be numeric/`asis', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'frenchspacing' - or $command eq 'xrefautomaticsectiontitle' - or $command eq 'codequoteundirected' - or $command eq 'codequotebacktick' - or $command eq 'deftypefnnewline') { - if ($line eq 'on' or $line eq 'off') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__("expected \@%s on or off, not `%s'"), - $command, $line), $line_nr); - } - } elsif ($command eq 'kbdinputstyle') { - if ($line eq 'code' or $line eq 'example' or $line eq 'distinct') { - $self->{'kbdinputstyle'} = $line - unless (_ignore_global_commands($self)); - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@kbdinputstyle arg must be `code'/`example'/`distinct', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'allowcodebreaks') { - if ($line eq 'true' or $line eq 'false') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@allowcodebreaks arg must be `true' or `false', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'urefbreakstyle') { - if ($line eq 'after' or $line eq 'before' or $line eq 'none') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__( - "\@urefbreakstyle arg must be `after'/`before'/`none', not `%s'"), - $line), $line_nr); - } - } elsif ($command eq 'headings') { - if ($line eq 'off' or $line eq 'on' or $line eq 'single' - or $line eq 'double' or $line eq 'singleafter' or $line eq 'doubleafter') { - $args = [$line]; - } else { - $self->line_error(sprintf($self->__("bad argument to \@%s: %s"), - $command, $line), $line_nr); - } - } - return $args; -} - -1; -__END__ -=head1 NAME - -Texinfo::Parser - Parse Texinfo code in a Perl tree - -=head1 SYNOPSIS - - use Texinfo::Parser; - my $parser = Texinfo::Parser::parser(); - my $tree = $parser->parse_texi_file("somefile.texi"); - my ($errors, $errors_count) = $parser->errors(); - foreach my $error_message (@$errors) { - warn $error_message->{'error_line'}; - } - - my ($index_names, $merged_indices_hash) - = $parser->indices_information(); - my $float_types_arrays = $parser->floats_information(); - my $internal_references_array - = $parser->internal_references_information(); - # An hash reference on normalized node/float/anchor names - my $labels_information = $parser->labels_information(); - # A hash reference, keys are @-command names, value is an - # array reference holding all the corresponding @-commands. - my $global_commands_information = $parser->global_commands_information(); - # a hash reference on some document informations (encodings, - # input file name, dircategory and direntry list, for exampel). - my $global_informations = $parser->global_informations(); - -=head1 DESCRIPTION - -Texinfo::Parser will parse Texinfo text into a perl tree. In one pass -it expands user defined @-commands, conditionals (@ifset, @ifinfo...) -and @value and constructs the tree. Some extra information is gathered -while doing the tree, for example the block command associated with @end, -the number of row in a multitable, the node associated with a section. - - - -=head1 METHODS - -No method is exported in the default case. The module allows both -an object oriented syntax, or traditional function, with the parser -as an opaque data structure given in argument to every function. - -=head2 Initialization - -The following method is used to construct a new C<Texinfo::Parser> object: - -=over - -=item $parser = Texinfo::Parser::parser($options); - -This method creates a new parser. The options may be provided as a hash -reference. There are two types of options. The first type of options -change the way the parser behave, they are described right here. The -other type of options allow to give to the parser some information as if -it came from texinfo code, for example allow to set aliases (as with -C<@alias>), values (as with C<@set>), merged indices (as with -C<@synindex>). These options are described below in L</Texinfo Parser options>. - -=over - -=item expanded_formats - -An array reference of the output formats for which C<@ifI<FORMAT>> -conditional blocks should be expanded. Default is empty. - -The raw block formats (within C<@html> blocks, for example) are -always kept. - -=item gettext - -If set, the function reference is used to translate error and warning -messages. It takes a string as argument and returns a string. The default -function returns the error message as is. - -=item GLOBAL_COMMANDS - -The associated value is a reference on an array. All the commands in the -array are collected during parsing. They are afterwards available -through L<global_informations|/$info = global_informations($parser)>. - -=item include_directories - -An array reference of directories in which C<@include> files should be -searched for. Default contains the working directory, F<.>. - -=item INLINE_INSERTCOPYING - -If set, C<@insertcopying> is replaced by the C<@copying> content as if -C<@insertcopying> was a user-defined macro. In the default case, it is -considered to be a simple @-command and kept as is in the tree. - -=item IGNORE_BEFORE_SETFILENAME - -If set, and C<@setfilename> exists, everything before C<@setfilename> -is put in a special container type, @C<preamble_before_setfilename>. -This option is set in the default case. - -=item IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME - -If set, spaces after an @-commande name that take braces are ignored. -Default on. - -=item MACRO_BODY_IGNORES_LEADING_SPACE - -If set, leading spaces are stripped from user defined macro bodies. - -=item MAX_MACRO_CALL_NESTING - -Maximal number of nested user-defined macro calls. Default is 100000. - -=item SHOW_MENU - -If false, no menu related error are reported. Default is true. - -=begin :comment - -Used by Sectioning only -=item TOP_NODE_UP - -Text for the up node of the Top node. The default is C<(dir)>. The -string may contain @-commands. - -=end :comment - -=back - -=back - -=head2 Parsing Texinfo text - -There are three methods that may be called to parse some Texinfo code, -C<parse_texi_line> for a line, C<parse_texi_text> for a text fragment, -and C<parse_texi_file> for a file. - -For all those functions, if the I<$parser> argument is undef, a new -parser object is generated to parse the line, otherwise the parser given -in argument is used to parse into a tree. - -When C<parse_texi_text> is used, the resulting tree is rooted at -a C<root_line> type container. Otherwise, the resulting tree should be -rooted at a C<text_root> type container if it do not contain nodes or -sections, at a C<document_root> type container otherwise. - -=over - -=item $tree = parse_texi_line($parser, $text, $first_line_number, $file_name, $macro_name, $fixed_line_number) - -This function is used to parse a short fragment of Texinfo code. - -I<$text> may be either an array reference of lines, or a text. - -The other arguments are optional and allow to specify the position -information of the Texinfo code. I<$first_line_number> is the line number -of the first text line. I<$file_name> is the name of the file the -text comes from. I<$macro> is for the user-defined macro name the text -is expanded from. If I<$fixed_line_number> is set, the line number is -not increased for the different lines, as if the text was the expansion -of a macro. - -=item $tree = parse_texi_text ($parser, $text, $line_numbers_specification, $file_name, $macro_name, $fixed_line_number) - -This function is used to parse some Texinfo text. - -I<$text> may be either an array reference of lines, or a text. - -The other arguments are optional and allow to specify the position -information of the Texinfo code. There are two distinct cases for -I<$line_numbers_specification>. - -=over - -=item 1. - -If it is an array reference, it is considered to hold objects describing -the position information of each text lines. - -=item 2. - -If I<$line_numbers_specification> is a scalar, it is the line number of -the first text line. In that case (like for C<parse_texi_text>), -I<$file_name> is the name of the file the text comes from. -I<$macro> is for the user-defined macro name the text -is expanded from. If I<$fixed_line_number> is set, the line number is -not increased for the different lines, as if the text was the expansion -of a macro. - -=back - -=item $tree = parse_texi_file($parser, $file_name) - -The file with name I<$file_name> is considered to be a Texinfo file and -is parsed into a tree. - -undef is returned if the file couldn't be read. - -=back - -The errors collected during the tree parsing are available through the -C<errors> method. This method comes from C<Texinfo::Report>, and is -described in L<errors|Texinfo::Report/($error_warnings_list, $error_count) = errors ($converter)>. - -=head2 Getting informations on the document - -After parsing some informations about the Texinfo code that was processed -are available from the parser. - -Some global informations is available through C<global_informations> - -=over - -=item $info = global_informations($parser) - -The I<$info> returned is a hash reference. The possible keys are - -=over - -=item input_file_name - -The name of the main Texinfo input file. - -=item input_encoding_name - -=item input_perl_encoding - -C<input_encoding_name> string is the encoding name used for the -Texinfo code. -C<input_perl_encoding> string is a corresponding perl encoding name. - -=item dircategory_direntry - -An array of successive C<@dircategory> and C<@direntry> as they appear -in the document. - -=item unassociated_menus - -An array of menus that are not associated with a node. - -=back - -=back - -Some command lists are available, such that it is possible to go through -the corresponding tree elements without walking the tree. They are -available through C<global_commands_information> - -=over - -=item $commands = global_commands_information($parser) - -I<$commands> is an hash reference. The keys are @-command names. The -associated values are array references containing all the corresponding -tree elements. - -=back - -All the @-commands that have an associated label, that can be the -target of cross references, C<@node>, C<@anchor> and C<@float> with -label have a normalized name associated, constructed as described in the -B<HTML Xref> node in the Texinfo manual. Those normalized labels and -the association with @-commands is available through C<labels_information>: - -=over - -=item $labels_information = labels_information($parser) - -I<$labels_information> is a hash reference whose keys are normalized -labels, and the associated value is the corresponding @-command. - -=back - -Information on C<@float> is also available, grouped by type of -floats, each type correponding to potential C<@listoffloats>. -This information is available through the method C<floats_information>. - -=over - -=item $float_types = floats_information($parser) - -I<$float_types> is a hash reference whose keys are normalized float -types (the first float argument, or the C<@listoffloats> argument). -The normalization is the same than for node names. The value is the list -of float tree elements appearing in the texinfo document. - -=back - -Internal references, that is, @-commands that refers to node, anchors -or floats within the document are also available: - -=over - -=item $internal_references_array = internal_references_information($parser); - -The function returns a list of cross reference commands referring to -the same document. - -=back - -Information about defined indices, merged indices and index entries is -also available through the C<indices_information> method. - -=over - -=item indices_information - - ($index_names, $merged_indices_hash) - = indices_information($parser); - -The index names is a hash reference. The keys are - -=over - -=item in_code - -1 if the index entries should be formatted as code, 0 in the opposite case. - -=item name - -The index name. - -=item prefix - -An array reference of prefix associated to the index. - -=item merged_in - -In case the index is merged to another index, this key holds the name of -the index the index is merged into. It takes into account indirectly -merged indices. - -=item contained_indices - -An hash reference holding names of indices that are merged to the index, -including itself. It also contains indirectly merged indices. This key -is present even if the index is itself later merged to another index. - -=item index_entries - -An array reference containing index entry structures for index entries -associated with the index. The index entry could be associated to -@-commands like C<@cindex>, or C<@item> in C<@vtable>, or definition -commands entries like C<@deffn>. - -The keys of the index entry structures are - -=over - -=item index_name - -The index name. - -=item index_prefix - -The associated index prefix. - -=item index_at_command - -The name of the @-command associated with the index entry. - -=item index_type_command - -The @-command associated with the index entry allowing to -find the index type. - -=item content - -An array reference corresponding to the index entry content. - -=item content_normalized - -An array reference corresponding to the index entry content, independent -of the current language. - -=item command - -The element in the parsed tree associated with the @-command holding the -index entry. - -=item node - -The node in the parsed tree containing the index entry. - -=item number - -The number of the index entry. - -=item region - -The region command (C<@copying>, C<@titlepage>) containing the index entry, -if it is in such an environement. - -=back - -=back - -The following shows the references corresponding with the default indexes -I<cp> and I<fn>, the I<fn> index having its entries formatted as code and -the indices corresponding to the following texinfo - - @defindex some - @defcodeindex code - - $index_names = {'cp' => {'name' => 'cp', 'in_code' => 0, - 'prefix' => ['c', 'cp']}, - 'fn' => {'name' => 'fn', 'in_code' => 1, - 'prefix' => ['f', 'fn']}, - 'some' => {'in_code' => 0}, - 'code' => {'in_code' => 1}}; - -If C<name> is not set, it is set to the index name. If C<prefix> is -not set, it is set to an array containing the index name. - -I<$merged_indices_hash> is a hash reference, the key is an index -name merged in the value. - -=back - -=head2 Texinfo Parser options - -Setting those options is the same as seeing some Texinfo constructs in the -document. - -=over - -=item aliases - -A hash reference. The key is a command name, the value is the alias, as -could be set by C<@alias>. - -=item clickstyle - -A string, the command name associated with C<@clickstyle>. - -=item documentlanguage - -A string corresponding to a document language set by C<@documentlanguage>. - -=item explained_commands - -A hash reference of explained commands (currently abbr or acronym). -The value is also a hash reference. The key of this hash is a normalized -first argument of these commands, the value is a content array -corresponding to the explanation associated with this first argument. - -For example giving as an option: - - 'explained_commands' - => {'acronym' => {'EU' => [{'text' => 'European Union'}]} - -is the same as having the following texinfo code in the document: - - @acronym{EU, European Union} - -=item INPUT_ENCODING_NAME - -=item INPUT_PERL_ENCODING - -C<INPUT_ENCODING_NAME> string is the encoding name as set -by C<@documentencoding>. -C<INPUT_PERL_ENCODING> string is a corresponding perl encoding name. -In general those two strings should be set simultaneously. - -=item indices - -If it is a hash reference, the keys are index names, the values are -index prefix hash references. The index prefix hash reference values are -prefix, the value is set if the corresponding index entries should be -formatted as if in C<@code>. An example is as L</indices_information>. - -If it is an array reference, it is a list of index names, as if they were -entered as - - @defindex name - -=item kbdinputstyle - -A string, the C<@kbdinputstyle> style. - -=item labels - -A hash reference. Keys are normalized node names as described in the -B<HTML Xref> node in the Texinfo manual. Instead of a node, it may also -be a float label or an anchor name. The value is the corresponding -@-command element in the tree. - -=item macros - -The associated hash reference has as key user-defined macro names. The -value is the reference on a macro definition element as obtained by -the Parser when parsing a C<@macro>. For example - - @macro mymacro{arg} - coucou \arg\ after arg - @end macro - -Is associated to a macro definition element - - {'cmdname' => 'macro', - 'args' => [{'text' => 'mymacro', 'type' => 'macro_name'}, - {'text' => 'arg', 'type' => 'macro_arg}], - 'contents' => [{'text' => "coucou \arg\ after arg\n", 'type' => 'raw'}], - 'extra' => {'arg_line' => " mymacro{arg}\n", - 'macrobody' => "coucou \arg\ after arg\n"}} - -= item merged_indices - -The associated hash reference holds merged indices information, each key -is merged in the value. Same as setting C<@synindex> or C<syncodeindex>. - -=item novalidate - -If set, it is as if C<@novalidate> was set in the document. - -=item sections_level - -Modifier of the sections level. Same as calling C<@lowersections> or -C<@raisesections>. - -=item values - -A hash reference. Keys are names, values are the corresponding values. -Same as values set by C<@set>. - -=back - -=head1 TEXINFO TREE - -A Texinfo tree element (called element because node is overloaded in -the Texinfo world) is an hash reference. There are three main category -of tree element. Tree elements associated with an @-command have a -C<cmdname> key holding the @-command name. Tree element corresponding -to text fragments have a C<text> key holding the corresponding text. -The last category corresponds to other containers (hereafter called -containers). In most case these containers have a C<type> key holding -their name. Text fragments and @-command elements may also have an -associated type when such information is needed. - -The children of @-command or container elements are in the array -correponding with the C<args> key or with the C<contents> key. The -C<args> key is for arguments of @-commands, in braces or on the @-command -line. C<args> is also used for the elements of a menu entry, as a menu -entry is well structured with a limited number of arguments. -The C<contents> key array holds the contents of the texinfo -code appearing within a block @-command, within a container, -within a C<@node> or sectioning @-command. - -Another important key for the elements is the C<extra> key which is -associated to a hash reference and holds all kinds of informations gathered -during the parsing and that may help with the conversion. - -=head2 Element keys - -=over - -=item cmdname - -The command name of @-command elements. - -=item text - -The text fragment of text elements. - -=item type - -The type of the element. For C<@verb> it is the delimiter. But otherwise -it is the type of element considered as a container. Frequent types -encountered are I<paragraph> for a paragraph container, -I<brace_command_arg> for the container holding the brace @-commands -contents, I<misc_line_arg> and I<block_line_arg> contain the arguments -appearing on the line of @-commands. Text fragments may have a type to -give an information of the kind of text fragment, for example -C<empty_spaces_before_argument> is associated to spaces after a brace -opening and before the argument. Many @-commands elements don't have -a type associated. - -=item args - -Arguments in braces or on @-command line, and the elements of a menu entry. - -=item contents - -The Texinfo appearing in the element. For block commands, other -containers, C<@node> and sectioning commands. - -=item parent - -The parent element. - -=item line_nr - -An hash reference corresponding to information on the location of the -element in the Texinfo input manual. It should only be available for -@-command elements, and only for @-commands that are considered to be -complex enough that the location in the document is needed, for example -to prepare an error message. - -The keys of the line number hash references are - -=over - -=item line_nr - -The line number of the @-command. - -=item file_name - -The file name where @-command appeared. - -=item macro - -The user macro name the @-command is expanded from. - -=back - -=item extra - -A hash reference holding any additional information. -See L</Information available in the extra key>. - -=back - -=head2 The containers and types - -Some types are associated with @-commands. As said above, for C<@verb> -the type is the delimiter. For a C<@value> command that is not -expanded because there is no corresponding value set, the type is the -value argument string. - -The following types also happen for @-commands: - -=over - -=item def_line - -This type may be associated with a definition command with a x form, -like C<@defunx>, C<@defvrx>. For the form without x, the associated -I<def_line> is the first C<contents> element. It is described in more -details below. - -=item command_as_argument - -This is the type of a command given in argument of C<@itemize>, -C<@table>, C<@vtable> or C<@ftable>. For example in - - @itemize @bullet - @item item - @end itemize - -the element corresponding with bullet has the following keys: - - 'cmdname' => 'bullet' - 'type' => 'command_as_argument' - -The parent @-command has an entry in extra for the I<command_as_argument> -element: - - 'cmdname' => 'itemize' - 'extra => {'command_as_argument' => $command_element_as_argument} - -=item index_entry_command - -This is the type of index entry command like C<@cindex>, and, more -importantly user defined index entry commands. So for example if there -is - - @defindex foo - ... - - @fooindex index entry - -the C<@fooindex> @-command element will have the I<index_entry_command> -type. - -=item following_arg - -This type is set for non alphabetic accent @-commands that don't use brace -but instead have their argument right after them, as - - @~n - -=item space_command_arg - -This type is set for accent @-commands that don't use brace but instead -have their argument after some space, as - - @ringaccent A - -=item definfoenclose_command - -This type is set for an @-command that is redefined by C<@definfoenclose>. -The beginning is in C<< {'extra'}->{'begin'} >> and the end in -C<< {'extra'}->{'end'} >>. - -=back - -The text elements may be associated to the following types: - -=over - -=item empty_line - -An empty line. - -=item raw - -Text in an environment where it should be kept as is (in C<@verbatim>, -C<@verb>, C<@html>, C<@macro> body). - -=item last_raw_newline - -The last end of line in a raw block (except for C<@verbatim>). - -=item empty_line_after_command - -=item empty_spaces_after_command - -The text is spaces for I<empty_spaces_after_command> -or spaces followed by a newline for -I<empty_line_after_command>, appearing after a @-command that -takes an argument on the line or a block -@-commands. - -=item spaces_at_end - -Space at the end of a @-command line, at the end of some @-commands -with braces or at the end of a bracketed content on a -C<@multitable> line. - -=item empty_space_at_end_def_bracketed - -Space at the end of a bracketed content on definition line. - -=item space_at_end_block_command - -Space at the end of a block @-command line. - -=item empty_spaces_before_argument - -The text is spaces appearing after an opening brace of after a -comma separated @-command arguments. - -=item empty_spaces_after_close_brace - -Spaces appearing after a closing brace, for some rare commands for which -this space should be ignorable (like C<@caption>). - -=item empty_spaces_before_paragraph - -Space appearing before a paragraph beginning. - -=item preamble_text - -Text appearing before real content, including the C<\input texinfo.tex>. - -=item space_at_end_menu_node - -=item after_description_line - -Space after a node in the menu entry, when there is no description, -and space appearing after the description line. - -=back - -Other special types are described in the following. - -=over - -=item text_root - -=item document_root - -=item root_line - -These types correspond to document roots. C<text_root> is the document -root when there is no C<@node> or sectioning command. When -such a command appears, a new root container is used, C<document_root>, -and C<text_root> becomes the first content of C<document_root>. -C<root_line> is the type of the root tree when parsing Texinfo line -fragments using C<parse_texi_line>. - -=item preamble - -This container holds the text appearing before the first content, including -the C<\input texinfo.tex> line and following blank lines. - -=item preamble_before_setfilename - -This container holds everything that appears before C<@setfilename> -if I<IGNORE_BEFORE_SETFILENAME> parser option is set. - -=item paragraph - -A paragraph. - -=item preformatted - -Texinfo code within a format that is not filled. Happens within some -block commands as C<@example>, but also in menu (in menu descriptions, -menu comments...). - -=item brace_command_arg - -=item brace_command_context - -=item block_line_arg - -=item misc_line_arg - -Those containers are within C<args> of @-commands with braces for -I<brace_command_arg>, @-commands with braces that start a new context -(C<@footnote>, C<@caption>, C<@math>) for I<brace_command_context>, -block command argument on their line for I<block_line_arg> and -other commands that take texinfo code as argument on their line -(C<@settitle>, C<@node>, C<@section> and similar) for I<misc_line_arg>. -They hold the content of the command argument. - -For example - - @code{in code} - -leads to - - {'cmdname' => 'code', - 'args' => [{'type' => 'brace_command_arg', - 'contents' => [{'text' => 'in code'}]}]} - -=item misc_arg - -Argument of @-command taking specific textual arguments on the line. -For example C<@set>, C<@clickstyle>, C<@unmacro>, C<@comment>. -The argument is associated to the I<text> key. - -=item menu_entry - -=item menu_entry_leading_text - -=item menu_entry_name - -=item menu_entry_separator - -=item menu_entry_node - -=item menu_entry_description - -A I<menu_entry> holds a full menu entry, like - - * node:: description. - -The different elements of the menu entry are directly in the -I<menu_entry> C<args> array reference. - -I<menu_entry_leading_text> holds the star and following spaces. -I<menu_entry_name> is the menu entry name (if present), I<menu_entry_node> -corresponds to the node in the menu entry, I<menu_entry_separator> holds -the text after the node and before the description, in most case -C<:: >. Last I<menu_entry_description> is for the description. - -=item menu_comment - -The I<menu_comment> container holds what is between menu entries -in menus. For example in - - @menu - Menu title - - * entry:: - - Between entries - * other:: - @end menu - -Both - - Menu title - -and - - Between entries - -will be in I<menu_comment>. - -=item macro_name - -=item macro_arg - -Taken from C<@macro> definition and put in the C<args> key array of -the macro, I<macro_name> is the type of the text fragment corresponding -to the macro name, I<macro_arg> is the type of the text fragments -correponding to macro formal arguments. - -=item before_item - -A container for content before the first C<@item> of block @-commands -with items (C<@table>, C<@multitable>, C<@enumerate>...). - -=item table_entry - -=item table_term - -=item table_item - -=item inter_item - -Those containers appear in C<@table>, C<@ftable> and C<@vtable>. -A I<table_entry> container contains C<@item> and C<@itemx> and -the text following the C<@item> and C<@itemx> entries. A I<table_term> -container holds all the C<@item> and C<@itemx> of the I<table_entry>. -The I<table_item> container holds the content following the I<table_term>. -If there is some content before an C<@itemx> (normally only comments, -empty lines or maybe index entriees are allowed), it will be in -a container with type I<inter_item>. - -=item def_line - -=item def_item - -=item inter_def_item - -The I<def_line> type is either associated with a container within a -definition command, or is the type of a definition command with a x -form, like C<@deffnx>. It holds the definition line arguments. -The container with type I<def_item> holds the definition text content. -Content appearing before a definition command with a x form is in -an I<inter_def_item> container. - -=item multitable_head - -=item multitable_body - -=item row - -In C<@multitable>, a I<multitable_head> container contains all the row -with C<@headitem>, while I<multitable_body> contains the rows associated -with C<@item>. A I<row> container contains the C<@item> and @<tab> -forming a row. - -=item bracketed - -This a special type containing content in brackets in the context -where they are valid, in C<@math>. - -=item bracketed_def_content - -Content in brackets on definition command lines. - -=item bracketed_multitable_prototype - -=item row_prototype - -On C<@multitable> line, content in brackets is in -I<bracketed_multitable_prototype>, text not in brackets -is in I<row_prototype>. - -=back - -=head2 Information available in the extra key - -Some extra keys are available for more than one @-command: - -=over - -=item block_command_line_contents - -=item brace_command_contents - -An array associated with block @-commands or @-commands with braces -taking more than one argument or with a simple text content -(C<@anchor>, C<@titlefont>, C<@dmn>). Each of the element of the -array is either undef, if there is no argument at that place, -or an array reference holding the argument contents. - -=item misc_content - -The contents of an @-command taking regular Texinfo code as -argument, like C<@sttitle> or C<@exdent>. - -=item end_command - -The C<@end> associated to the block @-command. - -=item missing_argument - -Set for some @-commands with line arguments and a missing argument. - -=item invalid_nesting - -Set if the @-command appears in a context it shouldn't appear in, -like a block @-command on sectioning @-command line. - -=item arg_line - -The string correspond to the line after the @-command -for @-commands that have special arguments on their line, -and for C<@macro> line. - -=item text_arg - -The string correspond to the line after the @-command for @-commands -that have an argument interpreted as simple text, like C<@setfilename>, -C<@end> or C<@documentencoding>. - -=item index_entry - -The index entry information (described in L</index_entries> -in details) is associated to @-commands that have an associated -index entry. - -=item misc_args - -An array holding strings, the arguments of @-commands taking simple -textual arguments as arguments, like C<@everyheadingmarks>, -C<@frenchspacing>, C<@alias>, C<@synindex>, C<@columnfractions>. -Also filled for C<@set>, C<@clickstyle>, C<@unmacro> or C<@comment> -arguments. - -=item spaces_after_command - -For @-commands followed by spaces, a reference to the corresponding -text element. - -=item spaces_before_argument - -For @-commands with opening brace followed by spaces held in a -C<empty_spaces_before_argument> element, a reference to that element. - -=item spaces - -For accent commands consisting in letter only, like C<@ringaccent> -appearing like - - @ringaccent A - -there is a I<spaces> key which holds the spaces appearing between -the command and the argument. - -=back - -Then there are extra keys specific of @-commands or containers. - -=over - -=item C<@macro> - -I<invalid_syntax> is set if there was an error on the C<@macro> -line. The key I<args_index> associates format arguments with -their index on the @macro line formal arguments definition. -The I<macrobody> holds the @macro body. I<arg_line> holds the -line after C<@macro>. - -=item C<@node> - -The arguments are in the I<nodes_manuals> array. Each -of the entry has a I<node_content> key for -an array holding the corresponding content, a I<manual_content> -if there is an associated external manual name, and I<normalized> -key for the normalized label, built as specified in the Texinfo manual -in the B<HTML Xref> node. - -An I<associated_section> key holds the tree element of the -sectioning command that follows the node. - -=item C<@part> - -The next sectioning command is in I<part_associated_section>. - -=item sectioning command - -The node preceding the command is in I<associated_node>. -The part preceding the command is in I<associated_part>. -If the level of the document was modified by C<@raisections> -or C<@lowersections>, the differential level is in I<sections_level>. - -=item C<@float> - -=item C<@listoffloats> - -If float has a second argument, and for C<@listoffloats> -argument there is a I<type> key which is also a hash reference, -with two keys. I<content> is an array holding the associated -contents, I<normalized> holds the normalized float type. - -I<caption> and I<shortcaption> holds the corresponding -tree elements for float. The C<@caption> or C<@shortcaption> -have the float tree element stored in I<float>. - -=item C<@float> - -=item C<@anchor> - -@-Commands that are targets for cross references have a I<normalized> -key for the normalized label, built as specified in the Texinfo manual -in the B<HTML Xref> node. There is also a I<node_content> key for -an array holding the corresponding content. - -C<@anchor> also has I<region> set to the special region name if -in a special region (C<@copying>, C<@titlepage>). - -=item C<@ref> - -=item C<@xref> - -=item C<@pxref> - -=item C<@inforef> - -The I<node_argument> entry holds a parsed node entry, like -the one appearing in the I<nodes_manuals> array for C<@node>. - -=item C<@abbr> - -=item C<@acronym> - -The first argument normalized is in I<normalized>. If there is no -second argument, but a second argument appeared previously for the -same first argument, the second argument content of the previous -command is stored in I<explanation_contents>. - -=item definition command - -I<def_command> holds the command name, without x if it is -an x form of a definition command. -I<original_def_cmdname> is the original def command. - -If it is an x form, it has I<not_after_command> set if not -appearing after the definition command without x. - -=item def_line - -The I<def_arg> extra key holds an array reference corresponding to -the parsed definition line argument. Each of the the element of the -array is a two element array reference. The first element is the type -which could be I<spaces> for a space, types specific of the -definition, like I<category>, I<name>, I<class>, I<type>, and, at the -end, a mix of I<arg>, I<typearg>, I<delimiter> depending on the definition. -The second element is a hash reference holding the content of the -type. - -The I<def_parsed_hash> hash reference has as key the type specific -of the definition, and as value the corresponding content tree. - -=item C<@multitable> - -The key I<max_columns> holds the maximal number of columns. If there -are prototypes on the line they are in the array associated with -I<prototypes>. In that case, I<prototypes_line> also holds this -information, and, in addition, keeps spaces with type C<prototype_space>. -If there is a C<@columnfractions> as argument, then the -I<columnfractions> key is associated with the array of columnfractions -arguments, holding all the column fractions. - -=item C<@enumerate> - -The extra key I<enumerate_specification> contains the enumerate -argument. - -=item C<@itemize> - -=item C<@table> - -=item C<@vtable> - -=item C<@ftable> - -The I<command_as_argument> extra key points on the @-command on -as argument on the @-command line. - -=item paragraph - -The I<indent> or I<noindent> key value is set if the corresponding -@-commands are associated with that paragraph. - -=item C<@item> in C<@enumerate> or C<@itemize> - -The I<item_number> extra key holds the number of this item. - -=item C<@item> and C<@tab> in C<@multitable> - -The I<cell_count> index key holds the index of the column of -the cell. - -=item row - -The I<row_number> index key holds the index of the row in -the C<@multitable>. - -=item C<@author> - -If in a C<@titlepage>, the titlepage is in I<titlepage>, if in -C<@quotation> or C<@smallquotation>, the corresponding tree element -is in I<quotation>. - -The author tree element is in the I<author> array of the C<@titlepage> -or the C<@quotation> or C<@smallquotation> it is associated with. - -=item C<@ifclear> - -=item C<@ifset> - -The original line is in I<line>. - -=item C<@end> - -The textual argument is in I<command_argument>. -The corresponding @-command is in I<command>. - -=item C<@documentencoding> - -The argument, normalized is in I<input_encoding_name> if it is recognized. -The corresponding perl encoding name is in I<input_perl_encoding>. - -=item C<@click> - -In I<clickstyle> there is the current clickstyle command. - -=item C<@kbd> - -I<code> is set depending on the context and C<@kbdinputstyle>. - -=item definfoenclose defined commands - -I<begin> holds the string beginning the definfoenclose, -I<end> holds the string ending the definfoenclose. - -=item menu_entry - -The I<menu_entry_description> and I<menu_entry_name> keys -are associated with the corresponding tree elements. The -I<menu_entry_node> holds the parsed node entry, like -the one appearing in the I<nodes_manuals> array for C<@node>. - -=item empty_line_after_command - -The corresponding command is in I<command>. - -=item C<@inlinefmt> - -=item C<@inlineraw> - -=item C<@inlinefmtifelse> - -=item C<@inlineifclear> - -=item C<@inlineifset> - -The first argument is in I<format>. If an argument has been determined -as being expanded by the Parser, the index of this argument is in -I<expand_index>. Index numbering begins at 0, but the first argument is -always the format or flag name, so, if set, it should be 1 or 2 for -C<@inlinefmtifelse>, and 1 for other commands. - -=back - -=head1 SEE ALSO - -L<Texinfo manual|http://www.gnu.org/s/texinfo/manual/texinfo/> - -=begin :comment - -Mention other useful documentation such as the documentation of -related modules or operating system documentation (such as man pages -in UNIX), or any relevant external documentation such as RFCs or -standards. - -If you have a mailing list set up for your module, mention it here. - -If you have a web site set up for your module, mention it here. - -=end :comment - -=head1 AUTHOR - -Patrice Dumas, E<lt>pertusus@free.frE<gt> - -=head1 COPYRIGHT AND LICENSE - -Copyright 2010, 2011, 2012 Free Software Foundation, Inc. - -This library is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 3 of the License, -or (at your option) any later version. - -=cut diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.c index b96050c8f6..0a123eeae6 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.c @@ -588,6 +588,22 @@ XS_EUPXS(XS_Texinfo__Parser_conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME) } +XS_EUPXS(XS_Texinfo__Parser_set_novalidate); /* prototype to pass -Wmissing-prototypes */ +XS_EUPXS(XS_Texinfo__Parser_set_novalidate) +{ + dVAR; dXSARGS; + if (items != 1) + croak_xs_usage(cv, "i"); + { + int i = (int)SvIV(ST(0)) +; + + set_novalidate(i); + } + XSRETURN_EMPTY; +} + + XS_EUPXS(XS_Texinfo__Parser_set_documentlanguage); /* prototype to pass -Wmissing-prototypes */ XS_EUPXS(XS_Texinfo__Parser_set_documentlanguage) { @@ -671,6 +687,7 @@ XS_EXTERNAL(boot_Texinfo__Parser) (void)newXSproto_portable("Texinfo::Parser::conf_set_show_menu", XS_Texinfo__Parser_conf_set_show_menu, file, "$"); (void)newXSproto_portable("Texinfo::Parser::conf_set_CPP_LINE_DIRECTIVES", XS_Texinfo__Parser_conf_set_CPP_LINE_DIRECTIVES, file, "$"); (void)newXSproto_portable("Texinfo::Parser::conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME", XS_Texinfo__Parser_conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME, file, "$"); + (void)newXSproto_portable("Texinfo::Parser::set_novalidate", XS_Texinfo__Parser_set_novalidate, file, "$"); (void)newXSproto_portable("Texinfo::Parser::set_documentlanguage", XS_Texinfo__Parser_set_documentlanguage, file, "$"); (void)newXSproto_portable("Texinfo::Parser::set_debug", XS_Texinfo__Parser_set_debug, file, "$"); #if PERL_VERSION_LE(5, 21, 5) diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.pm b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.pm index 5645a11c1b..ee88072f24 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.pm @@ -1,4 +1,4 @@ -# Copyright 2014-2019 Free Software Foundation, Inc. +# Copyright 2014-2020 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -13,12 +13,10 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -package Texinfo::Parser; +# File to be loaded in conjunction with Parsetexi.xs module -use Texinfo::XSLoader; +package Texinfo::Parser; -# same as texi2any.pl, although I don't know what the real requirement -# is for this module. use 5.00405; use strict; use warnings; @@ -53,12 +51,7 @@ sub get_conf($$) my %parser_default_configuration = (%Texinfo::Common::default_parser_state_configuration, - %Texinfo::Common::default_customization_values); - -my %tree_informations; -foreach my $tree_information ('values', 'macros', 'explained_commands', 'labels') { - $tree_informations{$tree_information} = 1; -} + %Texinfo::Common::default_parser_customization_values); use Storable qw(dclone); # standard in 5.007003 @@ -66,7 +59,7 @@ sub simple_parser { goto &parser; } -# Stub for Texinfo::Parser::parser (line 574) +# Stub for Texinfo::Parser::parser sub parser (;$$) { my $conf = shift; @@ -74,9 +67,11 @@ sub parser (;$$) my $parser = dclone(\%parser_default_configuration); reset_parser (); - # fixme: these are overwritten immediately after if (defined($conf)) { foreach my $key (keys (%$conf)) { + # Copy conf to parser object. Not used in parser module itself + # but some settings may be used elsewhere, especially in + # Structuring.pm. if ($key ne 'values' and ref($conf->{$key})) { $parser->{$key} = dclone($conf->{$key}); } else { @@ -103,21 +98,35 @@ sub parser (;$$) } elsif ($key eq 'expanded_formats') { clear_expanded_formats (); - for my $f (@{$parser->{$key}}) { + for my $f (@{$conf->{$key}}) { add_expanded_format ($f); } } elsif ($key eq 'documentlanguage') { if (defined ($conf->{$key})) { set_documentlanguage ($conf->{$key}); } - } elsif ($key eq 'SHOW_MENU') { - conf_set_show_menu ($conf->{$key}); + } elsif ($key eq 'info') { + if (defined($conf->{$key}->{'novalidate'})) { + set_novalidate($conf->{$key}->{'novalidate'}); + } + } elsif ($key eq 'FORMAT_MENU') { + if ($conf->{$key} eq 'menu') { + conf_set_show_menu (1); + } else { + conf_set_show_menu (0); + } } elsif ($key eq 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME') { conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME ($conf->{$key}); } elsif ($key eq 'CPP_LINE_DIRECTIVES') { conf_set_CPP_LINE_DIRECTIVES($conf->{$key}); + } elsif ($key eq 'DEBUG') { + set_debug($conf->{$key}) if $conf->{'key'}; + } elsif ($key eq 'in_gdt' + or $key eq 'ENABLE_ENCODING' + or defined($Texinfo::Common::default_structure_customization_values{$key})) { + # no action needed } else { - #warn "ignoring parser configuration value \"$key\"\n"; + warn "ignoring parser configuration value \"$key\"\n"; } } } @@ -185,6 +194,16 @@ sub get_parser_info { $self->{'info'} = $GLOBAL_INFO; $self->{'extra'} = $GLOBAL_INFO2; + # Propagate these settings from 'info' hash to 'values' hash. + # The 'values' hash is not otherwise used. Maybe we should use + # the 'info' hash for this instead in the pure Perl code. + for my $txi_flag ('txiindexatsignignore', 'txiindexbackslashignore', + 'txiindexhyphenignore', 'txiindexlessthanignore') { + if ($self->{'info'}->{$txi_flag}) { + $self->{'values'}->{$txi_flag} = 1; + } + } + _get_errors ($self); Texinfo::Common::complete_indices ($self); } @@ -221,7 +240,7 @@ sub _maybe_ignore_before_setfilename { } } -# Replacement for Texinfo::Parser::parse_texi_file (line 835) +# Replacement for Texinfo::Parser::parse_texi_file sub parse_texi_file ($$) { my $self = shift; @@ -239,7 +258,6 @@ sub parse_texi_file ($$) get_parser_info ($self); _complete_node_menus ($self, $TREE); - # line 899 my $text_root; if ($TREE->{'type'} eq 'text_root') { $text_root = $TREE; @@ -277,7 +295,7 @@ sub _get_errors($) } } -# Replacement for Texinfo::Parser::parse_texi_text (line 757) +# Replacement for Texinfo::Parser::parse_texi_text # # Used in tests under tp/t. sub parse_texi_text($$;$$$$) @@ -314,7 +332,7 @@ sub parse_texi_text($$;$$$$) return $tree; } -# Replacement for Texinfo::Parser::parse_texi_line (line 918) +# Replacement for Texinfo::Parser::parse_texi_line sub parse_texi_line($$;$$$$) { my $self = shift; @@ -333,7 +351,7 @@ sub parse_texi_line($$;$$$$) return $tree; } -# Public interfaces of Texinfo::Parser (starting line 942) +# Public interfaces of Texinfo::Parser sub indices_information($) { my $self = shift; @@ -376,18 +394,5 @@ sub labels_information($) goto &Texinfo::Common::labels_information; } -BEGIN { - Texinfo::XSLoader::init ( - "Texinfo::Parser", - "Texinfo::Parser", - "Texinfo::ParserNonXS", - "Parsetexi", - 0); -} # end BEGIN - -END { - #reset_parser (); # for debugging memory leaks -} - 1; __END__ diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.xs b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.xs index 2995cd5cdf..66cd76e145 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.xs +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/Parsetexi.xs @@ -101,6 +101,9 @@ void conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME (int i) void +set_novalidate (int i) + +void set_documentlanguage (value) char *value diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.c index 76832a3538..f85ceff097 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.c @@ -1,4 +1,4 @@ -/* Copyright 2010-2019 Free Software Foundation, Inc. +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -40,6 +40,7 @@ ELEMENT *Root; +#ifdef ENABLE_NLS #define LOCALEDIR DATADIR "/locale" @@ -56,7 +57,12 @@ find_locales_dir (char *builddir) dTHX; - asprintf (&s, "%s/LocaleData", builddir); + /* Can't use asprintf here, because it might come from Gnulib, and + will then use malloc that is different from Perl's malloc, whereas + free below is redirected to Perl's implementation. This could + cause crashes if the two malloc/free implementations were different. */ + s = malloc (strlen (builddir) + strlen ("/LocaleData") + 1); + sprintf (s, "%s/LocaleData", builddir); dir = opendir (s); if (!dir) { @@ -88,6 +94,16 @@ init (int texinfo_uninstalled, char *builddir) return 1; } +#else + +int +init (int texinfo_uninstalled, char *builddir) +{ + return 1; +} + +#endif + static void reset_floats () { @@ -267,22 +283,24 @@ element_to_perl_hash (ELEMENT *e) /* TODO: Same optimizations as for 'type'. */ } - /* TODO sort out all these special cases. - Makes no sense to have 'contents' created for glyph commands like - @arrow{} or for accent commands. */ + /* A lot of special cases for when an empty contents array should be + created. Largely by trial and error to match the Perl code. Some of + them don't make sense, like @arrow{}, @image, or for accent commands. */ if (e->contents.number > 0 || e->type == ET_text_root || e->type == ET_root_line || e->type == ET_bracketed || e->type == ET_bracketed_def_content || e->type == ET_line_arg - || e->cmd == CM_image // why image? + || e->cmd == CM_image || e->cmd == CM_item && e->parent && e->parent->type == ET_row + || e->cmd == CM_headitem && e->parent && e->parent->type == ET_row || e->cmd == CM_tab && e->parent && e->parent->type == ET_row || e->cmd == CM_anchor || e->cmd == CM_macro || e->cmd == CM_multitable || e->type == ET_menu_entry_name + || e->type == ET_menu_entry_description || e->type == ET_brace_command_arg || e->type == ET_brace_command_context || e->type == ET_block_line_arg @@ -292,6 +310,7 @@ element_to_perl_hash (ELEMENT *e) || e->type == ET_elided || e->type == ET_elided_block || e->type == ET_preformatted + || e->type == ET_paragraph || (command_flags(e) & CF_root) || (command_data(e->cmd).flags & CF_brace && (command_data(e->cmd).data >= 0 @@ -419,8 +438,10 @@ element_to_perl_hash (ELEMENT *e) break; } case extra_integer: - { /* A simple integer. */ - int value = (int) f; + { /* A simple integer. The intptr_t cast here prevents + a warning on MinGW ("cast from pointer to integer of + different size"). */ + IV value = (IV) (intptr_t) f; STORE(newSViv (value)); break; } @@ -513,7 +534,7 @@ element_to_perl_hash (ELEMENT *e) break; } default: - abort (); + fatal ("unknown extra type"); break; } } @@ -750,7 +771,7 @@ build_single_index_data (INDEX *i) if (!e->content->hv) { if (e->content->parent) - abort (); /* element should not be in-tree */ + fatal ("index element should not be in-tree"); element_to_perl_hash (e->content); } contents_array = hv_fetch (e->content->hv, @@ -876,6 +897,17 @@ build_global_info (void) hv_store (hv, "novalidate", strlen ("novalidate"), newSVpv ("1", 0), 0); } + + char *txi_flags[] = { "txiindexatsignignore", "txiindexbackslashignore", + "txiindexhyphenignore", "txiindexlessthanignore", 0}; + char **p; + + for (p = txi_flags; (*p); p++) + { + if (fetch_value (*p)) + hv_store (hv, *p, strlen (*p), newSVpv ("1", 0), 0); + } + return hv; } @@ -908,10 +940,6 @@ build_global_info2 (void) BUILD_GLOBAL_UNIQ(titlepage); BUILD_GLOBAL_UNIQ(top); BUILD_GLOBAL_UNIQ(documentdescription); - BUILD_GLOBAL_UNIQ(setcontentsaftertitlepage); - BUILD_GLOBAL_UNIQ(setshortcontentsaftertitlepage); - BUILD_GLOBAL_UNIQ(novalidate); - BUILD_GLOBAL_UNIQ(validatemenus); BUILD_GLOBAL_UNIQ(pagesizes); BUILD_GLOBAL_UNIQ(fonttextsize); BUILD_GLOBAL_UNIQ(footnotestyle); diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.h index 71ca3a0725..7843680255 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/api.h @@ -1,4 +1,6 @@ /* api.h - declarations for api.c */ +#ifndef API_H +#define API_H extern ELEMENT *Root; int init (int texinfo_uninstalled, char *srcdir_in); @@ -21,3 +23,4 @@ HV *build_global_info (void); HV *build_global_info2 (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/close.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/close.c index 3ddb41910b..2568be2052 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/close.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/close.c @@ -324,10 +324,11 @@ close_current (ELEMENT *current, case ET_menu_entry_description: c = pop_context (); if (c != ct_preformatted) - abort (); + fatal ("preformatted context expected"); /* Remove empty menu_comment */ - if (current->contents.number == 0) + if (current->type == ET_menu_comment + && current->contents.number == 0) { current = current->parent; destroy_element (pop_element_from_contents (current)); @@ -342,7 +343,7 @@ close_current (ELEMENT *current, if (c != ct_line && c != ct_def) { /* error */ - abort (); + fatal ("line or def context expected"); } current = current->parent; break; @@ -384,24 +385,30 @@ close_commands (ELEMENT *current, enum command_id closed_command, if (closed_command && current->cmd == closed_command) { - /* 1758 - various error messages */ if (command_data(current->cmd).flags & CF_preformatted) { if (pop_context () != ct_preformatted) - abort (); + fatal ("preformatted context expected"); } else if (command_data(current->cmd).flags & CF_format_raw) { if (pop_context () != ct_rawpreformatted) - abort (); - // pop expanded formats stack + fatal ("rawpreformatted context expected"); + // TODO: pop expanded formats stack } else if (command_data(current->cmd).flags & CF_menu) { enum context c; c = pop_context (); if (c != ct_menu && c != ct_preformatted) - abort (); + fatal ("menu or preformatted context expected"); + } + else if (current->cmd == CM_math || current->cmd == CM_displaymath) + { + enum context c; + c = pop_context (); + if (c != ct_math) + fatal ("math context expected"); } if (command_data(current->cmd).data == BLOCK_region) diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.awk b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.awk index 5fea87bf9c..9974d1aefb 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.awk +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.awk @@ -1,4 +1,4 @@ -# Copyright 2010-2019 Free Software Foundation, Inc. +# Copyright 2010-2021 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -74,6 +74,8 @@ BEGIN { CI = srcdir "/command_ids.h" print "/* This file automatically generated by command_data.awk */" > CI + print "#ifndef COMMAND_IDS_H" > CI + print "#define COMMAND_IDS_H" > CI print > CI print "/* Useful aliases */" > CI print "#define CM_hex_09 CM_TAB" > CI @@ -154,6 +156,7 @@ END { } print "};" > CD print "};" > CI + print "#endif" > CI } diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.c index 44548f867a..7d0e5a0976 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.c @@ -53,13 +53,14 @@ COMMAND builtin_command_data[] = { "author", CF_line | CF_global, LINE_line, "b", CF_brace, BRACE_style, "backslashchar", CF_brace, 0, +"bsixpaper", CF_line, LINE_skipline, "bullet", CF_brace, 0, "bye", CF_line, LINE_skipline, "c", CF_line, LINE_lineraw, "caption", CF_brace, BRACE_context, "cartouche", CF_block, 0, "center", CF_line, LINE_line, -"centerchap", CF_root | CF_line | CF_sectioning, LINE_line, +"centerchap", CF_root | CF_line | CF_sectioning | CF_deprecated, LINE_line, "chapheading", CF_line | CF_sectioning, LINE_line, "chapter", CF_root | CF_line | CF_sectioning, LINE_line, "cite", CF_brace, BRACE_style, @@ -83,7 +84,7 @@ COMMAND builtin_command_data[] = { "deffn", CF_block | CF_def, 0, "deffnx", CF_line | CF_def, LINE_line, "defindex", CF_line, 1, -"definfoenclose", CF_line, 3, +"definfoenclose", CF_line | CF_deprecated, 3, "defivar", CF_block | CF_def | CF_def_alias, 0, "defivarx", CF_line | CF_def | CF_def_alias, LINE_line, "defmac", CF_block | CF_def | CF_def_alias, 0, @@ -127,6 +128,7 @@ COMMAND builtin_command_data[] = { "dircategory", CF_line, LINE_line, "direntry", CF_block | CF_menu, 0, "display", CF_block | CF_preformatted, 0, +"displaymath", CF_block, 0, "dmn", CF_brace, 1, "docbook", CF_block | CF_format_raw, 0, "documentdescription", CF_block | CF_global_unique, BLOCK_region, @@ -153,7 +155,7 @@ COMMAND builtin_command_data[] = { "everyfootingmarks", CF_line | CF_global_unique, 1, "everyheading", CF_line | CF_global_unique, LINE_lineraw, "everyheadingmarks", CF_line | CF_global_unique, 1, -"example", CF_block | CF_preformatted | CF_preformatted_code, 0, +"example", CF_block | CF_preformatted | CF_preformatted_code, BLOCK_variadic, "exampleindent", CF_line | CF_global, 1, "exclamdown", CF_brace, 0, "exdent", CF_line, LINE_line, @@ -208,7 +210,7 @@ COMMAND builtin_command_data[] = { "indent", CF_other, OTHER_skipspace, "indentedblock", CF_block, 0, "indicateurl", CF_brace | CF_code_style, 1, -"inforef", CF_brace | CF_ref, 3, +"inforef", CF_brace | CF_ref | CF_deprecated, 3, "inlinefmt", CF_brace | CF_inline, 2, "inlinefmtifelse", CF_brace | CF_inline, 3, "inlineifclear", CF_brace | CF_inline, 2, @@ -270,7 +272,7 @@ COMMAND builtin_command_data[] = { "raisesections", CF_line, LINE_skipline, "rbracechar", CF_brace, 0, "ref", CF_brace | CF_ref, 5, -"refill", CF_other, OTHER_noarg, +"refill", CF_other | CF_deprecated, OTHER_noarg, "registeredsymbol", CF_brace, 0, "result", CF_brace, 0, "ringaccent", CF_brace | CF_accent, BRACE_accent, @@ -283,9 +285,7 @@ COMMAND builtin_command_data[] = { "seeentry", CF_brace, 1, "set", CF_line, LINE_special, "setchapternewpage", CF_line | CF_global_unique, 1, -"setcontentsaftertitlepage", CF_line | CF_deprecated, LINE_skipline, "setfilename", CF_line | CF_global_unique, LINE_text, -"setshortcontentsaftertitlepage", CF_line | CF_deprecated, LINE_skipline, "settitle", CF_line | CF_global_unique, LINE_line, "shortcaption", CF_brace, BRACE_context, "shortcontents", CF_line | CF_global, LINE_skipline, @@ -344,7 +344,6 @@ COMMAND builtin_command_data[] = { "urefbreakstyle", CF_line | CF_global, 1, "url", CF_brace, 3, "v", CF_brace | CF_accent, BRACE_accent, -"validatemenus", CF_line | CF_global_unique, 1, "value", CF_brace, 0, "var", CF_brace, BRACE_style, "verb", CF_brace | CF_code_style, BRACE_other, diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.txt b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.txt index e2b62eb554..e5d5643a46 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.txt +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_data.txt @@ -1,4 +1,4 @@ -# Copyright 2010-2019 Free Software Foundation, Inc. +# Copyright 2010-2020 Free Software Foundation, Inc. # # This file is part of GNU Texinfo. # @@ -18,138 +18,136 @@ # Information from tp/Texinfo/Common.pm # Not in Common.pm -value brace +value brace ############################################################## # no brace commands - single letter commands -* nobrace +* nobrace # space -\x20 nobrace -\t nobrace -\n nobrace -- nobrace -| nobrace -/ nobrace -: nobrace -! nobrace -? nobrace -. nobrace -@ nobrace -& nobrace -} nobrace -{ nobrace -\\ nobrace +\x20 nobrace +\t nobrace +\n nobrace +- nobrace +| nobrace +/ nobrace +: nobrace +! nobrace +? nobrace +. nobrace +@ nobrace +& nobrace +} nobrace +{ nobrace +\\ nobrace ########################################################## # # line commands # ########################################################## -node line LINE_line -bye line LINE_skipline -end line LINE_text +node line LINE_line +bye line LINE_skipline +end line LINE_text # set, clear -set line LINE_special -clear line LINE_special -unmacro line LINE_special +set line LINE_special +clear line LINE_special +unmacro line LINE_special # comments -c line LINE_lineraw -comment line LINE_lineraw +c line LINE_lineraw +comment line LINE_lineraw # special -definfoenclose line 3 -alias line 2 +definfoenclose line,deprecated 3 +alias line 2 # number of arguments is not known in advance. -columnfractions line 1 +columnfractions line 1 # file names -setfilename line,global_unique LINE_text -verbatiminclude line LINE_text -include line LINE_text - -raisesections line LINE_skipline -lowersections line LINE_skipline -contents line,global LINE_skipline -shortcontents line,global LINE_skipline -summarycontents line,global LINE_skipline -insertcopying line,global LINE_skipline -clickstyle line,global LINE_special +setfilename line,global_unique LINE_text +verbatiminclude line LINE_text +include line LINE_text + +raisesections line LINE_skipline +lowersections line LINE_skipline +contents line,global LINE_skipline +shortcontents line,global LINE_skipline +summarycontents line,global LINE_skipline +insertcopying line,global LINE_skipline +clickstyle line,global LINE_special # more relevant in preamble -setcontentsaftertitlepage line,deprecated LINE_skipline -setshortcontentsaftertitlepage line,deprecated LINE_skipline -documentencoding line,global LINE_text -novalidate line,global_unique LINE_skipline -validatemenus line,global_unique 1 -dircategory line LINE_line +documentencoding line,global LINE_text +novalidate line,global_unique LINE_skipline +dircategory line LINE_line # with direntry is significant -pagesizes line,global_unique LINE_line +pagesizes line,global_unique LINE_line # or one? 200mm,150mm 11.5in -finalout line LINE_skipline -paragraphindent line,global 1 +finalout line LINE_skipline +paragraphindent line,global 1 # or a number and forbids anything else on the line -firstparagraphindent line,global 1 -frenchspacing line,global 1 -codequoteundirected line,global 1 -codequotebacktick line,global 1 -xrefautomaticsectiontitle line,global 1 -deftypefnnewline line,global 1 -fonttextsize line,global_unique 1 -allowcodebreaks line,global 1 -exampleindent line,global 1 -footnotestyle line,global_unique 1 -urefbreakstyle line,global 1 -afourpaper line LINE_skipline -afivepaper line LINE_skipline -afourlatex line LINE_skipline -afourwide line LINE_skipline -headings line,global 1 -setchapternewpage line,global_unique 1 +firstparagraphindent line,global 1 +frenchspacing line,global 1 +codequoteundirected line,global 1 +codequotebacktick line,global 1 +xrefautomaticsectiontitle line,global 1 +deftypefnnewline line,global 1 +fonttextsize line,global_unique 1 +allowcodebreaks line,global 1 +exampleindent line,global 1 +footnotestyle line,global_unique 1 +urefbreakstyle line,global 1 +afourpaper line LINE_skipline +afivepaper line LINE_skipline +afourlatex line LINE_skipline +afourwide line LINE_skipline +bsixpaper line LINE_skipline +headings line,global 1 +setchapternewpage line,global_unique 1 # only relevant in TeX, and special -everyheading line,global_unique LINE_lineraw -everyfooting line,global_unique LINE_lineraw -evenheading line,global_unique LINE_lineraw -evenfooting line,global_unique LINE_lineraw -oddheading line,global_unique LINE_lineraw -oddfooting line,global_unique LINE_lineraw - -smallbook line LINE_skipline -syncodeindex line 2 -synindex line 2 -defindex line 1 -defcodeindex line 1 -documentlanguage line,global LINE_text -kbdinputstyle line,global 1 -everyheadingmarks line,global_unique 1 -everyfootingmarks line,global_unique 1 -evenheadingmarks line,global_unique 1 -oddheadingmarks line,global_unique 1 -evenfootingmarks line,global_unique 1 -oddfootingmarks line,global_unique 1 +everyheading line,global_unique LINE_lineraw +everyfooting line,global_unique LINE_lineraw +evenheading line,global_unique LINE_lineraw +evenfooting line,global_unique LINE_lineraw +oddheading line,global_unique LINE_lineraw +oddfooting line,global_unique LINE_lineraw + +smallbook line LINE_skipline +syncodeindex line 2 +synindex line 2 +defindex line 1 +defcodeindex line 1 +documentlanguage line,global LINE_text +kbdinputstyle line,global 1 +everyheadingmarks line,global_unique 1 +everyfootingmarks line,global_unique 1 +evenheadingmarks line,global_unique 1 +oddheadingmarks line,global_unique 1 +evenfootingmarks line,global_unique 1 +oddfootingmarks line,global_unique 1 # formatting -center line LINE_line -printindex line,global 1 -listoffloats line,global LINE_line +center line LINE_line +printindex line,global 1 +listoffloats line,global LINE_line # especially in titlepage # shorttitle -shorttitlepage line,global_unique LINE_line -settitle line,global_unique LINE_line -author line,global LINE_line -subtitle line,global LINE_line -title line,global_unique LINE_line -sp line 1 -page line LINE_skipline -need line 1 +shorttitlepage line,global_unique LINE_line +settitle line,global_unique LINE_line +author line,global LINE_line +subtitle line,global LINE_line +title line,global_unique LINE_line +sp line 1 +page line LINE_skipline +need line 1 # not valid for info (should be in @iftex) -vskip line LINE_lineraw +vskip line LINE_lineraw -exdent line LINE_line +exdent line LINE_line -item_LINE line LINE_line -itemx line LINE_line +item_LINE line LINE_line +itemx line LINE_line -subentry line LINE_line +subentry line LINE_line ########################################################## # @@ -158,20 +156,20 @@ subentry line LINE_line ########################################################## # formatting -noindent other OTHER_skipspace -indent other OTHER_skipspace -headitem other OTHER_skipspace -item other OTHER_skipspace -tab other OTHER_skipspace +noindent other OTHER_skipspace +indent other OTHER_skipspace +headitem other OTHER_skipspace +item other OTHER_skipspace +tab other OTHER_skipspace # only valid in heading or footing -thischapter other,in_heading OTHER_noarg -thischaptername other,in_heading OTHER_noarg -thischapternum other,in_heading OTHER_noarg -thisfile other,in_heading OTHER_noarg -thispage other,in_heading OTHER_noarg -thistitle other,in_heading OTHER_noarg +thischapter other,in_heading OTHER_noarg +thischaptername other,in_heading OTHER_noarg +thischapternum other,in_heading OTHER_noarg +thisfile other,in_heading OTHER_noarg +thispage other,in_heading OTHER_noarg +thistitle other,in_heading OTHER_noarg # obsolete @-commands. -refill other OTHER_noarg +refill other,deprecated OTHER_noarg @@ -182,312 +180,312 @@ refill other OTHER_noarg ########################################################## # letter_no_arg_commands -aa brace 0 -AA brace 0 -ae brace 0 -oe brace 0 -AE brace 0 -OE brace 0 -o brace 0 -O brace 0 -ss brace 0 -l brace 0 -L brace 0 -DH brace 0 -dh brace 0 -TH brace 0 -th brace 0 +aa brace 0 +AA brace 0 +ae brace 0 +oe brace 0 +AE brace 0 +OE brace 0 +o brace 0 +O brace 0 +ss brace 0 +l brace 0 +L brace 0 +DH brace 0 +dh brace 0 +TH brace 0 +th brace 0 # no arg commands -TeX brace 0 -LaTeX brace 0 -bullet brace 0 -copyright brace 0 -registeredsymbol brace 0 -dots brace 0 -enddots brace 0 -equiv brace 0 -error brace 0 -expansion brace 0 -arrow brace 0 -minus brace 0 -point brace 0 -print brace 0 -result brace 0 -today brace 0 -exclamdown brace 0 -questiondown brace 0 -pounds brace 0 -ordf brace 0 -ordm brace 0 -atchar brace 0 -ampchar brace 0 -lbracechar brace 0 -rbracechar brace 0 -backslashchar brace 0 -hashchar brace 0 -comma brace 0 -euro brace 0 -geq brace 0 -leq brace 0 -tie brace 0 -textdegree brace 0 -click brace 0 -quotedblleft brace 0 -quotedblright brace 0 -quoteleft brace 0 -quoteright brace 0 -quotedblbase brace 0 -quotesinglbase brace 0 -guillemetleft brace 0 -guillemetright brace 0 -guillemotleft brace 0 -guillemotright brace 0 -guilsinglleft brace 0 -guilsinglright brace 0 +TeX brace 0 +LaTeX brace 0 +bullet brace 0 +copyright brace 0 +registeredsymbol brace 0 +dots brace 0 +enddots brace 0 +equiv brace 0 +error brace 0 +expansion brace 0 +arrow brace 0 +minus brace 0 +point brace 0 +print brace 0 +result brace 0 +today brace 0 +exclamdown brace 0 +questiondown brace 0 +pounds brace 0 +ordf brace 0 +ordm brace 0 +atchar brace 0 +ampchar brace 0 +lbracechar brace 0 +rbracechar brace 0 +backslashchar brace 0 +hashchar brace 0 +comma brace 0 +euro brace 0 +geq brace 0 +leq brace 0 +tie brace 0 +textdegree brace 0 +click brace 0 +quotedblleft brace 0 +quotedblright brace 0 +quoteleft brace 0 +quoteright brace 0 +quotedblbase brace 0 +quotesinglbase brace 0 +guillemetleft brace 0 +guillemetright brace 0 +guillemotleft brace 0 +guillemotright brace 0 +guilsinglleft brace 0 +guilsinglright brace 0 # accent commands - can be called with or without braces -# TODO do we need "brace" here as well? -\" brace,accent BRACE_accent -~ brace,accent BRACE_accent -^ brace,accent BRACE_accent -` brace,accent BRACE_accent -' brace,accent BRACE_accent -, brace,accent BRACE_accent -= brace,accent BRACE_accent -ringaccent brace,accent BRACE_accent -H brace,accent BRACE_accent -dotaccent brace,accent BRACE_accent -u brace,accent BRACE_accent -ubaraccent brace,accent BRACE_accent -udotaccent brace,accent BRACE_accent -v brace,accent BRACE_accent -ogonek brace,accent BRACE_accent -tieaccent brace,accent BRACE_accent -dotless brace,accent BRACE_accent +\" brace,accent BRACE_accent +~ brace,accent BRACE_accent +^ brace,accent BRACE_accent +` brace,accent BRACE_accent +' brace,accent BRACE_accent +, brace,accent BRACE_accent += brace,accent BRACE_accent +ringaccent brace,accent BRACE_accent +H brace,accent BRACE_accent +dotaccent brace,accent BRACE_accent +u brace,accent BRACE_accent +ubaraccent brace,accent BRACE_accent +udotaccent brace,accent BRACE_accent +v brace,accent BRACE_accent +ogonek brace,accent BRACE_accent +tieaccent brace,accent BRACE_accent +dotless brace,accent BRACE_accent # Style commands -asis brace BRACE_style -cite brace BRACE_style -clicksequence brace BRACE_style -dfn brace BRACE_style -emph brace BRACE_style -sc brace BRACE_style -t brace BRACE_style -var brace BRACE_style -headitemfont brace BRACE_style -code brace,code_style BRACE_style -command brace,code_style BRACE_style -env brace,code_style BRACE_style -file brace,code_style BRACE_style -kbd brace,code_style BRACE_style -option brace,code_style BRACE_style -samp brace,code_style BRACE_style -strong brace BRACE_style -sub brace BRACE_style -sup brace BRACE_style +asis brace BRACE_style +cite brace BRACE_style +clicksequence brace BRACE_style +dfn brace BRACE_style +emph brace BRACE_style +sc brace BRACE_style +t brace BRACE_style +var brace BRACE_style +headitemfont brace BRACE_style +code brace,code_style BRACE_style +command brace,code_style BRACE_style +env brace,code_style BRACE_style +file brace,code_style BRACE_style +kbd brace,code_style BRACE_style +option brace,code_style BRACE_style +samp brace,code_style BRACE_style +strong brace BRACE_style +sub brace BRACE_style +sup brace BRACE_style # Regular font style commands -r brace BRACE_style -i brace BRACE_style -b brace BRACE_style -sansserif brace BRACE_style -slanted brace BRACE_style - -U brace 1 -dmn brace 1 -titlefont brace,global 1 - -w brace BRACE_other -key brace BRACE_other - -hyphenation brace,global BRACE_other -anchor brace 1 -errormsg brace 1 -sortas brace 1 -seeentry brace 1 -seealso brace 1 +r brace BRACE_style +i brace BRACE_style +b brace BRACE_style +sansserif brace BRACE_style +slanted brace BRACE_style + +U brace 1 +dmn brace 1 +titlefont brace,global 1 + +w brace BRACE_other +key brace BRACE_other + +hyphenation brace,global BRACE_other +anchor brace 1 +errormsg brace 1 +sortas brace 1 +seeentry brace 1 +seealso brace 1 # more code style commands -indicateurl brace,code_style 1 -verb brace,code_style BRACE_other -t brace,code_style BRACE_other +indicateurl brace,code_style 1 +verb brace,code_style BRACE_other +t brace,code_style BRACE_other # context brace commands - commands that enclose full texts -footnote brace,global BRACE_context -caption brace BRACE_context -shortcaption brace BRACE_context -math brace BRACE_context +footnote brace,global BRACE_context +caption brace BRACE_context +shortcaption brace BRACE_context +math brace BRACE_context # explained commands -abbr brace 2 -acronym brace 2 +abbr brace 2 +acronym brace 2 # inline format command -inlineraw brace,inline 2 -inlinefmt brace,inline 2 -inlinefmtifelse brace,inline 3 +inlineraw brace,inline 2 +inlinefmt brace,inline 2 +inlinefmtifelse brace,inline 3 # inline conditional commands -inlineifclear brace,inline 2 -inlineifset brace,inline 2 +inlineifclear brace,inline 2 +inlineifset brace,inline 2 -email brace 2 +email brace 2 -uref brace 3 -url brace 3 -inforef brace,ref 3 +uref brace 3 +url brace 3 +inforef brace,ref,deprecated 3 -xref brace,ref 5 -ref brace,ref 5 -pxref brace,ref 5 -image brace 5 +xref brace,ref 5 +ref brace,ref 5 +pxref brace,ref 5 +image brace 5 ########################################################## # def* commands -deffn block,def -defvr block,def -deftypefn block,def -deftypeop block,def -deftypevr block,def -defcv block,def -deftypecv block,def -defop block,def -deftp block,def +deffn block,def +defvr block,def +deftypefn block,def +deftypeop block,def +deftypevr block,def +defcv block,def +deftypecv block,def +defop block,def +deftp block,def # shortcuts -defun block,def,def_alias -defmac block,def,def_alias -defspec block,def,def_alias -defvar block,def,def_alias -defopt block,def,def_alias -deftypefun block,def,def_alias -deftypevar block,def,def_alias -defivar block,def,def_alias -deftypeivar block,def,def_alias -defmethod block,def,def_alias -deftypemethod block,def,def_alias +defun block,def,def_alias +defmac block,def,def_alias +defspec block,def,def_alias +defvar block,def,def_alias +defopt block,def,def_alias +deftypefun block,def,def_alias +deftypevar block,def,def_alias +defivar block,def,def_alias +deftypeivar block,def,def_alias +defmethod block,def,def_alias +deftypemethod block,def,def_alias # def*x -deffnx line,def LINE_line -defvrx line,def LINE_line -deftypefnx line,def LINE_line -deftypeopx line,def LINE_line -deftypevrx line,def LINE_line -defcvx line,def LINE_line -deftypecvx line,def LINE_line -defopx line,def LINE_line -deftpx line,def LINE_line +deffnx line,def LINE_line +defvrx line,def LINE_line +deftypefnx line,def LINE_line +deftypeopx line,def LINE_line +deftypevrx line,def LINE_line +defcvx line,def LINE_line +deftypecvx line,def LINE_line +defopx line,def LINE_line +deftpx line,def LINE_line # shortcuts -defunx line,def,def_alias LINE_line -defmacx line,def,def_alias LINE_line -defspecx line,def,def_alias LINE_line -defvarx line,def,def_alias LINE_line -defoptx line,def,def_alias LINE_line -deftypefunx line,def,def_alias LINE_line -deftypevarx line,def,def_alias LINE_line -defivarx line,def,def_alias LINE_line -deftypeivarx line,def,def_alias LINE_line -defmethodx line,def,def_alias LINE_line -deftypemethodx line,def,def_alias LINE_line - -multitable block,blockitem BLOCK_multitable +defunx line,def,def_alias LINE_line +defmacx line,def,def_alias LINE_line +defspecx line,def,def_alias LINE_line +defvarx line,def,def_alias LINE_line +defoptx line,def,def_alias LINE_line +deftypefunx line,def,def_alias LINE_line +deftypevarx line,def,def_alias LINE_line +defivarx line,def,def_alias LINE_line +deftypeivarx line,def,def_alias LINE_line +defmethodx line,def,def_alias LINE_line +deftypemethodx line,def,def_alias LINE_line + +multitable block,blockitem BLOCK_multitable # menu commands -menu block,menu -detailmenu block,menu,global -direntry block,menu +menu block,menu +detailmenu block,menu,global +direntry block,menu # align commands -raggedright block -flushleft block -flushright block +raggedright block +flushleft block +flushright block -cartouche block -group block -indentedblock block -smallindentedblock block +cartouche block +group block +indentedblock block +smallindentedblock block # region commands -titlepage block,global_unique BLOCK_region -copying block,global_unique BLOCK_region -documentdescription block,global_unique BLOCK_region +titlepage block,global_unique BLOCK_region +copying block,global_unique BLOCK_region +documentdescription block,global_unique BLOCK_region # preformatted commands -example block,preformatted,preformatted_code -smallexample block,preformatted,preformatted_code -lisp block,preformatted,preformatted_code -smalllisp block,preformatted,preformatted_code -display block,preformatted -smalldisplay block,preformatted -format block,preformatted -smallformat block,preformatted +example block,preformatted,preformatted_code BLOCK_variadic +smallexample block,preformatted,preformatted_code +lisp block,preformatted,preformatted_code +smalllisp block,preformatted,preformatted_code +display block,preformatted +smalldisplay block,preformatted +format block,preformatted +smallformat block,preformatted # format raw commands -html block,format_raw -tex block,format_raw -xml block,format_raw -docbook block,format_raw +html block,format_raw +tex block,format_raw +xml block,format_raw +docbook block,format_raw # raw commands -verbatim block BLOCK_raw -ignore block BLOCK_raw -macro block BLOCK_raw -rmacro block BLOCK_raw - -table block,blockitem 1 -ftable block,blockitem 1 -vtable block,blockitem 1 -itemize block,blockitem 1 -enumerate block,blockitem 1 -quotation block 1 -smallquotation block 1 - -float block 2 +verbatim block BLOCK_raw +ignore block BLOCK_raw +macro block BLOCK_raw +rmacro block BLOCK_raw + +table block,blockitem 1 +ftable block,blockitem 1 +vtable block,blockitem 1 +itemize block,blockitem 1 +enumerate block,blockitem 1 +quotation block 1 +smallquotation block 1 + +float block 2 +displaymath block 0 ################################################################## # Conditional commands -ifhtml block BLOCK_conditional -ifnothtml block BLOCK_conditional -iftex block BLOCK_conditional -ifnottex block BLOCK_conditional -ifxml block BLOCK_conditional -ifnotxml block BLOCK_conditional -ifdocbook block BLOCK_conditional -ifnotdocbook block BLOCK_conditional -ifinfo block BLOCK_conditional -ifnotinfo block BLOCK_conditional -ifplaintext block BLOCK_conditional -ifnotplaintext block BLOCK_conditional -ifset block BLOCK_conditional -ifclear block BLOCK_conditional -ifcommanddefined block BLOCK_conditional -ifcommandnotdefined block BLOCK_conditional +ifhtml block BLOCK_conditional +ifnothtml block BLOCK_conditional +iftex block BLOCK_conditional +ifnottex block BLOCK_conditional +ifxml block BLOCK_conditional +ifnotxml block BLOCK_conditional +ifdocbook block BLOCK_conditional +ifnotdocbook block BLOCK_conditional +ifinfo block BLOCK_conditional +ifnotinfo block BLOCK_conditional +ifplaintext block BLOCK_conditional +ifnotplaintext block BLOCK_conditional +ifset block BLOCK_conditional +ifclear block BLOCK_conditional +ifcommanddefined block BLOCK_conditional +ifcommandnotdefined block BLOCK_conditional ########################################################## # Root commands -node root,line LINE_line -top root,line,sectioning,global_unique LINE_line -chapter root,line,sectioning LINE_line -unnumbered root,line,sectioning LINE_line -chapheading line,sectioning LINE_line -appendix root,line,sectioning LINE_line -section root,line,sectioning LINE_line -unnumberedsec root,line,sectioning LINE_line -heading line,sectioning LINE_line -appendixsec root,line,sectioning LINE_line -subsection root,line,sectioning LINE_line -unnumberedsubsec root,line,sectioning LINE_line -subheading line,sectioning LINE_line -appendixsubsec root,line,sectioning LINE_line -subsubsection root,line,sectioning LINE_line -unnumberedsubsubsec root,line,sectioning LINE_line -subsubheading line,sectioning LINE_line -appendixsubsubsec root,line,sectioning LINE_line - -part root,line,sectioning,global LINE_line -appendixsection root,line,sectioning LINE_line -majorheading line,sectioning LINE_line -centerchap root,line,sectioning LINE_line +node root,line LINE_line +top root,line,sectioning,global_unique LINE_line +chapter root,line,sectioning LINE_line +unnumbered root,line,sectioning LINE_line +chapheading line,sectioning LINE_line +appendix root,line,sectioning LINE_line +section root,line,sectioning LINE_line +unnumberedsec root,line,sectioning LINE_line +heading line,sectioning LINE_line +appendixsec root,line,sectioning LINE_line +subsection root,line,sectioning LINE_line +unnumberedsubsec root,line,sectioning LINE_line +subheading line,sectioning LINE_line +appendixsubsec root,line,sectioning LINE_line +subsubsection root,line,sectioning LINE_line +unnumberedsubsubsec root,line,sectioning LINE_line +subsubheading line,sectioning LINE_line +appendixsubsubsec root,line,sectioning LINE_line + +part root,line,sectioning,global LINE_line +appendixsection root,line,sectioning LINE_line +majorheading line,sectioning LINE_line +centerchap root,line,sectioning,deprecated LINE_line diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_ids.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_ids.h index 23058049e9..1201e40484 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_ids.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/command_ids.h @@ -1,4 +1,6 @@ /* This file automatically generated by command_data.awk */ +#ifndef COMMAND_IDS_H +#define COMMAND_IDS_H /* Useful aliases */ #define CM_hex_09 CM_TAB @@ -83,6 +85,7 @@ CM_atchar, CM_author, CM_b, CM_backslashchar, +CM_bsixpaper, CM_bullet, CM_bye, CM_c, @@ -157,6 +160,7 @@ CM_dh, CM_dircategory, CM_direntry, CM_display, +CM_displaymath, CM_dmn, CM_docbook, CM_documentdescription, @@ -313,9 +317,7 @@ CM_seealso, CM_seeentry, CM_set, CM_setchapternewpage, -CM_setcontentsaftertitlepage, CM_setfilename, -CM_setshortcontentsaftertitlepage, CM_settitle, CM_shortcaption, CM_shortcontents, @@ -374,7 +376,6 @@ CM_uref, CM_urefbreakstyle, CM_url, CM_v, -CM_validatemenus, CM_value, CM_var, CM_verb, @@ -391,3 +392,4 @@ CM_hex_7c, CM_hex_7d, CM_hex_7e, }; +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.c index 7b2d65a3c9..e0caffd149 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.c @@ -1,4 +1,4 @@ -/* Copyright 2010-2019 Free Software Foundation, Inc. +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +20,8 @@ #include "command_ids.h" #include "commands.h" +#include "errors.h" + #include "command_data.c" COMMAND *user_defined_command_data = 0; @@ -82,7 +84,7 @@ add_texinfo_command (char *name) = realloc (user_defined_command_data, (user_defined_space += 10) * sizeof (COMMAND)); if (!user_defined_command_data) - abort (); + fatal ("could not realloc"); } user_defined_command_data[user_defined_number].cmdname = strdup (name); diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.h index df29dff316..92078a4c4d 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/commands.h @@ -1,5 +1,7 @@ /* commands.h - declarations for commands.c */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +#ifndef COMMANDS_H +#define COMMANDS_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -105,9 +107,12 @@ void wipe_user_commands (void); #define BLOCK_raw -2 #define BLOCK_multitable -3 #define BLOCK_region -4 +#define BLOCK_variadic -5 /* Types of brace command (CF_brace). */ #define BRACE_context -1 /* Can enclose paragraph breaks. */ #define BRACE_accent -2 #define BRACE_style -3 #define BRACE_other -4 + +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/conf.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/conf.h index d55a46614c..7efcb566b3 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/conf.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/conf.h @@ -1,5 +1,7 @@ /* conf.h - declarations for conf.c */ -/* Copyright 2010-2018 Free Software Foundation, Inc. +#ifndef CONF_H +#define CONF_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,3 +29,4 @@ void conf_set_CPP_LINE_DIRECTIVES (int i); void conf_set_IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME (int i); void reset_conf (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.c index b2b85cf582..b3fca7afc5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.c @@ -49,7 +49,7 @@ enum context pop_context () { if (top == 0) - abort (); + fatal ("context stack empty"); debug (">>>>>>>>>>>>>POPPING STACK AT %d", top - 1); return stack[--top]; @@ -96,7 +96,7 @@ ELEMENT * pop_region () { if (region_top == 0) - abort (); + fatal ("region stack empty"); debug (">>>>>>>>>>>>>POPPING REGION STACK AT %d", region_top - 1); return region_stack[--region_top]; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.h index 4fea34d1f3..9b4f93317c 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/context_stack.h @@ -1,5 +1,7 @@ /* context_stack.h - declarations for context_stack.c */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +#ifndef CONTEXT_STACK_H +#define CONTEXT_STACK_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "tree_types.h" + enum context { ct_NONE, ct_line, @@ -49,3 +53,4 @@ ELEMENT *current_region (void); enum command_id current_region_cmd (void); void reset_region_stack (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.c index 7138df7ab3..b932855c27 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.c @@ -211,11 +211,14 @@ convert_to_text (ELEMENT *e, int *superfluous_arg) ELEMENT *e1 = contents_child_by_index (e, i); if (e1->text.end > 0) ADD(e1->text.text); - else if (e1->cmd == CM_AT_SIGN) + else if (e1->cmd == CM_AT_SIGN + || e1->cmd == CM_atchar) ADD("@"); - else if (e1->cmd == CM_OPEN_BRACE) + else if (e1->cmd == CM_OPEN_BRACE + || e1->cmd == CM_lbracechar) ADD("{"); - else if (e1->cmd == CM_CLOSE_BRACE) + else if (e1->cmd == CM_CLOSE_BRACE + || e1->cmd == CM_rbracechar) ADD("}"); else *superfluous_arg = 1; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.h index 7535bc95e0..a0a9650428 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/convert.h @@ -1,4 +1,11 @@ /* convert.h - definitions for convert.c */ +#ifndef CONVERT_H +#define CONVERT_H + +#include "tree_types.h" + char *convert_to_texinfo (ELEMENT *e); char *convert_to_text (ELEMENT *e, int *superfluous_arg); char *node_extra_to_texi (NODE_SPEC_EXTRA *nse); + +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.c index 1c028e9c5d..79499e0eb5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.c @@ -1,4 +1,4 @@ -/* Copyright 2015-2019 Free Software Foundation, Inc. +/* Copyright 2015-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,8 +15,9 @@ #include <config.h> -#include "tree_types.h" #include "counter.h" +#include "parser.h" + void counter_push (COUNTER *c, ELEMENT *elt, int num) @@ -27,7 +28,7 @@ counter_push (COUNTER *c, ELEMENT *elt, int num) c->values = realloc (c->values, c->space * sizeof (int)); c->elts = realloc (c->elts, c->space * sizeof (ELEMENT *)); if (!c->values) - abort (); + fatal ("could not realloc"); } c->values[c->nvalues] = num; c->elts[c->nvalues] = elt; @@ -41,7 +42,7 @@ void counter_pop (COUNTER *c) { if (!c->nvalues) - abort (); + fatal ("could not realloc"); c->nvalues--; c->values[c->nvalues] = 0; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.h index 48c9bb21ec..9e5ce2873c 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/counter.h @@ -1,5 +1,10 @@ /* counter.h - definitions for counter.c */ -/* Copyright 2015-2019 Free Software Foundation, Inc. +#ifndef COUNTER_H +#define COUNTER_H + +#include "tree_types.h" + +/* Copyright 2015-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,6 +19,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "tree_types.h" + typedef struct { int *values; /* Array of values. */ ELEMENT **elts; /* Elements corresponding to each value. */ @@ -26,3 +33,9 @@ void counter_pop (COUNTER *c); void counter_inc (COUNTER *c); void counter_dec (COUNTER *c); int counter_value (COUNTER *c, ELEMENT *e); + +/* A large positive number used to represent an unlimited number of remaining + arguments. */ +#define COUNTER_VARIADIC 32767 + +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.c index b285096b14..b2bddb205f 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.c @@ -291,11 +291,10 @@ parse_def (enum command_id command, ELEMENT *current) if (def_aliases[i].alias == command) goto found; } - abort (); /* Has CF_def_alias but no alias defined. */ + fatal ("no alias for CF_def_alias command"); found: - /* Prepended content is stuck into contents, so - @defun is converted into - @deffn Function */ + /* Prepended content is inserted into contents, so + @defun is converted into @deffn Function */ category = def_aliases[i].category; command = def_aliases[i].command; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.h index 13e93ed51e..074e5286e0 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/def.h @@ -1,3 +1,9 @@ /* def.h - definitions for def.c */ +#ifndef DEF_H +#define DEF_H + +#include "tree_types.h" + void gather_def_item (ELEMENT *current, enum command_id next_command); DEF_INFO *parse_def (enum command_id command, ELEMENT *current); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.awk b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.awk index 5c2c062c50..83618f9929 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.awk +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.awk @@ -1,4 +1,4 @@ -# Copyright 2014-2018 Free Software Foundation, Inc. +# Copyright 2014-2021 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,6 +22,8 @@ BEGIN { ETC = srcdir "/element_types.c" print "/* This file automatically generated by element_types.awk */" > ETH + print "#ifndef ELEMENT_TYPES_H" > ETH + print "#define ELEMENT_TYPES_H" > ETH print "enum element_type {" > ETH print "ET_NONE," > ETH } @@ -35,6 +37,7 @@ END { print "};" > ETH print > ETH print "extern char *element_type_names[];" > ETH + print "#endif" > ETH print "char *element_type_names[] = {" > ETC print "0," > ETC diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.h index 32ab38121f..9a6fb59c9b 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/element_types.h @@ -1,4 +1,6 @@ /* This file automatically generated by element_types.awk */ +#ifndef ELEMENT_TYPES_H +#define ELEMENT_TYPES_H enum element_type { ET_NONE, ET_command_as_argument, @@ -70,3 +72,4 @@ ET_untranslated, }; extern char *element_type_names[]; +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/end_line.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/end_line.c index 0fd806613f..4556780052 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/end_line.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/end_line.c @@ -160,6 +160,7 @@ parse_special_misc_command (char *line, enum command_id cmd, int *has_comment) q = strpbrk (p, " \t\f\r\n" /* whitespace */ "{\\}~^+\"<>|@"); /* other bytes that aren't allowed */ + /* see also read_flag_name function in end_line.c */ r = skip_comment (p, has_comment); @@ -196,28 +197,29 @@ set_invalid: } case CM_clear: { - char *flag; + char *flag = 0; p = line; p += strspn (p, whitespace_chars); if (!*p) goto clear_no_name; q = p; - flag = read_command_name (&q); + flag = read_flag_name (&q); if (!flag) goto clear_invalid; - free (flag); r = q + strspn (q, whitespace_chars); if (*r) goto clear_invalid; /* Trailing argument. */ ADD_ARG (p, q - p); - clear_value (p, q - p); + clear_value (flag); + free (flag); break; clear_no_name: line_error ("@clear requires a name"); break; clear_invalid: + free (flag); line_error ("bad name for @clear"); break; } @@ -264,7 +266,7 @@ clickstyle_invalid: free (value); break; default: - abort (); + fatal ("unknown special line command"); } if (remaining) @@ -494,6 +496,8 @@ parse_line_command_args (ELEMENT *line_command) files being overwritten by the files read by texindex. */ { static char *forbidden_index_names[] = { + "cp", "fn", "ky", "pg", "tp", "vr", + "cps", "fns", "kys", "pgs", "tps", "vrs", "info", "ps", "pdf", "htm", "html", "log", "aux", "dvi", "texi", "txi", "texinfo", "tex", "bib", 0 @@ -504,7 +508,7 @@ parse_line_command_args (ELEMENT *line_command) goto defindex_reserved; if (index_by_name (name)) - goto defindex_reserved; + { free (name); break; } } add_index (name, cmd == CM_defcodeindex ? 1 : 0); @@ -1001,7 +1005,7 @@ end_line_starting_block (ELEMENT *current) enum context c; c = pop_context (); if (c != ct_line) - abort (); + fatal ("line context expected"); if (current->parent->cmd == CM_multitable) { @@ -1299,17 +1303,14 @@ end_line_misc_line (ELEMENT *current) misc_cmd = current; cmd = current->cmd; if (!cmd) - abort (); + fatal ("command name unknown for @end"); arg_type = command_data(cmd).data; /* Check 'line' is top of the context stack */ c = pop_context (); if (c != ct_line) - { - /* error */ - abort (); - } + fatal ("line context expected"); debug ("MISC END %s", command_name(cmd)); @@ -2013,9 +2014,13 @@ end_line (ELEMENT *current) if (description) description_or_menu_comment = description; else - { // 2707 - /* Normally this cannot happen. */ - abort (); + { + ELEMENT *e; + /* "Normally this cannot happen." */ + bug ("no description in menu entry"); + e = new_element (ET_menu_entry_description); + add_to_element_args (entry, e); + description_or_menu_comment = e; } } else if (menu->contents.number > 0 @@ -2029,10 +2034,10 @@ end_line (ELEMENT *current) if (current->contents.number > 0 && last_contents_child(current)->type == ET_preformatted) current = last_contents_child(current); - else // 2725 + else { /* This should not happen */ - //abort (); + bug ("description or menu comment not in preformatted"); ELEMENT *e; e = new_element (ET_preformatted); add_to_element_contents (current, e); @@ -2100,14 +2105,13 @@ end_line (ELEMENT *current) KEY_PAIR *k; if (pop_context () != ct_def) - abort (); + fatal ("def context expected"); k = lookup_extra (current->parent, "original_def_cmdname"); if (k) original_def_command = lookup_command ((char *) k->value); else original_def_command = current->parent->parent->cmd; - // TODO: What if k is not defined? def_command = original_def_command; /* Strip an trailing x from the command, e.g. @deffnx -> @deffn */ @@ -2266,9 +2270,8 @@ end_line (ELEMENT *current) } } - /* 3470 Check for infinite loop bugs */ if (current == current_old) - abort (); + fatal ("infinite loop when closing commands"); current = end_line (current); } diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.c index 1a57e0c8d0..522bd3434d 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.c @@ -1,4 +1,4 @@ -/* Copyright 2010-2019 Free Software Foundation, Inc. +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,17 +14,32 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <config.h> + + +#ifdef ENABLE_NLS #include <libintl.h> +#endif #include <stdlib.h> #include <stdarg.h> #include <stdio.h> -#include "tree_types.h" #include "errors.h" #include "input.h" #include "text.h" +void bug (char *message) +{ + fprintf (stderr, "texi2any (XS parser): bug: %s\n", message); +} + +void fatal (char *message) +{ + bug (message); + abort (); +} + + typedef struct { char *message; enum error_type type; @@ -40,8 +55,13 @@ line_error_internal (enum error_type type, LINE_NR *cmd_line_nr, char *format, va_list v) { char *message; +#ifdef ENABLE_NLS vasprintf (&message, gettext(format), v); - if (!message) abort (); +#else + vasprintf (&message, format, v); +#endif + if (!message) fatal ("vasprintf failed"); + if (error_number == error_space) { error_list = realloc (error_list, diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.h index 112669f46b..733d1372a5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/errors.h @@ -1,4 +1,11 @@ /* errors.h - declarations for error.c */ +#ifndef ERRORS_H +#define ERRORS_H + +#include "tree_types.h" + +void fatal (char *); +void bug (char *); enum error_type { error, warning }; @@ -12,3 +19,4 @@ void line_error_ext (enum error_type type, LINE_NR *cmd_line_nr, void bug_message (char *format, ...); char *dump_errors (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/extra.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/extra.c index 55cae17d6c..ccdb7fe5c0 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/extra.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/extra.c @@ -35,7 +35,7 @@ add_extra_key (ELEMENT *e, char *key, ELEMENT *value, e->extra = realloc (e->extra, (e->extra_space += 5) * sizeof (KEY_PAIR)); if (!e->extra) - abort (); + fatal ("realloc failed"); } e->extra_number++; } @@ -146,7 +146,7 @@ add_extra_string_dup (ELEMENT *e, char *key, char *value) } void -add_extra_integer (ELEMENT *e, char *key, int value) +add_extra_integer (ELEMENT *e, char *key, long value) { add_extra_key (e, key, (ELEMENT *) value, extra_integer); } diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.c index 91f43ac4b4..fa3ce138f3 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.c @@ -1,5 +1,5 @@ /* handle_commands.c -- what to do when a command name is first read */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +/* Copyright 2010-2020 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -324,7 +324,7 @@ handle_line_command (ELEMENT *current, char **line_inout, { if (cmd != CM_bye) { - /* Something to do with document_root and text_root. */ + /* Use document_root when we have nodes or sections. */ ELEMENT *new_root = new_element (ET_document_root); add_to_element_contents (new_root, current); current = new_root; @@ -334,7 +334,7 @@ handle_line_command (ELEMENT *current, char **line_inout, { current = current->parent; if (!current) - abort (); + fatal ("no parent element"); } } @@ -500,7 +500,7 @@ handle_line_command (ELEMENT *current, char **line_inout, } else if (cmd == CM_novalidate) { - /* do nothing - novalidate is set in build_global_info */ + global_info.novalidate = 1; } if (misc) @@ -530,7 +530,7 @@ handle_line_command (ELEMENT *current, char **line_inout, if (cmd == CM_item_LINE || cmd == CM_itemx) { ELEMENT *parent; - if (parent = item_line_parent (current)) + if ((parent = item_line_parent (current))) { debug ("ITEM_LINE"); current = parent; @@ -556,7 +556,7 @@ handle_line_command (ELEMENT *current, char **line_inout, if (cmd == CM_subentry) { - int level = 1; + long level = 1; ELEMENT *parent = current->parent; if (!(command_flags(parent) & CF_index_entry_command) @@ -571,7 +571,7 @@ handle_line_command (ELEMENT *current, char **line_inout, { KEY_PAIR *k = lookup_extra (parent, "level"); if (k && k->value) - level = (int) k->value + 1; + level = (long) k->value + 1; } add_extra_integer (misc, "level", level); if (level > 2) @@ -613,11 +613,11 @@ handle_line_command (ELEMENT *current, char **line_inout, base_name = strdup (base_name); base_len = strlen (base_name); if (base_name[base_len - 1] != 'x') - abort (); + fatal ("no x at end of def command name"); base_name[base_len - 1] = '\0'; base_command = lookup_command (base_name); if (base_command == CM_NONE) - abort (); + fatal ("no def base command"); add_extra_string (misc, "def_command", base_name); after_paragraph = check_no_text (current); @@ -804,7 +804,7 @@ handle_block_command (ELEMENT *current, char **line_inout, line_error ("@%s requires a name", command_name(cmd)); else { - char *flag = read_command_name (&p); + char *flag = read_flag_name (&p); if (!flag) goto bad_value; else @@ -826,7 +826,7 @@ handle_block_command (ELEMENT *current, char **line_inout, { if (cmd == CM_ifclear || cmd == CM_ifset) { - char *val = fetch_value (flag, strlen (flag)); + char *val = fetch_value (flag); if (val) iftrue = 1; if (cmd == CM_ifclear) @@ -907,7 +907,7 @@ handle_block_command (ELEMENT *current, char **line_inout, destroy_element (pop_element_from_contents (menu)); if (pop_context () != ct_preformatted) - abort (); + fatal ("preformatted context expected"); if (menu->type == ET_menu_entry) menu = menu->parent; current = menu; @@ -944,6 +944,8 @@ handle_block_command (ELEMENT *current, char **line_inout, { if (command_data(cmd).flags & CF_preformatted) push_context (ct_preformatted); + else if (cmd == CM_displaymath) + push_context (ct_math); else if (command_data(cmd).flags & CF_format_raw) { push_context (ct_rawpreformatted); @@ -1013,17 +1015,6 @@ handle_block_command (ELEMENT *current, char **line_inout, done in Perl code. */ } } - else if (cmd != CM_direntry) - { - if (conf.show_menu) - { - line_error ("@%s seen before first @node", - command_name(cmd)); - line_error ("perhaps your @top node should be " - "wrapped in @ifnottex rather than @ifinfo?"); - } - // 4810 unassociated menus - } } if (cmd == CM_itemize || cmd == CM_enumerate) @@ -1041,6 +1032,12 @@ handle_block_command (ELEMENT *current, char **line_inout, current, command_data (current->cmd).data - 1); } + else if (command_data (current->cmd).data == BLOCK_variadic) + { + /* Unlimited args */ + counter_push (&count_remaining_args, current, + COUNTER_VARIADIC); + } current = bla; if (!(command_data(cmd).flags & CF_def)) diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.h index 01847d1887..08673a2d5c 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/handle_commands.h @@ -1,4 +1,8 @@ /* handle_commands.h - declarations for handle_commands.c */ +#ifndef HANDLE_COMMANDS_H +#define HANDLE_COMMANDS_H + +#include "tree_types.h" ELEMENT *handle_other_command (ELEMENT *current, char **line_inout, enum command_id cmd_id, int *status); @@ -12,3 +16,4 @@ int check_no_text (ELEMENT *current); void clear_expanded_formats (void); void add_expanded_format (char *format); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.c index 35051094e0..f1dbc521f5 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.c @@ -42,7 +42,7 @@ associate_command_to_index (enum command_id cmd, INDEX *idx) cmd_to_idx = realloc (cmd_to_idx, sizeof (CMD_TO_IDX) * (cmd_to_idx_space += 10)); if (!cmd_to_idx) - abort (); + fatal ("no index for command"); } cmd_to_idx[num_index_commands].cmd = cmd; @@ -284,7 +284,7 @@ enter_index_entry (enum command_id index_type_command, idx->index_entries = realloc (idx->index_entries, sizeof (INDEX_ENTRY) * (idx->index_space += 20)); if (!idx->index_entries) - abort (); + fatal ("realloc failed"); } entry = &idx->index_entries[idx->index_number++]; memset (entry, 0, sizeof (INDEX_ENTRY)); diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.h index fa960b186f..043042d3e2 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/indices.h @@ -1,4 +1,8 @@ /* indices.h - declarations for indices.h */ +#ifndef INDICES_H +#define INDICES_H + +#include "tree_types.h" extern INDEX **index_names; @@ -12,3 +16,4 @@ void enter_index_entry (enum command_id index_type_command, ELEMENT *content); INDEX *ultimate_index (INDEX *index); void wipe_indices (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.c index 560e637f87..f97edbfd4e 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.c @@ -1,4 +1,4 @@ -/* Copyright 2010-2019 Free Software Foundation, Inc. +/* Copyright 2010-2020 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ #include <errno.h> #include <sys/stat.h> -#include "tree_types.h" +#include "errors.h" #include "input.h" #include "text.h" #include "commands.h" @@ -34,8 +34,11 @@ enum input_type { IN_file, IN_text }; enum character_encoding { ce_latin1, ce_latin2, + ce_latin15, ce_utf8, - ce_shiftjis + ce_shiftjis, + ce_koi8r, + ce_koi8u }; typedef struct { @@ -49,12 +52,28 @@ typedef struct { into lines. */ } INPUT; -static char *input_encoding; +enum character_encoding input_encoding; void set_input_encoding (char *encoding) { - input_encoding = encoding; + if (!strcasecmp (encoding, "utf-8")) + input_encoding = ce_utf8; + else if (!strcmp (encoding, "iso-8859-1") + || !strcmp (encoding, "us-ascii")) + input_encoding = ce_latin1; + else if (!strcmp (encoding, "iso-8859-2")) + input_encoding = ce_latin2; + else if (!strcmp (encoding, "iso-8859-15")) + input_encoding = ce_latin15; + else if (!strcmp (encoding, "shift_jis")) + input_encoding = ce_shiftjis; + else if (!strcmp (encoding, "koi8-r")) + input_encoding = ce_koi8r; + else if (!strcmp (encoding, "koi8-u")) + input_encoding = ce_koi8u; + else + fprintf (stderr, "warning: unhandled encoding %s\n", encoding); } @@ -112,9 +131,13 @@ new_line (void) } -static iconv_t iconv_from_latin1 = (iconv_t) 0; +static iconv_t iconv_from_latin1; static iconv_t iconv_from_latin2; +static iconv_t iconv_from_latin15; static iconv_t iconv_from_shiftjis; +static iconv_t iconv_from_koi8u; +static iconv_t iconv_from_koi8r; +static iconv_t iconv_validate_utf8; /* Run iconv using text buffer as output buffer. */ size_t @@ -141,70 +164,44 @@ text_buffer_iconv (TEXT *buf, iconv_t iconv_state, } - -/* Return conversion of S according to ENCODING. This function frees S. */ +/* Return conversion of S according to input_encoding. This function + frees S. */ static char * -convert_to_utf8 (char *s, char *encoding) +convert_to_utf8 (char *s) { - iconv_t our_iconv; + iconv_t our_iconv = (iconv_t) -1; static TEXT t; ICONV_CONST char *inptr; size_t bytes_left; size_t iconv_ret; enum character_encoding enc; /* Convert from @documentencoding to UTF-8. - It might be possible not to convert to UTF-8 and use an 8-bit encoding + It might be possible not to convert to UTF-8 and use an 8-bit encoding throughout, but then we'd have to not set the UTF-8 flag on the Perl strings in api.c. If multiple character encodings were used in a single file, then we'd have to keep track of which strings needed the UTF-8 flag and which didn't. */ - /* Could and check for malformed input: see - <http://savannah.gnu.org/bugs/?42896>. */ - + /* Initialize conversions for the first time. */ + if (iconv_validate_utf8 == (iconv_t) 0) + iconv_validate_utf8 = iconv_open ("UTF-8", "UTF-8"); if (iconv_from_latin1 == (iconv_t) 0) - { - /* Initialize the conversion for the first time. */ - iconv_from_latin1 = iconv_open ("UTF-8", "ISO-8859-1"); - if (iconv_from_latin1 == (iconv_t) -1) - { - abort (); - - /* big trouble. if we do return it unconverted, we will have to - remember not to set the UTF-8 flags on the Perl strings, otherwise - Perl will choke. */ - return s; - } - } + iconv_from_latin1 = iconv_open ("UTF-8", "ISO-8859-1"); if (iconv_from_latin2 == (iconv_t) 0) - { - /* Initialize the conversion for the first time. */ - iconv_from_latin2 = iconv_open ("UTF-8", "ISO-8859-2"); - if (iconv_from_latin2 == (iconv_t) -1) - iconv_from_latin2 = iconv_from_latin1; - } + iconv_from_latin2 = iconv_open ("UTF-8", "ISO-8859-2"); + if (iconv_from_latin15 == (iconv_t) 0) + iconv_from_latin15 = iconv_open ("UTF-8", "ISO-8859-15"); if (iconv_from_shiftjis == (iconv_t) 0) - { - /* Initialize the conversion for the first time. */ - iconv_from_shiftjis = iconv_open ("UTF-8", "SHIFT-JIS"); - if (iconv_from_shiftjis == (iconv_t) -1) - iconv_from_shiftjis = iconv_from_latin1; - } - - enc = ce_latin1; - if (!encoding) - ; - else if (!strcmp (encoding, "utf-8")) - enc = ce_utf8; - else if (!strcmp (encoding, "iso-8859-2")) - enc = ce_latin2; - else if (!strcmp (encoding, "shift_jis")) - enc = ce_shiftjis; + iconv_from_shiftjis = iconv_open ("UTF-8", "SHIFT-JIS"); + if (iconv_from_koi8r == (iconv_t) 0) + iconv_from_koi8r = iconv_open ("UTF-8", "KOI8-R"); + if (iconv_from_koi8u == (iconv_t) 0) + iconv_from_koi8u = iconv_open ("UTF-8", "KOI8-U"); - switch (enc) + switch (input_encoding) { case ce_utf8: - return s; /* no conversion required. */ + our_iconv = iconv_validate_utf8; break; case ce_latin1: our_iconv = iconv_from_latin1; @@ -212,9 +209,26 @@ convert_to_utf8 (char *s, char *encoding) case ce_latin2: our_iconv = iconv_from_latin2; break; + case ce_latin15: + our_iconv = iconv_from_latin15; + break; case ce_shiftjis: our_iconv = iconv_from_shiftjis; break; + case ce_koi8r: + our_iconv = iconv_from_koi8r; + break; + case ce_koi8u: + our_iconv = iconv_from_koi8u; + break; + } + + if (our_iconv == (iconv_t) -1) + { + /* In case the converter couldn't be initialised. + Danger: this will cause problems if the input is not in UTF-8 as + the Perl strings that are created are flagged as being UTF-8. */ + return s; } t.end = 0; @@ -236,20 +250,25 @@ convert_to_utf8 (char *s, char *encoding) /* Success: all of input converted. */ break; + if (bytes_left == 0) + break; + switch (errno) { case E2BIG: text_alloc (&t, t.space + 20); break; + case EILSEQ: default: - abort (); + fprintf(stderr, "%s:%d: encoding error at byte 0x%2x\n", + line_nr.file_name, line_nr.line_nr, *(unsigned char *)inptr); + inptr++; bytes_left--; break; } } free (s); t.text[t.end] = '\0'; - //fprintf (stderr, "CONVERTED STRING IS <<%s>>", t.text); return strdup (t.text); } @@ -310,7 +329,7 @@ next_text (void) return new; break; - case IN_file: // 1911 + case IN_file: input_file = input_stack[input_number - 1].file; status = getline (&line, &n, input_file); if (status != -1) @@ -333,12 +352,12 @@ next_text (void) i->line_nr.line_nr++; line_nr = i->line_nr; - return convert_to_utf8 (line, input_encoding); + return convert_to_utf8 (line); } free (line); line = 0; break; default: - abort (); + fatal ("unknown input source type"); } /* Top input source failed. Pop it and try the next one. */ @@ -369,7 +388,7 @@ input_push (char *text, char *macro, char *filename, int line_number) input_space++; input_space *= 1.5; input_stack = realloc (input_stack, input_space * sizeof (INPUT)); if (!input_stack) - abort (); + fatal ("realloc failed"); } input_stack[input_number].type = IN_text; @@ -405,7 +424,7 @@ save_string (char *string) small_strings = realloc (small_strings, small_strings_space * sizeof (char *)); if (!small_strings) - abort (); + fatal ("realloc failed"); } small_strings[small_strings_num++] = ret; } @@ -501,6 +520,7 @@ add_include_directory (char *filename) filename[len - 1] = '\0'; } +/* Return value to be freed by caller. */ char * locate_include_file (char *filename) { @@ -519,7 +539,7 @@ locate_include_file (char *filename) { status = stat (filename, &dummy); if (status == 0) - return filename; + return strdup (filename); } else { @@ -550,7 +570,7 @@ input_push_file (char *filename) { input_stack = realloc (input_stack, (input_space += 5) * sizeof (INPUT)); if (!input_stack) - abort (); + fatal ("realloc failed"); } /* Strip off a leading directory path. */ diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.h index 4ac660375b..6a906c0b47 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/input.h @@ -1,4 +1,8 @@ /* input.h - declarations for input.c */ +#ifndef INPUT_H +#define INPUT_H + +#include "tree_types.h" char *new_line (void); char *next_text (void); @@ -22,3 +26,4 @@ void free_small_strings (void); extern LINE_NR line_nr; extern int input_number; +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.c index 0b7f5f0375..8d3090f1a6 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.c @@ -37,7 +37,7 @@ register_label (ELEMENT *current, ELEMENT *label) labels_space *= 1.5; labels_list = realloc (labels_list, labels_space * sizeof (LABEL)); if (!labels_list) - abort (); + fatal ("realloc failed"); } labels_list[labels_number++].target = current; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.h index 7877987466..66624c56b0 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/labels.h @@ -1,5 +1,7 @@ /* labels.h - declarations for labels.c */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +#ifndef LABELS_H +#define LABELS_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "tree_types.h" + /* Information about a possible target of a cross-reference, often a node. */ typedef struct { /* Pointer to the element for the command defining this label, usually a @@ -35,3 +39,4 @@ extern size_t internal_xref_space; void remember_internal_xref (ELEMENT *element); void reset_internal_xrefs (void); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.c index 9c26ccf8d1..292e4cc08a 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.c @@ -51,7 +51,7 @@ new_macro (char *name, ELEMENT *macro) macro_list = realloc (macro_list, (macro_space += 5) * sizeof (MACRO)); if (!macro_list) - abort (); + fatal ("realloc failed"); } new = add_texinfo_command (name); m = ¯o_list[macro_number]; @@ -90,8 +90,8 @@ parse_macro_command_line (enum command_id cmd, char **line_inout, macro->line_nr = line_nr; add_extra_string (macro, "arg_line", strdup (line)); - /* TODO: This extra value isn't used much, so is a candidate for - simplification. */ + /* Note this extra value isn't used much, so it might be possible + to get rid of it. */ line += strspn (line, whitespace_chars); name = read_command_name (&line); @@ -143,7 +143,7 @@ parse_macro_command_line (enum command_id cmd, char **line_inout, if (!*q) { /* End of string reached before closing brace. */ - abort (); + goto check_trailing; } /* Disregard trailing whitespace. */ @@ -207,7 +207,6 @@ check_trailing: } //line += strlen (line); /* Discard rest of line. */ -funexit: *line_inout = line; return macro; } @@ -331,7 +330,7 @@ expand_macro_arguments (ELEMENT *macro, char **line_inout, enum command_id cmd) (1+(arg_space += 5)) * sizeof (char *)); /* Include space for terminating null element. */ if (!arg_list) - abort (); + fatal ("realloc failed"); } if (arg.space > 0) arg_list[arg_number++] = arg.text; @@ -418,9 +417,8 @@ expand_macro_body (MACRO *macro_record, char *arguments[], TEXT *expanded) bs = strchr (ptext, '\\'); if (!bs) { - // TODO: error - malformed + /* malformed input - unpaired backslash */ return; - abort (); } *bs = '\0'; @@ -507,7 +505,7 @@ handle_macro (ELEMENT *current, char **line_inout, enum command_id cmd) macro_record = lookup_macro (cmd); if (!macro_record) - abort (); + fatal ("no macro record"); macro = macro_record->element; /* Get number of args. - 1 for the macro name. */ @@ -542,7 +540,7 @@ handle_macro (ELEMENT *current, char **line_inout, enum command_id cmd) if (!line) line = ""; } - line += strspn (line, whitespace_chars); + line += strspn (line, whitespace_chars_except_newline); arguments = malloc (sizeof (char *) * 2); arguments[0] = strdup (line); @@ -663,14 +661,13 @@ store_value (char *name, char *value) v->value = strdup (value); } -/* Clear the value the name of which is LEN bytes at NAME */ void -clear_value (char *name, int len) +clear_value (char *name) { int i; for (i = 0; i < value_number; i++) { - if (!strncmp (value_list[i].name, name, len) && !value_list[i].name[len]) + if (!strcmp (value_list[i].name, name)) { value_list[i].name[0] = '\0'; value_list[i].value[0] = '\0'; @@ -678,14 +675,13 @@ clear_value (char *name, int len) } } -/* Look for a value the name of which is LEN bytes at NAME */ char * -fetch_value (char *name, int len) +fetch_value (char *name) { int i; for (i = 0; i < value_number; i++) { - if (!strncmp (value_list[i].name, name, len) && !value_list[i].name[len]) + if (!strcmp (value_list[i].name, name)) return value_list[i].value; } diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.h index 9fb4d09a6d..784a3ca497 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/macro.h @@ -1,5 +1,7 @@ /* macro.h - declarations for macro.c */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +#ifndef MACRO_H +#define MACRO_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,6 +16,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "tree_types.h" + typedef struct { enum command_id cmd; char *begin; @@ -37,8 +41,9 @@ MACRO *lookup_macro (enum command_id cmd); void wipe_macros (void); void store_value (char *name, char *value); -char *fetch_value (char *name, int len); -void clear_value (char *name, int len); +char *fetch_value (char *name); +void clear_value (char *name); INFO_ENCLOSE *lookup_infoenclose (enum command_id cmd); void add_infoenclose (enum command_id cmd, char *begin, char *end); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/menus.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/menus.c index d2e0ce1cc7..dce4b749c4 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/menus.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/menus.c @@ -159,7 +159,7 @@ handle_menu (ELEMENT **current_inout, char **line_inout) } if (pop_context () != ct_preformatted) - abort (); /* bug */ + fatal ("preformatted context expected"); menu_entry = new_element (ET_menu_entry); leading_text = new_element (ET_menu_entry_leading_text); diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.c index 8edc8a0e50..65498f0267 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.c @@ -24,8 +24,6 @@ #include "input.h" -/* Utility functions */ - const char *whitespace_chars = " \t\f\r\n"; const char *digit_chars = "0123456789"; @@ -62,6 +60,27 @@ read_command_name (char **ptr) return ret; } +/* Read a name used for @set and @value. */ +char * +read_flag_name (char **ptr) +{ + char *p = *ptr, *q; + char *ret = 0; + + q = p; + if (!isalnum (*q) && *q != '-' && *q != '_') + return 0; /* Invalid. */ + + while (!strchr (whitespace_chars, *q) + && !strchr ("{\\}~`^+\"<>|@", *q)) + q++; + ret = strndup (p, q - p); + p = q; + + *ptr = p; + return ret; +} + char * element_type_name (ELEMENT *e) { @@ -92,7 +111,7 @@ push_conditional_stack (enum command_id cond) (conditional_space += 5) * sizeof (enum command_id)); if (!conditional_stack) - abort (); + fatal ("realloc failed"); } conditional_stack[conditional_number++] = cond; } @@ -127,11 +146,19 @@ set_documentlanguage (char *value) global_documentlanguage = strdup (value); } +void +set_novalidate (int value) +{ + global_info.novalidate = value; +} + /* Record the information from a command of global effect. */ int register_global_command (ELEMENT *current) { enum command_id cmd = current->cmd; + if (cmd == CM_summarycontents) + cmd = CM_shortcontents; if (command_data(cmd).flags & CF_global) { @@ -187,8 +214,6 @@ register_global_command (ELEMENT *current) { ELEMENT **where = 0; - if (cmd == CM_shortcontents) - cmd = CM_summarycontents; if (!current->line_nr.line_nr) current->line_nr = line_nr; switch (cmd) @@ -210,8 +235,6 @@ register_global_command (ELEMENT *current) GLOBAL_UNIQUE_CASE(titlepage); GLOBAL_UNIQUE_CASE(top); GLOBAL_UNIQUE_CASE(documentdescription); - GLOBAL_UNIQUE_CASE(novalidate); - GLOBAL_UNIQUE_CASE(validatemenus); GLOBAL_UNIQUE_CASE(pagesizes); GLOBAL_UNIQUE_CASE(fonttextsize); GLOBAL_UNIQUE_CASE(footnotestyle); @@ -518,7 +541,7 @@ merge_text (ELEMENT *current, char *text) { additional = malloc (leading_spaces + 1); if (!additional) - abort (); + fatal ("malloc failed"); memcpy (additional, text, leading_spaces); additional[leading_spaces] = '\0'; } @@ -860,6 +883,7 @@ check_valid_nesting (ELEMENT *current, enum command_id cmd) && outer != CM_center && outer != CM_exdent) || ((outer_flags & CF_brace) + && !(outer_flags & CF_inline) && command_data(outer).data > 0) || outer == CM_shortcaption || outer == CM_math @@ -1162,7 +1186,7 @@ superfluous_arg: ELEMENT *popped; popped = pop_element_from_contents (current); if (popped->cmd != end_cmd) - abort(); //error + fatal ("command mismatch for ignored block"); /* Ignore until end of line */ if (!strchr (line, '\n')) @@ -1485,22 +1509,17 @@ superfluous_arg: if (cmd == CM_value) { char *arg_start; + char *flag; line += strspn (line, whitespace_chars); if (*line != '{') goto value_invalid; line++; - if (!isalnum (*line) && *line != '-' && *line != '_') - { - line--; - goto value_invalid; - } arg_start = line; + flag = read_flag_name (&line); + if (!flag) + goto value_invalid; - line++; - line = strpbrk (line, - " \t\f\r\n" /* whitespace */ - "{\\}~^+\"<>|@"); /* other bytes that aren't allowed */ if (*line != '}') { line = arg_start - 1; @@ -1511,7 +1530,7 @@ superfluous_arg: { char *value; value_valid: - value = fetch_value (arg_start, line - arg_start); + value = fetch_value (flag); if (!value) { /* Add element for unexpanded @value. @@ -1520,14 +1539,17 @@ value_valid: in undefined values. */ ELEMENT *value_elt; - line_warn ("undefined flag: %.*s", line - arg_start, - arg_start); + line_warn ("undefined flag: %s", flag); + /* Note: In the Perl code, this warning is conditional on + in_gdt setting, but the only effect that this possibly has + is on speed, as these warnings would not be printed to the + user. */ abort_empty_line (¤t, NULL); value_elt = new_element (ET_NONE); value_elt->cmd = CM_value; - text_append_n (&value_elt->text, arg_start, - line - arg_start); + text_append (&value_elt->text, flag); + /* In the Perl code, the name of the flag is stored in the "type" field. We need to store in 'text' instead and then output it as the type in @@ -1546,6 +1568,7 @@ value_valid: line += strlen (line); retval = STILL_MORE_TO_PROCESS; } + free (flag); goto funexit; } else @@ -1563,14 +1586,7 @@ value_invalid: char *msg = 0; switch (cmd) { - case CM_setcontentsaftertitlepage: - msg = "move your @contents command if you want the " - "contents after the title page"; - break; - case CM_setshortcontentsaftertitlepage: - msg = "move your @shortcontents and @contents command if " - "you want the contents after the title page"; - break; + /* messages for commands could go here. */ default: break; } @@ -1644,6 +1660,7 @@ value_invalid: if ((cmd == CM_sortas || cmd == CM_seeentry + || cmd == CM_seealso || cmd == CM_subentry) && current->contents.number > 0 && last_contents_child(current)->text.end > 0) diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.h index 55fe467a83..9cc2f240b4 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/parser.h @@ -2,7 +2,9 @@ declarations for close.c, end_line.c, debug.c, separator.c, parser.c, multitable.c, extra.c and menu.c. */ -/* Copyright 2010-2019 Free Software Foundation, Inc. +#ifndef PARSER_H +#define PARSER_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -33,6 +35,7 @@ typedef struct GLOBAL_INFO { char *input_encoding_name; char *input_perl_encoding; int sections_level; + int novalidate; ELEMENT dircategory_direntry; /* an array of elements */ /* Elements that should be unique. */ @@ -43,10 +46,6 @@ typedef struct GLOBAL_INFO { ELEMENT *top; ELEMENT *setfilename; ELEMENT *documentdescription; - ELEMENT *setcontentsaftertitlepage; - ELEMENT *setshortcontentsaftertitlepage; - ELEMENT *novalidate; - ELEMENT *validatemenus; ELEMENT *pagesizes; ELEMENT *fonttextsize; ELEMENT *footnotestyle; @@ -149,6 +148,7 @@ ELEMENT *end_preformatted (ELEMENT *current, enum command_id closed_command, enum command_id interrupting_command); char *read_command_name (char **ptr); +char *read_flag_name (char **ptr); ELEMENT *merge_text (ELEMENT *current, char *text); void start_empty_line_after_command (ELEMENT *current, char **line_inout, ELEMENT *command); @@ -157,6 +157,7 @@ int format_expanded_p (char *format); int is_end_current_command (ELEMENT *current, char **line, enum command_id *end_cmd); void set_documentlanguage (char *); +void set_novalidate (int value); char *element_type_name (ELEMENT *e); /* Return values */ @@ -203,9 +204,10 @@ void add_extra_def_info (ELEMENT *e, char *key, DEF_INFO *value); void add_extra_float_type (ELEMENT *e, char *key, EXTRA_FLOAT_TYPE *value); void add_extra_string (ELEMENT *e, char *key, char *value); void add_extra_string_dup (ELEMENT *e, char *key, char *value); -void add_extra_integer (ELEMENT *e, char *key, int value); +void add_extra_integer (ELEMENT *e, char *key, long value); KEY_PAIR *lookup_extra (ELEMENT *e, char *key); /* In menus.c */ int handle_menu (ELEMENT **current_inout, char **line_inout); ELEMENT *enter_menu_entry_node (ELEMENT *current); +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/separator.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/separator.c index ee58a2d6c5..b509de3d6a 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/separator.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/separator.c @@ -117,7 +117,7 @@ handle_open_brace (ELEMENT *current, char **line_inout) push_context (ct_math); break; default: - abort (); + fatal ("no context for command"); } { @@ -405,7 +405,7 @@ handle_close_brace (ELEMENT *current, char **line_inout) if (current->parent->cmd == CM_inlineraw) { if (ct_inlineraw != pop_context ()) - abort (); + fatal ("expected inlineraw context"); } if (current->parent->args.number == 0 || current->parent->args.list[0]->contents.number == 0) @@ -441,15 +441,15 @@ handle_close_brace (ELEMENT *current, char **line_inout) } else { - int val; - int ret = sscanf (arg, "%d", &val); + unsigned long int val; + int ret = sscanf (arg, "%lx", &val); if (ret != 1) { debug ("hex sscanf failed %s", arg); /* unknown error. possibly argument is too large for an int. */ } - if (ret != 1 || val > 0x10FFF) + if (ret != 1 || val > 0x10FFFF) { line_error ("argument for @U exceeds Unicode maximum 0x10FFFF: %s", @@ -472,21 +472,30 @@ handle_close_brace (ELEMENT *current, char **line_inout) || current->parent->cmd == CM_seeentry || current->parent->cmd == CM_seealso) { - ELEMENT *e = current->contents.list[0]; - - if (e->text.end > 0) + ELEMENT *index_elt; + if (current->parent->parent + && current->parent->parent->parent + && ((command_flags(current->parent->parent->parent) + & CF_index_entry_command) + || current->parent->parent->parent->cmd == CM_subentry)) { - ELEMENT *index_elt; - if (current->parent->parent - && current->parent->parent->parent - && ((command_flags(current->parent->parent->parent) - & CF_index_entry_command) - || current->parent->parent->parent->cmd == CM_subentry)) + index_elt = current->parent->parent->parent; + if (current->parent->cmd == CM_sortas) { - index_elt = current->parent->parent->parent; - add_extra_string_dup (index_elt, + int superfluous_arg; + char *arg = convert_to_text (current, &superfluous_arg); + if (arg && *arg) + { + add_extra_string (index_elt, command_name(current->parent->cmd), - e->text.text); + arg); + } + } + else + { + add_extra_element (index_elt, + command_name(current->parent->cmd), + current->parent); } } } @@ -518,7 +527,7 @@ handle_close_brace (ELEMENT *current, char **line_inout) add_to_element_contents (current, e); goto funexit; } - /* context brace command (e.g. @footnote) */ + /* context brace command (e.g. @footnote) when there is a paragraph inside */ else if (current_context() == ct_footnote || current_context() == ct_caption || current_context() == ct_shortcaption @@ -533,6 +542,7 @@ handle_close_brace (ELEMENT *current, char **line_inout) (void) pop_context (); debug ("CLOSING(context command)"); closed_command = current->parent->cmd; + counter_pop (&count_remaining_args); register_global_command (current->parent); current = current->parent->parent; @@ -606,7 +616,7 @@ handle_comma (ELEMENT *current, char **line_inout) || current->cmd == CM_inlineifclear) { expandp = 0; - if (fetch_value (inline_type, strlen (inline_type))) + if (fetch_value (inline_type)) expandp = 1; if (current->cmd == CM_inlineifclear) expandp = !expandp; @@ -714,7 +724,8 @@ inlinefmtifelse_done: } } - counter_dec (&count_remaining_args); + if (counter_value (&count_remaining_args, current) != COUNTER_VARIADIC) + counter_dec (&count_remaining_args); new_arg = new_element (type); add_to_element_args (current, new_arg); current = new_arg; diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.c index 3a5b70597f..224464f529 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.c @@ -1,4 +1,4 @@ -/* Copyright 2014-2019 Free Software Foundation, Inc. +/* Copyright 2014-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,7 +19,7 @@ #include <stdio.h> #include <stdarg.h> -#include "tree_types.h" +#include "errors.h" #include "text.h" /* Make sure there are LEN free bytes. */ @@ -36,7 +36,7 @@ text_alloc (TEXT *t, size_t len) t->space *= 2; t->text = realloc (t->text, t->space); if (!t->text) - abort (); + fatal ("realloc failed"); } } diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.h index 423d5559fd..11ea682aea 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/text.h @@ -1,5 +1,7 @@ /* text.h - declarations for text.c */ -/* Copyright 2014, 2015 Free Software Foundation, Inc. +#ifndef TEXT_H +#define TEXT_H +/* Copyright 2014-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -14,6 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ +typedef struct TEXT { + char *text; + size_t space; /* Allocated bytes in 'text', including terminating null. */ + size_t end; +} TEXT; + void text_init (TEXT *t); void text_append (TEXT *t, char *s); void text_append_n (TEXT *t, char *s, size_t len); @@ -22,3 +30,4 @@ void text_alloc (TEXT *t, size_t len); void text_reset (TEXT *t); #define text_base(t) ((t)->space ? (t)->text : (char *) 0) +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.c b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.c index 39859e2f79..f1b64742d9 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.c +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.c @@ -17,7 +17,7 @@ #include <stdlib.h> #include <string.h> -#include "tree_types.h" +#include "errors.h" #include "tree.h" //int element_counter; @@ -175,7 +175,7 @@ reallocate_list (ELEMENT_LIST *list) list->space += 10; list->list = realloc (list->list, list->space * sizeof (ELEMENT *)); if (!list->list) - abort (); /* Out of memory. */ + fatal ("realloc failed"); } } @@ -188,7 +188,7 @@ reallocate_list_for (int n, ELEMENT_LIST *list) list->space += n + 1; list->list = realloc (list->list, list->space * sizeof (ELEMENT *)); if (!list->list) - abort (); /* Out of memory. */ + fatal ("realloc failed"); } } @@ -234,7 +234,7 @@ insert_into_contents (ELEMENT *parent, ELEMENT *e, int where) where = list->number + where; if (where < 0 || where > list->number) - abort (); + fatal ("contents index out of bounds"); memmove (&list->list[where + 1], &list->list[where], (list->number - where) * sizeof (ELEMENT *)); @@ -254,7 +254,7 @@ insert_into_args (ELEMENT *parent, ELEMENT *e, int where) where = list->number + where; if (where < 0 || where > list->number) - abort (); + fatal ("arguments index out of bounds"); memmove (&list->list[where + 1], &list->list[where], (list->number - where) * sizeof (ELEMENT *)); @@ -292,7 +292,7 @@ remove_from_contents (ELEMENT *parent, int where) where = list->number + where; if (where < 0 || where > list->number) - abort (); + fatal ("contents index out of bounds"); removed = list->list[where]; memmove (&list->list[where], &list->list[where + 1], diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.h index 48e580a340..acb224abec 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree.h @@ -1,4 +1,8 @@ /* tree.h - declarations for tree.c */ +#ifndef TREE_H +#define TREE_H + +#include "tree_types.h" ELEMENT *new_element (enum element_type type); void add_to_element_contents (ELEMENT *parent, ELEMENT *e); @@ -22,3 +26,4 @@ void destroy_element_and_children (ELEMENT *e); #define element_args_number(e) ((e)->args.number) #define element_text(e) (text_base (&(e)->text)) +#endif diff --git a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree_types.h b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree_types.h index 344c8aa2e2..3f9f78209f 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree_types.h +++ b/macros/texinfo/texinfo/tp/Texinfo/XS/parsetexi/tree_types.h @@ -1,5 +1,7 @@ /* tree_types.h - types for the parse tree that are used in many places */ -/* Copyright 2010-2018 Free Software Foundation, Inc. +#ifndef TREE_TYPES_H +#define TREE_TYPES_H +/* Copyright 2010-2021 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,12 +20,7 @@ #include "command_ids.h" #include "element_types.h" - -typedef struct TEXT { - char *text; - size_t space; /* Allocated bytes in 'text', including terminating null. */ - size_t end; -} TEXT; +#include "text.h" enum extra_type { extra_element, @@ -131,3 +128,4 @@ typedef struct { } EXTRA_FLOAT_TYPE; +#endif |