diff options
author | Norbert Preining <norbert@preining.info> | 2022-11-09 03:02:50 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-11-09 03:02:50 +0000 |
commit | 5de83ec843cdc88e6adc42b1fe3f0ec48c52b7f3 (patch) | |
tree | b49f7c180396c8ef389c8fd519370b6d03f4e100 /macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm | |
parent | 118cb5bbc5236bf86ca1088b9b3bae04c7b09beb (diff) |
CTAN sync 202211090302
Diffstat (limited to 'macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm')
-rw-r--r-- | macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm | 1699 |
1 files changed, 980 insertions, 719 deletions
diff --git a/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm b/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm index a6899761b5..f71ebde3f6 100644 --- a/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm +++ b/macros/texinfo/texinfo/tp/Texinfo/Convert/DocBook.pm @@ -1,6 +1,6 @@ # DocBook.pm: output tree as DocBook. # -# Copyright 2011-2020 Free Software Foundation, Inc. +# Copyright 2011-2022 Free Software Foundation, Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -22,53 +22,45 @@ package Texinfo::Convert::DocBook; use 5.00405; use strict; -use Texinfo::Convert::Converter; +# To check if there is no erroneous autovivification +#no autovivification qw(fetch delete exists store strict); + +use Texinfo::Commands; use Texinfo::Common; + +# for debugging +use Texinfo::Convert::Texinfo; + use Texinfo::Convert::Unicode; +use Texinfo::Convert::Utils; use Texinfo::Convert::Text; +use Texinfo::Convert::Converter; use Texinfo::Convert::Plaintext; -# for debugging -use Texinfo::Convert::Texinfo; use Data::Dumper; use Carp qw(cluck); require Exporter; -use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); -@ISA = qw(Exporter Texinfo::Convert::Converter); +use vars qw($VERSION @ISA); +@ISA = qw(Texinfo::Convert::Converter); -%EXPORT_TAGS = ( 'all' => [ qw( - convert - convert_tree - output -) ] ); - -@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); - -@EXPORT = qw( -); +$VERSION = '7.0'; -$VERSION = '6.8'; +my %brace_commands = %Texinfo::Commands::brace_commands; my $nbsp = '&#'.hex('00A0').';'; -my $mdash = '&#'.hex('2014').';'; -my $ndash = '&#'.hex('2013').';'; -my $ldquo = '&#'.hex('201C').';'; -my $rdquo = '&#'.hex('201D').';'; -my $lsquo = '&#'.hex('2018').';'; -my $rsquo = '&#'.hex('2019').';'; my %defaults = ( #'ENABLE_ENCODING' => 0, 'FORMAT_MENU' => 'nomenu', 'EXTENSION' => 'xml', # dbk? 'OUTPUT_ENCODING_NAME' => 'utf-8', - 'OUTFILE' => undef, - 'SUBDIR' => undef, - 'output_format' => 'docbook', + 'converted_format' => 'docbook', 'SPLIT' => 0, - 'documentlanguage' => 'en', - 'OPEN_QUOTE_SYMBOL' => $lsquo, - 'CLOSE_QUOTE_SYMBOL' => $rsquo, + 'documentlanguage' => undef, + 'OPEN_QUOTE_SYMBOL' => '&#'.hex('2018').';', + 'CLOSE_QUOTE_SYMBOL' => '&#'.hex('2019').';', + 'USE_NUMERIC_ENTITY' => 1, + 'NO_TOP_NODE_OUTPUT' => 1, ); my @docbook_image_extensions @@ -91,16 +83,16 @@ my %docbook_specific_formatting = ( " " => $nbsp, 'tie' => $nbsp, ); -my %docbook_commands_formatting - = %{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}}; +my %docbook_no_arg_commands_formatting + = %Texinfo::Convert::Converter::xml_text_entity_no_arg_commands_formatting; foreach my $command (keys(%Texinfo::Convert::Unicode::unicode_entities)) { - $docbook_commands_formatting{$command} + $docbook_no_arg_commands_formatting{$command} = $Texinfo::Convert::Unicode::unicode_entities{$command}; } foreach my $command (keys(%docbook_specific_formatting)) { - $docbook_commands_formatting{$command} + $docbook_no_arg_commands_formatting{$command} = $docbook_specific_formatting{$command}; } @@ -131,13 +123,13 @@ my %style_attribute_commands; 'emph' => 'emphasis', 'env' => 'envar', 'file' => 'filename', - 'footnote' => 'footnote', + 'footnote' => 'footnote', # brace context command 'headitemfont' => 'emphasis role="bold"', # actually <th> instead of <td> 'i' => 'emphasis', 'indicateurl' => 'literal', 'kbd' => 'userinput', 'key' => 'keycap', - 'math' => 'mathphrase', + 'math' => 'mathphrase', # brace context command 'option' => 'option', 'r' => '', 'samp' => 'literal', @@ -147,15 +139,19 @@ my %style_attribute_commands; 'sup' => 'superscript', 't' => 'literal', 'var' => 'replaceable', - 'verb' => 'literal', + 'verb' => 'literal', # brace other command ); -# this weird construct does like uniq, it avoids duplicates. -# it may be required since math is not in the %style_commands as it is -# in context command. +my %style_brace_types = map {$_ => 1} ('style_other', 'style_code', 'style_no_code'); +# @all_style_commands is the union of style brace commands, commands +# in %style_attribute_commands and a few other, some not style brace commands. +# Using keys of a map generated hash does like uniq, it avoids duplicates. +# The first grep selects style brace commands, ie commands with %brace_commands +# type in %style_brace_types. my @all_style_commands = keys %{{ map { $_ => 1 } - (keys(%Texinfo::Common::style_commands), keys(%style_attribute_commands), - 'w', 'dmn', 'titlefont') }}; + ((grep {$style_brace_types{$brace_commands{$_}}} keys(%brace_commands)), + keys(%style_attribute_commands), 'w', 'dmn', 'titlefont') }}; + # special string for 'w'. my $w_command_mark = '<!-- /@w -->'; @@ -163,7 +159,7 @@ my %style_commands_formatting; foreach my $command(@all_style_commands) { $style_commands_formatting{$command} = {}; if ($style_attribute_commands{$command}) { - $style_commands_formatting{$command}->{'attribute'} + $style_commands_formatting{$command}->{'attribute'} = $style_attribute_commands{$command}; } if ($quoted_style_commands{$command}) { @@ -174,32 +170,28 @@ foreach my $command(@all_style_commands) { } } -my %docbook_misc_elements_with_arg_map = ( - 'settitle' => 'title', - 'exdent' => 'simpara', - 'center' => '', +my %docbook_line_elements_with_arg_map = ( + 'exdent' => 'simpara role="exdent"', + 'center' => 'simpara role="center"', ); -my %docbook_misc_commands = %Texinfo::Common::misc_commands; +my %docbook_nobrace_commands = %Texinfo::Commands::nobrace_commands; +foreach my $command ('item', 'headitem', 'tab', + keys(%docbook_no_arg_commands_formatting)) { + delete $docbook_nobrace_commands{$command}; +} -foreach my $command ('item', 'headitem', 'itemx', 'tab', - keys %Texinfo::Common::def_commands) { - delete $docbook_misc_commands{$command}; +my %docbook_line_commands = %Texinfo::Commands::line_commands; +foreach my $command ('itemx', keys %Texinfo::Commands::def_commands) { + delete $docbook_line_commands{$command}; } my %docbook_global_commands = ( - 'documentencoding' => 1, 'documentlanguage' => 1, ); -sub converter_global_commands($) -{ - return keys(%docbook_global_commands); -} - -my %default_args_code_style +my %default_args_code_style = %Texinfo::Convert::Converter::default_args_code_style; -my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands; my %defcommand_name_type = ( 'defcv' => 'property', @@ -216,21 +208,26 @@ my %defcommand_name_type = ( my %def_argument_types_docbook = ( 'type' => ['returnvalue'], 'class' => ['ooclass', 'classname'], - 'arg' => ['replaceable'], + # FIXME or a simple emphasis? + # replaceable is not used here, such that replaceable is only + # used if there is an explicit @var{} + 'arg' => ['emphasis role="arg"'], 'typearg' => ['type'], ); +my %ignored_block_commands; +foreach my $block_command ('copying', 'titlepage', 'documentdescription') { + $ignored_block_commands{$block_command} = 1; +} my %ignored_types; foreach my $type ( - 'empty_line_after_command', - 'empty_spaces_after_close_brace', - 'empty_spaces_after_command', - 'empty_spaces_before_argument', - 'empty_spaces_before_paragraph', + 'ignorable_spaces_after_command', + 'spaces_after_close_brace', + 'spaces_before_paragraph', 'menu_entry_leading_text', 'menu_entry_separator', - 'preamble', + 'preamble_before_beginning', 'preamble_before_setfilename', 'spaces_at_end', ) { @@ -260,13 +257,15 @@ my %docbook_preformatted_formats = ( 'smalldisplay' => 'literallayout', 'lisp' => 'programlisting', 'smalllisp' => 'programlisting', - 'format' => 'abstract', - 'smallformat' => 'screen', + 'format' => 'literallayout', + 'smallformat' => 'literallayout', # type 'menu_comment' => 'literallayout', 'menu_description' => 'literallayout', ); +my %sectioning_commands_done; + sub converter_defaults($$) { return %defaults; @@ -279,20 +278,27 @@ sub converter_initialize($) $self->{'document_context'} = [{'monospace' => [0], 'upper_case' => [0]}]; $self->{'context_block_commands'} = {%default_context_block_commands}; - foreach my $raw (keys (%Texinfo::Common::format_raw_commands)) { + foreach my $raw (grep {$Texinfo::Commands::block_commands{$_} eq 'format_raw'} + keys(%Texinfo::Commands::block_commands)) { $self->{'context_block_commands'}->{$raw} = 1 if $self->{'expanded_formats_hash'}->{$raw}; } } -sub convert($$;$) +sub convert($$) { my $self = shift; my $root = shift; - my $fh = shift; - $self->_set_global_multiple_commands(-1); - return $self->convert_document_sections($root, $fh); + if (! defined($self->{'lang_stack'})) { + $self->{'lang_stack'} = ['']; + } + # could even set to 0 if defined? + $self->{'in_skipped_node_top'} = 0 + if (! defined($self->{'in_skipped_node_top'})); + + %sectioning_commands_done = (); + return $self->convert_tree($root); } sub convert_tree($$) @@ -300,59 +306,214 @@ sub convert_tree($$) my $self = shift; my $root = shift; + if (! defined($self->{'lang_stack'})) { + $self->{'lang_stack'} = ['']; + } return $self->_convert($root); } +# not the same as a default for @documentlanguage. $DEFAULT_LANG +# is used in the lang attribute, but if there is no @documentlanguag, +# the lang_stack will start with an empty string, not with $DEFAULT_LANG. +my $DEFAULT_LANG = 'en'; sub output($$) { my $self = shift; my $root = shift; - $self->_set_outfile(); - return undef unless $self->_create_destination_directory(); + my ($output_file, $destination_directory, $output_filename) + = $self->determine_files_and_directory(); + + my ($encoded_destination_directory, $dir_encoding) + = $self->encoded_output_file_name($destination_directory); + my $succeeded + = $self->create_destination_directory($encoded_destination_directory, + $destination_directory); + return undef unless $succeeded; my $fh; - if (! $self->{'output_file'} eq '') { - $fh = $self->Texinfo::Common::open_out ($self->{'output_file'}); + my $encoded_output_file; + if (! $output_file eq '') { + my $path_encoding; + ($encoded_output_file, $path_encoding) + = $self->encoded_output_file_name($output_file); + my $error_message; + ($fh, $error_message) = Texinfo::Common::output_files_open_out( + $self->output_files_information(), $self, + $encoded_output_file); if (!$fh) { - $self->document_error(sprintf(__("could not open %s for writing: %s"), - $self->{'output_file'}, $!)); + $self->document_error($self, + sprintf(__("could not open %s for writing: %s"), + $output_file, $error_message)); return undef; } } - $self->_set_global_multiple_commands(-1); - my $encoding = ''; - if ($self->get_conf('OUTPUT_ENCODING_NAME') + if ($self->get_conf('OUTPUT_ENCODING_NAME') and $self->get_conf('OUTPUT_ENCODING_NAME') ne 'utf-8') { $encoding = " encoding=\"".$self->get_conf('OUTPUT_ENCODING_NAME')."\" "; } my $id; - if ($self->{'output_file'} ne '') { - my $output_filename = $self->{'output_filename'}; + if ($output_file ne '') { $id = " id=\"".$self->xml_protect_text($output_filename)."\""; } else { $id = ''; } + $self->{'lang_stack'} = []; + $self->{'in_skipped_node_top'} = 0; + my $lang = $DEFAULT_LANG; + $self->set_global_document_commands('preamble', ['documentlanguage']); + if (defined($self->get_conf('documentlanguage'))) { + $lang = $self->get_conf('documentlanguage'); + push @{$self->{'lang_stack'}}, $self->get_conf('documentlanguage'); + } else { + push @{$self->{'lang_stack'}}, ''; + } my $header = "<?xml version=\"1.0\"${encoding}?>".' -<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [ +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [ <!ENTITY tex "TeX"> <!ENTITY latex "LaTeX"> ]> -'. "<book${id} lang=\"".$self->get_conf('documentlanguage') ."\">\n"; +'. "<book${id} lang=\"$lang\">\n"; + + my $legalnotice; + if ($self->{'global_commands'}->{'copying'}) { + my $copying_element = $self->{'global_commands'}->{'copying'}; + my $copying_result + = $self->convert_tree({'contents' => $copying_element->{'contents'}}); + if ($copying_result ne '') { + $legalnotice = "<legalnotice>$copying_result</legalnotice>"; + } + } + + my $fulltitle_command; + foreach my $title_cmdname ('title', 'shorttitlepage', 'titlefont') { + if ($self->{'global_commands'}->{$title_cmdname}) { + my $command = $self->{'global_commands'}->{$title_cmdname}; + next if (!$command->{'args'} + or (!$command->{'args'}->[0]->{'contents'} + or $command->{'extra'}->{'missing_argument'})); + $fulltitle_command = $command; + last; + } + } + # get informations from the @titlepage. Since the fulltitle is gathered + # independently, only author and subtitle are gathered here. + my $subtitle_info = ''; + my $authors_info = ''; + if ($self->{'global_commands'}->{'titlepage'}) { + my $collected_commands = Texinfo::Common::collect_commands_list_in_tree( + $self->{'global_commands'}->{'titlepage'}, ['author', 'subtitle']); + + my @authors_elements; + my $subtitle_text = ''; + if (scalar(@{$collected_commands})) { + foreach my $element (@{$collected_commands}) { + my $cmdname = $element->{'cmdname'}; + if ($cmdname eq 'author') { + push @authors_elements, $element; + } elsif ($cmdname eq 'subtitle') { + # concatenate the text of @subtitle as DocBook only allows one. + my ($arg, $end_line) = $self->_convert_argument_and_end_line($element); + $subtitle_text .= $arg . $end_line + } + } + } + if ($subtitle_text ne '') { + chomp ($subtitle_text); + $subtitle_info = "<subtitle>$subtitle_text</subtitle>\n"; + } + + if (scalar(@authors_elements)) { + # using authorgroup and collab is the best, because it doesn't require + # knowing people name decomposition. Also it should work for group names. + # FIXME dblatex ignores collab/collabname. + $authors_info .= "<authorgroup>\n"; + foreach my $element (@authors_elements) { + my ($arg, $end_line) = $self->_convert_argument_and_end_line($element); + my $result = "<collab><collabname>$arg</collabname></collab>$end_line"; + chomp ($result); + $result .= "\n"; + $authors_info .= $result; + } + $authors_info .= "</authorgroup>\n"; + } + } + + my $settitle_command; + if ($self->{'global_commands'}->{'settitle'}) { + my $command = $self->{'global_commands'}->{'settitle'}; + $settitle_command = $command + unless (!$command->{'args'} + or (!$command->{'args'}->[0]->{'contents'} + or $command->{'extra'}->{'missing_argument'})); + + } + + my $titleabbrev_command; + if ($fulltitle_command) { + $titleabbrev_command = $settitle_command; + } elsif ($settitle_command) { + $fulltitle_command = $settitle_command; + } elsif (defined($legalnotice) and $self->{'global_commands'}->{'top'}) { + # if there is a legalnotice, we really want to have a title + # preceding it, so we also use @top + my $command = $self->{'global_commands'}->{'top'}; + $fulltitle_command = $command + unless (!$command->{'args'} + or (!$command->{'args'}->[0]->{'contents'} + or $command->{'extra'}->{'missing_argument'})); + } + + my $title_info = ''; + + if ($fulltitle_command) { + foreach my $element_command ([$fulltitle_command, 'title'], + [$titleabbrev_command, 'titleabbrev']) { + my ($element, $docbook_element) = @$element_command; + if (defined($element)) { + my ($arg, $end_line) = $self->_convert_argument_and_end_line($element); + my $result = "<$docbook_element>$arg</$docbook_element>$end_line"; + chomp ($result); + $result .= "\n"; + $title_info .= $result; + if ($docbook_element eq 'title') { + $title_info .= $subtitle_info; + } + } + } + } + $self->set_global_document_commands('before', ['documentlanguage']); + + my $document_info = ''; + $document_info .= $title_info . $authors_info; + $document_info .= $legalnotice if (defined($legalnotice)); + + # we duplicate title info, as it is explicitly said in the DocBook manual + # that it can be duplicated if exactly the same + $header .= $title_info; + + if ($document_info ne '') { + # FIXME DocBook 5 bookinfo->info + $header .= "<bookinfo>$document_info</bookinfo>\n"; + } + + %sectioning_commands_done = (); my $result = ''; - $result .= $self->_output_text($header, $fh); - $result .= $self->convert_document_sections($root, $fh); - $result .= $self->_output_text("</book>\n", $fh); - if ($fh and $self->{'output_file'} ne '-') { - $self->register_close_file($self->{'output_file'}); + $result .= $self->write_or_return($header, $fh); + $result .= $self->write_or_return($self->convert_tree($root), $fh); + $result .= $self->write_or_return("</book>\n", $fh); + if ($fh and $output_file ne '-') { + Texinfo::Common::output_files_register_closed( + $self->output_files_information(), $encoded_output_file); if (!close ($fh)) { - $self->document_error(sprintf(__("error on closing %s: %s"), - $self->{'output_file'}, $!)); + $self->document_error($self, + sprintf(__("error on closing %s: %s"), + $output_file, $!)); } } return $result; @@ -365,8 +526,12 @@ my %docbook_sections = ( 'unnumbered' => 'chapter', 'centerchap' => 'chapter', 'appendix' => 'appendix', - 'majorheading' => 'other', - 'chapheading' => 'other', + # chapter would have been the best choice here, but it is not a possibility + # for renderas in bridgehead (possibilities are sect1 to 5 and other). + # other is rendered small, smaller than sect1. So use sect1, even though + # it matches better with the @heading level. + 'majorheading' => 'sect1', + 'chapheading' => 'sect1', 'heading' => 'sect1', 'subheading' => 'sect2', 'subsubheading' => 'sect3', @@ -376,36 +541,39 @@ my %docbook_sections = ( ); my %docbook_special_unnumbered; -foreach my $special_unnumbered ('acknowledgements', 'colophon', +foreach my $special_unnumbered ('acknowledgements', 'colophon', 'dedication', 'preface') { $docbook_special_unnumbered{$special_unnumbered} = 1; } +# element is not texinfo tree element here, but xml element sub _docbook_section_element($$) { my $self = shift; - my $root = shift; - my $heading_level = $root->{'level'}; + my $element = shift; + + my $heading_level = $element->{'structure'}->{'section_level'}; if (exists $docbook_sections{$heading_level}) { return $docbook_sections{$heading_level}; } - my $command = $self->_level_corrected_section($root); - if ($command eq 'unnumbered' - and $root->{'extra'}->{'associated_node'} - and $root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'} - and $docbook_special_unnumbered{lc($root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'})}) { - return lc($root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}); + my $level_adjusted_cmdname + = Texinfo::Structuring::section_level_adjusted_command_name($element); + if ($level_adjusted_cmdname eq 'unnumbered' + and $element->{'extra'}->{'associated_node'} + and $element->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'} + and $docbook_special_unnumbered{lc($element->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'})}) { + return lc($element->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}); } - return $docbook_sections{$command}; + return $docbook_sections{$level_adjusted_cmdname}; } sub _index_entry($$) { my $self = shift; - my $root = shift; - if ($root->{'extra'} and $root->{'extra'}->{'index_entry'}) { - my $index_entry = $root->{'extra'}->{'index_entry'}; + my $element = shift; + if ($element->{'extra'} and $element->{'extra'}->{'index_entry'}) { + my $index_entry = $element->{'extra'}->{'index_entry'}; # FIXME DocBook 5 role->type my $result = "<indexterm role=\"$index_entry->{'index_name'}\">"; @@ -414,11 +582,11 @@ sub _index_entry($$) if ($index_entry->{'in_code'}); $result .= "<primary>"; - $result .= $self->_convert({'contents' => $index_entry->{'content'}}); + $result .= $self->_convert({'contents' => $index_entry->{'entry_content'}}); $result .= "</primary>"; # Add any index subentries. - my $tmp = $index_entry->{'command'}; + my $tmp = $index_entry->{'entry_element'}; my $level = "secondary"; while ($tmp->{'extra'}->{'subentry'}) { $result .= "<$level>"; @@ -427,14 +595,16 @@ sub _index_entry($$) $result .= "</$level>"; $level = "tertiary"; } - if ($index_entry->{'command'}->{'extra'}->{'seeentry'}) { + if ($index_entry->{'entry_element'}->{'extra'}->{'seeentry'}) { $result .= "<see>"; - $result .= $self->_convert({'contents' => $index_entry->{'command'}->{'extra'}->{'seeentry'}->{'args'}}); + $result .= $self->_convert({'contents' + => $index_entry->{'entry_element'}->{'extra'}->{'seeentry'}->{'args'}}); $result .= "</see>"; } - if ($index_entry->{'command'}->{'extra'}->{'seealso'}) { + if ($index_entry->{'entry_element'}->{'extra'}->{'seealso'}) { $result .= "<seealso>"; - $result .= $self->_convert({'contents' => $index_entry->{'command'}->{'extra'}->{'seealso'}->{'args'}}); + $result .= $self->_convert({'contents' + => $index_entry->{'entry_element'}->{'extra'}->{'seealso'}->{'args'}}); $result .= "</seealso>"; } @@ -446,29 +616,6 @@ sub _index_entry($$) return ''; } -sub docbook_accent($$$;$) -{ - my $self = shift; - my $text = shift; - my $command = shift; - my $in_upper_case = shift; - my $accent = $command->{'cmdname'}; - - if ($in_upper_case and $text =~ /^\w$/) { - $text = uc ($text); - } - if (exists($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}) - and exists($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}->{$text})) { - return '&#' . - hex($Texinfo::Convert::Unicode::unicode_accented_letters{$accent}->{$text}). ';'; - } - # FIXME it is not possible to call xml_protect_text since what is in $text - # may already be xml. But this means that each time ascii_accent changes - # it should be changed here too. - return $text . '<' if ($accent eq 'v'); - return Texinfo::Convert::Text::ascii_accent($text, $command); -} - sub _parse_attribute($) { my $element = shift; @@ -494,24 +641,14 @@ sub _protect_text($$) sub _convert_argument_and_end_line($$) { my $self = shift; - my $root = shift; - - my $converted = $self->convert_tree($root->{'args'}->[-1]); - - my $end_line = ''; + my $element = shift; - if ($root->{'extra'} and $root->{'extra'}->{'comment_at_end'}) { - $end_line = $self->convert_tree($root->{'extra'}->{'comment_at_end'}); - } else { - if (chomp($converted)) { - $end_line = "\n"; - } else { - $end_line = ""; - } - } + my $converted = $self->convert_tree($element->{'args'}->[-1]); + my $end_line = $self->format_comment_or_return_end_line($element); return ($converted, $end_line); } +my $debug_global_element_nr = 0; sub _convert($$;$); @@ -519,122 +656,122 @@ sub _convert($$;$); sub _convert($$;$) { my $self = shift; - my $root = shift; + my $element = shift; + my $debug_element_nr; #if (1) { - if (0) { # too verbose even for debugging, but for the bottom line ... - warn "root\n"; - warn " Command: $root->{'cmdname'}\n" if ($root->{'cmdname'}); - warn " Type: $root->{'type'}\n" if ($root->{'type'}); - warn " Text: $root->{'text'}\n" if (defined($root->{'text'})); - #warn " Special def_command: $root->{'extra'}->{'def_command'}\n" - # if (defined($root->{'extra'}) and $root->{'extra'}->{'def_command'}); + if (0) { # verbose even for debugging + $debug_element_nr = $debug_global_element_nr++; + print STDERR "element $debug_element_nr"; + print STDERR " cmd: $element->{'cmdname'}," if ($element->{'cmdname'}); + print STDERR " type: $element->{'type'}" if ($element->{'type'}); + my $text = $element->{'text'}; + if (defined($text)) { + $text =~ s/\n/\\n/; + print STDERR " text: $text"; + } + print STDERR "\n"; } - return '' if ($root->{'type'} and $ignored_types{$root->{'type'}}); + return '' if ($element->{'type'} and $ignored_types{$element->{'type'}}); my $result = ''; - if (defined($root->{'text'})) { - if (defined($root->{'type'}) and $root->{'type'} eq '_converted') { - return $root->{'text'}; + if (defined($element->{'text'})) { + if (defined($element->{'type'}) and $element->{'type'} eq '_converted') { + return $element->{'text'}; } elsif ($self->{'document_context'}->[-1]->{'raw'}) { - return $root->{'text'}; + return $element->{'text'}; } - $result = $root->{'text'}; + $result = $element->{'text'}; if ($self->{'document_context'}->[-1]->{'upper_case'}->[-1]) { $result = uc($result); } $result = $self->_protect_text($result); - if (! defined($root->{'type'}) or $root->{'type'} ne 'raw') { + if (! defined($element->{'type'}) or $element->{'type'} ne 'raw') { if (!$self->{'document_context'}->[-1]->{'monospace'}->[-1]) { - $result =~ s/``/$ldquo/g; - $result =~ s/\'\'/$rdquo/g; - $result =~ s/`/$lsquo/g; - $result =~ s/\'/$rsquo/g; - $result =~ s/---/$mdash/g; - $result =~ s/--/$ndash/g; + $result = $self->xml_format_text_with_numeric_entities($result); } } - #warn "had text `$root->{'text'}', returning $result\n"; + #warn "had text `$element->{'text'}', returning $result\n"; return $result; } #warn " onto main conditional\n"; - my @close_elements; - if ($root->{'cmdname'}) { - #warn " got cmdname $root->{'cmdname'}\n"; - if (defined($docbook_commands_formatting{$root->{'cmdname'}})) { - #warn " has commands_formatting \n"; + my @close_format_elements; + if ($element->{'cmdname'}) { + #warn " got cmdname $element->{'cmdname'}\n"; + if (defined($docbook_no_arg_commands_formatting{$element->{'cmdname'}})) { + #warn " has no_arg_commands_formatting \n"; my $command; - if ($root->{'cmdname'} eq 'click' - and $root->{'extra'} - and defined($root->{'extra'}->{'clickstyle'})) { - $command = $root->{'extra'}->{'clickstyle'}; + if ($element->{'cmdname'} eq 'click' + and $element->{'extra'} + and defined($element->{'extra'}->{'clickstyle'})) { + $command = $element->{'extra'}->{'clickstyle'}; } elsif ($self->{'document_context'}->[-1]->{'upper_case'}->[-1] - and $Texinfo::Common::letter_no_arg_commands{$root->{'cmdname'}} - and $Texinfo::Common::letter_no_arg_commands{uc($root->{'cmdname'})}) { - $command = uc($root->{'cmdname'}) + and $Texinfo::Commands::letter_no_arg_commands{$element->{'cmdname'}} + and $Texinfo::Commands::letter_no_arg_commands{uc($element->{'cmdname'})}) { + $command = uc($element->{'cmdname'}) } else { - $command = $root->{'cmdname'}; + $command = $element->{'cmdname'}; } - if ($self->{'translated_commands'}->{$command}) { - return $self->_convert(Texinfo::Common::translated_command_tree($self, - $command)); + my $translated_tree = Texinfo::Convert::Utils::translated_command_tree($self, + $command); + if ($translated_tree) { + return $self->_convert($translated_tree); } else { - return $docbook_commands_formatting{$command}; + return $docbook_no_arg_commands_formatting{$command}; } - } elsif ($root->{'cmdname'} eq 'today') { - return $self->_convert(Texinfo::Common::expand_today($self)); - } elsif ($Texinfo::Common::accent_commands{$root->{'cmdname'}}) { - return $self->convert_accents($root, \&docbook_accent, + } elsif ($element->{'cmdname'} eq 'today') { + return $self->_convert(Texinfo::Convert::Utils::expand_today($self)); + } elsif ($Texinfo::Commands::accent_commands{$element->{'cmdname'}}) { + return $self->xml_accents($element, $self->{'document_context'}->[-1]->{'upper_case'}->[-1]); - } elsif ($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx' - or $root->{'cmdname'} eq 'headitem' or $root->{'cmdname'} eq 'tab') { - if ($root->{'cmdname'} eq 'item' - and $root->{'parent'}->{'cmdname'} - and ($root->{'parent'}->{'cmdname'} eq 'itemize' - or $root->{'parent'}->{'cmdname'} eq 'enumerate')) { + } elsif ($element->{'cmdname'} eq 'item' or $element->{'cmdname'} eq 'itemx' + or $element->{'cmdname'} eq 'headitem' or $element->{'cmdname'} eq 'tab') { + if ($element->{'cmdname'} eq 'item' + and $element->{'parent'}->{'cmdname'} + and ($element->{'parent'}->{'cmdname'} eq 'itemize' + or $element->{'parent'}->{'cmdname'} eq 'enumerate')) { $result .= "<listitem>"; - if ($root->{'parent'}->{'cmdname'} eq 'itemize' - and $root->{'parent'}->{'extra'} - and !($root->{'parent'}->{'extra'}->{'command_as_argument'} - and $root->{'parent'}->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') - and $root->{'parent'}->{'args'} - and $root->{'parent'}->{'args'}->[0]) { + if ($element->{'parent'}->{'cmdname'} eq 'itemize' + and $element->{'parent'}->{'extra'} + and !($element->{'parent'}->{'extra'}->{'command_as_argument'} + and $element->{'parent'}->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') + and $element->{'parent'}->{'args'} + and $element->{'parent'}->{'args'}->[0]) { $self->{'pending_prepend'} - = $self->_convert($root->{'parent'}->{'args'}->[0]); + = $self->_convert($element->{'parent'}->{'args'}->[0]); $self->{'pending_prepend'} .= " "; } - push @close_elements, 'listitem'; - } elsif (($root->{'cmdname'} eq 'item' or $root->{'cmdname'} eq 'itemx') - and $root->{'parent'}->{'type'} - and $root->{'parent'}->{'type'} eq 'table_term') { + push @close_format_elements, 'listitem'; + } elsif (($element->{'cmdname'} eq 'item' or $element->{'cmdname'} eq 'itemx') + and $element->{'parent'}->{'type'} + and $element->{'parent'}->{'type'} eq 'table_term') { - my $converted_tree = $self->_table_item_content_tree($root, - $root->{'args'}->[0]->{'contents'}); + my $table_item_tree = $self->table_item_content_tree($element, + $element->{'args'}->[0]->{'contents'}); $result .= "<term>"; - $result .= $self->_index_entry($root); - $result .= $self->_convert($converted_tree); + $result .= $self->_index_entry($element); + $result .= $self->_convert($table_item_tree); chomp ($result); $result .= "\n"; $result .= "</term>"; } else { - unless (($root->{'cmdname'} eq 'item' - or $root->{'cmdname'} eq 'headitem' - or $root->{'cmdname'} eq 'tab') - and $root->{'parent'}->{'type'} - and $root->{'parent'}->{'type'} eq 'row') { + unless (($element->{'cmdname'} eq 'item' + or $element->{'cmdname'} eq 'headitem' + or $element->{'cmdname'} eq 'tab') + and $element->{'parent'}->{'type'} + and $element->{'parent'}->{'type'} eq 'row') { warn "BUG: multitable cell command not in a row " - .Texinfo::Common::_print_current($root); + .Texinfo::Common::debug_print_element($element); } - $result .= "<entry>"; - push @close_elements, 'entry'; + push @close_format_elements, 'entry'; } - } elsif ($root->{'type'} and $root->{'type'} eq 'index_entry_command') { + } elsif ($element->{'type'} and $element->{'type'} eq 'index_entry_command') { my $end_line; - if ($root->{'args'}->[0]) { - $end_line = $self->_end_line_or_comment($root); + if ($element->{'args'}->[0]) { + $end_line = $self->format_comment_or_return_end_line($element); if ($self->{'document_context'}->[-1]->{'in_preformatted'}) { chomp($end_line); } @@ -642,165 +779,205 @@ sub _convert($$;$) # May that happen? $end_line = ''; } - return $self->_index_entry($root).${end_line}; - - } elsif (exists($docbook_misc_commands{$root->{'cmdname'}})) { - #warn " is dbk misc command\n"; - if ($docbook_global_commands{$root->{'cmdname'}}) { - $self->_informative_command($root); + return $self->_index_entry($element).${end_line}; + + } elsif (exists($docbook_nobrace_commands{$element->{'cmdname'}})) { + return ''; + } elsif (exists($docbook_line_commands{$element->{'cmdname'}})) { + #warn " is dbk line command\n"; + if ($docbook_global_commands{$element->{'cmdname'}}) { + Texinfo::Common::set_informative_command_value($self, $element); return ''; } - my $command; - if (exists ($docbook_misc_elements_with_arg_map{$root->{'cmdname'}})) { - $command = $docbook_misc_elements_with_arg_map{$root->{'cmdname'}}; - } - my $type = $docbook_misc_commands{$root->{'cmdname'}}; - if ($type eq 'text') { - if ($root->{'cmdname'} eq 'verbatiminclude') { - my $verbatim_include_verbatim - = Texinfo::Common::expand_verbatiminclude($self, $root); - if (defined($verbatim_include_verbatim)) { - $result .= $self->_convert($verbatim_include_verbatim); - } else { - return ''; - } - } else { - return ''; - } - } elsif ($type eq 'line') { - if ($root->{'cmdname'} eq 'node') { - if ($root->{'extra'} and !$root->{'extra'}->{'associated_section'} - and defined($root->{'extra'}->{'normalized'})) { - $result .= "<anchor id=\"$root->{'extra'}->{'normalized'}\"/>\n"; + if ($Texinfo::Commands::root_commands{$element->{'cmdname'}}) { + if ($self->get_conf('NO_TOP_NODE_OUTPUT')) { + my $node_element; + if ($element->{'cmdname'} eq 'node') { + $node_element = $element; + } elsif ($element->{'cmdname'} eq 'part' and $element->{'extra'} + and $element->{'extra'}->{'part_following_node'}) { + $node_element = $element->{'extra'}->{'part_following_node'}; } - } elsif ($Texinfo::Common::root_commands{$root->{'cmdname'}}) { - my $attribute = ''; - # FIXME it is not clear that a label should be set for - # @appendix* or @chapter/@*section as the formatter should be - # able to figure it out. For @unnumbered or if ! NUMBER_SECTIONS - # having a label (empty) is important. - my $label = ''; - if (defined($root->{'number'}) - and ($self->get_conf('NUMBER_SECTIONS') - or !defined($self->get_conf('NUMBER_SECTIONS')))) { - # Looking at docbook2html output, Appendix is appended in the - # section title, so only the letter is used. - $label = $root->{'number'}; - } - $command = $self->_docbook_section_element($root); - if (! $docbook_special_unnumbered{$command}) { - $attribute = " label=\"$label\""; + if ($node_element or $element->{'cmdname'} eq 'part') { + # $node_element->{'extra'}->{'normalized'} not defined happens for + # empty nodes + if ($node_element and $node_element->{'extra'} + and $node_element->{'extra'}->{'normalized'} + and $node_element->{'extra'}->{'normalized'} eq 'Top') { + $self->{'in_skipped_node_top'} = 1; + } elsif (defined($self->{'in_skipped_node_top'}) + and $self->{'in_skipped_node_top'} == 1) { + $self->{'in_skipped_node_top'} = -1; + } } - if ($root->{'extra'} and $root->{'extra'}->{'associated_node'}) { - $attribute .= " id=\"$root->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}\""; + } + if ($element->{'cmdname'} eq 'node' + and (not $element->{'extra'} + or not $element->{'extra'}->{'associated_section'})) { + if ($element->{'extra'} and defined($element->{'extra'}->{'normalized'})) { + $result .= "<anchor id=\"$element->{'extra'}->{'normalized'}\"/>\n"; } - $result .= "<$command${attribute}>\n"; - if ($root->{'args'} and $root->{'args'}->[0]) { - my ($arg, $end_line) = $self->_convert_argument_and_end_line($root); - $result .= "<title>$arg</title>$end_line"; - chomp ($result); - $result .= "\n"; + } else { + # start the section at the associated node or part, or at the sectioning + # command if there is no associated node nor part + my $section_element; + my $part; + if ($element->{'cmdname'} eq 'node') { + $section_element = $element->{'extra'}->{'associated_section'}; + } elsif ($element->{'cmdname'} eq 'part') { + $part = $element; + if ($element->{'extra'}->{'part_associated_section'}) { + $section_element = $element->{'extra'}->{'part_associated_section'}; + } + } else { + $section_element = $element; } - if ($command eq 'part' and !Texinfo::Common::is_content_empty($root)) { - $result .= "<partintro>\n"; + if ($section_element and $section_element->{'extra'} + and $section_element->{'extra'}->{'associated_part'}) { + $part = $section_element->{'extra'}->{'associated_part'}; } - } elsif ($Texinfo::Common::sectioning_commands{$root->{'cmdname'}}) { - if ($root->{'args'} and $root->{'args'}->[0]) { - my ($arg, $end_line) = $self->_convert_argument_and_end_line($root); - $result .= - "<bridgehead renderas=\"$docbook_sections{$root->{'cmdname'}}\">$arg</bridgehead>$end_line"; - chomp ($result); - $result .= "\n"; - return $result; + my @opened_elements; + # we need to check if the section was already done in case there is + # both a node and a part, we do not know in which order they appear. + if (not ($section_element + and $sectioning_commands_done{$section_element})) { + push @opened_elements, $part if $part; + if ($section_element) { + push @opened_elements, $section_element; + } } - return ''; - } else { - my $attribute = ''; - if (defined($command)) { - my ($arg, $end_line) - = $self->_convert_argument_and_end_line($root); - if ($command eq '') { - $result .= "$arg$end_line"; + foreach my $opened_element (@opened_elements) { + if (not (defined($self->{'in_skipped_node_top'}) + and $self->{'in_skipped_node_top'} == 1)) { + if ($section_element and $opened_element eq $section_element) { + $sectioning_commands_done{$section_element} = 1; + } + my $section_attribute = ''; + # FIXME it is not clear that a label should be set for + # @appendix* or @chapter/@*section as the formatter should be + # able to figure it out. For @unnumbered or if ! NUMBER_SECTIONS + # having a label (empty) is important. + my $label = ''; + if (defined($opened_element->{'structure'}->{'section_number'}) + and ($self->get_conf('NUMBER_SECTIONS') + or !defined($self->get_conf('NUMBER_SECTIONS')))) { + # Looking at docbook2html output, Appendix is appended in the + # section title, so only the letter is used. + $label = $opened_element->{'structure'}->{'section_number'}; + } + my $docbook_sectioning_element + = $self->_docbook_section_element($opened_element); + if (! $docbook_special_unnumbered{$docbook_sectioning_element}) { + $section_attribute .= " label=\"$label\""; + } + if ($opened_element->{'extra'} and $opened_element->{'extra'}->{'associated_node'}) { + $section_attribute + .= " id=\"$opened_element->{'extra'}->{'associated_node'}->{'extra'}->{'normalized'}\""; + } + my $language = ''; + if (defined($self->get_conf('documentlanguage'))) { + $language = $self->get_conf('documentlanguage'); + if ($self->{'lang_stack'}->[-1] ne $language) { + $section_attribute .= ' lang="'.$language.'"'; + } + } + push @{$self->{'lang_stack'}}, $language; + $result .= "<$docbook_sectioning_element${section_attribute}>\n"; + if ($opened_element->{'args'} and $opened_element->{'args'}->[0]) { + my ($arg, $end_line) = $self->_convert_argument_and_end_line($opened_element); + $result .= "<title>$arg</title>$end_line"; + chomp ($result); + $result .= "\n"; + } + # if associated with a sectioning element, the part is opened before the + # sectioning element, such that the part content appears after the sectioning + # command opening, no need for partintro. + if ($docbook_sectioning_element eq 'part' + and not ($opened_element->{'extra'} + and $opened_element->{'extra'}->{'part_associated_section'}) + and !Texinfo::Common::is_content_empty($opened_element)) { + $result .= "<partintro>\n"; + } } else { - $result .= "<$command${attribute}>$arg</$command>$end_line"; + push @{$self->{'lang_stack'}}, $self->{'lang_stack'}->[-1]; } - chomp ($result); - $result .= "\n"; - return $result; } - # misc commands not handled especially are ignored here. - return ''; } - } elsif ($type eq 'skipline' or $type eq 'noarg') { - if ($root->{'cmdname'} eq 'insertcopying') { - if ($self->{'extra'} and $self->{'extra'}->{'copying'}) { - return $self->_convert({'contents' - => $self->{'extra'}->{'copying'}->{'contents'}}); - } else { - return ''; - } - } else { - return ''; + } elsif ($element->{'cmdname'} eq 'c' or $element->{'cmdname'} eq 'comment') { + return $self->xml_comment($element->{'args'}->[0]->{'text'}) + } elsif ($Texinfo::Commands::sectioning_heading_commands{$element->{'cmdname'}}) { + if ($element->{'args'} and $element->{'args'}->[0]) { + my ($arg, $end_line) = $self->_convert_argument_and_end_line($element); + $result .= + "<bridgehead renderas=\"$docbook_sections{$element->{'cmdname'}}\">$arg</bridgehead>$end_line"; + chomp ($result); + $result .= "\n"; + return $result; } - } elsif ($type eq 'special' or $type eq 'skipspace') { return ''; - } elsif ($type eq 'lineraw') { - if ($root->{'cmdname'} eq 'c' or $root->{'cmdname'} eq 'comment') { - return $self->xml_comment($root->{'args'}->[0]->{'text'}) + } elsif (exists ($docbook_line_elements_with_arg_map{$element->{'cmdname'}})) { + my ($docbook_element, $attribute_text) + = _parse_attribute($docbook_line_elements_with_arg_map{$element->{'cmdname'}}); + my ($arg, $end_line) + = $self->_convert_argument_and_end_line($element); + if ($docbook_element eq '') { + $result .= "$arg$end_line"; } else { - return ""; + $result .= "<$docbook_element${attribute_text}>$arg</$docbook_element>$end_line"; } - } else { - $self->_bug_message("unknown misc_command style $type", $root) - if ($type !~ /^\d$/); - if ($root->{'cmdname'} eq 'printindex') { - if (defined($root->{'extra'}) - and defined($root->{'extra'}->{'misc_args'})) { - # FIXME DocBook 5 - #return "<index type=\"$root->{'extra'}->{'misc_args'}->[0]\"></index>\n"; - return "<index role=\"$root->{'extra'}->{'misc_args'}->[0]\"></index>\n"; - } else { - return "<index></index>\n"; - } + chomp ($result); + $result .= "\n"; + return $result; + } elsif ($element->{'cmdname'} eq 'insertcopying') { + if ($self->{'global_commands'} + and $self->{'global_commands'}->{'copying'}) { + return $self->_convert({'contents' + => $self->{'global_commands'}->{'copying'}->{'contents'}}); } else { return ''; } + } elsif ($element->{'cmdname'} eq 'verbatiminclude') { + my $verbatim_include_verbatim + = Texinfo::Convert::Utils::expand_verbatiminclude($self, $self, $element); + if (defined($verbatim_include_verbatim)) { + $result .= $self->_convert($verbatim_include_verbatim); + } else { + return ''; + } + } elsif ($element->{'cmdname'} eq 'printindex') { + if (defined($element->{'extra'}) + and defined($element->{'extra'}->{'misc_args'})) { + # FIXME DocBook 5 + #return "<index type=\"$element->{'extra'}->{'misc_args'}->[0]\"></index>\n"; + return "<index role=\"$element->{'extra'}->{'misc_args'}->[0]\"></index>\n"; + } else { + return "<index></index>\n"; + } + } else { + # ignore all the other line commands + return ''; } - - } elsif ($root->{'type'} - and $root->{'type'} eq 'definfoenclose_command') { - my $in_monospace_not_normal; - if (defined($default_args_code_style{$root->{'cmdname'}}) - and $default_args_code_style{$root->{'cmdname'}}->[0]) { - $in_monospace_not_normal = 1; - } elsif ($regular_font_style_commands{$root->{'cmdname'}}) { - $in_monospace_not_normal = 0; - } - push @{$self->{'document_context'}->[-1]->{'monospace'}}, - $in_monospace_not_normal - if (defined($in_monospace_not_normal)); - my $arg = $self->_convert($root->{'args'}->[0]); - $result .= $self->xml_protect_text($root->{'extra'}->{'begin'}).$arg - .$self->xml_protect_text($root->{'extra'}->{'end'}); - pop @{$self->{'document_context'}->[-1]->{'monospace'}} - if (defined($in_monospace_not_normal)); - + } elsif ($element->{'type'} + and $element->{'type'} eq 'definfoenclose_command') { + my $arg = $self->_convert($element->{'args'}->[0]); + $result .= $arg; - } elsif ($root->{'args'} - and exists($Texinfo::Common::brace_commands{$root->{'cmdname'}})) { - #debug_list (" brace command with args", $root->{'args'}); - if ($style_commands_formatting{$root->{'cmdname'}}) { - if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) { + } elsif ($element->{'args'} + and exists($Texinfo::Commands::brace_commands{$element->{'cmdname'}})) { + #Texinfo::Common::debug_list(" brace command with args", $element->{'args'}); + if ($style_commands_formatting{$element->{'cmdname'}}) { + if ($Texinfo::Commands::brace_commands{$element->{'cmdname'}} eq 'context') { push (@{$self->{'document_context'}}, {'monospace' => [0], 'upper_case' => [0]}); } - my $formatting = $style_commands_formatting{$root->{'cmdname'}}; + my $formatting = $style_commands_formatting{$element->{'cmdname'}}; my $in_monospace_not_normal; - if (defined($default_args_code_style{$root->{'cmdname'}}) - and $default_args_code_style{$root->{'cmdname'}}->[0]) { + if (defined($default_args_code_style{$element->{'cmdname'}}) + and $default_args_code_style{$element->{'cmdname'}}->[0]) { $in_monospace_not_normal = 1; - } elsif ($regular_font_style_commands{$root->{'cmdname'}}) { + } elsif ($brace_commands{$element->{'cmdname'}} + and $brace_commands{$element->{'cmdname'}} eq 'style_no_code') { $in_monospace_not_normal = 0; } if ($formatting->{'upper_case'}) { @@ -811,11 +988,11 @@ sub _convert($$;$) if (defined($in_monospace_not_normal)); my ($style, $attribute_text) = _parse_attribute($formatting->{'attribute'}); - my $result = $self->_convert($root->{'args'}->[0]); + my $result = $self->_convert($element->{'args'}->[0]); if ($style ne '' and (!$self->{'document_context'}->[-1]->{'inline'} or $inline_elements{$style})) { $result = "<$style${attribute_text}>$result</$style>"; - if ($root->{'cmdname'} eq 'math') { + if ($element->{'cmdname'} eq 'math') { $result = "<inlineequation>$result</inlineequation>"; } } @@ -828,167 +1005,214 @@ sub _convert($$;$) } pop @{$self->{'document_context'}->[-1]->{'monospace'}} if (defined($in_monospace_not_normal)); - if ($Texinfo::Common::context_brace_commands{$root->{'cmdname'}}) { + if ($Texinfo::Commands::brace_commands{$element->{'cmdname'}} eq 'context') { pop @{$self->{'document_context'}}; } - if ($root->{'cmdname'} eq 'w') { + if ($element->{'cmdname'} eq 'w') { $result .= $w_command_mark; } return $result; - } elsif ($root->{'cmdname'} eq 'anchor') { - if ($root->{'extra'} and defined($root->{'extra'}->{'normalized'})) { - return "<anchor id=\"$root->{'extra'}->{'normalized'}\"/>"; + } elsif ($element->{'cmdname'} eq 'anchor') { + if ($element->{'extra'} and defined($element->{'extra'}->{'normalized'})) { + return "<anchor id=\"$element->{'extra'}->{'normalized'}\"/>"; } else { return ''; } - } elsif ($Texinfo::Common::ref_commands{$root->{'cmdname'}}) { - if ($root->{'args'}) { - if ($root->{'cmdname'} eq 'inforef') { - my $filename; - if (scalar(@{$root->{'args'}}) == 3 - and defined($root->{'args'}->[-1]) and @{$root->{'args'}->[-1]->{'contents'}}) { - $filename - = $self->xml_protect_text(Texinfo::Convert::Text::convert( - {'contents' => $root->{'args'}->[-1]->{'contents'}}, - {'code' => 1, Texinfo::Common::_convert_text_options($self)})); - } - my $node; - if (defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { - $node = {'contents' - => $root->{'args'}->[0]->{'contents'}}; - } - if ($node and defined($filename)) { - return $self->_convert($self->gdt( - "See Info file \@file{{myfile}}, node \@samp{{mynode}}", - { 'myfile' => {'type' => '_converted', 'text' => $filename}, - 'mynode' => $node})); - } elsif ($node) { - return $self->_convert($self->gdt( - "See node \@samp{{mynode}}", - {'mynode' => $node})); - } elsif (defined($filename)) { - return $self->_convert($self->gdt( - "See Info file \@file{{myfile}}", - { 'myfile' => {'type' => '_converted', 'text' => $filename}})); - } - #my $name; - #if (scalar(@{$root->{'args'}}) >= 2 - # and defined($root->{'args'}->[1]) and @{$root->{'args'}->[1]->{'contents'}}) { - # $name = $self->_convert({'contents' - # => $root->{'args'}->[0]->{'contents'}}); - #} + } elsif ($Texinfo::Commands::ref_commands{$element->{'cmdname'}}) { + if ($element->{'args'}) { + my $cmdname; + my $book_contents; + my ($section_name, $node_name); + my $manual_file_index = 3; + if ($element->{'cmdname'} eq 'inforef') { + $manual_file_index = 2; + $cmdname = 'xref'; } else { - my $book_contents; - if (scalar(@{$root->{'args'}}) == 5 - and defined($root->{'args'}->[-1]) and @{$root->{'args'}->[-1]->{'contents'}}) { - $book_contents = $root->{'args'}->[-1]->{'contents'}; - } - my $manual_file_contents; - if (scalar(@{$root->{'args'}}) >= 4 - and defined($root->{'args'}->[3]) and @{$root->{'args'}->[3]->{'contents'}}) { - $manual_file_contents = $root->{'args'}->[3]->{'contents'}; + if (scalar(@{$element->{'args'}}) == 5 + and defined($element->{'args'}->[-1]) + and @{$element->{'args'}->[-1]->{'contents'}}) { + $book_contents = $element->{'args'}->[-1]->{'contents'}; } - my ($section_name_contents, $section_name); - if (defined($root->{'args'}->[2]) and @{$root->{'args'}->[2]->{'contents'}}) { - $section_name_contents - = $root->{'args'}->[2]->{'contents'}; - $section_name = $self->_convert( - {'contents' => $section_name_contents}); - } elsif (defined($root->{'args'}->[1]) and @{$root->{'args'}->[1]->{'contents'}}) { - $section_name_contents - = $root->{'args'}->[1]->{'contents'}; - $section_name = $self->_convert( - {'contents' => $section_name_contents}); - } elsif (defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { - $section_name_contents - = $root->{'args'}->[0]->{'contents'}; + if (defined($element->{'args'}->[2]) + and $element->{'args'}->[2]->{'contents'} + and @{$element->{'args'}->[2]->{'contents'}}) { + my $section_name_contents = $element->{'args'}->[2]->{'contents'}; $section_name = $self->_convert( - {'contents' => $section_name_contents}); - - if ($book_contents - and !$root->{'extra'}->{'node_argument'}->{'manual_content'}) { - if ($section_name eq 'Top') { - $section_name = undef; - $section_name_contents = undef; - } - } - # Note: it would be nice to re-use $section_name instead of - # having 'gdt' convert $section_name_contents again, but - # there isn't a good way to pass an already-converted string - # into 'gdt'. + {'contents' => $section_name_contents}); } + $cmdname = $element->{'cmdname'}; + } + my $manual_file_contents; + if (scalar(@{$element->{'args'}}) >= $manual_file_index+1 + and defined($element->{'args'}->[$manual_file_index]) + and $element->{'args'}->[$manual_file_index]->{'contents'} + and @{$element->{'args'}->[$manual_file_index]->{'contents'}}) { + $manual_file_contents = $element->{'args'}->[$manual_file_index]->{'contents'}; + } + if (! defined($section_name) and defined($element->{'args'}->[1]) + and $element->{'args'}->[1]->{'contents'} + and @{$element->{'args'}->[1]->{'contents'}}) { + my $section_name_contents + = $element->{'args'}->[1]->{'contents'}; + $section_name = $self->_convert( + {'contents' => $section_name_contents}); + } elsif (defined($element->{'args'}->[0]) + and $element->{'args'}->[0]->{'contents'} + and @{$element->{'args'}->[0]->{'contents'}}) { + my $node_contents + = $element->{'args'}->[0]->{'contents'}; + $node_name = $self->_convert( + {'contents' => $node_contents}); + + if (($book_contents or $manual_file_contents) + and $node_name eq 'Top') { + $node_name = undef; + } + } - # external ref - if ($book_contents or $manual_file_contents) { - return '' if (!$book_contents); - if ($section_name_contents) { - if ($root->{'cmdname'} eq 'ref') { - return $self->_convert( - $self->gdt('section ``{section_name}\'\' in @cite{{book}}', - { 'section_name' => {'contents' => $section_name_contents}, - 'book' => $book_contents })); - } elsif ($root->{'cmdname'} eq 'xref') { - return $self->_convert( - $self->gdt('See section ``{section_name}\'\' in @cite{{book}}', - { 'section_name' => {'contents' => $section_name_contents}, - 'book' => $book_contents })); - } elsif ($root->{'cmdname'} eq 'pxref') { - return $self->_convert( - $self->gdt('see section ``{section_name}\'\' in @cite{{book}}', - { 'section_name' => {'contents' => $section_name_contents}, - 'book' => $book_contents })); - } - } else { - if ($root->{'cmdname'} eq 'ref') { - return $self->_convert( - $self->gdt('@cite{{book}}', - {'book' => $book_contents })); - } elsif ($root->{'cmdname'} eq 'xref') { - return $self->_convert( - $self->gdt('See @cite{{book}}', - {'book' => $book_contents })); - } elsif ($root->{'cmdname'} eq 'pxref') { - return $self->_convert( - $self->gdt('see @cite{{book}}', - {'book' => $book_contents })); - } + # external book ref + if ($book_contents) { + if ($section_name) { + if ($cmdname eq 'ref') { + return $self->_convert( + $self->gdt('section ``{section_name}\'\' in @cite{{book}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'book' => $book_contents })); + } elsif ($cmdname eq 'xref') { + return $self->_convert( + $self->gdt('See section ``{section_name}\'\' in @cite{{book}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'book' => $book_contents })); + } elsif ($cmdname eq 'pxref') { + return $self->_convert( + $self->gdt('see section ``{section_name}\'\' in @cite{{book}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'book' => $book_contents })); + } + } elsif ($node_name) { + if ($cmdname eq 'ref') { + return $self->_convert( + $self->gdt('``{node_name}\'\' in @cite{{book}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'book' => $book_contents })); + } elsif ($cmdname eq 'xref') { + return $self->_convert( + $self->gdt('See ``{node_name}\'\' in @cite{{book}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'book' => $book_contents })); + } elsif ($cmdname eq 'pxref') { + return $self->_convert( + $self->gdt('see ``{node_name}\'\' in @cite{{book}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'book' => $book_contents })); } } else { - my $linkend = ''; - if ($root->{'extra'}->{'node_argument'} - and defined($root->{'extra'}->{'node_argument'}->{'normalized'}) - and !$root->{'extra'}->{'node_argument'}->{'manual_content'}) { - $linkend = " linkend=\"$root->{'extra'}->{'node_argument'}->{'normalized'}\""; + if ($cmdname eq 'ref') { + return $self->_convert( + $self->gdt('@cite{{book}}', + {'book' => $book_contents })); + } elsif ($cmdname eq 'xref') { + return $self->_convert( + $self->gdt('See @cite{{book}}', + {'book' => $book_contents })); + } elsif ($cmdname eq 'pxref') { + return $self->_convert( + $self->gdt('see @cite{{book}}', + {'book' => $book_contents })); } - my $argument = "<link${linkend}>".$section_name."</link>"; - if ($root->{'cmdname'} eq 'ref') { + } + } elsif ($manual_file_contents) { + if ($section_name) { + if ($cmdname eq 'ref') { return $self->_convert( - $self->gdt('{title_ref}', {'title_ref' => - {'type' => '_converted', - 'text' => $argument}})); - } elsif ($root->{'cmdname'} eq 'xref') { + $self->gdt('section ``{section_name}\'\' in @file{{manual}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'xref') { return $self->_convert( - $self->gdt('See {title_ref}', {'title_ref' => - {'type' => '_converted', - 'text' => $argument}})); - } elsif ($root->{'cmdname'} eq 'pxref') { + $self->gdt('See section ``{section_name}\'\' in @file{{manual}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'pxref') { return $self->_convert( - $self->gdt('see {title_ref}', {'title_ref' => - {'type' => '_converted', - 'text' => $argument}})); + $self->gdt('see section ``{section_name}\'\' in @file{{manual}}', + { 'section_name' => {'type' => '_converted', 'text' => $section_name}, + 'manual' => $manual_file_contents })); } + } elsif ($node_name) { + if ($cmdname eq 'ref') { + return $self->_convert( + $self->gdt('``{node_name}\'\' in @file{{manual}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'xref') { + return $self->_convert( + $self->gdt('See ``{node_name}\'\' in @file{{manual}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'pxref') { + return $self->_convert( + $self->gdt('see ``{node_name}\'\' in @file{{manual}}', + { 'node_name' => {'type' => '_converted', 'text' => $node_name}, + 'manual' => $manual_file_contents })); + } + } else { + if ($cmdname eq 'ref') { + return $self->_convert( + $self->gdt('@file{{manual}}', + {'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'xref') { + return $self->_convert( + $self->gdt('See @file{{manual}}', + {'manual' => $manual_file_contents })); + } elsif ($cmdname eq 'pxref') { + return $self->_convert( + $self->gdt('see @file{{manual}}', + {'manual' => $manual_file_contents })); + } + } + } elsif ($element->{'cmdname'} eq 'inforef') { + return ''; + } else { + my $linkend = ''; + if ($element->{'extra'} + and $element->{'extra'}->{'node_argument'} + and defined($element->{'extra'}->{'node_argument'}->{'normalized'}) + and !$element->{'extra'}->{'node_argument'}->{'manual_content'}) { + $linkend = " linkend=\"$element->{'extra'}->{'node_argument'}->{'normalized'}\""; + } + my $link_text = $section_name; + $link_text = $node_name if (! defined($link_text)); + my $argument = "<link${linkend}>".$link_text."</link>"; + if ($element->{'cmdname'} eq 'ref') { + return $self->_convert( + $self->gdt('{title_ref}', {'title_ref' => + {'type' => '_converted', + 'text' => $argument}})); + } elsif ($element->{'cmdname'} eq 'xref') { + return $self->_convert( + $self->gdt('See {title_ref}', {'title_ref' => + {'type' => '_converted', + 'text' => $argument}})); + } elsif ($element->{'cmdname'} eq 'pxref') { + return $self->_convert( + $self->gdt('see {title_ref}', {'title_ref' => + {'type' => '_converted', + 'text' => $argument}})); } } } else { return ''; } - } elsif ($root->{'cmdname'} eq 'image') { - if (defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { - my $basefile = Texinfo::Convert::Text::convert( - {'contents' => $root->{'args'}->[0]->{'contents'}}, - {'code' => 1, Texinfo::Common::_convert_text_options($self)}); - my $element; - my $is_inline = $self->_is_inline($root); + } elsif ($element->{'cmdname'} eq 'image') { + if (defined($element->{'args'}->[0]) + and $element->{'args'}->[0]->{'contents'} + and @{$element->{'args'}->[0]->{'contents'}}) { + my $basefile = Texinfo::Convert::Text::convert_to_text( + {'contents' => $element->{'args'}->[0]->{'contents'}}, + {'code' => 1, + Texinfo::Convert::Text::copy_options_for_convert_text($self, 1)}); + my $is_inline = Texinfo::Common::element_is_inline($element); if ($is_inline) { $result .= "<inlinemediaobject>"; } else { @@ -996,7 +1220,9 @@ sub _convert($$;$) } my @files; foreach my $extension (@docbook_image_extensions) { - if ($self->Texinfo::Common::locate_include_file ("$basefile.$extension")) { + my ($file_name, $file_name_encoding) + = $self->encoded_input_file_name("$basefile.$extension"); + if ($self->Texinfo::Common::locate_include_file($file_name)) { push @files, ["$basefile.$extension", uc($extension)]; } } @@ -1009,17 +1235,16 @@ sub _convert($$;$) .$self->xml_protect_text($file->[0]) ."\" format=\"$file->[1]\"></imagedata></imageobject>"; } - my ($image_text, $image_width) - = $self->Texinfo::Convert::Plaintext::_image_text($root, $basefile); + my ($image_text, $image_width) = $self->txt_image_text($element, $basefile); if (defined($image_text)) { $result .= "<textobject><literallayout>" .$self->_protect_text($image_text) .'</literallayout></textobject>'; } if (!defined($image_text) and !$image_file_found) { - $self->line_warn(sprintf( + $self->line_warn($self, sprintf( __("\@image file `%s' not found, using `%s'"), - $basefile, "$basefile.jpg"), $root->{'line_nr'}); + $basefile, "$basefile.jpg"), $element->{'source_info'}); } if ($is_inline) { @@ -1028,22 +1253,26 @@ sub _convert($$;$) $result .= "</mediaobject></informalfigure>"; } } - } elsif ($root->{'cmdname'} eq 'email') { - if ($root->{'args'}) { + } elsif ($element->{'cmdname'} eq 'email') { + if ($element->{'args'}) { my $name; my $email; my $email_text; - if (scalar(@{$root->{'args'}}) == 2 - and defined($root->{'args'}->[-1]) and @{$root->{'args'}->[-1]->{'contents'}}) { - $name = $root->{'args'}->[1]->{'contents'}; + if (scalar(@{$element->{'args'}}) == 2 + and defined($element->{'args'}->[-1]) + and $element->{'args'}->[-1]->{'contents'} + and @{$element->{'args'}->[-1]->{'contents'}}) { + $name = $element->{'args'}->[1]->{'contents'}; } - if (defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { - $email = $root->{'args'}->[0]->{'contents'}; - $email_text - = $self->_protect_text(Texinfo::Convert::Text::convert( - {'contents' => $email}, - {'code' => 1, - Texinfo::Common::_convert_text_options($self)})); + if (defined($element->{'args'}->[0]) + and $element->{'args'}->[0]->{'contents'} + and @{$element->{'args'}->[0]->{'contents'}}) { + $email = $element->{'args'}->[0]->{'contents'}; + $email_text + = $self->_protect_text(Texinfo::Convert::Text::convert_to_text( + {'contents' => $email}, + {'code' => 1, + Texinfo::Convert::Text::copy_options_for_convert_text($self, 1)})); } if ($name and $email) { return "<ulink url=\"mailto:$email_text\">" @@ -1057,68 +1286,75 @@ sub _convert($$;$) return ''; } - } elsif ($root->{'cmdname'} eq 'uref' or $root->{'cmdname'} eq 'url') { - if ($root->{'args'}) { + } elsif ($element->{'cmdname'} eq 'uref' or $element->{'cmdname'} eq 'url') { + if ($element->{'args'}) { my ($url_text, $url_content); - if (defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { - $url_content = $root->{'args'}->[0]->{'contents'}; - $url_text = $self->_protect_text(Texinfo::Convert::Text::convert( - {'contents' => $url_content}, - {'code' => 1, - Texinfo::Common::_convert_text_options($self)})); + if (defined($element->{'args'}->[0]) + and $element->{'args'}->[0]->{'contents'} + and @{$element->{'args'}->[0]->{'contents'}}) { + $url_content = $element->{'args'}->[0]->{'contents'}; + $url_text = $self->_protect_text( + Texinfo::Convert::Text::convert_to_text( + {'contents' => $url_content}, + {'code' => 1, + Texinfo::Convert::Text::copy_options_for_convert_text($self, 1)})); } else { $url_text = ''; } my $replacement; - if (scalar(@{$root->{'args'}}) >= 2 - and defined($root->{'args'}->[1]) and @{$root->{'args'}->[1]->{'contents'}}) { + if (scalar(@{$element->{'args'}}) >= 2 + and defined($element->{'args'}->[1]) + and $element->{'args'}->[1]->{'contents'} + and @{$element->{'args'}->[1]->{'contents'}}) { $replacement = $self->_convert({'contents' - => $root->{'args'}->[1]->{'contents'}}); + => $element->{'args'}->[1]->{'contents'}}); } if (!defined($replacement) or $replacement eq '') { - if (scalar(@{$root->{'args'}}) == 3 - and defined($root->{'args'}->[2]) and @{$root->{'args'}->[2]->{'contents'}}) { + if (scalar(@{$element->{'args'}}) == 3 + and defined($element->{'args'}->[2]) + and $element->{'args'}->[2]->{'contents'} + and @{$element->{'args'}->[2]->{'contents'}}) { $replacement = $self->_convert({'contents' - => $root->{'args'}->[2]->{'contents'}}); + => $element->{'args'}->[2]->{'contents'}}); } } if (!defined($replacement) or $replacement eq '') { $replacement = $url_text; } return "<ulink url=\"$url_text\">$replacement</ulink>"; - # DocBook 5 - # return "<link xl:href=\"$url_text\">$replacement</link>"; + # FIXME DocBook 5 + # return "<link xlink:href=\"$url_text\">$replacement</link>"; } - } elsif ($root->{'cmdname'} eq 'abbr' or $root->{'cmdname'} eq 'acronym') { + } elsif ($element->{'cmdname'} eq 'abbr' or $element->{'cmdname'} eq 'acronym') { my $argument; - if (scalar(@{$root->{'args'}}) >= 1 - and defined($root->{'args'}->[0]) and @{$root->{'args'}->[0]->{'contents'}}) { + if (scalar(@{$element->{'args'}}) >= 1 + and defined($element->{'args'}->[0]) and @{$element->{'args'}->[0]->{'contents'}}) { my $arg = $self->_convert({'contents' - => $root->{'args'}->[0]->{'contents'}}); + => $element->{'args'}->[0]->{'contents'}}); if ($arg ne '') { - my $element; - if ($root->{'cmdname'} eq 'abbr') { - $element = 'abbrev'; + my $format_element; + if ($element->{'cmdname'} eq 'abbr') { + $format_element = 'abbrev'; } else { - $element = $root->{'cmdname'}; + $format_element = $element->{'cmdname'}; } - $argument = "<$element>$arg</$element>"; + $argument = "<$format_element>$arg</$format_element>"; } } # - if (scalar(@{$root->{'args'}}) == 2 - and defined($root->{'args'}->[-1]) and @{$root->{'args'}->[-1]->{'contents'}}) { + if (scalar(@{$element->{'args'}}) == 2 + and defined($element->{'args'}->[-1]) and @{$element->{'args'}->[-1]->{'contents'}}) { if (defined($argument)) { my $tree = $self->gdt('{abbr_or_acronym} ({explanation})', {'abbr_or_acronym' => {'type' => '_converted', 'text' => $argument}, 'explanation' => - $root->{'args'}->[-1]->{'contents'}}); + $element->{'args'}->[-1]->{'contents'}}); return $self->_convert($tree); } else { return $self->_convert({'contents' - => $root->{'args'}->[-1]->{'contents'}}); + => $element->{'args'}->[-1]->{'contents'}}); } } elsif (defined($argument)) { return $argument; @@ -1126,51 +1362,53 @@ sub _convert($$;$) return ''; } - } elsif ($root->{'cmdname'} eq 'U') { + } elsif ($element->{'cmdname'} eq 'U') { my $argument; - if ($root->{'args'} - and $root->{'args'}->[0] - and $root->{'args'}->[0]->{'contents'} - and $root->{'args'}->[0]->{'contents'}->[0] - and $root->{'args'}->[0]->{'contents'}->[0]->{'text'}) { - $argument = $root->{'args'}->[0]->{'contents'}->[0]->{'text'}; + if ($element->{'args'} + and $element->{'args'}->[0] + and $element->{'args'}->[0]->{'contents'} + and $element->{'args'}->[0]->{'contents'}->[0] + and $element->{'args'}->[0]->{'contents'}->[0]->{'text'}) { + $argument = $element->{'args'}->[0]->{'contents'}->[0]->{'text'}; } if ($argument) { $result = "&#x$argument;"; } else { - $self->line_warn(__("no argument specified for \@U"), - $root->{'line_nr'}); + $self->line_warn($self, + __("no argument specified for \@U"), + $element->{'source_info'}); $result = ''; } return $result; - } elsif ($Texinfo::Common::inline_commands{$root->{'cmdname'}}) { + } elsif ($Texinfo::Commands::brace_commands{$element->{'cmdname'}} eq 'inline') { my $expand = 0; - if ($Texinfo::Common::inline_format_commands{$root->{'cmdname'}}) { - if ($root->{'cmdname'} eq 'inlinefmtifelse' - or ($root->{'extra'} and $root->{'extra'}->{'format'} - and $self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}})) { + if ($Texinfo::Common::inline_format_commands{$element->{'cmdname'}}) { + if ($element->{'cmdname'} eq 'inlinefmtifelse' + or ($element->{'extra'} and $element->{'extra'}->{'format'} + and $self->{'expanded_formats_hash'}->{$element->{'extra'}->{'format'}})) { $expand = 1; } - } elsif (defined($root->{'extra'}->{'expand_index'})) { + } elsif (defined($element->{'extra'}->{'expand_index'})) { $expand = 1; } return '' if (! $expand); my $arg_index = 1; - if ($root->{'cmdname'} eq 'inlineraw') { + if ($element->{'cmdname'} eq 'inlineraw') { push @{$self->{'document_context'}}, {'monospace' => [0], 'upper_case' => [0]}; $self->{'document_context'}->[-1]->{'raw'} = 1; - } elsif ($root->{'cmdname'} eq 'inlinefmtifelse' - and ! $self->{'expanded_formats_hash'}->{$root->{'extra'}->{'format'}}) { + } elsif ($element->{'cmdname'} eq 'inlinefmtifelse' + and ! $self->{'expanded_formats_hash'}->{$element->{'extra'}->{'format'}}) { $arg_index = 2; } - if (scalar(@{$root->{'args'}}) > $arg_index - and defined($root->{'args'}->[$arg_index]) - and @{$root->{'args'}->[$arg_index]->{'contents'}}) { + if (scalar(@{$element->{'args'}}) > $arg_index + and defined($element->{'args'}->[$arg_index]) + and $element->{'args'}->[$arg_index]->{'contents'} + and @{$element->{'args'}->[$arg_index]->{'contents'}}) { $result .= $self->_convert({'contents' - => $root->{'args'}->[$arg_index]->{'contents'}}); + => $element->{'args'}->[$arg_index]->{'contents'}}); } - if ($root->{'cmdname'} eq 'inlineraw') { + if ($element->{'cmdname'} eq 'inlineraw') { pop @{$self->{'document_context'}}; } #warn " returning braced cmd result $result\n"; @@ -1180,31 +1418,33 @@ sub _convert($$;$) #warn " returning empty string for ignored braced cmd\n"; return ''; } - # special case to ensure that @w leads to something even if empty - } elsif ($root->{'cmdname'} eq 'w') { + } elsif ($element->{'cmdname'} eq 'w') { return $w_command_mark; - } elsif (exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) { - if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) { + } elsif (exists($Texinfo::Commands::block_commands{$element->{'cmdname'}})) { + if ($ignored_block_commands{$element->{'cmdname'}}) { + return ''; + } + if ($self->{'context_block_commands'}->{$element->{'cmdname'}}) { push (@{$self->{'document_context'}}, {'monospace' => [0], 'upper_case' => [0]}); } my @attributes; my $appended = ''; - my @elements; - if (exists($docbook_preformatted_formats{$root->{'cmdname'}})) { + my @format_elements; + if (exists($docbook_preformatted_formats{$element->{'cmdname'}})) { push @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}, - $docbook_preformatted_formats{$root->{'cmdname'}}; - } elsif ($root->{'cmdname'} eq 'enumerate') { - push @elements, 'orderedlist'; + $docbook_preformatted_formats{$element->{'cmdname'}}; + } elsif ($element->{'cmdname'} eq 'enumerate') { + push @format_elements, 'orderedlist'; my $numeration; - if ($root->{'extra'} - and $root->{'extra'}->{'enumerate_specification'}) { - if ($root->{'extra'}->{'enumerate_specification'} =~ /^[A-Z]/) { + if ($element->{'extra'} + and $element->{'extra'}->{'enumerate_specification'}) { + if ($element->{'extra'}->{'enumerate_specification'} =~ /^[A-Z]/) { $numeration = 'upperalpha'; - } elsif ($root->{'extra'}->{'enumerate_specification'} =~ /^[a-z]/) { + } elsif ($element->{'extra'}->{'enumerate_specification'} =~ /^[a-z]/) { $numeration = 'loweralpha'; } else { $numeration = 'arabic'; @@ -1213,36 +1453,36 @@ sub _convert($$;$) $numeration = 'arabic'; } push @attributes, " numeration=\"$numeration\""; - } elsif ($Texinfo::Common::item_line_commands{$root->{'cmdname'}}) { - push @elements, 'variablelist'; - } elsif ($root->{'cmdname'} eq 'itemize') { - push @elements, 'itemizedlist'; + } elsif ($Texinfo::Commands::block_commands{$element->{'cmdname'}} eq 'item_line') { + push @format_elements, 'variablelist'; + } elsif ($element->{'cmdname'} eq 'itemize') { + push @format_elements, 'itemizedlist'; #push @attributes, " mark=\"\""; - } elsif ($root->{'cmdname'} eq 'multitable') { - push @elements, "informaltable"; + } elsif ($element->{'cmdname'} eq 'multitable') { + push @format_elements, "informaltable"; push @attributes, ''; my $columns_count; - if ($root->{'extra'} and defined($root->{'extra'}->{'max_columns'})) { - $columns_count = $root->{'extra'}->{'max_columns'}; + if ($element->{'extra'} and defined($element->{'extra'}->{'max_columns'})) { + $columns_count = $element->{'extra'}->{'max_columns'}; } else { $columns_count = 0; } - push @elements, 'tgroup'; + push @format_elements, 'tgroup'; push @attributes, " cols=\"$columns_count\""; - if ($root->{'extra'}) { + if ($element->{'extra'}) { my @fractions; my $multiply; - if ($root->{'extra'}->{'prototypes'}) { + if ($element->{'extra'}->{'prototypes'}) { $multiply = 1; - foreach my $prototype (@{$root->{'extra'}->{'prototypes'}}) { + foreach my $prototype (@{$element->{'extra'}->{'prototypes'}}) { my $prototype_text - = Texinfo::Convert::Text::convert($prototype, - {Texinfo::Common::_convert_text_options($self)}); - push @fractions, + = Texinfo::Convert::Text::convert_to_text($prototype, + {Texinfo::Convert::Text::copy_options_for_convert_text($self, 1)}); + push @fractions, Texinfo::Convert::Unicode::string_width($prototype_text); } - } elsif ($root->{'extra'}->{'columnfractions'}) { - @fractions = @{$root->{'extra'}->{'columnfractions'}->{'extra'}->{'misc_args'}}; + } elsif ($element->{'extra'}->{'columnfractions'}) { + @fractions = @{$element->{'extra'}->{'columnfractions'}->{'extra'}->{'misc_args'}}; $multiply = 100; } foreach my $fraction (@fractions) { @@ -1250,24 +1490,24 @@ sub _convert($$;$) .'*"></colspec>'; } } - } elsif ($root->{'cmdname'} eq 'float') { - if ($root->{'extra'} and defined($root->{'extra'}->{'node_content'})) { + } elsif ($element->{'cmdname'} eq 'float') { + if ($element->{'extra'} and defined($element->{'extra'}->{'node_content'})) { my $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node ( - { 'contents' => $root->{'extra'}->{'node_content'} }); + { 'contents' => $element->{'extra'}->{'node_content'} }); $result .= "<anchor id=\"$normalized\"/>\n"; } - } elsif ($root->{'cmdname'} eq 'verbatim') { - push @elements, 'screen'; - } elsif ($root->{'cmdname'} eq 'displaymath') { - push @elements, 'informalequation'; - push @elements, 'mathphrase'; - } elsif ($root->{'cmdname'} eq 'quotation' - or $root->{'cmdname'} eq 'smallquotation') { - my $element; - if ($root->{'extra'}) { - if ($root->{'extra'}->{'authors'}) { - foreach my $author (@{$root->{'extra'}->{'authors'}}) { + } elsif ($element->{'cmdname'} eq 'verbatim') { + push @format_elements, 'screen'; + } elsif ($element->{'cmdname'} eq 'displaymath') { + push @format_elements, 'informalequation'; + push @format_elements, 'mathphrase'; + } elsif ($element->{'cmdname'} eq 'quotation' + or $element->{'cmdname'} eq 'smallquotation') { + my $format_element; + if ($element->{'extra'}) { + if ($element->{'extra'}->{'authors'}) { + foreach my $author (@{$element->{'extra'}->{'authors'}}) { if ($author->{'extra'} and $author->{'args'}->[0]->{'contents'}) { $appended .= '<attribution>'.$self->_convert( {'contents' => $author->{'args'}->[0]->{'contents'}}) @@ -1275,41 +1515,39 @@ sub _convert($$;$) } } } - if ($root->{'args'} and $root->{'args'}->[0] - and $root->{'args'}->[0]->{'contents'} - and @{$root->{'args'}->[0]->{'contents'}}) { - my $quotation_arg_text = Texinfo::Convert::Text::convert( - $root->{'args'}->[0], - {Texinfo::Common::_convert_text_options($self)}); + if ($element->{'args'} and $element->{'args'}->[0] + and $element->{'args'}->[0]->{'contents'} + and @{$element->{'args'}->[0]->{'contents'}}) { + my $quotation_arg_text + = Texinfo::Convert::Text::convert_to_text( + $element->{'args'}->[0], + {Texinfo::Convert::Text::copy_options_for_convert_text($self)}); if ($docbook_special_quotations{lc($quotation_arg_text)}) { - $element = lc($quotation_arg_text); + $format_element = lc($quotation_arg_text); } else { - $self->{'pending_prepend'} + $self->{'pending_prepend'} = $self->_convert($self->gdt('@b{{quotation_arg}:} ', {'quotation_arg' => - $root->{'args'}->[0]->{'contents'}})); + $element->{'args'}->[0]->{'contents'}})); } } } - $element = 'blockquote' if (!defined($element)); - push @elements, $element; - } elsif ($root->{'cmdname'} eq 'copying') { - push @elements, ('bookinfo', 'legalnotice'); - } elsif ($Texinfo::Common::format_raw_commands{$root->{'cmdname'}}) { - return '' if (!$self->{'expanded_formats_hash'}->{$root->{'cmdname'}}); + $format_element = 'blockquote' if (!defined($format_element)); + push @format_elements, $format_element; + } elsif ($Texinfo::Commands::block_commands{$element->{'cmdname'}} eq 'format_raw') { + return '' if (!$self->{'expanded_formats_hash'}->{$element->{'cmdname'}}); # the context is here only for the command, so this is forgotten # once all the raw internal text has been formatted $self->{'document_context'}->[-1]->{'raw'} = 1; - } elsif ($Texinfo::Common::block_commands{$root->{'cmdname'}} eq 'raw') { - return ''; - } elsif ($Texinfo::Common::menu_commands{$root->{'cmdname'}}) { + } elsif ($Texinfo::Commands::block_commands{$element->{'cmdname'}} eq 'raw' + or $Texinfo::Commands::block_commands{$element->{'cmdname'}} eq 'menu') { return ''; } - foreach my $element (@elements) { + foreach my $format_element (@format_elements) { my $attribute = shift @attributes; $attribute = '' if (!defined($attribute)); - $result .= "<$element${attribute}>"; - unshift @close_elements, $element; + $result .= "<$format_element${attribute}>"; + unshift @close_format_elements, $format_element; } $result .= $appended if (defined($appended)); } @@ -1317,35 +1555,31 @@ sub _convert($$;$) #warn " end of cmdname\n"; - if ($root->{'type'}) { - #warn " have type $root->{'type'}\n"; - if (exists($docbook_preformatted_formats{$root->{'type'}})) { + if ($element->{'type'}) { + #warn " have type $element->{'type'}\n"; + if (exists($docbook_preformatted_formats{$element->{'type'}})) { push @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}, - $docbook_preformatted_formats{$root->{'type'}}; + $docbook_preformatted_formats{$element->{'type'}}; } - if (defined($type_elements{$root->{'type'}})) { - $result .= "<$type_elements{$root->{'type'}}>"; - } elsif ($root->{'type'} eq 'preformatted') { + if (defined($type_elements{$element->{'type'}})) { + $result .= "<$type_elements{$element->{'type'}}>"; + } elsif ($element->{'type'} eq 'preformatted') { $result .= "<$self->{'document_context'}->[-1]->{'preformatted_stack'}->[-1]>"; $self->{'document_context'}->[-1]->{'in_preformatted'} = 1; - } elsif ($root->{'type'} eq 'def_line') { + } elsif ($element->{'type'} eq 'def_line') { $result .= "<synopsis>"; - $result .= $self->_index_entry($root); + $result .= $self->_index_entry($element); push @{$self->{'document_context'}}, {'monospace' => [1], 'upper_case' => [0]}; $self->{'document_context'}->[-1]->{'inline'}++; - if ($root->{'args'} and @{$root->{'args'}} - and $root->{'args'}->[0]->{'contents'}) { + if ($element->{'args'} and @{$element->{'args'}} + and $element->{'args'}->[0]->{'contents'}) { my $main_command; - if ($Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}}) { - $main_command = $Texinfo::Common::def_aliases{$root->{'extra'}->{'def_command'}}; + if ($Texinfo::Common::def_aliases{$element->{'extra'}->{'def_command'}}) { + $main_command = $Texinfo::Common::def_aliases{$element->{'extra'}->{'def_command'}}; } else { - $main_command = $root->{'extra'}->{'def_command'}; + $main_command = $element->{'extra'}->{'def_command'}; } - foreach my $arg (@{$root->{'args'}->[0]->{'contents'}}) { - next if $arg->{'type'} - and ($arg->{'type'} eq 'empty_spaces_after_command' - or $arg->{'type'} eq 'empty_line_after_command'); - + foreach my $arg (@{$element->{'args'}->[0]->{'contents'}}) { my $type = $arg->{'extra'}->{'def_role'}; next if !$type and $arg->{'type'} eq 'spaces'; @@ -1361,9 +1595,10 @@ sub _convert($$;$) warn "BUG: no def_argument_types_docbook for $type"; next; } - foreach my $element (reverse ( + foreach my $element_attribute (reverse ( @{$def_argument_types_docbook{$type}})) { - $content = "<$element>$content</$element>"; + my ($element, $attribute_text) = _parse_attribute($element_attribute); + $content = "<$element${attribute_text}>$content</$element>"; } $result .= $content; } @@ -1375,103 +1610,113 @@ sub _convert($$;$) } } - if ($root->{'contents'}) { - #warn " have contents $root->{'contents'}\n"; + if ($element->{'contents'}) { + #warn " have contents $element->{'contents'}\n"; my $in_code; - if ($root->{'cmdname'} - and ($Texinfo::Common::preformatted_code_commands{$root->{'cmdname'}} - or $Texinfo::Common::math_commands{$root->{'cmdname'}})) { + if ($element->{'cmdname'} + and ($Texinfo::Commands::preformatted_code_commands{$element->{'cmdname'}} + or $Texinfo::Commands::math_commands{$element->{'cmdname'}})) { $in_code = 1; } push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1 if ($in_code); - if (ref($root->{'contents'}) ne 'ARRAY') { - cluck "contents not an array($root->{'contents'})."; + if (ref($element->{'contents'}) ne 'ARRAY') { + cluck "contents not an array($element->{'contents'})."; } - if (defined($self->{'pending_prepend'}) and $self->_in_inline($root)) { + if (defined($self->{'pending_prepend'}) + and Texinfo::Common::element_is_inline($element, 1)) { $result .= $self->{'pending_prepend'}; delete $self->{'pending_prepend'}; } - foreach my $content (@{$root->{'contents'}}) { + #my $nr = -1; + foreach my $content (@{$element->{'contents'}}) { + #$nr++; + #print STDERR "C$debug_element_nr[$nr] ".Texinfo::Common::debug_print_element_short($content)."\n"; $result .= $self->_convert($content); } pop @{$self->{'document_context'}->[-1]->{'monospace'}} if ($in_code); } - if ($root->{'type'}) { - if (defined($type_elements{$root->{'type'}})) { - $result .= "</$type_elements{$root->{'type'}}>"; - } elsif ($root->{'type'} eq 'preformatted') { + if ($element->{'type'}) { + if (defined($type_elements{$element->{'type'}})) { + $result .= "</$type_elements{$element->{'type'}}>"; + } elsif ($element->{'type'} eq 'preformatted') { $result .= "</$self->{'document_context'}->[-1]->{'preformatted_stack'}->[-1]>"; delete $self->{'document_context'}->[-1]->{'in_preformatted'}; } } $result = '{'.$result.'}' - if ($root->{'type'} and $root->{'type'} eq 'bracketed' - and (!$root->{'parent'}->{'type'} or - ($root->{'parent'}->{'type'} ne 'block_line_arg' - and $root->{'parent'}->{'type'} ne 'line_arg'))); - foreach my $element (@close_elements) { - $result .= "</$element>"; + if ($element->{'type'} and $element->{'type'} eq 'bracketed' + and (!$element->{'parent'}->{'type'} or + ($element->{'parent'}->{'type'} ne 'block_line_arg' + and $element->{'parent'}->{'type'} ne 'line_arg'))); + foreach my $format_element (@close_format_elements) { + $result .= "</$format_element>"; } - if ($root->{'cmdname'} - and exists($Texinfo::Common::block_commands{$root->{'cmdname'}})) { + if ($element->{'cmdname'} + and exists($Texinfo::Commands::block_commands{$element->{'cmdname'}})) { # a pending_prepend still there may happen if a quotation is empty. delete $self->{'pending_prepend'}; - #$result .= "</$root->{'cmdname'}>\n"; + #$result .= "</$element->{'cmdname'}>\n"; if (!$self->{'document_context'}->[-1]->{'raw'} - and exists($docbook_preformatted_formats{$root->{'cmdname'}})) { + and exists($docbook_preformatted_formats{$element->{'cmdname'}})) { my $format = pop @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}; - die "BUG $format ne $docbook_preformatted_formats{$root->{'cmdname'}}" - if ($format ne $docbook_preformatted_formats{$root->{'cmdname'}}); + die "BUG $format ne $docbook_preformatted_formats{$element->{'cmdname'}}" + if ($format ne $docbook_preformatted_formats{$element->{'cmdname'}}); } - if ($self->{'context_block_commands'}->{$root->{'cmdname'}}) { + if ($self->{'context_block_commands'}->{$element->{'cmdname'}}) { pop @{$self->{'document_context'}}; } - } elsif ($root->{'type'} and exists($docbook_preformatted_formats{$root->{'type'}})) { + } elsif ($element->{'type'} and exists($docbook_preformatted_formats{$element->{'type'}})) { my $format = pop @{$self->{'document_context'}->[-1]->{'preformatted_stack'}}; - die "BUG $format ne $docbook_preformatted_formats{$root->{'type'}}" - if ($format ne $docbook_preformatted_formats{$root->{'type'}}); - - # The command is closed either when the corresponding tree element - # is done, and the command is not associated to an element, or when - # the element is closed. - } elsif (($root->{'type'} and $root->{'type'} eq 'element' - and $root->{'extra'} and $root->{'extra'}->{'element_command'}) - or ($root->{'cmdname'} - and $Texinfo::Common::root_commands{$root->{'cmdname'}} - and $root->{'cmdname'} ne 'node' - and !($root->{'parent'} and $root->{'parent'}->{'type'} - and $root->{'parent'}->{'type'} eq 'element' - and $root->{'parent'}->{'extra'} - and $root->{'parent'}->{'extra'}->{'element_command'} eq $root))) { - if ($root->{'type'} and $root->{'type'} eq 'element') { - $root = $root->{'extra'}->{'element_command'}; - } - my $command = $self->_docbook_section_element($root); - if ($command eq 'part' and !Texinfo::Common::is_content_empty($root)) { + die "BUG $format ne $docbook_preformatted_formats{$element->{'type'}}" + if ($format ne $docbook_preformatted_formats{$element->{'type'}}); + # close sectioning command + } elsif ($element->{'cmdname'} and $element->{'cmdname'} ne 'node' + and $Texinfo::Commands::root_commands{$element->{'cmdname'}}) { + my $docbook_sectioning_element = $self->_docbook_section_element($element); + if ($docbook_sectioning_element eq 'part' + and not ($element->{'extra'} + and $element->{'extra'}->{'part_associated_section'}) + and !Texinfo::Common::is_content_empty($element)) { $result .= "</partintro>\n"; } - my $command_texi = $self->_level_corrected_section($root); - if (!($root->{'section_childs'} and scalar(@{$root->{'section_childs'}})) - or $command_texi eq 'top') { - $result .= "</$command>\n"; - my $current = $root; - while ($current->{'section_up'} + my $level_adjusted_cmdname + = Texinfo::Structuring::section_level_adjusted_command_name($element); + if (!($element->{'structure'}->{'section_childs'} + and scalar(@{$element->{'structure'}->{'section_childs'}})) + or $level_adjusted_cmdname eq 'top') { + $result .= "</$docbook_sectioning_element>\n"; + pop @{$self->{'lang_stack'}}; + my $current = $element; + while ($current->{'structure'}->{'section_up'} # the most up element is a virtual sectioning root element, this # condition avoids getting into it - and $current->{'section_up'}->{'cmdname'} - and !$current->{'section_next'} - and $self->_level_corrected_section($current->{'section_up'}) ne 'top') { - $current = $current->{'section_up'}; + and $current->{'structure'}->{'section_up'}->{'cmdname'} + and !$current->{'structure'}->{'section_next'} + and Texinfo::Structuring::section_level_adjusted_command_name($current->{'structure'}->{'section_up'}) ne 'top') { + $current = $current->{'structure'}->{'section_up'}; $result .= '</'.$self->_docbook_section_element($current) .">\n"; + pop @{$self->{'lang_stack'}}; } } + } elsif ($element->{'type'} and $element->{'type'} eq 'before_node_section') { + # ignore text before the first @node or sectioning command + # as DocBook does not allow content not within some semantic + # markup + return ''; } - + + if ($element->{'cmdname'} + and $Texinfo::Commands::root_commands{$element->{'cmdname'}} + and defined($self->{'in_skipped_node_top'}) + and $self->{'in_skipped_node_top'} == 1) { + return ''; + } + #warn " returning $result\n"; return $result; } @@ -1481,7 +1726,6 @@ sub _convert($$;$) 1; __END__ -# $Id$ # Automatically generated from maintain/template.pod =head1 NAME @@ -1490,13 +1734,18 @@ Texinfo::Convert::DocBook - Convert Texinfo tree to DocBook =head1 SYNOPSIS - my $converter + my $converter = Texinfo::Convert::DocBook->converter({'parser' => $parser}); $converter->output($tree); $converter->convert($tree); $converter->convert_tree($tree); +=head1 NOTES + +The Texinfo Perl module main purpose is to be used in C<texi2any> to convert +Texinfo to other formats. There is no promise of API stability. + =head1 DESCRIPTION Texinfo::Convert::DocBook converts a Texinfo tree to DocBook. @@ -1507,15 +1756,18 @@ Texinfo::Convert::DocBook converts a Texinfo tree to DocBook. =item $converter = Texinfo::Convert::DocBook->converter($options) -Initialize converter from Texinfo to DocBook. +Initialize converter from Texinfo to DocBook. The I<$options> hash reference holds options for the converter. In -this option hash reference a parser object may be associated with the -I<parser> key. The other options should be configuration options -described in the Texinfo manual. Those options, when appropriate, -override the document content. +this option hash reference a L<parser object|Texinfo::Parser> +may be associated with the I<parser> key. The other options +are Texinfo customization options and a few other options that can +be passed to the converter. Most of the customization options are described in +the Texinfo manual. Those customization options, when appropriate, override +the document content. The parser should not be available directly anymore +after getting the associated information. -See L<Texinfo::Convert::Converter> for more informations. +See L<Texinfo::Convert::Converter> for more information. =item $converter->output($tree) @@ -1524,12 +1776,11 @@ described in the Texinfo manual. =item $result = $converter->convert($tree) -Convert a Texinfo tree I<$tree> or tree portion and return -the resulting output. +Convert a Texinfo tree I<$tree> and return the resulting output. =item $result = $converter->convert_tree($tree) -Convert a Texinfo tree portion I<$tree> and return the resulting +Convert a Texinfo tree portion I<$tree> and return the resulting output. This function does not try to output a full document but only portions. For a full document use C<convert>. @@ -1539,4 +1790,14 @@ portions. For a full document use C<convert>. Patrice Dumas, E<lt>pertusus@free.frE<gt> +=head1 COPYRIGHT AND LICENSE + +Copyright 2010- Free Software Foundation, Inc. See the source file for +all copyright years. + +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or (at +your option) any later version. + =cut |