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