Merge lp:~mitya57/ubuntu/saucy/texinfo/5.1.dfsg.1-4ubuntu1 into lp:ubuntu/saucy/texinfo

Proposed by Dmitry Shachnev
Status: Merged
Merged at revision: 19
Proposed branch: lp:~mitya57/ubuntu/saucy/texinfo/5.1.dfsg.1-4ubuntu1
Merge into: lp:ubuntu/saucy/texinfo
Diff against target: 9678 lines (+9582/-3)
10 files modified
.pc/applied-patches (+1/-0)
.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm (+2530/-0)
.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm (+6996/-0)
debian/changelog (+16/-0)
debian/control (+2/-1)
debian/patches/maybe-upstream-fix-itemize-start (+31/-0)
debian/patches/series (+1/-0)
debian/rules (+2/-0)
tp/Texinfo/Common.pm (+1/-1)
tp/Texinfo/Parser.pm (+2/-1)
To merge this branch: bzr merge lp:~mitya57/ubuntu/saucy/texinfo/5.1.dfsg.1-4ubuntu1
Reviewer Review Type Date Requested Status
Daniel Holbach (community) Approve
Ubuntu branches Pending
Review via email: mp+174617@code.launchpad.net

Description of the change

A trivial merge from Debian, which fixes a "Severity: serious" bug.

To post a comment you must log in.
Revision history for this message
Daniel Holbach (dholbach) wrote :

Thanks. Uploaded.

review: Approve
Revision history for this message
Dmitry Shachnev (mitya57) wrote :

Thanks Daniel, marking as merged.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2013-01-20 20:25:10 +0000
3+++ .pc/applied-patches 2013-07-14 15:17:24 +0000
4@@ -3,3 +3,4 @@
5 numerical-signal-names
6 info_universal_argument
7 dont_build_info
8+maybe-upstream-fix-itemize-start
9
10=== added directory '.pc/maybe-upstream-fix-itemize-start'
11=== added directory '.pc/maybe-upstream-fix-itemize-start/tp'
12=== added directory '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo'
13=== added file '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm'
14--- .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm 1970-01-01 00:00:00 +0000
15+++ .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm 2013-07-14 15:17:24 +0000
16@@ -0,0 +1,2530 @@
17+# Common.pm: definition of commands. Common code of other Texinfo modules.
18+#
19+# Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
20+#
21+# This program is free software; you can redistribute it and/or modify
22+# it under the terms of the GNU General Public License as published by
23+# the Free Software Foundation; either version 3 of the License,
24+# or (at your option) any later version.
25+#
26+# This program is distributed in the hope that it will be useful,
27+# but WITHOUT ANY WARRANTY; without even the implied warranty of
28+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+# GNU General Public License for more details.
30+#
31+# You should have received a copy of the GNU General Public License
32+# along with this program. If not, see <http://www.gnu.org/licenses/>.
33+#
34+# Original author: Patrice Dumas <pertusus@free.fr>
35+# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
36+
37+package Texinfo::Common;
38+
39+use strict;
40+
41+# for unicode/layer support in binmode
42+use 5.006;
43+
44+# to determine the null file
45+use Config;
46+use File::Spec;
47+
48+use Texinfo::Documentlanguages;
49+
50+# debugging
51+use Carp qw(cluck);
52+
53+require Exporter;
54+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
55+@ISA = qw(Exporter);
56+
57+# Items to export into callers namespace by default. Note: do not export
58+# names by default without a very good reason. Use EXPORT_OK instead.
59+# Do not simply export all your public functions/methods/constants.
60+
61+# This allows declaration use Texinfo::Covert::Text ':all';
62+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
63+# will save memory.
64+%EXPORT_TAGS = ( 'all' => [ qw(
65+expand_verbatiminclude
66+definition_category
67+expand_today
68+numbered_heading
69+trim_spaces_comment_from_content
70+float_name_caption
71+normalize_top_node_name
72+protect_comma_in_tree
73+protect_first_parenthesis
74+protect_hashchar_at_line_beginning
75+protect_colon_in_tree
76+protect_node_after_label_in_tree
77+valid_tree_transformation
78+move_index_entries_after_items_in_tree
79+) ] );
80+
81+@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
82+
83+@EXPORT = qw(
84+);
85+
86+$VERSION = '5.0';
87+
88+# i18n
89+sub N__($)
90+{
91+ return $_[0];
92+}
93+
94+# determine the null devices
95+my $default_null_device = File::Spec->devnull();
96+our %null_device_file = (
97+ $default_null_device => 1
98+);
99+# special case, djgpp recognizes both null devices
100+if ($Config{osname} eq 'dos' and $Config{osvers} eq 'djgpp') {
101+ $null_device_file{'/dev/null'} = 1;
102+ $null_device_file{'NUL'} = 1;
103+}
104+
105+# these are the default values for the parser state that may be
106+# initialized to values given by the user.
107+# They are defined here, because they are used below and we
108+# don't want Texinfo::Common to use Texinfo::Parser.
109+our %default_parser_state_configuration = (
110+ # this is the initial context. It is put at the bottom of the
111+ # 'context_stack'. It is not clear if this is really useful to be
112+ # able to customize that value.
113+ 'context' => '_root',
114+ 'expanded_formats' => [],
115+ 'gettext' => sub {return $_[0];},
116+ 'pgettext' => sub {return $_[1];},
117+ 'include_directories' => [ '.' ],
118+ # these are the user-added indices. May be an array reference on names
119+ # or an hash reference in the same format than %index_names below
120+ 'indices' => [],
121+ # the following are dynamically modified during the document parsing.
122+ 'aliases' => {}, # key is a command name value is the alias
123+ 'clickstyle' => 'arrow',
124+ 'documentlanguage' => undef,
125+ # Current documentlanguage set by
126+ # @documentlanguage
127+ 'explained_commands' => {}, # the key is a command name, either acronym
128+ # or abbr, the value is a hash. The key hash
129+ # is a normalized first argument of the
130+ # corresponding command, the value is the
131+ # contents array of the previous command with
132+ # this first arg and a second arg.
133+ 'kbdinputstyle' => 'distinct',
134+ 'labels' => {}, # keys are normalized label names, as described
135+ # in the `HTML Xref' node. Value should be
136+ # a node/anchor or float in the tree.
137+ 'macros' => {}, # the key is the user-defined macro name. The
138+ # value is the reference on a macro element
139+ # as obtained by parsing the @macro
140+ 'merged_indices' => {}, # the key is merged in the value
141+ 'novalidate' => 0, # same as setting @novalidate.
142+ 'sections_level' => 0, # modified by raise/lowersections
143+ 'values' => {'txicommandconditionals' => 1},
144+ # the key is the name, the value the @set name
145+ # argument. A Texinfo tree may also be used.
146+);
147+
148+# command-line options
149+#my @command_line_settable_at_commands = ('footnotestyle', 'novalidate',
150+# 'documentlanguage', 'paragraphindent');
151+
152+
153+# FIXME maybe this should better be set as texinfo passed to texi2any as
154+# texi2dvi --command
155+
156+# customization options
157+our %document_settable_at_commands = (
158+ 'allowcodebreaks' => 'true',
159+ 'clickstyle' => '@arrow',
160+ 'codequotebacktick' => 'off',
161+ 'codequoteundirected' => 'off',
162+ 'contents' => 0,
163+ 'deftypefnnewline' => 'off',
164+ 'documentencoding' => 'us-ascii',
165+ 'documentlanguage' => 'en',
166+ # is N ems in TeX, 0.4 in.
167+ 'exampleindent' => 5,
168+ 'firstparagraphindent' => 'none',
169+ 'frenchspacing' => 'off',
170+ 'headings' => 'on',
171+ 'kbdinputstyle' => 'distinct',
172+ 'paragraphindent' => 3,
173+ 'shortcontents' => 0,
174+ 'urefbreakstyle' => 'after',
175+ 'xrefautomaticsectiontitle' => 'off',
176+);
177+
178+# those should be unique
179+our %document_settable_unique_at_commands = (
180+ # when passed through a configuration variable, documentdescription
181+ # should be already formatted for HTML
182+ 'documentdescription' => undef,
183+ 'evenfootingmarks' => undef,
184+ 'evenheadingmarks' => undef,
185+ 'everyfootingmarks' => 'bottom',
186+ 'everyheadingmarks' => 'bottom',
187+ 'fonttextsize' => 11,
188+ 'footnotestyle' => 'end',
189+ 'novalidate' => 0,
190+ 'oddfootingmarks' => undef,
191+ 'oddheadingmarks' => undef,
192+ # FIXME not clear here.
193+ 'pagesizes' => undef,
194+ 'setchapternewpage' => 'on',
195+ 'setcontentsaftertitlepage' => 0,
196+ 'setfilename' => undef,
197+ 'setshortcontentsaftertitlepage' => 0,
198+ 'everyheading' => undef,
199+ 'everyfooting' => undef,
200+ 'evenheading' => undef,
201+ 'evenfooting' => undef,
202+ 'oddheading' => undef,
203+ 'oddfooting' => undef,
204+);
205+
206+my @command_line_settables = ('FILLCOLUMN', 'SPLIT', 'SPLIT_SIZE',
207+ 'HEADERS',
208+ 'MACRO_EXPAND', 'NUMBER_SECTIONS',
209+ 'NUMBER_FOOTNOTES', 'NODE_FILES',
210+ 'NO_WARN', 'VERBOSE',
211+ 'TRANSLITERATE_FILE_NAMES', 'ERROR_LIMIT', 'ENABLE_ENCODING',
212+ 'FORCE', 'INTERNAL_LINKS', 'OUTFILE', 'SUBDIR', 'OUT',
213+ 'SILENT', 'CASE_INSENSITIVE_FILENAMES',
214+);
215+
216+# documented in the Texinfo::Parser pod section
217+# all are lower cased in texi2any.pl
218+my @parser_options = map {uc($_)} (keys(%default_parser_state_configuration));
219+
220+my @obsolete_variables = ('TOP_HEADING_AT_BEGINNING', 'USE_SECTIONS',
221+ 'IDX_SUMMARY', 'I18N_PERL_HASH', 'USE_UNICODE', 'USE_NLS',
222+ 'USE_UP_FOR_ADJACENT_NODES', 'SEPARATE_DESCRIPTION',
223+ 'NEW_CROSSREF_STYLE', 'SHORT_REF', 'IGNORE_PREAMBLE_TEXT',
224+ 'OUT_ENCODING',
225+ 'IN_ENCODING', 'DEFAULT_ENCODING');
226+
227+my @variable_settables_not_used = ('COMPLETE_IMAGE_PATHS', 'TOC_FILE',
228+ 'SPLIT_INDEX');
229+
230+my @formats_settable = (
231+);
232+
233+my @variable_string_settables = (
234+ 'DEBUG', 'FRAMES', 'FRAMESET_DOCTYPE', 'DOCTYPE', 'TEST', 'DUMP_TEXI',
235+ 'TOP_FILE', 'SHOW_MENU', 'USE_NODES', 'TOC_LINKS', 'SHORTEXTN',
236+ 'PREFIX', 'DEF_TABLE', 'L2H', 'MONOLITHIC',
237+ 'L2H_L2H', 'L2H_SKIP', 'L2H_TMP', 'L2H_FILE', 'L2H_CLEAN',
238+ 'L2H_HTML_VERSION', 'EXTERNAL_DIR', 'USE_ISO',
239+ 'VERTICAL_HEAD_NAVIGATION', 'INLINE_CONTENTS', 'NODE_FILE_EXTENSION',
240+ 'NO_CSS', 'INLINE_CSS_STYLE', 'USE_TITLEPAGE_FOR_TITLE',
241+ 'SIMPLE_MENU', 'EXTENSION', 'INLINE_INSERTCOPYING', 'USE_NUMERIC_ENTITY',
242+ 'ENABLE_ENCODING_USE_ENTITY', 'ICONS',
243+ 'USE_UNIDECODE', 'DATE_IN_HEADER', 'OPEN_QUOTE_SYMBOL',
244+ 'CLOSE_QUOTE_SYMBOL', 'TOP_NODE_UP', 'TOP_NODE_UP_URL', 'TOP_NODE_FILE',
245+ 'TOP_NODE_FILE_TARGET', 'SHOW_TITLE', 'WORDS_IN_PAGE',
246+ 'HEADER_IN_TABLE', 'USE_ACCESSKEY', 'USE_REL_REV', 'USE_LINKS',
247+ 'OVERVIEW_LINK_TO_TOC', 'AVOID_MENU_REDUNDANCY', 'NODE_NAME_IN_MENU',
248+ 'NODE_NAME_IN_INDEX', 'NO_USE_SETFILENAME', 'USE_SETFILENAME_EXTENSION',
249+ 'COMPLEX_FORMAT_IN_TABLE',
250+ 'IGNORE_BEFORE_SETFILENAME', 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME',
251+ 'USE_NODE_TARGET',
252+ 'PROGRAM_NAME_IN_FOOTER', 'NODE_FILENAMES',
253+ 'EXTERNAL_CROSSREF_SPLIT', 'BODYTEXT',
254+ 'CSS_LINES', 'RENAMED_NODES_REDIRECTIONS', 'RENAMED_NODES_FILE',
255+ 'CPP_LINE_DIRECTIVES',
256+ 'TEXI2DVI', 'DUMP_TREE', 'MAX_MACRO_CALL_NESTING',
257+ 'INPUT_ENCODING_NAME', 'INPUT_PERL_ENCODING',
258+ 'OUTPUT_ENCODING_NAME', 'OUTPUT_PERL_ENCODING',
259+ 'PACKAGE_VERSION',
260+ 'PACKAGE_AND_VERSION', 'PACKAGE_URL', 'PACKAGE', 'PACKAGE_NAME', 'PROGRAM',
261+ 'PRE_BODY_CLOSE', 'AFTER_BODY_OPEN', 'PRE_ABOUT', 'AFTER_ABOUT',
262+ 'EXTRA_HEAD', 'DO_ABOUT',
263+ 'DEFAULT_RULE', 'BIG_RULE',
264+ 'MENU_ENTRY_COLON', 'INDEX_ENTRY_COLON', 'MENU_SYMBOL',
265+ 'MAX_HEADER_LEVEL', 'CHAPTER_HEADER_LEVEL',
266+ 'FOOTNOTE_END_HEADER_LEVEL', 'FOOTNOTE_SEPARATE_HEADER_LEVEL',
267+ 'USE_UP_NODE_FOR_ELEMENT_UP',
268+ 'BEFORE_OVERVIEW', 'AFTER_OVERVIEW',
269+ 'BEFORE_TOC_LINES', 'AFTER_TOC_LINES',
270+ 'SORT_ELEMENT_COUNT', 'SORT_ELEMENT_COUNT_WORDS',
271+ 'KEEP_TOP_EXTERNAL_REF',
272+ 'TEXI2HTML', 'IMAGE_LINK_PREFIX', 'FIX_TEXINFO',
273+ 'TREE_TRANSFORMATIONS', 'BASEFILENAME_LENGTH',
274+ 'TEXTCONTENT_COMMENT', 'XREF_USE_FLOAT_LABEL', 'XREF_USE_NODE_NAME_ARG',
275+ 'MACRO_BODY_IGNORES_LEADING_SPACE', 'CHECK_HTMLXREF',
276+ 'TEXINFO_DTD_VERSION', 'TEXINFO_COLUMN_FOR_DESCRIPTION',
277+ 'TEXINFO_OUTPUT_FORMAT',
278+);
279+# Not strings.
280+# FIXME To be documented somewhere, but where?
281+my @variable_other_settables = (
282+ 'LINKS_BUTTONS', 'TOP_BUTTONS', 'SECTION_BUTTONS', 'BUTTONS_TEXT',
283+ 'BUTTONS_ACCESSKEY', 'BUTTONS_REL', 'BUTTONS_GOTO',
284+ 'CHAPTER_FOOTER_BUTTONS', 'SECTION_FOOTER_BUTTONS',
285+ 'NODE_FOOTER_BUTTONS',
286+ 'MISC_BUTTONS', 'CHAPTER_BUTTONS', 'BUTTONS_NAME',
287+ 'BUTTONS_EXAMPLE', 'SPECIAL_ELEMENTS_NAME', 'SPECIAL_ELEMENTS_CLASS',
288+ 'ACTIVE_ICONS', 'PASSIVE_ICONS',
289+ 'CSS_FILES', 'CSS_REFS',
290+ 'GLOBAL_COMMANDS',
291+);
292+
293+my %valid_options;
294+foreach my $var (keys(%document_settable_at_commands),
295+ keys(%document_settable_unique_at_commands),
296+ @command_line_settables, @variable_string_settables,
297+ @variable_other_settables, @parser_options,
298+ @formats_settable,
299+ @obsolete_variables, @variable_settables_not_used) {
300+ $valid_options{$var} = 1;
301+}
302+
303+my %obsolete_options;
304+foreach my $var (@obsolete_variables) {
305+ $obsolete_options{$var} = 1;
306+}
307+
308+sub valid_option($)
309+{
310+ my $option = shift;
311+ return $valid_options{$option};
312+}
313+
314+sub obsolete_option($)
315+{
316+ my $option = shift;
317+ return $obsolete_options{$option};
318+}
319+
320+my %customization_variable_classes = (
321+ 'document_settable_at_commands' => [ sort(keys(%document_settable_at_commands)) ],
322+ 'document_settable_unique_at_commands' => [ sort(keys(%document_settable_unique_at_commands)) ],
323+ 'command_line_settables' => \@command_line_settables,
324+ 'variable_string_settables' => \@variable_string_settables,
325+ 'variable_other_settables' => \@variable_other_settables,
326+ 'parser_options' => \@parser_options,
327+ #'formats_settable' => \@formats_settable,
328+ 'obsolete_variables' => \@obsolete_variables,
329+ 'variable_settables_not_used' => \@variable_settables_not_used,
330+);
331+
332+my @secondary_customization_variables = (
333+ 'obsolete_variables', 'variable_settables_not_used'
334+);
335+sub _customization_variable_classes(;$)
336+{
337+ my $print_all = shift;
338+ my $result = '';
339+ foreach my $type (sort(keys(%customization_variable_classes))) {
340+ next if (!$print_all
341+ and grep {$_ eq $type} @secondary_customization_variables);
342+ foreach my $variable (@{$customization_variable_classes{$type}}) {
343+ $result .= "$variable\t$type\n";
344+ }
345+ }
346+ return $result;
347+}
348+
349+my %valid_tree_transformations;
350+foreach my $valid_transformation ('simple_menus',
351+ 'fill_gaps_in_sectioning', 'move_index_entries_after_items',
352+ 'insert_nodes_for_sectioning_commands',
353+ 'complete_tree_nodes_menus', 'regenerate_master_menu',
354+ 'indent_menu_descriptions') {
355+ $valid_tree_transformations{$valid_transformation} = 1;
356+}
357+
358+sub valid_tree_transformation ($)
359+{
360+ my $transformation = shift;
361+ return 1 if (defined($transformation)
362+ and $valid_tree_transformations{$transformation});
363+ return 0;
364+}
365+
366+our %no_brace_commands; # commands never taking braces
367+%no_brace_commands = (
368+ '*', "\n",
369+ ' ', ' ',
370+ "\t", ' ',
371+ "\n", ' ',
372+ '-', '', # hyphenation hint
373+ '|', '', # used in formatting commands @evenfooting and friends
374+ '/', '',
375+ ':', '',
376+ '!', '!',
377+ '?', '?',
378+ '.', '.',
379+ '@', '@',
380+ '}', '}',
381+ '{', '{',
382+ '\\', '\\', # should only appear in math
383+);
384+
385+
386+# commands taking a line as argument or no argument.
387+# sectioning commands and def* commands are added below.
388+# index commands are added dynamically.
389+#
390+# The values signification is:
391+# special: no value and macro expansion, all the line is used, and
392+# analysed during parsing (_parse_special_misc_command)
393+# lineraw: no value and macro expansion, the line is kept as-is, not
394+# analysed
395+# skipline: no argument, everything else on the line is skipped
396+# skipspace: no argument, following spaces are skipped.
397+# noarg: no argument
398+# text: the line is parsed as texinfo, and the argument is converted
399+# to simple text (in _end_line)
400+# line: the line is parsed as texinfo
401+# a number: the line is parsed as texinfo and the result should be plain
402+# text maybe followed by a comment; the result is analysed
403+# during parsing (_parse_line_command_args).
404+# The number is an indication of the number of arguments of
405+# the command.
406+#
407+# Beware that @item and @itemx may be like 'line' or 'skipspace' depending
408+# on the context.
409+our %misc_commands = (
410+ 'node' => 'line', # special arg
411+ 'bye' => 'skipline', # no arg
412+ 'end' => 'text',
413+ # set, clear
414+ 'set' => 'special', # special arg
415+ 'clear' => 'special', # special arg
416+ 'unmacro' => 'special',
417+ # comments
418+ 'comment' => 'lineraw',
419+ 'c' => 'lineraw',
420+ # special
421+ 'definfoenclose' => 3,
422+ 'alias' => 2,
423+ # number of arguments is not known in advance.
424+ 'columnfractions' => 1,
425+ # file names
426+ 'setfilename' => 'text',
427+ 'verbatiminclude' => 'text',
428+ 'include' => 'text',
429+
430+ 'raisesections' => 'skipline', # no arg
431+ 'lowersections' => 'skipline', # no arg
432+ 'contents' => 'skipline', # no arg
433+ 'shortcontents' => 'skipline', # no arg
434+ 'summarycontents' => 'skipline', # no arg
435+ 'insertcopying' => 'noarg', # no arg
436+ 'clickstyle' => 'special', # arg should be an @-command
437+ # more relevant in preamble
438+ 'setcontentsaftertitlepage' => 'skipline', # no arg
439+ 'setshortcontentsaftertitlepage' => 'skipline', # no arg
440+ 'documentencoding' => 'text', # or 1?
441+ 'novalidate' => 'skipline', # no arg
442+ 'dircategory' => 'line', # line. Position with regard
443+ # with direntry is significant
444+ 'pagesizes' => 'line', # can have 2 args
445+ # or one? 200mm,150mm 11.5in
446+ 'finalout' => 'skipline', # no arg
447+ 'paragraphindent' => 1, # arg none asis
448+ # or a number and forbids anything else on the line
449+ 'firstparagraphindent' => 1, # none insert
450+ 'frenchspacing' => 1, # on off
451+ 'codequoteundirected' => 1, # on off
452+ 'codequotebacktick' => 1, # on off
453+ 'xrefautomaticsectiontitle' => 1, # on off
454+ 'deftypefnnewline' => 1, # on off
455+ 'fonttextsize' => 1, # 10 11
456+ 'allowcodebreaks' => 1, # false or true
457+ 'exampleindent' => 1, # asis or a number
458+ 'footnotestyle' => 1, # end and separate, nothing else on the line
459+ 'urefbreakstyle' => 1, # after|before|none
460+ 'afourpaper' => 'skipline', # no arg
461+ 'afivepaper' => 'skipline', # no arg
462+ 'afourlatex' => 'skipline', # no arg
463+ 'afourwide' => 'skipline', # no arg
464+ 'headings' => 1, #off on single double singleafter doubleafter
465+ # interacts with setchapternewpage
466+ 'setchapternewpage' => 1, # off on odd
467+
468+ # only relevant in TeX, and special
469+ 'everyheading' => 'lineraw', # @*heading @*footing use @|
470+ 'everyfooting' => 'lineraw', # + @thispage @thissectionname
471+ 'evenheading' => 'lineraw', # @thissectionnum @thissection
472+ 'evenfooting' => 'lineraw', # @thischaptername @thischapternum
473+ 'oddheading' => 'lineraw', # @thischapter @thistitle @thisfile
474+ 'oddfooting' => 'lineraw',
475+
476+ 'smallbook' => 'skipline', # no arg
477+ 'syncodeindex' => 2, # args are index identifiers
478+ 'synindex' => 2,
479+ 'defindex' => 1, # one identifier arg
480+ 'defcodeindex' => 1, # one identifier arg
481+ 'documentlanguage' => 'text', # language code arg
482+ 'kbdinputstyle' => 1, # code example distinct
483+ 'everyheadingmarks' => 1, # top bottom
484+ 'everyfootingmarks' => 1,
485+ 'evenheadingmarks' => 1,
486+ 'oddheadingmarks' => 1,
487+ 'evenfootingmarks' => 1,
488+ 'oddfootingmarks' => 1,
489+ # not valid for info (should be in @iftex)
490+ 'cropmarks' => 'skipline', # no arg
491+
492+ # formatting
493+ 'center' => 'line',
494+ 'printindex' => 1,
495+ 'listoffloats' => 'line',
496+ # especially in titlepage
497+# 'shorttitle' => 'line',
498+ 'shorttitlepage' => 'line',
499+ 'settitle' => 'line',
500+ 'author' => 'line',
501+ 'subtitle' => 'line',
502+ 'title' => 'line',
503+ 'sp' => 1, # numerical arg
504+ 'page' => 'skipline', # no arg (pagebreak)
505+ 'need' => 1, # one numerical/real arg
506+ # formatting
507+ 'noindent' => 'skipspace', # no arg
508+ 'indent' => 'skipspace',
509+ 'exdent' => 'line',
510+ 'headitem' => 'skipspace',
511+ 'item' => 'skipspace', # or line, depending on the context
512+ 'itemx' => 'skipspace', # or line, depending on the context
513+ 'tab' => 'skipspace',
514+ # only valid in heading or footing
515+ 'thischapter' => 'noarg',
516+ 'thischaptername' => 'noarg',
517+ 'thischapternum' => 'noarg',
518+ 'thisfile' => 'noarg',
519+ 'thispage' => 'noarg',
520+ 'thistitle' => 'noarg',
521+ # not valid for info (should be in @iftex)
522+ 'vskip' => 'lineraw', # arg line in TeX
523+ # obsolete @-commands.
524+ 'refill' => 'noarg', # no arg (obsolete, to be ignored)
525+ # Remove spaces and end of lines after the
526+ # commands? If no, they can lead to empty lines
527+ 'quote-arg' => 'skipline',
528+ 'allow-recursion' => 'skipline',
529+);
530+
531+# key is index name, keys of the reference value are the prefixes.
532+# value associated with the prefix is 0 if the prefix is not a code-like
533+# prefix, 1 if it is a code-like prefix (set by defcodeindex/syncodeindex).
534+#our %index_names = (
535+# 'cp' => {'cp' => 0, 'c' => 0},
536+# 'fn' => {'fn' => 1, 'f' => 1},
537+# 'vr' => {'vr' => 1, 'v' => 1},
538+# 'ky' => {'ky' => 1, 'k' => 1},
539+# 'pg' => {'pg' => 1, 'p' => 1},
540+# 'tp' => {'tp' => 1, 't' => 1}
541+#);
542+
543+our %index_names = (
544+ 'cp' => {'prefix' => ['c'], 'in_code' => 0},
545+ 'fn' => {'prefix' => ['f'], 'in_code' => 1},
546+ 'vr' => {'prefix' => ['v'], 'in_code' => 1},
547+ 'ky' => {'prefix' => ['k'], 'in_code' => 1},
548+ 'pg' => {'prefix' => ['p'], 'in_code' => 1},
549+ 'tp' => {'prefix' => ['t'], 'in_code' => 1},
550+);
551+
552+foreach my $index(keys(%index_names)) {
553+ $index_names{$index}->{'name'} = $index;
554+ push @{$index_names{$index}->{'prefix'}}, $index;
555+}
556+
557+our %default_index_commands;
558+# all the commands are readded dynamically in the Parser.
559+foreach my $index_name (keys (%index_names)) {
560+ foreach my $index_prefix (@{$index_names{$index_name}->{'prefix'}}) {
561+ next if ($index_prefix eq $index_name);
562+ # only put the one letter versions in the hash.
563+ $misc_commands{$index_prefix.'index'} = 'line';
564+ $default_index_commands{$index_prefix.'index'} = 1;
565+ }
566+}
567+
568+# command with braces. Value is the max number of arguments.
569+our %brace_commands;
570+
571+our %letter_no_arg_commands;
572+foreach my $letter_no_arg_command ('aa','AA','ae','oe','AE','OE','o','O',
573+ 'ss','l','L','DH','dh','TH','th') {
574+ $letter_no_arg_commands{$letter_no_arg_command} = 1;
575+ $brace_commands{$letter_no_arg_command} = 0;
576+}
577+
578+foreach my $no_arg_command ('TeX','LaTeX','bullet','copyright',
579+ 'registeredsymbol','dots','enddots','equiv','error','expansion','arrow',
580+ 'minus','point','print','result','today',
581+ 'exclamdown','questiondown','pounds','ordf','ordm',
582+ 'atchar', 'lbracechar', 'rbracechar', 'backslashchar', 'hashchar', 'comma',
583+ 'euro', 'geq','leq','tie','textdegree','click',
584+ 'quotedblleft','quotedblright','quoteleft','quoteright','quotedblbase',
585+ 'quotesinglbase','guillemetleft','guillemetright','guillemotleft',
586+ 'guillemotright','guilsinglleft','guilsinglright') {
587+ $brace_commands{$no_arg_command} = 0;
588+}
589+
590+# accent commands. They may be called with and without braces.
591+our %accent_commands;
592+foreach my $accent_command ('"','~','^','`',"'",',','=',
593+ 'ringaccent','H','dotaccent','u','ubaraccent',
594+ 'udotaccent','v','ogonek','tieaccent', 'dotless') {
595+ $accent_commands{$accent_command} = 1;
596+ $brace_commands{$accent_command} = 1;
597+}
598+
599+our %style_commands;
600+foreach my $style_command ('asis','cite','clicksequence',
601+ 'dfn', 'emph',
602+ 'sc', 't', 'var',
603+ 'headitemfont', 'code', 'command', 'env', 'file', 'kbd',
604+ 'option', 'samp', 'strong') {
605+ $brace_commands{$style_command} = 1;
606+ $style_commands{$style_command} = 1;
607+}
608+
609+our %regular_font_style_commands;
610+foreach my $command ('r', 'i', 'b', 'sansserif', 'slanted') {
611+ $regular_font_style_commands{$command} = 1;
612+ $brace_commands{$command} = 1;
613+ $style_commands{$command} = 1;
614+}
615+
616+foreach my $one_arg_command (
617+ 'ctrl','dmn', 'w', 'key',
618+ 'titlefont','hyphenation','anchor','errormsg') {
619+ $brace_commands{$one_arg_command} = 1;
620+}
621+
622+our %code_style_commands;
623+foreach my $command ('code', 'command', 'env', 'file', 'kbd', 'key', 'option',
624+ 'samp', 'indicateurl', 'verb', 't') {
625+ $code_style_commands{$command} = 1;
626+ $brace_commands{$command} = 1;
627+}
628+
629+
630+# Commands that enclose full texts
631+our %context_brace_commands;
632+foreach my $context_brace_command ('footnote', 'caption', 'shortcaption', 'math') {
633+ $context_brace_commands{$context_brace_command} = $context_brace_command;
634+ $brace_commands{$context_brace_command} = 1;
635+}
636+
637+our %explained_commands;
638+foreach my $explained_command ('abbr', 'acronym') {
639+ $explained_commands{$explained_command} = 1;
640+ $brace_commands{$explained_command} = 2;
641+}
642+
643+our %inline_format_commands;
644+foreach my $inline_format_command ('inlineraw', 'inlinefmt') {
645+ $inline_format_commands{$inline_format_command} = 1;
646+ $brace_commands{$inline_format_command} = 2;
647+}
648+
649+foreach my $two_arg_command('email') {
650+ $brace_commands{$two_arg_command} = 2;
651+}
652+
653+foreach my $three_arg_command('uref','url','inforef') {
654+ $brace_commands{$three_arg_command} = 3;
655+}
656+
657+foreach my $five_arg_command('xref','ref','pxref','image') {
658+ $brace_commands{$five_arg_command} = 5;
659+}
660+
661+
662+# some classification to help converters
663+our %ref_commands;
664+foreach my $ref_command ('xref','ref','pxref','inforef') {
665+ $ref_commands{$ref_command} = 1;
666+}
667+
668+
669+our %in_heading_commands;
670+foreach my $in_heading_command ('thischapter', 'thischaptername',
671+ 'thischapternum', 'thisfile', 'thispage', 'thistitle') {
672+ $in_heading_commands{$in_heading_command} = 1;
673+}
674+
675+# commands delimiting blocks, with an @end.
676+# Value is either the number of arguments on the line separated by
677+# commas or the type of command, 'raw', 'def' or 'multitable'.
678+our %block_commands;
679+
680+# commands that have a possible content before an item
681+our %block_item_commands;
682+
683+sub gdt($)
684+{
685+ return $_[0];
686+}
687+
688+our %def_map = (
689+ # basic commands.
690+ # 'arg' and 'argtype' are for everything appearing after the other
691+ # arguments.
692+ 'deffn', [ 'category', 'name', 'arg' ],
693+ 'defvr', [ 'category', 'name' ],
694+ 'deftypefn', [ 'category', 'type', 'name', 'argtype' ],
695+ 'deftypeop', [ 'category', 'class' , 'type', 'name', 'argtype' ],
696+ 'deftypevr', [ 'category', 'type', 'name' ],
697+ 'defcv', [ 'category', 'class' , 'name' ],
698+ 'deftypecv', [ 'category', 'class' , 'type', 'name' ],
699+ 'defop', [ 'category', 'class' , 'name', 'arg' ],
700+ 'deftp', [ 'category', 'name', 'argtype' ],
701+ # shortcuts
702+ 'defun', {'deffn' => gdt('Function')},
703+ 'defmac', {'deffn' => gdt('Macro')},
704+ 'defspec', {'deffn' => '{'.gdt('Special Form').'}'},
705+ 'defvar', {'defvr' => gdt('Variable')},
706+ 'defopt', {'defvr' => '{'.gdt('User Option').'}'},
707+ 'deftypefun', {'deftypefn' => gdt('Function')},
708+ 'deftypevar', {'deftypevr' => gdt('Variable')},
709+ 'defivar', {'defcv' => '{'.gdt('Instance Variable').'}'},
710+ 'deftypeivar', {'deftypecv' => '{'.gdt('Instance Variable').'}'},
711+ 'defmethod', {'defop' => gdt('Method')},
712+ 'deftypemethod', {'deftypeop' => gdt('Method')},
713+);
714+
715+# the type of index, f: function, v: variable, t: type
716+my %index_type_def = (
717+ 'f' => ['deffn', 'deftypefn', 'deftypeop', 'defop'],
718+ 'v' => ['defvr', 'deftypevr', 'defcv', 'deftypecv' ],
719+ 't' => ['deftp']
720+);
721+
722+our %command_index_prefix;
723+
724+$command_index_prefix{'vtable'} = 'v';
725+$command_index_prefix{'ftable'} = 'f';
726+
727+foreach my $index_type (keys %index_type_def) {
728+ foreach my $def (@{$index_type_def{$index_type}}) {
729+ $command_index_prefix{$def} = $index_type;
730+ }
731+}
732+
733+our %def_commands;
734+our %def_aliases;
735+foreach my $def_command(keys %def_map) {
736+ if (ref($def_map{$def_command}) eq 'HASH') {
737+ my ($real_command) = keys (%{$def_map{$def_command}});
738+ $command_index_prefix{$def_command} = $command_index_prefix{$real_command};
739+ $def_aliases{$def_command} = $real_command;
740+ }
741+ $block_commands{$def_command} = 'def';
742+ $misc_commands{$def_command.'x'} = 'line';
743+ $def_commands{$def_command} = 1;
744+ $def_commands{$def_command.'x'} = 1;
745+ $command_index_prefix{$def_command.'x'} = $command_index_prefix{$def_command};
746+}
747+
748+#print STDERR "".Data::Dumper->Dump([\%def_aliases]);
749+#print STDERR "".Data::Dumper->Dump([\%def_prepended_content]);
750+
751+$block_commands{'multitable'} = 'multitable';
752+$block_item_commands{'multitable'} = 1;
753+
754+# block commands in which menu entry and menu comments appear
755+our %menu_commands;
756+foreach my $menu_command ('menu', 'detailmenu', 'direntry') {
757+ $menu_commands{$menu_command} = 1;
758+ $block_commands{$menu_command} = 0;
759+};
760+
761+our %align_commands;
762+foreach my $align_command('raggedright', 'flushleft', 'flushright') {
763+ $block_commands{$align_command} = 0;
764+ $align_commands{$align_command} = 1;
765+}
766+$align_commands{'center'} = 1;
767+
768+foreach my $block_command(
769+ 'cartouche', 'group', 'indentedblock', 'smallindentedblock') {
770+ $block_commands{$block_command} = 0;
771+}
772+
773+our %region_commands;
774+foreach my $block_command('titlepage', 'copying', 'documentdescription') {
775+ $block_commands{$block_command} = 0;
776+ $region_commands{$block_command} = 1;
777+}
778+
779+our %preformatted_commands;
780+our %preformatted_code_commands;
781+foreach my $preformatted_command(
782+ 'example', 'smallexample', 'lisp', 'smalllisp') {
783+ $block_commands{$preformatted_command} = 0;
784+ $preformatted_commands{$preformatted_command} = 1;
785+ $preformatted_code_commands{$preformatted_command} = 1;
786+}
787+
788+foreach my $preformatted_command(
789+ 'display', 'smalldisplay', 'format', 'smallformat') {
790+ $block_commands{$preformatted_command} = 0;
791+ $preformatted_commands{$preformatted_command} = 1;
792+}
793+
794+our %format_raw_commands;
795+foreach my $format_raw_command('html', 'tex', 'xml', 'docbook') {
796+ $block_commands{$format_raw_command} = 0;
797+ $format_raw_commands{$format_raw_command} = 1;
798+}
799+
800+our %raw_commands;
801+# macro/rmacro are special
802+foreach my $raw_command ('verbatim',
803+ 'ignore', 'macro', 'rmacro') {
804+ $block_commands{$raw_command} = 'raw';
805+ $raw_commands{$raw_command} = 1;
806+}
807+
808+our %texinfo_output_formats;
809+foreach my $command (keys(%format_raw_commands), 'info', 'plaintext') {
810+ $block_commands{'if' . $command} = 'conditional';
811+ $block_commands{'ifnot' . $command} = 'conditional';
812+ $texinfo_output_formats{$command} = $command;
813+}
814+
815+$block_commands{'ifset'} = 'conditional';
816+$block_commands{'ifclear'} = 'conditional';
817+
818+$block_commands{'ifcommanddefined'} = 'conditional';
819+$block_commands{'ifcommandnotdefined'} = 'conditional';
820+
821+# 'macro' ?
822+foreach my $block_command_one_arg('table', 'ftable', 'vtable',
823+ 'itemize', 'enumerate', 'quotation', 'smallquotation') {
824+ $block_commands{$block_command_one_arg} = 1;
825+ $block_item_commands{$block_command_one_arg} = 1
826+ unless ($block_command_one_arg =~ /quotation/);
827+}
828+
829+$block_commands{'float'} = 2;
830+
831+# commands that forces closing an opened paragraph.
832+our %close_paragraph_commands;
833+
834+foreach my $block_command (keys(%block_commands)) {
835+ $close_paragraph_commands{$block_command} = 1
836+ unless ($block_commands{$block_command} eq 'raw' or
837+ $block_commands{$block_command} eq 'conditional'
838+ or $format_raw_commands{$block_command});
839+}
840+
841+$close_paragraph_commands{'verbatim'} = 1;
842+
843+foreach my $close_paragraph_command ('titlefont', 'insertcopying', 'sp',
844+ 'verbatiminclude', 'page', 'item', 'itemx', 'tab', 'headitem',
845+ 'printindex', 'listoffloats', 'center', 'dircategory', 'contents',
846+ 'shortcontents', 'summarycontents', 'caption', 'shortcaption',
847+ 'setfilename', 'exdent') {
848+ $close_paragraph_commands{$close_paragraph_command} = 1;
849+}
850+
851+foreach my $close_paragraph_command (keys(%def_commands)) {
852+ $close_paragraph_commands{$close_paragraph_command} = 1;
853+}
854+
855+our %item_container_commands;
856+foreach my $item_container_command ('itemize', 'enumerate') {
857+ $item_container_commands{$item_container_command} = 1;
858+}
859+our %item_line_commands;
860+foreach my $item_line_command ('table', 'ftable', 'vtable') {
861+ $item_line_commands{$item_line_command} = 1;
862+}
863+
864+our %deprecated_commands = (
865+ 'ctrl' => '',
866+ 'allow-recursion' => N__('recursion is always allowed'),
867+ 'quote-arg' => N__('arguments are quoted by default'),
868+);
869+
870+# commands that should only appear at the root level and contain up to
871+# the next root command. @node and sectioning commands.
872+our %root_commands;
873+
874+our %command_structuring_level = (
875+ 'top', 0,
876+ 'chapter', 1,
877+ 'unnumbered', 1,
878+ 'chapheading', 1,
879+ 'appendix', 1,
880+ 'section', 2,
881+ 'unnumberedsec', 2,
882+ 'heading', 2,
883+ 'appendixsec', 2,
884+ 'subsection', 3,
885+ 'unnumberedsubsec', 3,
886+ 'subheading', 3,
887+ 'appendixsubsec', 3,
888+ 'subsubsection', 4,
889+ 'unnumberedsubsubsec', 4,
890+ 'subsubheading', 4,
891+ 'appendixsubsubsec', 4,
892+ );
893+
894+our %level_to_structuring_command;
895+
896+{
897+ my $sections = [ ];
898+ my $appendices = [ ];
899+ my $unnumbered = [ ];
900+ my $headings = [ ];
901+ foreach my $command (keys (%command_structuring_level)) {
902+ if ($command =~ /^appendix/) {
903+ $level_to_structuring_command{$command} = $appendices;
904+ } elsif ($command =~ /^unnumbered/ or $command eq 'top') {
905+ $level_to_structuring_command{$command} = $unnumbered;
906+ } elsif ($command =~ /section$/ or $command eq 'chapter') {
907+ $level_to_structuring_command{$command} = $sections;
908+ } else {
909+ $level_to_structuring_command{$command} = $headings;
910+ }
911+ $level_to_structuring_command{$command}->[$command_structuring_level{$command}]
912+ = $command;
913+ }
914+ $level_to_structuring_command{'appendixsection'} = $appendices;
915+ $level_to_structuring_command{'majorheading'} = $headings;
916+ $level_to_structuring_command{'centerchap'} = $unnumbered;
917+}
918+
919+
920+# out of the main hierarchy
921+$command_structuring_level{'part'} = 0;
922+# this are synonyms
923+$command_structuring_level{'appendixsection'} = 2;
924+# command_structuring_level{'majorheading'} is also 1 and not 0
925+$command_structuring_level{'majorheading'} = 1;
926+$command_structuring_level{'centerchap'} = 1;
927+
928+our %sectioning_commands;
929+
930+foreach my $sectioning_command (keys (%command_structuring_level)) {
931+ $misc_commands{$sectioning_command} = 'line';
932+ if ($sectioning_command =~ /heading/) {
933+ $close_paragraph_commands{$sectioning_command} = 1;
934+ } else {
935+ $root_commands{$sectioning_command} = 1;
936+ }
937+ $sectioning_commands{$sectioning_command} = 1;
938+}
939+
940+$root_commands{'node'} = 1;
941+
942+our %all_commands;
943+foreach my $command (
944+ keys(%Texinfo::Common::block_commands),
945+ keys(%Texinfo::Common::brace_commands),
946+ keys(%Texinfo::Common::misc_commands),
947+ keys(%Texinfo::Common::no_brace_commands),
948+ qw(value),
949+ ) {
950+ $all_commands{$command} = 1;
951+}
952+
953+our @MONTH_NAMES =
954+ (
955+ 'January', 'February', 'March', 'April', 'May',
956+ 'June', 'July', 'August', 'September', 'October',
957+ 'November', 'December'
958+ );
959+
960+sub locate_include_file($$)
961+{
962+ my $self = shift;
963+ my $text = shift;
964+ my $file;
965+
966+ my $ignore_include_directories = 0;
967+
968+ my ($volume, $directories, $filename) = File::Spec->splitpath($text);
969+ my @directories = File::Spec->splitdir($directories);
970+
971+ #print STDERR "$self $text @{$self->{'include_directories'}}\n";
972+ # If the path is absolute or begins with . or .., do not search in
973+ # include directories.
974+ if (File::Spec->file_name_is_absolute($text)) {
975+ $ignore_include_directories = 1;
976+ } else {
977+ foreach my $dir (@directories) {
978+ if ($dir eq File::Spec->updir() or $dir eq File::Spec->curdir()) {
979+ $ignore_include_directories = 1;
980+ last;
981+ } elsif ($dir ne '') {
982+ last;
983+ }
984+ }
985+ }
986+
987+ #if ($text =~ m,^(/|\./|\.\./),) {
988+ if ($ignore_include_directories) {
989+ $file = $text if (-e $text and -r $text);
990+ } else {
991+ my @dirs;
992+ if ($self) {
993+ @dirs = @{$self->{'include_directories'}};
994+ } else {
995+ # no object with directory list and not an absolute path, never succeed
996+ return undef;
997+ }
998+ foreach my $include_dir (@{$self->{'include_directories'}}) {
999+ my ($include_volume, $include_directories, $include_filename)
1000+ = File::Spec->splitpath($include_dir, 1);
1001+
1002+ my $possible_file = File::Spec->catpath($include_volume,
1003+ File::Spec->catdir(File::Spec->splitdir($include_directories),
1004+ @directories), $filename);
1005+ #$file = "$include_dir/$text" if (-e "$include_dir/$text" and -r "$include_dir/$text");
1006+ $file = "$possible_file" if (-e "$possible_file" and -r "$possible_file");
1007+ last if (defined($file));
1008+ }
1009+ }
1010+ return $file;
1011+}
1012+
1013+sub open_out($$;$)
1014+{
1015+ my $self = shift;
1016+ my $file = shift;
1017+ my $encoding = shift;
1018+
1019+ if (!defined($encoding) and $self
1020+ and defined($self->get_conf('OUTPUT_PERL_ENCODING'))) {
1021+ $encoding = $self->get_conf('OUTPUT_PERL_ENCODING');
1022+ }
1023+
1024+ if ($file eq '-') {
1025+ binmode(STDOUT, ":encoding($encoding)") if ($encoding);
1026+ if ($self) {
1027+ $self->{'unclosed_files'}->{$file} = \*STDOUT;
1028+ }
1029+ return \*STDOUT;
1030+ }
1031+ my $filehandle = do { local *FH };
1032+ if (!open ($filehandle, '>', $file)) {
1033+ return undef;
1034+ }
1035+ if ($encoding) {
1036+ if ($encoding eq 'utf8' or $encoding eq 'utf-8-strict') {
1037+ binmode($filehandle, ':utf8');
1038+ } else { # FIXME also right for shiftijs or similar encodings?
1039+ binmode($filehandle, ':bytes');
1040+ }
1041+ binmode($filehandle, ":encoding($encoding)");
1042+ }
1043+ if ($self) {
1044+ push @{$self->{'opened_files'}}, $file;
1045+ $self->{'unclosed_files'}->{$file} = $filehandle;
1046+ #print STDERR "OOOOOOO $file ".join('|',@{$self->{'opened_files'}})."\n";
1047+ #cluck;
1048+ }
1049+ return $filehandle;
1050+}
1051+
1052+sub warn_unknown_language($$) {
1053+ my $lang = shift;
1054+ my $gettext = shift;
1055+
1056+ my @messages = ();
1057+ my $lang_code = $lang;
1058+ my $region_code;
1059+
1060+ if ($lang =~ /^([a-z]+)_([A-Z]+)/) {
1061+ $lang_code = $1;
1062+ $region_code = $2;
1063+ }
1064+
1065+ if (! $Texinfo::Documentlanguages::language_codes{$lang_code}) {
1066+ push @messages, sprintf(&$gettext(N__("%s is not a valid language code")),
1067+ $lang_code);
1068+ }
1069+ if (defined($region_code)
1070+ and ! $Texinfo::Documentlanguages::region_codes{$region_code}) {
1071+ push @messages, sprintf(&$gettext(N__("%s is not a valid region code")),
1072+ $region_code);
1073+ }
1074+ return @messages;
1075+}
1076+
1077+my %possible_split = (
1078+ 'chapter' => 1,
1079+ 'section' => 1,
1080+ 'node' => 1,
1081+);
1082+
1083+sub warn_unknown_split($$) {
1084+ my $split = shift;
1085+ my $gettext = shift;
1086+
1087+ my @messages = ();
1088+ if ($split and !$possible_split{$split}) {
1089+ push @messages, sprintf(&$gettext(N__("%s is not a valid split possibility")),
1090+ $split);
1091+ }
1092+ return @messages;
1093+}
1094+
1095+# This should do the job, or at least don't do wrong if $self
1096+# is not defined, as could be the case if called from
1097+# Texinfo::Convert::Text.
1098+sub expand_verbatiminclude($$)
1099+{
1100+ my $self = shift;
1101+ my $current = shift;
1102+
1103+ return unless ($current->{'extra'} and defined($current->{'extra'}->{'text_arg'}));
1104+ my $text = $current->{'extra'}->{'text_arg'};
1105+ my $file = locate_include_file($self, $text);
1106+
1107+ my $verbatiminclude;
1108+
1109+ if (defined($file)) {
1110+ if (!open(VERBINCLUDE, $file)) {
1111+ if ($self) {
1112+ $self->line_error(sprintf($self->__("could not read %s: %s"), $file, $!),
1113+ $current->{'line_nr'});
1114+ }
1115+ } else {
1116+ if ($self and defined($self->get_conf('INPUT_PERL_ENCODING'))) {
1117+ binmode(VERBINCLUDE, ":encoding(".
1118+ $self->get_conf('INPUT_PERL_ENCODING').")");
1119+ }
1120+ $verbatiminclude = { 'cmdname' => 'verbatim',
1121+ 'parent' => $current->{'parent'},
1122+ 'extra' =>
1123+ {'text_arg' => $current->{'extra'}->{'text_arg'}} };
1124+ while (<VERBINCLUDE>) {
1125+ push @{$verbatiminclude->{'contents'}},
1126+ {'type' => 'raw', 'text' => $_ };
1127+ }
1128+ if (!close (VERBINCLUDE)) {
1129+ $self->document_warn(sprintf($self->__(
1130+ "error on closing \@verbatiminclude file %s: %s"),
1131+ $file, $!));
1132+ }
1133+ }
1134+ } elsif ($self) {
1135+ $self->line_error(sprintf($self->__("\@%s: could not find %s"),
1136+ $current->{'cmdname'}, $text), $current->{'line_nr'});
1137+ }
1138+ return $verbatiminclude;
1139+}
1140+
1141+sub definition_category($$)
1142+{
1143+ my $self = shift;
1144+ my $current = shift;
1145+
1146+ return undef if (!$current->{'extra'} or !$current->{'extra'}->{'def_args'});
1147+
1148+ my $arg_category = $current->{'extra'}->{'def_parsed_hash'}->{'category'};
1149+ my $arg_class = $current->{'extra'}->{'def_parsed_hash'}->{'class'};
1150+
1151+ return $arg_category
1152+ if (!defined($arg_class));
1153+
1154+ my $style =
1155+ $command_index_prefix{$current->{'extra'}->{'def_command'}};
1156+ if ($style eq 'f') {
1157+ if ($self) {
1158+ return $self->gdt('{category} on {class}', { 'category' => $arg_category,
1159+ 'class' => $arg_class });
1160+ } else {
1161+ return {'contents' => [$arg_category, {'text' => ' on '}, $arg_class]};
1162+ }
1163+ } elsif ($style eq 'v') {
1164+ if ($self) {
1165+ return $self->gdt('{category} of {class}', { 'category' => $arg_category,
1166+ 'class' => $arg_class });
1167+ } else {
1168+ return {'contents' => [$arg_category, {'text' => ' of '}, $arg_class]};
1169+ }
1170+ }
1171+}
1172+
1173+sub expand_today($)
1174+{
1175+ my $self = shift;
1176+ if ($self->get_conf('TEST')) {
1177+ return {'text' => 'a sunny day'};
1178+ }
1179+ my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
1180+ = localtime(time);
1181+ $year += ($year < 70) ? 2000 : 1900;
1182+ return $self->gdt('{month} {day}, {year}',
1183+ { 'month' => $self->gdt($MONTH_NAMES[$mon]),
1184+ 'day' => $mday, 'year' => $year });
1185+}
1186+
1187+sub translated_command_tree($$)
1188+{
1189+ my $self = shift;
1190+ my $cmdname = shift;
1191+ if ($self->{'translated_commands'}->{$cmdname}) {
1192+ return $self->gdt($self->{'translated_commands'}->{$cmdname});
1193+ }
1194+ return undef;
1195+}
1196+
1197+sub numbered_heading($$$;$)
1198+{
1199+ my $self = shift;
1200+ my $current = shift;
1201+ my $text = shift;
1202+ my $numbered = shift;
1203+
1204+ my $number;
1205+ if (defined($current->{'number'}) and ($numbered or !defined($numbered))) {
1206+ $number = $current->{'number'};
1207+ }
1208+
1209+ my $result;
1210+ if ($self) {
1211+ if (defined($number)) {
1212+ if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) {
1213+ $result = $self->gdt('Appendix {number} {section_title}',
1214+ {'number' => $number, 'section_title' => $text},
1215+ 'translated_text');
1216+ } else {
1217+ $result = $self->gdt('{number} {section_title}',
1218+ {'number' => $number, 'section_title' => $text},
1219+ 'translated_text');
1220+ }
1221+ } else {
1222+ $result = $text;
1223+ }
1224+ } else {
1225+ $result = $text;
1226+ $result = $number.' '.$result if (defined($number));
1227+ if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) {
1228+ $result = 'Appendix '.$result;
1229+ }
1230+ }
1231+ chomp ($result);
1232+ return $result;
1233+}
1234+
1235+sub definition_arguments_content($)
1236+{
1237+ my $root = shift;
1238+ my $result;
1239+
1240+ return undef if (!defined($root->{'extra'})
1241+ or !defined($root->{'extra'}->{'def_args'}));
1242+ my @args = @{$root->{'extra'}->{'def_args'}};
1243+ while (@args) {
1244+ last if ($args[0]->[0] ne 'spaces'
1245+ and !$root->{'extra'}->{'def_parsed_hash'}->{$args[0]->[0]});
1246+ shift @args;
1247+ }
1248+ if (@args) {
1249+ foreach my $arg (@args) {
1250+ push @$result, $arg->[1];
1251+ }
1252+ }
1253+ return $result;
1254+}
1255+
1256+# find the accent commands stack and the innermost text contents
1257+sub find_innermost_accent_contents($;$)
1258+{
1259+ my $current = shift;
1260+ my $encoding = shift;
1261+ my @accent_commands = ();
1262+ my $debug = 0;
1263+ ACCENT:
1264+ while (1) {
1265+ # the following can happen if called with a bad tree
1266+ if (!$current->{'cmdname'}
1267+ or !$accent_commands{$current->{'cmdname'}}) {
1268+ #print STDERR "BUG: Not an accent command in accent\n";
1269+ cluck "BUG: Not an accent command in accent\n";
1270+ #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n";
1271+ #print STDERR Data::Dumper->Dump([$current]);
1272+ last;
1273+ }
1274+ push @accent_commands, $current;
1275+ # A bogus accent, that may happen
1276+ if (!$current->{'args'}) {
1277+ return ([], \@accent_commands);
1278+ }
1279+ my $arg = $current->{'args'}->[0];
1280+ if (!$arg->{'contents'}) {
1281+ print STDERR "BUG: No content in accent command\n";
1282+ #print STDERR Data::Dumper->Dump([$current]);
1283+ #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n";
1284+ return ([], \@accent_commands);
1285+ }
1286+ # inside the argument of an accent
1287+ my $text_contents = [];
1288+ foreach my $content (@{$arg->{'contents'}}) {
1289+ if (!($content->{'extra'} and $content->{'extra'}->{'invalid_nesting'})
1290+ and !($content->{'cmdname'} and ($content->{'cmdname'} eq 'c'
1291+ or $content->{'cmdname'} eq 'comment'))) {
1292+ if ($content->{'cmdname'} and $accent_commands{$content->{'cmdname'}}) {
1293+ $current = $content;
1294+ next ACCENT;
1295+ } else {
1296+ push @$text_contents, $content;
1297+ }
1298+ }
1299+ }
1300+ # we go here if there was no nested accent
1301+ return ($text_contents, \@accent_commands);
1302+ }
1303+}
1304+
1305+sub trim_spaces_comment_from_content($)
1306+{
1307+ my $contents = shift;
1308+ shift @$contents
1309+ if ($contents->[0] and $contents->[0]->{'type'}
1310+ and ($contents->[0]->{'type'} eq 'empty_line_after_command'
1311+ or $contents->[0]->{'type'} eq 'empty_spaces_after_command'
1312+ or $contents->[0]->{'type'} eq 'empty_spaces_before_argument'
1313+ or $contents->[0]->{'type'} eq 'empty_space_at_end_def_bracketed'
1314+ or $contents->[0]->{'type'} eq 'empty_spaces_after_close_brace'));
1315+
1316+ while (@$contents
1317+ and (($contents->[-1]->{'cmdname'}
1318+ and ($contents->[-1]->{'cmdname'} eq 'c'
1319+ or $contents->[-1]->{'cmdname'} eq 'comment'))
1320+ or ($contents->[-1]->{'type'}
1321+ and ($contents->[-1]->{'type'} eq 'spaces_at_end'
1322+ or $contents->[-1]->{'type'} eq 'space_at_end_block_command')))) {
1323+ pop @$contents;
1324+ }
1325+}
1326+
1327+sub float_name_caption($$)
1328+{
1329+ my $self = shift;
1330+ my $root = shift;
1331+
1332+ my $caption;
1333+ if ($root->{'extra'}->{'caption'}) {
1334+ $caption = $root->{'extra'}->{'caption'};
1335+ } elsif ($root->{'extra'}->{'shortcaption'}) {
1336+ $caption = $root->{'extra'}->{'shortcaption'};
1337+ }
1338+ #if ($self->get_conf('DEBUG')) {
1339+ # my $caption_texi =
1340+ # Texinfo::Convert::Texinfo::convert({ 'contents' => $caption->{'contents'}});
1341+ # print STDERR " CAPTION: $caption_texi\n";
1342+ #}
1343+ my $type;
1344+ if ($root->{'extra'}->{'type'}->{'normalized'} ne '') {
1345+ $type = {'contents' => $root->{'extra'}->{'type'}->{'content'}};
1346+ }
1347+
1348+ my $prepended;
1349+ if ($type) {
1350+ if ($caption) {
1351+ if (defined($root->{'number'})) {
1352+ $prepended = $self->gdt('{float_type} {float_number}: ',
1353+ {'float_type' => $type,
1354+ 'float_number' => $root->{'number'}});
1355+ } else {
1356+ $prepended = $self->gdt('{float_type}: ',
1357+ {'float_type' => $type});
1358+ }
1359+ } else {
1360+ if (defined($root->{'number'})) {
1361+ $prepended = $self->gdt("{float_type} {float_number}\n",
1362+ {'float_type' => $type,
1363+ 'float_number' => $root->{'number'}});
1364+ } else {
1365+ $prepended = $self->gdt("{float_type}\n",
1366+ {'float_type' => $type});
1367+ }
1368+ }
1369+ } elsif (defined($root->{'number'})) {
1370+ if ($caption) {
1371+ $prepended = $self->gdt('{float_number}: ',
1372+ {'float_number' => $root->{'number'}});
1373+ } else {
1374+ $prepended = $self->gdt("{float_number}\n",
1375+ {'float_number' => $root->{'number'}});
1376+ }
1377+ }
1378+ return ($caption, $prepended);
1379+}
1380+
1381+# decompose a decimal number on a given base.
1382+sub _decompose_integer($$)
1383+{
1384+ my $number = shift;
1385+ my $base = shift;
1386+ my @result = ();
1387+
1388+ while ($number >= 0) {
1389+ my $factor = $number % $base;
1390+ push (@result, $factor);
1391+ $number = int(($number - $factor) / $base) - 1;
1392+ }
1393+ return @result;
1394+}
1395+
1396+sub enumerate_item_representation($$)
1397+{
1398+ my $specification = shift;
1399+ my $number = shift;
1400+
1401+ if ($specification =~ /^[0-9]$/) {
1402+ return $specification + $number -1;
1403+ }
1404+
1405+ my $result = '';
1406+ my $base_letter = ord('a');
1407+ $base_letter = ord('A') if (ucfirst($specification) eq $specification);
1408+ my @letter_ords = _decompose_integer(ord($specification) - $base_letter + $number - 1, 26);
1409+ foreach my $ord (@letter_ords) {
1410+ $result = chr($base_letter + $ord) . $result;
1411+ }
1412+ return $result;
1413+}
1414+
1415+
1416+our %htmlxref_entries = (
1417+ 'node' => [ 'node', 'section', 'chapter', 'mono' ],
1418+ 'section' => [ 'section', 'chapter','node', 'mono' ],
1419+ 'chapter' => [ 'chapter', 'section', 'node', 'mono' ],
1420+ 'mono' => [ 'mono', 'chapter', 'section', 'node' ],
1421+);
1422+
1423+sub parse_htmlxref_files($$)
1424+{
1425+ my $self = shift;
1426+ my $files = shift;
1427+ my $htmlxref;
1428+
1429+ foreach my $file (@$files) {
1430+ print STDERR "html refs config file: $file\n" if ($self->get_conf('DEBUG'));
1431+ unless (open (HTMLXREF, $file)) {
1432+ $self->document_warn(
1433+ sprintf($self->__("could not open html refs config file %s: %s"),
1434+ $file, $!));
1435+ next;
1436+ }
1437+ my $line_nr = 0;
1438+ my %variables;
1439+ while (my $hline = <HTMLXREF>) {
1440+ my $line = $hline;
1441+ $line_nr++;
1442+ next if $hline =~ /^\s*#/;
1443+ #$hline =~ s/[#]\s.*//;
1444+ $hline =~ s/^\s*//;
1445+ next if $hline =~ /^\s*$/;
1446+ chomp ($hline);
1447+ if ($hline =~ s/^\s*(\w+)\s*=\s*//) {
1448+ # handle variables
1449+ my $var = $1;
1450+ my $re = join '|', map { quotemeta $_ } keys %variables;
1451+ $hline =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
1452+ : "\${$1}"/ge;
1453+ $variables{$var} = $hline;
1454+ next;
1455+ }
1456+ my @htmlxref = split /\s+/, $hline;
1457+ my $manual = shift @htmlxref;
1458+ my $split_or_mono = shift @htmlxref;
1459+ #print STDERR "$split_or_mono $Texi2HTML::Config::htmlxref_entries{$split_or_mono} $line_nr\n";
1460+ if (!defined($split_or_mono)) {
1461+ $self->file_line_warn($self->__("missing type"), $file, $line_nr);
1462+ next;
1463+ } elsif (!defined($htmlxref_entries{$split_or_mono})) {
1464+ $self->file_line_warn(sprintf($self->__("unrecognized type: %s"),
1465+ $split_or_mono), $file, $line_nr);
1466+ next;
1467+ }
1468+ my $href = shift @htmlxref;
1469+ next if (exists($htmlxref->{$manual}->{$split_or_mono}));
1470+
1471+ if (defined($href)) { # substitute 'variables'
1472+ my $re = join '|', map { quotemeta $_ } keys %variables;
1473+ $href =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
1474+ : "\${$1}"/ge;
1475+ $href =~ s/\/*$// if ($split_or_mono ne 'mono');
1476+ }
1477+ $htmlxref->{$manual}->{$split_or_mono} = $href;
1478+ }
1479+ if (!close (HTMLXREF)) {
1480+ $self->document_warn(sprintf($self->__(
1481+ "error on closing html refs config file %s: %s"),
1482+ $file, $!));
1483+ }
1484+ }
1485+ return $htmlxref;
1486+}
1487+
1488+sub parse_renamed_nodes_file($$;$$)
1489+{
1490+ my $self = shift;
1491+ my $renamed_nodes_file = shift;
1492+ # if not given they are automatically created
1493+ my $renamed_nodes = shift;
1494+ my $renamed_nodes_lines = shift;
1495+
1496+ if (open(RENAMEDFILE, "<$renamed_nodes_file")) {
1497+ if ($self->get_conf('INPUT_PERL_ENCODING')) {
1498+ binmode(RENAMEDFILE, ":encoding(".
1499+ $self->get_conf('INPUT_PERL_ENCODING').")");
1500+ }
1501+ my $renamed_nodes_line_nr = 0;
1502+ my @old_names = ();
1503+ while (<RENAMEDFILE>) {
1504+ $renamed_nodes_line_nr++;
1505+ next unless (/\S/);
1506+ next if (/^\s*\@c\b/);
1507+ if (s/^\s*\@\@\{\}\s+(\S)/$1/) {
1508+ chomp;
1509+ if (scalar(@old_names)) {
1510+ foreach my $old_node_name (@old_names) {
1511+ $renamed_nodes->{$old_node_name} = $_;
1512+ }
1513+ $renamed_nodes_lines->{$_} = $renamed_nodes_line_nr;
1514+ @old_names = ();
1515+ } else {
1516+ $self->file_line_warn($self->__("no node to be renamed"),
1517+ $renamed_nodes_file, $renamed_nodes_line_nr);
1518+ }
1519+ } else {
1520+ chomp;
1521+ s/^\s*//;
1522+ $renamed_nodes_lines->{$_} = $renamed_nodes_line_nr;
1523+ push @old_names, $_;
1524+ }
1525+ }
1526+ if (scalar(@old_names)) {
1527+ $self->file_line_warn($self->__("nodes without a new name at the end of file"),
1528+ $renamed_nodes_file, $renamed_nodes_line_nr);
1529+ }
1530+ if (!close(RENAMEDFILE)) {
1531+ $self->document_warn(sprintf($self->__p(
1532+ "see HTML Xref Link Preservation in the Texinfo manual for context",
1533+ "error on closing node-renaming configuration file %s: %s"),
1534+ $renamed_nodes_file, $!));
1535+ }
1536+ } else {
1537+ $self->document_warn(sprintf($self->__("could not open %s: %s"),
1538+ $renamed_nodes_file, $!));
1539+ }
1540+ return ($renamed_nodes, $renamed_nodes_lines);
1541+}
1542+
1543+sub collect_renamed_nodes($$;$$)
1544+{
1545+ my $self = shift;
1546+ my $basename = shift;
1547+ my $renamed_nodes = shift;
1548+ my $renamed_nodes_lines = shift;
1549+
1550+ my $renamed_nodes_file;
1551+ if (defined($self->get_conf('RENAMED_NODES_FILE'))) {
1552+ $renamed_nodes_file = $self->get_conf('RENAMED_NODES_FILE');
1553+ } elsif (-f $basename . '-noderename.cnf') {
1554+ $renamed_nodes_file = $basename . '-noderename.cnf';
1555+ }
1556+ if (defined($renamed_nodes_file)) {
1557+ my ($renamed_nodes, $renamed_nodes_lines)
1558+ = parse_renamed_nodes_file($self, $renamed_nodes_file, $renamed_nodes,
1559+ $renamed_nodes_lines);
1560+ return ($renamed_nodes, $renamed_nodes_lines, $renamed_nodes_file);
1561+ }
1562+ return (undef, undef, undef);
1563+}
1564+
1565+sub normalize_top_node_name($)
1566+{
1567+ my $node = shift;
1568+ if ($node =~ /^top$/i) {
1569+ return 'Top';
1570+ }
1571+ return $node;
1572+}
1573+
1574+sub _convert_text_options($)
1575+{
1576+ my $self = shift;
1577+ my %options;
1578+ if ($self->get_conf('ENABLE_ENCODING')) {
1579+ if ($self->get_conf('OUTPUT_ENCODING_NAME')) {
1580+ $options{'enabled_encoding'} = $self->get_conf('OUTPUT_ENCODING_NAME');
1581+ } elsif ($self->get_conf('INPUT_ENCODING_NAME')) {
1582+ $options{'enabled_encoding'} = $self->get_conf('INPUT_ENCODING_NAME');
1583+ }
1584+ }
1585+ $options{'TEST'} = 1 if ($self->get_conf('TEST'));
1586+ $options{'NUMBER_SECTIONS'} = $self->get_conf('NUMBER_SECTIONS');
1587+ $options{'converter'} = $self;
1588+ $options{'expanded_formats_hash'} = $self->{'expanded_formats_hash'};
1589+ return %options;
1590+}
1591+
1592+sub count_bytes($$;$)
1593+{
1594+ my $self = shift;
1595+ my $string = shift;
1596+ my $encoding = shift;
1597+
1598+ if (!defined($encoding) and $self and $self->get_conf('OUTPUT_PERL_ENCODING')) {
1599+ $encoding = $self->get_conf('OUTPUT_PERL_ENCODING');
1600+ }
1601+
1602+ if ($encoding and $encoding ne 'ascii') {
1603+ return length(Encode::encode($encoding, $string));
1604+ } else {
1605+ return length($string);
1606+ }
1607+ # FIXME is the following required for correct count of end of lines?
1608+ #if ($encoding) {
1609+ # return length(Encode::encode($encoding, $string));
1610+ #} else {
1611+ # return length(Encode::encode('ascii', $string));
1612+ #}
1613+}
1614+
1615+# TODO
1616+# also recurse into
1617+# extra->misc_args, extra->args_index
1618+# extra->index_entry extra->type
1619+#
1620+# extra that should point to other elements:
1621+# command_as_argument
1622+# @block_command_line_contents @brace_command_contents @misc_content end_command
1623+# associated_section part_associated_section associated_node associated_part
1624+# @prototypes @columnfractions titlepage quotation @author command
1625+# menu_entry_description menu_entry_name
1626+#
1627+# should point to other elements, or be copied. And some should be recursed
1628+# into too.
1629+# extra->type->content
1630+# extra->nodes_manuals->[]
1631+# extra->node_content
1632+# extra->node_argument
1633+# extra->explanation_contents
1634+# extra->menu_entry_node
1635+# extra->def_arg
1636+
1637+
1638+sub _copy_tree($$$);
1639+sub _copy_tree($$$)
1640+{
1641+ my $current = shift;
1642+ my $parent = shift;
1643+ my $reference_associations = shift;
1644+ my $new = {};
1645+ $reference_associations->{$current} = $new;
1646+ $new->{'parent'} = $parent if ($parent);
1647+ foreach my $key ('type', 'cmdname', 'text') {
1648+ $new->{$key} = $current->{$key} if (exists($current->{$key}));
1649+ }
1650+ foreach my $key ('args', 'contents') {
1651+ if ($current->{$key}) {
1652+ if (ref($current->{$key}) ne 'ARRAY') {
1653+ my $command_or_type = '';
1654+ if ($new->{'cmdname'}) {
1655+ $command_or_type = '@'.$new->{'cmdname'};
1656+ } elsif ($new->{'type'}) {
1657+ $command_or_type = $new->{'type'};
1658+ }
1659+ print STDERR "Not an array [$command_or_type] $key ".ref($current->{$key})."\n";
1660+ }
1661+ $new->{$key} = [];
1662+ $reference_associations->{$current->{$key}} = $new->{$key};
1663+ foreach my $child (@{$current->{$key}}) {
1664+ push @{$new->{$key}}, _copy_tree($child, $new, $reference_associations);
1665+ }
1666+ }
1667+ }
1668+ if ($current->{'extra'}) {
1669+ $new->{'extra'} = {};
1670+ foreach my $key (keys %{$current->{'extra'}}) {
1671+ if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable'
1672+ and $key eq 'prototypes') {
1673+ $new->{'extra'}->{$key} = [];
1674+ $reference_associations->{$current->{'extra'}->{$key}} = $new->{$key};
1675+ foreach my $child (@{$current->{'extra'}->{$key}}) {
1676+ push @{$new->{'extra'}->{$key}},
1677+ _copy_tree($child, $new, $reference_associations);
1678+ }
1679+ } elsif (!ref($current->{'extra'}->{$key})) {
1680+ $new->{'extra'}->{$key} = $current->{'extra'}->{$key};
1681+ }
1682+ }
1683+ }
1684+ return $new;
1685+}
1686+
1687+# Not used.
1688+sub _collect_references($$);
1689+sub _collect_references($$)
1690+{
1691+ my $current = shift;
1692+ my $references = shift;
1693+ foreach my $key ('args', 'contents') {
1694+ if ($current->{$key}) {
1695+ $references->{$current->{$key}} = $current->{$key};
1696+ foreach my $child (@{$current->{$key}}) {
1697+ $references->{$child} = $child;
1698+ _collect_references($child, $references);
1699+ }
1700+ }
1701+ }
1702+}
1703+
1704+sub _substitute_references_in_array($$$);
1705+sub _substitute_references_in_array($$$)
1706+{
1707+ my $array = shift;
1708+ my $reference_associations = shift;
1709+ my $context = shift;
1710+
1711+ my $result = [];
1712+ my $index = 0;
1713+ foreach my $item (@{$array}) {
1714+ if (!ref($item)) {
1715+ push @{$result}, $item;
1716+ } elsif ($reference_associations->{$item}) {
1717+ push @{$result}, $reference_associations->{$item};
1718+ } elsif (ref($item) eq 'ARRAY') {
1719+ push @$result,
1720+ _substitute_references_in_array($item, $reference_associations,
1721+ "$context [$index]");
1722+ } elsif (defined($item->{'text'})) {
1723+ my $new_text = _copy_tree($item, undef, $reference_associations);
1724+ substitute_references($item, $new_text, $reference_associations);
1725+ push @{$result}, $new_text;
1726+ } else {
1727+ print STDERR "Trouble with $context [$index] (".ref($item).")\n";
1728+ push @{$result}, undef;
1729+ }
1730+ $index++;
1731+ }
1732+ return $result;
1733+}
1734+
1735+sub substitute_references($$$);
1736+sub substitute_references($$$)
1737+{
1738+ my $current = shift;
1739+ my $new = shift;
1740+ my $reference_associations = shift;
1741+
1742+ foreach my $key ('args', 'contents') {
1743+ if ($new->{$key}) {
1744+ my $index = 0;
1745+ foreach my $child (@{$new->{$key}}) {
1746+ substitute_references($child, $current->{$key}->[$index],
1747+ $reference_associations);
1748+ $index++;
1749+ }
1750+ }
1751+ }
1752+ if ($current->{'extra'}) {
1753+ foreach my $key (keys %{$current->{'extra'}}) {
1754+ if (ref($current->{'extra'}->{$key})) {
1755+ my $command_or_type = '';
1756+ if ($new->{'cmdname'}) {
1757+ $command_or_type = '@'.$new->{'cmdname'};
1758+ } elsif ($new->{'type'}) {
1759+ $command_or_type = $new->{'type'};
1760+ }
1761+
1762+ if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable'
1763+ and $key eq 'prototypes') {
1764+ my $index = 0;
1765+ foreach my $child (@{$new->{'extra'}->{$key}}) {
1766+ substitute_references($child, $current->{'extra'}->{$key}->[$index],
1767+ $reference_associations);
1768+ $index++;
1769+ }
1770+ } elsif ($reference_associations->{$current->{'extra'}->{$key}}) {
1771+ $new->{'extra'}->{$key}
1772+ = $reference_associations->{$current->{'extra'}->{$key}};
1773+ #print STDERR "Done [$command_or_type]: $key\n";
1774+ } else {
1775+ if (ref($current->{'extra'}->{$key}) eq 'ARRAY') {
1776+
1777+ #print STDERR "Array $command_or_type -> $key\n";
1778+ $new->{'extra'}->{$key} = _substitute_references_in_array(
1779+ $current->{'extra'}->{$key}, $reference_associations,
1780+ "[$command_or_type]{$key}");
1781+ } else {
1782+ if (($current->{'cmdname'}
1783+ and ($current->{'cmdname'} eq 'listoffloats'
1784+ or $current->{'cmdname'} eq 'float')
1785+ and $key eq 'type')
1786+ or ($key eq 'index_entry')
1787+ or ($current->{'type'}
1788+ and $current->{'type'} eq 'menu_entry'
1789+ and $key eq 'menu_entry_node')) {
1790+ foreach my $type_key (keys(%{$current->{'extra'}->{$key}})) {
1791+ if (!ref($current->{'extra'}->{$key}->{$type_key})) {
1792+ $new->{'extra'}->{$key}->{$type_key}
1793+ = $current->{'extra'}->{$key}->{$type_key};
1794+ } elsif ($reference_associations->{$current->{'extra'}->{$key}->{$type_key}}) {
1795+ $new->{'extra'}->{$key}->{$type_key}
1796+ = $reference_associations->{$current->{'extra'}->{$key}->{$type_key}};
1797+ } elsif (ref($current->{'extra'}->{$key}->{$type_key}) eq 'ARRAY') {
1798+ $new->{'extra'}->{$key}->{$type_key}
1799+ = _substitute_references_in_array(
1800+ $current->{'extra'}->{$key}->{$type_key},
1801+ $reference_associations,
1802+ "[$command_or_type]{$key}{$type_key}");
1803+ } else {
1804+ print STDERR "Not substituting [$command_or_type]{$key}: $type_key\n";
1805+ }
1806+ }
1807+ } else {
1808+ print STDERR "Not substituting [$command_or_type]: $key ($current->{'extra'}->{$key})\n";
1809+ }
1810+ }
1811+ }
1812+ }
1813+ }
1814+ }
1815+}
1816+
1817+sub copy_tree($;$)
1818+{
1819+ my $current = shift;
1820+ my $parent = shift;
1821+ my $reference_associations = {};
1822+ my $copy = _copy_tree($current, $parent, $reference_associations);
1823+ substitute_references($current, $copy, $reference_associations);
1824+ return $copy;
1825+}
1826+
1827+sub modify_tree($$$;$);
1828+sub modify_tree($$$;$)
1829+{
1830+ my $self = shift;
1831+ my $tree = shift;
1832+ my $operation = shift;
1833+ my $argument = shift;
1834+ #print STDERR "modify_tree tree: $tree\n";
1835+
1836+ if ($tree->{'args'}) {
1837+ my @args = @{$tree->{'args'}};
1838+ for (my $i = 0; $i <= $#args; $i++) {
1839+ my @new_args = &$operation($self, 'arg', $args[$i], $argument);
1840+ modify_tree($self, $args[$i], $operation, $argument);
1841+ # this puts the new args at the place of the old arg using the
1842+ # offset from the end of the array
1843+ splice (@{$tree->{'args'}}, $i - $#args -1, 1, @new_args);
1844+ #foreach my $arg (@new_args) {
1845+ # modify_tree($self, $arg, $operation);
1846+ #}
1847+ }
1848+ }
1849+ if ($tree->{'contents'}) {
1850+ my @contents = @{$tree->{'contents'}};
1851+ for (my $i = 0; $i <= $#contents; $i++) {
1852+ my @new_contents = &$operation($self, 'content', $contents[$i], $argument);
1853+ modify_tree($self, $contents[$i], $operation, $argument);
1854+ # this puts the new contents at the place of the old content using the
1855+ # offset from the end of the array
1856+ splice (@{$tree->{'contents'}}, $i - $#contents -1, 1, @new_contents);
1857+ #foreach my $content (@new_contents) {
1858+ # modify_tree($self, $content, $operation);
1859+ #}
1860+ }
1861+ }
1862+ return $tree;
1863+}
1864+
1865+sub _protect_comma($$$)
1866+{
1867+ my $self = shift;
1868+ my $type = shift;
1869+ my $current = shift;
1870+
1871+ return _protect_text($current, quotemeta(','));
1872+}
1873+
1874+sub protect_comma_in_tree($)
1875+{
1876+ my $tree = shift;
1877+ return modify_tree(undef, $tree, \&_protect_comma);
1878+}
1879+
1880+sub _new_asis_command_with_text($$;$)
1881+{
1882+ my $text = shift;
1883+ my $parent = shift;
1884+ my $text_type = shift;
1885+ my $new_command = {'cmdname' => 'asis', 'parent' => $parent };
1886+ push @{$new_command->{'args'}}, {'type' => 'brace_command_arg',
1887+ 'parent' => $new_command};
1888+ push @{$new_command->{'args'}->[0]->{'contents'}}, {
1889+ 'text' => $text,
1890+ 'parent' => $new_command->{'args'}->[0]};
1891+ if (defined($text_type)) {
1892+ $new_command->{'args'}->[0]->{'contents'}->[0]->{'type'} = $text_type;
1893+ }
1894+ return $new_command;
1895+}
1896+
1897+sub _protect_text($$)
1898+{
1899+ my $current = shift;
1900+ my $to_protect = shift;
1901+
1902+ #print STDERR "$to_protect: $current "._print_current($current)."\n";
1903+ if (defined($current->{'text'}) and $current->{'text'} =~ /$to_protect/
1904+ and !(defined($current->{'type'}) and $current->{'type'} eq 'raw')) {
1905+ my @result = ();
1906+ my $remaining_text = $current->{'text'};
1907+ while ($remaining_text) {
1908+ if ($remaining_text =~ s/^(.*?)(($to_protect)+)//) {
1909+ if ($1 ne '') {
1910+ push @result, {'text' => $1, 'parent' => $current->{'parent'}};
1911+ $result[-1]->{'type'} = $current->{'type'}
1912+ if defined($current->{'type'});
1913+ }
1914+ if ($to_protect eq quotemeta(',')) {
1915+ for (my $i = 0; $i < length($2); $i++) {
1916+ push @result, {'cmdname' => 'comma', 'parent' => $current->{'parent'},
1917+ 'args' => [{'type' => 'brace_command_arg'}]};
1918+ }
1919+ } else {
1920+ push @result, _new_asis_command_with_text($2, $current->{'parent'},
1921+ $current->{'type'});
1922+ }
1923+ } else {
1924+ push @result, {'text' => $remaining_text, 'parent' => $current->{'parent'}};
1925+ $result[-1]->{'type'} = $current->{'type'}
1926+ if defined($current->{'type'});
1927+ last;
1928+ }
1929+ }
1930+ #print STDERR "Result: @result\n";
1931+ return @result;
1932+ } else {
1933+ #print STDERR "No change: $current\n";
1934+ return ($current);
1935+ }
1936+}
1937+
1938+sub _protect_colon($$$)
1939+{
1940+ my $self = shift;
1941+ my $type = shift;
1942+ my $current = shift;
1943+
1944+ return _protect_text ($current, quotemeta(':'));
1945+}
1946+
1947+sub protect_colon_in_tree($)
1948+{
1949+ my $tree = shift;
1950+ return modify_tree(undef, $tree, \&_protect_colon);
1951+}
1952+
1953+sub _protect_node_after_label($$$)
1954+{
1955+ my $self = shift;
1956+ my $type = shift;
1957+ my $current = shift;
1958+
1959+ return _protect_text ($current, '['. quotemeta(".\t,") .']');
1960+}
1961+
1962+sub protect_node_after_label_in_tree($)
1963+{
1964+ my $tree = shift;
1965+ return modify_tree(undef, $tree, \&_protect_node_after_label);
1966+}
1967+
1968+sub _is_cpp_line($)
1969+{
1970+ my $text = shift;
1971+ return 1 if ($text =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/);
1972+ return 0;
1973+}
1974+
1975+sub _protect_hashchar_at_line_beginning($$$)
1976+{
1977+ my $self = shift;
1978+ my $type = shift;
1979+ my $current = shift;
1980+
1981+ #print STDERR "$type $current "._print_current($current)."\n";
1982+ # if the next is a hash character at line beginning, mark it
1983+ if (defined($current->{'text'}) and $current->{'text'} =~ /\n$/
1984+ and $current->{'parent'} and $current->{'parent'}->{'contents'}) {
1985+ my $parent = $current->{'parent'};
1986+ #print STDERR "End of line in $current, parent $parent: (@{$parent->{'contents'}})\n";
1987+ my $current_found = 0;
1988+ foreach my $content (@{$parent->{'contents'}}) {
1989+ if ($current_found) {
1990+ #print STDERR "after $current: $content $content->{'text'}\n";
1991+ if ($content->{'text'} and _is_cpp_line($content->{'text'})) {
1992+ $content->{'extra'}->{'_protect_hashchar'} = 1;
1993+ }
1994+ last;
1995+ } elsif ($content eq $current) {
1996+ $current_found = 1;
1997+ }
1998+ }
1999+ }
2000+
2001+ my $protect_hash = 0;
2002+ # if marked, or first and a cpp_line protect a leading hash character
2003+ if ($current->{'extra'} and $current->{'extra'}->{'_protect_hashchar'}) {
2004+ delete $current->{'extra'}->{'_protect_hashchar'};
2005+ if (!scalar(keys(%{$current->{'extra'}}))) {
2006+ delete $current->{'extra'};
2007+ }
2008+ $protect_hash = 1;
2009+ } elsif ($current->{'parent'} and $current->{'parent'}->{'contents'}
2010+ and $current->{'parent'}->{'contents'}->[0]
2011+ and $current->{'parent'}->{'contents'}->[0] eq $current
2012+ and $current->{'text'}
2013+ and _is_cpp_line($current->{'text'})) {
2014+ $protect_hash = 1;
2015+ }
2016+ if ($protect_hash) {
2017+ my @result = ();
2018+ if ($current->{'type'} and $current->{'type'} eq 'raw') {
2019+ if ($self) {
2020+ my $parent = $current->{'parent'};
2021+ while ($parent) {
2022+ if ($parent->{'cmdname'} and $parent->{'line_nr'}) {
2023+ $self->line_warn(sprintf($self->__(
2024+ "could not protect hash character in \@%s"),
2025+ $parent->{'cmdname'}), $parent->{'line_nr'});
2026+ last;
2027+ }
2028+ $parent = $parent->{'parent'};
2029+ }
2030+ }
2031+ } else {
2032+ $current->{'text'} =~ s/^(\s*)#//;
2033+ if ($1 ne '') {
2034+ push @result, {'text' => $1, 'parent' => $current->{'parent'}};
2035+ }
2036+ push @result, {'cmdname' => 'hashchar', 'parent' => $current->{'parent'},
2037+ 'args' => [{'type' => 'brace_command_arg'}]};
2038+ }
2039+ push @result, $current;
2040+ return @result;
2041+ } else {
2042+ return ($current);
2043+ }
2044+}
2045+
2046+sub protect_hashchar_at_line_beginning($$)
2047+{
2048+ my $self = shift;
2049+ my $tree = shift;
2050+ return modify_tree($self, $tree, \&_protect_hashchar_at_line_beginning);
2051+}
2052+
2053+sub protect_first_parenthesis($)
2054+{
2055+ my $contents = shift;
2056+ return undef if (!defined ($contents));
2057+ my @contents = @$contents;
2058+ my $brace;
2059+ if ($contents[0] and $contents->[0]{'text'} and $contents[0]->{'text'} =~ /^\(/) {
2060+ if ($contents[0]->{'text'} !~ /^\($/) {
2061+ $brace = shift @contents;
2062+ my $brace_text = $brace->{'text'};
2063+ $brace_text =~ s/^\(//;
2064+ unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'},
2065+ 'parent' => $brace->{'parent'} } if $brace_text ne '';
2066+ } else {
2067+ $brace = shift @contents;
2068+ }
2069+ unshift @contents, _new_asis_command_with_text('(', $brace->{'parent'},
2070+ $brace->{'type'});
2071+ }
2072+ return \@contents;
2073+}
2074+
2075+sub find_parent_root_command($$)
2076+{
2077+ my $parser = shift;
2078+ my $current = shift;
2079+
2080+ my $root_command;
2081+ while (1) {
2082+ if ($current->{'cmdname'}) {
2083+ if ($root_commands{$current->{'cmdname'}}) {
2084+ return $current;
2085+ } elsif ($region_commands{$current->{'cmdname'}}) {
2086+ if ($current->{'cmdname'} eq 'copying' and $parser
2087+ and $parser->{'extra'} and $parser->{'extra'}->{'insertcopying'}) {
2088+ foreach my $insertcopying(@{$parser->{'extra'}->{'insertcopying'}}) {
2089+ my $root_command
2090+ = $parser->find_parent_root_command($insertcopying);
2091+ return $root_command if (defined($root_command));
2092+ }
2093+ } else {
2094+ return undef;
2095+ }
2096+ }
2097+ }
2098+ if ($current->{'parent'}) {
2099+ $current = $current->{'parent'};
2100+ } else {
2101+ return undef;
2102+ }
2103+ }
2104+ # Should never get there
2105+ return undef;
2106+}
2107+
2108+# for debugging
2109+sub _print_current($)
2110+{
2111+ my $current = shift;
2112+ if (ref($current) ne 'HASH') {
2113+ return "_print_current: $current not a hash\n";
2114+ }
2115+ my $type = '';
2116+ my $cmd = '';
2117+ my $parent_string = '';
2118+ my $text = '';
2119+ $type = "($current->{'type'})" if (defined($current->{'type'}));
2120+ $cmd = "\@$current->{'cmdname'}" if (defined($current->{'cmdname'}));
2121+ $cmd .= "($current->{'level'})" if (defined($current->{'level'}));
2122+ $text = "[text: $current->{'text'}]" if (defined($current->{'text'}));
2123+ if ($current->{'parent'}) {
2124+ my $parent = $current->{'parent'};
2125+ my $parent_cmd = '';
2126+ my $parent_type = '';
2127+ $parent_cmd = "\@$parent->{'cmdname'}" if (defined($parent->{'cmdname'}));
2128+ $parent_type = "($parent->{'type'})" if (defined($parent->{'type'}));
2129+ $parent_string = " <- $parent_cmd$parent_type\n";
2130+ }
2131+ my $args = '';
2132+ my $contents = '';
2133+ $args = "args(".scalar(@{$current->{'args'}}).')' if $current->{'args'};
2134+ $contents = "contents(".scalar(@{$current->{'contents'}}).')'
2135+ if $current->{'contents'};
2136+ if ("$cmd$type" ne '') {
2137+ return "$cmd$type : $text $args $contents\n$parent_string";
2138+ } else {
2139+ return "$text $args $contents\n$parent_string";
2140+ }
2141+}
2142+
2143+sub move_index_entries_after_items($) {
2144+ # enumerate or itemize
2145+ my $current = shift;
2146+
2147+ return unless ($current->{'contents'});
2148+
2149+ my $previous;
2150+ foreach my $item (@{$current->{'contents'}}) {
2151+ #print STDERR "Before proceeding: $previous $item->{'cmdname'} (@{$previous->{'contents'}})\n" if ($previous and $previous->{'contents'});
2152+ if (defined($previous) and $item->{'cmdname'}
2153+ and $item->{'cmdname'} eq 'item'
2154+ and $previous->{'contents'} and scalar(@{$previous->{'contents'}})) {
2155+
2156+ my $previous_ending_container;
2157+ if ($previous->{'contents'}->[-1]->{'type'}
2158+ and ($previous->{'contents'}->[-1]->{'type'} eq 'paragraph'
2159+ or $previous->{'contents'}->[-1]->{'type'} eq 'preformatted')) {
2160+ $previous_ending_container = $previous->{'contents'}->[-1];
2161+ } else {
2162+ $previous_ending_container = $previous;
2163+ }
2164+
2165+ my @gathered_index_entries;
2166+
2167+ #print STDERR "Gathering for item $item in previous $previous ($previous_ending_container)\n";
2168+ while ($previous_ending_container->{'contents'}->[-1]
2169+ and (($previous_ending_container->{'contents'}->[-1]->{'type'}
2170+ and $previous_ending_container->{'contents'}->[-1]->{'type'} eq 'index_entry_command')
2171+ or ($previous_ending_container->{'contents'}->[-1]->{'cmdname'}
2172+ and ($previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'c'
2173+ or $previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'comment')))) {
2174+ unshift @gathered_index_entries, pop @{$previous_ending_container->{'contents'}};
2175+ }
2176+ #print STDERR "Gathered: @gathered_index_entries\n";
2177+ if (scalar(@gathered_index_entries)) {
2178+ # put back leading comments
2179+ while ($gathered_index_entries[0]
2180+ and (!$gathered_index_entries[0]->{'type'}
2181+ or $gathered_index_entries[0]->{'type'} ne 'index_entry_command')) {
2182+ #print STDERR "Putting back $gathered_index_entries[0] $gathered_index_entries[0]->{'cmdname'}\n";
2183+ push @{$previous_ending_container->{'contents'}},
2184+ shift @gathered_index_entries;
2185+ }
2186+
2187+ # We have the index entries of the previous @item or before item.
2188+ # Now put them right after the current @item command.
2189+ if (scalar(@gathered_index_entries)) {
2190+ my $item_container;
2191+ if ($item->{'contents'} and $item->{'contents'}->[0]
2192+ and $item->{'contents'}->[0]->{'type'}
2193+ and $item->{'contents'}->[0]->{'type'} eq 'preformatted') {
2194+ $item_container = $item->{'contents'}->[0];
2195+ } else {
2196+ $item_container = $item;
2197+ }
2198+ foreach my $entry(@gathered_index_entries) {
2199+ $entry->{'parent'} = $item_container;
2200+ }
2201+ if ($item_container->{'contents'}
2202+ and $item_container->{'contents'}->[0]
2203+ and $item_container->{'contents'}->[0]->{'type'}) {
2204+ if ($item_container->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') {
2205+
2206+ unshift @gathered_index_entries, shift @{$item_container->{'contents'}};
2207+ } elsif ($item_container->{'contents'}->[0]->{'type'} eq 'empty_spaces_after_command') {
2208+ unshift @gathered_index_entries, shift @{$item_container->{'contents'}};
2209+ $gathered_index_entries[0]->{'type'} = 'empty_line_after_command';
2210+ $gathered_index_entries[0]->{'text'} .= "\n";
2211+ }
2212+ }
2213+ unshift @{$item_container->{'contents'}}, @gathered_index_entries;
2214+ }
2215+ }
2216+ }
2217+ $previous = $item;
2218+ }
2219+}
2220+
2221+sub _move_index_entries_after_items($$$)
2222+{
2223+ my $self = shift;
2224+ my $type = shift;
2225+ my $current = shift;
2226+
2227+ if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'enumerate'
2228+ or $current->{'cmdname'} eq 'itemize')) {
2229+ move_index_entries_after_items($current);
2230+ }
2231+ return ($current);
2232+}
2233+
2234+sub move_index_entries_after_items_in_tree($)
2235+{
2236+ my $tree = shift;
2237+ return modify_tree(undef, $tree, \&_move_index_entries_after_items);
2238+}
2239+
2240+1;
2241+
2242+__END__
2243+
2244+=head1 NAME
2245+
2246+Texinfo::Common - Classification of commands and miscellaneous methods
2247+
2248+=head1 SYNOPSIS
2249+
2250+ use Texinfo::Common qw(expand_today expand_verbatiminclude);
2251+ if ($Texinfo::Common::accent_commands{$a_command}) {
2252+ print STDERR "$a_command is an accent command\n";
2253+ }
2254+
2255+ my $today_tree = expand_today($converter);
2256+ my $verbatiminclude_tree
2257+ = expand_verbatiminclude(undef, $verbatiminclude);
2258+
2259+=head1 DESCRIPTION
2260+
2261+Texinfo::Common holds interesting hashes classifying Texinfo @-commands,
2262+as well as miscellaneous methods that may be useful for any backend
2263+converting texinfo trees.
2264+
2265+It also defines, as our variable a hash for default indices,
2266+named C<%index_names>. The format of this hash is described in
2267+L<Texinfo::Parser/indices_information>.
2268+
2269+=head1 COMMAND CLASSES
2270+
2271+Hashes are defined as C<our> variables, and are therefore available
2272+outside of the module.
2273+
2274+The key of the hashes are @-command names without the @. The
2275+following hashes are available:
2276+
2277+=over
2278+
2279+=item %all_commands
2280+
2281+All the @-commands.
2282+
2283+=item %no_brace_commands
2284+
2285+Commands without brace with a single character as name, like C<*>
2286+or C<:>. The value is an ascii representation of the command. It
2287+may be an empty string.
2288+
2289+=item %misc_commands
2290+
2291+Command that do not take braces and are not block commands either, like
2292+C<@node>, C<@chapter>, C<@cindex>, C<@deffnx>, C<@end>, C<@footnotestyle>,
2293+C<@set>, C<@settitle>, C<@indent>, C<@definfoenclose>, C<@comment> and many
2294+others.
2295+
2296+=item %default_index_commands
2297+
2298+Index entry commands corresponding to default indices. For example
2299+C<@cindex>.
2300+
2301+=item %root_commands
2302+
2303+Commands that are at the root of a Texinfo document, namely
2304+C<@node> and sectioning commands, except heading commands.
2305+
2306+=item %sectioning_commands
2307+
2308+All the sectioning and heading commands.
2309+
2310+=item %brace_commands
2311+
2312+The commands that take braces. The associated value is the maximum
2313+number of arguments.
2314+
2315+=item %letter_no_arg_commands
2316+
2317+@-commands with braces but no argument corresponding to letters,
2318+like C<@AA{}> or C<@ss{}> or C<@o{}>.
2319+
2320+=item %accent_commands
2321+
2322+Accent @-commands taking an argument, like C<@'> or C<@ringaccent>
2323+including C<@dotless> and C<@tieaccent>.
2324+
2325+=item %style_commands
2326+
2327+Commands that mark a fragment of texinfo, like C<@strong>,
2328+C<@cite>, C<@code> or C<@asis>.
2329+
2330+=item %code_style_commands
2331+
2332+I<style_commands> that have their argument in code style, like
2333+C<@code>.
2334+
2335+=item %regular_font_style_commands
2336+
2337+I<style_commands> that have their argument in regular font, like
2338+C<@r> or C<@slanted>.
2339+
2340+=item %context_brace_commands
2341+
2342+@-commands with brace like C<@footnote>, C<@caption> and C<@math>
2343+whose argument is outside of the main text flow in one way or another.
2344+
2345+=item %ref_commands
2346+
2347+Cross reference @-command referencing nodes, like C<@xref>.
2348+
2349+=item %explained_commands
2350+
2351+@-commands whose second argument explain first argument and further
2352+@-command call without first argument, as C<@abbr> and C<@acronym>.
2353+
2354+=item %block commands
2355+
2356+Commands delimiting a block with a closing C<@end>. The value
2357+is I<conditional> for C<@if> commands, I<def> for definition
2358+commands like C<@deffn>, I<raw> for @-commands that have no expansion
2359+of @-commands in their bodies and I<multitable> for C<@multitable>.
2360+Otherwise it is set to the number of arguments separated by commas
2361+that may appear on the @-command line. That means 0 in most cases,
2362+1 for C<@quotation> and 2 for C<@float>.
2363+
2364+=item %raw_commands
2365+
2366+@-commands that have no expansion of @-commands in their bodies,
2367+as C<@macro>, C<@verbatim> or C<@ignore>.
2368+
2369+=item %format_raw_commands
2370+
2371+@-commands associated with raw output format, like C<@html>, or
2372+C<@docbook>.
2373+
2374+=item %texinfo_output_formats
2375+
2376+Cannonical output formats that have associated conditionals. In
2377+practice C<%format_raw_commands> plus C<info> and C<plaintext>.
2378+
2379+=item %def_commands
2380+
2381+=item %def_aliases
2382+
2383+Definition commands. C<%def_aliases> associates an aliased command
2384+to the original command, for example C<defun> is associated to C<deffn>.
2385+
2386+=item %menu_commands
2387+
2388+@-commands with menu entries.
2389+
2390+=item %align_commands
2391+
2392+@-commands related with alignement of text.
2393+
2394+=item %region_commands
2395+
2396+Block @-commands that enclose full text regions, like C<@titlepage>.
2397+
2398+=item %preformatted_commands
2399+
2400+=item %preformatted_code_commands
2401+
2402+I<%preformatted_commands> is for commands whose content should not
2403+be filled, like C<@example> or C<@display>. If the command is meant
2404+for code, it is also in I<%preformatted_code_commands>, like C<@example>.
2405+
2406+=item %item_container_commands
2407+
2408+Commands holding C<@item> with C<@item> that contains blocks of text,
2409+like C<@itemize>.
2410+
2411+=item %item_line_commands
2412+
2413+Commands with C<@item> that have their arguments on their lines, like
2414+C<@ftable>.
2415+
2416+=back
2417+
2418+=head1 METHODS
2419+
2420+No method is exported in the default case.
2421+
2422+Most methods takes a I<$converter> as argument, sometime optionally,
2423+to get some information and use methods for error reporting,
2424+see L<Texinfo::Convert::Converter> and L<Texinfo::Report>.
2425+
2426+=over
2427+
2428+=item $tree = expand_today($converter)
2429+
2430+Expand today's date, as a texinfo tree with translations.
2431+
2432+=item $tree = expand_verbatiminclude($converter, $verbatiminclude)
2433+
2434+The I<$converter> argument may be undef. I<$verbatiminclude> is a
2435+C<@verbatiminclude> tree element. This function returns a
2436+C<@verbatim> tree elements after finding the included file and
2437+reading it.
2438+
2439+=item $tree = definition_category($converter, $def_line)
2440+
2441+The I<$converter> argument may be undef. I<$def_line> is a
2442+C<def_line> texinfo tree container. This function
2443+returns a texinfo tree corresponding to the category of the
2444+I<$def_line> taking the class into account, if there is one.
2445+
2446+=item $result = numbered_heading ($converter, $heading_element, $heading_text, $do_number)
2447+
2448+The I<$converter> argument may be undef. I<$heading_element> is
2449+a heading command tree element. I<$heading_text> is the already
2450+formatted heading text. if the I<$do_number> optional argument is
2451+defined and false, no number is used and the text is returned as is.
2452+This function returns the heading with a number and the appendix
2453+part if needed.
2454+
2455+=item ($caption, $prepended) = float_name_caption ($converter, $float)
2456+
2457+I<$float> is a texinfo tree C<@float> element. This function
2458+returns the caption that should be used for the float formatting
2459+and the I<$prepended> texinfo tree combining the type and label
2460+of the float.
2461+
2462+=item $text = enumerate_item_representation($specification, $number)
2463+
2464+This function returns the number or letter correponding to item
2465+number I<$number> for an C<@enumerate> specification I<$specification>,
2466+appearing on an C<@enumerate> line. For example
2467+
2468+ enumerate_item_representation('c', 3)
2469+
2470+is C<e>.
2471+
2472+=item trim_spaces_comment_from_content($contents)
2473+
2474+Remove empty spaces after commands or braces at begin and
2475+spaces and comments at end from a content array, modifying it.
2476+
2477+=item $normalized_name = normalize_top_node_name ($node_string)
2478+
2479+Normalize the node name string given in argument, by normalizing
2480+Top node case.
2481+
2482+=item protect_comma_in_tree($tree)
2483+
2484+Protect comma characters, replacing C<,> with @comma{} in tree.
2485+
2486+=item protect_colon_in_tree($tree)
2487+
2488+=item protect_node_after_label_in_tree($tree)
2489+
2490+Protect colon with C<protect_colon_in_tree> and characters that
2491+are special in node names after a label in menu entries (tab
2492+dot and comma) with C<protect_node_after_label_in_tree>.
2493+The protection is achieved by putting protected characters
2494+in C<@asis{}>.
2495+
2496+=item $contents_result = protect_first_parenthesis ($contents)
2497+
2498+Return a contents array reference with first parenthesis in the
2499+contents array reference protected.
2500+
2501+=item protect_hashchar_at_line_beginning($parser, $tree)
2502+
2503+Protect hash character at beginning of line if the line is a cpp
2504+line directive. The I<$parser> argument maybe undef, if it is
2505+defined it is used for error reporting in case an hash character
2506+could not be protected because it appeared in a raw environment.
2507+
2508+=item move_index_entries_after_items_in_tree($tree)
2509+
2510+In C<@enumerate> and C<@itemize> from the tree, move index entries
2511+appearing just before C<@item> after the C<@item>. Comment lines
2512+between index entries are moved too.
2513+
2514+=item $command = find_parent_root_command($parser, $tree_element)
2515+
2516+Find the parent root command of a tree element (sectioning command or node).
2517+The C<$parser> argument is optional, it is used to continue
2518+through C<@insertcopying> if in a C<@copying>.
2519+
2520+=item valid_tree_transformation($name)
2521+
2522+Return true if the I<$name> is a known tree transformation name
2523+that may be passed with C<TREE_TRANSFORMATIONS> to modify a texinfo
2524+tree.
2525+
2526+=back
2527+
2528+=head1 SEE ALSO
2529+
2530+L<Texinfo::Parser>, L<Texinfo::Convert::Converter> and L<Texinfo::Report>.
2531+
2532+=head1 AUTHOR
2533+
2534+Patrice Dumas, E<lt>pertusus@free.frE<gt>
2535+
2536+=head1 COPYRIGHT AND LICENSE
2537+
2538+Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
2539+
2540+This library is free software; you can redistribute it and/or modify
2541+it under the terms of the GNU General Public License as published by
2542+the Free Software Foundation; either version 3 of the License,
2543+or (at your option) any later version.
2544+
2545+=cut
2546+
2547
2548=== added file '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm'
2549--- .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm 1970-01-01 00:00:00 +0000
2550+++ .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm 2013-07-14 15:17:24 +0000
2551@@ -0,0 +1,6996 @@
2552+# Parser.pm: parse texinfo code into a tree.
2553+#
2554+# Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
2555+#
2556+# This program is free software; you can redistribute it and/or modify
2557+# it under the terms of the GNU General Public License as published by
2558+# the Free Software Foundation; either version 3 of the License,
2559+# or (at your option) any later version.
2560+#
2561+# This program is distributed in the hope that it will be useful,
2562+# but WITHOUT ANY WARRANTY; without even the implied warranty of
2563+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2564+# GNU General Public License for more details.
2565+#
2566+# You should have received a copy of the GNU General Public License
2567+# along with this program. If not, see <http://www.gnu.org/licenses/>.
2568+#
2569+# Original author: Patrice Dumas <pertusus@free.fr>
2570+# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
2571+
2572+# The organization of the file is the following:
2573+# module definitions.
2574+# default parser state. With explanation of the internal structures.
2575+# initializations, determination of command types.
2576+# user visible subroutines.
2577+# internal subroutines, doing the parsing.
2578+
2579+package Texinfo::Parser;
2580+
2581+# We need the unicode stuff.
2582+use 5.006;
2583+use strict;
2584+
2585+# debug
2586+use Carp qw(cluck);
2587+
2588+use Data::Dumper;
2589+
2590+# to detect if an encoding may be used to open the files
2591+use Encode;
2592+
2593+# for fileparse
2594+use File::Basename;
2595+
2596+#use POSIX qw(setlocale LC_ALL LC_CTYPE LC_MESSAGES);
2597+
2598+# commands definitions
2599+use Texinfo::Common;
2600+# Error reporting and counting, translation of strings.
2601+use Texinfo::Report;
2602+# encoding_alias
2603+use Texinfo::Encoding;
2604+
2605+# to expand file names in @include and similar @-commands
2606+use Texinfo::Convert::Text;
2607+# to normalize node name, anchor, float arg, listoffloats and first *ref argument.
2608+use Texinfo::Convert::NodeNameNormalization;
2609+# in error messages, and for macro body expansion
2610+use Texinfo::Convert::Texinfo;
2611+
2612+require Exporter;
2613+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
2614+@ISA = qw(Exporter Texinfo::Report);
2615+
2616+# Items to export into callers namespace by default. Note: do not export
2617+# names by default without a very good reason. Use EXPORT_OK instead.
2618+# Do not simply export all your public functions/methods/constants.
2619+
2620+# This allows declaration use Texinfo::Parser ':all';
2621+# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
2622+# will save memory.
2623+%EXPORT_TAGS = ( 'all' => [ qw(
2624+ parser
2625+ parse_texi_text
2626+ parse_texi_line
2627+ parse_texi_file
2628+ indices_information
2629+ floats_information
2630+ internal_references_information
2631+ labels_information
2632+ global_commands_information
2633+ global_informations
2634+) ] );
2635+
2636+@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
2637+
2638+@EXPORT = qw(
2639+);
2640+
2641+$VERSION = '5.0';
2642+
2643+sub N__($)
2644+{
2645+ return $_[0];
2646+}
2647+
2648+#sub __($$)
2649+#{
2650+# my $self = shift;
2651+# return &{$self->{'gettext'}}(@_);
2652+#}
2653+
2654+# Customization variables obeyed by the Parser, and the default values.
2655+our %default_customization_values = (
2656+ 'TEST' => 0,
2657+ 'DEBUG' => 0, # if >= 10, tree is printed in texi2any.pl after parsing.
2658+ # If >= 100 tree is printed every line.
2659+ 'SHOW_MENU' => 1, # if false no menu error related.
2660+ 'INLINE_INSERTCOPYING' => 0,
2661+ 'IGNORE_BEFORE_SETFILENAME' => 1,
2662+ 'MACRO_BODY_IGNORES_LEADING_SPACE' => 0,
2663+ 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME' => 1,
2664+ 'INPUT_PERL_ENCODING' => undef, # input perl encoding name, set from
2665+ # @documentencoding in the default case
2666+ 'INPUT_ENCODING_NAME' => undef, # encoding name normalized as preferred
2667+ # IANA, set from @documentencoding in the default
2668+ # case
2669+ 'CPP_LINE_DIRECTIVES' => 1, # handle cpp like synchronization lines
2670+ 'MAX_MACRO_CALL_NESTING' => 100000, # max number of nested macro calls
2671+ 'GLOBAL_COMMANDS' => [], # list of commands registered
2672+ # This is not used directly, but passed to Convert::Text through
2673+ # Texinfo::Common::_convert_text_options
2674+ 'ENABLE_ENCODING' => 1, # output accented and special characters
2675+ # based on @documentencoding
2676+ # following are used in Texinfo::Structuring
2677+ 'TOP_NODE_UP' => '(dir)', # up node of Top node
2678+ 'SIMPLE_MENU' => 0, # not used in the parser but in structuring
2679+ 'USE_UP_NODE_FOR_ELEMENT_UP' => 0, # Use node up for Up if there is no
2680+ # section up.
2681+);
2682+
2683+my %parser_default_configuration = (%Texinfo::Common::default_parser_state_configuration,
2684+ %default_customization_values);
2685+
2686+# the other possible keys for the parser state are:
2687+#
2688+# expanded_formats_hash each key comes from expanded_formats value is 1
2689+# index_names a structure holding the link between index
2690+# names, prefixes, merged indices,
2691+# initial value is %index_names in Texinfo::Common.
2692+# context_stack stack of the contexts, more recent on top.
2693+# 'line' is added when on a line or
2694+# block @-command line,
2695+# 'def' is added instead if on a definition line.
2696+# 'preformatted' is added in block commands
2697+# where there is no paragraphs and spaces are kept
2698+# (format, example, display...)
2699+# 'rawpreformatted' is added in raw block commands
2700+# (html, xml, docbook...)
2701+# 'menu' is added in menu commands
2702+# 'math', 'footnote', 'caption', 'shortcaption',
2703+# 'inlineraw' are also added when in those commands
2704+# conditionals_stack a stack of conditional commands that are expanded.
2705+# raw_formats_stack a stack of 1 or 0 for raw formats (@html... or
2706+# @inlineraw), is 0 if within a raw format that is
2707+# not expanded.
2708+# macro_stack stack of macros being expanded (more recent first)
2709+# definfoenclose an hash, key is the command name, value is an array
2710+# reference with 2 values, beginning and ending.
2711+# input a stack, with last at bottom. Holds the opened files
2712+# or text. Pending macro expansion or text expansion
2713+# is also in that structure.
2714+# misc_commands the same than %misc_commands in Texinfo::Common,
2715+# but with index entry commands dynamically added
2716+# close_paragraph_commands same than %close_paragraph_commands, but with
2717+# insertcopying removed if INLINE_INSERTCOPYING
2718+# close_preformatted_commands same than %close_preformatted_commands, but with
2719+# insertcopying removed if INLINE_INSERTCOPYING
2720+# no_paragraph_commands the same than %default_no_paragraph_commands
2721+# below, with index
2722+# entry commands dynamically added
2723+# simple_text_commands the same than %simple_text_commands below, but
2724+# with index entry commands dynamically added
2725+# current_node last seen node.
2726+# current_section last seen section.
2727+# nodes list of nodes.
2728+# command_index_prefix associate a command name to an index prefix.
2729+# prefix_to_index_name associate an index prefix to the index name.
2730+# floats key is the normalized float type, value is an array
2731+# reference holding all the floats.
2732+# internal_references an array holding all the internal references.
2733+
2734+# set points to the value set when initializing, for
2735+# configuration items that are not to be overriden
2736+# by @-commands. For example documentlanguage.
2737+
2738+# A line information is an hash reference with the keys:
2739+# line_nr the line number
2740+# file_name the file name
2741+# macro if in a macro expansion, the name of the macro
2742+#
2743+# A text fragment information is a 2 element array reference, the first is the
2744+# text fragment, the second is the line information.
2745+
2746+# The input structure is an array, the first is the most recently included
2747+# file. The last element may be a file if the parsing is done on a file,
2748+# with parse_texi_file, or simply pending text, if called as parse_texi_text.
2749+# each element of the array is a hash reference. The key are:
2750+# pending an array reference containing pending text fragments, either the
2751+# text given as parse_texi_text or macro expansion text.
2752+# name file name
2753+# line_nr current line number in the file
2754+# fh filehandle for the file
2755+
2756+# content is not copied but reference is copied when duplicating a parser.
2757+my %tree_informations;
2758+foreach my $tree_information ('values', 'macros', 'explained_commands', 'labels') {
2759+ $tree_informations{$tree_information} = 1;
2760+}
2761+
2762+# The commands in initialization_overrides are not set in the document if
2763+# set at the parser initialization.
2764+my %initialization_overrides = (
2765+ 'INPUT_ENCODING_NAME' => 1,
2766+ 'documentlanguage' => 1,
2767+);
2768+
2769+my %no_brace_commands = %Texinfo::Common::no_brace_commands;
2770+my %misc_commands = %Texinfo::Common::misc_commands;
2771+my %brace_commands = %Texinfo::Common::brace_commands;
2772+my %accent_commands = %Texinfo::Common::accent_commands;
2773+my %context_brace_commands = %Texinfo::Common::context_brace_commands;
2774+my %block_commands = %Texinfo::Common::block_commands;
2775+my %block_item_commands = %Texinfo::Common::block_item_commands;
2776+my %close_paragraph_commands = %Texinfo::Common::close_paragraph_commands;
2777+my %def_map = %Texinfo::Common::def_map;
2778+my %def_commands = %Texinfo::Common::def_commands;
2779+my %def_aliases = %Texinfo::Common::def_aliases;
2780+my %menu_commands = %Texinfo::Common::menu_commands;
2781+my %preformatted_commands = %Texinfo::Common::preformatted_commands;
2782+my %format_raw_commands = %Texinfo::Common::format_raw_commands;
2783+my %item_container_commands = %Texinfo::Common::item_container_commands;
2784+my %item_line_commands = %Texinfo::Common::item_line_commands;
2785+my %deprecated_commands = %Texinfo::Common::deprecated_commands;
2786+my %root_commands = %Texinfo::Common::root_commands;
2787+my %sectioning_commands = %Texinfo::Common::sectioning_commands;
2788+my %command_index_prefix = %Texinfo::Common::command_index_prefix;
2789+my %command_structuring_level = %Texinfo::Common::command_structuring_level;
2790+my %ref_commands = %Texinfo::Common::ref_commands;
2791+my %region_commands = %Texinfo::Common::region_commands;
2792+my %code_style_commands = %Texinfo::Common::code_style_commands;
2793+my %in_heading_commands = %Texinfo::Common::in_heading_commands;
2794+my %explained_commands = %Texinfo::Common::explained_commands;
2795+my %inline_format_commands = %Texinfo::Common::inline_format_commands;
2796+my %all_commands = %Texinfo::Common::all_commands;
2797+
2798+# equivalence between a @set flag and an @@-command
2799+my %set_flag_command_equivalent = (
2800+ 'txicodequoteundirected' => 'codequoteundirected',
2801+ 'txicodequotebacktick' => 'codequotebacktick',
2802+# 'txideftypefnnl' => 'deftypefnnewline',
2803+);
2804+
2805+
2806+# keep line information for those commands.
2807+my %keep_line_nr_brace_commands = %context_brace_commands;
2808+foreach my $keep_line_nr_brace_command ('titlefont', 'anchor') {
2809+ $keep_line_nr_brace_commands{$keep_line_nr_brace_command} = 1;
2810+}
2811+foreach my $brace_command (keys (%brace_commands)) {
2812+ $keep_line_nr_brace_commands{$brace_command} = 1
2813+ if ($brace_commands{$brace_command} > 1);
2814+}
2815+
2816+my %type_with_paragraph;
2817+foreach my $type ('before_item', 'text_root', 'document_root',
2818+ 'brace_command_context') {
2819+ $type_with_paragraph{$type} = 1;
2820+}
2821+
2822+my %command_ignore_space_after;
2823+foreach my $command ('anchor', 'hyphenation', 'caption', 'shortcaption') {
2824+ $command_ignore_space_after{$command} = 1;
2825+}
2826+
2827+my %global_multiple_commands;
2828+foreach my $global_multiple_command (
2829+ 'author', 'footnote', 'hyphenation', 'insertcopying', 'printindex',
2830+ 'subtitle','titlefont', 'listoffloats', 'detailmenu',
2831+ keys(%Texinfo::Common::document_settable_at_commands), ) {
2832+ $global_multiple_commands{$global_multiple_command} = 1;
2833+}
2834+
2835+my %global_unique_commands;
2836+foreach my $global_unique_command (
2837+ 'copying', 'settitle',
2838+ 'shorttitlepage', 'title', 'titlepage', 'top',
2839+ keys(%Texinfo::Common::document_settable_unique_at_commands), ) {
2840+ $global_unique_commands{$global_unique_command} = 1;
2841+}
2842+
2843+my %index_names = %Texinfo::Common::index_names;
2844+
2845+# index names that cannot be set by the user.
2846+my %forbidden_index_name = ();
2847+
2848+foreach my $name(keys(%index_names)) {
2849+ foreach my $prefix (@{$index_names{$name}->{'prefix'}}) {
2850+ $forbidden_index_name{$prefix} = 1;
2851+ }
2852+}
2853+
2854+foreach my $other_forbidden_index_name ('info','ps','pdf','htm',
2855+ 'html', 'log','aux','dvi','texi','txi','texinfo','tex','bib') {
2856+ $forbidden_index_name{$other_forbidden_index_name} = 1;
2857+}
2858+
2859+# @-commands that do not start a paragraph
2860+my %default_no_paragraph_commands;
2861+# @-commands that should be at a line beginning
2862+my %begin_line_commands;
2863+
2864+foreach my $command ('node', 'end') {
2865+ $begin_line_commands{$command} = $command;
2866+}
2867+
2868+foreach my $no_paragraph_command ('titlefont', 'caption', 'shortcaption',
2869+ 'image', '*', 'hyphenation', 'anchor', 'errormsg') {
2870+ $default_no_paragraph_commands{$no_paragraph_command} = 1;
2871+}
2872+
2873+foreach my $no_paragraph_command (keys(%misc_commands)) {
2874+ $default_no_paragraph_commands{$no_paragraph_command} = 1;
2875+ $begin_line_commands{$no_paragraph_command} = 1;
2876+}
2877+
2878+# verbatiminclude is not said to begin at the beginning of the line
2879+# in the manual
2880+foreach my $misc_not_begin_line ('comment', 'c', 'sp', 'refill',
2881+ 'noindent', 'indent', 'columnfractions',
2882+ 'tab', 'item', 'headitem', 'verbatiminclude',
2883+ 'set', 'clear',
2884+ 'vskip', keys(%in_heading_commands)) {
2885+ delete $begin_line_commands{$misc_not_begin_line};
2886+}
2887+
2888+my %block_arg_commands;
2889+foreach my $block_command (keys(%block_commands)) {
2890+ $begin_line_commands{$block_command} = 1;
2891+ $default_no_paragraph_commands{$block_command} = 1;
2892+ $block_arg_commands{$block_command} = 1
2893+ if ($block_commands{$block_command} ne 'raw');
2894+# and ! $format_raw_commands{$block_command});
2895+}
2896+
2897+my %close_preformatted_commands = %close_paragraph_commands;
2898+foreach my $no_close_preformatted('sp') {
2899+ delete $close_preformatted_commands{$no_close_preformatted};
2900+}
2901+# FIXME to close preformated or not to close?
2902+#foreach my $format_raw_command(keys(%format_raw_commands)) {
2903+# $close_preformatted_commands{$format_raw_command} = 1;
2904+#}
2905+
2906+# commands that may appear in accents
2907+my %in_accent_commands = %accent_commands;
2908+foreach my $brace_command(keys(%brace_commands)) {
2909+ $in_accent_commands{$brace_command} = 1 if (!$brace_commands{$brace_command});
2910+}
2911+foreach my $no_brace_command (keys(%no_brace_commands)) {
2912+ $in_accent_commands{$no_brace_command} = 1;
2913+}
2914+$in_accent_commands{'c'} = 1;
2915+$in_accent_commands{'comment'} = 1;
2916+
2917+# commands that may appear in texts arguments
2918+my %in_full_text_commands;
2919+foreach my $command (keys(%brace_commands), keys(%no_brace_commands)) {
2920+ $in_full_text_commands{$command} = 1;
2921+}
2922+foreach my $misc_command_in_full_text('c', 'comment', 'refill', 'noindent',
2923+ 'indent', 'columnfractions', 'set', 'clear', 'end') {
2924+ $in_full_text_commands{$misc_command_in_full_text} = 1;
2925+}
2926+
2927+foreach my $out_format (keys(%format_raw_commands)) {
2928+ $in_full_text_commands{$out_format} = 1;
2929+}
2930+delete $in_full_text_commands{'caption'};
2931+delete $in_full_text_commands{'shortcaption'};
2932+foreach my $block_command (keys(%block_commands)) {
2933+ $in_full_text_commands{$block_command} = 1
2934+ if ($block_commands{$block_command} eq 'conditional');
2935+}
2936+
2937+# commands that may happen on lines where everything is
2938+# permitted
2939+my %in_full_line_commands = %in_full_text_commands;
2940+foreach my $not_in_full_line_commands('noindent', 'indent') {
2941+ delete $in_full_line_commands{$not_in_full_line_commands};
2942+}
2943+
2944+# commands that may happen on sectioning commands
2945+my %in_full_line_commands_no_refs = %in_full_line_commands;
2946+foreach my $not_in_full_line_commands_no_refs ('titlefont',
2947+ 'anchor', 'footnote', 'verb') {
2948+ delete $in_full_line_commands_no_refs{$not_in_full_line_commands_no_refs};
2949+}
2950+
2951+# commands that may happen in simple text arguments
2952+my %in_simple_text_commands = %in_full_line_commands_no_refs;
2953+foreach my $not_in_simple_text_command('xref', 'ref', 'pxref', 'inforef') {
2954+ delete $in_simple_text_commands{$not_in_simple_text_command};
2955+}
2956+
2957+# commands that only accept simple text as argument in any context.
2958+my %simple_text_commands;
2959+foreach my $misc_command(keys(%misc_commands)) {
2960+ if ($misc_commands{$misc_command} =~ /^\d+$/
2961+ or ($misc_commands{$misc_command} eq 'line'
2962+ and !($sectioning_commands{$misc_command}
2963+ or $def_commands{$misc_command}))
2964+ or $misc_commands{$misc_command} eq 'text') {
2965+ $simple_text_commands{$misc_command} = 1;
2966+ }
2967+}
2968+
2969+my %full_line_commands_no_refs = (%sectioning_commands,
2970+ %def_commands);
2971+
2972+delete $simple_text_commands{'center'};
2973+delete $simple_text_commands{'exdent'};
2974+foreach my $command ('titlefont', 'anchor', 'xref','ref','pxref',
2975+ 'inforef', 'shortcaption', 'math', 'indicateurl',
2976+ 'email', 'uref', 'url', 'image', 'abbr', 'acronym',
2977+ 'dmn', 'ctrl', 'errormsg') {
2978+ $simple_text_commands{$command} = 1;
2979+}
2980+
2981+# commands that accept full text, but no block or top-level commands
2982+my %full_text_commands;
2983+foreach my $brace_command (keys (%brace_commands)) {
2984+ if ($brace_commands{$brace_command} == 1
2985+ and !$simple_text_commands{$brace_command}
2986+ and !$context_brace_commands{$brace_command}
2987+ and !$accent_commands{$brace_command}) {
2988+ $full_text_commands{$brace_command} = 1;
2989+ }
2990+}
2991+
2992+# commands that accept almost the same than in full text, except
2993+# what do not make sense on a line.
2994+my %full_line_commands;
2995+$full_line_commands{'center'} = 1;
2996+$full_line_commands{'exdent'} = 1;
2997+$full_line_commands{'item'} = 1;
2998+$full_line_commands{'itemx'} = 1;
2999+
3000+# Fill the valid nestings hash. All commands not in that hash
3001+# are considered to accept anything within. There are additional
3002+# context tests, to make sure, for instance that we are testing
3003+# @-commands on the block, misc or node @-command line and not
3004+# in the content.
3005+# index entry commands are dynamically set as in_simple_text_commands
3006+my %default_valid_nestings;
3007+
3008+foreach my $command (keys(%accent_commands)) {
3009+ $default_valid_nestings{$command} = \%in_accent_commands;
3010+}
3011+foreach my $command (keys(%full_text_commands)) {
3012+ $default_valid_nestings{$command} = \%in_full_text_commands;
3013+}
3014+foreach my $command (keys(%simple_text_commands)) {
3015+ $default_valid_nestings{$command} = \%in_simple_text_commands;
3016+}
3017+foreach my $command (keys(%full_line_commands)) {
3018+ $default_valid_nestings{$command} = \%in_full_line_commands;
3019+}
3020+foreach my $command (keys(%full_line_commands_no_refs)) {
3021+ $default_valid_nestings{$command} = \%in_full_line_commands_no_refs;
3022+}
3023+# Only for block commands with line arguments
3024+foreach my $command (keys(%block_commands)) {
3025+ if ($block_commands{$command} and $block_commands{$command} ne 'raw'
3026+ and $block_commands{$command} ne 'conditional'
3027+ and !$def_commands{$command}) {
3028+ $default_valid_nestings{$command} = \%in_simple_text_commands;
3029+ }
3030+}
3031+
3032+
3033+my @preformatted_contexts = ('preformatted', 'rawpreformatted');
3034+my %preformatted_contexts;
3035+foreach my $preformatted_context (@preformatted_contexts) {
3036+ $preformatted_contexts{$preformatted_context} = 1;
3037+}
3038+
3039+# contexts on the context_stack stack where empty line don't trigger
3040+# paragraph
3041+my %no_paragraph_contexts;
3042+foreach my $no_paragraph_context ('math', 'menu', @preformatted_contexts,
3043+ 'def', 'inlineraw') {
3044+ $no_paragraph_contexts{$no_paragraph_context} = 1;
3045+};
3046+
3047+
3048+
3049
3050+# Format a bug message
3051+sub _bug_message($$;$$)
3052+{
3053+ my $self = shift;
3054+ my $message = shift;
3055+ my $line_number = shift;
3056+ my $current = shift;
3057+
3058+ my $line_message = '';
3059+ if ($line_number) {
3060+ my $file = $line_number->{'file_name'};
3061+ $line_message
3062+ = "last location: $line_number->{'file_name'}:$line_number->{'line_nr'}";
3063+ if ($line_number->{'macro'} ne '') {
3064+ $line_message .= " (possibly involving $line_number->{'macro'})";
3065+ }
3066+ $line_message .= "\n";
3067+ }
3068+ my $message_context_stack = "context_stack: (@{$self->{'context_stack'}})\n";
3069+ my $current_element_message = '';
3070+ if ($current) {
3071+ $current_element_message = "current: ". _print_current($current);
3072+ }
3073+ warn "You found a bug: $message\n\n".
3074+ "Additional informations:\n".
3075+ $line_message.$message_context_stack.$current_element_message;
3076+
3077+}
3078+
3079+# simple deep copy of a structure
3080+sub _deep_copy($)
3081+{
3082+ my $struct = shift;
3083+ my $string = Data::Dumper->Dump([$struct], ['struct']);
3084+ eval $string;
3085+ return $struct;
3086+}
3087+
3088+# return true if effect of global commands should be ignored.
3089+sub _ignore_global_commands($)
3090+{
3091+ my $self = shift;
3092+ return !$self->{'raw_formats_stack'}->[-1];
3093+}
3094+
3095+# enter all the commands associated with an index name using the prefix
3096+# list
3097+sub _register_index_commands($$)
3098+{
3099+ my $self = shift;
3100+ my $index_name = shift;
3101+ if (!$self->{'index_names'}->{$index_name}->{'prefix'}) {
3102+ $self->{'index_names'}->{$index_name}->{'prefix'} = [$index_name];
3103+ }
3104+ if (!exists($self->{'index_names'}->{$index_name}->{'name'})) {
3105+ $self->{'index_names'}->{$index_name}->{'name'} = $index_name;
3106+ }
3107+ if (!exists($self->{'index_names'}->{$index_name}->{'contained_indices'})) {
3108+ $self->{'index_names'}->{$index_name}->{'contained_indices'}->{$index_name} = 1;
3109+ }
3110+ foreach my $prefix (@{$self->{'index_names'}->{$index_name}->{'prefix'}}) {
3111+ $self->{'misc_commands'}->{$prefix.'index'} = 'line';
3112+ $self->{'no_paragraph_commands'}->{$prefix.'index'} = 1;
3113+ $self->{'valid_nestings'}->{$prefix.'index'} = \%in_simple_text_commands;
3114+ $self->{'command_index_prefix'}->{$prefix.'index'} = $prefix;
3115+ $self->{'prefix_to_index_name'}->{$prefix} = $index_name;
3116+ }
3117+}
3118+
3119+# initialization entry point. Set up a parser.
3120+# The last argument, optional, is a hash provided by the user to change
3121+# the default values for what is present in %parser_default_configuration.
3122+# The exact arguments of the function depend on how it was called,
3123+# in a object oriented way or not.
3124+sub parser(;$$)
3125+{
3126+ my $class = shift;
3127+ my $conf;
3128+
3129+ my $parser = _deep_copy(\%parser_default_configuration);
3130+ # _deep_copy doesn't handle subs
3131+ $parser->{'gettext'} = $parser_default_configuration{'gettext'};
3132+ $parser->{'pgettext'} = $parser_default_configuration{'pgettext'};
3133+
3134+ # called not object-oriented
3135+ if (ref($class) eq 'HASH') {
3136+ #print STDERR "Not oo\n"
3137+ $conf = $class;
3138+ bless $parser;
3139+
3140+ } elsif (ref($class)) {
3141+ # called on an existing parser, interpreted as a duplication
3142+ my $old_parser = $class;
3143+ $class = ref($class);
3144+ foreach my $key (keys(%parser_default_configuration)) {
3145+ if ($tree_informations{$key}) {
3146+ if (defined($old_parser->{$key})) {
3147+ foreach my $info_key (keys(%{$old_parser->{$key}})) {
3148+ $parser->{$key}->{$info_key}
3149+ = $old_parser->{$key}->{$info_key};
3150+ }
3151+ }
3152+ } else {
3153+ $parser->{$key} = _deep_copy($old_parser->{$key});
3154+ }
3155+ }
3156+ #$parser = _deep_copy($old_parser);
3157+ $parser->{'gettext'} = $old_parser->{'gettext'};
3158+ $parser->{'pgettext'} = $old_parser->{'pgettext'};
3159+ bless $parser, $class;
3160+ $conf = shift;
3161+
3162+ } elsif (defined($class)) {
3163+ bless $parser, $class;
3164+ $conf = shift;
3165+ } else {
3166+ bless $parser;
3167+ $conf = shift;
3168+ }
3169+
3170+ if (defined($conf)) {
3171+ foreach my $key (keys(%$conf)) {
3172+ if (exists($parser_default_configuration{$key})) {
3173+ if (ref($conf->{$key}) ne 'CODE' and $key ne 'values') {
3174+ $parser->{$key} = _deep_copy($conf->{$key});
3175+ } else {
3176+ $parser->{$key} = $conf->{$key};
3177+ }
3178+ if ($initialization_overrides{$key}) {
3179+ $parser->{'set'}->{$key} = $parser->{$key};
3180+ }
3181+ } else {
3182+ warn "$key not a possible customization in Texinfo::Parser::parser\n";
3183+ }
3184+ }
3185+ }
3186+ #foreach my $value (keys %{$parser->{'values'}}) {
3187+ # print STDERR " -> $value $parser->{'values'}->{$value}\n";
3188+ #}
3189+ # Now initialize command hash that are dynamically modified, notably
3190+ # those for index commands, and lists, based on defaults and user provided.
3191+ $parser->{'misc_commands'} = _deep_copy (\%misc_commands);
3192+ $parser->{'valid_nestings'} = _deep_copy (\%default_valid_nestings);
3193+ $parser->{'no_paragraph_commands'} = { %default_no_paragraph_commands };
3194+ $parser->{'index_names'} = _deep_copy (\%index_names);
3195+ $parser->{'command_index_prefix'} = {%command_index_prefix};
3196+ $parser->{'close_paragraph_commands'} = {%close_paragraph_commands};
3197+ $parser->{'close_preformatted_commands'} = {%close_preformatted_commands};
3198+ if ($parser->{'INLINE_INSERTCOPYING'}) {
3199+ delete $parser->{'close_paragraph_commands'}->{'insercopying'};
3200+ delete $parser->{'close_preformatted_commands'}->{'insercopying'};
3201+ }
3202+ # a hash is simply concatenated. It should be like %index_names.
3203+ if (ref($parser->{'indices'}) eq 'HASH') {
3204+ %{$parser->{'index_names'}} = (%{$parser->{'index_names'}},
3205+ %{$parser->{'indices'}});
3206+ } else { # an array holds index names defined with @defindex
3207+ foreach my $name (@{$parser->{'indices'}}) {
3208+ $parser->{'index_names'}->{$name} = {'in_code' => 0};
3209+ }
3210+ }
3211+ foreach my $index (keys (%{$parser->{'index_names'}})) {
3212+ $parser->_register_index_commands($index);
3213+ }
3214+ if ($parser->{'merged_indices'}) {
3215+ foreach my $index_from (keys (%{$parser->{'merged_indices'}})) {
3216+ my $index_to = $parser->{'merged_indices'}->{$index_from};
3217+ if (defined($parser->{'index_names'}->{$index_from})
3218+ and defined($parser->{'index_names'}->{$index_to})) {
3219+ $parser->{'index_names'}->{$index_from}->{'merged_in'} = $index_to;
3220+ $parser->{'index_names'}->{$index_to}->{'contained_indices'}->{$index_from} = 1;
3221+ }
3222+ }
3223+ }
3224+ foreach my $explained_command(keys(%explained_commands)) {
3225+ if (!defined($parser->{'explained_commands'}->{$explained_command})) {
3226+ $parser->{'explained_commands'}->{$explained_command} = {};
3227+ }
3228+ }
3229+ $parser->{'context_stack'} = [ $parser->{'context'} ];
3230+ $parser->{'regions_stack'} = [];
3231+ $parser->{'macro_stack'} = [];
3232+ $parser->{'conditionals_stack'} = [];
3233+ $parser->{'raw_formats_stack'} = [1];
3234+
3235+ # turn the array to a hash for speed. Not sure it really matters for such
3236+ # a small array.
3237+ foreach my $expanded_format(@{$parser->{'expanded_formats'}}) {
3238+ $parser->{'expanded_formats_hash'}->{$expanded_format} = 1;
3239+ }
3240+
3241+ %{$parser->{'global_commands'}} = %global_multiple_commands;
3242+
3243+ foreach my $global_command (@{$parser->{'GLOBAL_COMMANDS'}}) {
3244+ $parser->{'global_commands'}->{$global_command} = 1;
3245+ }
3246+
3247+ $parser->Texinfo::Report::new;
3248+
3249+ return $parser;
3250+}
3251+
3252+sub get_conf($$)
3253+{
3254+ my $self = shift;
3255+ my $var = shift;
3256+ return $self->{$var};
3257+}
3258+
3259+# split a scalar text in an array lines.
3260+sub _text_to_lines($)
3261+{
3262+ my $text = shift;
3263+ die if (!defined($text));
3264+ my $had_final_end_line = chomp($text);
3265+ my $lines = [ map {$_."\n"} split (/\n/, $text, -1) ];
3266+ $lines = [''] if (!@$lines);
3267+ chomp($lines->[-1]) unless ($had_final_end_line);
3268+ return $lines;
3269+}
3270+
3271+# construct a text fragments array matching a lines array, based on information
3272+# supplied.
3273+# If $fixed_line_number is set the line number is not increased, otherwise
3274+# it is increased, beginning at $first_line.
3275+sub _complete_line_nr($$;$$$)
3276+{
3277+ my $lines = shift;
3278+ my $first_line = shift;
3279+ my $file = shift;
3280+ my $macro = shift;
3281+ my $fixed_line_number = shift;
3282+
3283+ $macro = '' if (!defined($macro));
3284+ $file = '' if (!defined($file));
3285+ my $new_lines = [];
3286+
3287+ if (defined($first_line)) {
3288+ my $line_index = $first_line;
3289+ foreach my $index(0..scalar(@$lines)-1) {
3290+ $line_index = $index+$first_line if (!$fixed_line_number);
3291+ $new_lines->[$index] = [ $lines->[$index],
3292+ { 'line_nr' => $line_index,
3293+ 'file_name' => $file, 'macro' => $macro } ];
3294+ }
3295+ } else {
3296+ foreach my $line (@$lines) {
3297+ push @$new_lines, [ $line ];
3298+ }
3299+ }
3300+ return $new_lines;
3301+}
3302+
3303+# entry point for text fragments.
3304+# Used in tests.
3305+# Note that it has no associated root type a opposed to parse_texi_line
3306+# and parse_texi_file.
3307+sub parse_texi_text($$;$$$$)
3308+{
3309+ my $self = shift;
3310+ my $text = shift;
3311+ my $lines_nr = shift;
3312+ my $file = shift;
3313+ my $macro = shift;
3314+ my $fixed_line_number = shift;
3315+
3316+ return undef if (!defined($text));
3317+
3318+ my $lines_array = [];
3319+ if (!ref($text)) {
3320+ $text = _text_to_lines($text);
3321+ }
3322+ $lines_nr = [] if (!defined($lines_nr));
3323+ if (!ref($lines_nr)) {
3324+ #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
3325+ $lines_array = _complete_line_nr($text, $lines_nr, $file,
3326+ $macro, $fixed_line_number);
3327+ } else {
3328+ while (@$text) {
3329+ my $line_nr = shift @$lines_nr;
3330+ my $line = shift @$text;
3331+ push @$lines_array, [$line, $line_nr];
3332+ }
3333+ }
3334+
3335+ $self = parser() if (!defined($self));
3336+ $self->{'input'} = [{'pending' => $lines_array}];
3337+ my $tree = $self->_parse_texi();
3338+ return $tree;
3339+}
3340+
3341+# Not used for now, as a @contents after the first sectioning command
3342+# is correct if not using TeX.
3343+sub _check_contents_location($$)
3344+{
3345+ my $self = shift;
3346+ my $tree = shift;
3347+
3348+ my $commands = $self->global_commands_information();
3349+ return unless ($commands);
3350+ # Find the last sectioning command
3351+ my $index = -1;
3352+ my %ending_root_commands;
3353+ my $found = 0;
3354+ while ($tree->{'contents'}->[$index]) {
3355+ if (defined($tree->{'contents'}->[$index]->{'cmdname'})) {
3356+ $ending_root_commands{$tree->{'contents'}->[$index]} = 1;
3357+ if ($sectioning_commands{$tree->{'contents'}->[$index]->{'cmdname'}}) {
3358+ $found = 1;
3359+ last;
3360+ }
3361+ }
3362+ $index--;
3363+ }
3364+ return if (!$found);
3365+
3366+ #print STDERR "ending_root_commands ".join('|',keys(%ending_root_commands))."\n";
3367+ #print STDERR "tree contents: ".join('|', @{$tree->{'contents'}})."\n";
3368+ foreach my $command ('contents', 'shortcontents', 'summarycontents') {
3369+ if ($commands->{$command}) {
3370+ foreach my $current (@{$commands->{$command}}) {
3371+ my $root_command = $self->Texinfo::Common::find_parent_root_command($current);
3372+ #print STDERR "root_command for $current->{'cmdname'}: $root_command\n";
3373+ if (defined($root_command)
3374+ and !$ending_root_commands{$root_command}) {
3375+ $self->line_warn(sprintf($self->__(
3376+ "\@%s should only appear at beginning or end of document"),
3377+ $current->{'cmdname'}), $current->{'line_nr'});
3378+ }
3379+ }
3380+ }
3381+ }
3382+}
3383+
3384+# parse a texi file
3385+sub parse_texi_file($$)
3386+{
3387+ my $self = shift;
3388+ my $file_name = shift;
3389+
3390+ my $filehandle = do { local *FH };
3391+ if (! open($filehandle, $file_name)) {
3392+ $self->document_error(sprintf($self->__("could not open %s: %s"),
3393+ $file_name, $!));
3394+ return undef;
3395+ }
3396+ my $line_nr = 0;
3397+ my $line;
3398+ my @first_lines;
3399+
3400+ my $pending_first_texi_line;
3401+ # the first line not empty and not with \input is kept in
3402+ # $pending_first_texi_line and put in the pending lines just below
3403+ while ($line = <$filehandle>) {
3404+ $line_nr++;
3405+ if ($line =~ /^ *\\input/ or $line =~ /^\s*$/) {
3406+ $line =~ s/\x{7F}.*\s*//;
3407+ push @first_lines, $line;
3408+ } else {
3409+ $pending_first_texi_line = $line;
3410+ last;
3411+ }
3412+ }
3413+ my $root = { 'contents' => [], 'type' => 'text_root' };
3414+ if (@first_lines) {
3415+ push @{$root->{'contents'}}, { 'type' => 'preamble', 'contents' => [] };
3416+ foreach my $line (@first_lines) {
3417+ push @{$root->{'contents'}->[-1]->{'contents'}},
3418+ { 'text' => $line,
3419+ 'type' => 'preamble_text' };
3420+ }
3421+ }
3422+ my ($directories, $suffix);
3423+ ($file_name, $directories, $suffix) = fileparse($file_name)
3424+ if ($self->{'TEST'});
3425+ $self = parser() if (!defined($self));
3426+ $self->{'input'} = [{
3427+ 'pending' => [[$pending_first_texi_line, {'line_nr' => $line_nr,
3428+ 'macro' => '', 'file_name' => $file_name}]],
3429+ 'name' => $file_name,
3430+ 'line_nr' => $line_nr,
3431+ 'fh' => $filehandle
3432+ }];
3433+ $self->{'info'}->{'input_file_name'} = $file_name;
3434+ my $tree = $self->_parse_texi($root);
3435+
3436+ # Find 'text_root', which contains everything before first node/section.
3437+ # if there are elements, 'text_root' is the first content, otherwise it
3438+ # is the root.
3439+ my $text_root;
3440+ if ($tree->{'type'} eq 'text_root') {
3441+ $text_root = $tree;
3442+ } elsif ($tree->{'contents'} and $tree->{'contents'}->[0]
3443+ and $tree->{'contents'}->[0]->{'type'} eq 'text_root') {
3444+ $text_root = $tree->{'contents'}->[0];
3445+ }
3446+
3447+ # Put everything before @setfilename in a special type. This allows to
3448+ # ignore everything before @setfilename.
3449+ if ($self->{'IGNORE_BEFORE_SETFILENAME'} and $text_root and
3450+ $self->{'extra'} and $self->{'extra'}->{'setfilename'}
3451+ and $self->{'extra'}->{'setfilename'}->{'parent'} eq $text_root) {
3452+ my $before_setfilename = {'type' => 'preamble_before_setfilename',
3453+ 'parent' => $text_root,
3454+ 'contents' => []};
3455+ while ($text_root->{'contents'}->[0] ne $self->{'extra'}->{'setfilename'}) {
3456+ my $content = shift @{$text_root->{'contents'}};
3457+ $content->{'parent'} = $before_setfilename;
3458+ push @{$before_setfilename->{'contents'}}, $content;
3459+ }
3460+ unshift (@{$text_root->{'contents'}}, $before_setfilename)
3461+ if (@{$before_setfilename->{'contents'}});
3462+ }
3463+ #$self->_check_contents_location($tree);
3464+
3465+ return $tree;
3466+}
3467+
3468+sub parse_texi_line($$;$$$$)
3469+{
3470+ my $self = shift;
3471+ my $text = shift;
3472+ my $lines_nr = shift;
3473+ my $file = shift;
3474+ my $macro = shift;
3475+ my $fixed_line_number = shift;
3476+
3477+ return undef if (!defined($text));
3478+
3479+ if (!ref($text)) {
3480+ $text = _text_to_lines($text);
3481+ }
3482+ #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
3483+ my $lines_array = _complete_line_nr($text, $lines_nr, $file,
3484+ $macro, $fixed_line_number);
3485+
3486+ $self = parser() if (!defined($self));
3487+ $self->{'input'} = [{'pending' => $lines_array}];
3488+ my $tree = $self->_parse_texi({'contents' => [], 'type' => 'root_line'});
3489+ return $tree;
3490+}
3491+
3492+# return indices informations
3493+sub indices_information($)
3494+{
3495+ my $self = shift;
3496+ return ($self->{'index_names'}, $self->{'merged_indices'});
3497+ #return ($self->{'index_names'}, $self->{'merged_indices'}, $self->{'index_entries'});
3498+}
3499+
3500+sub floats_information($)
3501+{
3502+ my $self = shift;
3503+ return $self->{'floats'};
3504+}
3505+
3506+sub internal_references_information($)
3507+{
3508+ my $self = shift;
3509+ return $self->{'internal_references'};
3510+}
3511+
3512+sub global_commands_information($)
3513+{
3514+ my $self = shift;
3515+ return $self->{'extra'};
3516+}
3517+
3518+# @ dircategory_direntry
3519+# @ unassociated_menus
3520+# perl_encoding
3521+# input_encoding_name
3522+# input_file_name
3523+sub global_informations($)
3524+{
3525+ my $self = shift;
3526+ return $self->{'info'};
3527+}
3528+
3529+sub labels_information($)
3530+{
3531+ my $self = shift;
3532+ return $self->{'labels'};
3533+}
3534+
3535+# Following are the internal subroutines. The most important are
3536+# _parse_texi: the main parser loop.
3537+# _end_line: called at an end of line. Handling of @include lines is
3538+# done here.
3539+# _next_text: present the next text fragment, from pending text or line,
3540+# as described above.
3541+
3542+# for debugging
3543+sub _print_current($)
3544+{
3545+ my $current = shift;
3546+ return Texinfo::Common::_print_current($current);
3547+}
3548+
3549+# for debugging
3550+sub _print_command_args_texi($)
3551+{
3552+ my $current = shift;
3553+ return '' if (!$current->{'cmdname'});
3554+ my $args = '';
3555+ my $with_brace;
3556+ if ($current->{'args'} and @{$current->{'args'}}) {
3557+ $with_brace
3558+ = ($current->{'args'}->[0]->{'type'} eq 'brace_command_arg'
3559+ or $current->{'args'}->[0]->{'type'} eq 'brace_command_context');
3560+ $args .= '{' if ($with_brace);
3561+ foreach my $arg (@{$current->{'args'}}) {
3562+ $args .= Texinfo::Convert::Texinfo::convert($arg).', ';
3563+ }
3564+ $args =~ s/, $//;
3565+ }
3566+ chomp($args);
3567+ if ($with_brace) {
3568+ $args .= '}';
3569+ }
3570+ return '@'.$current->{'cmdname'} .$args."\n";
3571+}
3572+
3573+sub _print_current_keys($)
3574+{
3575+ my $current = shift;
3576+ my $string = _print_current($current);
3577+ foreach my $key (keys (%$current)) {
3578+ $string .= " $key: $current->{$key}\n";
3579+ }
3580+ if ($current->{'extra'}) {
3581+ $string .= " EXTRA\n";
3582+ foreach my $key (keys (%{$current->{'extra'}})) {
3583+ $string .= " $key: $current->{'extra'}->{$key}\n";
3584+ }
3585+ }
3586+ return $string;
3587+}
3588+
3589+# For debugging
3590+my @kept_keys = ('contents', 'cmdname', 'type', 'text', 'args');
3591+my %kept_keys;
3592+foreach my $key (@kept_keys) {
3593+ $kept_keys{$key} = 1;
3594+}
3595+sub _filter_print_keys { [grep {$kept_keys{$_}} ( sort keys %{$_[0]} )] };
3596+sub _print_tree($)
3597+{
3598+ my $tree = shift;
3599+ local $Data::Dumper::Sortkeys = \&_filter_print_keys;
3600+ local $Data::Dumper::Purity = 1;
3601+ local $Data::Dumper::Indent = 1;
3602+
3603+ return Data::Dumper->Dump([$tree]);
3604+}
3605+
3606+sub _register_global_command($$$$)
3607+{
3608+ my $self = shift;
3609+ my $command = shift;
3610+ my $current = shift;
3611+ my $line_nr = shift;
3612+ if ($command eq 'summarycontents' and !$self->{'global_commands'}->{$command}) {
3613+ $command = 'shortcontents';
3614+ }
3615+ if ($self->{'global_commands'}->{$command} and $command ne 'author') {
3616+ push @{$self->{'extra'}->{$command}}, $current
3617+ unless (_ignore_global_commands($self));
3618+ $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
3619+ return 1;
3620+ } elsif ($global_unique_commands{$command}) {
3621+ # setfilename ignored in an included file
3622+ $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
3623+ if ($command eq 'setfilename'
3624+ and scalar(@{$self->{'input'}}) > 1) {
3625+ } elsif (exists ($self->{'extra'}->{$current->{'cmdname'}})) {
3626+ $self->line_warn(sprintf($self->__('multiple @%s'),
3627+ $current->{'cmdname'}), $line_nr);
3628+ } else {
3629+ $self->{'extra'}->{$current->{'cmdname'}} = $current
3630+ unless (_ignore_global_commands($self));
3631+ }
3632+ return 1;
3633+ }
3634+ return 0;
3635+}
3636+
3637+# parse a @macro line
3638+sub _parse_macro_command_line($$$$$;$)
3639+{
3640+ my $self = shift;
3641+ my $command = shift;
3642+ my $line = shift;
3643+ my $parent = shift;
3644+ my $line_nr = shift;
3645+ my $macro = { 'cmdname' => $command, 'parent' => $parent, 'contents' => [],
3646+ 'extra' => {'arg_line' => $line}, 'line_nr' => $line_nr };
3647+ # REMACRO
3648+ if ($line =~ /^\s+([[:alnum:]][[:alnum:]-]*)\s*(.*)/) {
3649+ my $macro_name = $1;
3650+ my $args_def = $2;
3651+ my @args;
3652+
3653+ if ($args_def =~ s/^\s*{\s*(.*?)\s*}\s*//) {
3654+ @args = split(/\s*,\s*/, $1);
3655+ }
3656+
3657+ # accept an @-command after the arguments in case there is a @c or
3658+ # @comment
3659+ if ($args_def =~ /^\s*[^\@]/) {
3660+ $self->line_error(sprintf($self->__("bad syntax for \@%s argument: %s"),
3661+ $command, $args_def),
3662+ $line_nr);
3663+ $macro->{'extra'}->{'invalid_syntax'} = 1;
3664+ }
3665+ print STDERR "MACRO \@$command $macro_name\n" if ($self->{'DEBUG'});
3666+
3667+ $macro->{'args'} = [
3668+ { 'type' => 'macro_name', 'text' => $macro_name,
3669+ 'parent' => $macro } ];
3670+ my $index = 0;
3671+ foreach my $formal_arg (@args) {
3672+ push @{$macro->{'args'}},
3673+ { 'type' => 'macro_arg', 'text' => $formal_arg,
3674+ 'parent' => $macro};
3675+ if ($formal_arg !~ /^[\w\-]+$/) {
3676+ $self->line_error(sprintf($self->__("bad or empty \@%s formal argument: %s"),
3677+ $command, $formal_arg), $line_nr);
3678+ $macro->{'extra'}->{'invalid_syntax'} = 1;
3679+ }
3680+ $macro->{'extra'}->{'args_index'}->{$formal_arg} = $index;
3681+ $index++;
3682+ }
3683+ } elsif ($line !~ /\S/) {
3684+ $self->line_error(sprintf($self->
3685+ __("%c%s requires a name"), ord('@'), $command), $line_nr);
3686+ $macro->{'extra'}->{'invalid_syntax'} = 1;
3687+ } else {
3688+ $self->line_error(sprintf($self->
3689+ __("bad name for \@%s"), $command), $line_nr);
3690+ $macro->{'extra'}->{'invalid_syntax'} = 1;
3691+ }
3692+ return $macro;
3693+}
3694+
3695+# start a paragraph if in a context where paragraphs are to be started.
3696+sub _begin_paragraph($$;$)
3697+{
3698+ my $self = shift;
3699+ my $current = shift;
3700+ my $line_nr = shift;
3701+
3702+ if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
3703+ and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
3704+ if (!defined($current->{'contents'})) {
3705+ $self->_bug_message("contents undef", $line_nr, $current);
3706+ die;
3707+ }
3708+
3709+ # find whether an @indent precedes the paragraph
3710+ my $indent;
3711+ if (scalar(@{$current->{'contents'}})) {
3712+ my $index = scalar(@{$current->{'contents'}}) -1;
3713+ while ($index >= 0
3714+ and !($current->{'contents'}->[$index]->{'type'}
3715+ and ($current->{'contents'}->[$index]->{'type'} eq 'empty_line'
3716+ or $current->{'contents'}->[$index]->{'type'} eq 'paragraph'))
3717+ and !($current->{'contents'}->[$index]->{'cmdname'}
3718+ and $self->{'close_paragraph_commands'}->{$current->{'contents'}->[$index]->{'cmdname'}})) {
3719+ if ($current->{'contents'}->[$index]->{'cmdname'}
3720+ and ($current->{'contents'}->[$index]->{'cmdname'} eq 'indent'
3721+ or $current->{'contents'}->[$index]->{'cmdname'} eq 'noindent')) {
3722+ $indent = $current->{'contents'}->[$index]->{'cmdname'};
3723+ last;
3724+ }
3725+ $index--;
3726+ }
3727+ }
3728+ push @{$current->{'contents'}},
3729+ { 'type' => 'paragraph', 'parent' => $current, 'contents' => [] };
3730+ $current->{'contents'}->[-1]->{'extra'}->{$indent} = 1 if ($indent);
3731+ $current = $current->{'contents'}->[-1];
3732+ print STDERR "PARAGRAPH\n" if ($self->{'DEBUG'});
3733+ return $current;
3734+ }
3735+ return 0;
3736+}
3737+
3738+sub _begin_preformatted($)
3739+{
3740+ my $self = shift;
3741+ my $current = shift;
3742+ if ($preformatted_contexts{$self->{'context_stack'}->[-1]}) {
3743+ push @{$current->{'contents'}},
3744+ { 'type' => $self->{'context_stack'}->[-1],
3745+ 'parent' => $current, 'contents' => [] };
3746+ $current = $current->{'contents'}->[-1];
3747+ print STDERR "PREFORMATTED $self->{'context_stack'}->[-1]\n" if ($self->{'DEBUG'});
3748+ }
3749+ return $current;
3750+}
3751+
3752+# wrapper around line_warn. Set line_nr to be the line_nr of the command,
3753+# corresponding to the opening of the command. Call line_warn with
3754+# sprintf if needed.
3755+sub _command_warn($$$$;@)
3756+{
3757+ my $self = shift;
3758+ my $current = shift;
3759+ my $line_nr = shift;
3760+ my $message = shift;
3761+
3762+ if ($current->{'line_nr'}) {
3763+ $line_nr = $current->{'line_nr'};
3764+ }
3765+ if (@_) {
3766+ $self->line_warn(sprintf($message, @_), $line_nr);
3767+ } else {
3768+ $self->line_warn($message, $line_nr);
3769+ }
3770+}
3771+
3772+sub _command_error($$$$;@)
3773+{
3774+ my $self = shift;
3775+ my $current = shift;
3776+ my $line_nr = shift;
3777+ my $message = shift;
3778+
3779+ # use the beginning of the @-command for the error message
3780+ # line number if available.
3781+ # FIXME line_nr currently not registered for regular brace commands
3782+ if ($current->{'line_nr'}) {
3783+ $line_nr = $current->{'line_nr'};
3784+ }
3785+ if (@_) {
3786+ $self->line_error(sprintf($message, @_), $line_nr);
3787+ } else {
3788+ $self->line_error($message, $line_nr);
3789+ }
3790+}
3791+
3792+# currently doesn't do much more than
3793+# return $_[1]->{'parent'}
3794+sub _close_brace_command($$$;$$)
3795+{
3796+ my $self = shift;
3797+ my $current = shift;
3798+ my $line_nr = shift;
3799+ my $closed_command = shift;
3800+ my $interrupting_command = shift;
3801+
3802+ if ($current->{'cmdname'} ne 'verb' or $current->{'type'} eq '') {
3803+ if (defined($closed_command)) {
3804+ $self->_command_error($current, $line_nr,
3805+ $self->__("\@end %s seen before \@%s closing brace"),
3806+ $closed_command, $current->{'cmdname'});
3807+ } elsif (defined($interrupting_command)) {
3808+ $self->_command_error($current, $line_nr,
3809+ $self->__("\@%s seen before \@%s closing brace"),
3810+ $interrupting_command, $current->{'cmdname'});
3811+
3812+ } else {
3813+ $self->_command_error($current, $line_nr,
3814+ $self->__("%c%s missing close brace"), ord('@'), $current->{'cmdname'});
3815+ }
3816+ } else {
3817+ $self->_command_error($current, $line_nr,
3818+ $self->__("\@%s missing closing delimiter sequence: %s}"),
3819+ $current->{'cmdname'}, $current->{'type'});
3820+ }
3821+ $current = $current->{'parent'};
3822+ return $current;
3823+}
3824+
3825+sub _in_code($$)
3826+{
3827+ my $self = shift;
3828+ my $current = shift;
3829+
3830+ while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
3831+ and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
3832+ and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
3833+ return 1 if ($code_style_commands{$current->{'parent'}->{'cmdname'}});
3834+ $current = $current->{'parent'}->{'parent'};
3835+ }
3836+ return 0;
3837+}
3838+
3839+# close brace commands, that don't set a new context (ie @caption, @footnote)
3840+sub _close_all_style_commands($$$;$$)
3841+{
3842+ my $self = shift;
3843+ my $current = shift;
3844+ my $line_nr = shift;
3845+ my $closed_command = shift;
3846+ my $interrupting_command = shift;
3847+
3848+ while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
3849+ and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
3850+ and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
3851+ $current = _close_brace_command($self, $current->{'parent'}, $line_nr,
3852+ $closed_command, $interrupting_command);
3853+ }
3854+ return $current;
3855+}
3856+
3857+# close brace commands except for @caption, @footnote then the paragraph
3858+sub _end_paragraph($$$;$$)
3859+{
3860+ my $self = shift;
3861+ my $current = shift;
3862+ my $line_nr = shift;
3863+ my $closed_command = shift;
3864+ my $interrupting_command = shift;
3865+
3866+ $current = _close_all_style_commands($self, $current, $line_nr,
3867+ $closed_command, $interrupting_command);
3868+ if ($current->{'type'} and $current->{'type'} eq 'paragraph') {
3869+ print STDERR "CLOSE PARA\n" if ($self->{'DEBUG'});
3870+ $current = $current->{'parent'};
3871+ }
3872+ return $current;
3873+}
3874+
3875+# close brace commands except for @caption, @footnote then the preformatted
3876+sub _end_preformatted($$$;$$)
3877+{
3878+ my $self = shift;
3879+ my $current = shift;
3880+ my $line_nr = shift;
3881+ my $closed_command = shift;
3882+ my $interrupting_command = shift;
3883+
3884+ $current = _close_all_style_commands($self, $current, $line_nr,
3885+ $closed_command, $interrupting_command);
3886+ if ($current->{'type'} and $preformatted_contexts{$current->{'type'}}) {
3887+ print STDERR "CLOSE PREFORMATTED $current->{'type'}\n" if ($self->{'DEBUG'});
3888+ # completly remove void preformatted contexts
3889+ if (!@{$current->{'contents'}}) {
3890+ my $removed = pop @{$current->{'parent'}->{'contents'}};
3891+ print STDERR "popping $removed->{'type'}\n" if ($self->{'DEBUG'});
3892+ }
3893+ $current = $current->{'parent'};
3894+ }
3895+ return $current;
3896+}
3897+
3898+# check that there are no text holding environment (currently
3899+# checking only paragraphs and preformatted) in contents
3900+sub _check_no_text($)
3901+{
3902+ my $current = shift;
3903+ my $after_paragraph = 0;
3904+ foreach my $content (@{$current->{'contents'}}) {
3905+ if ($content->{'type'} and $content->{'type'} eq 'paragraph') {
3906+ $after_paragraph = 1;
3907+ last;
3908+ } elsif ($content->{'type'} and $preformatted_contexts{$content->{'type'}}) {
3909+ foreach my $preformatted_content (@{$content->{'contents'}}) {
3910+ if ((defined($preformatted_content->{'text'})
3911+ and $preformatted_content->{'text'} =~ /\S/)
3912+ or ($preformatted_content->{'cmdname'}
3913+ and ($preformatted_content->{'cmdname'} ne 'c'
3914+ and $preformatted_content->{'cmdname'} ne 'comment')
3915+ and !($preformatted_content->{'type'}
3916+ and $preformatted_content->{'type'} eq 'index_entry_command'))) {
3917+ $after_paragraph = 1;
3918+ last;
3919+ }
3920+ }
3921+ last if ($after_paragraph);
3922+ }
3923+ }
3924+ return $after_paragraph;
3925+}
3926+
3927+# put everything after the last @item/@itemx in an item_table type container
3928+# and distinguish table_term and table_entry.
3929+sub _gather_previous_item($$;$$)
3930+{
3931+ my $self = shift;
3932+ my $current = shift;
3933+ my $next_command = shift;
3934+ my $line_nr = shift;
3935+
3936+ # nothing to do in that case.
3937+ if ($current->{'contents'}->[-1]->{'type'}
3938+ and $current->{'contents'}->[-1]->{'type'} eq 'before_item') {
3939+ if ($next_command and $next_command eq 'itemx') {
3940+ $self->line_warn(sprintf($self->__("\@itemx should not begin \@%s"),
3941+ $current->{'cmdname'}), $line_nr);
3942+ }
3943+ return;
3944+ }
3945+ #print STDERR "GATHER "._print_current($current)."\n";
3946+ my $type;
3947+ # if before an itemx, the type is different since there should not be
3948+ # real content, so it may be treated differently
3949+ if ($next_command and $next_command eq 'itemx') {
3950+ $type = 'inter_item';
3951+ } else {
3952+ $type = 'table_item';
3953+ }
3954+ my $table_gathered = {'type' => $type,
3955+ 'contents' => []};
3956+ # remove everything that is not an @item/@items or before_item to
3957+ # put it in the table_item, starting from the end.
3958+ my $contents_count = scalar(@{$current->{'contents'}});
3959+ for (my $i = 0; $i < $contents_count; $i++) {
3960+ #print STDERR "_gather_previous_item $i on $contents_count: "._print_current($current->{'contents'}->[-1])."\n";
3961+ if ($current->{'contents'}->[-1]->{'cmdname'}
3962+ and ($current->{'contents'}->[-1]->{'cmdname'} eq 'item'
3963+ or ($current->{'contents'}->[-1]->{'cmdname'} eq 'itemx'))) {
3964+ last;
3965+ } else {
3966+ my $item_content = pop @{$current->{'contents'}};
3967+ $item_content->{'parent'} = $table_gathered;
3968+ unshift @{$table_gathered->{'contents'}}, $item_content;
3969+ }
3970+ }
3971+ if ($type eq 'table_item') {
3972+ my $table_entry = {'type' => 'table_entry',
3973+ 'parent' => $current,
3974+ 'contents' => []};
3975+ my $table_term = {'type' => 'table_term',
3976+ 'parent' => $table_entry,
3977+ 'contents' => []};
3978+ push @{$table_entry->{'contents'}}, $table_term;
3979+ my $contents_count = scalar(@{$current->{'contents'}});
3980+ for (my $i = 0; $i < $contents_count; $i++) {
3981+ if ($current->{'contents'}->[-1]->{'type'}
3982+ and ($current->{'contents'}->[-1]->{'type'} eq 'before_item'
3983+ or $current->{'contents'}->[-1]->{'type'} eq 'table_entry')) {
3984+ last;
3985+ } else {
3986+ my $item_content = pop @{$current->{'contents'}};
3987+ $item_content->{'parent'} = $table_term;
3988+ unshift @{$table_term->{'contents'}}, $item_content;
3989+ # debug
3990+ if (! (($item_content->{'cmdname'}
3991+ and ($item_content->{'cmdname'} eq 'itemx'
3992+ or $item_content->{'cmdname'} eq 'item'))
3993+ or ($item_content->{'type'}
3994+ and $item_content->{'type'} eq 'inter_item'))) {
3995+ $self->_bug_message("wrong element in table term", $line_nr,
3996+ $item_content);
3997+ }
3998+ }
3999+ }
4000+ push @{$current->{'contents'}}, $table_entry;
4001+ if (scalar(@{$table_gathered->{'contents'}})) {
4002+ push @{$table_entry->{'contents'}}, $table_gathered;
4003+ $table_gathered->{'parent'} = $table_entry;
4004+ }
4005+ } else {
4006+ my $after_paragraph = _check_no_text($table_gathered);
4007+ if ($after_paragraph) {
4008+ $self->line_error($self->__("\@itemx must follow \@item"), $line_nr);
4009+ }
4010+ if (scalar(@{$table_gathered->{'contents'}})) {
4011+ push @{$current->{'contents'}}, $table_gathered;
4012+ $table_gathered->{'parent'} = $current;
4013+ }
4014+ }
4015+}
4016+
4017+# Starting from the end, gather everything util the def_line to put in
4018+# a def_item
4019+sub _gather_def_item($;$)
4020+{
4021+ my $current = shift;
4022+ my $next_command = shift;
4023+ my $type;
4024+ # means that we are between a @def*x and a @def
4025+ if ($next_command) {
4026+ $type = 'inter_def_item';
4027+ } else {
4028+ $type = 'def_item';
4029+ }
4030+
4031+ # This may happen for a construct like
4032+ # @deffnx a b @section
4033+ # but otherwise the end of line will lead to the command closing
4034+ return if (!$current->{'cmdname'} or $current->{'cmdname'} =~ /x$/);
4035+ #print STDERR "_gather_def_item($type) in "._print_current($current)."\n";
4036+ my $def_item = {'type' => $type,
4037+ 'parent' => $current,
4038+ 'contents' => []};
4039+ # remove everything that is not a def_line to put it in the def_item,
4040+ # starting from the end.
4041+ my $contents_count = scalar(@{$current->{'contents'}});
4042+ for (my $i = 0; $i < $contents_count; $i++) {
4043+ #print STDERR "_gather_def_item $type ($i on $contents_count) "._print_current($current->{'contents'}->[-1])."\n";
4044+ if ($current->{'contents'}->[-1]->{'type'}
4045+ and $current->{'contents'}->[-1]->{'type'} eq 'def_line') {
4046+ # and !$current->{'contents'}->[-1]->{'extra'}->{'not_after_command'}) {
4047+ last;
4048+ } else {
4049+ my $item_content = pop @{$current->{'contents'}};
4050+ $item_content->{'parent'} = $def_item;
4051+ unshift @{$def_item->{'contents'}}, $item_content;
4052+ }
4053+ }
4054+ if (scalar(@{$def_item->{'contents'}})) {
4055+ push @{$current->{'contents'}}, $def_item;
4056+ }
4057+}
4058+
4059+# close formats
4060+sub _close_command_cleanup($$$) {
4061+ my $self = shift;
4062+ my $current = shift;
4063+
4064+ return unless ($current->{'cmdname'});
4065+ # remove the dynamic counters in multitable, they are not of use in the final
4066+ # tree. Also determine the multitable_body and multitable_head with
4067+ # @item or @headitem rows.
4068+ if ($current->{'cmdname'} eq 'multitable') {
4069+ my $in_head_or_rows;
4070+ my @contents = @{$current->{'contents'}};
4071+ $current->{'contents'} = [];
4072+ foreach my $row (@contents) {
4073+ if ($row->{'type'} and $row->{'type'} eq 'row') {
4074+ delete $row->{'cells_count'};
4075+ if ($row->{'contents'}->[0]->{'cmdname'} eq 'headitem') {
4076+ if (!$in_head_or_rows) {
4077+ push @{$current->{'contents'}}, {'type' => 'multitable_head',
4078+ 'parent' => $current};
4079+ $in_head_or_rows = 1;
4080+ }
4081+ } elsif ($row->{'contents'}->[0]->{'cmdname'} eq 'item') {
4082+ if (!defined($in_head_or_rows) or $in_head_or_rows) {
4083+ push @{$current->{'contents'}}, {'type' => 'multitable_body',
4084+ 'parent' => $current};
4085+ $in_head_or_rows = 0;
4086+ }
4087+ }
4088+ push @{$current->{'contents'}->[-1]->{'contents'}}, $row;
4089+ $row->{'parent'} = $current->{'contents'}->[-1];
4090+ } else {
4091+ push @{$current->{'contents'}}, $row;
4092+ $in_head_or_rows = undef;
4093+ }
4094+ }
4095+ delete $current->{'rows_count'};
4096+ } elsif ($item_container_commands{$current->{'cmdname'}}) {
4097+ delete $current->{'items_count'};
4098+ }
4099+
4100+ # put everything after the last @def*x command in a def_item type container.
4101+ if ($def_commands{$current->{'cmdname'}}) {
4102+ # At this point the end command hasn't been added to the command contents.
4103+ # so checks cannot be done at this point.
4104+ _gather_def_item($current);
4105+ }
4106+
4107+ if ($item_line_commands{$current->{'cmdname'}}) {
4108+ # At this point the end command hasn't been added to the command contents.
4109+ # so checks cannot be done at this point.
4110+ if (@{$current->{'contents'}}) {
4111+ $self->_gather_previous_item($current);
4112+ }
4113+ }
4114+
4115+ # put end out of before_item, and replace it at the end of the parent.
4116+ # remove empty before_item.
4117+ # warn if not empty before_item, but format is empty
4118+ if ($block_item_commands{$current->{'cmdname'}}) {
4119+ if (@{$current->{'contents'}}) {
4120+ my $leading_spaces = 0;
4121+ my $before_item;
4122+ if ($current->{'contents'}->[0]->{'type'}
4123+ and $current->{'contents'}->[0]->{'type'} eq 'empty_line_after_command'
4124+ and $current->{'contents'}->[1]
4125+ and $current->{'contents'}->[1]->{'type'}
4126+ and $current->{'contents'}->[1]->{'type'} eq 'before_item') {
4127+ $leading_spaces = 1;
4128+ $before_item = $current->{'contents'}->[1];
4129+ } elsif ($current->{'contents'}->[0]->{'type'}
4130+ and $current->{'contents'}->[0]->{'type'} eq 'before_item') {
4131+ $before_item = $current->{'contents'}->[0];
4132+ }
4133+ if ($before_item) {
4134+ if ($current->{'extra'}->{'end_command'}
4135+ and @{$before_item->{'contents'}}
4136+ and $before_item->{'contents'}->[-1] eq $current->{'extra'}->{'end_command'}) {
4137+ my $end = pop @{$before_item->{'contents'}};
4138+ $end->{'parent'} = $current;
4139+ push @{$current->{'contents'}}, $end;
4140+ }
4141+ # remove empty before_items
4142+ if (!@{$before_item->{'contents'}}) {
4143+ if ($leading_spaces) {
4144+ my $space = shift @{$current->{'contents'}};
4145+ shift @{$current->{'contents'}};
4146+ unshift @{$current->{'contents'}}, $space;
4147+ } else {
4148+ shift @{$current->{'contents'}};
4149+ }
4150+ } else {
4151+ # warn if not empty before_item, but format is empty
4152+ my $empty_before_item = 1;
4153+ foreach my $before_item_content (@{$before_item->{'contents'}}) {
4154+ if (!$before_item_content->{'cmdname'} or
4155+ ($before_item_content->{'cmdname'} ne 'c'
4156+ and $before_item_content->{'cmdname'} ne 'comment')) {
4157+ $empty_before_item = 0;
4158+ last;
4159+ }
4160+ }
4161+ if (!$empty_before_item) {
4162+ my $empty_format = 1;
4163+ foreach my $format_content (@{$current->{'contents'}}) {
4164+ next if ($format_content eq $before_item);
4165+ if (($format_content->{'cmdname'} and
4166+ ($format_content->{'cmdname'} ne 'c'
4167+ and $format_content->{'cmdname'} ne 'comment'
4168+ and $format_content->{'cmdname'} ne 'end'))
4169+ or ($format_content->{'type'} and
4170+ ($format_content->{'type'} ne 'empty_line_after_command'))) {
4171+ $empty_format = 0;
4172+ last;
4173+ }
4174+ }
4175+ if ($empty_format) {
4176+ $self->line_warn(sprintf($self->__("\@%s has text but no \@item"),
4177+ $current->{'cmdname'}), $current->{'line_nr'});
4178+ }
4179+ }
4180+ }
4181+ }
4182+ }
4183+ }
4184+}
4185+
4186+# close the current command, with error messages and give the parent.
4187+# If the last argument is given it is the command being closed if
4188+# hadn't there be an error, currently only block command, used for a
4189+# better error message.
4190+sub _close_current($$$;$$)
4191+{
4192+ my $self = shift;
4193+ my $current = shift;
4194+ my $line_nr = shift;
4195+ my $closed_command = shift;
4196+ my $interrupting_command = shift;
4197+
4198+ if ($current->{'cmdname'}) {
4199+ print STDERR "CLOSING(_close_current) \@$current->{'cmdname'}\n" if ($self->{'DEBUG'});
4200+ if (exists($brace_commands{$current->{'cmdname'}})) {
4201+ pop @{$self->{'context_stack'}}
4202+ if (exists $context_brace_commands{$current->{'cmdname'}});
4203+ $current = _close_brace_command($self, $current, $line_nr,
4204+ $closed_command, $interrupting_command);
4205+ } elsif (exists($block_commands{$current->{'cmdname'}})) {
4206+ if (defined($closed_command)) {
4207+ $self->line_error(sprintf($self->__("`\@end' expected `%s', but saw `%s'"),
4208+ $current->{'cmdname'}, $closed_command), $line_nr);
4209+ } elsif ($interrupting_command) {
4210+ $self->line_error(sprintf($self->__("\@%s seen before \@end %s"),
4211+ $interrupting_command, $current->{'cmdname'}),
4212+ $line_nr);
4213+ } else {
4214+ $self->line_error(sprintf($self->__("no matching `%cend %s'"),
4215+ ord('@'), $current->{'cmdname'}), $line_nr);
4216+ if ($block_commands{$current->{'cmdname'}} eq 'conditional') {
4217+ # in this case we are within an ignored conditional
4218+ my $conditional = pop @{$current->{'parent'}->{'contents'}};
4219+ }
4220+ }
4221+ if ($preformatted_commands{$current->{'cmdname'}}
4222+ or $menu_commands{$current->{'cmdname'}}
4223+ or $format_raw_commands{$current->{'cmdname'}}) {
4224+ my $context = pop @{$self->{'context_stack'}};
4225+ pop @{$self->{'raw_formats_stack'}}
4226+ if ($format_raw_commands{$current->{'cmdname'}});
4227+ }
4228+ pop @{$self->{'regions_stack'}}
4229+ if ($region_commands{$current->{'cmdname'}});
4230+ $current = $current->{'parent'};
4231+ } else {
4232+ # There @item and @tab commands are closed, and also line commands
4233+ # with invalid content
4234+ $current = $current->{'parent'};
4235+ }
4236+ } elsif ($current->{'type'}) {
4237+ print STDERR "CLOSING type $current->{'type'}\n" if ($self->{'DEBUG'});
4238+ if ($current->{'type'} eq 'bracketed') {
4239+ $self->_command_error($current, $line_nr,
4240+ $self->__("misplaced %c"), ord('{'));
4241+ } elsif ($current->{'type'} eq 'menu_comment'
4242+ or $current->{'type'} eq 'menu_entry_description') {
4243+ my $context = pop @{$self->{'context_stack'}};
4244+ if ($context ne 'preformatted') {
4245+ $self->_bug_message("context $context instead of preformatted",
4246+ $line_nr, $current);
4247+ }
4248+ # close empty menu_comment
4249+ if (!@{$current->{'contents'}}) {
4250+ pop @{$current->{'parent'}->{'contents'}};
4251+ }
4252+ } elsif ($current->{'type'} eq 'misc_line_arg'
4253+ or $current->{'type'} eq 'block_line_arg') {
4254+ my $context = pop @{$self->{'context_stack'}};
4255+ if ($context ne 'line' and $context ne 'def') {
4256+ $self->_bug_message("context $context instead of line or def",
4257+ $line_nr, $current);
4258+ die;
4259+ }
4260+ }
4261+ $current = $current->{'parent'};
4262+ } else { # Should never go here.
4263+ $current = $current->{'parent'} if ($current->{'parent'});
4264+ $self->_bug_message("No type nor cmdname when closing",
4265+ $line_nr, $current);
4266+ }
4267+ return $current;
4268+}
4269+
4270+# a closed_command arg means closing until that command is found.
4271+# no command arg means closing until the root or a root_command
4272+# is found.
4273+sub _close_commands($$$;$$)
4274+{
4275+ my $self = shift;
4276+ my $current = shift;
4277+ my $line_nr = shift;
4278+ my $closed_command = shift;
4279+ my $interrupting_command = shift;;
4280+
4281+ $current = _end_paragraph($self, $current, $line_nr, $closed_command,
4282+ $interrupting_command);
4283+ $current = _end_preformatted($self, $current, $line_nr, $closed_command,
4284+ $interrupting_command);
4285+
4286+ # stop if the command is found
4287+ while (!($closed_command and $current->{'cmdname'}
4288+ and $current->{'cmdname'} eq $closed_command)
4289+ # stop if at the root
4290+ and $current->{'parent'}
4291+ # stop if in a root command
4292+ # or in a context_brace_commands and searching for a specific
4293+ # end block command (with $closed_command set).
4294+ # This second condition means that a footnote is not closed when
4295+ # looking for the end of a block command, but is closed when
4296+ # completly closing the stack.
4297+ and !($current->{'cmdname'}
4298+ and ($root_commands{$current->{'cmdname'}}
4299+ or ($closed_command and $current->{'parent'}->{'cmdname'}
4300+ and $context_brace_commands{$current->{'parent'}->{'cmdname'}})))){
4301+ $self->_close_command_cleanup($current);
4302+ $current = $self->_close_current($current, $line_nr, $closed_command,
4303+ $interrupting_command);
4304+ }
4305+
4306+ my $closed_element;
4307+ if ($closed_command and $current->{'cmdname'}
4308+ and $current->{'cmdname'} eq $closed_command) {
4309+ if ($preformatted_commands{$current->{'cmdname'}}) {
4310+ my $context = pop @{$self->{'context_stack'}};
4311+ if ($context ne 'preformatted') {
4312+ $self->_bug_message("context $context instead of preformatted for $closed_command",
4313+ $line_nr, $current);
4314+ }
4315+ } elsif ($format_raw_commands{$current->{'cmdname'}}) {
4316+ my $context = pop @{$self->{'context_stack'}};
4317+ if ($context ne 'rawpreformatted') {
4318+ $self->_bug_message("context $context instead of rawpreformatted for $closed_command",
4319+ $line_nr, $current);
4320+ }
4321+ pop @{$self->{'raw_formats_stack'}};
4322+ } elsif ($menu_commands{$current->{'cmdname'}}) {
4323+ my $context = pop @{$self->{'context_stack'}};
4324+ # may be in menu, but context is preformatted if in a preformatted too.
4325+ if ($context ne 'menu' and $context ne 'preformatted') {
4326+ $self->_bug_message("context $context instead of preformatted or menu for $closed_command",
4327+ $line_nr, $current);
4328+ }
4329+ }
4330+ #print STDERR "close context $context for $current->{'cmdname'}\n"
4331+ # if ($self->{'DEBUG'});
4332+ pop @{$self->{'regions_stack'}}
4333+ if ($region_commands{$current->{'cmdname'}});
4334+ $closed_element = $current;
4335+ #$self->_close_command_cleanup($current);
4336+ $current = $current->{'parent'};
4337+ } elsif ($closed_command) {
4338+ $self->line_error(sprintf($self->__("unmatched `%c%s'"),
4339+ ord('@'), "end $closed_command"), $line_nr);
4340+ }
4341+ return ($closed_element, $current);
4342+}
4343+
4344+# begin paragraph if needed. If not try to merge with the previous
4345+# content if it is also some text.
4346+sub _merge_text($$$)
4347+{
4348+ my $self = shift;
4349+ my $current = shift;
4350+ my $text = shift;
4351+
4352+ my $paragraph;
4353+
4354+ my $no_merge_with_following_text = 0;
4355+ if ($text =~ /\S/) {
4356+ my $leading_spaces;
4357+ if ($text =~ /^(\s+)/) {
4358+ $leading_spaces = $1;
4359+ }
4360+ if ($current->{'contents'} and @{$current->{'contents'}}
4361+ and $current->{'contents'}->[-1]->{'type'}
4362+ and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
4363+ or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
4364+ or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
4365+ $no_merge_with_following_text = 1;
4366+ }
4367+ if (_abort_empty_line($self, $current, $leading_spaces)) {
4368+ $text =~ s/^(\s+)//;
4369+ }
4370+
4371+ $paragraph = _begin_paragraph($self, $current);
4372+ $current = $paragraph if ($paragraph);
4373+ }
4374+
4375+ if (!defined($current->{'contents'})) {
4376+ $self->_bug_message("No contents in _merge_text",
4377+ undef, $current);
4378+ die;
4379+ }
4380+
4381+ if (@{$current->{'contents'}}
4382+ and exists($current->{'contents'}->[-1]->{'text'})
4383+ and $current->{'contents'}->[-1]->{'text'} !~ /\n/
4384+ and !$no_merge_with_following_text) {
4385+ $current->{'contents'}->[-1]->{'text'} .= $text;
4386+ print STDERR "MERGED TEXT: $text|||\n" if ($self->{'DEBUG'});
4387+ } else {
4388+ push @{$current->{'contents'}}, { 'text' => $text, 'parent' => $current };
4389+ print STDERR "NEW TEXT: $text|||\n" if ($self->{'DEBUG'});
4390+ }
4391+ return $current;
4392+}
4393+
4394+# return the parent if in a item_container command, itemize or enumerate
4395+sub _item_container_parent($)
4396+{
4397+ my $current = shift;
4398+ if ((($current->{'cmdname'} and $current->{'cmdname'} eq 'item')
4399+ or ($current->{'type'} and $current->{'type'} eq 'before_item'))
4400+ and ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
4401+ and $item_container_commands{$current->{'parent'}->{'cmdname'}})) {
4402+ return ($current->{'parent'});
4403+ }
4404+ return undef;
4405+}
4406+
4407+# return the parent if in a item_line command, @*table
4408+sub _item_line_parent($)
4409+{
4410+ my $current = shift;
4411+ if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'item'
4412+ or $current->{'cmdname'} eq 'itemx')) {
4413+ $current = $current->{'parent'}->{'parent'};
4414+ } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
4415+ and $current->{'parent'}) {
4416+ $current = $current->{'parent'};
4417+ }
4418+ return $current if ($current->{'cmdname'}
4419+ and $item_line_commands{$current->{'cmdname'}});
4420+ return undef;
4421+}
4422+
4423+# return the parent if in a multitable
4424+sub _item_multitable_parent($)
4425+{
4426+ my $current = shift;
4427+ if (($current->{'cmdname'} and ($current->{'cmdname'} eq 'headitem'
4428+ or $current->{'cmdname'} eq 'item' or $current->{'cmdname'} eq 'tab'))
4429+ and $current->{'parent'} and $current->{'parent'}->{'parent'}) {
4430+ $current = $current->{'parent'}->{'parent'};
4431+ } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
4432+ and $current->{'parent'}) {
4433+ $current = $current->{'parent'};
4434+ }
4435+ return $current if ($current->{'cmdname'}
4436+ and $current->{'cmdname'} eq 'multitable');
4437+ return undef;
4438+}
4439+
4440+# returns next text fragment, be it pending from a macro expansion or
4441+# text or file
4442+sub _next_text($$$)
4443+{
4444+ my $self = shift;
4445+ my $line_nr = shift;
4446+ my $current = shift;
4447+
4448+ while (@{$self->{'input'}}) {
4449+ my $input = $self->{'input'}->[0];
4450+ if (@{$input->{'pending'}}) {
4451+ my $new_text = shift @{$input->{'pending'}};
4452+ if ($new_text->[1] and $new_text->[1]->{'end_macro'}) {
4453+ delete $new_text->[1]->{'end_macro'};
4454+ my $top_macro = shift @{$self->{'macro_stack'}};
4455+ print STDERR "SHIFT MACRO_STACK(@{$self->{'macro_stack'}}): $top_macro->{'args'}->[0]->{'text'}\n"
4456+ if ($self->{'DEBUG'});
4457+ }
4458+ return ($new_text->[0], $new_text->[1]);
4459+ } elsif ($input->{'fh'}) {
4460+ my $fh = $input->{'fh'};
4461+ my $line = <$fh>;
4462+ while (defined($line)) {
4463+ $line =~ s/\x{7F}.*\s*//;
4464+ if ($self->{'CPP_LINE_DIRECTIVES'}
4465+ # no cpp directives in ignored/macro/verbatim
4466+ and defined ($current)
4467+ and not
4468+ (($current->{'cmdname'}
4469+ and $block_commands{$current->{'cmdname'}}
4470+ and ($block_commands{$current->{'cmdname'}} eq 'raw'
4471+ or $block_commands{$current->{'cmdname'}} eq 'conditional'))
4472+ or
4473+ ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
4474+ and $current->{'parent'}->{'cmdname'} eq 'verb')
4475+ )
4476+ and $line =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/) {
4477+ $input->{'line_nr'} = $2;
4478+ if (defined($5)) {
4479+ $input->{'name'} = $5;
4480+ }
4481+ $line = <$fh>;
4482+ } else {
4483+ $input->{'line_nr'}++;
4484+ return ($line, {'line_nr' => $input->{'line_nr'},
4485+ 'file_name' => $input->{'name'},
4486+ 'macro' => ''});
4487+ }
4488+ }
4489+ }
4490+ my $previous_input = shift(@{$self->{'input'}});
4491+ # Don't close STDIN
4492+ if ($previous_input->{'fh'} and $previous_input->{'name'} ne '-') {
4493+ if (!close($previous_input->{'fh'})) {
4494+ $self->document_warn(sprintf($self->__("error on closing %s: %s"),
4495+ $previous_input->{'name'}, $!));
4496+
4497+ }
4498+ }
4499+ }
4500+
4501+ return (undef, $line_nr);
4502+}
4503+
4504+# collect text and line numbers until an end of line is found.
4505+sub _new_line($$$)
4506+{
4507+ my $self = shift;
4508+ my $line_nr = shift;
4509+ my $current = shift;
4510+ my $new_line = '';
4511+
4512+ while (1) {
4513+ my $new_text;
4514+ ($new_text, $line_nr) = _next_text($self, $line_nr, $current);
4515+ if (!defined($new_text)) {
4516+ $new_line = undef if ($new_line eq '');
4517+ last;
4518+ }
4519+
4520+ $new_line .= $new_text;
4521+
4522+ my $chomped_text = $new_text;
4523+ last if chomp($chomped_text);
4524+ }
4525+ return ($new_line, $line_nr);
4526+}
4527+
4528+sub _expand_macro_arguments($$$$)
4529+{
4530+ my $self = shift;
4531+ my $macro = shift;
4532+ my $line = shift;
4533+ my $line_nr = shift;
4534+ my $braces_level = 1;
4535+ my $arguments = [ '' ];
4536+ my $arg_nr = 0;
4537+ my $args_total = scalar(@{$macro->{'args'}}) -1;
4538+ my $name = $macro->{'args'}->[0]->{'text'};
4539+
4540+ my $line_nr_orig = $line_nr;
4541+
4542+ while (1) {
4543+ if ($line =~ s/([^\\{},]*)([\\{},])//) {
4544+ my $separator = $2;
4545+ $arguments->[-1] .= $1;
4546+ if ($separator eq '\\') {
4547+ if ($line =~ s/^(.)//) {
4548+ my $protected_char = $1;
4549+ if ($protected_char !~ /[\\{},]/) {
4550+ $arguments->[-1] .= '\\';
4551+ }
4552+ $arguments->[-1] .= $protected_char;
4553+
4554+ print STDERR "MACRO ARG: $separator: $protected_char\n" if ($self->{'DEBUG'});
4555+ } else {
4556+ $arguments->[-1] .= '\\';
4557+ print STDERR "MACRO ARG: $separator\n" if ($self->{'DEBUG'});
4558+ }
4559+ } elsif ($separator eq ',') {
4560+ if ($braces_level == 1) {
4561+ if (scalar(@$arguments) < $args_total) {
4562+ push @$arguments, '';
4563+ $line =~ s/^\s*//;
4564+ print STDERR "MACRO NEW ARG\n" if ($self->{'DEBUG'});
4565+ } else {
4566+ # implicit quoting when there is one argument.
4567+ if ($args_total != 1) {
4568+ $self->line_error(sprintf($self->__(
4569+ "macro `%s' called with too many args"),
4570+ $name), $line_nr);
4571+ }
4572+ $arguments->[-1] .= ',';
4573+ }
4574+ } else {
4575+ $arguments->[-1] .= ',';
4576+ }
4577+ } elsif ($separator eq '}') {
4578+ $braces_level--;
4579+ last if ($braces_level == 0);
4580+ $arguments->[-1] .= $separator;
4581+ } elsif ($separator eq '{') {
4582+ $braces_level++;
4583+ $arguments->[-1] .= $separator;
4584+ }
4585+ } else {
4586+ print STDERR "MACRO ARG end of line\n" if ($self->{'DEBUG'});
4587+ $arguments->[-1] .= $line;
4588+
4589+ ($line, $line_nr) = _new_line($self, $line_nr, $macro);
4590+ if (!defined($line)) {
4591+ $self->line_error(sprintf($self->__("\@%s missing close brace"),
4592+ $name), $line_nr_orig);
4593+ return ($arguments, "\n", $line_nr);
4594+ }
4595+ }
4596+ }
4597+ if ($args_total == 0 and $arguments->[0] =~ /\S/) {
4598+ $self->line_error(sprintf($self->__(
4599+ "macro `%s' declared without argument called with an argument"),
4600+ $name), $line_nr);
4601+ }
4602+ print STDERR "END MACRO ARGS EXPANSION(".scalar(@$arguments)."): ".
4603+ join("|\n", @$arguments) ."|\n" if ($self->{'DEBUG'});
4604+ return ($arguments, $line, $line_nr);
4605+}
4606+
4607+sub _expand_macro_body($$$$) {
4608+ my $self = shift;
4609+ my $macro = shift;
4610+ my $args = shift;
4611+ my $line_nr = shift;
4612+
4613+ my $macrobody = $macro->{'extra'}->{'macrobody'};
4614+ my $args_total = scalar(@{$macro->{'args'}}) -1;
4615+ my $args_index = $macro->{'extra'}->{'args_index'};
4616+
4617+ my $i;
4618+ for ($i=0; $i<=$args_total; $i++) {
4619+ $args->[$i] = "" unless (defined($args->[$i]));
4620+ }
4621+
4622+ my $result = '';
4623+ while ($macrobody ne '') {
4624+ if ($macrobody =~ s/^([^\\]*)\\//o) {
4625+ $result .= $1;
4626+ if ($macrobody =~ s/^\\//) {
4627+ $result .= '\\';
4628+ } elsif ($macrobody =~ s/^([^\\]*)\\//) {
4629+ my $arg = $1;
4630+ if (defined($args_index->{$arg})) {
4631+ $result .= $args->[$args_index->{$arg}];
4632+ } else {
4633+ $self->line_error(sprintf($self->__(
4634+ "\\ in \@%s expansion followed `%s' instead of parameter name or \\"),
4635+ $macro->{'args'}->[0]->{'text'}, $arg), $line_nr);
4636+ $result .= '\\' . $arg;
4637+ }
4638+ }
4639+ next;
4640+ }
4641+ $result .= $macrobody;
4642+ last;
4643+ }
4644+ return $result;
4645+}
4646+
4647+# each time a new line appeared, a container is opened to hold the text
4648+# consisting only of spaces. This container is removed here, typically
4649+# this is called when non-space happens on a line.
4650+sub _abort_empty_line($$;$)
4651+{
4652+ my $self = shift;
4653+ my $current = shift;
4654+ my $additional_text = shift;
4655+ $additional_text = '' if (!defined($additional_text));
4656+ if ($current->{'contents'} and @{$current->{'contents'}}
4657+ and $current->{'contents'}->[-1]->{'type'}
4658+ and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line'
4659+ or $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
4660+ or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
4661+ or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
4662+ print STDERR "ABORT EMPTY additional text |$additional_text|, current |$current->{'contents'}->[-1]->{'text'}|\n" if ($self->{'DEBUG'});
4663+ $current->{'contents'}->[-1]->{'text'} .= $additional_text;
4664+ if ($current->{'contents'}->[-1]->{'text'} eq '') {
4665+ if ($current->{'extra'}
4666+ and $current->{'extra'}->{'spaces_before_argument'}
4667+ and $current->{'extra'}->{'spaces_before_argument'}
4668+ eq $current->{'contents'}->[-1]) {
4669+ delete ($current->{'extra'}->{'spaces_before_argument'});
4670+ delete ($current->{'extra'}) if !(keys(%{$current->{'extra'}}));
4671+ }
4672+ pop @{$current->{'contents'}}
4673+ } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line') {
4674+ # exactly the same condition than to begin a paragraph
4675+ if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
4676+ and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
4677+ $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_before_paragraph';
4678+ } else {
4679+ delete $current->{'contents'}->[-1]->{'type'};
4680+ }
4681+ } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command') {
4682+ $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_after_command';
4683+ }
4684+ return 1;
4685+ }
4686+ return 0;
4687+}
4688+
4689+# isolate last space in a command to help expansion disregard unuseful spaces.
4690+sub _isolate_last_space($$;$)
4691+{
4692+ my $self = shift;
4693+ my $current = shift;
4694+ my $type = shift;
4695+ $type = 'spaces_at_end' if (!defined($type));
4696+ if ($current->{'contents'} and @{$current->{'contents'}}) {
4697+ my $index = -1;
4698+ $index = -2
4699+ if (scalar(@{$current->{'contents'}}) > 1
4700+ and $current->{'contents'}->[-1]->{'cmdname'}
4701+ and $self->{'misc_commands'}->{$current->{'contents'}->[-1]->{'cmdname'}});
4702+ if (defined($current->{'contents'}->[$index]->{'text'})
4703+ and !$current->{'contents'}->[$index]->{'type'}
4704+ and $current->{'contents'}->[$index]->{'text'} =~ /\s+$/) {
4705+ if ($current->{'contents'}->[$index]->{'text'} !~ /\S/) {
4706+ $current->{'contents'}->[$index]->{'type'} = $type;
4707+ } else {
4708+ $current->{'contents'}->[$index]->{'text'} =~ s/(\s+)$//;
4709+ my $spaces = $1;
4710+ my $new_spaces = { 'text' => $spaces, 'parent' => $current,
4711+ 'type' => $type };
4712+ if ($index == -1) {
4713+ push @{$current->{'contents'}}, $new_spaces;
4714+ } else {
4715+ splice (@{$current->{'contents'}}, $index+1, 0, $new_spaces);
4716+ }
4717+ }
4718+ }
4719+ }
4720+}
4721+
4722+# used to put a node name in error messages.
4723+sub _node_extra_to_texi($)
4724+{
4725+ my $node = shift;
4726+ my $result = '';
4727+ if ($node->{'manual_content'}) {
4728+ $result = '('.Texinfo::Convert::Texinfo::convert({'contents'
4729+ => $node->{'manual_content'}}) .')';
4730+ }
4731+ if ($node->{'node_content'}) {
4732+ $result .= Texinfo::Convert::Texinfo::convert ({'contents'
4733+ => $node->{'node_content'}});
4734+ }
4735+ return $result;
4736+}
4737+
4738+sub _find_end_brace($$)
4739+{
4740+ my $text = shift;
4741+ my $braces_count = shift;
4742+
4743+ my $before = '';
4744+ while ($braces_count > 0 and length($text)) {
4745+ if ($text =~ s/([^()]*)([()])//) {
4746+ $before .= $1.$2;
4747+ my $brace = $2;
4748+ if ($brace eq '(') {
4749+ $braces_count++;
4750+ } else {
4751+ $braces_count--;
4752+ if ($braces_count == 0) {
4753+ return ($before, $text, 0);
4754+ }
4755+ }
4756+ } else {
4757+ $before .= $text;
4758+ $text = '';
4759+ }
4760+ }
4761+ return ($before, undef, $braces_count);
4762+}
4763+
4764+# This only counts opening braces, and returns 0 once all the parentheses
4765+# are closed
4766+sub _count_opened_tree_braces($$);
4767+sub _count_opened_tree_braces($$)
4768+{
4769+ my $current = shift;
4770+ my $braces_count = shift;
4771+ if (defined($current->{'text'})) {
4772+ my ($before, $after);
4773+ ($before, $after, $braces_count) = _find_end_brace($current->{'text'},
4774+ $braces_count);
4775+ }
4776+ if ($current->{'args'}) {
4777+ foreach my $arg (@{$current->{'args'}}) {
4778+ $braces_count = _count_opened_tree_braces($arg, $braces_count);
4779+ return $braces_count if ($braces_count == 0);
4780+ }
4781+ }
4782+ if ($current->{'contents'}) {
4783+ foreach my $content (@{$current->{'contents'}}) {
4784+ $braces_count = _count_opened_tree_braces($content, $braces_count);
4785+ return $braces_count if ($braces_count == 0);
4786+ }
4787+ }
4788+ return $braces_count;
4789+}
4790+
4791+# retrieve a leading manual name in parentheses, if there is one.
4792+sub _parse_node_manual($)
4793+{
4794+ my $node = shift;
4795+ my @contents = @{$node->{'contents'}};
4796+ _trim_spaces_comment_from_content(\@contents);
4797+
4798+ my $manual;
4799+ my $result;
4800+#print STDERR "RRR $contents[0] and $contents[0]->{'text'} \n";
4801+ if ($contents[0] and $contents[0]->{'text'} and $contents[0]->{'text'} =~ /^\(/) {
4802+ my $braces_count = 1;
4803+ if ($contents[0]->{'text'} !~ /^\($/) {
4804+ my $brace = shift @contents;
4805+ my $brace_text = $brace->{'text'};
4806+ $brace_text =~ s/^\(//;
4807+ unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'},
4808+ 'parent' => $brace->{'parent'} } if $brace_text ne '';
4809+ } else {
4810+ shift @contents;
4811+ }
4812+ while(@contents) {
4813+ my $content = shift @contents;
4814+ if (!defined($content->{'text'}) or $content->{'text'} !~ /\)/) {
4815+ push @$manual, $content;
4816+ $braces_count = _count_opened_tree_braces($content, $braces_count);
4817+ # This is an error, braces were closed in a command
4818+ if ($braces_count == 0) {
4819+ last;
4820+ }
4821+ } else {
4822+ my ($before, $after);
4823+ ($before, $after, $braces_count) = _find_end_brace($content->{'text'},
4824+ $braces_count);
4825+ if ($braces_count == 0) {
4826+ $before =~ s/\)$//;
4827+ push @$manual, { 'text' => $before, 'parent' => $content->{'parent'} }
4828+ if ($before ne '');
4829+ $after =~ s/^\s*//;
4830+ unshift @contents, { 'text' => $after, 'parent' => $content->{'parent'} }
4831+ if ($after ne '');
4832+ last;
4833+ } else {
4834+ push @$manual, $content;
4835+ }
4836+ }
4837+ }
4838+ $result->{'manual_content'} = $manual if (defined($manual));
4839+ }
4840+ if (@contents) {
4841+ $result->{'node_content'} = \@contents;
4842+ $result->{'normalized'} =
4843+ Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => \@contents});
4844+ }
4845+ return $result;
4846+}
4847+
4848+sub _parse_float_type($)
4849+{
4850+ my $current = shift;
4851+ if (@{$current->{'args'}}) {
4852+ my @type_contents = @{$current->{'args'}->[0]->{'contents'}};
4853+ _trim_spaces_comment_from_content(\@type_contents);
4854+ if (@type_contents) {
4855+ my $normalized
4856+ = Texinfo::Convert::NodeNameNormalization::normalize_node(
4857+ {'contents' => \@type_contents});
4858+ $current->{'extra'}->{'type'}->{'content'} = \@type_contents;
4859+ if ($normalized =~ /[^-]/) {
4860+ $current->{'extra'}->{'type'}->{'normalized'} = $normalized;
4861+ return 1;
4862+ }
4863+ }
4864+ }
4865+ $current->{'extra'}->{'type'}->{'normalized'} = '';
4866+ return 0;
4867+}
4868+
4869+# used for definition line parsing
4870+sub _next_bracketed_or_word($$)
4871+{
4872+ my $self = shift;
4873+ my $contents = shift;
4874+ return undef if (!scalar(@{$contents}));
4875+ my $spaces;
4876+ $spaces = shift @{$contents} if (defined($contents->[0]->{'text'}) and
4877+ $contents->[0]->{'text'} !~ /\S/);
4878+ if (defined($spaces)) {
4879+ $spaces->{'type'} = 'spaces';
4880+ chomp $spaces->{'text'};
4881+ $spaces = undef if ($spaces->{'text'} eq '');
4882+ }
4883+ return ($spaces, undef) if (!scalar(@{$contents}));
4884+
4885+ #print STDERR "BEFORE PROCESSING ".Texinfo::Convert::Texinfo::convert({'contents' => $contents});
4886+ if ($contents->[0]->{'type'} and $contents->[0]->{'type'} eq 'bracketed') {
4887+ #print STDERR "Return bracketed\n";
4888+ my $bracketed = shift @{$contents};
4889+ $self->_isolate_last_space($bracketed, 'empty_space_at_end_def_bracketed');
4890+ my $bracketed_def_content = { 'contents' => $bracketed->{'contents'},
4891+ 'parent' => $bracketed->{'parent'},
4892+ 'type' => 'bracketed_def_content', };
4893+ if ($bracketed->{'extra'} and $bracketed->{'extra'}->{'spaces_before_argument'}) {
4894+ $bracketed_def_content->{'extra'}->{'spaces_before_argument'}
4895+ = $bracketed->{'extra'}->{'spaces_before_argument'};
4896+ }
4897+ return ($spaces, $bracketed_def_content);
4898+ } elsif ($contents->[0]->{'cmdname'}) {
4899+ #print STDERR "Return command $contents->[0]->{'cmdname'}\n";
4900+ return ($spaces, shift @{$contents});
4901+ } else {
4902+ #print STDERR "Process $contents->[0]->{'text'}\n";
4903+ $contents->[0]->{'text'} =~ s/^(\s*)//;
4904+ my $space_text = $1;
4905+ $spaces = {'text' => $space_text, 'type' => 'spaces'} if ($space_text);
4906+ $contents->[0]->{'text'} =~ s/^(\S+)//;
4907+ shift @{$contents} if ($contents->[0]->{'text'} eq '');
4908+ return ($spaces, {'text' => $1});
4909+ }
4910+}
4911+
4912+# definition line parsing
4913+sub _parse_def($$$)
4914+{
4915+ my $self = shift;
4916+ my $command = shift;
4917+ my $contents = shift;
4918+
4919+ my @contents = @$contents;
4920+ shift @contents if ($contents[0] and $contents[0]->{'type'}
4921+ and $contents[0]->{'type'} eq 'empty_spaces_after_command');
4922+ if ($def_aliases{$command}) {
4923+ my $real_command = $def_aliases{$command};
4924+ my $prepended = $def_map{$command}->{$real_command};
4925+ my @prepended_content;
4926+
4927+ my $text;
4928+ my $in_bracketed;
4929+ if ($prepended =~ /^\{/) {
4930+ $text = $prepended;
4931+ $text =~ s/\{([^\}]+)\}/$1/;
4932+ $in_bracketed = 1;
4933+ } else {
4934+ $text = $prepended;
4935+ }
4936+ my $tree = $self->gdt($text);
4937+ if ($in_bracketed or @{$tree->{'contents'}} > 1) {
4938+ my $bracketed = { 'type' => 'bracketed' };
4939+ $bracketed->{'contents'} = $tree->{'contents'};
4940+ foreach my $content (@{$tree->{'contents'}}) {
4941+ $content->{'parent'} = $bracketed;
4942+ }
4943+ @prepended_content = ($bracketed);
4944+ } else {
4945+ @prepended_content = (@{$tree->{'contents'}});
4946+ }
4947+ push @prepended_content, { 'text' => ' ' };
4948+
4949+ unshift @contents, @prepended_content;
4950+
4951+ $command = $def_aliases{$command};
4952+ }
4953+ foreach (my $i = 0; $i < scalar(@contents); $i++) {
4954+ # copy, to avoid changing the original
4955+ $contents[$i] = {'text' => $contents[$i]->{'text'}}
4956+ if (defined($contents[$i]->{'text'}));
4957+ }
4958+ my @result;
4959+ my @args = @{$def_map{$command}};
4960+ my $arg_type;
4961+ # Even when $arg_type is not set, that is for def* that is not documented
4962+ # to take args, everything is as is arg_type was set to arg.
4963+ $arg_type = pop @args if ($args[-1] eq 'arg' or $args[-1] eq 'argtype');
4964+ foreach my $arg (@args) {
4965+ #print STDERR "$command $arg"._print_current($contents[0]);
4966+ #foreach my $content (@contents) {print STDERR " "._print_current($content)};
4967+ #print STDERR " contents ->".Texinfo::Convert::Texinfo::convert ({'contents' => \@contents});
4968+ my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
4969+ last if (!defined($next));
4970+ #print STDERR "NEXT[$arg] ".Texinfo::Convert::Texinfo::convert($next)."\n";
4971+ push @result, ['spaces', $spaces] if (defined($spaces));
4972+ push @result, [$arg, $next];
4973+ }
4974+
4975+ my @args_results;
4976+ while (@contents) {
4977+ my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
4978+ push @args_results, ['spaces', $spaces] if (defined($spaces));
4979+ last if (!defined($next));
4980+ if (defined($next->{'text'})) {
4981+ while (1) {
4982+ if ($next->{'text'} =~ s/^([^\[\](),]+)//) {
4983+ push @args_results, ['arg', {'text' => $1}];
4984+ } elsif ($next->{'text'} =~ s/^([\[\](),])//) {
4985+ push @args_results, ['delimiter',
4986+ {'text' => $1, 'type' => 'delimiter'}];
4987+ } else {
4988+ last;
4989+ }
4990+ }
4991+ } else {
4992+ push @args_results, [ 'arg', $next ];
4993+ }
4994+ }
4995+ if ($arg_type and $arg_type eq 'argtype') {
4996+ my $next_is_type = 1;
4997+ foreach my $arg(@args_results) {
4998+ if ($arg->[0] eq 'spaces') {
4999+ } elsif ($arg->[0] eq 'delimiter') {
5000+ $next_is_type = 1;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches