1# Common.pm: definition of commands. Common code of other Texinfo modules. 2# 3# Copyright 2010-2020 Free Software Foundation, Inc. 4# 5# This program is free software; you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation; either version 3 of the License, 8# or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17# 18# Original author: Patrice Dumas <pertusus@free.fr> 19# Parts (also from Patrice Dumas) come from texi2html.pl or texi2html.init. 20 21package Texinfo::Common; 22 23use strict; 24 25# for unicode/layer support in binmode 26use 5.006; 27 28# to determine the null file 29use Config; 30use File::Spec; 31 32use Texinfo::Documentlanguages; 33 34# debugging 35use Carp qw(cluck); 36 37require Exporter; 38use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); 39@ISA = qw(Exporter); 40 41%EXPORT_TAGS = ( 'all' => [ qw( 42debug_hash 43debug_list 44definition_category 45expand_verbatiminclude 46expand_today 47float_name_caption 48is_content_empty 49move_index_entries_after_items_in_tree 50normalize_top_node_name 51numbered_heading 52protect_comma_in_tree 53protect_first_parenthesis 54protect_hashchar_at_line_beginning 55protect_colon_in_tree 56protect_node_after_label_in_tree 57trim_spaces_comment_from_content 58valid_tree_transformation 59) ] ); 60 61@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); 62 63@EXPORT = qw( 64__ __p print_tree 65); 66 67$VERSION = '6.8'; 68 69# i18n 70sub N__($) 71{ 72 return $_[0]; 73} 74 75# determine the null devices 76my $default_null_device = File::Spec->devnull(); 77our %null_device_file = ( 78 $default_null_device => 1 79); 80# special case, djgpp recognizes both null devices 81if ($Config{osname} eq 'dos' and $Config{osvers} eq 'djgpp') { 82 $null_device_file{'/dev/null'} = 1; 83 $null_device_file{'NUL'} = 1; 84} 85 86use Locale::Messages; 87 88my $messages_textdomain = 'texinfo'; 89 90sub __($) { 91 my $msgid = shift; 92 return Locale::Messages::dgettext($messages_textdomain, $msgid); 93} 94 95sub __p($$) { 96 my $context = shift; 97 my $msgid = shift; 98 return Locale::Messages::dpgettext($messages_textdomain, $context, $msgid); 99} 100 101# these are the default values for the parser state that may be 102# initialized to values given by the user. 103# They are defined here, because they are used below and we 104# don't want Texinfo::Common to use Texinfo::Parser. 105our %default_parser_state_configuration = ( 106 'expanded_formats' => [], 107 'include_directories' => [ '.' ], 108 # these are the user-added indices. May be an array reference on names 109 # or an hash reference in the same format than %index_names below 110 'indices' => [], 111 # the following are dynamically modified during the document parsing. 112 'aliases' => {}, # key is a command name value is the alias 113 'documentlanguage' => undef, 114 # Current documentlanguage set by 115 # @documentlanguage 116 'explained_commands' => {}, # the key is a command name, either acronym 117 # or abbr, the value is a hash. The key hash 118 # is a normalized first argument of the 119 # corresponding command, the value is the 120 # contents array of the previous command with 121 # this first arg and a second arg. 122 'labels' => {}, # keys are normalized label names, as described 123 # in the `HTML Xref' node. Value should be 124 # a node/anchor or float in the tree. 125 'targets' => [], # array of elements used to build 'labels' 126 'macros' => {}, # the key is the user-defined macro name. The 127 # value is the reference on a macro element 128 # as obtained by parsing the @macro 129 'merged_indices' => {}, # the key is merged in the value 130 'sections_level' => 0, # modified by raise/lowersections 131 'values' => {'txicommandconditionals' => 1}, 132 # the key is the name, the value the @set name 133 # argument. A Texinfo tree may also be used. 134 'info' => { 135 'novalidate' => 0, # same as setting @novalidate. 136 'input_encoding_name' => 'utf-8', 137 'input_perl_encoding' => 'utf-8' 138 }, 139 'in_gdt' => 0 # whether we are being called by gdt 140); 141 142 143# Customization variables obeyed by the parser, and the default values. 144our %default_parser_customization_values = ( 145 'DEBUG' => 0, # if >= 10, tree is printed in texi2any.pl after parsing. 146 # If >= 100 tree is printed every line. 147 'FORMAT_MENU' => 'menu', # if not 'menu' no menu error related. 148 'IGNORE_BEFORE_SETFILENAME' => 1, 149 'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME' => 1, 150 'CPP_LINE_DIRECTIVES' => 1, # handle cpp like synchronization lines 151 'MAX_MACRO_CALL_NESTING' => 100000, # max number of nested macro calls 152 # This is not used directly, but passed to Convert::Text through 153 # Texinfo::Common::_convert_text_options 154 'ENABLE_ENCODING' => 1, # output accented and special characters 155 # based on @documentencoding 156); 157 158# Customization variables set in the parser for other modules, and the 159# default values. 160our %default_structure_customization_values = ( 161 # following are used in Texinfo::Structuring 162 'USE_UP_NODE_FOR_ELEMENT_UP' => 0, # Use node up for Up if there is no 163 # section up. 164 'CHECK_NORMAL_MENU_STRUCTURE' => 0, # output warnings when node with 165 # automatic direction does directions in menu are not consistent 166 # with sectionning, and when node directions are not consistent 167 # with menu directions 168); 169 170 171# customization options 172our %document_settable_at_commands = ( 173 'allowcodebreaks' => 'true', 174 'clickstyle' => '@arrow', 175 'codequotebacktick' => 'off', 176 'codequoteundirected' => 'off', 177 'contents' => 0, 178 'deftypefnnewline' => 'off', 179 'documentencoding' => 'us-ascii', 180 'documentlanguage' => 'en', 181 # is N ems in TeX, 0.4 in. 182 'exampleindent' => 5, 183 'firstparagraphindent' => 'none', 184 'frenchspacing' => 'off', 185 'headings' => 'on', 186 'kbdinputstyle' => 'distinct', 187 'paragraphindent' => 3, 188 'shortcontents' => 0, 189 'urefbreakstyle' => 'after', 190 'xrefautomaticsectiontitle' => 'off', 191); 192 193# those should be unique 194our %document_settable_unique_at_commands = ( 195 # when passed through a configuration variable, documentdescription 196 # should be already formatted for HTML 197 'documentdescription' => undef, 198 'evenfootingmarks' => undef, 199 'evenheadingmarks' => undef, 200 'everyfootingmarks' => 'bottom', 201 'everyheadingmarks' => 'bottom', 202 'fonttextsize' => 11, 203 'footnotestyle' => 'end', 204 'novalidate' => 0, 205 'oddfootingmarks' => undef, 206 'oddheadingmarks' => undef, 207 # FIXME not clear here. 208 'pagesizes' => undef, 209 'setchapternewpage' => 'on', 210 'setfilename' => undef, 211 'everyheading' => undef, 212 'everyfooting' => undef, 213 'evenheading' => undef, 214 'evenfooting' => undef, 215 'oddheading' => undef, 216 'oddfooting' => undef, 217); 218 219my @command_line_settables = ( 220 'CASE_INSENSITIVE_FILENAMES', 'ENABLE_ENCODING', 'ERROR_LIMIT', 221 'FILLCOLUMN', 'FORCE', 'HEADERS', 'INTERNAL_LINKS', 'MACRO_EXPAND', 222 'NODE_FILES', 'NO_WARN', 'NUMBER_FOOTNOTES', 'NUMBER_SECTIONS', 223 'OUTFILE', 'SPLIT', 'SPLIT_SIZE', 'SUBDIR', 'TRANSLITERATE_FILE_NAMES', 224 'VERBOSE' 225); 226 227# documented in the Texinfo::Parser pod section 228# all are lower cased in texi2any.pl 229my @parser_options = map {uc($_)} (keys(%default_parser_state_configuration)); 230 231our @variable_string_settables = ( 232'AFTER_ABOUT', 233'AFTER_BODY_OPEN', 234'AFTER_OVERVIEW', 235'AFTER_TOC_LINES', 236'AVOID_MENU_REDUNDANCY', 237'BASEFILENAME_LENGTH', 238'BEFORE_OVERVIEW', 239'BEFORE_TOC_LINES', 240'BIG_RULE', 241'BODYTEXT', 242'COPIABLE_ANCHORS', 243'CHAPTER_HEADER_LEVEL', 244'CHECK_HTMLXREF', 245'CHECK_NORMAL_MENU_STRUCTURE', 246'CLOSE_QUOTE_SYMBOL', 247'COMPLEX_FORMAT_IN_TABLE', 248'CONTENTS_OUTPUT_LOCATION', 249'CPP_LINE_DIRECTIVES', 250'CSS_LINES', 251'DATE_IN_HEADER', 252'DEBUG', 253'DEFAULT_RULE', 254'DEF_TABLE', 255'DO_ABOUT', 256'DOCTYPE', 257'DUMP_TEXI', 258'DUMP_TREE', 259'ENABLE_ENCODING_USE_ENTITY', 260'EXTENSION', 261'EXTERNAL_CROSSREF_SPLIT', 262'EXTERNAL_DIR', 263'EXTRA_HEAD', 264'FOOTNOTE_END_HEADER_LEVEL', 265'FOOTNOTE_SEPARATE_HEADER_LEVEL', 266'FRAMES', 267'FRAMESET_DOCTYPE', 268'HEADER_IN_TABLE', 269'HTML_MATH', 270'HTMLXREF', 271'ICONS', 272'IGNORE_BEFORE_SETFILENAME', 273'IGNORE_SPACE_AFTER_BRACED_COMMAND_NAME', 274'IMAGE_LINK_PREFIX', 275'INDEX_ENTRY_COLON', 276'INDEX_SPECIAL_CHARS_WARNING', 277'INFO_JS_DIR', 278'INFO_SPECIAL_CHARS_QUOTE', 279'INFO_SPECIAL_CHARS_WARNING', 280'INLINE_CSS_STYLE', 281'JS_WEBLABELS', 282'JS_WEBLABELS_FILE', 283'KEEP_TOP_EXTERNAL_REF', 284'L2H', 285'L2H_CLEAN', 286'L2H_FILE', 287'L2H_HTML_VERSION', 288'L2H_L2H', 289'L2H_SKIP', 290'L2H_TMP', 291'MATHJAX_SCRIPT', 292'MATHJAX_SOURCE', 293'MAX_HEADER_LEVEL', 294'MAX_MACRO_CALL_NESTING', 295'MENU_ENTRY_COLON', 296'MENU_SYMBOL', 297'MONOLITHIC', 298'NO_CSS', 299'NODE_FILE_EXTENSION', 300'NODE_FILENAMES', 301'NODE_NAME_IN_INDEX', 302'NODE_NAME_IN_MENU', 303'NO_USE_SETFILENAME', 304'OPEN_QUOTE_SYMBOL', 305'OUTPUT_ENCODING_NAME', 306'OUTPUT_PERL_ENCODING', 307'OVERVIEW_LINK_TO_TOC', 308'PACKAGE', 309'PACKAGE_AND_VERSION', 310'PACKAGE_NAME', 311'PACKAGE_URL', 312'PACKAGE_VERSION', 313'PRE_ABOUT', 314'PRE_BODY_CLOSE', 315'PREFIX', 316'PROGRAM', 317'PROGRAM_NAME_IN_FOOTER', 318'SECTION_NAME_IN_TITLE', 319'SHORTEXTN', 320'FORMAT_MENU', 321'SHOW_TITLE', 322'SIMPLE_MENU', 323'SORT_ELEMENT_COUNT', 324'SORT_ELEMENT_COUNT_WORDS', 325'TEST', 326'TEXI2DVI', 327'TEXI2HTML', 328'TEXINFO_DTD_VERSION', 329'TEXINFO_OUTPUT_FORMAT', 330'TEXTCONTENT_COMMENT', 331'TOC_LINKS', 332'TOP_FILE', 333'TOP_NODE_FILE_TARGET', 334'TOP_NODE_UP', 335'TOP_NODE_UP_URL', 336'TREE_TRANSFORMATIONS', 337'USE_ACCESSKEY', 338'USE_ISO', 339'USE_LINKS', 340'USE_NODES', 341'USE_NODE_DIRECTIONS', 342'USE_NUMERIC_ENTITY', 343'USE_REL_REV', 344'USE_SETFILENAME_EXTENSION', 345'USE_TITLEPAGE_FOR_TITLE', 346'USE_UNIDECODE', 347'USE_UP_NODE_FOR_ELEMENT_UP', 348'VERTICAL_HEAD_NAVIGATION', 349'WORDS_IN_PAGE', 350'XREF_USE_FLOAT_LABEL', 351'XREF_USE_NODE_NAME_ARG', 352); 353 354# Not strings. 355# FIXME To be documented somewhere, but where? 356my @variable_other_settables = ( 357 'LINKS_BUTTONS', 'TOP_BUTTONS', 'SECTION_BUTTONS', 'BUTTONS_TEXT', 358 'BUTTONS_ACCESSKEY', 'BUTTONS_REL', 'BUTTONS_GOTO', 359 'CHAPTER_FOOTER_BUTTONS', 'SECTION_FOOTER_BUTTONS', 360 'NODE_FOOTER_BUTTONS', 361 'MISC_BUTTONS', 'CHAPTER_BUTTONS', 'BUTTONS_NAME', 362 'BUTTONS_EXAMPLE', 'SPECIAL_ELEMENTS_NAME', 'SPECIAL_ELEMENTS_CLASS', 363 'ACTIVE_ICONS', 'PASSIVE_ICONS', 364 'CSS_FILES', 'CSS_REFS', 365 'GLOBAL_COMMANDS', 366); 367 368my %valid_options; 369foreach my $var (keys(%document_settable_at_commands), 370 keys(%document_settable_unique_at_commands), 371 @command_line_settables, @variable_string_settables, 372 @variable_other_settables, @parser_options) { 373 $valid_options{$var} = 1; 374} 375 376sub valid_option($) 377{ 378 my $option = shift; 379 return $valid_options{$option}; 380} 381 382sub add_valid_option($) 383{ 384 my $option = shift; 385 if ($option =~ /^[A-Z][A-Z_]{2,}$/) { 386 $valid_options{$option} = 1; 387 return 1; 388 } 389 return 0; 390} 391 392my %customization_variable_classes = ( 393 'document_settable_at_commands' => [ sort(keys(%document_settable_at_commands)) ], 394 'document_settable_unique_at_commands' => [ sort(keys(%document_settable_unique_at_commands)) ], 395 'command_line_settables' => \@command_line_settables, 396 'variable_string_settables' => \@variable_string_settables, 397 'variable_other_settables' => \@variable_other_settables, 398 'parser_options' => \@parser_options, 399); 400 401my %valid_tree_transformations; 402foreach my $valid_transformation ('simple_menus', 403 'fill_gaps_in_sectioning', 'move_index_entries_after_items', 404 'insert_nodes_for_sectioning_commands', 405 'complete_tree_nodes_menus', 'regenerate_master_menu', 406 'indent_menu_descriptions') { 407 $valid_tree_transformations{$valid_transformation} = 1; 408} 409 410sub valid_tree_transformation ($) 411{ 412 my $transformation = shift; 413 return 1 if (defined($transformation) 414 and $valid_tree_transformations{$transformation}); 415 return 0; 416} 417 418our %no_brace_commands; # commands never taking braces 419%no_brace_commands = ( 420 '*', "\n", 421 ' ', ' ', 422 "\t", ' ', 423 "\n", ' ', 424 '-', '', # hyphenation hint 425 '|', '', # used in formatting commands @evenfooting and friends 426 '/', '', 427 ':', '', 428 '!', '!', 429 '?', '?', 430 '.', '.', 431 '@', '@', 432 '}', '}', 433 '{', '{', 434 '&', '&', 435 '\\', '\\', # should only appear in math 436); 437 438 439# commands taking a line as argument or no argument. 440# sectioning commands and def* commands are added below. 441# index commands are added dynamically. 442# 443# The values signification is: 444# special: no value and macro expansion, all the line is used, and 445# analysed during parsing (_parse_special_misc_command) 446# lineraw: no value and macro expansion, the line is kept as-is, not 447# analysed 448# skipline: no argument, everything else on the line is skipped 449# text: the line is parsed as texinfo, and the argument is converted 450# to simple text (in _end_line) 451# line: the line is parsed as texinfo 452# a number: the line is parsed as texinfo and the result should be plain 453# text maybe followed by a comment; the result is analysed 454# during parsing (_parse_line_command_args). 455# The number is an indication of the number of arguments of 456# the command. 457# 458# Beware that @item may be a 'line' command or an 'other' command 459# depending on the context. 460our %line_commands = ( 461 'node' => 'line', # special arg 462 'bye' => 'skipline', # no arg 463 'end' => 'text', 464 # set, clear 465 'set' => 'special', # special arg 466 'clear' => 'special', # special arg 467 'unmacro' => 'special', 468 # comments 469 'comment' => 'lineraw', 470 'c' => 'lineraw', 471 # special 472 'definfoenclose' => 3, 473 'alias' => 2, 474 # number of arguments is not known in advance. 475 'columnfractions' => 1, 476 # file names 477 'setfilename' => 'text', 478 'verbatiminclude' => 'text', 479 'include' => 'text', 480 481 'raisesections' => 'skipline', # no arg 482 'lowersections' => 'skipline', # no arg 483 'contents' => 'skipline', # no arg 484 'shortcontents' => 'skipline', # no arg 485 'summarycontents' => 'skipline', # no arg 486 'insertcopying' => 'skipline', # no arg 487 'clickstyle' => 'special', # arg should be an @-command 488 # more relevant in preamble 489 'documentencoding' => 'text', # or 1? 490 'novalidate' => 'skipline', # no arg 491 'dircategory' => 'line', # line. Position with regard 492 # with direntry is significant 493 'pagesizes' => 'line', # can have 2 args 494 # or one? 200mm,150mm 11.5in 495 'finalout' => 'skipline', # no arg 496 'paragraphindent' => 1, # arg none asis 497 # or a number and forbids anything else on the line 498 'firstparagraphindent' => 1, # none insert 499 'frenchspacing' => 1, # on off 500 'codequoteundirected' => 1, # on off 501 'codequotebacktick' => 1, # on off 502 'xrefautomaticsectiontitle' => 1, # on off 503 'deftypefnnewline' => 1, # on off 504 'fonttextsize' => 1, # 10 11 505 'allowcodebreaks' => 1, # false or true 506 'exampleindent' => 1, # asis or a number 507 'footnotestyle' => 1, # end and separate, nothing else on the line 508 'urefbreakstyle' => 1, # after|before|none 509 'afourpaper' => 'skipline', # no arg 510 'afivepaper' => 'skipline', # no arg 511 'afourlatex' => 'skipline', # no arg 512 'afourwide' => 'skipline', # no arg 513 'bsixpaper' => 'skipline', # no arg 514 'headings' => 1, #off on single double singleafter doubleafter 515 # interacts with setchapternewpage 516 'setchapternewpage' => 1, # off on odd 517 518 # only relevant in TeX, and special 519 'everyheading' => 'lineraw', # @*heading @*footing use @| 520 'everyfooting' => 'lineraw', # + @thispage @thissectionname 521 'evenheading' => 'lineraw', # @thissectionnum @thissection 522 'evenfooting' => 'lineraw', # @thischaptername @thischapternum 523 'oddheading' => 'lineraw', # @thischapter @thistitle @thisfile 524 'oddfooting' => 'lineraw', 525 526 'smallbook' => 'skipline', # no arg 527 'syncodeindex' => 2, # args are index identifiers 528 'synindex' => 2, 529 'defindex' => 1, # one identifier arg 530 'defcodeindex' => 1, # one identifier arg 531 'documentlanguage' => 'text', # language code arg 532 'kbdinputstyle' => 1, # code example distinct 533 'everyheadingmarks' => 1, # top bottom 534 'everyfootingmarks' => 1, 535 'evenheadingmarks' => 1, 536 'oddheadingmarks' => 1, 537 'evenfootingmarks' => 1, 538 'oddfootingmarks' => 1, 539 540 # formatting 541 'center' => 'line', 542 'printindex' => 1, 543 'listoffloats' => 'line', 544 # especially in titlepage 545# 'shorttitle' => 'line', 546 'shorttitlepage' => 'line', 547 'settitle' => 'line', 548 'author' => 'line', 549 'subtitle' => 'line', 550 'title' => 'line', 551 'sp' => 1, # numerical arg 552 'page' => 'skipline', # no arg (pagebreak) 553 'need' => 1, # one numerical/real arg 554 # formatting 555 'exdent' => 'line', 556 'item' => 'line', # or skipspace, depending on the context 557 'itemx' => 'line', 558 # not valid for info (should be in @iftex) 559 'vskip' => 'lineraw', # arg line in TeX 560 'subentry' => 'line', 561); 562 563# commands that do not take the whole line as argument 564# 565# skipspace: no argument, following spaces are skipped. 566# noarg: no argument 567# 568our %other_commands = ( 569 # formatting 570 'noindent' => 'skipspace', 571 'indent' => 'skipspace', 572 'headitem' => 'skipspace', 573 'item' => 'skipspace', # or line, depending on the context 574 'tab' => 'skipspace', 575 'refill' => 'noarg', # obsolete 576); 577 578# only valid in heading or footing 579our %in_heading_commands; 580foreach my $in_heading_command ('thischapter', 'thischaptername', 581 'thischapternum', 'thisfile', 'thispage', 'thistitle') { 582 $in_heading_commands{$in_heading_command} = 1; 583 584 $other_commands{$in_heading_command} = 'noarg'; 585} 586 587 588# only valid in index entries 589our %in_index_commands; 590foreach my $in_index_command ('sortas', 'seeentry', 'seealso', 'subentry') { 591 $in_index_commands{$in_index_command} = 1; 592} 593 594 595our %index_names = ( 596 'cp' => {'in_code' => 0}, 597 'fn' => {'in_code' => 1}, 598 'vr' => {'in_code' => 1}, 599 'ky' => {'in_code' => 1}, 600 'pg' => {'in_code' => 1}, 601 'tp' => {'in_code' => 1}, 602); 603 604foreach my $index(keys(%index_names)) { 605 $index_names{$index}->{'name'} = $index; 606} 607 608our %default_index_commands; 609# all the commands are readded dynamically in the Parser. 610foreach my $index_name (keys (%index_names)) { 611 if ($index_name =~ /^(.).$/) { 612 my $index_prefix = $1; 613 # only put the one letter versions in the hash. 614 $line_commands{$index_prefix.'index'} = 'line'; 615 $default_index_commands{$index_prefix.'index'} = 1; 616 } 617} 618 619# command with braces. Value is the max number of arguments. 620our %brace_commands; 621 622our %letter_no_arg_commands; 623foreach my $letter_no_arg_command ('aa','AA','ae','oe','AE','OE','o','O', 624 'ss','l','L','DH','dh','TH','th') { 625 $letter_no_arg_commands{$letter_no_arg_command} = 1; 626 $brace_commands{$letter_no_arg_command} = 0; 627} 628 629foreach my $no_arg_command ('TeX','LaTeX','bullet','copyright', 630 'registeredsymbol','dots','enddots','equiv','error','expansion','arrow', 631 'minus','point','print','result','today', 632 'exclamdown','questiondown','pounds','ordf','ordm', 633 'atchar', 'lbracechar', 'rbracechar', 'backslashchar', 'hashchar', 'comma', 634 'ampchar', 635 'euro', 'geq','leq','tie','textdegree','click', 636 'quotedblleft','quotedblright','quoteleft','quoteright','quotedblbase', 637 'quotesinglbase','guillemetleft','guillemetright','guillemotleft', 638 'guillemotright','guilsinglleft','guilsinglright') { 639 $brace_commands{$no_arg_command} = 0; 640} 641 642# accent commands. They may be called with and without braces. 643our %accent_commands; 644foreach my $accent_command ('"','~','^','`',"'",',','=', 645 'ringaccent','H','dotaccent','u','ubaraccent', 646 'udotaccent','v','ogonek','tieaccent', 'dotless') { 647 $accent_commands{$accent_command} = 1; 648 $brace_commands{$accent_command} = 'accent'; 649} 650 651our %style_commands; 652foreach my $style_command ('asis','cite','clicksequence', 653 'dfn', 'emph', 654 'sc', 't', 'var', 655 'headitemfont', 'code', 'command', 'env', 'file', 'kbd', 656 'option', 'samp', 'strong', 'sub', 'sup') { 657 $brace_commands{$style_command} = 'style'; 658 $style_commands{$style_command} = 1; 659} 660 661our %regular_font_style_commands; 662foreach my $command ('r', 'i', 'b', 'sansserif', 'slanted') { 663 $regular_font_style_commands{$command} = 1; 664 $brace_commands{$command} = 'style'; 665 $style_commands{$command} = 1; 666} 667 668foreach my $one_arg_command ('U', 'dmn', 'key', 669 'titlefont', 'anchor', 'errormsg', 'sortas', 'seeentry', 'seealso') { 670 $brace_commands{$one_arg_command} = 1; 671} 672 673# FIXME: 'key', 'verb', 't'? 674foreach my $other_arg_command ('w', 'hyphenation') { 675 $brace_commands{$other_arg_command} = 'other'; 676} 677 678our %code_style_commands; 679foreach my $command ('code', 'command', 'env', 'file', 'kbd', 'key', 'option', 680 'samp', 'verb', 't') { 681 $code_style_commands{$command} = 1; 682 $brace_commands{$command} = 'style'; 683} 684 685# FIXME: a special case? 686$code_style_commands{'indicateurl'} = 1; 687$brace_commands{'indicateurl'} = 1; 688 689 690# Commands that enclose full texts, that can contain multiple paragraphs. 691our %context_brace_commands; 692foreach my $context_brace_command ('footnote', 'caption', 693 'shortcaption') { 694 $context_brace_commands{$context_brace_command} = $context_brace_command; 695 $brace_commands{$context_brace_command} = 'context'; 696} 697 698our %math_commands; 699# Commands that enclose full texts, that can contain multiple paragraphs 700# and contain maths 701foreach my $math_brace_command ('math') { 702 $context_brace_commands{$math_brace_command} = $math_brace_command; 703 $brace_commands{$math_brace_command} = 'context'; 704 $math_commands{$math_brace_command} = 1; 705} 706 707our %explained_commands; 708foreach my $explained_command ('abbr', 'acronym') { 709 $explained_commands{$explained_command} = 1; 710 $brace_commands{$explained_command} = 2; 711} 712 713 714our %inline_format_commands; 715our %inline_commands; 716foreach my $inline_format_command ('inlineraw', 'inlinefmt', 717 'inlinefmtifelse') { 718 $inline_format_commands{$inline_format_command} = 1; 719 $brace_commands{$inline_format_command} = 2; 720 $inline_commands{$inline_format_command} = 1; 721} 722 723$brace_commands{'inlinefmtifelse'} = 3; 724 725our %inline_conditional_commands; 726foreach my $inline_conditional_command ('inlineifclear', 'inlineifset') { 727 $inline_conditional_commands{$inline_conditional_command} = 1; 728 $brace_commands{$inline_conditional_command} = 2; 729 $inline_commands{$inline_conditional_command} = 1; 730} 731 732foreach my $two_arg_command('email') { 733 $brace_commands{$two_arg_command} = 2; 734} 735 736foreach my $three_arg_command('uref','url','inforef') { 737 $brace_commands{$three_arg_command} = 3; 738} 739 740foreach my $five_arg_command('xref','ref','pxref','image') { 741 $brace_commands{$five_arg_command} = 5; 742} 743 744 745# some classification to help converters 746our %ref_commands; 747foreach my $ref_command ('xref','ref','pxref','inforef') { 748 $ref_commands{$ref_command} = 1; 749} 750 751 752# brace command that is not replaced with text. 753my %unformatted_brace_commands; 754foreach my $unformatted_brace_command ('anchor', 'shortcaption', 755 'caption', 'hyphenation', 'errormsg') { 756 $unformatted_brace_commands{$unformatted_brace_command} = 1; 757} 758 759 760# commands delimiting blocks, with an @end. 761# Value is either the number of arguments on the line separated by 762# commas or the type of command, 'raw', 'def', 'conditional', 763# or 'multitable'. 764our %block_commands; 765 766# commands that have a possible content before an item 767our %block_item_commands; 768 769sub gdt($) 770{ 771 return $_[0]; 772} 773 774our %def_map = ( 775 # basic commands. 776 # 'arg' and 'argtype' are for everything appearing after the other 777 # arguments. 778 'deffn', [ 'category', 'name', 'arg' ], 779 'defvr', [ 'category', 'name' ], 780 'deftypefn', [ 'category', 'type', 'name', 'argtype' ], 781 'deftypeop', [ 'category', 'class' , 'type', 'name', 'argtype' ], 782 'deftypevr', [ 'category', 'type', 'name' ], 783 'defcv', [ 'category', 'class' , 'name' ], 784 'deftypecv', [ 'category', 'class' , 'type', 'name' ], 785 'defop', [ 'category', 'class' , 'name', 'arg' ], 786 'deftp', [ 'category', 'name', 'argtype' ], 787 # shortcuts 788 'defun', {'deffn' => gdt('Function')}, 789 'defmac', {'deffn' => gdt('Macro')}, 790 'defspec', {'deffn' => gdt('Special Form')}, 791 'defvar', {'defvr' => gdt('Variable')}, 792 'defopt', {'defvr' => gdt('User Option')}, 793 'deftypefun', {'deftypefn' => gdt('Function')}, 794 'deftypevar', {'deftypevr' => gdt('Variable')}, 795 'defivar', {'defcv' => gdt('Instance Variable')}, 796 'deftypeivar', {'deftypecv' => gdt('Instance Variable')}, 797 'defmethod', {'defop' => gdt('Method')}, 798 'deftypemethod', {'deftypeop' => gdt('Method')}, 799); 800 801# the type of index, fn: function, vr: variable, tp: type 802my %index_type_def = ( 803 'fn' => ['deffn', 'deftypefn', 'deftypeop', 'defop'], 804 'vr' => ['defvr', 'deftypevr', 'defcv', 'deftypecv' ], 805 'tp' => ['deftp'] 806); 807 808# Keys are commmands, values are names of indices. 809our %command_index; 810 811$command_index{'vtable'} = 'vr'; 812$command_index{'ftable'} = 'fn'; 813 814foreach my $index_type (keys %index_type_def) { 815 foreach my $def (@{$index_type_def{$index_type}}) { 816 $command_index{$def} = $index_type; 817 } 818} 819 820our %def_commands; 821our %def_aliases; 822foreach my $def_command(keys %def_map) { 823 if (ref($def_map{$def_command}) eq 'HASH') { 824 my ($real_command) = keys (%{$def_map{$def_command}}); 825 $command_index{$def_command} = $command_index{$real_command}; 826 $def_aliases{$def_command} = $real_command; 827 } 828 $block_commands{$def_command} = 'def'; 829 $line_commands{$def_command.'x'} = 'line'; 830 $def_commands{$def_command} = 1; 831 $def_commands{$def_command.'x'} = 1; 832 $command_index{$def_command.'x'} = $command_index{$def_command}; 833} 834 835#print STDERR "".Data::Dumper->Dump([\%def_aliases]); 836#print STDERR "".Data::Dumper->Dump([\%def_prepended_content]); 837 838$block_commands{'multitable'} = 'multitable'; 839$block_item_commands{'multitable'} = 1; 840 841# block commands in which menu entry and menu comments appear 842our %menu_commands; 843foreach my $menu_command ('menu', 'detailmenu', 'direntry') { 844 $menu_commands{$menu_command} = 1; 845 $block_commands{$menu_command} = 0; 846}; 847 848our %align_commands; 849foreach my $align_command('raggedright', 'flushleft', 'flushright') { 850 $block_commands{$align_command} = 0; 851 $align_commands{$align_command} = 1; 852} 853$align_commands{'center'} = 1; 854 855foreach my $block_command( 856 'cartouche', 'group', 'indentedblock', 'smallindentedblock') { 857 $block_commands{$block_command} = 0; 858} 859 860our %region_commands; 861foreach my $block_command('titlepage', 'copying', 'documentdescription') { 862 $block_commands{$block_command} = 0; 863 $region_commands{$block_command} = 1; 864} 865 866our %preformatted_commands; 867our %preformatted_code_commands; 868foreach my $preformatted_command( 869 'example', 'smallexample', 'lisp', 'smalllisp') { 870 $block_commands{$preformatted_command} = 0; 871 $preformatted_commands{$preformatted_command} = 1; 872 $preformatted_code_commands{$preformatted_command} = 1; 873} 874$block_commands{'example'} = 'variadic'; # unlimited arguments 875 876foreach my $preformatted_command( 877 'display', 'smalldisplay', 'format', 'smallformat') { 878 $block_commands{$preformatted_command} = 0; 879 $preformatted_commands{$preformatted_command} = 1; 880} 881 882foreach my $block_math_command('displaymath') { 883 $block_commands{$block_math_command} = 0; 884 $math_commands{$block_math_command} = 1; 885} 886 887our %format_raw_commands; 888foreach my $format_raw_command('html', 'tex', 'xml', 'docbook') { 889 $block_commands{$format_raw_command} = 0; 890 $format_raw_commands{$format_raw_command} = 1; 891} 892 893our %raw_commands; 894# macro/rmacro are special 895foreach my $raw_command ('verbatim', 896 'ignore', 'macro', 'rmacro') { 897 $block_commands{$raw_command} = 'raw'; 898 $raw_commands{$raw_command} = 1; 899} 900 901our %texinfo_output_formats; 902foreach my $command (keys(%format_raw_commands), 'info', 'plaintext') { 903 $block_commands{'if' . $command} = 'conditional'; 904 $block_commands{'ifnot' . $command} = 'conditional'; 905 $texinfo_output_formats{$command} = $command; 906} 907 908$block_commands{'ifset'} = 'conditional'; 909$block_commands{'ifclear'} = 'conditional'; 910 911$block_commands{'ifcommanddefined'} = 'conditional'; 912$block_commands{'ifcommandnotdefined'} = 'conditional'; 913 914# 'macro' ? 915foreach my $block_command_one_arg('table', 'ftable', 'vtable', 916 'itemize', 'enumerate', 'quotation', 'smallquotation') { 917 $block_commands{$block_command_one_arg} = 1; 918 $block_item_commands{$block_command_one_arg} = 1 919 unless ($block_command_one_arg =~ /quotation/); 920} 921 922$block_commands{'float'} = 2; 923 924# commands that forces closing an opened paragraph. 925our %close_paragraph_commands; 926 927foreach my $block_command (keys(%block_commands)) { 928 $close_paragraph_commands{$block_command} = 1 929 unless ($block_commands{$block_command} eq 'raw' or 930 $block_commands{$block_command} eq 'conditional' 931 or $format_raw_commands{$block_command}); 932} 933 934$close_paragraph_commands{'verbatim'} = 1; 935 936foreach my $close_paragraph_command ('titlefont', 'insertcopying', 'sp', 937 'verbatiminclude', 'page', 'item', 'itemx', 'tab', 'headitem', 938 'printindex', 'listoffloats', 'center', 'dircategory', 'contents', 939 'shortcontents', 'summarycontents', 'caption', 'shortcaption', 940 'setfilename', 'exdent') { 941 $close_paragraph_commands{$close_paragraph_command} = 1; 942} 943 944foreach my $close_paragraph_command (keys(%def_commands)) { 945 $close_paragraph_commands{$close_paragraph_command} = 1; 946} 947 948our %item_container_commands; 949foreach my $item_container_command ('itemize', 'enumerate') { 950 $item_container_commands{$item_container_command} = 1; 951} 952our %item_line_commands; 953foreach my $item_line_command ('table', 'ftable', 'vtable') { 954 $item_line_commands{$item_line_command} = 1; 955} 956 957our %deprecated_commands = ( 958 'definfoenclose' => '', 959 'refill' => '', 960 'inforef' => '', 961 'centerchap' => '', 962); 963 964my %unformatted_block_commands; 965foreach my $unformatted_block_command ('ignore', 'macro', 'rmacro') { 966 $unformatted_block_commands{$unformatted_block_command} = 1; 967} 968 969 970# commands that should only appear at the root level and contain up to 971# the next root command. @node and sectioning commands. 972our %root_commands; 973 974our %command_structuring_level = ( 975 'top', 0, 976 'chapter', 1, 977 'unnumbered', 1, 978 'chapheading', 1, 979 'appendix', 1, 980 'section', 2, 981 'unnumberedsec', 2, 982 'heading', 2, 983 'appendixsec', 2, 984 'subsection', 3, 985 'unnumberedsubsec', 3, 986 'subheading', 3, 987 'appendixsubsec', 3, 988 'subsubsection', 4, 989 'unnumberedsubsubsec', 4, 990 'subsubheading', 4, 991 'appendixsubsubsec', 4, 992 ); 993 994our %level_to_structuring_command; 995 996{ 997 my $sections = [ ]; 998 my $appendices = [ ]; 999 my $unnumbered = [ ]; 1000 my $headings = [ ]; 1001 foreach my $command (keys (%command_structuring_level)) { 1002 if ($command =~ /^appendix/) { 1003 $level_to_structuring_command{$command} = $appendices; 1004 } elsif ($command =~ /^unnumbered/ or $command eq 'top') { 1005 $level_to_structuring_command{$command} = $unnumbered; 1006 } elsif ($command =~ /section$/ or $command eq 'chapter') { 1007 $level_to_structuring_command{$command} = $sections; 1008 } else { 1009 $level_to_structuring_command{$command} = $headings; 1010 } 1011 $level_to_structuring_command{$command}->[$command_structuring_level{$command}] 1012 = $command; 1013 } 1014 $level_to_structuring_command{'appendixsection'} = $appendices; 1015 $level_to_structuring_command{'majorheading'} = $headings; 1016 $level_to_structuring_command{'centerchap'} = $unnumbered; 1017} 1018 1019 1020# out of the main hierarchy 1021$command_structuring_level{'part'} = 0; 1022# this are synonyms 1023$command_structuring_level{'appendixsection'} = 2; 1024# command_structuring_level{'majorheading'} is also 1 and not 0 1025$command_structuring_level{'majorheading'} = 1; 1026$command_structuring_level{'centerchap'} = 1; 1027 1028our %sectioning_commands; 1029 1030foreach my $sectioning_command (keys (%command_structuring_level)) { 1031 $line_commands{$sectioning_command} = 'line'; 1032 if ($sectioning_command =~ /heading/) { 1033 $close_paragraph_commands{$sectioning_command} = 1; 1034 } else { 1035 $root_commands{$sectioning_command} = 1; 1036 } 1037 $sectioning_commands{$sectioning_command} = 1; 1038} 1039 1040 1041# misc commands that may be formatted as text. 1042# index commands may be too, but index command may be added with 1043# @def*index so they are not added here. 1044my %formatted_misc_commands; 1045foreach my $formatted_misc_command ('insertcopying', 'contents', 1046 'shortcontents', 'summarycontents', 'center', 'printindex', 1047 'listoffloats', 'shorttitlepage', 'settitle', 1048 'author', 'subtitle', 'title', 'sp', 'exdent', 'headitem', 'item', 1049 'itemx', 'tab', 'node', keys(%sectioning_commands)) { 1050 $formatted_misc_commands{$formatted_misc_command} = 1; 1051} 1052 1053 1054our %misc_commands = (%line_commands, %other_commands); 1055 1056$root_commands{'node'} = 1; 1057 1058our %all_commands; 1059foreach my $command ( 1060 keys(%Texinfo::Common::block_commands), 1061 keys(%Texinfo::Common::brace_commands), 1062 keys(%Texinfo::Common::misc_commands), 1063 keys(%Texinfo::Common::no_brace_commands), 1064 qw(value), 1065 ) { 1066 $all_commands{$command} = 1; 1067} 1068 1069our @MONTH_NAMES = 1070 ( 1071 'January', 'February', 'March', 'April', 'May', 1072 'June', 'July', 'August', 'September', 'October', 1073 'November', 'December' 1074 ); 1075 1076# file: file name to locate. It can be a file path. 1077# directories: a reference on a array containing a list of directories to 1078# search the file in. 1079# all_files: if true collect all the files with that name, otherwise stop 1080# at first match. 1081sub locate_init_file($$$) 1082{ 1083 my $file = shift; 1084 my $directories = shift; 1085 my $all_files = shift; 1086 1087 if (File::Spec->file_name_is_absolute($file)) { 1088 return $file if (-e $file and -r $file); 1089 } else { 1090 my @files; 1091 foreach my $dir (@$directories) { 1092 next unless (-d $dir); 1093 my $possible_file = File::Spec->catfile($dir, $file); 1094 if ($all_files) { 1095 push (@files, $possible_file) 1096 if (-e $possible_file and -r $possible_file); 1097 } else { 1098 return $possible_file if (-e $possible_file and -r $possible_file); 1099 } 1100 } 1101 return @files if ($all_files); 1102 } 1103 return undef; 1104} 1105 1106sub locate_include_file($$) 1107{ 1108 my $self = shift; 1109 my $text = shift; 1110 my $file; 1111 1112 my $ignore_include_directories = 0; 1113 1114 my ($volume, $directories, $filename) = File::Spec->splitpath($text); 1115 my @directories = File::Spec->splitdir($directories); 1116 1117 #print STDERR "$self $text @{$self->{'include_directories'}}\n"; 1118 # If the path is absolute or begins with . or .., do not search in 1119 # include directories. 1120 if (File::Spec->file_name_is_absolute($text)) { 1121 $ignore_include_directories = 1; 1122 } else { 1123 foreach my $dir (@directories) { 1124 if ($dir eq File::Spec->updir() or $dir eq File::Spec->curdir()) { 1125 $ignore_include_directories = 1; 1126 last; 1127 } elsif ($dir ne '') { 1128 last; 1129 } 1130 } 1131 } 1132 1133 #if ($text =~ m,^(/|\./|\.\./),) { 1134 if ($ignore_include_directories) { 1135 $file = $text if (-e $text and -r $text); 1136 } else { 1137 my @dirs; 1138 if ($self) { 1139 @dirs = @{$self->{'include_directories'}}; 1140 } else { 1141 # no object with directory list and not an absolute path, never succeed 1142 return undef; 1143 } 1144 foreach my $include_dir (@{$self->{'include_directories'}}) { 1145 my ($include_volume, $include_directories, $include_filename) 1146 = File::Spec->splitpath($include_dir, 1); 1147 1148 my $possible_file = File::Spec->catpath($include_volume, 1149 File::Spec->catdir(File::Spec->splitdir($include_directories), 1150 @directories), $filename); 1151 #$file = "$include_dir/$text" if (-e "$include_dir/$text" and -r "$include_dir/$text"); 1152 $file = "$possible_file" if (-e "$possible_file" and -r "$possible_file"); 1153 last if (defined($file)); 1154 } 1155 } 1156 return $file; 1157} 1158 1159sub open_out($$;$$) 1160{ 1161 my $self = shift; 1162 my $file = shift; 1163 my $encoding = shift; 1164 my $use_binmode = shift; 1165 1166 if (!defined($encoding) and $self 1167 and defined($self->get_conf('OUTPUT_PERL_ENCODING'))) { 1168 $encoding = $self->get_conf('OUTPUT_PERL_ENCODING'); 1169 } 1170 1171 if ($file eq '-') { 1172 binmode(STDOUT) if $use_binmode; 1173 binmode(STDOUT, ":encoding($encoding)") if ($encoding); 1174 if ($self) { 1175 $self->{'unclosed_files'}->{$file} = \*STDOUT; 1176 } 1177 return \*STDOUT; 1178 } 1179 my $filehandle = do { local *FH }; 1180 if (!open ($filehandle, '>', $file)) { 1181 return undef; 1182 } 1183 # We run binmode to turn off outputting LF as CR LF under MS-Windows, 1184 # so that Info tag tables will have correct offsets. This must be done 1185 # before setting the encoding filters with binmode. 1186 binmode($filehandle) if $use_binmode; 1187 if ($encoding) { 1188 if ($encoding eq 'utf8' 1189 or $encoding eq 'utf-8' 1190 or $encoding eq 'utf-8-strict') { 1191 binmode($filehandle, ':utf8'); 1192 } else { # FIXME also right for shiftijs or similar encodings? 1193 binmode($filehandle, ':bytes'); 1194 } 1195 binmode($filehandle, ":encoding($encoding)"); 1196 } 1197 if ($self) { 1198 push @{$self->{'opened_files'}}, $file; 1199 $self->{'unclosed_files'}->{$file} = $filehandle; 1200 } 1201 return $filehandle; 1202} 1203 1204sub warn_unknown_language($) { 1205 my $lang = shift; 1206 1207 my @messages = (); 1208 my $lang_code = $lang; 1209 my $region_code; 1210 1211 if ($lang =~ /^([a-z]+)_([A-Z]+)/) { 1212 $lang_code = $1; 1213 $region_code = $2; 1214 } 1215 1216 if (! $Texinfo::Documentlanguages::language_codes{$lang_code}) { 1217 push @messages, sprintf(__("%s is not a valid language code"), 1218 $lang_code); 1219 } 1220 if (defined($region_code) 1221 and ! $Texinfo::Documentlanguages::region_codes{$region_code}) { 1222 push @messages, sprintf(__("%s is not a valid region code"), 1223 $region_code); 1224 } 1225 return @messages; 1226} 1227 1228my %possible_split = ( 1229 'chapter' => 1, 1230 'section' => 1, 1231 'node' => 1, 1232); 1233 1234sub warn_unknown_split($) { 1235 my $split = shift; 1236 1237 my @messages = (); 1238 if ($split and !$possible_split{$split}) { 1239 push @messages, sprintf(__("%s is not a valid split possibility"), $split); 1240 } 1241 return @messages; 1242} 1243 1244# This should do the job, or at least don't do wrong if $self 1245# is not defined, as could be the case if called from 1246# Texinfo::Convert::Text. 1247sub expand_verbatiminclude($$) 1248{ 1249 my $self = shift; 1250 my $current = shift; 1251 1252 return unless ($current->{'extra'} and defined($current->{'extra'}->{'text_arg'})); 1253 my $text = $current->{'extra'}->{'text_arg'}; 1254 my $file = locate_include_file($self, $text); 1255 1256 my $verbatiminclude; 1257 1258 if (defined($file)) { 1259 if (!open(VERBINCLUDE, $file)) { 1260 if ($self) { 1261 $self->line_error(sprintf(__("could not read %s: %s"), $file, $!), 1262 $current->{'line_nr'}); 1263 } 1264 } else { 1265 if (defined $current->{'extra'}->{'input_perl_encoding'}) { 1266 binmode(VERBINCLUDE, ":encoding(" 1267 . $current->{'extra'}->{'input_perl_encoding'} 1268 . ")"); 1269 } 1270 $verbatiminclude = { 'cmdname' => 'verbatim', 1271 'parent' => $current->{'parent'}, 1272 'extra' => 1273 {'text_arg' => $current->{'extra'}->{'text_arg'}} }; 1274 while (<VERBINCLUDE>) { 1275 push @{$verbatiminclude->{'contents'}}, 1276 {'type' => 'raw', 'text' => $_ }; 1277 } 1278 if (!close (VERBINCLUDE)) { 1279 if ($self) { 1280 $self->document_warn(sprintf(__( 1281 "error on closing \@verbatiminclude file %s: %s"), 1282 $file, $!)); 1283 } 1284 } 1285 } 1286 } elsif ($self) { 1287 $self->line_error(sprintf(__("\@%s: could not find %s"), 1288 $current->{'cmdname'}, $text), $current->{'line_nr'}); 1289 } 1290 return $verbatiminclude; 1291} 1292 1293sub definition_category($$) 1294{ 1295 my $self = shift; 1296 my $current = shift; 1297 1298 return undef if (!$current->{'extra'} 1299 or !$current->{'extra'}->{'def_parsed_hash'}); 1300 1301 my $arg_category = $current->{'extra'}->{'def_parsed_hash'}->{'category'}; 1302 my $arg_class = $current->{'extra'}->{'def_parsed_hash'}->{'class'}; 1303 1304 return $arg_category 1305 if (!defined($arg_class)); 1306 1307 my $style = $command_index{$current->{'extra'}->{'def_command'}}; 1308 if ($style eq 'fn') { 1309 if ($self) { 1310 return $self->gdt('{category} on {class}', { 'category' => $arg_category, 1311 'class' => $arg_class }); 1312 } else { 1313 return {'contents' => [$arg_category, {'text' => ' on '}, $arg_class]}; 1314 } 1315 } elsif ($style eq 'vr') { 1316 if ($self) { 1317 return $self->gdt('{category} of {class}', { 'category' => $arg_category, 1318 'class' => $arg_class }); 1319 } else { 1320 return {'contents' => [$arg_category, {'text' => ' of '}, $arg_class]}; 1321 } 1322 } 1323} 1324 1325sub expand_today($) 1326{ 1327 my $self = shift; 1328 if ($self->get_conf('TEST')) { 1329 return {'text' => 'a sunny day'}; 1330 } 1331 1332 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) 1333 = ($ENV{SOURCE_DATE_EPOCH} 1334 ? gmtime($ENV{SOURCE_DATE_EPOCH}) 1335 : localtime(time)); 1336 # See https://reproducible-builds.org/specs/source-date-epoch/. 1337 1338 $year += ($year < 70) ? 2000 : 1900; 1339 return $self->gdt('{month} {day}, {year}', 1340 { 'month' => $self->gdt($MONTH_NAMES[$mon]), 1341 'day' => $mday, 'year' => $year }); 1342} 1343 1344sub translated_command_tree($$) 1345{ 1346 my $self = shift; 1347 my $cmdname = shift; 1348 if ($self->{'translated_commands'}->{$cmdname}) { 1349 return $self->gdt($self->{'translated_commands'}->{$cmdname}); 1350 } 1351 return undef; 1352} 1353 1354sub numbered_heading($$$;$) 1355{ 1356 my $self = shift; 1357 my $current = shift; 1358 my $text = shift; 1359 my $numbered = shift; 1360 1361 my $number; 1362 if (defined($current->{'number'}) and ($numbered or !defined($numbered))) { 1363 $number = $current->{'number'}; 1364 } 1365 1366 my $result; 1367 if ($self) { 1368 if (defined($number)) { 1369 if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) { 1370 $result = $self->gdt('Appendix {number} {section_title}', 1371 {'number' => $number, 'section_title' => $text}, 1372 'translated_text'); 1373 } else { 1374 $result = $self->gdt('{number} {section_title}', 1375 {'number' => $number, 'section_title' => $text}, 1376 'translated_text'); 1377 } 1378 } else { 1379 $result = $text; 1380 } 1381 } else { 1382 $result = $text; 1383 $result = $number.' '.$result if (defined($number)); 1384 if ($current->{'cmdname'} eq 'appendix' and $current->{'level'} == 1) { 1385 $result = 'Appendix '.$result; 1386 } 1387 } 1388 chomp ($result); 1389 return $result; 1390} 1391 1392sub definition_arguments_content($) 1393{ 1394 my $root = shift; 1395 my $result; 1396 1397 return undef if (!defined($root->{'extra'}) 1398 or !defined($root->{'extra'}->{'def_parsed_hash'})); 1399 my @args = @{$root->{'args'}->[0]->{'contents'}}; 1400 while (@args) { 1401 last if (defined($args[0]->{'extra'}) 1402 and defined($args[0]->{'extra'}->{'def_role'}) 1403 and $args[0]->{'extra'}->{'def_role'} ne 'spaces' 1404 and !$root->{'extra'}->{'def_parsed_hash'} 1405 ->{$args[0]->{'extra'}->{'def_role'}}); 1406 shift @args; 1407 } 1408 if (scalar(@args) > 0) { 1409 return \@args; 1410 } else { 1411 return undef; 1412 } 1413} 1414 1415# find the accent commands stack and the innermost text contents 1416sub find_innermost_accent_contents($;$) 1417{ 1418 my $current = shift; 1419 my $encoding = shift; 1420 my @accent_commands = (); 1421 my $debug = 0; 1422 ACCENT: 1423 while (1) { 1424 # the following can happen if called with a bad tree 1425 if (!$current->{'cmdname'} 1426 or !$accent_commands{$current->{'cmdname'}}) { 1427 #print STDERR "BUG: Not an accent command in accent\n"; 1428 cluck "BUG: Not an accent command in accent\n"; 1429 #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n"; 1430 #print STDERR Data::Dumper->Dump([$current]); 1431 last; 1432 } 1433 push @accent_commands, $current; 1434 # A bogus accent, that may happen 1435 if (!$current->{'args'}) { 1436 return ([], \@accent_commands); 1437 } 1438 my $arg = $current->{'args'}->[0]; 1439 if (!$arg->{'contents'}) { 1440 print STDERR "BUG: No content in accent command\n"; 1441 #print STDERR Data::Dumper->Dump([$current]); 1442 #print STDERR Texinfo::Convert::Texinfo::convert($current)."\n"; 1443 return ([], \@accent_commands); 1444 } 1445 # inside the argument of an accent 1446 my $text_contents = []; 1447 foreach my $content (@{$arg->{'contents'}}) { 1448 if (!($content->{'cmdname'} and ($content->{'cmdname'} eq 'c' 1449 or $content->{'cmdname'} eq 'comment'))) { 1450 if ($content->{'cmdname'} and $accent_commands{$content->{'cmdname'}}) { 1451 $current = $content; 1452 next ACCENT; 1453 } else { 1454 push @$text_contents, $content; 1455 } 1456 } 1457 } 1458 # we go here if there was no nested accent 1459 return ($text_contents, \@accent_commands); 1460 } 1461} 1462 1463sub trim_spaces_comment_from_content($) 1464{ 1465 my $contents = shift; 1466 shift @$contents 1467 if ($contents->[0] and $contents->[0]->{'type'} 1468 and ($contents->[0]->{'type'} eq 'empty_line_after_command' 1469 or $contents->[0]->{'type'} eq 'empty_spaces_after_command' 1470 or $contents->[0]->{'type'} eq 'empty_spaces_before_argument' 1471 or $contents->[0]->{'type'} eq 'empty_spaces_after_close_brace')); 1472 1473 while (@$contents 1474 and (($contents->[-1]->{'cmdname'} 1475 and ($contents->[-1]->{'cmdname'} eq 'c' 1476 or $contents->[-1]->{'cmdname'} eq 'comment')) 1477 or ($contents->[-1]->{'type'} 1478 and ($contents->[-1]->{'type'} eq 'spaces_at_end' 1479 or $contents->[-1]->{'type'} eq 'space_at_end_block_command')))) { 1480 pop @$contents; 1481 } 1482} 1483 1484sub _find_end_brace($$) 1485{ 1486 my $text = shift; 1487 my $braces_count = shift; 1488 1489 my $before = ''; 1490 while ($braces_count > 0 and length($text)) { 1491 if ($text =~ s/([^()]*)([()])//) { 1492 $before .= $1.$2; 1493 my $brace = $2; 1494 if ($brace eq '(') { 1495 $braces_count++; 1496 } else { 1497 $braces_count--; 1498 if ($braces_count == 0) { 1499 return ($before, $text, 0); 1500 } 1501 } 1502 } else { 1503 $before .= $text; 1504 $text = ''; 1505 } 1506 } 1507 return ($before, undef, $braces_count); 1508} 1509 1510# This only counts opening braces, and returns 0 once all the parentheses 1511# are closed 1512sub _count_opened_tree_braces($$); 1513sub _count_opened_tree_braces($$) 1514{ 1515 my $current = shift; 1516 my $braces_count = shift; 1517 if (defined($current->{'text'})) { 1518 my ($before, $after); 1519 ($before, $after, $braces_count) = _find_end_brace($current->{'text'}, 1520 $braces_count); 1521 } 1522 return $braces_count; 1523} 1524 1525# $NODE->{'contents'} is the Texinfo for the specification of a node. 1526# Returned object is a hash with two fields: 1527# 1528# manual_content - Texinfo tree for a manual name extracted from the 1529# node specification. 1530# node_content - Texinfo tree for the node name on its own 1531# 1532# retrieve a leading manual name in parentheses, if there is one. 1533sub parse_node_manual($) 1534{ 1535 my $node = shift; 1536 my @contents = @{$node->{'contents'}}; 1537 1538 my $manual; 1539 my $result; 1540 my ($end_paren, $spaces_after); 1541 1542 if ($contents[0] and $contents[0]->{'text'} and $contents[0]->{'text'} =~ /^\(/) { 1543 my $braces_count = 1; 1544 if ($contents[0]->{'text'} !~ /^\($/) { 1545 my $brace = shift @contents; 1546 my $brace_text = $brace->{'text'}; 1547 $brace_text =~ s/^\(//; 1548 unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'}, 1549 'parent' => $brace->{'parent'} } if $brace_text ne ''; 1550 } else { 1551 shift @contents; 1552 } 1553 while(@contents) { 1554 my $content = shift @contents; 1555 if (!defined($content->{'text'}) or $content->{'text'} !~ /\)/) { 1556 push @$manual, $content; 1557 $braces_count = _count_opened_tree_braces($content, $braces_count); 1558 # This is an error, braces were closed in a command 1559 if ($braces_count == 0) { 1560 last; 1561 } 1562 } else { 1563 my ($before, $after); 1564 ($before, $after, $braces_count) = _find_end_brace($content->{'text'}, 1565 $braces_count); 1566 if ($braces_count == 0) { 1567 $before =~ s/(\))$//; 1568 $end_paren = $1; 1569 push @$manual, { 'text' => $before, 'parent' => $content->{'parent'} } 1570 if ($before ne ''); 1571 $after =~ s/^(\s*)//; 1572 $spaces_after = $1; 1573 unshift @contents, { 'text' => $after, 'parent' => $content->{'parent'} } 1574 if ($after ne ''); 1575 last; 1576 } else { 1577 push @$manual, $content; 1578 } 1579 } 1580 } 1581 if ($braces_count == 0) { 1582 $result->{'manual_content'} = $manual if (defined($manual)); 1583 } else { 1584 @contents = ({ 'text' => '(', 'parent' => $node }, @$manual); 1585 } 1586 } 1587 if (@contents) { 1588 $result->{'node_content'} = \@contents; 1589 } 1590 1591 # Overwrite the contents array so that all the elements in 'manual_content' 1592 # and 'node_content' are in the main tree. 1593 my $new_contents = []; 1594 if (defined($result) and defined($result->{'manual_content'})) { 1595 @$new_contents = ({ 'text' => '(', 'parent' => $node }, 1596 @$manual); 1597 push @$new_contents, { 'text' => ')', 'parent' => $node } 1598 if $end_paren; 1599 push @$new_contents, { 'text' => $spaces_after, 'parent' => $node } 1600 if $spaces_after; 1601 } 1602 if (@contents) { 1603 @$new_contents = (@$new_contents, @contents); 1604 } 1605 $node->{'contents'} = $new_contents; 1606 1607 return $result; 1608} 1609 1610sub float_name_caption($$) 1611{ 1612 my $self = shift; 1613 my $root = shift; 1614 1615 my $caption; 1616 if ($root->{'extra'}->{'caption'}) { 1617 $caption = $root->{'extra'}->{'caption'}; 1618 } elsif ($root->{'extra'}->{'shortcaption'}) { 1619 $caption = $root->{'extra'}->{'shortcaption'}; 1620 } 1621 #if ($self->get_conf('DEBUG')) { 1622 # my $caption_texi = 1623 # Texinfo::Convert::Texinfo::convert({ 'contents' => $caption->{'contents'}}); 1624 # print STDERR " CAPTION: $caption_texi\n"; 1625 #} 1626 my $type; 1627 if ($root->{'extra'}->{'type'}->{'normalized'} ne '') { 1628 $type = {'contents' => $root->{'extra'}->{'type'}->{'content'}}; 1629 } 1630 1631 my $prepended; 1632 if ($type) { 1633 if ($caption) { 1634 if (defined($root->{'number'})) { 1635 $prepended = $self->gdt('{float_type} {float_number}: ', 1636 {'float_type' => $type, 1637 'float_number' => $root->{'number'}}); 1638 } else { 1639 $prepended = $self->gdt('{float_type}: ', 1640 {'float_type' => $type}); 1641 } 1642 } else { 1643 if (defined($root->{'number'})) { 1644 $prepended = $self->gdt("{float_type} {float_number}\n", 1645 {'float_type' => $type, 1646 'float_number' => $root->{'number'}}); 1647 } else { 1648 $prepended = $self->gdt("{float_type}\n", 1649 {'float_type' => $type}); 1650 } 1651 } 1652 } elsif (defined($root->{'number'})) { 1653 if ($caption) { 1654 $prepended = $self->gdt('{float_number}: ', 1655 {'float_number' => $root->{'number'}}); 1656 } else { 1657 $prepended = $self->gdt("{float_number}\n", 1658 {'float_number' => $root->{'number'}}); 1659 } 1660 } 1661 return ($caption, $prepended); 1662} 1663 1664# decompose a decimal number on a given base. 1665sub _decompose_integer($$) 1666{ 1667 my $number = shift; 1668 my $base = shift; 1669 my @result = (); 1670 1671 while ($number >= 0) { 1672 my $factor = $number % $base; 1673 push (@result, $factor); 1674 $number = int(($number - $factor) / $base) - 1; 1675 } 1676 return @result; 1677} 1678 1679sub enumerate_item_representation($$) 1680{ 1681 my $specification = shift; 1682 my $number = shift; 1683 1684 if ($specification =~ /^[0-9]+$/) { 1685 return $specification + $number -1; 1686 } 1687 1688 my $result = ''; 1689 my $base_letter = ord('a'); 1690 $base_letter = ord('A') if (ucfirst($specification) eq $specification); 1691 my @letter_ords = _decompose_integer(ord($specification) - $base_letter + $number - 1, 26); 1692 foreach my $ord (@letter_ords) { 1693 $result = chr($base_letter + $ord) . $result; 1694 } 1695 return $result; 1696} 1697 1698sub is_content_empty($;$); 1699sub is_content_empty($;$) 1700{ 1701 my $tree = shift; 1702 my $do_not_ignore_index_entries = shift; 1703 if (!defined($tree) or !exists($tree->{'contents'})) { 1704 return 1; 1705 } 1706 foreach my $content (@{$tree->{'contents'}}) { 1707 #print STDERR _print_current($content); 1708 if ($content->{'cmdname'}) { 1709 if ($content->{'type'} and $content->{'type'} eq 'index_entry_command') { 1710 if ($do_not_ignore_index_entries) { 1711 return 0; 1712 } else { 1713 next; 1714 } 1715 } 1716 if (exists($misc_commands{$content->{'cmdname'}})) { 1717 my @truc = keys(%formatted_misc_commands); 1718 if ($formatted_misc_commands{$content->{'cmdname'}}) { 1719 return 0; 1720 } else { 1721 next; 1722 } 1723 } elsif ($unformatted_brace_commands{$content->{'cmdname'}} 1724 or $unformatted_block_commands{$content->{'cmdname'}}) { 1725 next; 1726 } else { 1727 return 0; 1728 } 1729 } 1730 if ($content->{'type'}) { 1731 if ($content->{'type'} eq 'paragraph') { 1732 return 0; 1733 } 1734 } 1735 if ($content->{'text'} and $content->{'text'} =~ /\S/) { 1736 return 0; 1737 } 1738 if (not is_content_empty($content, $do_not_ignore_index_entries)) { 1739 return 0; 1740 } 1741 } 1742 return 1; 1743} 1744sub normalize_top_node_name($) 1745{ 1746 my $node = shift; 1747 if ($node =~ /^top$/i) { 1748 return 'Top'; 1749 } 1750 return $node; 1751} 1752 1753# Argument is a converter object 1754sub _convert_text_options($) 1755{ 1756 my $self = shift; 1757 my %options; 1758 if ($self->get_conf('ENABLE_ENCODING')) { 1759 if ($self->get_conf('OUTPUT_ENCODING_NAME')) { 1760 $options{'enabled_encoding'} = $self->get_conf('OUTPUT_ENCODING_NAME'); 1761 } 1762 } 1763 $options{'TEST'} = 1 if ($self->get_conf('TEST')); 1764 $options{'NUMBER_SECTIONS'} = $self->get_conf('NUMBER_SECTIONS'); 1765 $options{'converter'} = $self; 1766 $options{'expanded_formats_hash'} = $self->{'expanded_formats_hash'}; 1767 return %options; 1768} 1769 1770# Used in count_bytes 1771my $Encode_encoding_object; 1772my $last_encoding; 1773 1774sub count_bytes($$;$) 1775{ 1776 my $self = shift; 1777 my $string = shift; 1778 my $encoding = shift; 1779 1780 if (!defined($encoding) and $self and $self->get_conf('OUTPUT_PERL_ENCODING')) { 1781 $encoding = $self->get_conf('OUTPUT_PERL_ENCODING'); 1782 } 1783 1784 if ($encoding eq 'utf-8' 1785 or $encoding eq 'utf-8-strict') { 1786 if (Encode::is_utf8($string)) { 1787 # Get the number of bytes in the underlying storage. This may 1788 # be slightly faster than calling Encode::encode_utf8. 1789 use bytes; 1790 return length($string); 1791 1792 # Here's another way of doing it. 1793 #Encode::_utf8_off($string); 1794 #my $length = length($string); 1795 #Encode::_utf8_on($string); 1796 #return $length 1797 } else { 1798 return length(Encode::encode_utf8($string)); 1799 } 1800 } elsif ($encoding and $encoding ne 'ascii') { 1801 if (!defined($last_encoding) or $last_encoding ne $encoding) { 1802 # Look up and save encoding object for next time. This is 1803 # slightly faster than calling Encode::encode. 1804 $last_encoding = $encoding; 1805 $Encode_encoding_object = Encode::find_encoding($encoding); 1806 if (!defined($Encode_encoding_object)) { 1807 Carp::croak "Unknown encoding '$encoding'"; 1808 } 1809 } 1810 return length($Encode_encoding_object->encode($string)); 1811 } else { 1812 return length($string); 1813 #my $length = length($string); 1814 #$string =~ s/\n/\\n/g; 1815 #$string =~ s/\f/\\f/g; 1816 #print STDERR "Count($length): $string\n"; 1817 #return $length; 1818 } 1819 # FIXME is the following required for correct count of end of lines? 1820 #if ($encoding) { 1821 # return length(Encode::encode($encoding, $string)); 1822 #} else { 1823 # return length(Encode::encode('ascii', $string)); 1824 #} 1825} 1826 1827# TODO 1828# also recurse into 1829# extra->misc_args, extra->args_index 1830# extra->index_entry extra->type 1831# 1832# extra that should point to other elements: 1833# command_as_argument end_command 1834# associated_section part_associated_section associated_node associated_part 1835# @prototypes @columnfractions titlepage quotation @author command 1836# menu_entry_description menu_entry_name 1837# 1838# should point to other elements, or be copied. And some should be recursed 1839# into too. 1840# extra->type->content 1841# extra->nodes_manuals->[] 1842# extra->node_content 1843# extra->node_argument 1844# extra->explanation_contents 1845# extra->menu_entry_node 1846 1847 1848sub _copy_tree($$$); 1849sub _copy_tree($$$) 1850{ 1851 my $current = shift; 1852 my $parent = shift; 1853 my $reference_associations = shift; 1854 my $new = {}; 1855 $reference_associations->{$current} = $new; 1856 $new->{'parent'} = $parent if ($parent); 1857 foreach my $key ('type', 'cmdname', 'text') { 1858 $new->{$key} = $current->{$key} if (exists($current->{$key})); 1859 } 1860 foreach my $key ('args', 'contents') { 1861 if ($current->{$key}) { 1862 if (ref($current->{$key}) ne 'ARRAY') { 1863 my $command_or_type = ''; 1864 if ($new->{'cmdname'}) { 1865 $command_or_type = '@'.$new->{'cmdname'}; 1866 } elsif ($new->{'type'}) { 1867 $command_or_type = $new->{'type'}; 1868 } 1869 print STDERR "Not an array [$command_or_type] $key ".ref($current->{$key})."\n"; 1870 } 1871 $new->{$key} = []; 1872 $reference_associations->{$current->{$key}} = $new->{$key}; 1873 foreach my $child (@{$current->{$key}}) { 1874 push @{$new->{$key}}, _copy_tree($child, $new, $reference_associations); 1875 } 1876 } 1877 } 1878 if ($current->{'extra'}) { 1879 $new->{'extra'} = {}; 1880 foreach my $key (keys %{$current->{'extra'}}) { 1881 if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable' 1882 and $key eq 'prototypes') { 1883 $new->{'extra'}->{$key} = []; 1884 $reference_associations->{$current->{'extra'}->{$key}} = $new->{$key}; 1885 foreach my $child (@{$current->{'extra'}->{$key}}) { 1886 push @{$new->{'extra'}->{$key}}, 1887 _copy_tree($child, $new, $reference_associations); 1888 } 1889 } elsif (!ref($current->{'extra'}->{$key})) { 1890 $new->{'extra'}->{$key} = $current->{'extra'}->{$key}; 1891 } 1892 } 1893 } 1894 return $new; 1895} 1896 1897# for user-defined code 1898sub collect_commands_in_tree($$) 1899{ 1900 my $root = shift; 1901 my $commands_list = shift; 1902 1903 my $commands_hash = {}; 1904 foreach my $command_name (@$commands_list) { 1905 $commands_hash->{$command_name} = []; 1906 } 1907 _collect_commands_in_tree($root, $commands_hash); 1908 return $commands_hash; 1909} 1910 1911sub _collect_commands_in_tree($$); 1912sub _collect_commands_in_tree($$) 1913{ 1914 my $current = shift; 1915 my $commands_hash = shift; 1916 1917 if (defined($current->{'cmdname'}) 1918 and defined($commands_hash->{$current->{'cmdname'}})) { 1919 push @{$commands_hash->{$current->{'cmdname'}}}, $current; 1920 } 1921 foreach my $key ('args', 'contents') { 1922 if ($current->{$key}) { 1923 foreach my $child (@{$current->{$key}}) { 1924 _collect_commands_in_tree($child, $commands_hash); 1925 } 1926 } 1927 } 1928} 1929 1930# Not used. 1931sub _collect_references($$); 1932sub _collect_references($$) 1933{ 1934 my $current = shift; 1935 my $references = shift; 1936 foreach my $key ('args', 'contents') { 1937 if ($current->{$key}) { 1938 $references->{$current->{$key}} = $current->{$key}; 1939 foreach my $child (@{$current->{$key}}) { 1940 $references->{$child} = $child; 1941 _collect_references($child, $references); 1942 } 1943 } 1944 } 1945} 1946 1947sub _substitute_references_in_array($$$); 1948sub _substitute_references_in_array($$$) 1949{ 1950 my $array = shift; 1951 my $reference_associations = shift; 1952 my $context = shift; 1953 1954 my $result = []; 1955 my $index = 0; 1956 foreach my $item (@{$array}) { 1957 if (!ref($item)) { 1958 push @{$result}, $item; 1959 } elsif ($reference_associations->{$item}) { 1960 push @{$result}, $reference_associations->{$item}; 1961 } elsif (ref($item) eq 'ARRAY') { 1962 push @$result, 1963 _substitute_references_in_array($item, $reference_associations, 1964 "$context [$index]"); 1965 } elsif (defined($item->{'text'})) { 1966 my $new_text = _copy_tree($item, undef, $reference_associations); 1967 substitute_references($item, $new_text, $reference_associations); 1968 push @{$result}, $new_text; 1969 } else { 1970 print STDERR "Trouble with $context [$index] (".ref($item).")\n"; 1971 push @{$result}, undef; 1972 } 1973 $index++; 1974 } 1975 return $result; 1976} 1977 1978sub substitute_references($$$); 1979sub substitute_references($$$) 1980{ 1981 my $current = shift; 1982 my $new = shift; 1983 my $reference_associations = shift; 1984 1985 foreach my $key ('args', 'contents') { 1986 if ($new->{$key}) { 1987 my $index = 0; 1988 foreach my $child (@{$new->{$key}}) { 1989 substitute_references($child, $current->{$key}->[$index], 1990 $reference_associations); 1991 $index++; 1992 } 1993 } 1994 } 1995 if ($current->{'extra'}) { 1996 foreach my $key (keys %{$current->{'extra'}}) { 1997 if (ref($current->{'extra'}->{$key})) { 1998 my $command_or_type = ''; 1999 if ($new->{'cmdname'}) { 2000 $command_or_type = '@'.$new->{'cmdname'}; 2001 } elsif ($new->{'type'}) { 2002 $command_or_type = $new->{'type'}; 2003 } 2004 2005 if ($current->{'cmdname'} and $current->{'cmdname'} eq 'multitable' 2006 and $key eq 'prototypes') { 2007 my $index = 0; 2008 foreach my $child (@{$new->{'extra'}->{$key}}) { 2009 substitute_references($child, $current->{'extra'}->{$key}->[$index], 2010 $reference_associations); 2011 $index++; 2012 } 2013 } elsif ($reference_associations->{$current->{'extra'}->{$key}}) { 2014 $new->{'extra'}->{$key} 2015 = $reference_associations->{$current->{'extra'}->{$key}}; 2016 #print STDERR "Done [$command_or_type]: $key\n"; 2017 } else { 2018 if (ref($current->{'extra'}->{$key}) eq 'ARRAY') { 2019 2020 #print STDERR "Array $command_or_type -> $key\n"; 2021 $new->{'extra'}->{$key} = _substitute_references_in_array( 2022 $current->{'extra'}->{$key}, $reference_associations, 2023 "[$command_or_type]{$key}"); 2024 } else { 2025 if (($current->{'cmdname'} 2026 and ($current->{'cmdname'} eq 'listoffloats' 2027 or $current->{'cmdname'} eq 'float') 2028 and $key eq 'type') 2029 or ($key eq 'index_entry') 2030 or ($current->{'type'} 2031 and $current->{'type'} eq 'menu_entry' 2032 and $key eq 'menu_entry_node')) { 2033 foreach my $type_key (keys(%{$current->{'extra'}->{$key}})) { 2034 if (!ref($current->{'extra'}->{$key}->{$type_key})) { 2035 $new->{'extra'}->{$key}->{$type_key} 2036 = $current->{'extra'}->{$key}->{$type_key}; 2037 } elsif ($reference_associations->{$current->{'extra'}->{$key}->{$type_key}}) { 2038 $new->{'extra'}->{$key}->{$type_key} 2039 = $reference_associations->{$current->{'extra'}->{$key}->{$type_key}}; 2040 } elsif (ref($current->{'extra'}->{$key}->{$type_key}) eq 'ARRAY') { 2041 $new->{'extra'}->{$key}->{$type_key} 2042 = _substitute_references_in_array( 2043 $current->{'extra'}->{$key}->{$type_key}, 2044 $reference_associations, 2045 "[$command_or_type]{$key}{$type_key}"); 2046 } else { 2047 print STDERR "Not substituting [$command_or_type]{$key}: $type_key\n"; 2048 } 2049 } 2050 } else { 2051 print STDERR "Not substituting [$command_or_type]: $key ($current->{'extra'}->{$key})\n"; 2052 } 2053 } 2054 } 2055 } 2056 } 2057 } 2058} 2059 2060sub copy_tree($;$) 2061{ 2062 my $current = shift; 2063 my $parent = shift; 2064 my $reference_associations = {}; 2065 my $copy = _copy_tree($current, $parent, $reference_associations); 2066 substitute_references($current, $copy, $reference_associations); 2067 return $copy; 2068} 2069 2070sub modify_tree($$$;$); 2071sub modify_tree($$$;$) 2072{ 2073 my $self = shift; 2074 my $tree = shift; 2075 my $operation = shift; 2076 my $argument = shift; 2077 #print STDERR "modify_tree tree: $tree\n"; 2078 2079 if ($tree->{'args'}) { 2080 my @args = @{$tree->{'args'}}; 2081 for (my $i = 0; $i <= $#args; $i++) { 2082 my @new_args = &$operation($self, 'arg', $args[$i], $argument); 2083 modify_tree($self, $args[$i], $operation, $argument); 2084 # this puts the new args at the place of the old arg using the 2085 # offset from the end of the array 2086 splice (@{$tree->{'args'}}, $i - $#args -1, 1, @new_args); 2087 #foreach my $arg (@new_args) { 2088 # modify_tree($self, $arg, $operation); 2089 #} 2090 } 2091 } 2092 if ($tree->{'contents'}) { 2093 my @contents = @{$tree->{'contents'}}; 2094 for (my $i = 0; $i <= $#contents; $i++) { 2095 my @new_contents = &$operation($self, 'content', $contents[$i], $argument); 2096 modify_tree($self, $contents[$i], $operation, $argument); 2097 # this puts the new contents at the place of the old content using the 2098 # offset from the end of the array 2099 splice (@{$tree->{'contents'}}, $i - $#contents -1, 1, @new_contents); 2100 #foreach my $content (@new_contents) { 2101 # modify_tree($self, $content, $operation); 2102 #} 2103 } 2104 } 2105 return $tree; 2106} 2107 2108sub _protect_comma($$$) 2109{ 2110 my $self = shift; 2111 my $type = shift; 2112 my $current = shift; 2113 2114 return _protect_text($current, quotemeta(',')); 2115} 2116 2117sub protect_comma_in_tree($) 2118{ 2119 my $tree = shift; 2120 return modify_tree(undef, $tree, \&_protect_comma); 2121} 2122 2123sub _new_asis_command_with_text($$;$) 2124{ 2125 my $text = shift; 2126 my $parent = shift; 2127 my $text_type = shift; 2128 my $new_command = {'cmdname' => 'asis', 'parent' => $parent }; 2129 push @{$new_command->{'args'}}, {'type' => 'brace_command_arg', 2130 'parent' => $new_command}; 2131 push @{$new_command->{'args'}->[0]->{'contents'}}, { 2132 'text' => $text, 2133 'parent' => $new_command->{'args'}->[0]}; 2134 if (defined($text_type)) { 2135 $new_command->{'args'}->[0]->{'contents'}->[0]->{'type'} = $text_type; 2136 } 2137 return $new_command; 2138} 2139 2140sub _protect_text($$) 2141{ 2142 my $current = shift; 2143 my $to_protect = shift; 2144 2145 #print STDERR "$to_protect: $current "._print_current($current)."\n"; 2146 if (defined($current->{'text'}) and $current->{'text'} =~ /$to_protect/ 2147 and !(defined($current->{'type'}) and $current->{'type'} eq 'raw')) { 2148 my @result = (); 2149 my $remaining_text = $current->{'text'}; 2150 while ($remaining_text) { 2151 if ($remaining_text =~ s/^(.*?)(($to_protect)+)//) { 2152 if ($1 ne '') { 2153 push @result, {'text' => $1, 'parent' => $current->{'parent'}}; 2154 $result[-1]->{'type'} = $current->{'type'} 2155 if defined($current->{'type'}); 2156 } 2157 if ($to_protect eq quotemeta(',')) { 2158 for (my $i = 0; $i < length($2); $i++) { 2159 push @result, {'cmdname' => 'comma', 'parent' => $current->{'parent'}, 2160 'args' => [{'type' => 'brace_command_arg'}]}; 2161 } 2162 } else { 2163 push @result, _new_asis_command_with_text($2, $current->{'parent'}, 2164 $current->{'type'}); 2165 } 2166 } else { 2167 push @result, {'text' => $remaining_text, 'parent' => $current->{'parent'}}; 2168 $result[-1]->{'type'} = $current->{'type'} 2169 if defined($current->{'type'}); 2170 last; 2171 } 2172 } 2173 #print STDERR "Result: @result\n"; 2174 return @result; 2175 } else { 2176 #print STDERR "No change: $current\n"; 2177 return ($current); 2178 } 2179} 2180 2181sub _protect_colon($$$) 2182{ 2183 my $self = shift; 2184 my $type = shift; 2185 my $current = shift; 2186 2187 return _protect_text ($current, quotemeta(':')); 2188} 2189 2190sub protect_colon_in_tree($) 2191{ 2192 my $tree = shift; 2193 return modify_tree(undef, $tree, \&_protect_colon); 2194} 2195 2196sub _protect_node_after_label($$$) 2197{ 2198 my $self = shift; 2199 my $type = shift; 2200 my $current = shift; 2201 2202 return _protect_text ($current, '['. quotemeta(".\t,") .']'); 2203} 2204 2205sub protect_node_after_label_in_tree($) 2206{ 2207 my $tree = shift; 2208 return modify_tree(undef, $tree, \&_protect_node_after_label); 2209} 2210 2211sub _is_cpp_line($) 2212{ 2213 my $text = shift; 2214 return 1 if ($text =~ /^\s*#\s*(line)? (\d+)(( "([^"]+)")(\s+\d+)*)?\s*$/); 2215 return 0; 2216} 2217 2218sub _protect_hashchar_at_line_beginning($$$) 2219{ 2220 my $self = shift; 2221 my $type = shift; 2222 my $current = shift; 2223 2224 #print STDERR "$type $current "._print_current($current)."\n"; 2225 # if the next is a hash character at line beginning, mark it 2226 if (defined($current->{'text'}) and $current->{'text'} =~ /\n$/ 2227 and $current->{'parent'} and $current->{'parent'}->{'contents'}) { 2228 my $parent = $current->{'parent'}; 2229 #print STDERR "End of line in $current, parent $parent: (@{$parent->{'contents'}})\n"; 2230 my $current_found = 0; 2231 foreach my $content (@{$parent->{'contents'}}) { 2232 if ($current_found) { 2233 #print STDERR "after $current: $content $content->{'text'}\n"; 2234 if ($content->{'text'} and _is_cpp_line($content->{'text'})) { 2235 $content->{'extra'}->{'_protect_hashchar'} = 1; 2236 } 2237 last; 2238 } elsif ($content eq $current) { 2239 $current_found = 1; 2240 } 2241 } 2242 } 2243 2244 my $protect_hash = 0; 2245 # if marked, or first and a cpp_line protect a leading hash character 2246 if ($current->{'extra'} and $current->{'extra'}->{'_protect_hashchar'}) { 2247 delete $current->{'extra'}->{'_protect_hashchar'}; 2248 if (!scalar(keys(%{$current->{'extra'}}))) { 2249 delete $current->{'extra'}; 2250 } 2251 $protect_hash = 1; 2252 } elsif ($current->{'parent'} and $current->{'parent'}->{'contents'} 2253 and $current->{'parent'}->{'contents'}->[0] 2254 and $current->{'parent'}->{'contents'}->[0] eq $current 2255 and $current->{'text'} 2256 and _is_cpp_line($current->{'text'})) { 2257 $protect_hash = 1; 2258 } 2259 if ($protect_hash) { 2260 my @result = (); 2261 if ($current->{'type'} and $current->{'type'} eq 'raw') { 2262 if ($self) { 2263 my $parent = $current->{'parent'}; 2264 while ($parent) { 2265 if ($parent->{'cmdname'} and $parent->{'line_nr'}) { 2266 $self->line_warn(sprintf(__( 2267 "could not protect hash character in \@%s"), 2268 $parent->{'cmdname'}), $parent->{'line_nr'}); 2269 last; 2270 } 2271 $parent = $parent->{'parent'}; 2272 } 2273 } 2274 } else { 2275 $current->{'text'} =~ s/^(\s*)#//; 2276 if ($1 ne '') { 2277 push @result, {'text' => $1, 'parent' => $current->{'parent'}}; 2278 } 2279 push @result, {'cmdname' => 'hashchar', 'parent' => $current->{'parent'}, 2280 'args' => [{'type' => 'brace_command_arg'}]}; 2281 } 2282 push @result, $current; 2283 return @result; 2284 } else { 2285 return ($current); 2286 } 2287} 2288 2289sub protect_hashchar_at_line_beginning($$) 2290{ 2291 my $self = shift; 2292 my $tree = shift; 2293 return modify_tree($self, $tree, \&_protect_hashchar_at_line_beginning); 2294} 2295 2296sub protect_first_parenthesis($) 2297{ 2298 my $contents = shift; 2299 return undef if (!defined ($contents)); 2300 my @contents = @$contents; 2301 my $brace; 2302 if ($contents[0] and $contents->[0]{'text'} and $contents[0]->{'text'} =~ /^\(/) { 2303 if ($contents[0]->{'text'} !~ /^\($/) { 2304 $brace = shift @contents; 2305 my $brace_text = $brace->{'text'}; 2306 $brace_text =~ s/^\(//; 2307 unshift @contents, { 'text' => $brace_text, 'type' => $brace->{'type'}, 2308 'parent' => $brace->{'parent'} } if $brace_text ne ''; 2309 } else { 2310 $brace = shift @contents; 2311 } 2312 unshift @contents, _new_asis_command_with_text('(', $brace->{'parent'}, 2313 $brace->{'type'}); 2314 } 2315 return \@contents; 2316} 2317 2318sub find_parent_root_command($$) 2319{ 2320 my $parser = shift; 2321 my $current = shift; 2322 2323 my $root_command; 2324 while (1) { 2325 if ($current->{'cmdname'}) { 2326 if ($root_commands{$current->{'cmdname'}}) { 2327 return $current; 2328 } elsif ($region_commands{$current->{'cmdname'}}) { 2329 if ($current->{'cmdname'} eq 'copying' and $parser 2330 and $parser->{'extra'} and $parser->{'extra'}->{'insertcopying'}) { 2331 foreach my $insertcopying(@{$parser->{'extra'}->{'insertcopying'}}) { 2332 my $root_command 2333 = $parser->find_parent_root_command($insertcopying); 2334 return $root_command if (defined($root_command)); 2335 } 2336 } else { 2337 return undef; 2338 } 2339 } 2340 } 2341 if ($current->{'parent'}) { 2342 $current = $current->{'parent'}; 2343 } else { 2344 return undef; 2345 } 2346 } 2347 # Should never get there 2348 return undef; 2349} 2350 2351# for debugging 2352sub _print_current($) 2353{ 2354 my $current = shift; 2355 if (ref($current) ne 'HASH') { 2356 return "_print_current: $current not a hash\n"; 2357 } 2358 my $type = ''; 2359 my $cmd = ''; 2360 my $parent_string = ''; 2361 my $text = ''; 2362 $type = "($current->{'type'})" if (defined($current->{'type'})); 2363 $cmd = "\@$current->{'cmdname'}" if (defined($current->{'cmdname'})); 2364 $cmd .= "($current->{'level'})" if (defined($current->{'level'})); 2365 $text = "[text: $current->{'text'}]" if (defined($current->{'text'})); 2366 if ($current->{'parent'}) { 2367 my $parent = $current->{'parent'}; 2368 my $parent_cmd = ''; 2369 my $parent_type = ''; 2370 $parent_cmd = "\@$parent->{'cmdname'}" if (defined($parent->{'cmdname'})); 2371 $parent_type = "($parent->{'type'})" if (defined($parent->{'type'})); 2372 $parent_string = " <- $parent_cmd$parent_type\n"; 2373 } 2374 my $args = ''; 2375 my $contents = ''; 2376 $args = "args(".scalar(@{$current->{'args'}}).')' if $current->{'args'}; 2377 $contents = "contents(".scalar(@{$current->{'contents'}}).')' 2378 if $current->{'contents'}; 2379 if ("$cmd$type" ne '') { 2380 return "$cmd$type : $text $args $contents\n$parent_string"; 2381 } else { 2382 return "$text $args $contents\n$parent_string"; 2383 } 2384} 2385 2386sub move_index_entries_after_items($) { 2387 # enumerate or itemize 2388 my $current = shift; 2389 2390 return unless ($current->{'contents'}); 2391 2392 my $previous; 2393 foreach my $item (@{$current->{'contents'}}) { 2394 #print STDERR "Before proceeding: $previous $item->{'cmdname'} (@{$previous->{'contents'}})\n" if ($previous and $previous->{'contents'}); 2395 if (defined($previous) and $item->{'cmdname'} 2396 and $item->{'cmdname'} eq 'item' 2397 and $previous->{'contents'} and scalar(@{$previous->{'contents'}})) { 2398 2399 my $previous_ending_container; 2400 if ($previous->{'contents'}->[-1]->{'type'} 2401 and ($previous->{'contents'}->[-1]->{'type'} eq 'paragraph' 2402 or $previous->{'contents'}->[-1]->{'type'} eq 'preformatted')) { 2403 $previous_ending_container = $previous->{'contents'}->[-1]; 2404 } else { 2405 $previous_ending_container = $previous; 2406 } 2407 2408 my @gathered_index_entries; 2409 2410 #print STDERR "Gathering for item $item in previous $previous ($previous_ending_container)\n"; 2411 while ($previous_ending_container->{'contents'}->[-1] 2412 and (($previous_ending_container->{'contents'}->[-1]->{'type'} 2413 and $previous_ending_container->{'contents'}->[-1]->{'type'} eq 'index_entry_command') 2414 or ($previous_ending_container->{'contents'}->[-1]->{'cmdname'} 2415 and ($previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'c' 2416 or $previous_ending_container->{'contents'}->[-1]->{'cmdname'} eq 'comment')))) { 2417 unshift @gathered_index_entries, pop @{$previous_ending_container->{'contents'}}; 2418 } 2419 #print STDERR "Gathered: @gathered_index_entries\n"; 2420 if (scalar(@gathered_index_entries)) { 2421 # put back leading comments 2422 while ($gathered_index_entries[0] 2423 and (!$gathered_index_entries[0]->{'type'} 2424 or $gathered_index_entries[0]->{'type'} ne 'index_entry_command')) { 2425 #print STDERR "Putting back $gathered_index_entries[0] $gathered_index_entries[0]->{'cmdname'}\n"; 2426 push @{$previous_ending_container->{'contents'}}, 2427 shift @gathered_index_entries; 2428 } 2429 2430 # We have the index entries of the previous @item or before item. 2431 # Now put them right after the current @item command. 2432 if (scalar(@gathered_index_entries)) { 2433 my $item_container; 2434 if ($item->{'contents'} and $item->{'contents'}->[0] 2435 and $item->{'contents'}->[0]->{'type'} 2436 and $item->{'contents'}->[0]->{'type'} eq 'preformatted') { 2437 $item_container = $item->{'contents'}->[0]; 2438 } else { 2439 $item_container = $item; 2440 } 2441 foreach my $entry(@gathered_index_entries) { 2442 $entry->{'parent'} = $item_container; 2443 } 2444 if ($item->{'extra'} 2445 and $item->{'extra'}->{'spaces_before_argument'} 2446 and $item->{'extra'}->{'spaces_before_argument'} !~ /\n$/) { 2447 $item->{'extra'}->{'spaces_before_argument'} .= "\n"; 2448 # TODO: could we delete all these cases down here? 2449 } elsif ($item_container->{'contents'} 2450 and $item_container->{'contents'}->[0] 2451 and $item_container->{'contents'}->[0]->{'type'}) { 2452 if ($item_container->{'contents'}->[0]->{'type'} eq 'empty_line_after_command') { 2453 unshift @gathered_index_entries, shift @{$item_container->{'contents'}}; 2454 } elsif ($item_container->{'contents'}->[0]->{'type'} eq 'empty_spaces_after_command') { 2455 unshift @gathered_index_entries, shift @{$item_container->{'contents'}}; 2456 $gathered_index_entries[0]->{'type'} = 'empty_line_after_command'; 2457 $gathered_index_entries[0]->{'text'} .= "\n"; 2458 } 2459 } 2460 unshift @{$item_container->{'contents'}}, @gathered_index_entries; 2461 } 2462 } 2463 } 2464 $previous = $item; 2465 } 2466} 2467 2468sub _move_index_entries_after_items($$$) 2469{ 2470 my $self = shift; 2471 my $type = shift; 2472 my $current = shift; 2473 2474 if ($current->{'cmdname'} and ($current->{'cmdname'} eq 'enumerate' 2475 or $current->{'cmdname'} eq 'itemize')) { 2476 move_index_entries_after_items($current); 2477 } 2478 return ($current); 2479} 2480 2481sub move_index_entries_after_items_in_tree($) 2482{ 2483 my $tree = shift; 2484 return modify_tree(undef, $tree, \&_move_index_entries_after_items); 2485} 2486 2487sub _relate_index_entry_to_table_entry($) 2488{ 2489 my $current = shift; # table_entry 2490 2491 my ($table_term, $table_item, $item); 2492 2493 if ($current->{'contents'} 2494 and $current->{'contents'}->[0] 2495 and $current->{'contents'}->[0]->{'type'} eq 'table_term') { 2496 $table_term = $current->{'contents'}->[0]; 2497 } 2498 2499 if ($current->{'contents'} 2500 and $current->{'contents'}->[1] 2501 and $current->{'contents'}->[1]->{'type'} eq 'table_item') { 2502 $table_item = $current->{'contents'}->[1]; 2503 } 2504 2505 if ($table_term->{'contents'} 2506 and $table_term->{'contents'}->[0] 2507 and (!$table_term->{'contents'}->[0]->{'extra'} 2508 or !$table_term->{'contents'}->[0]->{'extra'}->{'index_entry'})) { 2509 $item = $table_term->{'contents'}->[0]; 2510 } 2511 2512 return if !$table_term or !$table_item or !$item; 2513 2514 if ($table_item->{'contents'} 2515 and $table_item->{'contents'}->[0] 2516 and $table_item->{'contents'}->[0]->{'type'} 2517 and $table_item->{'contents'}->[0]->{'type'} eq 'index_entry_command') { 2518 my $index_command = shift @{$table_item->{'contents'}}; 2519 delete $index_command->{'parent'}; 2520 $item->{'extra'}->{'index_entry'} 2521 = $index_command->{'extra'}->{'index_entry'}; 2522 $item->{'extra'}->{'index_entry'}->{'command'} = $item; 2523 } 2524} 2525 2526sub _relate_index_entries_to_table_entries_in_tree($$$) 2527{ 2528 my ($self, $type, $current) = @_; 2529 2530 if ($current->{'type'} and ($current->{'type'} eq 'table_entry')) { 2531 _relate_index_entry_to_table_entry($current); 2532 } 2533 return ($current); 2534} 2535 2536sub relate_index_entries_to_table_entries_in_tree($) 2537{ 2538 my $tree = shift; 2539 return modify_tree(undef, $tree, 2540 \&_relate_index_entries_to_table_entries_in_tree); 2541} 2542 2543 2544sub debug_list 2545{ 2546 my ($label) = shift; 2547 my (@list) = (ref $_[0] && $_[0] =~ /.*ARRAY.*/) ? @{$_[0]} : @_; 2548 2549 my $str = "$label: ["; 2550 my @items = (); 2551 for my $item (@list) { 2552 $item = "" if ! defined ($item); 2553 $item =~ s/\n/\\n/g; 2554 push (@items, $item); 2555 } 2556 $str .= join (" ", @items); 2557 $str .= "]"; 2558 2559 warn "$str\n"; 2560} 2561# 2562sub debug_hash 2563{ 2564 my ($label) = shift; 2565 my (%hash) = (ref $_[0] && $_[0] =~ /.*HASH.*/) ? %{$_[0]} : @_; 2566 2567 my $str = "$label: {"; 2568 my @items = (); 2569 for my $key (sort keys %hash) { 2570 my $val = $hash{$key} || ""; # no undef 2571 $key =~ s/\n/\\n/g; 2572 $val =~ s/\n/\\n/g; 2573 push (@items, "$key:$val"); 2574 } 2575 $str .= join (",", @items); 2576 $str .= "}"; 2577 2578 warn "$str\n"; 2579} 2580 2581use Data::Dumper; 2582 2583my @kept_keys = ('contents', 'cmdname', 'type', 'text', 'args', 2584 'extra', 'def_role', 'spaces_before_argument', 2585 'spaces_after_argument', 'comment_at_end', 'index_entry' 2586); 2587my %kept_keys; 2588foreach my $key (@kept_keys) { 2589 $kept_keys{$key} = 1; 2590} 2591sub _filter_print_keys { [grep {$kept_keys{$_}} ( sort keys %{$_[0]} )] }; 2592sub print_tree($) 2593{ 2594 my $tree = shift; 2595 local $Data::Dumper::Sortkeys = \&_filter_print_keys; 2596 local $Data::Dumper::Purity = 1; 2597 local $Data::Dumper::Indent = 1; 2598 2599 return Data::Dumper->Dump([$tree]); 2600} 2601 2602# common parser functions 2603 2604sub _non_bracketed_contents { 2605 my $current = shift; 2606 2607 if ($current->{'type'} and $current->{'type'} eq 'bracketed') { 2608 my $new = {}; 2609 $new->{'contents'} = $current->{'contents'} if ($current->{'parent'}); 2610 $new->{'parent'} = $current->{'parent'} if ($current->{'parent'}); 2611 return $new; 2612 } else { 2613 return $current; 2614 } 2615} 2616 2617# In a handful of cases, we delay storing the contents of the 2618# index entry until now to avoid needing Texinfo::Report::gdt 2619# in the main code of Parser.pm. Also set 'in_code' value on 2620# index entries. 2621 2622sub complete_indices { 2623 my $self = shift; 2624 2625 my ($index_entry, $index_contents_normalized); 2626 2627 my $save_lang = $self->get_conf('documentlanguage'); 2628 2629 foreach my $index_name (keys(%{$self->{'index_names'}})) { 2630 next if !defined $self->{'index_names'}->{$index_name}->{'index_entries'}; 2631 foreach my $entry (@{$self->{'index_names'}->{$index_name}->{'index_entries'}}) { 2632 $entry->{'in_code'} = $self->{'index_names'}->{$index_name}->{'in_code'}; 2633 2634 if (!defined $entry->{'content'}) { 2635 my $def_command = $entry->{'command'}->{'extra'}->{'def_command'}; 2636 2637 my $def_parsed_hash = $entry->{'command'}->{'extra'}->{'def_parsed_hash'}; 2638 if ($def_parsed_hash and $def_parsed_hash->{'class'} 2639 and $def_command) { 2640 # Use the document language that was current when the command was 2641 # used for getting the translation. 2642 $self->{'documentlanguage'} = $entry->{'command'}->{'extra'}->{'documentlanguage'}; 2643 delete $entry->{'command'}->{'extra'}->{'documentlanguage'}; 2644 if ($def_command eq 'defop' 2645 or $def_command eq 'deftypeop' 2646 or $def_command eq 'defmethod' 2647 or $def_command eq 'deftypemethod') { 2648 $index_entry = $self->gdt('{name} on {class}', 2649 {'name' => $def_parsed_hash->{'name'}, 2650 'class' => $def_parsed_hash->{'class'}}); 2651 $index_contents_normalized 2652 = [_non_bracketed_contents($def_parsed_hash->{'name'}), 2653 { 'text' => ' on '}, 2654 _non_bracketed_contents($def_parsed_hash->{'class'})]; 2655 } elsif ($def_command eq 'defivar' 2656 or $def_command eq 'deftypeivar' 2657 or $def_command eq 'deftypecv') { 2658 $index_entry = $self->gdt('{name} of {class}', 2659 {'name' => $def_parsed_hash->{'name'}, 2660 'class' => $def_parsed_hash->{'class'}}); 2661 $index_contents_normalized 2662 = [_non_bracketed_contents($def_parsed_hash->{'name'}), 2663 { 'text' => ' of '}, 2664 _non_bracketed_contents($def_parsed_hash->{'class'})]; 2665 } 2666 } 2667 # 'root_line' is the container returned by gdt. 2668 if ($index_entry->{'type'} and $index_entry->{'type'} eq 'root_line') { 2669 for my $child (@{$index_entry->{'contents'}}) { 2670 delete $child->{'parent'}; 2671 } 2672 } 2673 if ($index_entry->{'contents'}) { 2674 $entry->{'content'} = [@{$index_entry->{'contents'}}]; 2675 $entry->{'content_normalized'} = $index_contents_normalized; 2676 } 2677 } 2678 } 2679 } 2680 $self->{'documentlanguage'} = $save_lang; 2681} 2682 2683# Called from Texinfo::Parser and Texinfo::XS::parsetexi::Parsetexi. 2684sub labels_information 2685{ 2686 my $self = shift; 2687 if (defined $self->{'targets'}) { 2688 my %labels = (); 2689 for my $target (@{$self->{'targets'}}) { 2690 if ($target->{'cmdname'} eq 'node') { 2691 if ($target->{'extra'}->{'nodes_manuals'}) { 2692 for my $node_manual (@{$target->{'extra'}{'nodes_manuals'}}) { 2693 if (defined $node_manual 2694 and defined $node_manual->{'node_content'}) { 2695 my $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => $node_manual->{'node_content'}}); 2696 $node_manual->{'normalized'} = $normalized; 2697 } 2698 } 2699 } 2700 } 2701 if (defined $target->{'extra'} 2702 and defined $target->{'extra'}->{'node_content'}) { 2703 my $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node({'contents' => $target->{'extra'}->{'node_content'}}); 2704 2705 if ($normalized !~ /[^-]/) { 2706 $self->line_error (sprintf(__("empty node name after expansion `%s'"), 2707 Texinfo::Convert::Texinfo::convert({'contents' 2708 => $target->{'extra'}->{'node_content'}})), 2709 $target->{'line_nr'}); 2710 delete $target->{'extra'}->{'node_content'}; 2711 } else { 2712 if (defined $labels{$normalized}) { 2713 $self->line_error( 2714 sprintf(__("\@%s `%s' previously defined"), 2715 $target->{'cmdname'}, 2716 Texinfo::Convert::Texinfo::convert({'contents' => 2717 $target->{'extra'}->{'node_content'}})), 2718 $target->{'line_nr'}); 2719 $self->line_error( 2720 sprintf(__("here is the previous definition as \@%s"), 2721 $labels{$normalized}->{'cmdname'}), 2722 $labels{$normalized}->{'line_nr'}); 2723 delete $target->{'extra'}->{'node_content'}; 2724 } else { 2725 $labels{$normalized} = $target; 2726 $target->{'extra'}->{'normalized'} = $normalized; 2727 if ($target->{'cmdname'} eq 'node') { 2728 if ($target->{'extra'} 2729 and $target->{'extra'}{'node_argument'}) { 2730 $target->{'extra'}{'node_argument'}{'normalized'} 2731 = $normalized; 2732 } 2733 push @{$self->{'nodes'}}, $target; 2734 } 2735 } 2736 } 2737 } else { 2738 if ($target->{'cmdname'} eq 'node') { 2739 $self->line_error (sprintf(__("empty argument in \@%s"), 2740 $target->{'cmdname'}), $target->{'line_nr'}); 2741 delete $target->{'extra'}->{'node_content'}; 2742 } 2743 } 2744 } 2745 $self->{'labels'} = \%labels; 2746 delete $self->{'targets'}; 2747 } 2748 return $self->{'labels'}; 2749} 2750 27511; 2752 2753__END__ 2754 2755=head1 NAME 2756 2757Texinfo::Common - Classification of commands and miscellaneous methods 2758 2759=head1 SYNOPSIS 2760 2761 use Texinfo::Common qw(expand_today expand_verbatiminclude); 2762 if ($Texinfo::Common::accent_commands{$a_command}) { 2763 print STDERR "$a_command is an accent command\n"; 2764 } 2765 2766 my $today_tree = expand_today($converter); 2767 my $verbatiminclude_tree 2768 = expand_verbatiminclude(undef, $verbatiminclude); 2769 2770=head1 DESCRIPTION 2771 2772Texinfo::Common holds interesting hashes classifying Texinfo @-commands, 2773as well as miscellaneous methods that may be useful for any backend 2774converting texinfo trees. 2775 2776It also defines, as our variable a hash for default indices, 2777named C<%index_names>. The format of this hash is described in 2778L<Texinfo::Parser/indices_information>. 2779 2780=head1 COMMAND CLASSES 2781 2782Hashes are defined as C<our> variables, and are therefore available 2783outside of the module. 2784 2785The key of the hashes are @-command names without the @. The 2786following hashes are available: 2787 2788=over 2789 2790=item %all_commands 2791 2792All the @-commands. 2793 2794=item %no_brace_commands 2795 2796Commands without brace with a single character as name, like C<*> 2797or C<:>. The value is an ascii representation of the command. It 2798may be an empty string. 2799 2800=item %misc_commands 2801 2802Command that do not take braces and are not block commands either, like 2803C<@node>, C<@chapter>, C<@cindex>, C<@deffnx>, C<@end>, C<@footnotestyle>, 2804C<@set>, C<@settitle>, C<@indent>, C<@definfoenclose>, C<@comment> and many 2805others. 2806 2807=item %default_index_commands 2808 2809Index entry commands corresponding to default indices. For example 2810C<@cindex>. 2811 2812=item %root_commands 2813 2814Commands that are at the root of a Texinfo document, namely 2815C<@node> and sectioning commands, except heading commands. 2816 2817=item %sectioning_commands 2818 2819All the sectioning and heading commands. 2820 2821=item %brace_commands 2822 2823The commands that take braces. The associated value is the maximum 2824number of arguments. 2825 2826=item %letter_no_arg_commands 2827 2828@-commands with braces but no argument corresponding to letters, 2829like C<@AA{}> or C<@ss{}> or C<@o{}>. 2830 2831=item %accent_commands 2832 2833Accent @-commands taking an argument, like C<@'> or C<@ringaccent> 2834including C<@dotless> and C<@tieaccent>. 2835 2836=item %style_commands 2837 2838Commands that mark a fragment of texinfo, like C<@strong>, 2839C<@cite>, C<@code> or C<@asis>. 2840 2841=item %code_style_commands 2842 2843I<style_commands> that have their argument in code style, like 2844C<@code>. 2845 2846=item %regular_font_style_commands 2847 2848I<style_commands> that have their argument in regular font, like 2849C<@r> or C<@slanted>. 2850 2851=item %context_brace_commands 2852 2853@-commands with brace like C<@footnote>, C<@caption> and C<@math> 2854whose argument is outside of the main text flow in one way or another. 2855 2856=item %ref_commands 2857 2858Cross reference @-command referencing nodes, like C<@xref>. 2859 2860=item %explained_commands 2861 2862@-commands whose second argument explain first argument and further 2863@-command call without first argument, as C<@abbr> and C<@acronym>. 2864 2865=item %block commands 2866 2867Commands delimiting a block with a closing C<@end>. The value 2868is I<conditional> for C<@if> commands, I<def> for definition 2869commands like C<@deffn>, I<raw> for @-commands that have no expansion 2870of @-commands in their bodies and I<multitable> for C<@multitable>. 2871Otherwise it is set to the number of arguments separated by commas 2872that may appear on the @-command line. That means 0 in most cases, 28731 for C<@quotation> and 2 for C<@float>. 2874 2875=item %raw_commands 2876 2877@-commands that have no expansion of @-commands in their bodies, 2878as C<@macro>, C<@verbatim> or C<@ignore>. 2879 2880=item %format_raw_commands 2881 2882@-commands associated with raw output format, like C<@html>, or 2883C<@docbook>. 2884 2885=item %math_commands 2886 2887@-commands which contains math, like C<@math> or C<@displaymath>. 2888 2889=item %texinfo_output_formats 2890 2891Cannonical output formats that have associated conditionals. In 2892practice C<%format_raw_commands> plus C<info> and C<plaintext>. 2893 2894=item %def_commands 2895 2896=item %def_aliases 2897 2898Definition commands. C<%def_aliases> associates an aliased command 2899to the original command, for example C<defun> is associated to C<deffn>. 2900 2901=item %menu_commands 2902 2903@-commands with menu entries. 2904 2905=item %align_commands 2906 2907@-commands related with alignement of text. 2908 2909=item %region_commands 2910 2911Block @-commands that enclose full text regions, like C<@titlepage>. 2912 2913=item %preformatted_commands 2914 2915=item %preformatted_code_commands 2916 2917I<%preformatted_commands> is for commands whose content should not 2918be filled, like C<@example> or C<@display>. If the command is meant 2919for code, it is also in I<%preformatted_code_commands>, like C<@example>. 2920 2921=item %item_container_commands 2922 2923Commands holding C<@item> with C<@item> that contains blocks of text, 2924like C<@itemize>. 2925 2926=item %item_line_commands 2927 2928Commands with C<@item> that have their arguments on their lines, like 2929C<@ftable>. 2930 2931=back 2932 2933=head1 METHODS 2934 2935No method is exported in the default case. 2936 2937Most methods takes a I<$converter> as argument, sometime optionally, 2938to get some information and use methods for error reporting, 2939see L<Texinfo::Convert::Converter> and L<Texinfo::Report>. 2940 2941=over 2942 2943=item $tree = expand_today($converter) 2944 2945Expand today's date, as a texinfo tree with translations. 2946 2947=item $tree = expand_verbatiminclude($converter, $verbatiminclude) 2948 2949The I<$converter> argument may be undef. I<$verbatiminclude> is a 2950C<@verbatiminclude> tree element. This function returns a 2951C<@verbatim> tree elements after finding the included file and 2952reading it. If I<$converter> is not defined, the document encoding 2953is not taken into account when reading the file. 2954 2955=item $tree = definition_category($converter, $def_line) 2956 2957The I<$converter> argument may be undef. I<$def_line> is a 2958C<def_line> texinfo tree container. This function 2959returns a texinfo tree corresponding to the category of the 2960I<$def_line> taking the class into account, if there is one. 2961If I<$converter> is not defined, the resulting string won't be 2962translated. 2963 2964=item $result = is_content_empty($tree, $do_not_ignore_index_entries) 2965 2966Return true if the C<$tree> has content that could be formatted. 2967C<$do_not_ignore_index_entries> is optional. If set, index entries 2968are considered to be formatted. 2969 2970=item $result = numbered_heading ($converter, $heading_element, $heading_text, $do_number) 2971 2972The I<$converter> argument may be undef. I<$heading_element> is 2973a heading command tree element. I<$heading_text> is the already 2974formatted heading text. if the I<$do_number> optional argument is 2975defined and false, no number is used and the text is returned as is. 2976This function returns the heading with a number and the appendix 2977part if needed. If I<$converter> is not defined, the resulting 2978string won't be translated. 2979 2980=item ($caption, $prepended) = float_name_caption ($converter, $float) 2981 2982I<$float> is a texinfo tree C<@float> element. This function 2983returns the caption that should be used for the float formatting 2984and the I<$prepended> texinfo tree combining the type and label 2985of the float. 2986 2987=item $text = enumerate_item_representation($specification, $number) 2988 2989This function returns the number or letter correponding to item 2990number I<$number> for an C<@enumerate> specification I<$specification>, 2991appearing on an C<@enumerate> line. For example 2992 2993 enumerate_item_representation('c', 3) 2994 2995is C<e>. 2996 2997=item trim_spaces_comment_from_content($contents) 2998 2999Remove empty spaces after commands or braces at begin and 3000spaces and comments at end from a content array, modifying it. 3001 3002=item $normalized_name = normalize_top_node_name ($node_string) 3003 3004Normalize the node name string given in argument, by normalizing 3005Top node case. 3006 3007=item protect_comma_in_tree($tree) 3008 3009Protect comma characters, replacing C<,> with @comma{} in tree. 3010 3011=item protect_colon_in_tree($tree) 3012 3013=item protect_node_after_label_in_tree($tree) 3014 3015Protect colon with C<protect_colon_in_tree> and characters that 3016are special in node names after a label in menu entries (tab 3017dot and comma) with C<protect_node_after_label_in_tree>. 3018The protection is achieved by putting protected characters 3019in C<@asis{}>. 3020 3021=item $contents_result = protect_first_parenthesis ($contents) 3022 3023Return a contents array reference with first parenthesis in the 3024contents array reference protected. 3025 3026=item protect_hashchar_at_line_beginning($parser, $tree) 3027 3028Protect hash character at beginning of line if the line is a cpp 3029line directive. The I<$parser> argument maybe undef, if it is 3030defined it is used for error reporting in case an hash character 3031could not be protected because it appeared in a raw environment. 3032 3033=item move_index_entries_after_items_in_tree($tree) 3034 3035In C<@enumerate> and C<@itemize> from the tree, move index entries 3036appearing just before C<@item> after the C<@item>. Comment lines 3037between index entries are moved too. 3038 3039=item $command = find_parent_root_command($parser, $tree_element) 3040 3041Find the parent root command of a tree element (sectioning command or node). 3042The C<$parser> argument is optional, it is used to continue 3043through C<@insertcopying> if in a C<@copying>. 3044 3045=item valid_tree_transformation($name) 3046 3047Return true if the I<$name> is a known tree transformation name 3048that may be passed with C<TREE_TRANSFORMATIONS> to modify a texinfo 3049tree. 3050 3051=item collect_commands_in_tree($tree, $commands_list) 3052 3053Returns a hash reference with keys @-commands names specified 3054in the I<$commands_list> array reference and values arrays of 3055tree elements corresponding to those @-command found in I<$tree> 3056by traversing the tree. 3057 3058=back 3059 3060=head1 SEE ALSO 3061 3062L<Texinfo::Parser>, L<Texinfo::Convert::Converter> and L<Texinfo::Report>. 3063 3064=head1 AUTHOR 3065 3066Patrice Dumas, E<lt>pertusus@free.frE<gt> 3067 3068=cut 3069