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
=== modified file '.pc/applied-patches'
--- .pc/applied-patches 2013-01-20 20:25:10 +0000
+++ .pc/applied-patches 2013-07-14 15:17:24 +0000
@@ -3,3 +3,4 @@
3numerical-signal-names3numerical-signal-names
4info_universal_argument4info_universal_argument
5dont_build_info5dont_build_info
6maybe-upstream-fix-itemize-start
67
=== added directory '.pc/maybe-upstream-fix-itemize-start'
=== added directory '.pc/maybe-upstream-fix-itemize-start/tp'
=== added directory '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo'
=== added file '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm'
--- .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm 1970-01-01 00:00:00 +0000
+++ .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Common.pm 2013-07-14 15:17:24 +0000
@@ -0,0 +1,2530 @@
1# Common.pm: definition of commands. Common code of other Texinfo modules.
2#
3# Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License,
8# or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# Original author: Patrice Dumas <pertusus@free.fr>
19# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
20
21package Texinfo::Common;
22
23use strict;
24
25# for unicode/layer support in binmode
26use 5.006;
27
28# to determine the null file
29use Config;
30use File::Spec;
31
32use Texinfo::Documentlanguages;
33
34# debugging
35use Carp qw(cluck);
36
37require Exporter;
38use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
39@ISA = qw(Exporter);
40
41# Items to export into callers namespace by default. Note: do not export
42# names by default without a very good reason. Use EXPORT_OK instead.
43# Do not simply export all your public functions/methods/constants.
44
45# This allows declaration use Texinfo::Covert::Text ':all';
46# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
47# will save memory.
48%EXPORT_TAGS = ( 'all' => [ qw(
49expand_verbatiminclude
50definition_category
51expand_today
52numbered_heading
53trim_spaces_comment_from_content
54float_name_caption
55normalize_top_node_name
56protect_comma_in_tree
57protect_first_parenthesis
58protect_hashchar_at_line_beginning
59protect_colon_in_tree
60protect_node_after_label_in_tree
61valid_tree_transformation
62move_index_entries_after_items_in_tree
63) ] );
64
65@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
66
67@EXPORT = qw(
68);
69
70$VERSION = '5.0';
71
72# i18n
73sub N__($)
74{
75 return $_[0];
76}
77
78# determine the null devices
79my $default_null_device = File::Spec->devnull();
80our %null_device_file = (
81 $default_null_device => 1
82);
83# special case, djgpp recognizes both null devices
84if ($Config{osname} eq 'dos' and $Config{osvers} eq 'djgpp') {
85 $null_device_file{'/dev/null'} = 1;
86 $null_device_file{'NUL'} = 1;
87}
88
89# these are the default values for the parser state that may be
90# initialized to values given by the user.
91# They are defined here, because they are used below and we
92# don't want Texinfo::Common to use Texinfo::Parser.
93our %default_parser_state_configuration = (
94 # this is the initial context. It is put at the bottom of the
95 # 'context_stack'. It is not clear if this is really useful to be
96 # able to customize that value.
97 'context' => '_root',
98 'expanded_formats' => [],
99 'gettext' => sub {return $_[0];},
100 'pgettext' => sub {return $_[1];},
101 'include_directories' => [ '.' ],
102 # these are the user-added indices. May be an array reference on names
103 # or an hash reference in the same format than %index_names below
104 'indices' => [],
105 # the following are dynamically modified during the document parsing.
106 'aliases' => {}, # key is a command name value is the alias
107 'clickstyle' => 'arrow',
108 'documentlanguage' => undef,
109 # Current documentlanguage set by
110 # @documentlanguage
111 'explained_commands' => {}, # the key is a command name, either acronym
112 # or abbr, the value is a hash. The key hash
113 # is a normalized first argument of the
114 # corresponding command, the value is the
115 # contents array of the previous command with
116 # this first arg and a second arg.
117 'kbdinputstyle' => 'distinct',
118 'labels' => {}, # keys are normalized label names, as described
119 # in the `HTML Xref' node. Value should be
120 # a node/anchor or float in the tree.
121 'macros' => {}, # the key is the user-defined macro name. The
122 # value is the reference on a macro element
123 # as obtained by parsing the @macro
124 'merged_indices' => {}, # the key is merged in the value
125 'novalidate' => 0, # same as setting @novalidate.
126 'sections_level' => 0, # modified by raise/lowersections
127 'values' => {'txicommandconditionals' => 1},
128 # the key is the name, the value the @set name
129 # argument. A Texinfo tree may also be used.
130);
131
132# command-line options
133#my @command_line_settable_at_commands = ('footnotestyle', 'novalidate',
134# 'documentlanguage', 'paragraphindent');
135
136
137# FIXME maybe this should better be set as texinfo passed to texi2any as
138# texi2dvi --command
139
140# customization options
141our %document_settable_at_commands = (
142 'allowcodebreaks' => 'true',
143 'clickstyle' => '@arrow',
144 'codequotebacktick' => 'off',
145 'codequoteundirected' => 'off',
146 'contents' => 0,
147 'deftypefnnewline' => 'off',
148 'documentencoding' => 'us-ascii',
149 'documentlanguage' => 'en',
150 # is N ems in TeX, 0.4 in.
151 'exampleindent' => 5,
152 'firstparagraphindent' => 'none',
153 'frenchspacing' => 'off',
154 'headings' => 'on',
155 'kbdinputstyle' => 'distinct',
156 'paragraphindent' => 3,
157 'shortcontents' => 0,
158 'urefbreakstyle' => 'after',
159 'xrefautomaticsectiontitle' => 'off',
160);
161
162# those should be unique
163our %document_settable_unique_at_commands = (
164 # when passed through a configuration variable, documentdescription
165 # should be already formatted for HTML
166 'documentdescription' => undef,
167 'evenfootingmarks' => undef,
168 'evenheadingmarks' => undef,
169 'everyfootingmarks' => 'bottom',
170 'everyheadingmarks' => 'bottom',
171 'fonttextsize' => 11,
172 'footnotestyle' => 'end',
173 'novalidate' => 0,
174 'oddfootingmarks' => undef,
175 'oddheadingmarks' => undef,
176 # FIXME not clear here.
177 'pagesizes' => undef,
178 'setchapternewpage' => 'on',
179 'setcontentsaftertitlepage' => 0,
180 'setfilename' => undef,
181 'setshortcontentsaftertitlepage' => 0,
182 'everyheading' => undef,
183 'everyfooting' => undef,
184 'evenheading' => undef,
185 'evenfooting' => undef,
186 'oddheading' => undef,
187 'oddfooting' => undef,
188);
189
190my @command_line_settables = ('FILLCOLUMN', 'SPLIT', 'SPLIT_SIZE',
191 'HEADERS',
192 'MACRO_EXPAND', 'NUMBER_SECTIONS',
193 'NUMBER_FOOTNOTES', 'NODE_FILES',
194 'NO_WARN', 'VERBOSE',
195 'TRANSLITERATE_FILE_NAMES', 'ERROR_LIMIT', 'ENABLE_ENCODING',
196 'FORCE', 'INTERNAL_LINKS', 'OUTFILE', 'SUBDIR', 'OUT',
197 'SILENT', 'CASE_INSENSITIVE_FILENAMES',
198);
199
200# documented in the Texinfo::Parser pod section
201# all are lower cased in texi2any.pl
202my @parser_options = map {uc($_)} (keys(%default_parser_state_configuration));
203
204my @obsolete_variables = ('TOP_HEADING_AT_BEGINNING', 'USE_SECTIONS',
205 'IDX_SUMMARY', 'I18N_PERL_HASH', 'USE_UNICODE', 'USE_NLS',
206 'USE_UP_FOR_ADJACENT_NODES', 'SEPARATE_DESCRIPTION',
207 'NEW_CROSSREF_STYLE', 'SHORT_REF', 'IGNORE_PREAMBLE_TEXT',
208 'OUT_ENCODING',
209 'IN_ENCODING', 'DEFAULT_ENCODING');
210
211my @variable_settables_not_used = ('COMPLETE_IMAGE_PATHS', 'TOC_FILE',
212 'SPLIT_INDEX');
213
214my @formats_settable = (
215);
216
217my @variable_string_settables = (
218 'DEBUG', 'FRAMES', 'FRAMESET_DOCTYPE', 'DOCTYPE', 'TEST', 'DUMP_TEXI',
219 'TOP_FILE', 'SHOW_MENU', 'USE_NODES', 'TOC_LINKS', 'SHORTEXTN',
220 'PREFIX', 'DEF_TABLE', 'L2H', 'MONOLITHIC',
221 'L2H_L2H', 'L2H_SKIP', 'L2H_TMP', 'L2H_FILE', 'L2H_CLEAN',
222 'L2H_HTML_VERSION', 'EXTERNAL_DIR', 'USE_ISO',
223 'VERTICAL_HEAD_NAVIGATION', 'INLINE_CONTENTS', 'NODE_FILE_EXTENSION',
224 'NO_CSS', 'INLINE_CSS_STYLE', 'USE_TITLEPAGE_FOR_TITLE',
225 'SIMPLE_MENU', 'EXTENSION', 'INLINE_INSERTCOPYING', 'USE_NUMERIC_ENTITY',
226 'ENABLE_ENCODING_USE_ENTITY', 'ICONS',
227 'USE_UNIDECODE', 'DATE_IN_HEADER', 'OPEN_QUOTE_SYMBOL',
228 'CLOSE_QUOTE_SYMBOL', 'TOP_NODE_UP', 'TOP_NODE_UP_URL', 'TOP_NODE_FILE',
229 'TOP_NODE_FILE_TARGET', 'SHOW_TITLE', 'WORDS_IN_PAGE',
230 'HEADER_IN_TABLE', 'USE_ACCESSKEY', 'USE_REL_REV', 'USE_LINKS',
231 'OVERVIEW_LINK_TO_TOC', 'AVOID_MENU_REDUNDANCY', 'NODE_NAME_IN_MENU',
232 'NODE_NAME_IN_INDEX', 'NO_USE_SETFILENAME', 'USE_SETFILENAME_EXTENSION',
233 'COMPLEX_FORMAT_IN_TABLE',
234 'IGNORE_BEFORE_SETFILENAME', 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME',
235 'USE_NODE_TARGET',
236 'PROGRAM_NAME_IN_FOOTER', 'NODE_FILENAMES',
237 'EXTERNAL_CROSSREF_SPLIT', 'BODYTEXT',
238 'CSS_LINES', 'RENAMED_NODES_REDIRECTIONS', 'RENAMED_NODES_FILE',
239 'CPP_LINE_DIRECTIVES',
240 'TEXI2DVI', 'DUMP_TREE', 'MAX_MACRO_CALL_NESTING',
241 'INPUT_ENCODING_NAME', 'INPUT_PERL_ENCODING',
242 'OUTPUT_ENCODING_NAME', 'OUTPUT_PERL_ENCODING',
243 'PACKAGE_VERSION',
244 'PACKAGE_AND_VERSION', 'PACKAGE_URL', 'PACKAGE', 'PACKAGE_NAME', 'PROGRAM',
245 'PRE_BODY_CLOSE', 'AFTER_BODY_OPEN', 'PRE_ABOUT', 'AFTER_ABOUT',
246 'EXTRA_HEAD', 'DO_ABOUT',
247 'DEFAULT_RULE', 'BIG_RULE',
248 'MENU_ENTRY_COLON', 'INDEX_ENTRY_COLON', 'MENU_SYMBOL',
249 'MAX_HEADER_LEVEL', 'CHAPTER_HEADER_LEVEL',
250 'FOOTNOTE_END_HEADER_LEVEL', 'FOOTNOTE_SEPARATE_HEADER_LEVEL',
251 'USE_UP_NODE_FOR_ELEMENT_UP',
252 'BEFORE_OVERVIEW', 'AFTER_OVERVIEW',
253 'BEFORE_TOC_LINES', 'AFTER_TOC_LINES',
254 'SORT_ELEMENT_COUNT', 'SORT_ELEMENT_COUNT_WORDS',
255 'KEEP_TOP_EXTERNAL_REF',
256 'TEXI2HTML', 'IMAGE_LINK_PREFIX', 'FIX_TEXINFO',
257 'TREE_TRANSFORMATIONS', 'BASEFILENAME_LENGTH',
258 'TEXTCONTENT_COMMENT', 'XREF_USE_FLOAT_LABEL', 'XREF_USE_NODE_NAME_ARG',
259 'MACRO_BODY_IGNORES_LEADING_SPACE', 'CHECK_HTMLXREF',
260 'TEXINFO_DTD_VERSION', 'TEXINFO_COLUMN_FOR_DESCRIPTION',
261 'TEXINFO_OUTPUT_FORMAT',
262);
263# Not strings.
264# FIXME To be documented somewhere, but where?
265my @variable_other_settables = (
266 'LINKS_BUTTONS', 'TOP_BUTTONS', 'SECTION_BUTTONS', 'BUTTONS_TEXT',
267 'BUTTONS_ACCESSKEY', 'BUTTONS_REL', 'BUTTONS_GOTO',
268 'CHAPTER_FOOTER_BUTTONS', 'SECTION_FOOTER_BUTTONS',
269 'NODE_FOOTER_BUTTONS',
270 'MISC_BUTTONS', 'CHAPTER_BUTTONS', 'BUTTONS_NAME',
271 'BUTTONS_EXAMPLE', 'SPECIAL_ELEMENTS_NAME', 'SPECIAL_ELEMENTS_CLASS',
272 'ACTIVE_ICONS', 'PASSIVE_ICONS',
273 'CSS_FILES', 'CSS_REFS',
274 'GLOBAL_COMMANDS',
275);
276
277my %valid_options;
278foreach my $var (keys(%document_settable_at_commands),
279 keys(%document_settable_unique_at_commands),
280 @command_line_settables, @variable_string_settables,
281 @variable_other_settables, @parser_options,
282 @formats_settable,
283 @obsolete_variables, @variable_settables_not_used) {
284 $valid_options{$var} = 1;
285}
286
287my %obsolete_options;
288foreach my $var (@obsolete_variables) {
289 $obsolete_options{$var} = 1;
290}
291
292sub valid_option($)
293{
294 my $option = shift;
295 return $valid_options{$option};
296}
297
298sub obsolete_option($)
299{
300 my $option = shift;
301 return $obsolete_options{$option};
302}
303
304my %customization_variable_classes = (
305 'document_settable_at_commands' => [ sort(keys(%document_settable_at_commands)) ],
306 'document_settable_unique_at_commands' => [ sort(keys(%document_settable_unique_at_commands)) ],
307 'command_line_settables' => \@command_line_settables,
308 'variable_string_settables' => \@variable_string_settables,
309 'variable_other_settables' => \@variable_other_settables,
310 'parser_options' => \@parser_options,
311 #'formats_settable' => \@formats_settable,
312 'obsolete_variables' => \@obsolete_variables,
313 'variable_settables_not_used' => \@variable_settables_not_used,
314);
315
316my @secondary_customization_variables = (
317 'obsolete_variables', 'variable_settables_not_used'
318);
319sub _customization_variable_classes(;$)
320{
321 my $print_all = shift;
322 my $result = '';
323 foreach my $type (sort(keys(%customization_variable_classes))) {
324 next if (!$print_all
325 and grep {$_ eq $type} @secondary_customization_variables);
326 foreach my $variable (@{$customization_variable_classes{$type}}) {
327 $result .= "$variable\t$type\n";
328 }
329 }
330 return $result;
331}
332
333my %valid_tree_transformations;
334foreach my $valid_transformation ('simple_menus',
335 'fill_gaps_in_sectioning', 'move_index_entries_after_items',
336 'insert_nodes_for_sectioning_commands',
337 'complete_tree_nodes_menus', 'regenerate_master_menu',
338 'indent_menu_descriptions') {
339 $valid_tree_transformations{$valid_transformation} = 1;
340}
341
342sub valid_tree_transformation ($)
343{
344 my $transformation = shift;
345 return 1 if (defined($transformation)
346 and $valid_tree_transformations{$transformation});
347 return 0;
348}
349
350our %no_brace_commands; # commands never taking braces
351%no_brace_commands = (
352 '*', "\n",
353 ' ', ' ',
354 "\t", ' ',
355 "\n", ' ',
356 '-', '', # hyphenation hint
357 '|', '', # used in formatting commands @evenfooting and friends
358 '/', '',
359 ':', '',
360 '!', '!',
361 '?', '?',
362 '.', '.',
363 '@', '@',
364 '}', '}',
365 '{', '{',
366 '\\', '\\', # should only appear in math
367);
368
369
370# commands taking a line as argument or no argument.
371# sectioning commands and def* commands are added below.
372# index commands are added dynamically.
373#
374# The values signification is:
375# special: no value and macro expansion, all the line is used, and
376# analysed during parsing (_parse_special_misc_command)
377# lineraw: no value and macro expansion, the line is kept as-is, not
378# analysed
379# skipline: no argument, everything else on the line is skipped
380# skipspace: no argument, following spaces are skipped.
381# noarg: no argument
382# text: the line is parsed as texinfo, and the argument is converted
383# to simple text (in _end_line)
384# line: the line is parsed as texinfo
385# a number: the line is parsed as texinfo and the result should be plain
386# text maybe followed by a comment; the result is analysed
387# during parsing (_parse_line_command_args).
388# The number is an indication of the number of arguments of
389# the command.
390#
391# Beware that @item and @itemx may be like 'line' or 'skipspace' depending
392# on the context.
393our %misc_commands = (
394 'node' => 'line', # special arg
395 'bye' => 'skipline', # no arg
396 'end' => 'text',
397 # set, clear
398 'set' => 'special', # special arg
399 'clear' => 'special', # special arg
400 'unmacro' => 'special',
401 # comments
402 'comment' => 'lineraw',
403 'c' => 'lineraw',
404 # special
405 'definfoenclose' => 3,
406 'alias' => 2,
407 # number of arguments is not known in advance.
408 'columnfractions' => 1,
409 # file names
410 'setfilename' => 'text',
411 'verbatiminclude' => 'text',
412 'include' => 'text',
413
414 'raisesections' => 'skipline', # no arg
415 'lowersections' => 'skipline', # no arg
416 'contents' => 'skipline', # no arg
417 'shortcontents' => 'skipline', # no arg
418 'summarycontents' => 'skipline', # no arg
419 'insertcopying' => 'noarg', # no arg
420 'clickstyle' => 'special', # arg should be an @-command
421 # more relevant in preamble
422 'setcontentsaftertitlepage' => 'skipline', # no arg
423 'setshortcontentsaftertitlepage' => 'skipline', # no arg
424 'documentencoding' => 'text', # or 1?
425 'novalidate' => 'skipline', # no arg
426 'dircategory' => 'line', # line. Position with regard
427 # with direntry is significant
428 'pagesizes' => 'line', # can have 2 args
429 # or one? 200mm,150mm 11.5in
430 'finalout' => 'skipline', # no arg
431 'paragraphindent' => 1, # arg none asis
432 # or a number and forbids anything else on the line
433 'firstparagraphindent' => 1, # none insert
434 'frenchspacing' => 1, # on off
435 'codequoteundirected' => 1, # on off
436 'codequotebacktick' => 1, # on off
437 'xrefautomaticsectiontitle' => 1, # on off
438 'deftypefnnewline' => 1, # on off
439 'fonttextsize' => 1, # 10 11
440 'allowcodebreaks' => 1, # false or true
441 'exampleindent' => 1, # asis or a number
442 'footnotestyle' => 1, # end and separate, nothing else on the line
443 'urefbreakstyle' => 1, # after|before|none
444 'afourpaper' => 'skipline', # no arg
445 'afivepaper' => 'skipline', # no arg
446 'afourlatex' => 'skipline', # no arg
447 'afourwide' => 'skipline', # no arg
448 'headings' => 1, #off on single double singleafter doubleafter
449 # interacts with setchapternewpage
450 'setchapternewpage' => 1, # off on odd
451
452 # only relevant in TeX, and special
453 'everyheading' => 'lineraw', # @*heading @*footing use @|
454 'everyfooting' => 'lineraw', # + @thispage @thissectionname
455 'evenheading' => 'lineraw', # @thissectionnum @thissection
456 'evenfooting' => 'lineraw', # @thischaptername @thischapternum
457 'oddheading' => 'lineraw', # @thischapter @thistitle @thisfile
458 'oddfooting' => 'lineraw',
459
460 'smallbook' => 'skipline', # no arg
461 'syncodeindex' => 2, # args are index identifiers
462 'synindex' => 2,
463 'defindex' => 1, # one identifier arg
464 'defcodeindex' => 1, # one identifier arg
465 'documentlanguage' => 'text', # language code arg
466 'kbdinputstyle' => 1, # code example distinct
467 'everyheadingmarks' => 1, # top bottom
468 'everyfootingmarks' => 1,
469 'evenheadingmarks' => 1,
470 'oddheadingmarks' => 1,
471 'evenfootingmarks' => 1,
472 'oddfootingmarks' => 1,
473 # not valid for info (should be in @iftex)
474 'cropmarks' => 'skipline', # no arg
475
476 # formatting
477 'center' => 'line',
478 'printindex' => 1,
479 'listoffloats' => 'line',
480 # especially in titlepage
481# 'shorttitle' => 'line',
482 'shorttitlepage' => 'line',
483 'settitle' => 'line',
484 'author' => 'line',
485 'subtitle' => 'line',
486 'title' => 'line',
487 'sp' => 1, # numerical arg
488 'page' => 'skipline', # no arg (pagebreak)
489 'need' => 1, # one numerical/real arg
490 # formatting
491 'noindent' => 'skipspace', # no arg
492 'indent' => 'skipspace',
493 'exdent' => 'line',
494 'headitem' => 'skipspace',
495 'item' => 'skipspace', # or line, depending on the context
496 'itemx' => 'skipspace', # or line, depending on the context
497 'tab' => 'skipspace',
498 # only valid in heading or footing
499 'thischapter' => 'noarg',
500 'thischaptername' => 'noarg',
501 'thischapternum' => 'noarg',
502 'thisfile' => 'noarg',
503 'thispage' => 'noarg',
504 'thistitle' => 'noarg',
505 # not valid for info (should be in @iftex)
506 'vskip' => 'lineraw', # arg line in TeX
507 # obsolete @-commands.
508 'refill' => 'noarg', # no arg (obsolete, to be ignored)
509 # Remove spaces and end of lines after the
510 # commands? If no, they can lead to empty lines
511 'quote-arg' => 'skipline',
512 'allow-recursion' => 'skipline',
513);
514
515# key is index name, keys of the reference value are the prefixes.
516# value associated with the prefix is 0 if the prefix is not a code-like
517# prefix, 1 if it is a code-like prefix (set by defcodeindex/syncodeindex).
518#our %index_names = (
519# 'cp' => {'cp' => 0, 'c' => 0},
520# 'fn' => {'fn' => 1, 'f' => 1},
521# 'vr' => {'vr' => 1, 'v' => 1},
522# 'ky' => {'ky' => 1, 'k' => 1},
523# 'pg' => {'pg' => 1, 'p' => 1},
524# 'tp' => {'tp' => 1, 't' => 1}
525#);
526
527our %index_names = (
528 'cp' => {'prefix' => ['c'], 'in_code' => 0},
529 'fn' => {'prefix' => ['f'], 'in_code' => 1},
530 'vr' => {'prefix' => ['v'], 'in_code' => 1},
531 'ky' => {'prefix' => ['k'], 'in_code' => 1},
532 'pg' => {'prefix' => ['p'], 'in_code' => 1},
533 'tp' => {'prefix' => ['t'], 'in_code' => 1},
534);
535
536foreach my $index(keys(%index_names)) {
537 $index_names{$index}->{'name'} = $index;
538 push @{$index_names{$index}->{'prefix'}}, $index;
539}
540
541our %default_index_commands;
542# all the commands are readded dynamically in the Parser.
543foreach my $index_name (keys (%index_names)) {
544 foreach my $index_prefix (@{$index_names{$index_name}->{'prefix'}}) {
545 next if ($index_prefix eq $index_name);
546 # only put the one letter versions in the hash.
547 $misc_commands{$index_prefix.'index'} = 'line';
548 $default_index_commands{$index_prefix.'index'} = 1;
549 }
550}
551
552# command with braces. Value is the max number of arguments.
553our %brace_commands;
554
555our %letter_no_arg_commands;
556foreach my $letter_no_arg_command ('aa','AA','ae','oe','AE','OE','o','O',
557 'ss','l','L','DH','dh','TH','th') {
558 $letter_no_arg_commands{$letter_no_arg_command} = 1;
559 $brace_commands{$letter_no_arg_command} = 0;
560}
561
562foreach my $no_arg_command ('TeX','LaTeX','bullet','copyright',
563 'registeredsymbol','dots','enddots','equiv','error','expansion','arrow',
564 'minus','point','print','result','today',
565 'exclamdown','questiondown','pounds','ordf','ordm',
566 'atchar', 'lbracechar', 'rbracechar', 'backslashchar', 'hashchar', 'comma',
567 'euro', 'geq','leq','tie','textdegree','click',
568 'quotedblleft','quotedblright','quoteleft','quoteright','quotedblbase',
569 'quotesinglbase','guillemetleft','guillemetright','guillemotleft',
570 'guillemotright','guilsinglleft','guilsinglright') {
571 $brace_commands{$no_arg_command} = 0;
572}
573
574# accent commands. They may be called with and without braces.
575our %accent_commands;
576foreach my $accent_command ('"','~','^','`',"'",',','=',
577 'ringaccent','H','dotaccent','u','ubaraccent',
578 'udotaccent','v','ogonek','tieaccent', 'dotless') {
579 $accent_commands{$accent_command} = 1;
580 $brace_commands{$accent_command} = 1;
581}
582
583our %style_commands;
584foreach my $style_command ('asis','cite','clicksequence',
585 'dfn', 'emph',
586 'sc', 't', 'var',
587 'headitemfont', 'code', 'command', 'env', 'file', 'kbd',
588 'option', 'samp', 'strong') {
589 $brace_commands{$style_command} = 1;
590 $style_commands{$style_command} = 1;
591}
592
593our %regular_font_style_commands;
594foreach my $command ('r', 'i', 'b', 'sansserif', 'slanted') {
595 $regular_font_style_commands{$command} = 1;
596 $brace_commands{$command} = 1;
597 $style_commands{$command} = 1;
598}
599
600foreach my $one_arg_command (
601 'ctrl','dmn', 'w', 'key',
602 'titlefont','hyphenation','anchor','errormsg') {
603 $brace_commands{$one_arg_command} = 1;
604}
605
606our %code_style_commands;
607foreach my $command ('code', 'command', 'env', 'file', 'kbd', 'key', 'option',
608 'samp', 'indicateurl', 'verb', 't') {
609 $code_style_commands{$command} = 1;
610 $brace_commands{$command} = 1;
611}
612
613
614# Commands that enclose full texts
615our %context_brace_commands;
616foreach my $context_brace_command ('footnote', 'caption', 'shortcaption', 'math') {
617 $context_brace_commands{$context_brace_command} = $context_brace_command;
618 $brace_commands{$context_brace_command} = 1;
619}
620
621our %explained_commands;
622foreach my $explained_command ('abbr', 'acronym') {
623 $explained_commands{$explained_command} = 1;
624 $brace_commands{$explained_command} = 2;
625}
626
627our %inline_format_commands;
628foreach my $inline_format_command ('inlineraw', 'inlinefmt') {
629 $inline_format_commands{$inline_format_command} = 1;
630 $brace_commands{$inline_format_command} = 2;
631}
632
633foreach my $two_arg_command('email') {
634 $brace_commands{$two_arg_command} = 2;
635}
636
637foreach my $three_arg_command('uref','url','inforef') {
638 $brace_commands{$three_arg_command} = 3;
639}
640
641foreach my $five_arg_command('xref','ref','pxref','image') {
642 $brace_commands{$five_arg_command} = 5;
643}
644
645
646# some classification to help converters
647our %ref_commands;
648foreach my $ref_command ('xref','ref','pxref','inforef') {
649 $ref_commands{$ref_command} = 1;
650}
651
652
653our %in_heading_commands;
654foreach my $in_heading_command ('thischapter', 'thischaptername',
655 'thischapternum', 'thisfile', 'thispage', 'thistitle') {
656 $in_heading_commands{$in_heading_command} = 1;
657}
658
659# commands delimiting blocks, with an @end.
660# Value is either the number of arguments on the line separated by
661# commas or the type of command, 'raw', 'def' or 'multitable'.
662our %block_commands;
663
664# commands that have a possible content before an item
665our %block_item_commands;
666
667sub gdt($)
668{
669 return $_[0];
670}
671
672our %def_map = (
673 # basic commands.
674 # 'arg' and 'argtype' are for everything appearing after the other
675 # arguments.
676 'deffn', [ 'category', 'name', 'arg' ],
677 'defvr', [ 'category', 'name' ],
678 'deftypefn', [ 'category', 'type', 'name', 'argtype' ],
679 'deftypeop', [ 'category', 'class' , 'type', 'name', 'argtype' ],
680 'deftypevr', [ 'category', 'type', 'name' ],
681 'defcv', [ 'category', 'class' , 'name' ],
682 'deftypecv', [ 'category', 'class' , 'type', 'name' ],
683 'defop', [ 'category', 'class' , 'name', 'arg' ],
684 'deftp', [ 'category', 'name', 'argtype' ],
685 # shortcuts
686 'defun', {'deffn' => gdt('Function')},
687 'defmac', {'deffn' => gdt('Macro')},
688 'defspec', {'deffn' => '{'.gdt('Special Form').'}'},
689 'defvar', {'defvr' => gdt('Variable')},
690 'defopt', {'defvr' => '{'.gdt('User Option').'}'},
691 'deftypefun', {'deftypefn' => gdt('Function')},
692 'deftypevar', {'deftypevr' => gdt('Variable')},
693 'defivar', {'defcv' => '{'.gdt('Instance Variable').'}'},
694 'deftypeivar', {'deftypecv' => '{'.gdt('Instance Variable').'}'},
695 'defmethod', {'defop' => gdt('Method')},
696 'deftypemethod', {'deftypeop' => gdt('Method')},
697);
698
699# the type of index, f: function, v: variable, t: type
700my %index_type_def = (
701 'f' => ['deffn', 'deftypefn', 'deftypeop', 'defop'],
702 'v' => ['defvr', 'deftypevr', 'defcv', 'deftypecv' ],
703 't' => ['deftp']
704);
705
706our %command_index_prefix;
707
708$command_index_prefix{'vtable'} = 'v';
709$command_index_prefix{'ftable'} = 'f';
710
711foreach my $index_type (keys %index_type_def) {
712 foreach my $def (@{$index_type_def{$index_type}}) {
713 $command_index_prefix{$def} = $index_type;
714 }
715}
716
717our %def_commands;
718our %def_aliases;
719foreach my $def_command(keys %def_map) {
720 if (ref($def_map{$def_command}) eq 'HASH') {
721 my ($real_command) = keys (%{$def_map{$def_command}});
722 $command_index_prefix{$def_command} = $command_index_prefix{$real_command};
723 $def_aliases{$def_command} = $real_command;
724 }
725 $block_commands{$def_command} = 'def';
726 $misc_commands{$def_command.'x'} = 'line';
727 $def_commands{$def_command} = 1;
728 $def_commands{$def_command.'x'} = 1;
729 $command_index_prefix{$def_command.'x'} = $command_index_prefix{$def_command};
730}
731
732#print STDERR "".Data::Dumper->Dump([\%def_aliases]);
733#print STDERR "".Data::Dumper->Dump([\%def_prepended_content]);
734
735$block_commands{'multitable'} = 'multitable';
736$block_item_commands{'multitable'} = 1;
737
738# block commands in which menu entry and menu comments appear
739our %menu_commands;
740foreach my $menu_command ('menu', 'detailmenu', 'direntry') {
741 $menu_commands{$menu_command} = 1;
742 $block_commands{$menu_command} = 0;
743};
744
745our %align_commands;
746foreach my $align_command('raggedright', 'flushleft', 'flushright') {
747 $block_commands{$align_command} = 0;
748 $align_commands{$align_command} = 1;
749}
750$align_commands{'center'} = 1;
751
752foreach my $block_command(
753 'cartouche', 'group', 'indentedblock', 'smallindentedblock') {
754 $block_commands{$block_command} = 0;
755}
756
757our %region_commands;
758foreach my $block_command('titlepage', 'copying', 'documentdescription') {
759 $block_commands{$block_command} = 0;
760 $region_commands{$block_command} = 1;
761}
762
763our %preformatted_commands;
764our %preformatted_code_commands;
765foreach my $preformatted_command(
766 'example', 'smallexample', 'lisp', 'smalllisp') {
767 $block_commands{$preformatted_command} = 0;
768 $preformatted_commands{$preformatted_command} = 1;
769 $preformatted_code_commands{$preformatted_command} = 1;
770}
771
772foreach my $preformatted_command(
773 'display', 'smalldisplay', 'format', 'smallformat') {
774 $block_commands{$preformatted_command} = 0;
775 $preformatted_commands{$preformatted_command} = 1;
776}
777
778our %format_raw_commands;
779foreach my $format_raw_command('html', 'tex', 'xml', 'docbook') {
780 $block_commands{$format_raw_command} = 0;
781 $format_raw_commands{$format_raw_command} = 1;
782}
783
784our %raw_commands;
785# macro/rmacro are special
786foreach my $raw_command ('verbatim',
787 'ignore', 'macro', 'rmacro') {
788 $block_commands{$raw_command} = 'raw';
789 $raw_commands{$raw_command} = 1;
790}
791
792our %texinfo_output_formats;
793foreach my $command (keys(%format_raw_commands), 'info', 'plaintext') {
794 $block_commands{'if' . $command} = 'conditional';
795 $block_commands{'ifnot' . $command} = 'conditional';
796 $texinfo_output_formats{$command} = $command;
797}
798
799$block_commands{'ifset'} = 'conditional';
800$block_commands{'ifclear'} = 'conditional';
801
802$block_commands{'ifcommanddefined'} = 'conditional';
803$block_commands{'ifcommandnotdefined'} = 'conditional';
804
805# 'macro' ?
806foreach my $block_command_one_arg('table', 'ftable', 'vtable',
807 'itemize', 'enumerate', 'quotation', 'smallquotation') {
808 $block_commands{$block_command_one_arg} = 1;
809 $block_item_commands{$block_command_one_arg} = 1
810 unless ($block_command_one_arg =~ /quotation/);
811}
812
813$block_commands{'float'} = 2;
814
815# commands that forces closing an opened paragraph.
816our %close_paragraph_commands;
817
818foreach my $block_command (keys(%block_commands)) {
819 $close_paragraph_commands{$block_command} = 1
820 unless ($block_commands{$block_command} eq 'raw' or
821 $block_commands{$block_command} eq 'conditional'
822 or $format_raw_commands{$block_command});
823}
824
825$close_paragraph_commands{'verbatim'} = 1;
826
827foreach my $close_paragraph_command ('titlefont', 'insertcopying', 'sp',
828 'verbatiminclude', 'page', 'item', 'itemx', 'tab', 'headitem',
829 'printindex', 'listoffloats', 'center', 'dircategory', 'contents',
830 'shortcontents', 'summarycontents', 'caption', 'shortcaption',
831 'setfilename', 'exdent') {
832 $close_paragraph_commands{$close_paragraph_command} = 1;
833}
834
835foreach my $close_paragraph_command (keys(%def_commands)) {
836 $close_paragraph_commands{$close_paragraph_command} = 1;
837}
838
839our %item_container_commands;
840foreach my $item_container_command ('itemize', 'enumerate') {
841 $item_container_commands{$item_container_command} = 1;
842}
843our %item_line_commands;
844foreach my $item_line_command ('table', 'ftable', 'vtable') {
845 $item_line_commands{$item_line_command} = 1;
846}
847
848our %deprecated_commands = (
849 'ctrl' => '',
850 'allow-recursion' => N__('recursion is always allowed'),
851 'quote-arg' => N__('arguments are quoted by default'),
852);
853
854# commands that should only appear at the root level and contain up to
855# the next root command. @node and sectioning commands.
856our %root_commands;
857
858our %command_structuring_level = (
859 'top', 0,
860 'chapter', 1,
861 'unnumbered', 1,
862 'chapheading', 1,
863 'appendix', 1,
864 'section', 2,
865 'unnumberedsec', 2,
866 'heading', 2,
867 'appendixsec', 2,
868 'subsection', 3,
869 'unnumberedsubsec', 3,
870 'subheading', 3,
871 'appendixsubsec', 3,
872 'subsubsection', 4,
873 'unnumberedsubsubsec', 4,
874 'subsubheading', 4,
875 'appendixsubsubsec', 4,
876 );
877
878our %level_to_structuring_command;
879
880{
881 my $sections = [ ];
882 my $appendices = [ ];
883 my $unnumbered = [ ];
884 my $headings = [ ];
885 foreach my $command (keys (%command_structuring_level)) {
886 if ($command =~ /^appendix/) {
887 $level_to_structuring_command{$command} = $appendices;
888 } elsif ($command =~ /^unnumbered/ or $command eq 'top') {
889 $level_to_structuring_command{$command} = $unnumbered;
890 } elsif ($command =~ /section$/ or $command eq 'chapter') {
891 $level_to_structuring_command{$command} = $sections;
892 } else {
893 $level_to_structuring_command{$command} = $headings;
894 }
895 $level_to_structuring_command{$command}->[$command_structuring_level{$command}]
896 = $command;
897 }
898 $level_to_structuring_command{'appendixsection'} = $appendices;
899 $level_to_structuring_command{'majorheading'} = $headings;
900 $level_to_structuring_command{'centerchap'} = $unnumbered;
901}
902
903
904# out of the main hierarchy
905$command_structuring_level{'part'} = 0;
906# this are synonyms
907$command_structuring_level{'appendixsection'} = 2;
908# command_structuring_level{'majorheading'} is also 1 and not 0
909$command_structuring_level{'majorheading'} = 1;
910$command_structuring_level{'centerchap'} = 1;
911
912our %sectioning_commands;
913
914foreach my $sectioning_command (keys (%command_structuring_level)) {
915 $misc_commands{$sectioning_command} = 'line';
916 if ($sectioning_command =~ /heading/) {
917 $close_paragraph_commands{$sectioning_command} = 1;
918 } else {
919 $root_commands{$sectioning_command} = 1;
920 }
921 $sectioning_commands{$sectioning_command} = 1;
922}
923
924$root_commands{'node'} = 1;
925
926our %all_commands;
927foreach my $command (
928 keys(%Texinfo::Common::block_commands),
929 keys(%Texinfo::Common::brace_commands),
930 keys(%Texinfo::Common::misc_commands),
931 keys(%Texinfo::Common::no_brace_commands),
932 qw(value),
933 ) {
934 $all_commands{$command} = 1;
935}
936
937our @MONTH_NAMES =
938 (
939 'January', 'February', 'March', 'April', 'May',
940 'June', 'July', 'August', 'September', 'October',
941 'November', 'December'
942 );
943
944sub locate_include_file($$)
945{
946 my $self = shift;
947 my $text = shift;
948 my $file;
949
950 my $ignore_include_directories = 0;
951
952 my ($volume, $directories, $filename) = File::Spec->splitpath($text);
953 my @directories = File::Spec->splitdir($directories);
954
955 #print STDERR "$self $text @{$self->{'include_directories'}}\n";
956 # If the path is absolute or begins with . or .., do not search in
957 # include directories.
958 if (File::Spec->file_name_is_absolute($text)) {
959 $ignore_include_directories = 1;
960 } else {
961 foreach my $dir (@directories) {
962 if ($dir eq File::Spec->updir() or $dir eq File::Spec->curdir()) {
963 $ignore_include_directories = 1;
964 last;
965 } elsif ($dir ne '') {
966 last;
967 }
968 }
969 }
970
971 #if ($text =~ m,^(/|\./|\.\./),) {
972 if ($ignore_include_directories) {
973 $file = $text if (-e $text and -r $text);
974 } else {
975 my @dirs;
976 if ($self) {
977 @dirs = @{$self->{'include_directories'}};
978 } else {
979 # no object with directory list and not an absolute path, never succeed
980 return undef;
981 }
982 foreach my $include_dir (@{$self->{'include_directories'}}) {
983 my ($include_volume, $include_directories, $include_filename)
984 = File::Spec->splitpath($include_dir, 1);
985
986 my $possible_file = File::Spec->catpath($include_volume,
987 File::Spec->catdir(File::Spec->splitdir($include_directories),
988 @directories), $filename);
989 #$file = "$include_dir/$text" if (-e "$include_dir/$text" and -r "$include_dir/$text");
990 $file = "$possible_file" if (-e "$possible_file" and -r "$possible_file");
991 last if (defined($file));
992 }
993 }
994 return $file;
995}
996
997sub open_out($$;$)
998{
999 my $self = shift;
1000 my $file = shift;
1001 my $encoding = shift;
1002
1003 if (!defined($encoding) and $self
1004 and defined($self->get_conf('OUTPUT_PERL_ENCODING'))) {
1005 $encoding = $self->get_conf('OUTPUT_PERL_ENCODING');
1006 }
1007
1008 if ($file eq '-') {
1009 binmode(STDOUT, ":encoding($encoding)") if ($encoding);
1010 if ($self) {
1011 $self->{'unclosed_files'}->{$file} = \*STDOUT;
1012 }
1013 return \*STDOUT;
1014 }
1015 my $filehandle = do { local *FH };
1016 if (!open ($filehandle, '>', $file)) {
1017 return undef;
1018 }
1019 if ($encoding) {
1020 if ($encoding eq 'utf8' or $encoding eq 'utf-8-strict') {
1021 binmode($filehandle, ':utf8');
1022 } else { # FIXME also right for shiftijs or similar encodings?
1023 binmode($filehandle, ':bytes');
1024 }
1025 binmode($filehandle, ":encoding($encoding)");
1026 }
1027 if ($self) {
1028 push @{$self->{'opened_files'}}, $file;
1029 $self->{'unclosed_files'}->{$file} = $filehandle;
1030 #print STDERR "OOOOOOO $file ".join('|',@{$self->{'opened_files'}})."\n";
1031 #cluck;
1032 }
1033 return $filehandle;
1034}
1035
1036sub warn_unknown_language($$) {
1037 my $lang = shift;
1038 my $gettext = shift;
1039
1040 my @messages = ();
1041 my $lang_code = $lang;
1042 my $region_code;
1043
1044 if ($lang =~ /^([a-z]+)_([A-Z]+)/) {
1045 $lang_code = $1;
1046 $region_code = $2;
1047 }
1048
1049 if (! $Texinfo::Documentlanguages::language_codes{$lang_code}) {
1050 push @messages, sprintf(&$gettext(N__("%s is not a valid language code")),
1051 $lang_code);
1052 }
1053 if (defined($region_code)
1054 and ! $Texinfo::Documentlanguages::region_codes{$region_code}) {
1055 push @messages, sprintf(&$gettext(N__("%s is not a valid region code")),
1056 $region_code);
1057 }
1058 return @messages;
1059}
1060
1061my %possible_split = (
1062 'chapter' => 1,
1063 'section' => 1,
1064 'node' => 1,
1065);
1066
1067sub warn_unknown_split($$) {
1068 my $split = shift;
1069 my $gettext = shift;
1070
1071 my @messages = ();
1072 if ($split and !$possible_split{$split}) {
1073 push @messages, sprintf(&$gettext(N__("%s is not a valid split possibility")),
1074 $split);
1075 }
1076 return @messages;
1077}
1078
1079# This should do the job, or at least don't do wrong if $self
1080# is not defined, as could be the case if called from
1081# Texinfo::Convert::Text.
1082sub expand_verbatiminclude($$)
1083{
1084 my $self = shift;
1085 my $current = shift;
1086
1087 return unless ($current->{'extra'} and defined($current->{'extra'}->{'text_arg'}));
1088 my $text = $current->{'extra'}->{'text_arg'};
1089 my $file = locate_include_file($self, $text);
1090
1091 my $verbatiminclude;
1092
1093 if (defined($file)) {
1094 if (!open(VERBINCLUDE, $file)) {
1095 if ($self) {
1096 $self->line_error(sprintf($self->__("could not read %s: %s"), $file, $!),
1097 $current->{'line_nr'});
1098 }
1099 } else {
1100 if ($self and defined($self->get_conf('INPUT_PERL_ENCODING'))) {
1101 binmode(VERBINCLUDE, ":encoding(".
1102 $self->get_conf('INPUT_PERL_ENCODING').")");
1103 }
1104 $verbatiminclude = { 'cmdname' => 'verbatim',
1105 'parent' => $current->{'parent'},
1106 'extra' =>
1107 {'text_arg' => $current->{'extra'}->{'text_arg'}} };
1108 while (<VERBINCLUDE>) {
1109 push @{$verbatiminclude->{'contents'}},
1110 {'type' => 'raw', 'text' => $_ };
1111 }
1112 if (!close (VERBINCLUDE)) {
1113 $self->document_warn(sprintf($self->__(
1114 "error on closing \@verbatiminclude file %s: %s"),
1115 $file, $!));
1116 }
1117 }
1118 } elsif ($self) {
1119 $self->line_error(sprintf($self->__("\@%s: could not find %s"),
1120 $current->{'cmdname'}, $text), $current->{'line_nr'});
1121 }
1122 return $verbatiminclude;
1123}
1124
1125sub definition_category($$)
1126{
1127 my $self = shift;
1128 my $current = shift;
1129
1130 return undef if (!$current->{'extra'} or !$current->{'extra'}->{'def_args'});
1131
1132 my $arg_category = $current->{'extra'}->{'def_parsed_hash'}->{'category'};
1133 my $arg_class = $current->{'extra'}->{'def_parsed_hash'}->{'class'};
1134
1135 return $arg_category
1136 if (!defined($arg_class));
1137
1138 my $style =
1139 $command_index_prefix{$current->{'extra'}->{'def_command'}};
1140 if ($style eq 'f') {
1141 if ($self) {
1142 return $self->gdt('{category} on {class}', { 'category' => $arg_category,
1143 'class' => $arg_class });
1144 } else {
1145 return {'contents' => [$arg_category, {'text' => ' on '}, $arg_class]};
1146 }
1147 } elsif ($style eq 'v') {
1148 if ($self) {
1149 return $self->gdt('{category} of {class}', { 'category' => $arg_category,
1150 'class' => $arg_class });
1151 } else {
1152 return {'contents' => [$arg_category, {'text' => ' of '}, $arg_class]};
1153 }
1154 }
1155}
1156
1157sub expand_today($)
1158{
1159 my $self = shift;
1160 if ($self->get_conf('TEST')) {
1161 return {'text' => 'a sunny day'};
1162 }
1163 my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst)
1164 = localtime(time);
1165 $year += ($year < 70) ? 2000 : 1900;
1166 return $self->gdt('{month} {day}, {year}',
1167 { 'month' => $self->gdt($MONTH_NAMES[$mon]),
1168 'day' => $mday, 'year' => $year });
1169}
1170
1171sub translated_command_tree($$)
1172{
1173 my $self = shift;
1174 my $cmdname = shift;
1175 if ($self->{'translated_commands'}->{$cmdname}) {
1176 return $self->gdt($self->{'translated_commands'}->{$cmdname});
1177 }
1178 return undef;
1179}
1180
1181sub numbered_heading($$$;$)
1182{
1183 my $self = shift;
1184 my $current = shift;
1185 my $text = shift;
1186 my $numbered = shift;
1187
1188 my $number;
1189 if (defined($current->{'number'}) and ($numbered or !defined($numbered))) {
1190 $number = $current->{'number'};
1191 }
1192
1193 my $result;
1194 if ($self) {
1195 if (defined($number)) {
1196 if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) {
1197 $result = $self->gdt('Appendix {number} {section_title}',
1198 {'number' => $number, 'section_title' => $text},
1199 'translated_text');
1200 } else {
1201 $result = $self->gdt('{number} {section_title}',
1202 {'number' => $number, 'section_title' => $text},
1203 'translated_text');
1204 }
1205 } else {
1206 $result = $text;
1207 }
1208 } else {
1209 $result = $text;
1210 $result = $number.' '.$result if (defined($number));
1211 if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) {
1212 $result = 'Appendix '.$result;
1213 }
1214 }
1215 chomp ($result);
1216 return $result;
1217}
1218
1219sub definition_arguments_content($)
1220{
1221 my $root = shift;
1222 my $result;
1223
1224 return undef if (!defined($root->{'extra'})
1225 or !defined($root->{'extra'}->{'def_args'}));
1226 my @args = @{$root->{'extra'}->{'def_args'}};
1227 while (@args) {
1228 last if ($args[0]->[0] ne 'spaces'
1229 and !$root->{'extra'}->{'def_parsed_hash'}->{$args[0]->[0]});
1230 shift @args;
1231 }
1232 if (@args) {
1233 foreach my $arg (@args) {
1234 push @$result, $arg->[1];
1235 }
1236 }
1237 return $result;
1238}
1239
1240# find the accent commands stack and the innermost text contents
1241sub find_innermost_accent_contents($;$)
1242{
1243 my $current = shift;
1244 my $encoding = shift;
1245 my @accent_commands = ();
1246 my $debug = 0;
1247 ACCENT:
1248 while (1) {
1249 # the following can happen if called with a bad tree
1250 if (!$current->{'cmdname'}
1251 or !$accent_commands{$current->{'cmdname'}}) {
1252 #print STDERR "BUG: Not an accent command in accent\n";
1253 cluck "BUG: Not an accent command in accent\n";
1254 #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n";
1255 #print STDERR Data::Dumper->Dump([$current]);
1256 last;
1257 }
1258 push @accent_commands, $current;
1259 # A bogus accent, that may happen
1260 if (!$current->{'args'}) {
1261 return ([], \@accent_commands);
1262 }
1263 my $arg = $current->{'args'}->[0];
1264 if (!$arg->{'contents'}) {
1265 print STDERR "BUG: No content in accent command\n";
1266 #print STDERR Data::Dumper->Dump([$current]);
1267 #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n";
1268 return ([], \@accent_commands);
1269 }
1270 # inside the argument of an accent
1271 my $text_contents = [];
1272 foreach my $content (@{$arg->{'contents'}}) {
1273 if (!($content->{'extra'} and $content->{'extra'}->{'invalid_nesting'})
1274 and !($content->{'cmdname'} and ($content->{'cmdname'} eq 'c'
1275 or $content->{'cmdname'} eq 'comment'))) {
1276 if ($content->{'cmdname'} and $accent_commands{$content->{'cmdname'}}) {
1277 $current = $content;
1278 next ACCENT;
1279 } else {
1280 push @$text_contents, $content;
1281 }
1282 }
1283 }
1284 # we go here if there was no nested accent
1285 return ($text_contents, \@accent_commands);
1286 }
1287}
1288
1289sub trim_spaces_comment_from_content($)
1290{
1291 my $contents = shift;
1292 shift @$contents
1293 if ($contents->[0] and $contents->[0]->{'type'}
1294 and ($contents->[0]->{'type'} eq 'empty_line_after_command'
1295 or $contents->[0]->{'type'} eq 'empty_spaces_after_command'
1296 or $contents->[0]->{'type'} eq 'empty_spaces_before_argument'
1297 or $contents->[0]->{'type'} eq 'empty_space_at_end_def_bracketed'
1298 or $contents->[0]->{'type'} eq 'empty_spaces_after_close_brace'));
1299
1300 while (@$contents
1301 and (($contents->[-1]->{'cmdname'}
1302 and ($contents->[-1]->{'cmdname'} eq 'c'
1303 or $contents->[-1]->{'cmdname'} eq 'comment'))
1304 or ($contents->[-1]->{'type'}
1305 and ($contents->[-1]->{'type'} eq 'spaces_at_end'
1306 or $contents->[-1]->{'type'} eq 'space_at_end_block_command')))) {
1307 pop @$contents;
1308 }
1309}
1310
1311sub float_name_caption($$)
1312{
1313 my $self = shift;
1314 my $root = shift;
1315
1316 my $caption;
1317 if ($root->{'extra'}->{'caption'}) {
1318 $caption = $root->{'extra'}->{'caption'};
1319 } elsif ($root->{'extra'}->{'shortcaption'}) {
1320 $caption = $root->{'extra'}->{'shortcaption'};
1321 }
1322 #if ($self->get_conf('DEBUG')) {
1323 # my $caption_texi =
1324 # Texinfo::Convert::Texinfo::convert({ 'contents' => $caption->{'contents'}});
1325 # print STDERR " CAPTION: $caption_texi\n";
1326 #}
1327 my $type;
1328 if ($root->{'extra'}->{'type'}->{'normalized'} ne '') {
1329 $type = {'contents' => $root->{'extra'}->{'type'}->{'content'}};
1330 }
1331
1332 my $prepended;
1333 if ($type) {
1334 if ($caption) {
1335 if (defined($root->{'number'})) {
1336 $prepended = $self->gdt('{float_type} {float_number}: ',
1337 {'float_type' => $type,
1338 'float_number' => $root->{'number'}});
1339 } else {
1340 $prepended = $self->gdt('{float_type}: ',
1341 {'float_type' => $type});
1342 }
1343 } else {
1344 if (defined($root->{'number'})) {
1345 $prepended = $self->gdt("{float_type} {float_number}\n",
1346 {'float_type' => $type,
1347 'float_number' => $root->{'number'}});
1348 } else {
1349 $prepended = $self->gdt("{float_type}\n",
1350 {'float_type' => $type});
1351 }
1352 }
1353 } elsif (defined($root->{'number'})) {
1354 if ($caption) {
1355 $prepended = $self->gdt('{float_number}: ',
1356 {'float_number' => $root->{'number'}});
1357 } else {
1358 $prepended = $self->gdt("{float_number}\n",
1359 {'float_number' => $root->{'number'}});
1360 }
1361 }
1362 return ($caption, $prepended);
1363}
1364
1365# decompose a decimal number on a given base.
1366sub _decompose_integer($$)
1367{
1368 my $number = shift;
1369 my $base = shift;
1370 my @result = ();
1371
1372 while ($number >= 0) {
1373 my $factor = $number % $base;
1374 push (@result, $factor);
1375 $number = int(($number - $factor) / $base) - 1;
1376 }
1377 return @result;
1378}
1379
1380sub enumerate_item_representation($$)
1381{
1382 my $specification = shift;
1383 my $number = shift;
1384
1385 if ($specification =~ /^[0-9]$/) {
1386 return $specification + $number -1;
1387 }
1388
1389 my $result = '';
1390 my $base_letter = ord('a');
1391 $base_letter = ord('A') if (ucfirst($specification) eq $specification);
1392 my @letter_ords = _decompose_integer(ord($specification) - $base_letter + $number - 1, 26);
1393 foreach my $ord (@letter_ords) {
1394 $result = chr($base_letter + $ord) . $result;
1395 }
1396 return $result;
1397}
1398
1399
1400our %htmlxref_entries = (
1401 'node' => [ 'node', 'section', 'chapter', 'mono' ],
1402 'section' => [ 'section', 'chapter','node', 'mono' ],
1403 'chapter' => [ 'chapter', 'section', 'node', 'mono' ],
1404 'mono' => [ 'mono', 'chapter', 'section', 'node' ],
1405);
1406
1407sub parse_htmlxref_files($$)
1408{
1409 my $self = shift;
1410 my $files = shift;
1411 my $htmlxref;
1412
1413 foreach my $file (@$files) {
1414 print STDERR "html refs config file: $file\n" if ($self->get_conf('DEBUG'));
1415 unless (open (HTMLXREF, $file)) {
1416 $self->document_warn(
1417 sprintf($self->__("could not open html refs config file %s: %s"),
1418 $file, $!));
1419 next;
1420 }
1421 my $line_nr = 0;
1422 my %variables;
1423 while (my $hline = <HTMLXREF>) {
1424 my $line = $hline;
1425 $line_nr++;
1426 next if $hline =~ /^\s*#/;
1427 #$hline =~ s/[#]\s.*//;
1428 $hline =~ s/^\s*//;
1429 next if $hline =~ /^\s*$/;
1430 chomp ($hline);
1431 if ($hline =~ s/^\s*(\w+)\s*=\s*//) {
1432 # handle variables
1433 my $var = $1;
1434 my $re = join '|', map { quotemeta $_ } keys %variables;
1435 $hline =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
1436 : "\${$1}"/ge;
1437 $variables{$var} = $hline;
1438 next;
1439 }
1440 my @htmlxref = split /\s+/, $hline;
1441 my $manual = shift @htmlxref;
1442 my $split_or_mono = shift @htmlxref;
1443 #print STDERR "$split_or_mono $Texi2HTML::Config::htmlxref_entries{$split_or_mono} $line_nr\n";
1444 if (!defined($split_or_mono)) {
1445 $self->file_line_warn($self->__("missing type"), $file, $line_nr);
1446 next;
1447 } elsif (!defined($htmlxref_entries{$split_or_mono})) {
1448 $self->file_line_warn(sprintf($self->__("unrecognized type: %s"),
1449 $split_or_mono), $file, $line_nr);
1450 next;
1451 }
1452 my $href = shift @htmlxref;
1453 next if (exists($htmlxref->{$manual}->{$split_or_mono}));
1454
1455 if (defined($href)) { # substitute 'variables'
1456 my $re = join '|', map { quotemeta $_ } keys %variables;
1457 $href =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
1458 : "\${$1}"/ge;
1459 $href =~ s/\/*$// if ($split_or_mono ne 'mono');
1460 }
1461 $htmlxref->{$manual}->{$split_or_mono} = $href;
1462 }
1463 if (!close (HTMLXREF)) {
1464 $self->document_warn(sprintf($self->__(
1465 "error on closing html refs config file %s: %s"),
1466 $file, $!));
1467 }
1468 }
1469 return $htmlxref;
1470}
1471
1472sub parse_renamed_nodes_file($$;$$)
1473{
1474 my $self = shift;
1475 my $renamed_nodes_file = shift;
1476 # if not given they are automatically created
1477 my $renamed_nodes = shift;
1478 my $renamed_nodes_lines = shift;
1479
1480 if (open(RENAMEDFILE, "<$renamed_nodes_file")) {
1481 if ($self->get_conf('INPUT_PERL_ENCODING')) {
1482 binmode(RENAMEDFILE, ":encoding(".
1483 $self->get_conf('INPUT_PERL_ENCODING').")");
1484 }
1485 my $renamed_nodes_line_nr = 0;
1486 my @old_names = ();
1487 while (<RENAMEDFILE>) {
1488 $renamed_nodes_line_nr++;
1489 next unless (/\S/);
1490 next if (/^\s*\@c\b/);
1491 if (s/^\s*\@\@\{\}\s+(\S)/$1/) {
1492 chomp;
1493 if (scalar(@old_names)) {
1494 foreach my $old_node_name (@old_names) {
1495 $renamed_nodes->{$old_node_name} = $_;
1496 }
1497 $renamed_nodes_lines->{$_} = $renamed_nodes_line_nr;
1498 @old_names = ();
1499 } else {
1500 $self->file_line_warn($self->__("no node to be renamed"),
1501 $renamed_nodes_file, $renamed_nodes_line_nr);
1502 }
1503 } else {
1504 chomp;
1505 s/^\s*//;
1506 $renamed_nodes_lines->{$_} = $renamed_nodes_line_nr;
1507 push @old_names, $_;
1508 }
1509 }
1510 if (scalar(@old_names)) {
1511 $self->file_line_warn($self->__("nodes without a new name at the end of file"),
1512 $renamed_nodes_file, $renamed_nodes_line_nr);
1513 }
1514 if (!close(RENAMEDFILE)) {
1515 $self->document_warn(sprintf($self->__p(
1516 "see HTML Xref Link Preservation in the Texinfo manual for context",
1517 "error on closing node-renaming configuration file %s: %s"),
1518 $renamed_nodes_file, $!));
1519 }
1520 } else {
1521 $self->document_warn(sprintf($self->__("could not open %s: %s"),
1522 $renamed_nodes_file, $!));
1523 }
1524 return ($renamed_nodes, $renamed_nodes_lines);
1525}
1526
1527sub collect_renamed_nodes($$;$$)
1528{
1529 my $self = shift;
1530 my $basename = shift;
1531 my $renamed_nodes = shift;
1532 my $renamed_nodes_lines = shift;
1533
1534 my $renamed_nodes_file;
1535 if (defined($self->get_conf('RENAMED_NODES_FILE'))) {
1536 $renamed_nodes_file = $self->get_conf('RENAMED_NODES_FILE');
1537 } elsif (-f $basename . '-noderename.cnf') {
1538 $renamed_nodes_file = $basename . '-noderename.cnf';
1539 }
1540 if (defined($renamed_nodes_file)) {
1541 my ($renamed_nodes, $renamed_nodes_lines)
1542 = parse_renamed_nodes_file($self, $renamed_nodes_file, $renamed_nodes,
1543 $renamed_nodes_lines);
1544 return ($renamed_nodes, $renamed_nodes_lines, $renamed_nodes_file);
1545 }
1546 return (undef, undef, undef);
1547}
1548
1549sub normalize_top_node_name($)
1550{
1551 my $node = shift;
1552 if ($node =~ /^top$/i) {
1553 return 'Top';
1554 }
1555 return $node;
1556}
1557
1558sub _convert_text_options($)
1559{
1560 my $self = shift;
1561 my %options;
1562 if ($self->get_conf('ENABLE_ENCODING')) {
1563 if ($self->get_conf('OUTPUT_ENCODING_NAME')) {
1564 $options{'enabled_encoding'} = $self->get_conf('OUTPUT_ENCODING_NAME');
1565 } elsif ($self->get_conf('INPUT_ENCODING_NAME')) {
1566 $options{'enabled_encoding'} = $self->get_conf('INPUT_ENCODING_NAME');
1567 }
1568 }
1569 $options{'TEST'} = 1 if ($self->get_conf('TEST'));
1570 $options{'NUMBER_SECTIONS'} = $self->get_conf('NUMBER_SECTIONS');
1571 $options{'converter'} = $self;
1572 $options{'expanded_formats_hash'} = $self->{'expanded_formats_hash'};
1573 return %options;
1574}
1575
1576sub count_bytes($$;$)
1577{
1578 my $self = shift;
1579 my $string = shift;
1580 my $encoding = shift;
1581
1582 if (!defined($encoding) and $self and $self->get_conf('OUTPUT_PERL_ENCODING')) {
1583 $encoding = $self->get_conf('OUTPUT_PERL_ENCODING');
1584 }
1585
1586 if ($encoding and $encoding ne 'ascii') {
1587 return length(Encode::encode($encoding, $string));
1588 } else {
1589 return length($string);
1590 }
1591 # FIXME is the following required for correct count of end of lines?
1592 #if ($encoding) {
1593 # return length(Encode::encode($encoding, $string));
1594 #} else {
1595 # return length(Encode::encode('ascii', $string));
1596 #}
1597}
1598
1599# TODO
1600# also recurse into
1601# extra->misc_args, extra->args_index
1602# extra->index_entry extra->type
1603#
1604# extra that should point to other elements:
1605# command_as_argument
1606# @block_command_line_contents @brace_command_contents @misc_content end_command
1607# associated_section part_associated_section associated_node associated_part
1608# @prototypes @columnfractions titlepage quotation @author command
1609# menu_entry_description menu_entry_name
1610#
1611# should point to other elements, or be copied. And some should be recursed
1612# into too.
1613# extra->type->content
1614# extra->nodes_manuals->[]
1615# extra->node_content
1616# extra->node_argument
1617# extra->explanation_contents
1618# extra->menu_entry_node
1619# extra->def_arg
1620
1621
1622sub _copy_tree($$$);
1623sub _copy_tree($$$)
1624{
1625 my $current = shift;
1626 my $parent = shift;
1627 my $reference_associations = shift;
1628 my $new = {};
1629 $reference_associations->{$current} = $new;
1630 $new->{'parent'} = $parent if ($parent);
1631 foreach my $key ('type', 'cmdname', 'text') {
1632 $new->{$key} = $current->{$key} if (exists($current->{$key}));
1633 }
1634 foreach my $key ('args', 'contents') {
1635 if ($current->{$key}) {
1636 if (ref($current->{$key}) ne 'ARRAY') {
1637 my $command_or_type = '';
1638 if ($new->{'cmdname'}) {
1639 $command_or_type = '@'.$new->{'cmdname'};
1640 } elsif ($new->{'type'}) {
1641 $command_or_type = $new->{'type'};
1642 }
1643 print STDERR "Not an array [$command_or_type] $key ".ref($current->{$key})."\n";
1644 }
1645 $new->{$key} = [];
1646 $reference_associations->{$current->{$key}} = $new->{$key};
1647 foreach my $child (@{$current->{$key}}) {
1648 push @{$new->{$key}}, _copy_tree($child, $new, $reference_associations);
1649 }
1650 }
1651 }
1652 if ($current->{'extra'}) {
1653 $new->{'extra'} = {};
1654 foreach my $key (keys %{$current->{'extra'}}) {
1655 if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable'
1656 and $key eq 'prototypes') {
1657 $new->{'extra'}->{$key} = [];
1658 $reference_associations->{$current->{'extra'}->{$key}} = $new->{$key};
1659 foreach my $child (@{$current->{'extra'}->{$key}}) {
1660 push @{$new->{'extra'}->{$key}},
1661 _copy_tree($child, $new, $reference_associations);
1662 }
1663 } elsif (!ref($current->{'extra'}->{$key})) {
1664 $new->{'extra'}->{$key} = $current->{'extra'}->{$key};
1665 }
1666 }
1667 }
1668 return $new;
1669}
1670
1671# Not used.
1672sub _collect_references($$);
1673sub _collect_references($$)
1674{
1675 my $current = shift;
1676 my $references = shift;
1677 foreach my $key ('args', 'contents') {
1678 if ($current->{$key}) {
1679 $references->{$current->{$key}} = $current->{$key};
1680 foreach my $child (@{$current->{$key}}) {
1681 $references->{$child} = $child;
1682 _collect_references($child, $references);
1683 }
1684 }
1685 }
1686}
1687
1688sub _substitute_references_in_array($$$);
1689sub _substitute_references_in_array($$$)
1690{
1691 my $array = shift;
1692 my $reference_associations = shift;
1693 my $context = shift;
1694
1695 my $result = [];
1696 my $index = 0;
1697 foreach my $item (@{$array}) {
1698 if (!ref($item)) {
1699 push @{$result}, $item;
1700 } elsif ($reference_associations->{$item}) {
1701 push @{$result}, $reference_associations->{$item};
1702 } elsif (ref($item) eq 'ARRAY') {
1703 push @$result,
1704 _substitute_references_in_array($item, $reference_associations,
1705 "$context [$index]");
1706 } elsif (defined($item->{'text'})) {
1707 my $new_text = _copy_tree($item, undef, $reference_associations);
1708 substitute_references($item, $new_text, $reference_associations);
1709 push @{$result}, $new_text;
1710 } else {
1711 print STDERR "Trouble with $context [$index] (".ref($item).")\n";
1712 push @{$result}, undef;
1713 }
1714 $index++;
1715 }
1716 return $result;
1717}
1718
1719sub substitute_references($$$);
1720sub substitute_references($$$)
1721{
1722 my $current = shift;
1723 my $new = shift;
1724 my $reference_associations = shift;
1725
1726 foreach my $key ('args', 'contents') {
1727 if ($new->{$key}) {
1728 my $index = 0;
1729 foreach my $child (@{$new->{$key}}) {
1730 substitute_references($child, $current->{$key}->[$index],
1731 $reference_associations);
1732 $index++;
1733 }
1734 }
1735 }
1736 if ($current->{'extra'}) {
1737 foreach my $key (keys %{$current->{'extra'}}) {
1738 if (ref($current->{'extra'}->{$key})) {
1739 my $command_or_type = '';
1740 if ($new->{'cmdname'}) {
1741 $command_or_type = '@'.$new->{'cmdname'};
1742 } elsif ($new->{'type'}) {
1743 $command_or_type = $new->{'type'};
1744 }
1745
1746 if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable'
1747 and $key eq 'prototypes') {
1748 my $index = 0;
1749 foreach my $child (@{$new->{'extra'}->{$key}}) {
1750 substitute_references($child, $current->{'extra'}->{$key}->[$index],
1751 $reference_associations);
1752 $index++;
1753 }
1754 } elsif ($reference_associations->{$current->{'extra'}->{$key}}) {
1755 $new->{'extra'}->{$key}
1756 = $reference_associations->{$current->{'extra'}->{$key}};
1757 #print STDERR "Done [$command_or_type]: $key\n";
1758 } else {
1759 if (ref($current->{'extra'}->{$key}) eq 'ARRAY') {
1760
1761 #print STDERR "Array $command_or_type -> $key\n";
1762 $new->{'extra'}->{$key} = _substitute_references_in_array(
1763 $current->{'extra'}->{$key}, $reference_associations,
1764 "[$command_or_type]{$key}");
1765 } else {
1766 if (($current->{'cmdname'}
1767 and ($current->{'cmdname'} eq 'listoffloats'
1768 or $current->{'cmdname'} eq 'float')
1769 and $key eq 'type')
1770 or ($key eq 'index_entry')
1771 or ($current->{'type'}
1772 and $current->{'type'} eq 'menu_entry'
1773 and $key eq 'menu_entry_node')) {
1774 foreach my $type_key (keys(%{$current->{'extra'}->{$key}})) {
1775 if (!ref($current->{'extra'}->{$key}->{$type_key})) {
1776 $new->{'extra'}->{$key}->{$type_key}
1777 = $current->{'extra'}->{$key}->{$type_key};
1778 } elsif ($reference_associations->{$current->{'extra'}->{$key}->{$type_key}}) {
1779 $new->{'extra'}->{$key}->{$type_key}
1780 = $reference_associations->{$current->{'extra'}->{$key}->{$type_key}};
1781 } elsif (ref($current->{'extra'}->{$key}->{$type_key}) eq 'ARRAY') {
1782 $new->{'extra'}->{$key}->{$type_key}
1783 = _substitute_references_in_array(
1784 $current->{'extra'}->{$key}->{$type_key},
1785 $reference_associations,
1786 "[$command_or_type]{$key}{$type_key}");
1787 } else {
1788 print STDERR "Not substituting [$command_or_type]{$key}: $type_key\n";
1789 }
1790 }
1791 } else {
1792 print STDERR "Not substituting [$command_or_type]: $key ($current->{'extra'}->{$key})\n";
1793 }
1794 }
1795 }
1796 }
1797 }
1798 }
1799}
1800
1801sub copy_tree($;$)
1802{
1803 my $current = shift;
1804 my $parent = shift;
1805 my $reference_associations = {};
1806 my $copy = _copy_tree($current, $parent, $reference_associations);
1807 substitute_references($current, $copy, $reference_associations);
1808 return $copy;
1809}
1810
1811sub modify_tree($$$;$);
1812sub modify_tree($$$;$)
1813{
1814 my $self = shift;
1815 my $tree = shift;
1816 my $operation = shift;
1817 my $argument = shift;
1818 #print STDERR "modify_tree tree: $tree\n";
1819
1820 if ($tree->{'args'}) {
1821 my @args = @{$tree->{'args'}};
1822 for (my $i = 0; $i <= $#args; $i++) {
1823 my @new_args = &$operation($self, 'arg', $args[$i], $argument);
1824 modify_tree($self, $args[$i], $operation, $argument);
1825 # this puts the new args at the place of the old arg using the
1826 # offset from the end of the array
1827 splice (@{$tree->{'args'}}, $i - $#args -1, 1, @new_args);
1828 #foreach my $arg (@new_args) {
1829 # modify_tree($self, $arg, $operation);
1830 #}
1831 }
1832 }
1833 if ($tree->{'contents'}) {
1834 my @contents = @{$tree->{'contents'}};
1835 for (my $i = 0; $i <= $#contents; $i++) {
1836 my @new_contents = &$operation($self, 'content', $contents[$i], $argument);
1837 modify_tree($self, $contents[$i], $operation, $argument);
1838 # this puts the new contents at the place of the old content using the
1839 # offset from the end of the array
1840 splice (@{$tree->{'contents'}}, $i - $#contents -1, 1, @new_contents);
1841 #foreach my $content (@new_contents) {
1842 # modify_tree($self, $content, $operation);
1843 #}
1844 }
1845 }
1846 return $tree;
1847}
1848
1849sub _protect_comma($$$)
1850{
1851 my $self = shift;
1852 my $type = shift;
1853 my $current = shift;
1854
1855 return _protect_text($current, quotemeta(','));
1856}
1857
1858sub protect_comma_in_tree($)
1859{
1860 my $tree = shift;
1861 return modify_tree(undef, $tree, \&_protect_comma);
1862}
1863
1864sub _new_asis_command_with_text($$;$)
1865{
1866 my $text = shift;
1867 my $parent = shift;
1868 my $text_type = shift;
1869 my $new_command = {'cmdname' => 'asis', 'parent' => $parent };
1870 push @{$new_command->{'args'}}, {'type' => 'brace_command_arg',
1871 'parent' => $new_command};
1872 push @{$new_command->{'args'}->[0]->{'contents'}}, {
1873 'text' => $text,
1874 'parent' => $new_command->{'args'}->[0]};
1875 if (defined($text_type)) {
1876 $new_command->{'args'}->[0]->{'contents'}->[0]->{'type'} = $text_type;
1877 }
1878 return $new_command;
1879}
1880
1881sub _protect_text($$)
1882{
1883 my $current = shift;
1884 my $to_protect = shift;
1885
1886 #print STDERR "$to_protect: $current "._print_current($current)."\n";
1887 if (defined($current->{'text'}) and $current->{'text'} =~ /$to_protect/
1888 and !(defined($current->{'type'}) and $current->{'type'} eq 'raw')) {
1889 my @result = ();
1890 my $remaining_text = $current->{'text'};
1891 while ($remaining_text) {
1892 if ($remaining_text =~ s/^(.*?)(($to_protect)+)//) {
1893 if ($1 ne '') {
1894 push @result, {'text' => $1, 'parent' => $current->{'parent'}};
1895 $result[-1]->{'type'} = $current->{'type'}
1896 if defined($current->{'type'});
1897 }
1898 if ($to_protect eq quotemeta(',')) {
1899 for (my $i = 0; $i < length($2); $i++) {
1900 push @result, {'cmdname' => 'comma', 'parent' => $current->{'parent'},
1901 'args' => [{'type' => 'brace_command_arg'}]};
1902 }
1903 } else {
1904 push @result, _new_asis_command_with_text($2, $current->{'parent'},
1905 $current->{'type'});
1906 }
1907 } else {
1908 push @result, {'text' => $remaining_text, 'parent' => $current->{'parent'}};
1909 $result[-1]->{'type'} = $current->{'type'}
1910 if defined($current->{'type'});
1911 last;
1912 }
1913 }
1914 #print STDERR "Result: @result\n";
1915 return @result;
1916 } else {
1917 #print STDERR "No change: $current\n";
1918 return ($current);
1919 }
1920}
1921
1922sub _protect_colon($$$)
1923{
1924 my $self = shift;
1925 my $type = shift;
1926 my $current = shift;
1927
1928 return _protect_text ($current, quotemeta(':'));
1929}
1930
1931sub protect_colon_in_tree($)
1932{
1933 my $tree = shift;
1934 return modify_tree(undef, $tree, \&_protect_colon);
1935}
1936
1937sub _protect_node_after_label($$$)
1938{
1939 my $self = shift;
1940 my $type = shift;
1941 my $current = shift;
1942
1943 return _protect_text ($current, '['. quotemeta(".\t,") .']');
1944}
1945
1946sub protect_node_after_label_in_tree($)
1947{
1948 my $tree = shift;
1949 return modify_tree(undef, $tree, \&_protect_node_after_label);
1950}
1951
1952sub _is_cpp_line($)
1953{
1954 my $text = shift;
1955 return 1 if ($text =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/);
1956 return 0;
1957}
1958
1959sub _protect_hashchar_at_line_beginning($$$)
1960{
1961 my $self = shift;
1962 my $type = shift;
1963 my $current = shift;
1964
1965 #print STDERR "$type $current "._print_current($current)."\n";
1966 # if the next is a hash character at line beginning, mark it
1967 if (defined($current->{'text'}) and $current->{'text'} =~ /\n$/
1968 and $current->{'parent'} and $current->{'parent'}->{'contents'}) {
1969 my $parent = $current->{'parent'};
1970 #print STDERR "End of line in $current, parent $parent: (@{$parent->{'contents'}})\n";
1971 my $current_found = 0;
1972 foreach my $content (@{$parent->{'contents'}}) {
1973 if ($current_found) {
1974 #print STDERR "after $current: $content $content->{'text'}\n";
1975 if ($content->{'text'} and _is_cpp_line($content->{'text'})) {
1976 $content->{'extra'}->{'_protect_hashchar'} = 1;
1977 }
1978 last;
1979 } elsif ($content eq $current) {
1980 $current_found = 1;
1981 }
1982 }
1983 }
1984
1985 my $protect_hash = 0;
1986 # if marked, or first and a cpp_line protect a leading hash character
1987 if ($current->{'extra'} and $current->{'extra'}->{'_protect_hashchar'}) {
1988 delete $current->{'extra'}->{'_protect_hashchar'};
1989 if (!scalar(keys(%{$current->{'extra'}}))) {
1990 delete $current->{'extra'};
1991 }
1992 $protect_hash = 1;
1993 } elsif ($current->{'parent'} and $current->{'parent'}->{'contents'}
1994 and $current->{'parent'}->{'contents'}->[0]
1995 and $current->{'parent'}->{'contents'}->[0] eq $current
1996 and $current->{'text'}
1997 and _is_cpp_line($current->{'text'})) {
1998 $protect_hash = 1;
1999 }
2000 if ($protect_hash) {
2001 my @result = ();
2002 if ($current->{'type'} and $current->{'type'} eq 'raw') {
2003 if ($self) {
2004 my $parent = $current->{'parent'};
2005 while ($parent) {
2006 if ($parent->{'cmdname'} and $parent->{'line_nr'}) {
2007 $self->line_warn(sprintf($self->__(
2008 "could not protect hash character in \@%s"),
2009 $parent->{'cmdname'}), $parent->{'line_nr'});
2010 last;
2011 }
2012 $parent = $parent->{'parent'};
2013 }
2014 }
2015 } else {
2016 $current->{'text'} =~ s/^(\s*)#//;
2017 if ($1 ne '') {
2018 push @result, {'text' => $1, 'parent' => $current->{'parent'}};
2019 }
2020 push @result, {'cmdname' => 'hashchar', 'parent' => $current->{'parent'},
2021 'args' => [{'type' => 'brace_command_arg'}]};
2022 }
2023 push @result, $current;
2024 return @result;
2025 } else {
2026 return ($current);
2027 }
2028}
2029
2030sub protect_hashchar_at_line_beginning($$)
2031{
2032 my $self = shift;
2033 my $tree = shift;
2034 return modify_tree($self, $tree, \&_protect_hashchar_at_line_beginning);
2035}
2036
2037sub protect_first_parenthesis($)
2038{
2039 my $contents = shift;
2040 return undef if (!defined ($contents));
2041 my @contents = @$contents;
2042 my $brace;
2043 if ($contents[0] and $contents->[0]{'text'} and $contents[0]->{'text'} =~ /^\(/) {
2044 if ($contents[0]->{'text'} !~ /^\($/) {
2045 $brace = shift @contents;
2046 my $brace_text = $brace->{'text'};
2047 $brace_text =~ s/^\(//;
2048 unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'},
2049 'parent' => $brace->{'parent'} } if $brace_text ne '';
2050 } else {
2051 $brace = shift @contents;
2052 }
2053 unshift @contents, _new_asis_command_with_text('(', $brace->{'parent'},
2054 $brace->{'type'});
2055 }
2056 return \@contents;
2057}
2058
2059sub find_parent_root_command($$)
2060{
2061 my $parser = shift;
2062 my $current = shift;
2063
2064 my $root_command;
2065 while (1) {
2066 if ($current->{'cmdname'}) {
2067 if ($root_commands{$current->{'cmdname'}}) {
2068 return $current;
2069 } elsif ($region_commands{$current->{'cmdname'}}) {
2070 if ($current->{'cmdname'} eq 'copying' and $parser
2071 and $parser->{'extra'} and $parser->{'extra'}->{'insertcopying'}) {
2072 foreach my $insertcopying(@{$parser->{'extra'}->{'insertcopying'}}) {
2073 my $root_command
2074 = $parser->find_parent_root_command($insertcopying);
2075 return $root_command if (defined($root_command));
2076 }
2077 } else {
2078 return undef;
2079 }
2080 }
2081 }
2082 if ($current->{'parent'}) {
2083 $current = $current->{'parent'};
2084 } else {
2085 return undef;
2086 }
2087 }
2088 # Should never get there
2089 return undef;
2090}
2091
2092# for debugging
2093sub _print_current($)
2094{
2095 my $current = shift;
2096 if (ref($current) ne 'HASH') {
2097 return "_print_current: $current not a hash\n";
2098 }
2099 my $type = '';
2100 my $cmd = '';
2101 my $parent_string = '';
2102 my $text = '';
2103 $type = "($current->{'type'})" if (defined($current->{'type'}));
2104 $cmd = "\@$current->{'cmdname'}" if (defined($current->{'cmdname'}));
2105 $cmd .= "($current->{'level'})" if (defined($current->{'level'}));
2106 $text = "[text: $current->{'text'}]" if (defined($current->{'text'}));
2107 if ($current->{'parent'}) {
2108 my $parent = $current->{'parent'};
2109 my $parent_cmd = '';
2110 my $parent_type = '';
2111 $parent_cmd = "\@$parent->{'cmdname'}" if (defined($parent->{'cmdname'}));
2112 $parent_type = "($parent->{'type'})" if (defined($parent->{'type'}));
2113 $parent_string = " <- $parent_cmd$parent_type\n";
2114 }
2115 my $args = '';
2116 my $contents = '';
2117 $args = "args(".scalar(@{$current->{'args'}}).')' if $current->{'args'};
2118 $contents = "contents(".scalar(@{$current->{'contents'}}).')'
2119 if $current->{'contents'};
2120 if ("$cmd$type" ne '') {
2121 return "$cmd$type : $text $args $contents\n$parent_string";
2122 } else {
2123 return "$text $args $contents\n$parent_string";
2124 }
2125}
2126
2127sub move_index_entries_after_items($) {
2128 # enumerate or itemize
2129 my $current = shift;
2130
2131 return unless ($current->{'contents'});
2132
2133 my $previous;
2134 foreach my $item (@{$current->{'contents'}}) {
2135 #print STDERR "Before proceeding: $previous $item->{'cmdname'} (@{$previous->{'contents'}})\n" if ($previous and $previous->{'contents'});
2136 if (defined($previous) and $item->{'cmdname'}
2137 and $item->{'cmdname'} eq 'item'
2138 and $previous->{'contents'} and scalar(@{$previous->{'contents'}})) {
2139
2140 my $previous_ending_container;
2141 if ($previous->{'contents'}->[-1]->{'type'}
2142 and ($previous->{'contents'}->[-1]->{'type'} eq 'paragraph'
2143 or $previous->{'contents'}->[-1]->{'type'} eq 'preformatted')) {
2144 $previous_ending_container = $previous->{'contents'}->[-1];
2145 } else {
2146 $previous_ending_container = $previous;
2147 }
2148
2149 my @gathered_index_entries;
2150
2151 #print STDERR "Gathering for item $item in previous $previous ($previous_ending_container)\n";
2152 while ($previous_ending_container->{'contents'}->[-1]
2153 and (($previous_ending_container->{'contents'}->[-1]->{'type'}
2154 and $previous_ending_container->{'contents'}->[-1]->{'type'} eq 'index_entry_command')
2155 or ($previous_ending_container->{'contents'}->[-1]->{'cmdname'}
2156 and ($previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'c'
2157 or $previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'comment')))) {
2158 unshift @gathered_index_entries, pop @{$previous_ending_container->{'contents'}};
2159 }
2160 #print STDERR "Gathered: @gathered_index_entries\n";
2161 if (scalar(@gathered_index_entries)) {
2162 # put back leading comments
2163 while ($gathered_index_entries[0]
2164 and (!$gathered_index_entries[0]->{'type'}
2165 or $gathered_index_entries[0]->{'type'} ne 'index_entry_command')) {
2166 #print STDERR "Putting back $gathered_index_entries[0] $gathered_index_entries[0]->{'cmdname'}\n";
2167 push @{$previous_ending_container->{'contents'}},
2168 shift @gathered_index_entries;
2169 }
2170
2171 # We have the index entries of the previous @item or before item.
2172 # Now put them right after the current @item command.
2173 if (scalar(@gathered_index_entries)) {
2174 my $item_container;
2175 if ($item->{'contents'} and $item->{'contents'}->[0]
2176 and $item->{'contents'}->[0]->{'type'}
2177 and $item->{'contents'}->[0]->{'type'} eq 'preformatted') {
2178 $item_container = $item->{'contents'}->[0];
2179 } else {
2180 $item_container = $item;
2181 }
2182 foreach my $entry(@gathered_index_entries) {
2183 $entry->{'parent'} = $item_container;
2184 }
2185 if ($item_container->{'contents'}
2186 and $item_container->{'contents'}->[0]
2187 and $item_container->{'contents'}->[0]->{'type'}) {
2188 if ($item_container->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') {
2189
2190 unshift @gathered_index_entries, shift @{$item_container->{'contents'}};
2191 } elsif ($item_container->{'contents'}->[0]->{'type'} eq 'empty_spaces_after_command') {
2192 unshift @gathered_index_entries, shift @{$item_container->{'contents'}};
2193 $gathered_index_entries[0]->{'type'} = 'empty_line_after_command';
2194 $gathered_index_entries[0]->{'text'} .= "\n";
2195 }
2196 }
2197 unshift @{$item_container->{'contents'}}, @gathered_index_entries;
2198 }
2199 }
2200 }
2201 $previous = $item;
2202 }
2203}
2204
2205sub _move_index_entries_after_items($$$)
2206{
2207 my $self = shift;
2208 my $type = shift;
2209 my $current = shift;
2210
2211 if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'enumerate'
2212 or $current->{'cmdname'} eq 'itemize')) {
2213 move_index_entries_after_items($current);
2214 }
2215 return ($current);
2216}
2217
2218sub move_index_entries_after_items_in_tree($)
2219{
2220 my $tree = shift;
2221 return modify_tree(undef, $tree, \&_move_index_entries_after_items);
2222}
2223
22241;
2225
2226__END__
2227
2228=head1 NAME
2229
2230Texinfo::Common - Classification of commands and miscellaneous methods
2231
2232=head1 SYNOPSIS
2233
2234 use Texinfo::Common qw(expand_today expand_verbatiminclude);
2235 if ($Texinfo::Common::accent_commands{$a_command}) {
2236 print STDERR "$a_command is an accent command\n";
2237 }
2238
2239 my $today_tree = expand_today($converter);
2240 my $verbatiminclude_tree
2241 = expand_verbatiminclude(undef, $verbatiminclude);
2242
2243=head1 DESCRIPTION
2244
2245Texinfo::Common holds interesting hashes classifying Texinfo @-commands,
2246as well as miscellaneous methods that may be useful for any backend
2247converting texinfo trees.
2248
2249It also defines, as our variable a hash for default indices,
2250named C<%index_names>. The format of this hash is described in
2251L<Texinfo::Parser/indices_information>.
2252
2253=head1 COMMAND CLASSES
2254
2255Hashes are defined as C<our> variables, and are therefore available
2256outside of the module.
2257
2258The key of the hashes are @-command names without the @. The
2259following hashes are available:
2260
2261=over
2262
2263=item %all_commands
2264
2265All the @-commands.
2266
2267=item %no_brace_commands
2268
2269Commands without brace with a single character as name, like C<*>
2270or C<:>. The value is an ascii representation of the command. It
2271may be an empty string.
2272
2273=item %misc_commands
2274
2275Command that do not take braces and are not block commands either, like
2276C<@node>, C<@chapter>, C<@cindex>, C<@deffnx>, C<@end>, C<@footnotestyle>,
2277C<@set>, C<@settitle>, C<@indent>, C<@definfoenclose>, C<@comment> and many
2278others.
2279
2280=item %default_index_commands
2281
2282Index entry commands corresponding to default indices. For example
2283C<@cindex>.
2284
2285=item %root_commands
2286
2287Commands that are at the root of a Texinfo document, namely
2288C<@node> and sectioning commands, except heading commands.
2289
2290=item %sectioning_commands
2291
2292All the sectioning and heading commands.
2293
2294=item %brace_commands
2295
2296The commands that take braces. The associated value is the maximum
2297number of arguments.
2298
2299=item %letter_no_arg_commands
2300
2301@-commands with braces but no argument corresponding to letters,
2302like C<@AA{}> or C<@ss{}> or C<@o{}>.
2303
2304=item %accent_commands
2305
2306Accent @-commands taking an argument, like C<@'> or C<@ringaccent>
2307including C<@dotless> and C<@tieaccent>.
2308
2309=item %style_commands
2310
2311Commands that mark a fragment of texinfo, like C<@strong>,
2312C<@cite>, C<@code> or C<@asis>.
2313
2314=item %code_style_commands
2315
2316I<style_commands> that have their argument in code style, like
2317C<@code>.
2318
2319=item %regular_font_style_commands
2320
2321I<style_commands> that have their argument in regular font, like
2322C<@r> or C<@slanted>.
2323
2324=item %context_brace_commands
2325
2326@-commands with brace like C<@footnote>, C<@caption> and C<@math>
2327whose argument is outside of the main text flow in one way or another.
2328
2329=item %ref_commands
2330
2331Cross reference @-command referencing nodes, like C<@xref>.
2332
2333=item %explained_commands
2334
2335@-commands whose second argument explain first argument and further
2336@-command call without first argument, as C<@abbr> and C<@acronym>.
2337
2338=item %block commands
2339
2340Commands delimiting a block with a closing C<@end>. The value
2341is I<conditional> for C<@if> commands, I<def> for definition
2342commands like C<@deffn>, I<raw> for @-commands that have no expansion
2343of @-commands in their bodies and I<multitable> for C<@multitable>.
2344Otherwise it is set to the number of arguments separated by commas
2345that may appear on the @-command line. That means 0 in most cases,
23461 for C<@quotation> and 2 for C<@float>.
2347
2348=item %raw_commands
2349
2350@-commands that have no expansion of @-commands in their bodies,
2351as C<@macro>, C<@verbatim> or C<@ignore>.
2352
2353=item %format_raw_commands
2354
2355@-commands associated with raw output format, like C<@html>, or
2356C<@docbook>.
2357
2358=item %texinfo_output_formats
2359
2360Cannonical output formats that have associated conditionals. In
2361practice C<%format_raw_commands> plus C<info> and C<plaintext>.
2362
2363=item %def_commands
2364
2365=item %def_aliases
2366
2367Definition commands. C<%def_aliases> associates an aliased command
2368to the original command, for example C<defun> is associated to C<deffn>.
2369
2370=item %menu_commands
2371
2372@-commands with menu entries.
2373
2374=item %align_commands
2375
2376@-commands related with alignement of text.
2377
2378=item %region_commands
2379
2380Block @-commands that enclose full text regions, like C<@titlepage>.
2381
2382=item %preformatted_commands
2383
2384=item %preformatted_code_commands
2385
2386I<%preformatted_commands> is for commands whose content should not
2387be filled, like C<@example> or C<@display>. If the command is meant
2388for code, it is also in I<%preformatted_code_commands>, like C<@example>.
2389
2390=item %item_container_commands
2391
2392Commands holding C<@item> with C<@item> that contains blocks of text,
2393like C<@itemize>.
2394
2395=item %item_line_commands
2396
2397Commands with C<@item> that have their arguments on their lines, like
2398C<@ftable>.
2399
2400=back
2401
2402=head1 METHODS
2403
2404No method is exported in the default case.
2405
2406Most methods takes a I<$converter> as argument, sometime optionally,
2407to get some information and use methods for error reporting,
2408see L<Texinfo::Convert::Converter> and L<Texinfo::Report>.
2409
2410=over
2411
2412=item $tree = expand_today($converter)
2413
2414Expand today's date, as a texinfo tree with translations.
2415
2416=item $tree = expand_verbatiminclude($converter, $verbatiminclude)
2417
2418The I<$converter> argument may be undef. I<$verbatiminclude> is a
2419C<@verbatiminclude> tree element. This function returns a
2420C<@verbatim> tree elements after finding the included file and
2421reading it.
2422
2423=item $tree = definition_category($converter, $def_line)
2424
2425The I<$converter> argument may be undef. I<$def_line> is a
2426C<def_line> texinfo tree container. This function
2427returns a texinfo tree corresponding to the category of the
2428I<$def_line> taking the class into account, if there is one.
2429
2430=item $result = numbered_heading ($converter, $heading_element, $heading_text, $do_number)
2431
2432The I<$converter> argument may be undef. I<$heading_element> is
2433a heading command tree element. I<$heading_text> is the already
2434formatted heading text. if the I<$do_number> optional argument is
2435defined and false, no number is used and the text is returned as is.
2436This function returns the heading with a number and the appendix
2437part if needed.
2438
2439=item ($caption, $prepended) = float_name_caption ($converter, $float)
2440
2441I<$float> is a texinfo tree C<@float> element. This function
2442returns the caption that should be used for the float formatting
2443and the I<$prepended> texinfo tree combining the type and label
2444of the float.
2445
2446=item $text = enumerate_item_representation($specification, $number)
2447
2448This function returns the number or letter correponding to item
2449number I<$number> for an C<@enumerate> specification I<$specification>,
2450appearing on an C<@enumerate> line. For example
2451
2452 enumerate_item_representation('c', 3)
2453
2454is C<e>.
2455
2456=item trim_spaces_comment_from_content($contents)
2457
2458Remove empty spaces after commands or braces at begin and
2459spaces and comments at end from a content array, modifying it.
2460
2461=item $normalized_name = normalize_top_node_name ($node_string)
2462
2463Normalize the node name string given in argument, by normalizing
2464Top node case.
2465
2466=item protect_comma_in_tree($tree)
2467
2468Protect comma characters, replacing C<,> with @comma{} in tree.
2469
2470=item protect_colon_in_tree($tree)
2471
2472=item protect_node_after_label_in_tree($tree)
2473
2474Protect colon with C<protect_colon_in_tree> and characters that
2475are special in node names after a label in menu entries (tab
2476dot and comma) with C<protect_node_after_label_in_tree>.
2477The protection is achieved by putting protected characters
2478in C<@asis{}>.
2479
2480=item $contents_result = protect_first_parenthesis ($contents)
2481
2482Return a contents array reference with first parenthesis in the
2483contents array reference protected.
2484
2485=item protect_hashchar_at_line_beginning($parser, $tree)
2486
2487Protect hash character at beginning of line if the line is a cpp
2488line directive. The I<$parser> argument maybe undef, if it is
2489defined it is used for error reporting in case an hash character
2490could not be protected because it appeared in a raw environment.
2491
2492=item move_index_entries_after_items_in_tree($tree)
2493
2494In C<@enumerate> and C<@itemize> from the tree, move index entries
2495appearing just before C<@item> after the C<@item>. Comment lines
2496between index entries are moved too.
2497
2498=item $command = find_parent_root_command($parser, $tree_element)
2499
2500Find the parent root command of a tree element (sectioning command or node).
2501The C<$parser> argument is optional, it is used to continue
2502through C<@insertcopying> if in a C<@copying>.
2503
2504=item valid_tree_transformation($name)
2505
2506Return true if the I<$name> is a known tree transformation name
2507that may be passed with C<TREE_TRANSFORMATIONS> to modify a texinfo
2508tree.
2509
2510=back
2511
2512=head1 SEE ALSO
2513
2514L<Texinfo::Parser>, L<Texinfo::Convert::Converter> and L<Texinfo::Report>.
2515
2516=head1 AUTHOR
2517
2518Patrice Dumas, E<lt>pertusus@free.frE<gt>
2519
2520=head1 COPYRIGHT AND LICENSE
2521
2522Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
2523
2524This library is free software; you can redistribute it and/or modify
2525it under the terms of the GNU General Public License as published by
2526the Free Software Foundation; either version 3 of the License,
2527or (at your option) any later version.
2528
2529=cut
2530
02531
=== added file '.pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm'
--- .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm 1970-01-01 00:00:00 +0000
+++ .pc/maybe-upstream-fix-itemize-start/tp/Texinfo/Parser.pm 2013-07-14 15:17:24 +0000
@@ -0,0 +1,6996 @@
1# Parser.pm: parse texinfo code into a tree.
2#
3# Copyright 2010, 2011, 2012 Free Software Foundation, Inc.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License,
8# or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# Original author: Patrice Dumas <pertusus@free.fr>
19# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init.
20
21# The organization of the file is the following:
22# module definitions.
23# default parser state. With explanation of the internal structures.
24# initializations, determination of command types.
25# user visible subroutines.
26# internal subroutines, doing the parsing.
27
28package Texinfo::Parser;
29
30# We need the unicode stuff.
31use 5.006;
32use strict;
33
34# debug
35use Carp qw(cluck);
36
37use Data::Dumper;
38
39# to detect if an encoding may be used to open the files
40use Encode;
41
42# for fileparse
43use File::Basename;
44
45#use POSIX qw(setlocale LC_ALL LC_CTYPE LC_MESSAGES);
46
47# commands definitions
48use Texinfo::Common;
49# Error reporting and counting, translation of strings.
50use Texinfo::Report;
51# encoding_alias
52use Texinfo::Encoding;
53
54# to expand file names in @include and similar @-commands
55use Texinfo::Convert::Text;
56# to normalize node name, anchor, float arg, listoffloats and first *ref argument.
57use Texinfo::Convert::NodeNameNormalization;
58# in error messages, and for macro body expansion
59use Texinfo::Convert::Texinfo;
60
61require Exporter;
62use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
63@ISA = qw(Exporter Texinfo::Report);
64
65# Items to export into callers namespace by default. Note: do not export
66# names by default without a very good reason. Use EXPORT_OK instead.
67# Do not simply export all your public functions/methods/constants.
68
69# This allows declaration use Texinfo::Parser ':all';
70# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
71# will save memory.
72%EXPORT_TAGS = ( 'all' => [ qw(
73 parser
74 parse_texi_text
75 parse_texi_line
76 parse_texi_file
77 indices_information
78 floats_information
79 internal_references_information
80 labels_information
81 global_commands_information
82 global_informations
83) ] );
84
85@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
86
87@EXPORT = qw(
88);
89
90$VERSION = '5.0';
91
92sub N__($)
93{
94 return $_[0];
95}
96
97#sub __($$)
98#{
99# my $self = shift;
100# return &{$self->{'gettext'}}(@_);
101#}
102
103# Customization variables obeyed by the Parser, and the default values.
104our %default_customization_values = (
105 'TEST' => 0,
106 'DEBUG' => 0, # if >= 10, tree is printed in texi2any.pl after parsing.
107 # If >= 100 tree is printed every line.
108 'SHOW_MENU' => 1, # if false no menu error related.
109 'INLINE_INSERTCOPYING' => 0,
110 'IGNORE_BEFORE_SETFILENAME' => 1,
111 'MACRO_BODY_IGNORES_LEADING_SPACE' => 0,
112 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME' => 1,
113 'INPUT_PERL_ENCODING' => undef, # input perl encoding name, set from
114 # @documentencoding in the default case
115 'INPUT_ENCODING_NAME' => undef, # encoding name normalized as preferred
116 # IANA, set from @documentencoding in the default
117 # case
118 'CPP_LINE_DIRECTIVES' => 1, # handle cpp like synchronization lines
119 'MAX_MACRO_CALL_NESTING' => 100000, # max number of nested macro calls
120 'GLOBAL_COMMANDS' => [], # list of commands registered
121 # This is not used directly, but passed to Convert::Text through
122 # Texinfo::Common::_convert_text_options
123 'ENABLE_ENCODING' => 1, # output accented and special characters
124 # based on @documentencoding
125 # following are used in Texinfo::Structuring
126 'TOP_NODE_UP' => '(dir)', # up node of Top node
127 'SIMPLE_MENU' => 0, # not used in the parser but in structuring
128 'USE_UP_NODE_FOR_ELEMENT_UP' => 0, # Use node up for Up if there is no
129 # section up.
130);
131
132my %parser_default_configuration = (%Texinfo::Common::default_parser_state_configuration,
133 %default_customization_values);
134
135# the other possible keys for the parser state are:
136#
137# expanded_formats_hash each key comes from expanded_formats value is 1
138# index_names a structure holding the link between index
139# names, prefixes, merged indices,
140# initial value is %index_names in Texinfo::Common.
141# context_stack stack of the contexts, more recent on top.
142# 'line' is added when on a line or
143# block @-command line,
144# 'def' is added instead if on a definition line.
145# 'preformatted' is added in block commands
146# where there is no paragraphs and spaces are kept
147# (format, example, display...)
148# 'rawpreformatted' is added in raw block commands
149# (html, xml, docbook...)
150# 'menu' is added in menu commands
151# 'math', 'footnote', 'caption', 'shortcaption',
152# 'inlineraw' are also added when in those commands
153# conditionals_stack a stack of conditional commands that are expanded.
154# raw_formats_stack a stack of 1 or 0 for raw formats (@html... or
155# @inlineraw), is 0 if within a raw format that is
156# not expanded.
157# macro_stack stack of macros being expanded (more recent first)
158# definfoenclose an hash, key is the command name, value is an array
159# reference with 2 values, beginning and ending.
160# input a stack, with last at bottom. Holds the opened files
161# or text. Pending macro expansion or text expansion
162# is also in that structure.
163# misc_commands the same than %misc_commands in Texinfo::Common,
164# but with index entry commands dynamically added
165# close_paragraph_commands same than %close_paragraph_commands, but with
166# insertcopying removed if INLINE_INSERTCOPYING
167# close_preformatted_commands same than %close_preformatted_commands, but with
168# insertcopying removed if INLINE_INSERTCOPYING
169# no_paragraph_commands the same than %default_no_paragraph_commands
170# below, with index
171# entry commands dynamically added
172# simple_text_commands the same than %simple_text_commands below, but
173# with index entry commands dynamically added
174# current_node last seen node.
175# current_section last seen section.
176# nodes list of nodes.
177# command_index_prefix associate a command name to an index prefix.
178# prefix_to_index_name associate an index prefix to the index name.
179# floats key is the normalized float type, value is an array
180# reference holding all the floats.
181# internal_references an array holding all the internal references.
182
183# set points to the value set when initializing, for
184# configuration items that are not to be overriden
185# by @-commands. For example documentlanguage.
186
187# A line information is an hash reference with the keys:
188# line_nr the line number
189# file_name the file name
190# macro if in a macro expansion, the name of the macro
191#
192# A text fragment information is a 2 element array reference, the first is the
193# text fragment, the second is the line information.
194
195# The input structure is an array, the first is the most recently included
196# file. The last element may be a file if the parsing is done on a file,
197# with parse_texi_file, or simply pending text, if called as parse_texi_text.
198# each element of the array is a hash reference. The key are:
199# pending an array reference containing pending text fragments, either the
200# text given as parse_texi_text or macro expansion text.
201# name file name
202# line_nr current line number in the file
203# fh filehandle for the file
204
205# content is not copied but reference is copied when duplicating a parser.
206my %tree_informations;
207foreach my $tree_information ('values', 'macros', 'explained_commands', 'labels') {
208 $tree_informations{$tree_information} = 1;
209}
210
211# The commands in initialization_overrides are not set in the document if
212# set at the parser initialization.
213my %initialization_overrides = (
214 'INPUT_ENCODING_NAME' => 1,
215 'documentlanguage' => 1,
216);
217
218my %no_brace_commands = %Texinfo::Common::no_brace_commands;
219my %misc_commands = %Texinfo::Common::misc_commands;
220my %brace_commands = %Texinfo::Common::brace_commands;
221my %accent_commands = %Texinfo::Common::accent_commands;
222my %context_brace_commands = %Texinfo::Common::context_brace_commands;
223my %block_commands = %Texinfo::Common::block_commands;
224my %block_item_commands = %Texinfo::Common::block_item_commands;
225my %close_paragraph_commands = %Texinfo::Common::close_paragraph_commands;
226my %def_map = %Texinfo::Common::def_map;
227my %def_commands = %Texinfo::Common::def_commands;
228my %def_aliases = %Texinfo::Common::def_aliases;
229my %menu_commands = %Texinfo::Common::menu_commands;
230my %preformatted_commands = %Texinfo::Common::preformatted_commands;
231my %format_raw_commands = %Texinfo::Common::format_raw_commands;
232my %item_container_commands = %Texinfo::Common::item_container_commands;
233my %item_line_commands = %Texinfo::Common::item_line_commands;
234my %deprecated_commands = %Texinfo::Common::deprecated_commands;
235my %root_commands = %Texinfo::Common::root_commands;
236my %sectioning_commands = %Texinfo::Common::sectioning_commands;
237my %command_index_prefix = %Texinfo::Common::command_index_prefix;
238my %command_structuring_level = %Texinfo::Common::command_structuring_level;
239my %ref_commands = %Texinfo::Common::ref_commands;
240my %region_commands = %Texinfo::Common::region_commands;
241my %code_style_commands = %Texinfo::Common::code_style_commands;
242my %in_heading_commands = %Texinfo::Common::in_heading_commands;
243my %explained_commands = %Texinfo::Common::explained_commands;
244my %inline_format_commands = %Texinfo::Common::inline_format_commands;
245my %all_commands = %Texinfo::Common::all_commands;
246
247# equivalence between a @set flag and an @@-command
248my %set_flag_command_equivalent = (
249 'txicodequoteundirected' => 'codequoteundirected',
250 'txicodequotebacktick' => 'codequotebacktick',
251# 'txideftypefnnl' => 'deftypefnnewline',
252);
253
254
255# keep line information for those commands.
256my %keep_line_nr_brace_commands = %context_brace_commands;
257foreach my $keep_line_nr_brace_command ('titlefont', 'anchor') {
258 $keep_line_nr_brace_commands{$keep_line_nr_brace_command} = 1;
259}
260foreach my $brace_command (keys (%brace_commands)) {
261 $keep_line_nr_brace_commands{$brace_command} = 1
262 if ($brace_commands{$brace_command} > 1);
263}
264
265my %type_with_paragraph;
266foreach my $type ('before_item', 'text_root', 'document_root',
267 'brace_command_context') {
268 $type_with_paragraph{$type} = 1;
269}
270
271my %command_ignore_space_after;
272foreach my $command ('anchor', 'hyphenation', 'caption', 'shortcaption') {
273 $command_ignore_space_after{$command} = 1;
274}
275
276my %global_multiple_commands;
277foreach my $global_multiple_command (
278 'author', 'footnote', 'hyphenation', 'insertcopying', 'printindex',
279 'subtitle','titlefont', 'listoffloats', 'detailmenu',
280 keys(%Texinfo::Common::document_settable_at_commands), ) {
281 $global_multiple_commands{$global_multiple_command} = 1;
282}
283
284my %global_unique_commands;
285foreach my $global_unique_command (
286 'copying', 'settitle',
287 'shorttitlepage', 'title', 'titlepage', 'top',
288 keys(%Texinfo::Common::document_settable_unique_at_commands), ) {
289 $global_unique_commands{$global_unique_command} = 1;
290}
291
292my %index_names = %Texinfo::Common::index_names;
293
294# index names that cannot be set by the user.
295my %forbidden_index_name = ();
296
297foreach my $name(keys(%index_names)) {
298 foreach my $prefix (@{$index_names{$name}->{'prefix'}}) {
299 $forbidden_index_name{$prefix} = 1;
300 }
301}
302
303foreach my $other_forbidden_index_name ('info','ps','pdf','htm',
304 'html', 'log','aux','dvi','texi','txi','texinfo','tex','bib') {
305 $forbidden_index_name{$other_forbidden_index_name} = 1;
306}
307
308# @-commands that do not start a paragraph
309my %default_no_paragraph_commands;
310# @-commands that should be at a line beginning
311my %begin_line_commands;
312
313foreach my $command ('node', 'end') {
314 $begin_line_commands{$command} = $command;
315}
316
317foreach my $no_paragraph_command ('titlefont', 'caption', 'shortcaption',
318 'image', '*', 'hyphenation', 'anchor', 'errormsg') {
319 $default_no_paragraph_commands{$no_paragraph_command} = 1;
320}
321
322foreach my $no_paragraph_command (keys(%misc_commands)) {
323 $default_no_paragraph_commands{$no_paragraph_command} = 1;
324 $begin_line_commands{$no_paragraph_command} = 1;
325}
326
327# verbatiminclude is not said to begin at the beginning of the line
328# in the manual
329foreach my $misc_not_begin_line ('comment', 'c', 'sp', 'refill',
330 'noindent', 'indent', 'columnfractions',
331 'tab', 'item', 'headitem', 'verbatiminclude',
332 'set', 'clear',
333 'vskip', keys(%in_heading_commands)) {
334 delete $begin_line_commands{$misc_not_begin_line};
335}
336
337my %block_arg_commands;
338foreach my $block_command (keys(%block_commands)) {
339 $begin_line_commands{$block_command} = 1;
340 $default_no_paragraph_commands{$block_command} = 1;
341 $block_arg_commands{$block_command} = 1
342 if ($block_commands{$block_command} ne 'raw');
343# and ! $format_raw_commands{$block_command});
344}
345
346my %close_preformatted_commands = %close_paragraph_commands;
347foreach my $no_close_preformatted('sp') {
348 delete $close_preformatted_commands{$no_close_preformatted};
349}
350# FIXME to close preformated or not to close?
351#foreach my $format_raw_command(keys(%format_raw_commands)) {
352# $close_preformatted_commands{$format_raw_command} = 1;
353#}
354
355# commands that may appear in accents
356my %in_accent_commands = %accent_commands;
357foreach my $brace_command(keys(%brace_commands)) {
358 $in_accent_commands{$brace_command} = 1 if (!$brace_commands{$brace_command});
359}
360foreach my $no_brace_command (keys(%no_brace_commands)) {
361 $in_accent_commands{$no_brace_command} = 1;
362}
363$in_accent_commands{'c'} = 1;
364$in_accent_commands{'comment'} = 1;
365
366# commands that may appear in texts arguments
367my %in_full_text_commands;
368foreach my $command (keys(%brace_commands), keys(%no_brace_commands)) {
369 $in_full_text_commands{$command} = 1;
370}
371foreach my $misc_command_in_full_text('c', 'comment', 'refill', 'noindent',
372 'indent', 'columnfractions', 'set', 'clear', 'end') {
373 $in_full_text_commands{$misc_command_in_full_text} = 1;
374}
375
376foreach my $out_format (keys(%format_raw_commands)) {
377 $in_full_text_commands{$out_format} = 1;
378}
379delete $in_full_text_commands{'caption'};
380delete $in_full_text_commands{'shortcaption'};
381foreach my $block_command (keys(%block_commands)) {
382 $in_full_text_commands{$block_command} = 1
383 if ($block_commands{$block_command} eq 'conditional');
384}
385
386# commands that may happen on lines where everything is
387# permitted
388my %in_full_line_commands = %in_full_text_commands;
389foreach my $not_in_full_line_commands('noindent', 'indent') {
390 delete $in_full_line_commands{$not_in_full_line_commands};
391}
392
393# commands that may happen on sectioning commands
394my %in_full_line_commands_no_refs = %in_full_line_commands;
395foreach my $not_in_full_line_commands_no_refs ('titlefont',
396 'anchor', 'footnote', 'verb') {
397 delete $in_full_line_commands_no_refs{$not_in_full_line_commands_no_refs};
398}
399
400# commands that may happen in simple text arguments
401my %in_simple_text_commands = %in_full_line_commands_no_refs;
402foreach my $not_in_simple_text_command('xref', 'ref', 'pxref', 'inforef') {
403 delete $in_simple_text_commands{$not_in_simple_text_command};
404}
405
406# commands that only accept simple text as argument in any context.
407my %simple_text_commands;
408foreach my $misc_command(keys(%misc_commands)) {
409 if ($misc_commands{$misc_command} =~ /^\d+$/
410 or ($misc_commands{$misc_command} eq 'line'
411 and !($sectioning_commands{$misc_command}
412 or $def_commands{$misc_command}))
413 or $misc_commands{$misc_command} eq 'text') {
414 $simple_text_commands{$misc_command} = 1;
415 }
416}
417
418my %full_line_commands_no_refs = (%sectioning_commands,
419 %def_commands);
420
421delete $simple_text_commands{'center'};
422delete $simple_text_commands{'exdent'};
423foreach my $command ('titlefont', 'anchor', 'xref','ref','pxref',
424 'inforef', 'shortcaption', 'math', 'indicateurl',
425 'email', 'uref', 'url', 'image', 'abbr', 'acronym',
426 'dmn', 'ctrl', 'errormsg') {
427 $simple_text_commands{$command} = 1;
428}
429
430# commands that accept full text, but no block or top-level commands
431my %full_text_commands;
432foreach my $brace_command (keys (%brace_commands)) {
433 if ($brace_commands{$brace_command} == 1
434 and !$simple_text_commands{$brace_command}
435 and !$context_brace_commands{$brace_command}
436 and !$accent_commands{$brace_command}) {
437 $full_text_commands{$brace_command} = 1;
438 }
439}
440
441# commands that accept almost the same than in full text, except
442# what do not make sense on a line.
443my %full_line_commands;
444$full_line_commands{'center'} = 1;
445$full_line_commands{'exdent'} = 1;
446$full_line_commands{'item'} = 1;
447$full_line_commands{'itemx'} = 1;
448
449# Fill the valid nestings hash. All commands not in that hash
450# are considered to accept anything within. There are additional
451# context tests, to make sure, for instance that we are testing
452# @-commands on the block, misc or node @-command line and not
453# in the content.
454# index entry commands are dynamically set as in_simple_text_commands
455my %default_valid_nestings;
456
457foreach my $command (keys(%accent_commands)) {
458 $default_valid_nestings{$command} = \%in_accent_commands;
459}
460foreach my $command (keys(%full_text_commands)) {
461 $default_valid_nestings{$command} = \%in_full_text_commands;
462}
463foreach my $command (keys(%simple_text_commands)) {
464 $default_valid_nestings{$command} = \%in_simple_text_commands;
465}
466foreach my $command (keys(%full_line_commands)) {
467 $default_valid_nestings{$command} = \%in_full_line_commands;
468}
469foreach my $command (keys(%full_line_commands_no_refs)) {
470 $default_valid_nestings{$command} = \%in_full_line_commands_no_refs;
471}
472# Only for block commands with line arguments
473foreach my $command (keys(%block_commands)) {
474 if ($block_commands{$command} and $block_commands{$command} ne 'raw'
475 and $block_commands{$command} ne 'conditional'
476 and !$def_commands{$command}) {
477 $default_valid_nestings{$command} = \%in_simple_text_commands;
478 }
479}
480
481
482my @preformatted_contexts = ('preformatted', 'rawpreformatted');
483my %preformatted_contexts;
484foreach my $preformatted_context (@preformatted_contexts) {
485 $preformatted_contexts{$preformatted_context} = 1;
486}
487
488# contexts on the context_stack stack where empty line don't trigger
489# paragraph
490my %no_paragraph_contexts;
491foreach my $no_paragraph_context ('math', 'menu', @preformatted_contexts,
492 'def', 'inlineraw') {
493 $no_paragraph_contexts{$no_paragraph_context} = 1;
494};
495
496
497
0498
499# Format a bug message
500sub _bug_message($$;$$)
501{
502 my $self = shift;
503 my $message = shift;
504 my $line_number = shift;
505 my $current = shift;
506
507 my $line_message = '';
508 if ($line_number) {
509 my $file = $line_number->{'file_name'};
510 $line_message
511 = "last location: $line_number->{'file_name'}:$line_number->{'line_nr'}";
512 if ($line_number->{'macro'} ne '') {
513 $line_message .= " (possibly involving $line_number->{'macro'})";
514 }
515 $line_message .= "\n";
516 }
517 my $message_context_stack = "context_stack: (@{$self->{'context_stack'}})\n";
518 my $current_element_message = '';
519 if ($current) {
520 $current_element_message = "current: ". _print_current($current);
521 }
522 warn "You found a bug: $message\n\n".
523 "Additional informations:\n".
524 $line_message.$message_context_stack.$current_element_message;
525
526}
527
528# simple deep copy of a structure
529sub _deep_copy($)
530{
531 my $struct = shift;
532 my $string = Data::Dumper->Dump([$struct], ['struct']);
533 eval $string;
534 return $struct;
535}
536
537# return true if effect of global commands should be ignored.
538sub _ignore_global_commands($)
539{
540 my $self = shift;
541 return !$self->{'raw_formats_stack'}->[-1];
542}
543
544# enter all the commands associated with an index name using the prefix
545# list
546sub _register_index_commands($$)
547{
548 my $self = shift;
549 my $index_name = shift;
550 if (!$self->{'index_names'}->{$index_name}->{'prefix'}) {
551 $self->{'index_names'}->{$index_name}->{'prefix'} = [$index_name];
552 }
553 if (!exists($self->{'index_names'}->{$index_name}->{'name'})) {
554 $self->{'index_names'}->{$index_name}->{'name'} = $index_name;
555 }
556 if (!exists($self->{'index_names'}->{$index_name}->{'contained_indices'})) {
557 $self->{'index_names'}->{$index_name}->{'contained_indices'}->{$index_name} = 1;
558 }
559 foreach my $prefix (@{$self->{'index_names'}->{$index_name}->{'prefix'}}) {
560 $self->{'misc_commands'}->{$prefix.'index'} = 'line';
561 $self->{'no_paragraph_commands'}->{$prefix.'index'} = 1;
562 $self->{'valid_nestings'}->{$prefix.'index'} = \%in_simple_text_commands;
563 $self->{'command_index_prefix'}->{$prefix.'index'} = $prefix;
564 $self->{'prefix_to_index_name'}->{$prefix} = $index_name;
565 }
566}
567
568# initialization entry point. Set up a parser.
569# The last argument, optional, is a hash provided by the user to change
570# the default values for what is present in %parser_default_configuration.
571# The exact arguments of the function depend on how it was called,
572# in a object oriented way or not.
573sub parser(;$$)
574{
575 my $class = shift;
576 my $conf;
577
578 my $parser = _deep_copy(\%parser_default_configuration);
579 # _deep_copy doesn't handle subs
580 $parser->{'gettext'} = $parser_default_configuration{'gettext'};
581 $parser->{'pgettext'} = $parser_default_configuration{'pgettext'};
582
583 # called not object-oriented
584 if (ref($class) eq 'HASH') {
585 #print STDERR "Not oo\n"
586 $conf = $class;
587 bless $parser;
588
589 } elsif (ref($class)) {
590 # called on an existing parser, interpreted as a duplication
591 my $old_parser = $class;
592 $class = ref($class);
593 foreach my $key (keys(%parser_default_configuration)) {
594 if ($tree_informations{$key}) {
595 if (defined($old_parser->{$key})) {
596 foreach my $info_key (keys(%{$old_parser->{$key}})) {
597 $parser->{$key}->{$info_key}
598 = $old_parser->{$key}->{$info_key};
599 }
600 }
601 } else {
602 $parser->{$key} = _deep_copy($old_parser->{$key});
603 }
604 }
605 #$parser = _deep_copy($old_parser);
606 $parser->{'gettext'} = $old_parser->{'gettext'};
607 $parser->{'pgettext'} = $old_parser->{'pgettext'};
608 bless $parser, $class;
609 $conf = shift;
610
611 } elsif (defined($class)) {
612 bless $parser, $class;
613 $conf = shift;
614 } else {
615 bless $parser;
616 $conf = shift;
617 }
618
619 if (defined($conf)) {
620 foreach my $key (keys(%$conf)) {
621 if (exists($parser_default_configuration{$key})) {
622 if (ref($conf->{$key}) ne 'CODE' and $key ne 'values') {
623 $parser->{$key} = _deep_copy($conf->{$key});
624 } else {
625 $parser->{$key} = $conf->{$key};
626 }
627 if ($initialization_overrides{$key}) {
628 $parser->{'set'}->{$key} = $parser->{$key};
629 }
630 } else {
631 warn "$key not a possible customization in Texinfo::Parser::parser\n";
632 }
633 }
634 }
635 #foreach my $value (keys %{$parser->{'values'}}) {
636 # print STDERR " -> $value $parser->{'values'}->{$value}\n";
637 #}
638 # Now initialize command hash that are dynamically modified, notably
639 # those for index commands, and lists, based on defaults and user provided.
640 $parser->{'misc_commands'} = _deep_copy (\%misc_commands);
641 $parser->{'valid_nestings'} = _deep_copy (\%default_valid_nestings);
642 $parser->{'no_paragraph_commands'} = { %default_no_paragraph_commands };
643 $parser->{'index_names'} = _deep_copy (\%index_names);
644 $parser->{'command_index_prefix'} = {%command_index_prefix};
645 $parser->{'close_paragraph_commands'} = {%close_paragraph_commands};
646 $parser->{'close_preformatted_commands'} = {%close_preformatted_commands};
647 if ($parser->{'INLINE_INSERTCOPYING'}) {
648 delete $parser->{'close_paragraph_commands'}->{'insercopying'};
649 delete $parser->{'close_preformatted_commands'}->{'insercopying'};
650 }
651 # a hash is simply concatenated. It should be like %index_names.
652 if (ref($parser->{'indices'}) eq 'HASH') {
653 %{$parser->{'index_names'}} = (%{$parser->{'index_names'}},
654 %{$parser->{'indices'}});
655 } else { # an array holds index names defined with @defindex
656 foreach my $name (@{$parser->{'indices'}}) {
657 $parser->{'index_names'}->{$name} = {'in_code' => 0};
658 }
659 }
660 foreach my $index (keys (%{$parser->{'index_names'}})) {
661 $parser->_register_index_commands($index);
662 }
663 if ($parser->{'merged_indices'}) {
664 foreach my $index_from (keys (%{$parser->{'merged_indices'}})) {
665 my $index_to = $parser->{'merged_indices'}->{$index_from};
666 if (defined($parser->{'index_names'}->{$index_from})
667 and defined($parser->{'index_names'}->{$index_to})) {
668 $parser->{'index_names'}->{$index_from}->{'merged_in'} = $index_to;
669 $parser->{'index_names'}->{$index_to}->{'contained_indices'}->{$index_from} = 1;
670 }
671 }
672 }
673 foreach my $explained_command(keys(%explained_commands)) {
674 if (!defined($parser->{'explained_commands'}->{$explained_command})) {
675 $parser->{'explained_commands'}->{$explained_command} = {};
676 }
677 }
678 $parser->{'context_stack'} = [ $parser->{'context'} ];
679 $parser->{'regions_stack'} = [];
680 $parser->{'macro_stack'} = [];
681 $parser->{'conditionals_stack'} = [];
682 $parser->{'raw_formats_stack'} = [1];
683
684 # turn the array to a hash for speed. Not sure it really matters for such
685 # a small array.
686 foreach my $expanded_format(@{$parser->{'expanded_formats'}}) {
687 $parser->{'expanded_formats_hash'}->{$expanded_format} = 1;
688 }
689
690 %{$parser->{'global_commands'}} = %global_multiple_commands;
691
692 foreach my $global_command (@{$parser->{'GLOBAL_COMMANDS'}}) {
693 $parser->{'global_commands'}->{$global_command} = 1;
694 }
695
696 $parser->Texinfo::Report::new;
697
698 return $parser;
699}
700
701sub get_conf($$)
702{
703 my $self = shift;
704 my $var = shift;
705 return $self->{$var};
706}
707
708# split a scalar text in an array lines.
709sub _text_to_lines($)
710{
711 my $text = shift;
712 die if (!defined($text));
713 my $had_final_end_line = chomp($text);
714 my $lines = [ map {$_."\n"} split (/\n/, $text, -1) ];
715 $lines = [''] if (!@$lines);
716 chomp($lines->[-1]) unless ($had_final_end_line);
717 return $lines;
718}
719
720# construct a text fragments array matching a lines array, based on information
721# supplied.
722# If $fixed_line_number is set the line number is not increased, otherwise
723# it is increased, beginning at $first_line.
724sub _complete_line_nr($$;$$$)
725{
726 my $lines = shift;
727 my $first_line = shift;
728 my $file = shift;
729 my $macro = shift;
730 my $fixed_line_number = shift;
731
732 $macro = '' if (!defined($macro));
733 $file = '' if (!defined($file));
734 my $new_lines = [];
735
736 if (defined($first_line)) {
737 my $line_index = $first_line;
738 foreach my $index(0..scalar(@$lines)-1) {
739 $line_index = $index+$first_line if (!$fixed_line_number);
740 $new_lines->[$index] = [ $lines->[$index],
741 { 'line_nr' => $line_index,
742 'file_name' => $file, 'macro' => $macro } ];
743 }
744 } else {
745 foreach my $line (@$lines) {
746 push @$new_lines, [ $line ];
747 }
748 }
749 return $new_lines;
750}
751
752# entry point for text fragments.
753# Used in tests.
754# Note that it has no associated root type a opposed to parse_texi_line
755# and parse_texi_file.
756sub parse_texi_text($$;$$$$)
757{
758 my $self = shift;
759 my $text = shift;
760 my $lines_nr = shift;
761 my $file = shift;
762 my $macro = shift;
763 my $fixed_line_number = shift;
764
765 return undef if (!defined($text));
766
767 my $lines_array = [];
768 if (!ref($text)) {
769 $text = _text_to_lines($text);
770 }
771 $lines_nr = [] if (!defined($lines_nr));
772 if (!ref($lines_nr)) {
773 #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
774 $lines_array = _complete_line_nr($text, $lines_nr, $file,
775 $macro, $fixed_line_number);
776 } else {
777 while (@$text) {
778 my $line_nr = shift @$lines_nr;
779 my $line = shift @$text;
780 push @$lines_array, [$line, $line_nr];
781 }
782 }
783
784 $self = parser() if (!defined($self));
785 $self->{'input'} = [{'pending' => $lines_array}];
786 my $tree = $self->_parse_texi();
787 return $tree;
788}
789
790# Not used for now, as a @contents after the first sectioning command
791# is correct if not using TeX.
792sub _check_contents_location($$)
793{
794 my $self = shift;
795 my $tree = shift;
796
797 my $commands = $self->global_commands_information();
798 return unless ($commands);
799 # Find the last sectioning command
800 my $index = -1;
801 my %ending_root_commands;
802 my $found = 0;
803 while ($tree->{'contents'}->[$index]) {
804 if (defined($tree->{'contents'}->[$index]->{'cmdname'})) {
805 $ending_root_commands{$tree->{'contents'}->[$index]} = 1;
806 if ($sectioning_commands{$tree->{'contents'}->[$index]->{'cmdname'}}) {
807 $found = 1;
808 last;
809 }
810 }
811 $index--;
812 }
813 return if (!$found);
814
815 #print STDERR "ending_root_commands ".join('|',keys(%ending_root_commands))."\n";
816 #print STDERR "tree contents: ".join('|', @{$tree->{'contents'}})."\n";
817 foreach my $command ('contents', 'shortcontents', 'summarycontents') {
818 if ($commands->{$command}) {
819 foreach my $current (@{$commands->{$command}}) {
820 my $root_command = $self->Texinfo::Common::find_parent_root_command($current);
821 #print STDERR "root_command for $current->{'cmdname'}: $root_command\n";
822 if (defined($root_command)
823 and !$ending_root_commands{$root_command}) {
824 $self->line_warn(sprintf($self->__(
825 "\@%s should only appear at beginning or end of document"),
826 $current->{'cmdname'}), $current->{'line_nr'});
827 }
828 }
829 }
830 }
831}
832
833# parse a texi file
834sub parse_texi_file($$)
835{
836 my $self = shift;
837 my $file_name = shift;
838
839 my $filehandle = do { local *FH };
840 if (! open($filehandle, $file_name)) {
841 $self->document_error(sprintf($self->__("could not open %s: %s"),
842 $file_name, $!));
843 return undef;
844 }
845 my $line_nr = 0;
846 my $line;
847 my @first_lines;
848
849 my $pending_first_texi_line;
850 # the first line not empty and not with \input is kept in
851 # $pending_first_texi_line and put in the pending lines just below
852 while ($line = <$filehandle>) {
853 $line_nr++;
854 if ($line =~ /^ *\\input/ or $line =~ /^\s*$/) {
855 $line =~ s/\x{7F}.*\s*//;
856 push @first_lines, $line;
857 } else {
858 $pending_first_texi_line = $line;
859 last;
860 }
861 }
862 my $root = { 'contents' => [], 'type' => 'text_root' };
863 if (@first_lines) {
864 push @{$root->{'contents'}}, { 'type' => 'preamble', 'contents' => [] };
865 foreach my $line (@first_lines) {
866 push @{$root->{'contents'}->[-1]->{'contents'}},
867 { 'text' => $line,
868 'type' => 'preamble_text' };
869 }
870 }
871 my ($directories, $suffix);
872 ($file_name, $directories, $suffix) = fileparse($file_name)
873 if ($self->{'TEST'});
874 $self = parser() if (!defined($self));
875 $self->{'input'} = [{
876 'pending' => [[$pending_first_texi_line, {'line_nr' => $line_nr,
877 'macro' => '', 'file_name' => $file_name}]],
878 'name' => $file_name,
879 'line_nr' => $line_nr,
880 'fh' => $filehandle
881 }];
882 $self->{'info'}->{'input_file_name'} = $file_name;
883 my $tree = $self->_parse_texi($root);
884
885 # Find 'text_root', which contains everything before first node/section.
886 # if there are elements, 'text_root' is the first content, otherwise it
887 # is the root.
888 my $text_root;
889 if ($tree->{'type'} eq 'text_root') {
890 $text_root = $tree;
891 } elsif ($tree->{'contents'} and $tree->{'contents'}->[0]
892 and $tree->{'contents'}->[0]->{'type'} eq 'text_root') {
893 $text_root = $tree->{'contents'}->[0];
894 }
895
896 # Put everything before @setfilename in a special type. This allows to
897 # ignore everything before @setfilename.
898 if ($self->{'IGNORE_BEFORE_SETFILENAME'} and $text_root and
899 $self->{'extra'} and $self->{'extra'}->{'setfilename'}
900 and $self->{'extra'}->{'setfilename'}->{'parent'} eq $text_root) {
901 my $before_setfilename = {'type' => 'preamble_before_setfilename',
902 'parent' => $text_root,
903 'contents' => []};
904 while ($text_root->{'contents'}->[0] ne $self->{'extra'}->{'setfilename'}) {
905 my $content = shift @{$text_root->{'contents'}};
906 $content->{'parent'} = $before_setfilename;
907 push @{$before_setfilename->{'contents'}}, $content;
908 }
909 unshift (@{$text_root->{'contents'}}, $before_setfilename)
910 if (@{$before_setfilename->{'contents'}});
911 }
912 #$self->_check_contents_location($tree);
913
914 return $tree;
915}
916
917sub parse_texi_line($$;$$$$)
918{
919 my $self = shift;
920 my $text = shift;
921 my $lines_nr = shift;
922 my $file = shift;
923 my $macro = shift;
924 my $fixed_line_number = shift;
925
926 return undef if (!defined($text));
927
928 if (!ref($text)) {
929 $text = _text_to_lines($text);
930 }
931 #$file =~ s/^.*\/// if (defined($file) and $self->{'TEST'});
932 my $lines_array = _complete_line_nr($text, $lines_nr, $file,
933 $macro, $fixed_line_number);
934
935 $self = parser() if (!defined($self));
936 $self->{'input'} = [{'pending' => $lines_array}];
937 my $tree = $self->_parse_texi({'contents' => [], 'type' => 'root_line'});
938 return $tree;
939}
940
941# return indices informations
942sub indices_information($)
943{
944 my $self = shift;
945 return ($self->{'index_names'}, $self->{'merged_indices'});
946 #return ($self->{'index_names'}, $self->{'merged_indices'}, $self->{'index_entries'});
947}
948
949sub floats_information($)
950{
951 my $self = shift;
952 return $self->{'floats'};
953}
954
955sub internal_references_information($)
956{
957 my $self = shift;
958 return $self->{'internal_references'};
959}
960
961sub global_commands_information($)
962{
963 my $self = shift;
964 return $self->{'extra'};
965}
966
967# @ dircategory_direntry
968# @ unassociated_menus
969# perl_encoding
970# input_encoding_name
971# input_file_name
972sub global_informations($)
973{
974 my $self = shift;
975 return $self->{'info'};
976}
977
978sub labels_information($)
979{
980 my $self = shift;
981 return $self->{'labels'};
982}
983
984# Following are the internal subroutines. The most important are
985# _parse_texi: the main parser loop.
986# _end_line: called at an end of line. Handling of @include lines is
987# done here.
988# _next_text: present the next text fragment, from pending text or line,
989# as described above.
990
991# for debugging
992sub _print_current($)
993{
994 my $current = shift;
995 return Texinfo::Common::_print_current($current);
996}
997
998# for debugging
999sub _print_command_args_texi($)
1000{
1001 my $current = shift;
1002 return '' if (!$current->{'cmdname'});
1003 my $args = '';
1004 my $with_brace;
1005 if ($current->{'args'} and @{$current->{'args'}}) {
1006 $with_brace
1007 = ($current->{'args'}->[0]->{'type'} eq 'brace_command_arg'
1008 or $current->{'args'}->[0]->{'type'} eq 'brace_command_context');
1009 $args .= '{' if ($with_brace);
1010 foreach my $arg (@{$current->{'args'}}) {
1011 $args .= Texinfo::Convert::Texinfo::convert($arg).', ';
1012 }
1013 $args =~ s/, $//;
1014 }
1015 chomp($args);
1016 if ($with_brace) {
1017 $args .= '}';
1018 }
1019 return '@'.$current->{'cmdname'} .$args."\n";
1020}
1021
1022sub _print_current_keys($)
1023{
1024 my $current = shift;
1025 my $string = _print_current($current);
1026 foreach my $key (keys (%$current)) {
1027 $string .= " $key: $current->{$key}\n";
1028 }
1029 if ($current->{'extra'}) {
1030 $string .= " EXTRA\n";
1031 foreach my $key (keys (%{$current->{'extra'}})) {
1032 $string .= " $key: $current->{'extra'}->{$key}\n";
1033 }
1034 }
1035 return $string;
1036}
1037
1038# For debugging
1039my @kept_keys = ('contents', 'cmdname', 'type', 'text', 'args');
1040my %kept_keys;
1041foreach my $key (@kept_keys) {
1042 $kept_keys{$key} = 1;
1043}
1044sub _filter_print_keys { [grep {$kept_keys{$_}} ( sort keys %{$_[0]} )] };
1045sub _print_tree($)
1046{
1047 my $tree = shift;
1048 local $Data::Dumper::Sortkeys = \&_filter_print_keys;
1049 local $Data::Dumper::Purity = 1;
1050 local $Data::Dumper::Indent = 1;
1051
1052 return Data::Dumper->Dump([$tree]);
1053}
1054
1055sub _register_global_command($$$$)
1056{
1057 my $self = shift;
1058 my $command = shift;
1059 my $current = shift;
1060 my $line_nr = shift;
1061 if ($command eq 'summarycontents' and !$self->{'global_commands'}->{$command}) {
1062 $command = 'shortcontents';
1063 }
1064 if ($self->{'global_commands'}->{$command} and $command ne 'author') {
1065 push @{$self->{'extra'}->{$command}}, $current
1066 unless (_ignore_global_commands($self));
1067 $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
1068 return 1;
1069 } elsif ($global_unique_commands{$command}) {
1070 # setfilename ignored in an included file
1071 $current->{'line_nr'} = $line_nr if (!$current->{'line_nr'});
1072 if ($command eq 'setfilename'
1073 and scalar(@{$self->{'input'}}) > 1) {
1074 } elsif (exists ($self->{'extra'}->{$current->{'cmdname'}})) {
1075 $self->line_warn(sprintf($self->__('multiple @%s'),
1076 $current->{'cmdname'}), $line_nr);
1077 } else {
1078 $self->{'extra'}->{$current->{'cmdname'}} = $current
1079 unless (_ignore_global_commands($self));
1080 }
1081 return 1;
1082 }
1083 return 0;
1084}
1085
1086# parse a @macro line
1087sub _parse_macro_command_line($$$$$;$)
1088{
1089 my $self = shift;
1090 my $command = shift;
1091 my $line = shift;
1092 my $parent = shift;
1093 my $line_nr = shift;
1094 my $macro = { 'cmdname' => $command, 'parent' => $parent, 'contents' => [],
1095 'extra' => {'arg_line' => $line}, 'line_nr' => $line_nr };
1096 # REMACRO
1097 if ($line =~ /^\s+([[:alnum:]][[:alnum:]-]*)\s*(.*)/) {
1098 my $macro_name = $1;
1099 my $args_def = $2;
1100 my @args;
1101
1102 if ($args_def =~ s/^\s*{\s*(.*?)\s*}\s*//) {
1103 @args = split(/\s*,\s*/, $1);
1104 }
1105
1106 # accept an @-command after the arguments in case there is a @c or
1107 # @comment
1108 if ($args_def =~ /^\s*[^\@]/) {
1109 $self->line_error(sprintf($self->__("bad syntax for \@%s argument: %s"),
1110 $command, $args_def),
1111 $line_nr);
1112 $macro->{'extra'}->{'invalid_syntax'} = 1;
1113 }
1114 print STDERR "MACRO \@$command $macro_name\n" if ($self->{'DEBUG'});
1115
1116 $macro->{'args'} = [
1117 { 'type' => 'macro_name', 'text' => $macro_name,
1118 'parent' => $macro } ];
1119 my $index = 0;
1120 foreach my $formal_arg (@args) {
1121 push @{$macro->{'args'}},
1122 { 'type' => 'macro_arg', 'text' => $formal_arg,
1123 'parent' => $macro};
1124 if ($formal_arg !~ /^[\w\-]+$/) {
1125 $self->line_error(sprintf($self->__("bad or empty \@%s formal argument: %s"),
1126 $command, $formal_arg), $line_nr);
1127 $macro->{'extra'}->{'invalid_syntax'} = 1;
1128 }
1129 $macro->{'extra'}->{'args_index'}->{$formal_arg} = $index;
1130 $index++;
1131 }
1132 } elsif ($line !~ /\S/) {
1133 $self->line_error(sprintf($self->
1134 __("%c%s requires a name"), ord('@'), $command), $line_nr);
1135 $macro->{'extra'}->{'invalid_syntax'} = 1;
1136 } else {
1137 $self->line_error(sprintf($self->
1138 __("bad name for \@%s"), $command), $line_nr);
1139 $macro->{'extra'}->{'invalid_syntax'} = 1;
1140 }
1141 return $macro;
1142}
1143
1144# start a paragraph if in a context where paragraphs are to be started.
1145sub _begin_paragraph($$;$)
1146{
1147 my $self = shift;
1148 my $current = shift;
1149 my $line_nr = shift;
1150
1151 if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
1152 and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
1153 if (!defined($current->{'contents'})) {
1154 $self->_bug_message("contents undef", $line_nr, $current);
1155 die;
1156 }
1157
1158 # find whether an @indent precedes the paragraph
1159 my $indent;
1160 if (scalar(@{$current->{'contents'}})) {
1161 my $index = scalar(@{$current->{'contents'}}) -1;
1162 while ($index >= 0
1163 and !($current->{'contents'}->[$index]->{'type'}
1164 and ($current->{'contents'}->[$index]->{'type'} eq 'empty_line'
1165 or $current->{'contents'}->[$index]->{'type'} eq 'paragraph'))
1166 and !($current->{'contents'}->[$index]->{'cmdname'}
1167 and $self->{'close_paragraph_commands'}->{$current->{'contents'}->[$index]->{'cmdname'}})) {
1168 if ($current->{'contents'}->[$index]->{'cmdname'}
1169 and ($current->{'contents'}->[$index]->{'cmdname'} eq 'indent'
1170 or $current->{'contents'}->[$index]->{'cmdname'} eq 'noindent')) {
1171 $indent = $current->{'contents'}->[$index]->{'cmdname'};
1172 last;
1173 }
1174 $index--;
1175 }
1176 }
1177 push @{$current->{'contents'}},
1178 { 'type' => 'paragraph', 'parent' => $current, 'contents' => [] };
1179 $current->{'contents'}->[-1]->{'extra'}->{$indent} = 1 if ($indent);
1180 $current = $current->{'contents'}->[-1];
1181 print STDERR "PARAGRAPH\n" if ($self->{'DEBUG'});
1182 return $current;
1183 }
1184 return 0;
1185}
1186
1187sub _begin_preformatted($)
1188{
1189 my $self = shift;
1190 my $current = shift;
1191 if ($preformatted_contexts{$self->{'context_stack'}->[-1]}) {
1192 push @{$current->{'contents'}},
1193 { 'type' => $self->{'context_stack'}->[-1],
1194 'parent' => $current, 'contents' => [] };
1195 $current = $current->{'contents'}->[-1];
1196 print STDERR "PREFORMATTED $self->{'context_stack'}->[-1]\n" if ($self->{'DEBUG'});
1197 }
1198 return $current;
1199}
1200
1201# wrapper around line_warn. Set line_nr to be the line_nr of the command,
1202# corresponding to the opening of the command. Call line_warn with
1203# sprintf if needed.
1204sub _command_warn($$$$;@)
1205{
1206 my $self = shift;
1207 my $current = shift;
1208 my $line_nr = shift;
1209 my $message = shift;
1210
1211 if ($current->{'line_nr'}) {
1212 $line_nr = $current->{'line_nr'};
1213 }
1214 if (@_) {
1215 $self->line_warn(sprintf($message, @_), $line_nr);
1216 } else {
1217 $self->line_warn($message, $line_nr);
1218 }
1219}
1220
1221sub _command_error($$$$;@)
1222{
1223 my $self = shift;
1224 my $current = shift;
1225 my $line_nr = shift;
1226 my $message = shift;
1227
1228 # use the beginning of the @-command for the error message
1229 # line number if available.
1230 # FIXME line_nr currently not registered for regular brace commands
1231 if ($current->{'line_nr'}) {
1232 $line_nr = $current->{'line_nr'};
1233 }
1234 if (@_) {
1235 $self->line_error(sprintf($message, @_), $line_nr);
1236 } else {
1237 $self->line_error($message, $line_nr);
1238 }
1239}
1240
1241# currently doesn't do much more than
1242# return $_[1]->{'parent'}
1243sub _close_brace_command($$$;$$)
1244{
1245 my $self = shift;
1246 my $current = shift;
1247 my $line_nr = shift;
1248 my $closed_command = shift;
1249 my $interrupting_command = shift;
1250
1251 if ($current->{'cmdname'} ne 'verb' or $current->{'type'} eq '') {
1252 if (defined($closed_command)) {
1253 $self->_command_error($current, $line_nr,
1254 $self->__("\@end %s seen before \@%s closing brace"),
1255 $closed_command, $current->{'cmdname'});
1256 } elsif (defined($interrupting_command)) {
1257 $self->_command_error($current, $line_nr,
1258 $self->__("\@%s seen before \@%s closing brace"),
1259 $interrupting_command, $current->{'cmdname'});
1260
1261 } else {
1262 $self->_command_error($current, $line_nr,
1263 $self->__("%c%s missing close brace"), ord('@'), $current->{'cmdname'});
1264 }
1265 } else {
1266 $self->_command_error($current, $line_nr,
1267 $self->__("\@%s missing closing delimiter sequence: %s}"),
1268 $current->{'cmdname'}, $current->{'type'});
1269 }
1270 $current = $current->{'parent'};
1271 return $current;
1272}
1273
1274sub _in_code($$)
1275{
1276 my $self = shift;
1277 my $current = shift;
1278
1279 while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
1280 and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
1281 and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
1282 return 1 if ($code_style_commands{$current->{'parent'}->{'cmdname'}});
1283 $current = $current->{'parent'}->{'parent'};
1284 }
1285 return 0;
1286}
1287
1288# close brace commands, that don't set a new context (ie @caption, @footnote)
1289sub _close_all_style_commands($$$;$$)
1290{
1291 my $self = shift;
1292 my $current = shift;
1293 my $line_nr = shift;
1294 my $closed_command = shift;
1295 my $interrupting_command = shift;
1296
1297 while ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
1298 and exists $brace_commands{$current->{'parent'}->{'cmdname'}}
1299 and !exists $context_brace_commands{$current->{'parent'}->{'cmdname'}}) {
1300 $current = _close_brace_command($self, $current->{'parent'}, $line_nr,
1301 $closed_command, $interrupting_command);
1302 }
1303 return $current;
1304}
1305
1306# close brace commands except for @caption, @footnote then the paragraph
1307sub _end_paragraph($$$;$$)
1308{
1309 my $self = shift;
1310 my $current = shift;
1311 my $line_nr = shift;
1312 my $closed_command = shift;
1313 my $interrupting_command = shift;
1314
1315 $current = _close_all_style_commands($self, $current, $line_nr,
1316 $closed_command, $interrupting_command);
1317 if ($current->{'type'} and $current->{'type'} eq 'paragraph') {
1318 print STDERR "CLOSE PARA\n" if ($self->{'DEBUG'});
1319 $current = $current->{'parent'};
1320 }
1321 return $current;
1322}
1323
1324# close brace commands except for @caption, @footnote then the preformatted
1325sub _end_preformatted($$$;$$)
1326{
1327 my $self = shift;
1328 my $current = shift;
1329 my $line_nr = shift;
1330 my $closed_command = shift;
1331 my $interrupting_command = shift;
1332
1333 $current = _close_all_style_commands($self, $current, $line_nr,
1334 $closed_command, $interrupting_command);
1335 if ($current->{'type'} and $preformatted_contexts{$current->{'type'}}) {
1336 print STDERR "CLOSE PREFORMATTED $current->{'type'}\n" if ($self->{'DEBUG'});
1337 # completly remove void preformatted contexts
1338 if (!@{$current->{'contents'}}) {
1339 my $removed = pop @{$current->{'parent'}->{'contents'}};
1340 print STDERR "popping $removed->{'type'}\n" if ($self->{'DEBUG'});
1341 }
1342 $current = $current->{'parent'};
1343 }
1344 return $current;
1345}
1346
1347# check that there are no text holding environment (currently
1348# checking only paragraphs and preformatted) in contents
1349sub _check_no_text($)
1350{
1351 my $current = shift;
1352 my $after_paragraph = 0;
1353 foreach my $content (@{$current->{'contents'}}) {
1354 if ($content->{'type'} and $content->{'type'} eq 'paragraph') {
1355 $after_paragraph = 1;
1356 last;
1357 } elsif ($content->{'type'} and $preformatted_contexts{$content->{'type'}}) {
1358 foreach my $preformatted_content (@{$content->{'contents'}}) {
1359 if ((defined($preformatted_content->{'text'})
1360 and $preformatted_content->{'text'} =~ /\S/)
1361 or ($preformatted_content->{'cmdname'}
1362 and ($preformatted_content->{'cmdname'} ne 'c'
1363 and $preformatted_content->{'cmdname'} ne 'comment')
1364 and !($preformatted_content->{'type'}
1365 and $preformatted_content->{'type'} eq 'index_entry_command'))) {
1366 $after_paragraph = 1;
1367 last;
1368 }
1369 }
1370 last if ($after_paragraph);
1371 }
1372 }
1373 return $after_paragraph;
1374}
1375
1376# put everything after the last @item/@itemx in an item_table type container
1377# and distinguish table_term and table_entry.
1378sub _gather_previous_item($$;$$)
1379{
1380 my $self = shift;
1381 my $current = shift;
1382 my $next_command = shift;
1383 my $line_nr = shift;
1384
1385 # nothing to do in that case.
1386 if ($current->{'contents'}->[-1]->{'type'}
1387 and $current->{'contents'}->[-1]->{'type'} eq 'before_item') {
1388 if ($next_command and $next_command eq 'itemx') {
1389 $self->line_warn(sprintf($self->__("\@itemx should not begin \@%s"),
1390 $current->{'cmdname'}), $line_nr);
1391 }
1392 return;
1393 }
1394 #print STDERR "GATHER "._print_current($current)."\n";
1395 my $type;
1396 # if before an itemx, the type is different since there should not be
1397 # real content, so it may be treated differently
1398 if ($next_command and $next_command eq 'itemx') {
1399 $type = 'inter_item';
1400 } else {
1401 $type = 'table_item';
1402 }
1403 my $table_gathered = {'type' => $type,
1404 'contents' => []};
1405 # remove everything that is not an @item/@items or before_item to
1406 # put it in the table_item, starting from the end.
1407 my $contents_count = scalar(@{$current->{'contents'}});
1408 for (my $i = 0; $i < $contents_count; $i++) {
1409 #print STDERR "_gather_previous_item $i on $contents_count: "._print_current($current->{'contents'}->[-1])."\n";
1410 if ($current->{'contents'}->[-1]->{'cmdname'}
1411 and ($current->{'contents'}->[-1]->{'cmdname'} eq 'item'
1412 or ($current->{'contents'}->[-1]->{'cmdname'} eq 'itemx'))) {
1413 last;
1414 } else {
1415 my $item_content = pop @{$current->{'contents'}};
1416 $item_content->{'parent'} = $table_gathered;
1417 unshift @{$table_gathered->{'contents'}}, $item_content;
1418 }
1419 }
1420 if ($type eq 'table_item') {
1421 my $table_entry = {'type' => 'table_entry',
1422 'parent' => $current,
1423 'contents' => []};
1424 my $table_term = {'type' => 'table_term',
1425 'parent' => $table_entry,
1426 'contents' => []};
1427 push @{$table_entry->{'contents'}}, $table_term;
1428 my $contents_count = scalar(@{$current->{'contents'}});
1429 for (my $i = 0; $i < $contents_count; $i++) {
1430 if ($current->{'contents'}->[-1]->{'type'}
1431 and ($current->{'contents'}->[-1]->{'type'} eq 'before_item'
1432 or $current->{'contents'}->[-1]->{'type'} eq 'table_entry')) {
1433 last;
1434 } else {
1435 my $item_content = pop @{$current->{'contents'}};
1436 $item_content->{'parent'} = $table_term;
1437 unshift @{$table_term->{'contents'}}, $item_content;
1438 # debug
1439 if (! (($item_content->{'cmdname'}
1440 and ($item_content->{'cmdname'} eq 'itemx'
1441 or $item_content->{'cmdname'} eq 'item'))
1442 or ($item_content->{'type'}
1443 and $item_content->{'type'} eq 'inter_item'))) {
1444 $self->_bug_message("wrong element in table term", $line_nr,
1445 $item_content);
1446 }
1447 }
1448 }
1449 push @{$current->{'contents'}}, $table_entry;
1450 if (scalar(@{$table_gathered->{'contents'}})) {
1451 push @{$table_entry->{'contents'}}, $table_gathered;
1452 $table_gathered->{'parent'} = $table_entry;
1453 }
1454 } else {
1455 my $after_paragraph = _check_no_text($table_gathered);
1456 if ($after_paragraph) {
1457 $self->line_error($self->__("\@itemx must follow \@item"), $line_nr);
1458 }
1459 if (scalar(@{$table_gathered->{'contents'}})) {
1460 push @{$current->{'contents'}}, $table_gathered;
1461 $table_gathered->{'parent'} = $current;
1462 }
1463 }
1464}
1465
1466# Starting from the end, gather everything util the def_line to put in
1467# a def_item
1468sub _gather_def_item($;$)
1469{
1470 my $current = shift;
1471 my $next_command = shift;
1472 my $type;
1473 # means that we are between a @def*x and a @def
1474 if ($next_command) {
1475 $type = 'inter_def_item';
1476 } else {
1477 $type = 'def_item';
1478 }
1479
1480 # This may happen for a construct like
1481 # @deffnx a b @section
1482 # but otherwise the end of line will lead to the command closing
1483 return if (!$current->{'cmdname'} or $current->{'cmdname'} =~ /x$/);
1484 #print STDERR "_gather_def_item($type) in "._print_current($current)."\n";
1485 my $def_item = {'type' => $type,
1486 'parent' => $current,
1487 'contents' => []};
1488 # remove everything that is not a def_line to put it in the def_item,
1489 # starting from the end.
1490 my $contents_count = scalar(@{$current->{'contents'}});
1491 for (my $i = 0; $i < $contents_count; $i++) {
1492 #print STDERR "_gather_def_item $type ($i on $contents_count) "._print_current($current->{'contents'}->[-1])."\n";
1493 if ($current->{'contents'}->[-1]->{'type'}
1494 and $current->{'contents'}->[-1]->{'type'} eq 'def_line') {
1495 # and !$current->{'contents'}->[-1]->{'extra'}->{'not_after_command'}) {
1496 last;
1497 } else {
1498 my $item_content = pop @{$current->{'contents'}};
1499 $item_content->{'parent'} = $def_item;
1500 unshift @{$def_item->{'contents'}}, $item_content;
1501 }
1502 }
1503 if (scalar(@{$def_item->{'contents'}})) {
1504 push @{$current->{'contents'}}, $def_item;
1505 }
1506}
1507
1508# close formats
1509sub _close_command_cleanup($$$) {
1510 my $self = shift;
1511 my $current = shift;
1512
1513 return unless ($current->{'cmdname'});
1514 # remove the dynamic counters in multitable, they are not of use in the final
1515 # tree. Also determine the multitable_body and multitable_head with
1516 # @item or @headitem rows.
1517 if ($current->{'cmdname'} eq 'multitable') {
1518 my $in_head_or_rows;
1519 my @contents = @{$current->{'contents'}};
1520 $current->{'contents'} = [];
1521 foreach my $row (@contents) {
1522 if ($row->{'type'} and $row->{'type'} eq 'row') {
1523 delete $row->{'cells_count'};
1524 if ($row->{'contents'}->[0]->{'cmdname'} eq 'headitem') {
1525 if (!$in_head_or_rows) {
1526 push @{$current->{'contents'}}, {'type' => 'multitable_head',
1527 'parent' => $current};
1528 $in_head_or_rows = 1;
1529 }
1530 } elsif ($row->{'contents'}->[0]->{'cmdname'} eq 'item') {
1531 if (!defined($in_head_or_rows) or $in_head_or_rows) {
1532 push @{$current->{'contents'}}, {'type' => 'multitable_body',
1533 'parent' => $current};
1534 $in_head_or_rows = 0;
1535 }
1536 }
1537 push @{$current->{'contents'}->[-1]->{'contents'}}, $row;
1538 $row->{'parent'} = $current->{'contents'}->[-1];
1539 } else {
1540 push @{$current->{'contents'}}, $row;
1541 $in_head_or_rows = undef;
1542 }
1543 }
1544 delete $current->{'rows_count'};
1545 } elsif ($item_container_commands{$current->{'cmdname'}}) {
1546 delete $current->{'items_count'};
1547 }
1548
1549 # put everything after the last @def*x command in a def_item type container.
1550 if ($def_commands{$current->{'cmdname'}}) {
1551 # At this point the end command hasn't been added to the command contents.
1552 # so checks cannot be done at this point.
1553 _gather_def_item($current);
1554 }
1555
1556 if ($item_line_commands{$current->{'cmdname'}}) {
1557 # At this point the end command hasn't been added to the command contents.
1558 # so checks cannot be done at this point.
1559 if (@{$current->{'contents'}}) {
1560 $self->_gather_previous_item($current);
1561 }
1562 }
1563
1564 # put end out of before_item, and replace it at the end of the parent.
1565 # remove empty before_item.
1566 # warn if not empty before_item, but format is empty
1567 if ($block_item_commands{$current->{'cmdname'}}) {
1568 if (@{$current->{'contents'}}) {
1569 my $leading_spaces = 0;
1570 my $before_item;
1571 if ($current->{'contents'}->[0]->{'type'}
1572 and $current->{'contents'}->[0]->{'type'} eq 'empty_line_after_command'
1573 and $current->{'contents'}->[1]
1574 and $current->{'contents'}->[1]->{'type'}
1575 and $current->{'contents'}->[1]->{'type'} eq 'before_item') {
1576 $leading_spaces = 1;
1577 $before_item = $current->{'contents'}->[1];
1578 } elsif ($current->{'contents'}->[0]->{'type'}
1579 and $current->{'contents'}->[0]->{'type'} eq 'before_item') {
1580 $before_item = $current->{'contents'}->[0];
1581 }
1582 if ($before_item) {
1583 if ($current->{'extra'}->{'end_command'}
1584 and @{$before_item->{'contents'}}
1585 and $before_item->{'contents'}->[-1] eq $current->{'extra'}->{'end_command'}) {
1586 my $end = pop @{$before_item->{'contents'}};
1587 $end->{'parent'} = $current;
1588 push @{$current->{'contents'}}, $end;
1589 }
1590 # remove empty before_items
1591 if (!@{$before_item->{'contents'}}) {
1592 if ($leading_spaces) {
1593 my $space = shift @{$current->{'contents'}};
1594 shift @{$current->{'contents'}};
1595 unshift @{$current->{'contents'}}, $space;
1596 } else {
1597 shift @{$current->{'contents'}};
1598 }
1599 } else {
1600 # warn if not empty before_item, but format is empty
1601 my $empty_before_item = 1;
1602 foreach my $before_item_content (@{$before_item->{'contents'}}) {
1603 if (!$before_item_content->{'cmdname'} or
1604 ($before_item_content->{'cmdname'} ne 'c'
1605 and $before_item_content->{'cmdname'} ne 'comment')) {
1606 $empty_before_item = 0;
1607 last;
1608 }
1609 }
1610 if (!$empty_before_item) {
1611 my $empty_format = 1;
1612 foreach my $format_content (@{$current->{'contents'}}) {
1613 next if ($format_content eq $before_item);
1614 if (($format_content->{'cmdname'} and
1615 ($format_content->{'cmdname'} ne 'c'
1616 and $format_content->{'cmdname'} ne 'comment'
1617 and $format_content->{'cmdname'} ne 'end'))
1618 or ($format_content->{'type'} and
1619 ($format_content->{'type'} ne 'empty_line_after_command'))) {
1620 $empty_format = 0;
1621 last;
1622 }
1623 }
1624 if ($empty_format) {
1625 $self->line_warn(sprintf($self->__("\@%s has text but no \@item"),
1626 $current->{'cmdname'}), $current->{'line_nr'});
1627 }
1628 }
1629 }
1630 }
1631 }
1632 }
1633}
1634
1635# close the current command, with error messages and give the parent.
1636# If the last argument is given it is the command being closed if
1637# hadn't there be an error, currently only block command, used for a
1638# better error message.
1639sub _close_current($$$;$$)
1640{
1641 my $self = shift;
1642 my $current = shift;
1643 my $line_nr = shift;
1644 my $closed_command = shift;
1645 my $interrupting_command = shift;
1646
1647 if ($current->{'cmdname'}) {
1648 print STDERR "CLOSING(_close_current) \@$current->{'cmdname'}\n" if ($self->{'DEBUG'});
1649 if (exists($brace_commands{$current->{'cmdname'}})) {
1650 pop @{$self->{'context_stack'}}
1651 if (exists $context_brace_commands{$current->{'cmdname'}});
1652 $current = _close_brace_command($self, $current, $line_nr,
1653 $closed_command, $interrupting_command);
1654 } elsif (exists($block_commands{$current->{'cmdname'}})) {
1655 if (defined($closed_command)) {
1656 $self->line_error(sprintf($self->__("`\@end' expected `%s', but saw `%s'"),
1657 $current->{'cmdname'}, $closed_command), $line_nr);
1658 } elsif ($interrupting_command) {
1659 $self->line_error(sprintf($self->__("\@%s seen before \@end %s"),
1660 $interrupting_command, $current->{'cmdname'}),
1661 $line_nr);
1662 } else {
1663 $self->line_error(sprintf($self->__("no matching `%cend %s'"),
1664 ord('@'), $current->{'cmdname'}), $line_nr);
1665 if ($block_commands{$current->{'cmdname'}} eq 'conditional') {
1666 # in this case we are within an ignored conditional
1667 my $conditional = pop @{$current->{'parent'}->{'contents'}};
1668 }
1669 }
1670 if ($preformatted_commands{$current->{'cmdname'}}
1671 or $menu_commands{$current->{'cmdname'}}
1672 or $format_raw_commands{$current->{'cmdname'}}) {
1673 my $context = pop @{$self->{'context_stack'}};
1674 pop @{$self->{'raw_formats_stack'}}
1675 if ($format_raw_commands{$current->{'cmdname'}});
1676 }
1677 pop @{$self->{'regions_stack'}}
1678 if ($region_commands{$current->{'cmdname'}});
1679 $current = $current->{'parent'};
1680 } else {
1681 # There @item and @tab commands are closed, and also line commands
1682 # with invalid content
1683 $current = $current->{'parent'};
1684 }
1685 } elsif ($current->{'type'}) {
1686 print STDERR "CLOSING type $current->{'type'}\n" if ($self->{'DEBUG'});
1687 if ($current->{'type'} eq 'bracketed') {
1688 $self->_command_error($current, $line_nr,
1689 $self->__("misplaced %c"), ord('{'));
1690 } elsif ($current->{'type'} eq 'menu_comment'
1691 or $current->{'type'} eq 'menu_entry_description') {
1692 my $context = pop @{$self->{'context_stack'}};
1693 if ($context ne 'preformatted') {
1694 $self->_bug_message("context $context instead of preformatted",
1695 $line_nr, $current);
1696 }
1697 # close empty menu_comment
1698 if (!@{$current->{'contents'}}) {
1699 pop @{$current->{'parent'}->{'contents'}};
1700 }
1701 } elsif ($current->{'type'} eq 'misc_line_arg'
1702 or $current->{'type'} eq 'block_line_arg') {
1703 my $context = pop @{$self->{'context_stack'}};
1704 if ($context ne 'line' and $context ne 'def') {
1705 $self->_bug_message("context $context instead of line or def",
1706 $line_nr, $current);
1707 die;
1708 }
1709 }
1710 $current = $current->{'parent'};
1711 } else { # Should never go here.
1712 $current = $current->{'parent'} if ($current->{'parent'});
1713 $self->_bug_message("No type nor cmdname when closing",
1714 $line_nr, $current);
1715 }
1716 return $current;
1717}
1718
1719# a closed_command arg means closing until that command is found.
1720# no command arg means closing until the root or a root_command
1721# is found.
1722sub _close_commands($$$;$$)
1723{
1724 my $self = shift;
1725 my $current = shift;
1726 my $line_nr = shift;
1727 my $closed_command = shift;
1728 my $interrupting_command = shift;;
1729
1730 $current = _end_paragraph($self, $current, $line_nr, $closed_command,
1731 $interrupting_command);
1732 $current = _end_preformatted($self, $current, $line_nr, $closed_command,
1733 $interrupting_command);
1734
1735 # stop if the command is found
1736 while (!($closed_command and $current->{'cmdname'}
1737 and $current->{'cmdname'} eq $closed_command)
1738 # stop if at the root
1739 and $current->{'parent'}
1740 # stop if in a root command
1741 # or in a context_brace_commands and searching for a specific
1742 # end block command (with $closed_command set).
1743 # This second condition means that a footnote is not closed when
1744 # looking for the end of a block command, but is closed when
1745 # completly closing the stack.
1746 and !($current->{'cmdname'}
1747 and ($root_commands{$current->{'cmdname'}}
1748 or ($closed_command and $current->{'parent'}->{'cmdname'}
1749 and $context_brace_commands{$current->{'parent'}->{'cmdname'}})))){
1750 $self->_close_command_cleanup($current);
1751 $current = $self->_close_current($current, $line_nr, $closed_command,
1752 $interrupting_command);
1753 }
1754
1755 my $closed_element;
1756 if ($closed_command and $current->{'cmdname'}
1757 and $current->{'cmdname'} eq $closed_command) {
1758 if ($preformatted_commands{$current->{'cmdname'}}) {
1759 my $context = pop @{$self->{'context_stack'}};
1760 if ($context ne 'preformatted') {
1761 $self->_bug_message("context $context instead of preformatted for $closed_command",
1762 $line_nr, $current);
1763 }
1764 } elsif ($format_raw_commands{$current->{'cmdname'}}) {
1765 my $context = pop @{$self->{'context_stack'}};
1766 if ($context ne 'rawpreformatted') {
1767 $self->_bug_message("context $context instead of rawpreformatted for $closed_command",
1768 $line_nr, $current);
1769 }
1770 pop @{$self->{'raw_formats_stack'}};
1771 } elsif ($menu_commands{$current->{'cmdname'}}) {
1772 my $context = pop @{$self->{'context_stack'}};
1773 # may be in menu, but context is preformatted if in a preformatted too.
1774 if ($context ne 'menu' and $context ne 'preformatted') {
1775 $self->_bug_message("context $context instead of preformatted or menu for $closed_command",
1776 $line_nr, $current);
1777 }
1778 }
1779 #print STDERR "close context $context for $current->{'cmdname'}\n"
1780 # if ($self->{'DEBUG'});
1781 pop @{$self->{'regions_stack'}}
1782 if ($region_commands{$current->{'cmdname'}});
1783 $closed_element = $current;
1784 #$self->_close_command_cleanup($current);
1785 $current = $current->{'parent'};
1786 } elsif ($closed_command) {
1787 $self->line_error(sprintf($self->__("unmatched `%c%s'"),
1788 ord('@'), "end $closed_command"), $line_nr);
1789 }
1790 return ($closed_element, $current);
1791}
1792
1793# begin paragraph if needed. If not try to merge with the previous
1794# content if it is also some text.
1795sub _merge_text($$$)
1796{
1797 my $self = shift;
1798 my $current = shift;
1799 my $text = shift;
1800
1801 my $paragraph;
1802
1803 my $no_merge_with_following_text = 0;
1804 if ($text =~ /\S/) {
1805 my $leading_spaces;
1806 if ($text =~ /^(\s+)/) {
1807 $leading_spaces = $1;
1808 }
1809 if ($current->{'contents'} and @{$current->{'contents'}}
1810 and $current->{'contents'}->[-1]->{'type'}
1811 and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
1812 or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
1813 or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
1814 $no_merge_with_following_text = 1;
1815 }
1816 if (_abort_empty_line($self, $current, $leading_spaces)) {
1817 $text =~ s/^(\s+)//;
1818 }
1819
1820 $paragraph = _begin_paragraph($self, $current);
1821 $current = $paragraph if ($paragraph);
1822 }
1823
1824 if (!defined($current->{'contents'})) {
1825 $self->_bug_message("No contents in _merge_text",
1826 undef, $current);
1827 die;
1828 }
1829
1830 if (@{$current->{'contents'}}
1831 and exists($current->{'contents'}->[-1]->{'text'})
1832 and $current->{'contents'}->[-1]->{'text'} !~ /\n/
1833 and !$no_merge_with_following_text) {
1834 $current->{'contents'}->[-1]->{'text'} .= $text;
1835 print STDERR "MERGED TEXT: $text|||\n" if ($self->{'DEBUG'});
1836 } else {
1837 push @{$current->{'contents'}}, { 'text' => $text, 'parent' => $current };
1838 print STDERR "NEW TEXT: $text|||\n" if ($self->{'DEBUG'});
1839 }
1840 return $current;
1841}
1842
1843# return the parent if in a item_container command, itemize or enumerate
1844sub _item_container_parent($)
1845{
1846 my $current = shift;
1847 if ((($current->{'cmdname'} and $current->{'cmdname'} eq 'item')
1848 or ($current->{'type'} and $current->{'type'} eq 'before_item'))
1849 and ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
1850 and $item_container_commands{$current->{'parent'}->{'cmdname'}})) {
1851 return ($current->{'parent'});
1852 }
1853 return undef;
1854}
1855
1856# return the parent if in a item_line command, @*table
1857sub _item_line_parent($)
1858{
1859 my $current = shift;
1860 if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'item'
1861 or $current->{'cmdname'} eq 'itemx')) {
1862 $current = $current->{'parent'}->{'parent'};
1863 } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
1864 and $current->{'parent'}) {
1865 $current = $current->{'parent'};
1866 }
1867 return $current if ($current->{'cmdname'}
1868 and $item_line_commands{$current->{'cmdname'}});
1869 return undef;
1870}
1871
1872# return the parent if in a multitable
1873sub _item_multitable_parent($)
1874{
1875 my $current = shift;
1876 if (($current->{'cmdname'} and ($current->{'cmdname'} eq 'headitem'
1877 or $current->{'cmdname'} eq 'item' or $current->{'cmdname'} eq 'tab'))
1878 and $current->{'parent'} and $current->{'parent'}->{'parent'}) {
1879 $current = $current->{'parent'}->{'parent'};
1880 } elsif ($current->{'type'} and $current->{'type'} eq 'before_item'
1881 and $current->{'parent'}) {
1882 $current = $current->{'parent'};
1883 }
1884 return $current if ($current->{'cmdname'}
1885 and $current->{'cmdname'} eq 'multitable');
1886 return undef;
1887}
1888
1889# returns next text fragment, be it pending from a macro expansion or
1890# text or file
1891sub _next_text($$$)
1892{
1893 my $self = shift;
1894 my $line_nr = shift;
1895 my $current = shift;
1896
1897 while (@{$self->{'input'}}) {
1898 my $input = $self->{'input'}->[0];
1899 if (@{$input->{'pending'}}) {
1900 my $new_text = shift @{$input->{'pending'}};
1901 if ($new_text->[1] and $new_text->[1]->{'end_macro'}) {
1902 delete $new_text->[1]->{'end_macro'};
1903 my $top_macro = shift @{$self->{'macro_stack'}};
1904 print STDERR "SHIFT MACRO_STACK(@{$self->{'macro_stack'}}): $top_macro->{'args'}->[0]->{'text'}\n"
1905 if ($self->{'DEBUG'});
1906 }
1907 return ($new_text->[0], $new_text->[1]);
1908 } elsif ($input->{'fh'}) {
1909 my $fh = $input->{'fh'};
1910 my $line = <$fh>;
1911 while (defined($line)) {
1912 $line =~ s/\x{7F}.*\s*//;
1913 if ($self->{'CPP_LINE_DIRECTIVES'}
1914 # no cpp directives in ignored/macro/verbatim
1915 and defined ($current)
1916 and not
1917 (($current->{'cmdname'}
1918 and $block_commands{$current->{'cmdname'}}
1919 and ($block_commands{$current->{'cmdname'}} eq 'raw'
1920 or $block_commands{$current->{'cmdname'}} eq 'conditional'))
1921 or
1922 ($current->{'parent'} and $current->{'parent'}->{'cmdname'}
1923 and $current->{'parent'}->{'cmdname'} eq 'verb')
1924 )
1925 and $line =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/) {
1926 $input->{'line_nr'} = $2;
1927 if (defined($5)) {
1928 $input->{'name'} = $5;
1929 }
1930 $line = <$fh>;
1931 } else {
1932 $input->{'line_nr'}++;
1933 return ($line, {'line_nr' => $input->{'line_nr'},
1934 'file_name' => $input->{'name'},
1935 'macro' => ''});
1936 }
1937 }
1938 }
1939 my $previous_input = shift(@{$self->{'input'}});
1940 # Don't close STDIN
1941 if ($previous_input->{'fh'} and $previous_input->{'name'} ne '-') {
1942 if (!close($previous_input->{'fh'})) {
1943 $self->document_warn(sprintf($self->__("error on closing %s: %s"),
1944 $previous_input->{'name'}, $!));
1945
1946 }
1947 }
1948 }
1949
1950 return (undef, $line_nr);
1951}
1952
1953# collect text and line numbers until an end of line is found.
1954sub _new_line($$$)
1955{
1956 my $self = shift;
1957 my $line_nr = shift;
1958 my $current = shift;
1959 my $new_line = '';
1960
1961 while (1) {
1962 my $new_text;
1963 ($new_text, $line_nr) = _next_text($self, $line_nr, $current);
1964 if (!defined($new_text)) {
1965 $new_line = undef if ($new_line eq '');
1966 last;
1967 }
1968
1969 $new_line .= $new_text;
1970
1971 my $chomped_text = $new_text;
1972 last if chomp($chomped_text);
1973 }
1974 return ($new_line, $line_nr);
1975}
1976
1977sub _expand_macro_arguments($$$$)
1978{
1979 my $self = shift;
1980 my $macro = shift;
1981 my $line = shift;
1982 my $line_nr = shift;
1983 my $braces_level = 1;
1984 my $arguments = [ '' ];
1985 my $arg_nr = 0;
1986 my $args_total = scalar(@{$macro->{'args'}}) -1;
1987 my $name = $macro->{'args'}->[0]->{'text'};
1988
1989 my $line_nr_orig = $line_nr;
1990
1991 while (1) {
1992 if ($line =~ s/([^\\{},]*)([\\{},])//) {
1993 my $separator = $2;
1994 $arguments->[-1] .= $1;
1995 if ($separator eq '\\') {
1996 if ($line =~ s/^(.)//) {
1997 my $protected_char = $1;
1998 if ($protected_char !~ /[\\{},]/) {
1999 $arguments->[-1] .= '\\';
2000 }
2001 $arguments->[-1] .= $protected_char;
2002
2003 print STDERR "MACRO ARG: $separator: $protected_char\n" if ($self->{'DEBUG'});
2004 } else {
2005 $arguments->[-1] .= '\\';
2006 print STDERR "MACRO ARG: $separator\n" if ($self->{'DEBUG'});
2007 }
2008 } elsif ($separator eq ',') {
2009 if ($braces_level == 1) {
2010 if (scalar(@$arguments) < $args_total) {
2011 push @$arguments, '';
2012 $line =~ s/^\s*//;
2013 print STDERR "MACRO NEW ARG\n" if ($self->{'DEBUG'});
2014 } else {
2015 # implicit quoting when there is one argument.
2016 if ($args_total != 1) {
2017 $self->line_error(sprintf($self->__(
2018 "macro `%s' called with too many args"),
2019 $name), $line_nr);
2020 }
2021 $arguments->[-1] .= ',';
2022 }
2023 } else {
2024 $arguments->[-1] .= ',';
2025 }
2026 } elsif ($separator eq '}') {
2027 $braces_level--;
2028 last if ($braces_level == 0);
2029 $arguments->[-1] .= $separator;
2030 } elsif ($separator eq '{') {
2031 $braces_level++;
2032 $arguments->[-1] .= $separator;
2033 }
2034 } else {
2035 print STDERR "MACRO ARG end of line\n" if ($self->{'DEBUG'});
2036 $arguments->[-1] .= $line;
2037
2038 ($line, $line_nr) = _new_line($self, $line_nr, $macro);
2039 if (!defined($line)) {
2040 $self->line_error(sprintf($self->__("\@%s missing close brace"),
2041 $name), $line_nr_orig);
2042 return ($arguments, "\n", $line_nr);
2043 }
2044 }
2045 }
2046 if ($args_total == 0 and $arguments->[0] =~ /\S/) {
2047 $self->line_error(sprintf($self->__(
2048 "macro `%s' declared without argument called with an argument"),
2049 $name), $line_nr);
2050 }
2051 print STDERR "END MACRO ARGS EXPANSION(".scalar(@$arguments)."): ".
2052 join("|\n", @$arguments) ."|\n" if ($self->{'DEBUG'});
2053 return ($arguments, $line, $line_nr);
2054}
2055
2056sub _expand_macro_body($$$$) {
2057 my $self = shift;
2058 my $macro = shift;
2059 my $args = shift;
2060 my $line_nr = shift;
2061
2062 my $macrobody = $macro->{'extra'}->{'macrobody'};
2063 my $args_total = scalar(@{$macro->{'args'}}) -1;
2064 my $args_index = $macro->{'extra'}->{'args_index'};
2065
2066 my $i;
2067 for ($i=0; $i<=$args_total; $i++) {
2068 $args->[$i] = "" unless (defined($args->[$i]));
2069 }
2070
2071 my $result = '';
2072 while ($macrobody ne '') {
2073 if ($macrobody =~ s/^([^\\]*)\\//o) {
2074 $result .= $1;
2075 if ($macrobody =~ s/^\\//) {
2076 $result .= '\\';
2077 } elsif ($macrobody =~ s/^([^\\]*)\\//) {
2078 my $arg = $1;
2079 if (defined($args_index->{$arg})) {
2080 $result .= $args->[$args_index->{$arg}];
2081 } else {
2082 $self->line_error(sprintf($self->__(
2083 "\\ in \@%s expansion followed `%s' instead of parameter name or \\"),
2084 $macro->{'args'}->[0]->{'text'}, $arg), $line_nr);
2085 $result .= '\\' . $arg;
2086 }
2087 }
2088 next;
2089 }
2090 $result .= $macrobody;
2091 last;
2092 }
2093 return $result;
2094}
2095
2096# each time a new line appeared, a container is opened to hold the text
2097# consisting only of spaces. This container is removed here, typically
2098# this is called when non-space happens on a line.
2099sub _abort_empty_line($$;$)
2100{
2101 my $self = shift;
2102 my $current = shift;
2103 my $additional_text = shift;
2104 $additional_text = '' if (!defined($additional_text));
2105 if ($current->{'contents'} and @{$current->{'contents'}}
2106 and $current->{'contents'}->[-1]->{'type'}
2107 and ($current->{'contents'}->[-1]->{'type'} eq 'empty_line'
2108 or $current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command'
2109 or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_before_argument'
2110 or $current->{'contents'}->[-1]->{'type'} eq 'empty_spaces_after_close_brace')) {
2111 print STDERR "ABORT EMPTY additional text |$additional_text|, current |$current->{'contents'}->[-1]->{'text'}|\n" if ($self->{'DEBUG'});
2112 $current->{'contents'}->[-1]->{'text'} .= $additional_text;
2113 if ($current->{'contents'}->[-1]->{'text'} eq '') {
2114 if ($current->{'extra'}
2115 and $current->{'extra'}->{'spaces_before_argument'}
2116 and $current->{'extra'}->{'spaces_before_argument'}
2117 eq $current->{'contents'}->[-1]) {
2118 delete ($current->{'extra'}->{'spaces_before_argument'});
2119 delete ($current->{'extra'}) if !(keys(%{$current->{'extra'}}));
2120 }
2121 pop @{$current->{'contents'}}
2122 } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line') {
2123 # exactly the same condition than to begin a paragraph
2124 if ((!$current->{'type'} or $type_with_paragraph{$current->{'type'}})
2125 and !$no_paragraph_contexts{$self->{'context_stack'}->[-1]}) {
2126 $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_before_paragraph';
2127 } else {
2128 delete $current->{'contents'}->[-1]->{'type'};
2129 }
2130 } elsif ($current->{'contents'}->[-1]->{'type'} eq 'empty_line_after_command') {
2131 $current->{'contents'}->[-1]->{'type'} = 'empty_spaces_after_command';
2132 }
2133 return 1;
2134 }
2135 return 0;
2136}
2137
2138# isolate last space in a command to help expansion disregard unuseful spaces.
2139sub _isolate_last_space($$;$)
2140{
2141 my $self = shift;
2142 my $current = shift;
2143 my $type = shift;
2144 $type = 'spaces_at_end' if (!defined($type));
2145 if ($current->{'contents'} and @{$current->{'contents'}}) {
2146 my $index = -1;
2147 $index = -2
2148 if (scalar(@{$current->{'contents'}}) > 1
2149 and $current->{'contents'}->[-1]->{'cmdname'}
2150 and $self->{'misc_commands'}->{$current->{'contents'}->[-1]->{'cmdname'}});
2151 if (defined($current->{'contents'}->[$index]->{'text'})
2152 and !$current->{'contents'}->[$index]->{'type'}
2153 and $current->{'contents'}->[$index]->{'text'} =~ /\s+$/) {
2154 if ($current->{'contents'}->[$index]->{'text'} !~ /\S/) {
2155 $current->{'contents'}->[$index]->{'type'} = $type;
2156 } else {
2157 $current->{'contents'}->[$index]->{'text'} =~ s/(\s+)$//;
2158 my $spaces = $1;
2159 my $new_spaces = { 'text' => $spaces, 'parent' => $current,
2160 'type' => $type };
2161 if ($index == -1) {
2162 push @{$current->{'contents'}}, $new_spaces;
2163 } else {
2164 splice (@{$current->{'contents'}}, $index+1, 0, $new_spaces);
2165 }
2166 }
2167 }
2168 }
2169}
2170
2171# used to put a node name in error messages.
2172sub _node_extra_to_texi($)
2173{
2174 my $node = shift;
2175 my $result = '';
2176 if ($node->{'manual_content'}) {
2177 $result = '('.Texinfo::Convert::Texinfo::convert({'contents'
2178 => $node->{'manual_content'}}) .')';
2179 }
2180 if ($node->{'node_content'}) {
2181 $result .= Texinfo::Convert::Texinfo::convert ({'contents'
2182 => $node->{'node_content'}});
2183 }
2184 return $result;
2185}
2186
2187sub _find_end_brace($$)
2188{
2189 my $text = shift;
2190 my $braces_count = shift;
2191
2192 my $before = '';
2193 while ($braces_count > 0 and length($text)) {
2194 if ($text =~ s/([^()]*)([()])//) {
2195 $before .= $1.$2;
2196 my $brace = $2;
2197 if ($brace eq '(') {
2198 $braces_count++;
2199 } else {
2200 $braces_count--;
2201 if ($braces_count == 0) {
2202 return ($before, $text, 0);
2203 }
2204 }
2205 } else {
2206 $before .= $text;
2207 $text = '';
2208 }
2209 }
2210 return ($before, undef, $braces_count);
2211}
2212
2213# This only counts opening braces, and returns 0 once all the parentheses
2214# are closed
2215sub _count_opened_tree_braces($$);
2216sub _count_opened_tree_braces($$)
2217{
2218 my $current = shift;
2219 my $braces_count = shift;
2220 if (defined($current->{'text'})) {
2221 my ($before, $after);
2222 ($before, $after, $braces_count) = _find_end_brace($current->{'text'},
2223 $braces_count);
2224 }
2225 if ($current->{'args'}) {
2226 foreach my $arg (@{$current->{'args'}}) {
2227 $braces_count = _count_opened_tree_braces($arg, $braces_count);
2228 return $braces_count if ($braces_count == 0);
2229 }
2230 }
2231 if ($current->{'contents'}) {
2232 foreach my $content (@{$current->{'contents'}}) {
2233 $braces_count = _count_opened_tree_braces($content, $braces_count);
2234 return $braces_count if ($braces_count == 0);
2235 }
2236 }
2237 return $braces_count;
2238}
2239
2240# retrieve a leading manual name in parentheses, if there is one.
2241sub _parse_node_manual($)
2242{
2243 my $node = shift;
2244 my @contents = @{$node->{'contents'}};
2245 _trim_spaces_comment_from_content(\@contents);
2246
2247 my $manual;
2248 my $result;
2249#print STDERR "RRR $contents[0] and $contents[0]->{'text'} \n";
2250 if ($contents[0] and $contents[0]->{'text'} and $contents[0]->{'text'} =~ /^\(/) {
2251 my $braces_count = 1;
2252 if ($contents[0]->{'text'} !~ /^\($/) {
2253 my $brace = shift @contents;
2254 my $brace_text = $brace->{'text'};
2255 $brace_text =~ s/^\(//;
2256 unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'},
2257 'parent' => $brace->{'parent'} } if $brace_text ne '';
2258 } else {
2259 shift @contents;
2260 }
2261 while(@contents) {
2262 my $content = shift @contents;
2263 if (!defined($content->{'text'}) or $content->{'text'} !~ /\)/) {
2264 push @$manual, $content;
2265 $braces_count = _count_opened_tree_braces($content, $braces_count);
2266 # This is an error, braces were closed in a command
2267 if ($braces_count == 0) {
2268 last;
2269 }
2270 } else {
2271 my ($before, $after);
2272 ($before, $after, $braces_count) = _find_end_brace($content->{'text'},
2273 $braces_count);
2274 if ($braces_count == 0) {
2275 $before =~ s/\)$//;
2276 push @$manual, { 'text' => $before, 'parent' => $content->{'parent'} }
2277 if ($before ne '');
2278 $after =~ s/^\s*//;
2279 unshift @contents, { 'text' => $after, 'parent' => $content->{'parent'} }
2280 if ($after ne '');
2281 last;
2282 } else {
2283 push @$manual, $content;
2284 }
2285 }
2286 }
2287 $result->{'manual_content'} = $manual if (defined($manual));
2288 }
2289 if (@contents) {
2290 $result->{'node_content'} = \@contents;
2291 $result->{'normalized'} =
2292 Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => \@contents});
2293 }
2294 return $result;
2295}
2296
2297sub _parse_float_type($)
2298{
2299 my $current = shift;
2300 if (@{$current->{'args'}}) {
2301 my @type_contents = @{$current->{'args'}->[0]->{'contents'}};
2302 _trim_spaces_comment_from_content(\@type_contents);
2303 if (@type_contents) {
2304 my $normalized
2305 = Texinfo::Convert::NodeNameNormalization::normalize_node(
2306 {'contents' => \@type_contents});
2307 $current->{'extra'}->{'type'}->{'content'} = \@type_contents;
2308 if ($normalized =~ /[^-]/) {
2309 $current->{'extra'}->{'type'}->{'normalized'} = $normalized;
2310 return 1;
2311 }
2312 }
2313 }
2314 $current->{'extra'}->{'type'}->{'normalized'} = '';
2315 return 0;
2316}
2317
2318# used for definition line parsing
2319sub _next_bracketed_or_word($$)
2320{
2321 my $self = shift;
2322 my $contents = shift;
2323 return undef if (!scalar(@{$contents}));
2324 my $spaces;
2325 $spaces = shift @{$contents} if (defined($contents->[0]->{'text'}) and
2326 $contents->[0]->{'text'} !~ /\S/);
2327 if (defined($spaces)) {
2328 $spaces->{'type'} = 'spaces';
2329 chomp $spaces->{'text'};
2330 $spaces = undef if ($spaces->{'text'} eq '');
2331 }
2332 return ($spaces, undef) if (!scalar(@{$contents}));
2333
2334 #print STDERR "BEFORE PROCESSING ".Texinfo::Convert::Texinfo::convert({'contents' => $contents});
2335 if ($contents->[0]->{'type'} and $contents->[0]->{'type'} eq 'bracketed') {
2336 #print STDERR "Return bracketed\n";
2337 my $bracketed = shift @{$contents};
2338 $self->_isolate_last_space($bracketed, 'empty_space_at_end_def_bracketed');
2339 my $bracketed_def_content = { 'contents' => $bracketed->{'contents'},
2340 'parent' => $bracketed->{'parent'},
2341 'type' => 'bracketed_def_content', };
2342 if ($bracketed->{'extra'} and $bracketed->{'extra'}->{'spaces_before_argument'}) {
2343 $bracketed_def_content->{'extra'}->{'spaces_before_argument'}
2344 = $bracketed->{'extra'}->{'spaces_before_argument'};
2345 }
2346 return ($spaces, $bracketed_def_content);
2347 } elsif ($contents->[0]->{'cmdname'}) {
2348 #print STDERR "Return command $contents->[0]->{'cmdname'}\n";
2349 return ($spaces, shift @{$contents});
2350 } else {
2351 #print STDERR "Process $contents->[0]->{'text'}\n";
2352 $contents->[0]->{'text'} =~ s/^(\s*)//;
2353 my $space_text = $1;
2354 $spaces = {'text' => $space_text, 'type' => 'spaces'} if ($space_text);
2355 $contents->[0]->{'text'} =~ s/^(\S+)//;
2356 shift @{$contents} if ($contents->[0]->{'text'} eq '');
2357 return ($spaces, {'text' => $1});
2358 }
2359}
2360
2361# definition line parsing
2362sub _parse_def($$$)
2363{
2364 my $self = shift;
2365 my $command = shift;
2366 my $contents = shift;
2367
2368 my @contents = @$contents;
2369 shift @contents if ($contents[0] and $contents[0]->{'type'}
2370 and $contents[0]->{'type'} eq 'empty_spaces_after_command');
2371 if ($def_aliases{$command}) {
2372 my $real_command = $def_aliases{$command};
2373 my $prepended = $def_map{$command}->{$real_command};
2374 my @prepended_content;
2375
2376 my $text;
2377 my $in_bracketed;
2378 if ($prepended =~ /^\{/) {
2379 $text = $prepended;
2380 $text =~ s/\{([^\}]+)\}/$1/;
2381 $in_bracketed = 1;
2382 } else {
2383 $text = $prepended;
2384 }
2385 my $tree = $self->gdt($text);
2386 if ($in_bracketed or @{$tree->{'contents'}} > 1) {
2387 my $bracketed = { 'type' => 'bracketed' };
2388 $bracketed->{'contents'} = $tree->{'contents'};
2389 foreach my $content (@{$tree->{'contents'}}) {
2390 $content->{'parent'} = $bracketed;
2391 }
2392 @prepended_content = ($bracketed);
2393 } else {
2394 @prepended_content = (@{$tree->{'contents'}});
2395 }
2396 push @prepended_content, { 'text' => ' ' };
2397
2398 unshift @contents, @prepended_content;
2399
2400 $command = $def_aliases{$command};
2401 }
2402 foreach (my $i = 0; $i < scalar(@contents); $i++) {
2403 # copy, to avoid changing the original
2404 $contents[$i] = {'text' => $contents[$i]->{'text'}}
2405 if (defined($contents[$i]->{'text'}));
2406 }
2407 my @result;
2408 my @args = @{$def_map{$command}};
2409 my $arg_type;
2410 # Even when $arg_type is not set, that is for def* that is not documented
2411 # to take args, everything is as is arg_type was set to arg.
2412 $arg_type = pop @args if ($args[-1] eq 'arg' or $args[-1] eq 'argtype');
2413 foreach my $arg (@args) {
2414 #print STDERR "$command $arg"._print_current($contents[0]);
2415 #foreach my $content (@contents) {print STDERR " "._print_current($content)};
2416 #print STDERR " contents ->".Texinfo::Convert::Texinfo::convert ({'contents' => \@contents});
2417 my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
2418 last if (!defined($next));
2419 #print STDERR "NEXT[$arg] ".Texinfo::Convert::Texinfo::convert($next)."\n";
2420 push @result, ['spaces', $spaces] if (defined($spaces));
2421 push @result, [$arg, $next];
2422 }
2423
2424 my @args_results;
2425 while (@contents) {
2426 my ($spaces, $next) = $self->_next_bracketed_or_word(\@contents);
2427 push @args_results, ['spaces', $spaces] if (defined($spaces));
2428 last if (!defined($next));
2429 if (defined($next->{'text'})) {
2430 while (1) {
2431 if ($next->{'text'} =~ s/^([^\[\](),]+)//) {
2432 push @args_results, ['arg', {'text' => $1}];
2433 } elsif ($next->{'text'} =~ s/^([\[\](),])//) {
2434 push @args_results, ['delimiter',
2435 {'text' => $1, 'type' => 'delimiter'}];
2436 } else {
2437 last;
2438 }
2439 }
2440 } else {
2441 push @args_results, [ 'arg', $next ];
2442 }
2443 }
2444 if ($arg_type and $arg_type eq 'argtype') {
2445 my $next_is_type = 1;
2446 foreach my $arg(@args_results) {
2447 if ($arg->[0] eq 'spaces') {
2448 } elsif ($arg->[0] eq 'delimiter') {
2449 $next_is_type = 1;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches