# vim: set filetype=perl: # #+############################################################################## # # info.pm: convert to info # # Copyright (C) 2008, 2009, 2010 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 # the Free Software Foundation; either version 3 of the License, # or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # # Some error messages come from texinfo (makeinfo), so copyright holder # is the FSF or the individual who wrote them. All come from before the # switch of texinfo to GPLv3+. # # # Originally written by Patrice Dumas. # #-############################################################################## use Data::Dumper; use strict; $Data::Dumper::Maxdepth = 25; my %info_default_indented_commands; my %info_default_format; my %info_default_enable_encoding_accents; my @simple_quoted_commands; my @asis_commands; my @chevron_commands; my %info_default_accent_commands = (); my %info_default_leaf_command = (); my $info_default_end_sentence_character; my $info_default_after_punctuation_characters; my $info_default_indent_length; my %info_default_indent_format_length; my $info_default_index_length_to_node; my $info_default_listoffloat_caption_entry_length; my $info_default_listoffloat_append; my %info_default_index_entries_counts; sub info_default_load(;$) { my $from_command_line = shift; t2h_default_set_variables_default(); set_default('USE_SECTIONS', 0); set_default('USE_NODES', 1); set_default('SPLIT', ''); @T2H_FORMAT_EXPAND = ('info', 'direntry'); set_default('EXTENSION', 'info'); set_default('SHOW_MENU', 1); set_default('SHOW_TITLE', 0); set_default('USE_SETFILENAME_EXTENSION', 1); set_default('INLINE_INSERTCOPYING', 1); set_default('SIMPLE_MENU', 1); $MENU_SYMBOL = '*'; set_default('ENABLE_ENCODING_USE_ENTITY', 0); set_default('ENABLE_ENCODING', 1); @IMAGE_EXTENSIONS = ('png', 'jpg', 'txt'); $CAPTION_STYLE = 'asis'; set_default('DEFAULT_ENCODING', 'ascii'); set_default('HEADERS', 1); set_default('INLINE_CONTENTS', 0); $no_paragraph_commands{'anchor'} = 1; %simple_map = %default_simple_map; %simple_map_pre = %simple_map; %simple_map_texi = %simple_map; %things_map = %default_things_map; %pre_map = %things_map; %line_command_map = ( 'dircategory' => '' ); # sc and var upcase. @simple_quoted_commands = ('cite', 'code', 'command', 'env', 'file', 'kbd', 'option', 'samp'); @asis_commands = ('asis', 'w', 'b', 'ctrl', 'i', 'math', 'sc', 't', 'r', 'slanted', 'sansserif', 'var', 'titlefont', 'verb', 'clickstyle', 'headitemfont'); @chevron_commands = ('key', 'indicateurl'); %info_default_accent_commands = (); %info_default_leaf_command = (); %style_map = (); t2h_default_copy_style_map (\%default_style_map, \%style_map); foreach my $accent_command ('tieaccent', 'dotless', keys(%unicode_accents), keys(%accent_map)) { $info_default_accent_commands{$accent_command} = 1; $style_map{$accent_command} = { 'function' => \&info_default_accent }; } foreach my $command (keys(%style_map)) { delete $style_map{$command}->{'quote'} if (exists($style_map{$command}->{'quote'})); if (grep {$_ eq $command} @simple_quoted_commands) { delete $style_map{$command}->{'function'} if (exists($style_map{$command}->{'function'})); $style_map{$command}->{'begin'} = '`'; $style_map{$command}->{'end'} = "'"; next; } elsif (grep {$_ eq $command} @asis_commands) { delete $style_map{$command}->{'function'} if (exists($style_map{$command}->{'function'})); delete $style_map{$command}->{'begin'} if (exists($style_map{$command}->{'begin'})); delete $style_map{$command}->{'end'} if (exists($style_map{$command}->{'end'})); } if (grep {$_ eq $command} @chevron_commands) { delete $style_map{$command}->{'function'} if (exists($style_map{$command}->{'function'})); $style_map{$command}->{'begin'} = '<'; $style_map{$command}->{'end'} = '>'; next; } $info_default_leaf_command{$command} = 1 if ($style_map{$command}->{'type'} and $style_map{$command}->{'type'} eq 'simple_style'); } $style_map{'strong'}->{'begin'} = '*'; $style_map{'strong'}->{'end'} = '*'; $style_map{'dfn'}->{'begin'} = '"'; $style_map{'dfn'}->{'end'} = '"'; $style_map{'emph'}->{'begin'} = '_'; $style_map{'emph'}->{'end'} = '_'; foreach my $command (keys(%info_default_leaf_command)) { if (defined ($style_map{$command}->{'args'})) { $style_map{$command}->{'orig_args'} = [ @{$style_map{$command}->{'args'}} ]; } else { $style_map{$command}->{'orig_args'} = [ 'normal' ]; } $style_map{$command}->{'args'} = []; foreach my $arg (@{$style_map{$command}->{'orig_args'}}) { push @{$style_map{$command}->{'args'}}, 'keep'; } } $style_map{'uref'}->{'function'} = \&info_default_uref; $style_map{'url'}->{'function'} = \&info_default_uref; $style_map{'email'}->{'function'} = \&info_default_email; %style_map_pre = (); %style_map_texi = (); t2h_default_copy_style_map (\%style_map, \%style_map_pre); t2h_default_copy_style_map (\%style_map, \%style_map_texi); $special_list_commands{'itemize'} = {}; %info_default_indent_format_length = ('enumerate' => 2, 'itemize' => 3, 'table' => 0, 'vtable' => 0, 'ftable' => 0, ); %format_map = (); foreach my $format ('group', 'raggedright', 'cartouche') { $format_map{$format} = ''; } foreach my $menu_command('menu', 'detailmenu', 'direntry', 'menu_comment') { $complex_format_map{$menu_command} = {'begin' => '' , 'end' => ''}; } foreach my $command (keys (%complex_format_map), keys(%info_default_indent_format_length), 'quotation', 'smallquotation', 'deff_item', 'deff_itemx') { $info_default_indented_commands{$command} = 1; } foreach my $command (keys(%info_default_indented_commands), 'multitable', 'float', 'flushright', 'flushleft', 'center') { $info_default_format{$command} = 1; } # it doesn't change anything for multitable foreach my $non_indented_command('format', 'smallformat', 'menu', 'detailmenu', 'direntry', 'multitable') { $info_default_indented_commands{$non_indented_command} = 0; } $info_default_end_sentence_character = quotemeta($punctuation_characters); $info_default_after_punctuation_characters = quotemeta($after_punctuation_characters); $info_default_indent_length = 5; $info_default_index_length_to_node = 41; $info_default_listoffloat_caption_entry_length = 41; $info_default_listoffloat_append = '...'; t2h_default_push_handler(\&info_default_init_accent_enable_encoding, \@command_handler_init); t2h_default_push_handler(\&info_default_init_variables, \@command_handler_init); $style = \&info_default_style; $print_page_head = \&info_default_print_page_head; $contents = \&info_default_noop; $shortcontents = \&info_default_noop; $about_body = \&info_default_noop; $print_Footnotes = \&info_default_noop; $copying_comment = \&info_default_copying_comment; $element_heading = \&info_default_element_heading; $heading = \&info_default_heading; $normal_text = \&info_default_normal_text; $paragraph = \&info_default_paragraph; $preformatted = \&info_default_preformatted; $empty_preformatted = \&info_default_preformatted; $empty_line = \&info_default_empty_line; # maybe should not be called from the main program? $print_page_foot = \&info_default_print_page_foot; $print_Top_footer = \&info_default_print_Top_footer; $print_Top = \&info_default_print_section; $print_section = \&info_default_print_section; $end_section = \&info_default_end_section; $one_section = \&info_default_one_section; $begin_format_texi = \&info_default_begin_format_texi; $begin_style_texi = \&info_default_begin_style_texi; $begin_paragraph_texi = \&info_default_begin_paragraph_texi; $simple_command = \&info_default_simple_command; $thing_command = \&info_default_thing_command; $begin_special_region = \&info_default_begin_special_region; $end_special_region = \&info_default_end_special_region; $anchor_label = \&info_default_anchor_label; $element_label = \&info_default_noop; $menu_link = \&info_default_menu_link; #$menu_command = \&info_default_menu_command; $complex_format = \&info_default_complex_format; $quotation = \&info_default_quotation; $misc_command_line = \&info_default_misc_commands; $external_ref = \&info_default_external_ref; $internal_ref = \&info_default_internal_ref; $image = \&info_default_image; $image_files = \&info_default_image_files; $index_summary = \&info_default_index_summary; $summary_letter = \&info_default_summary_letter; $index_entry = \&info_default_index_entry; $index_entry_command = \&t2h_default_index_entry_command; $index_letter = \&info_default_index_letter; $printindex = \&info_default_printindex; $print_index = \&info_default_print_index; $index_entry_label = \&info_default_index_entry_label; $foot_section = \&info_default_foot_lines; $foot_line_and_ref = \&info_default_foot_line_and_ref; $footnote_texi = \&info_default_footnote_texi; $list_item = \&info_default_list_item; $format_list_item_texi = \&info_default_format_list_item_texi; $format = \&info_default_format; $tab_item_texi = \&info_default_tab_item_texi; $acronym_like = \&info_default_acronym_like; $sp = \&info_default_sp; $paragraph_style_command = \&info_default_paragraph_style_command; $cell = \&info_default_cell; $row = \&info_default_row; $table_list = \&info_default_table_list; $def_item = \&info_default_def_item; $def = \&info_default_def; $def_line = \&info_default_def_line; $float = \&info_default_float; $listoffloats_entry = \&info_default_listoffloats_entry; $listoffloats = \&info_default_listoffloats; $colon_command = \&info_default_colon_command; $raw = \&info_default_raw; $line_command = \&info_default_line_command; $comment = \&t2h_default_comment; $unknown_style = \&info_default_unknown_style; $heading_text = \&t2h_default_heading_text; } my %info_default_state_map = (); my $info_default_out_file_nr = 1; my $info_default_dir_specification = ''; my @info_default_pending_indirect = (); my @info_default_pending_footnotes = (); my $info_default_state_nr = 0; # maximal length of index entries line number information. Each entry is # an index name. my %info_default_index_line_string_length = (); my %info_default_index_entries = (); my $info_default_footnote_index = 0; my $info_default_current_node = undef; my %info_default_command_handler_expand; sub info_default_intercept_handler { my $command = $_[0]; my $result = &{$info_default_command_handler_expand{$command}}(@_); return info_default_store_text (undef, $result, $command); } sub info_default_init_variables() { %info_default_state_map = (); $info_default_out_file_nr = 1; $info_default_dir_specification = ''; @info_default_pending_indirect = (); @info_default_pending_footnotes = (); $info_default_state_nr = 0; %info_default_index_line_string_length = (); %info_default_index_entries = (); $info_default_footnote_index = 0; $info_default_current_node = undef; #$Texi2HTML::THISDOC{'SPLIT'} = 0 if ($OUTPUT_FORMAT eq 'info'); #$FRAMES = 0 if ($OUTPUT_FORMAT eq 'info'); foreach my $command (keys (%command_handler)) { if ($command_handler{$command}->{'expand'}) { $info_default_command_handler_expand{$command} = $command_handler{$command}->{'expand'}; $command_handler{$command}->{'expand'} = \&info_default_intercept_handler; } } } # this is put in command_handler_init such that it sets things right # in case ENABLE_ENCODING is set and has lead to modification of the # accent functions sub info_default_init_accent_enable_encoding() { return unless (get_conf('ENABLE_ENCODING') and get_conf('USE_UNICODE')); foreach my $key (keys(%unicode_accents), 'dotless') { $info_default_enable_encoding_accents{$key} = 1; $t2h_enable_encoding_default_accent{'normal'}->{$key} = \&t2h_default_accent; $t2h_enable_encoding_default_accent{'texi'}->{$key} = \&t2h_default_accent; $t2h_enable_encoding_default_accent{'pre'}->{$key} = \&t2h_default_accent; $style_map{$key}->{'function'} = \&info_default_accent; $style_map_texi{$key}->{'function'} = \&info_default_accent; $style_map_pre{$key}->{'function'} = \&info_default_accent; } } sub info_default_uref($$) { shift; my $args = shift; my $url = shift @$args; my $text = shift @$args; my $replacement = shift @$args; $url = main::normalise_space($url); $replacement = '' if (!defined($replacement)); $replacement = main::normalise_space($replacement); return $replacement if ($replacement ne ''); $text = '' if (!defined($text)); $text = main::normalise_space($text); return "`$url'" if ($text eq ''); return "$text ($url)"; } sub info_default_email($$) { my $command = shift; my $args = shift; my $mail = shift @$args; my $text = shift @$args; $mail = main::normalise_space($mail); $text = '' if (!defined($text)); $text = main::normalise_space($text); $mail = "<$mail>"; return $mail unless ($text ne ''); return "$text $mail"; } sub info_default_accent($$$) { my @args = @_; my $command = shift; my $args = shift; my $text = $args->[0]; my $style_stack = shift; my $state = shift; my $result; if (get_conf('ENABLE_ENCODING') and $info_default_enable_encoding_accents{$command}) { $result = &t2h_enable_encoding_normal_accent(@args); } else { $result = &t2h_default_accent(@args); } if (scalar(@$style_stack) and $info_default_accent_commands{$style_stack->[-1]}) { # still more accents on the stack return $result; } return info_default_store_text($state,$result,'accents_commands'); } sub info_default_noop { return ''; } sub info_default_copying_comment($$$$) { my $copying_lines = shift; my $copying_text = shift; my $copying_no_texi = shift; my $copying_simple_text = shift; return '' if ($copying_text eq ''); return $copying_text; } sub info_default_byte_count($) { my $string = shift; my $out_encoding = get_conf('OUT_ENCODING'); if ($out_encoding and lc($out_encoding) ne 'us-ascii' and get_conf('USE_UNICODE')) { return length (Encode::encode($out_encoding, $string)); } # There is no default encoding. We assume it is us-ascii. Not sure # about what perl thinks it is... #print STDERR "Unknown encoding for: $string\n" if (!$out_encoding); return length($string); } sub info_default_count_lines($;$$) { my $text = shift; my $indent_length = shift; my $indentation_done = shift; my $blank_line; my $no_indentation = 0; if (!defined($indentation_done) or $indentation_done) { $no_indentation = 1; $indent_length = 0; } my @lines = split /^/, $text; # don't accept empty text. @lines = ('') if (!@lines); my $line_passed = scalar(@lines); $line_passed-- if ($line_passed); my $end_of_line = 0; if (($#lines > 1) and !$end_of_line and ($lines[-1] !~ /\S/) and ($lines[-2] !~ /\S/)) { $blank_line = 1; } my $last_line = $lines[-1]; my $indented_text = shift (@lines); #print STDERR "COUNT info_default_count_lines(i_done $no_indentation, i_l $indent_length) i_t `$indented_text'\n"; foreach my $line (@lines) { if ($indent_length and $line =~ /\S/) { $indented_text .= ' ' x $indent_length . $line; } else { $indented_text .= $line; } } if (chomp($text)) { $line_passed++; $end_of_line = 1; } return ($line_passed, $end_of_line, $last_line, $indented_text, $blank_line); } sub info_default_get_state($) { my $state = shift; if (!exists $info_default_state_map{$state}) { #print STDERR "NEW state $info_default_state_nr\n"; my ($current_command, $top_stack); $info_default_state_map{$state} = {}; info_default_reset_state($info_default_state_map{$state}); # since the page head always leave a blank line, and the state may be # used for text right after the page head, we set it to 1 here. # it may be wrong in other contexts, to be seen. $info_default_state_map{$state}->{'blank_line'} = 1; $info_default_state_map{$state}->{'only_spaces'} = 1; # this is the first line, so set to 1. This is reset later in # most cases, when a node is seen, but may still be useful in # @footnote, for example $info_default_state_map{$state}->{'line_count'} = 1; $info_default_state_map{$state}->{'offset_in_file'} = 0; $info_default_state_map{$state}->{'nr'} = $info_default_state_nr; $info_default_state_map{$state}->{'state'} = $state; $info_default_state_map{$state}->{'multitable_stack'} = []; @{$info_default_state_map{$state}->{'align_stack'}} = ({'command'=>'normal'}); $info_default_state_nr++; } #print STDERR "RETURN state $state $info_default_state_map{$state} $info_default_state_map{$state}->{'nr'}\n"; return $info_default_state_map{$state}; } sub info_default_reset_state($) { my $info_state = shift; $info_state->{'top'} = {}; $info_state->{'current'} = $info_state->{'top'}; } sub info_default_iterator_next($$$) { my $current_command = shift; my $command_index = shift; my $command_close = shift; #print STDERR "NNNNNNNNNNNNNN iterator_next current $current_command idx $command_index close $command_close\n"; my $sub_command = $current_command->{'content'}->[$command_index]; if ($sub_command->{'content'} and !$command_close) { return ($sub_command, 0, 0); } if ($current_command->{'content'}->[$command_index+1]) { return ($current_command, $command_index+1, 0); } elsif (defined($current_command->{'parent'})) { return ($current_command->{'parent'}, $current_command->{'index_in_parent'}, 1); } else { return (undef, undef, undef); } } # return ($current_next, $index_next, $close_next, $text, $command); # # returns the next in tree, identified by the triplet # ($current_next, $index_next, $close_next) and also the command # and/or text if defined, in $text and $command. sub info_default_next($$$) { my $current = shift; my $index = shift; my $close = shift; my $text; my $command; my ($current_next, $index_next, $close_next) = info_default_iterator_next($current, $index, $close); return ($current_next, $index_next, $close_next, $text, $command) if (!defined($current_next)); my $content = $current_next->{'content'}->[$index_next]; $command = $content->{'command'} if (defined($content->{'command'})); if ($close_next) { return ($current_next, $index_next, $close_next, $content->{'end'}, $command); } if (defined($content->{'text'})) { return ($current_next, $index_next, $close_next, $content->{'text'}, $command); } if (defined($content->{'begin'})) { $text = $content->{'begin'}; } if (defined($content->{'end'}) and !defined($content->{'content'})) { if (!defined($text)) { $text = $content->{'end'}; } else { $text .= $content->{'end'}; } } return ($current_next, $index_next, $close_next, $text, $command); } sub info_default_process_line_text($$$) { my $text = shift; my $line_width_counter = shift; my $indent_length = shift; $indent_length = 0 if (!defined($indent_length)); my $line_passed = 0; my $chomped_text = $text; my $end_of_line = chomp($chomped_text); if ($indent_length > $line_width_counter and $chomped_text ne '') { $text = ' ' x ($indent_length - $line_width_counter) . $text; } $line_width_counter += t2h_default_string_width($text); # it seems like it never happens in the tests. if ($end_of_line) { $line_passed = 1; $line_width_counter = 0; } return ($line_width_counter, $line_passed, $text); } # Beware that there is a pending word if the text doesn't end with # a space sub info_default_process_para_text($$$$$;$$) { my $text = shift; my $line_char_counter = shift; my $pending_spaces_word = shift; my $indent_length = shift; my $max_column = shift; my $keep_end_of_lines = shift; $keep_end_of_lines = 0 if (!$keep_end_of_lines); # indentation for the lines except for the first one my $indent_length_next = shift; $indent_length = 0 if (!defined($indent_length)); $indent_length_next = $indent_length if (!defined($indent_length_next)); my $line_passed = 0; my $result = ''; #print STDERR "process_text(indent($indent_length,$indent_length_next),keep_eol $keep_end_of_lines) spaces `$pending_spaces_word->{'spaces'}') line_char_counter $line_char_counter |$text|\n"; while ($text ne '') { #print STDERR "l_c_c $line_char_counter pending_word ".var_to_str($pending_spaces_word->{'word'}).", pending_spaces `$pending_spaces_word->{'spaces'}', result `$result'\n"; if (!$keep_end_of_lines and $text =~ s/^(\s+)//) { my $new_spaces = $1; # in general there are no end of lines in the lines cut, since they # are replaced by spaces in the main loop. However, it may happen # with @* in @def* lines my @lines = split /^/, $new_spaces; my $eol_spaces; # last line is in $new_spaces, other lines are in $eol_spaces if (@lines > 1) { $new_spaces = pop @lines; $eol_spaces = join ("", @lines); #print STDERR "EOL_SPACES[$line_char_counter](+$pending_spaces_word->{'spaces'}) `$eol_spaces'\n"; } if (defined($pending_spaces_word->{'word'})) { # add spaces in front if needed for the indentation if ($indent_length > $line_char_counter + t2h_default_string_width($pending_spaces_word->{'spaces'})) { $pending_spaces_word->{'spaces'} = ' ' x ($indent_length - $line_char_counter) . $pending_spaces_word->{'spaces'}; } $result .= $pending_spaces_word->{'spaces'} . $pending_spaces_word->{'word'}; $line_char_counter += t2h_default_string_width($pending_spaces_word->{'spaces'})+t2h_default_string_width($pending_spaces_word->{'word'}); $pending_spaces_word->{'spaces'} = $new_spaces; $pending_spaces_word->{'word'} = undef; } elsif (!$eol_spaces) { $pending_spaces_word->{'spaces'} .= $new_spaces; } if ($eol_spaces) { $result .= $eol_spaces; $line_passed += scalar(@lines); $indent_length = $indent_length_next; $line_char_counter = 0; $pending_spaces_word->{'spaces'} = ''; } if ((t2h_default_string_width($pending_spaces_word->{'spaces'}) + $line_char_counter > $max_column)) { $pending_spaces_word->{'spaces'} = ''; $result .= "\n"; $line_passed++; $indent_length = $indent_length_next; $line_char_counter = 0; } } else { my $word; if ($keep_end_of_lines) { $word = $text; $text = ''; } elsif ($text =~ s/^([^\s]+)//) { $word = $1; } #else #{ # die "BUG: Impossible situation.\n"; #} $pending_spaces_word->{'word'} = '' if (!defined($pending_spaces_word->{'word'})); $pending_spaces_word->{'word'} .= $word; # The $line_char_counter != 0 is here to cope with the case of a # word longer than $line_char_counter followed by more text: # a line would be passed each time some piece text is appended. if ((t2h_default_string_width($pending_spaces_word->{'spaces'})+t2h_default_string_width($pending_spaces_word->{'word'}) + $line_char_counter > $max_column) and $line_char_counter != 0) { $pending_spaces_word->{'spaces'} = ''; $result .= "\n"; $line_passed++; $indent_length = $indent_length_next; $line_char_counter = 0; } } } return ($line_char_counter, $pending_spaces_word, $line_passed, $result) } sub info_default_skip_spaces($$$) { my $current = shift; my $index = shift; my $close = shift; #print STDERR "SKIP_SPACES\n"; while(1) { my ($current_next, $index_next, $close_next) = info_default_iterator_next($current, $index, $close); return if ($close_next or (!defined($current_next))); my $content = $current_next->{'content'}->[$index_next]; if (defined($content->{'begin'})) { $content->{'begin'} =~ s/^\s*//; #print STDERR "SKIP_SPACES begin\n"; return if ($content->{'begin'} ne ''); } if (defined($content->{'content'}) or defined($content->{'format_name'}) or $content->{'definition_line'}) { # non empty commands stop space skipping, even if they contain # only spaces, like @asis{ } # also for item(x) that have format_name defined #print STDERR "SKIP_SPACES command?\n"; return; } if (defined($content->{'text'})) { my $command = ''; $command = $content->{'command'} if (defined($content->{'command'})); #print STDERR "SKIP_SPACES($command) text\n"; $content->{'text'} =~ s/^\s*//; return if ($content->{'text'} ne ''); } if (defined($content->{'end'})) { #print STDERR "SKIP_SPACES end\n"; $content->{'end'} =~ s/^\s*//; return if ($content->{'end'} ne ''); } ($current, $index, $close) = ($current_next, $index_next, $close_next); } } sub info_default_store_pending($$;$) { my $line_char_counter = shift; my $pending_spaces_word = shift; my $indent_length = shift; $indent_length = 0 if (!defined($indent_length)); my $indent_text = ''; $indent_text = ' ' x $indent_length; #print STDERR "store_pending(spaces `$pending_spaces_word->{'spaces'}', indent($indent_length) `$indent_text' word `".var_to_str($pending_spaces_word->{'word'})."'\n"; my $result = $pending_spaces_word->{'spaces'}; $pending_spaces_word->{'spaces'} = ''; if (defined($pending_spaces_word->{'word'})) { $result .= $pending_spaces_word->{'word'}; $pending_spaces_word->{'word'} = undef; } my $chomped_result = $result; chomp ($chomped_result); if ($line_char_counter == 0 and $chomped_result ne '') { $result = $indent_text . $result; } $line_char_counter += t2h_default_string_width($result); return ($line_char_counter, $pending_spaces_word, $result); } sub info_default_output($) { my $info_state = shift; my $result = ''; #print STDERR "Storing the stack\n"; print STDERR "" . Data::Dumper->Dump([$info_state->{'top'}]) if (get_conf('DEBUG')); my ($bytes_count, $lines_count); ($bytes_count, $result, $lines_count) = info_default_process_content($info_state->{'top'}, $info_state); $info_state->{'offset_in_file'} += $bytes_count; $info_state->{'line_count'} += $lines_count; #print STDERR "HHHHHH($lines_count) $info_state->{'line_count'}: $result\n"; info_default_reset_state($info_state) if (!defined($info_state->{'current'}->{'command'})); return $result; } sub info_default_process_content($$) { my $current_command = shift; my $info_state = shift; my $length = 0; my $result = ''; my $line_char_counter = 0; my $all_line_passed = 0; my $pending_spaces_word; $pending_spaces_word->{'spaces'} = ''; my $preformatted = 0; my $indent_level = 0; my $item_pending; my $in_exdent = 0; my $in_para = 0; my $in_w = 0; my $table_item_line = 0; my $in_table_item = 0; my $max_column = get_conf('FILLCOLUMN'); my $direntry = 0; my $preformatted_format = 0; my $indent_length = 0; # for formats that needs to process a full line (center and flushright) # to know the line length before outputing my $current_line = undef; my ($current, $index, $close) = ($current_command, 0, 0); #print STDERR "info_default_process_content: $current_command\n"; while(1) { last if (!defined($current)); my $content = $current->{'content'}->[$index]; my $text_added = ''; my $line_added_before_item = 0; my $indentation_done = 0; my $prepend_newline; if (get_conf('DEBUG')) { my $text_item_pending = ''; $text_item_pending = $item_pending if (defined($item_pending)); my $text_length = ''; $text_length = "$content->{'text'}" if defined($content->{'text'}); my $text_command = ''; $text_command = $content->{'command'} if defined($content->{'command'}); my $in_node_count = 0; $in_node_count = $info_state->{'line_count'} if defined($info_state->{'line_count'}); print STDERR "($text_command|$text_length|$close|${all_line_passed}+$in_node_count|l_c_cnt $line_char_counter) prfrmted $preformatted para $in_para indent_lvl $indent_level($indent_length) in_exdent $in_exdent in_w $in_w only_spaces $info_state->{'only_spaces'} blank_line $info_state->{'blank_line'} table_item_line $table_item_line in_table_item $in_table_item item_pending $text_item_pending spaces: `$pending_spaces_word->{'spaces'}' word: ".main::var_to_str($pending_spaces_word->{'word'})."\n"; } if ($close) { if (defined($content->{'end'})) { $text_added .= $content->{'end'}; } if ($complex_format_map{$content->{'command'}} and $content->{'content'}) { $preformatted_format--; } # the format is always empty in the main program so the warning # has to be done here if (defined($content->{'total_item_nr'}) and !$content->{'total_item_nr'} and $content->{'content'}) { main::line_warn (sprintf(__("\@%s has text but no \@item"), $content->{'command'}), $content->{'line_nr'}); } # check whether there is a blank line following, to avoid adding # one when closing a format. # This is not a required check if not in preformatted since doubled # blank lines are discarded. my $followed_by_blank_line = 0; if ($preformatted_format) { my ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current, $index, $close); if (defined($command_next) and $command_next eq 'preformatted') { ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current_next, $index_next, $close_next); $followed_by_blank_line = 1 if (defined($text_next) and $text_next =~ /^\s*$/); } } if ($info_default_indented_commands{$content->{'command'}}) { $indent_level--; $indent_length = $indent_level * $info_default_indent_length; # $preformatteed cannot be used here since preformatted # is closed before the end of a format #if ($indent_level > 0 and !$info_state->{'blank_line'} and $content->{'command'} !~ /^deff_item/ and !$preformatted_format) if ($indent_level > 0 and !$info_state->{'blank_line'} and $content->{'command'} !~ /^deff_item/ and !$followed_by_blank_line) { $text_added .= "\n"; } # this nullify a potential noindent in a random format $info_state->{'indent_para'} = undef; } elsif (($complex_format_map{$content->{'command'}} and $content->{'command'} ne 'menu') or $content->{'command'} eq 'cartouche') { if (!$info_state->{'blank_line'} and $info_state->{'only_spaces'} and ($indent_level > 0) and !$followed_by_blank_line) { $text_added .= "\n"; } } if ($content->{'command'} eq 'paragraph' and $info_state->{'align_stack'}->[-1]->{'command'} eq 'normal') { # if there is no space at the end of a paragraph, there may be # pending text, for example, if there is an ending line like # Some text@c a comment my $pending; ($line_char_counter, $pending_spaces_word, $pending) = info_default_store_pending($line_char_counter, $pending_spaces_word, $indent_length); $text_added .= $pending if (defined($pending)); $text_added =~ s/\s*$//; $pending_spaces_word->{'spaces'} = ''; $in_para = 0; $info_state->{'indent_para'} = undef; $text_added .= "\n" unless (($line_char_counter + t2h_default_string_width($text_added)) == 0); } elsif ($content->{'command'} eq 'preformatted') { # if preformatted doesn't end with a newline, it is added here $text_added .= "\n" unless ($line_char_counter == 0); $preformatted--; } elsif ($content->{'command'} eq 'menu') { $text_added .= "\n" unless ($info_state->{'blank_line'}); } elsif ($content->{'command'} eq 'float') { #$text_added = "\n" . $text_added unless ($info_state->{'blank_line'}); $prepend_newline = 1 unless ($info_state->{'blank_line'}); } elsif ($content->{'command'} eq 'w') { $in_w--; } elsif ($paragraph_style{$content->{'command'}}) { my $popped = pop @{$info_state->{'align_stack'}}; print STDERR "BUG".main::format_line_number().": align_stack, popped $popped->{'command'} ne command $content->{'command'}\n" if ($popped->{'command'} ne $content->{'command'}); } elsif ($content->{'command'} eq 'multitable') { my $multitable = pop @{$info_state->{'multitable_stack'}}; if (!defined($multitable->{'cells'}) and ($result ne '')) { $multitable->{'result'} .= $result; $multitable->{'length'} += $length; $multitable->{'line_count'} += $all_line_passed; } $max_column = $multitable->{'max_column_kept'}; $result = $multitable->{'result_kept'}; $line_char_counter = $multitable->{'line_char_counter_kept'}; $all_line_passed = $multitable->{'all_line_passed_kept'}; $indent_level = $multitable->{'indent_level_kept'}; $indent_length = $multitable->{'indent_length_kept'}; #$indent_length_next_line = undef; $length = $multitable->{'length_kept'}; $info_state->{'offset_in_file'} = $multitable->{'offset_in_file_kept'}; $info_state->{'line_count'} = $multitable->{'line_count_kept'}; #print STDERR "MULTITABLE close, lines: $multitable->{'line_count_kept'} + $all_line_passed\n"; foreach my $anchor_and_index (@{$multitable->{'anchors'}}, @{$multitable->{'index_entries'}}) { $anchor_and_index->{'line_nr'} += $multitable->{'line_count_kept'} + $all_line_passed; } if (! scalar(@{$info_state->{'multitable_stack'}})) { #print STDERR "MULTITABLE close, lengths: $multitable->{'offset_in_file_kept'} + $length\n"; foreach my $anchor (@{$multitable->{'anchors'}}) { $anchor->{'info_offset'} += $multitable->{'offset_in_file_kept'} + $length; } } else { push @{$info_state->{'multitable_stack'}->[-1]->{'anchors'}}, @{$multitable->{'anchors'}}; push @{$info_state->{'multitable_stack'}->[-1]->{'index_entries'}}, @{$multitable->{'index_entries'}}; } $text_added .= $multitable->{'result'}; $indentation_done = 1; goto new_text; } elsif ($content->{'command'} eq 'multitable_cell') { my $cell = $info_state->{'multitable_stack'}->[-1]->{'cells'}->[-1]; $cell->{'result'} = $result; $cell->{'length'} = $length; $cell->{'line_passed'} = $all_line_passed; } elsif ($content->{'command'} eq 'direntry') { $direntry--; # this has to be done here, otherwise, at the end, $direntry # would be 0 $info_default_dir_specification .= $text_added; $text_added = ''; } elsif ($content->{'command'} eq 'multitable_row') { my $multitable = $info_state->{'multitable_stack'}->[-1]; my $indent_len = $multitable->{'indent_length_kept'}; #print STDERR "INDENT: $indent_len\n"; my $row_length = 0; my $row = ''; my $max_lines = 0; my $cell_beginning = 0; my @anchor_lines_array; my $cell_idx = 0; my @anchors; my @indices; foreach my $cell (@{$multitable->{'cells'}}) { $cell->{'beginning'} = $cell_beginning; $cell_beginning += $cell->{'cell_width'}+1; @{$cell->{'lines'}} = split /^/, $cell->{'result'}; $max_lines = scalar(@{$cell->{'lines'}}) if (scalar(@{$cell->{'lines'}}) > $max_lines); foreach my $anchor (@{$cell->{'anchors'}}) { push @{$anchor_lines_array[$anchor->{'line_nr'}]}, $anchor; $anchor->{'cell_idx'} = $cell_idx; push @anchors, $anchor; } push @indices, @{$cell->{'index_entries'}}; $cell_idx++; } my $previous_last_cell = scalar(@{$multitable->{'cells'}}); #print STDERR "ROW cell_beginning $cell_beginning, max_lines $max_lines, previous_last_cell $previous_last_cell\n"; for (my $line_idx = 0; $line_idx < $max_lines; $line_idx++) { my $line_width = $indent_len; my $line_bytes = info_default_byte_count(' ' x$indent_len); my $line = ''; # determine the last cell in the line, to fill spaces in # cells preceding that cell on the line my $last_cell = 0; for (my $cell_idx = 0; $cell_idx < $previous_last_cell; $cell_idx++) { $last_cell = $cell_idx+1 if (defined($multitable->{'cells'}->[$cell_idx]->{'lines'}->[$line_idx])); } #print STDERR " L(last_cell $last_cell): $line_idx\n"; for (my $cell_idx = 0; $cell_idx < $last_cell; $cell_idx++) { my $cell_text = $multitable->{'cells'}->[$cell_idx]->{'lines'}->[$line_idx]; #print STDERR " C($cell_idx) "; if (defined($cell_text)) { chomp($cell_text); #print STDERR "$cell_text"; if ($line eq '' and $cell_text ne '') { $line = ' ' x $indent_len; } $line .= $cell_text; $line_width += t2h_default_string_width($cell_text); $line_bytes += info_default_byte_count($cell_text); } if ($cell_idx+1 < $last_cell) { if ($line_width < $indent_len + $multitable->{'cells'}->[$cell_idx+1]->{'beginning'}) { if ($line eq '') { $line = ' ' x $indent_len; } my $spaces = ' ' x ($indent_len + $multitable->{'cells'}->[$cell_idx+1]->{'beginning'} - $line_width); $line_width += t2h_default_string_width($spaces); $line_bytes += info_default_byte_count($spaces); $line .= $spaces; #print STDERR " Csp($line_width) `$spaces'"; } } } if (defined($anchor_lines_array[$line_idx])) { foreach my $anchor (@{$anchor_lines_array[$line_idx]}) { my $anchor_position = $indent_len + $anchor->{'line_char_counter'} + $multitable->{'cells'}->[$anchor->{'cell_idx'}]->{'beginning'}; if ($anchor_position > $line_width) { my $spaces = ' ' x ($anchor_position - $line_width); $line .= $spaces; $line_width += t2h_default_string_width($spaces); $line_bytes += info_default_byte_count($spaces); } $anchor->{'info_offset'} = $line_bytes + $row_length + $multitable->{'length'}; #print STDERR "ROW anchor close: anchor[$anchor->{'cell_idx'}]($multitable->{'cells'}->[$anchor->{'cell_idx'}]->{'beginning'}+$anchor->{'line_char_counter'}) $anchor_position $anchor->{'info_offset'}\n"; $anchor->{'line_char_counter'} = $anchor_position; } } $line .= "\n"; $row_length += info_default_byte_count($line); #print STDERR " ($line_width,".length($line).") $line"; $row .= $line; $previous_last_cell = $last_cell; } foreach my $anchor_and_index (@anchors, @indices) { $anchor_and_index->{'line_nr'} += $multitable->{'line_count'}; #print STDERR "ROW close: new line count: $anchor_and_index->{'line_nr'} + \n"; } if ($content->{'item_command'} eq 'headitem') { # at this point cell_beginning is at the beginning of # the cell following the end of the table -> full width my $line = ' ' x $indent_len . '-' x $cell_beginning . "\n"; $row .= $line; $row_length += info_default_byte_count($line); } #print STDERR "ROW_LENGTH $row_length\n"; $multitable->{'result'} .= $row; $multitable->{'length'} += $row_length; $multitable->{'line_count'} += $max_lines; $multitable->{'cells'} = []; push @{$multitable->{'anchors'}}, @anchors; push @{$multitable->{'index_entries'}}, @indices; } } else { if ($content->{'command'}) { # if processing a paragraph, there may be some pending text # and spaces, as the idea is to write them down only when # there is a space in case of pending text, or when there is some # text in case of pending space. So all the commands # that should write something within paragraph must flush the # pending text/spaces _before_ they output something, or the # text order will be reversed, with the pending things output # after the other commands text. my $pending_added_length = 0; my $pending_added_bytes = 0; if ($content->{'command'} eq 'anchor' or $content->{'command'} eq 'image' or $content->{'command'} eq 'index_command' or $content->{'command'} eq 'sp' or $content->{'raw_command'}) { my $pending; ($line_char_counter, $pending_spaces_word, $pending) = info_default_store_pending($line_char_counter, $pending_spaces_word, $indent_length); # here spaces out of any environment are ignored. if ($in_para or $preformatted or $pending =~ /\S/) { # this has to be done before the anchor related code # to have the right count. # FIXME this is wrong if an end of line was passed. # in that case line_char_counter has been increased and # $pending ends with an end of line $pending_added_length += t2h_default_string_width($pending); $pending_added_bytes += info_default_byte_count($pending); $text_added .= $pending; } } if ($content->{'command'} eq 'strong') { my ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current, $index, $close); if (defined($text_next) and $text_next =~ /^Note\b/i) { main::line_warn(__("\@strong{Note...} produces a spurious cross-reference in Info; reword to avoid that"), $content->{'line_nr'}); } } elsif ($content->{'command'} eq 'w') { $in_w++ if ($content->{'content'}); } elsif ($content->{'command'} eq 'anchor' or ($content->{'command'} eq 'float' and $content->{'anchor_reference'})) { #print STDERR "anchor: offset_in_file $info_state->{'offset_in_file'}, line_count $info_state->{'line_count'}, line_char_counter $line_char_counter pending_added_length $pending_added_length\n"; $content->{'anchor_reference'}->{'info_offset'} = $length + $info_state->{'offset_in_file'} + $pending_added_bytes; $content->{'anchor_reference'}->{'line_nr'} = $all_line_passed + $info_state->{'line_count'}; $content->{'anchor_reference'}->{'line_char_counter'} = $line_char_counter + $pending_added_length; if (@{$info_state->{'multitable_stack'}}) { if ($info_state->{'multitable_stack'}->[-1]->{'cells'}) { push @{$info_state->{'multitable_stack'}->[-1]->{'cells'}->[-1]->{'anchors'}}, $content->{'anchor_reference'}; } else { push @{$info_state->{'multitable_stack'}->[-1]->{'anchors'}}, $content->{'anchor_reference'}; } } push @{$info_state->{'pending_tags'}}, $content->{'anchor_reference'}; push @{$info_state->{'align_stack'}->[-1]->{'anchors'}}, $content->{'anchor_reference'} if ($info_state->{'align_stack'}->[-1]->{'command'} eq 'center' or $info_state->{'align_stack'}->[-1]->{'command'} eq 'flushright'); } elsif ($content->{'command'} eq 'index_label') { #print STDERR "FFFFFFFFF($content->{'index_command'}) $all_line_passed + $info_state->{'line_count'} `$content->{'texi_entry'}'\n"; my $index_line_nr = $all_line_passed + $info_state->{'line_count'}; if ($info_state->{'blank_line'} and $content->{'index_command'} =~ /index$/) { my ($current_next, $index_next, $close_next) = info_default_iterator_next($current, $index, $close); $index_line_nr-- if (!defined($current_next)); } elsif ($content->{'index_command'} =~ /^[vf]table$/) { # if in a table, index label is systematically entered after # the line is processed, as the line is processed with the # item command, while the index entry is entered with the # index_label callback that is done much later. $index_line_nr--; } #print STDERR "index in a blank_line $content->{'index_command'} `$content->{'texi_entry'}'\n" if ($info_state->{'blank_line'}); my $index_name = $content->{'index_entry_reference'}->{'index_name'}; $info_default_index_line_string_length{$index_name} = t2h_default_string_width($index_line_nr) if (!defined($info_default_index_line_string_length{$index_name}) or $info_default_index_line_string_length{$index_name} < t2h_default_string_width($index_line_nr)); #print STDERR "RRRRRRRRRRRRR($content->{'index_entry_reference'}) $content->{'index_entry_reference'}->{'texi'} name: $index_name line: $index_line_nr max: $info_default_index_line_string_length{$index_name}\n"; my $index_ref = { 'index_entry_reference' => $content->{'index_entry_reference'}, 'line_nr' => $index_line_nr }; #print STDERR "INDEX($index_name) line $index_line_nr\n"; $info_default_index_entries{$content->{'index_entry_reference'}} = $index_ref; # there may be no cell in case of an empty multitable if (@{$info_state->{'multitable_stack'}}) { if ($info_state->{'multitable_stack'}->[-1]->{'cells'}) { push @{$info_state->{'multitable_stack'}->[-1]->{'cells'}->[-1]->{'index_entries'}}, $index_ref; } else { push @{$info_state->{'multitable_stack'}->[-1]->{'index_entries'}}, $index_ref; } } push @{$info_state->{'pending_index_entries'}}, $index_ref; } elsif ($content->{'command'} eq '*' and !$preformatted) { if (defined($pending_spaces_word->{'word'})) { $text_added .= $pending_spaces_word->{'spaces'} . $pending_spaces_word->{'word'}; $pending_spaces_word->{'word'} = undef; } # spaces preceding @* are skipped $pending_spaces_word->{'spaces'} = ''; $text_added .= $content->{'text'}; # just like following spaces info_default_skip_spaces($current, $index, $close); # this isn't done otherwise, though, here it is not important # since this end the line $line_char_counter += t2h_default_string_width($content->{'text'}); goto new_text; } elsif ($content->{'command'} eq 'paragraph' and $info_state->{'align_stack'}->[-1]->{'command'} eq 'normal') { # empty paragraph goto new_text if (!$content->{'content'}); my $paragraphindent = get_conf('paragraphindent'); $paragraphindent = 0 if ($paragraphindent eq 'none'); if ($paragraphindent ne 'asis') { info_default_skip_spaces($current, $index, $close); } # if within a format $content->{'paragraph_in_element_nr'} # should not be defined so no indentation will take place if ($paragraphindent ne 'asis' and $paragraphindent and $line_char_counter == 0 and (defined($content->{'paragraph_in_element_nr'})) and ($info_state->{'indent_para'} or (!defined($info_state->{'indent_para'}) and ($content->{'paragraph_in_element_nr'} or (get_conf('firstparagraphindent') eq 'insert'))))) { $text_added .= ' ' x $paragraphindent; } $in_para = 1; } elsif ($content->{'command'} eq 'preformatted') { $preformatted++ if ($content->{'content'}); } elsif ($content->{'command'} eq 'exdent') { # if an end of line is added, in_exdent is set to 2 and # set to one when processing the end of line that was just # added, and set to 0 at the end of the line. # if there is no end of line added, it is only set to 1. if ($line_char_counter != 0) { $text_added .= "\n"; $in_exdent = 2; } else { $in_exdent = 1; } $indent_length = ($indent_level -1) * $info_default_indent_length if ($indent_level > 0); #goto new_text; } elsif ($content->{'command'} eq 'indent') { $info_state->{'indent_para'} = 1; } elsif ($content->{'command'} eq 'noindent') { $info_state->{'indent_para'} = 0; } elsif ($content->{'command'} eq 'sp') { $text_added .= $content->{'text'}; goto new_text; } elsif ($content->{'command'} eq 'image') { # @image result count isn't counted in line_char_counter # since it is not displayed in info my $indent_added = 0; $indent_added = ($indent_length - $line_char_counter) if ($indent_length - $line_char_counter > 0); $text_added .= ' ' x $indent_added . $content->{'text'}; $line_char_counter += $indent_added; goto new_text; } elsif ($content->{'command'} eq 'ref') { # adds a . if needed. if ($content->{'text'} !~ /[\.,]$/ and $content->{'text'} !~ /::$/) { my ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current, $index, $close); if (!defined($text_next) or $text_next !~ /^[\.,]/) { $content->{'text'} .= '.'; } } } elsif ($content->{'command'} eq 'xref') { # warn if there is no punctuation following my ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current, $index, $close); if (!defined($text_next) or $text_next !~ /^./) { # in makeinfo it is # "End of file reached while looking for `.' or `,'" # but maybe it may not be true. main::line_warn(__("`.' or `,' must follow \@xref."), $Texi2HTML::THISDOC{'line_nr'}); } elsif ($text_next !~ /^[\.,]/) { my $char = substr($text_next, 0, 1); main::line_warn(sprintf(__("`.' or `,' must follow \@xref, not %s"), $char), $Texi2HTML::THISDOC{'line_nr'}); } } elsif ($content->{'definition_line'}) { my $dummy_line_passed; print STDERR "BUG: defined pending_word before DEFINITION_LINE\n" if defined($pending_spaces_word->{'word'}); #print STDERR "DEFINITION_LINE($line_char_counter,$pending_spaces_word->{'spaces'},$indent_length,$in_para,$max_column): $content->{'text'}"; ($line_char_counter, $pending_spaces_word, $dummy_line_passed, $text_added) = info_default_process_para_text($content->{'text'}, $line_char_counter, $pending_spaces_word, $indent_length, $max_column, 0, $indent_length+2*$info_default_indent_length); $text_added .= $pending_spaces_word->{'spaces'}; $pending_spaces_word->{'spaces'} = ''; #print STDERR "DEFINITION_LINE($line_char_counter,$pending_spaces_word->{'spaces'}) -> $text_added"; print STDERR "BUG: defined pending_word after DEFINITION_LINE\n" if defined($pending_spaces_word->{'word'}); $indentation_done = 1; goto new_text; } elsif (($content->{'command'} eq 'item' or $content->{'command'} eq 'itemx') and exists $info_default_indent_format_length{$content->{'format_name'}}) { $item_pending = $content->{'format_name'}; #if (!$info_state->{'blank_line'} and $content->{'command'} eq 'item') my $first_item = 0; if ($content->{'command'} eq 'item') { if (!defined($content->{'parent'}->{'item_nr'})) { $content->{'parent'}->{'item_nr'} = 1; $first_item = 1; } else { $content->{'parent'}->{'item_nr'}++; } } if ($item_pending =~ /table$/) { $table_item_line = 1; $indent_length = ($indent_level -1) * $info_default_indent_length if ($indent_level > 0); $in_table_item = 0; } else { $indent_length = ($info_default_indent_format_length{$item_pending} +($indent_level -1)* $info_default_indent_length); } if (!$info_state->{'blank_line'} and ($content->{'command'} ne 'itemx') and (!$first_item or $indent_level > 1)) { my $dummy_line_passed; ($line_char_counter, $dummy_line_passed, $text_added) = info_default_process_line_text($text_added, $line_char_counter, $indent_length); $indentation_done = 1; $line_added_before_item = 1; $prepend_newline = 1; #$text_added = "\n" . $text_added; } if ($item_pending =~ /table$/) { # one less indentation level and no line break # adding line_added_before_item allows the table_item_line to # still be active after the additional blank line $table_item_line = 1+$line_added_before_item; } else { info_default_skip_spaces($current, $index, $close); } } elsif ($content->{'command'} eq 'menu' or $content->{'command'} eq 'listoffloats' or $content->{'heading_command'}) { $text_added .= "\n" unless ($info_state->{'blank_line'}); } elsif ($content->{'command'} eq 'direntry') { if ($content->{'content'}) { $direntry++; } } elsif ($paragraph_style{$content->{'command'}}) { goto new_text if (!$content->{'content'}); push @{$info_state->{'align_stack'}}, {'command' => $content->{'command'}}; } elsif ($content->{'command'} eq 'verbatim' or $content->{'command'} eq 'verbatiminclude') { # $preformatted cannot be used here since preformatted # is closed before a verbatim, $preformatted_format is used if (!$preformatted_format and $indent_level != 0) { if (!$info_state->{'blank_line'} and $info_state->{'only_spaces'}) { $text_added .= "\n"; } my $verb_text = $content->{'text'}; my ($line_passed, $end_of_line, $last_line, $text_indented, $blank_line) = info_default_count_lines($verb_text); $content->{'text'} .= "\n" unless ($blank_line or ($last_line =~ /^\s*$/)); } } # other raw commands elsif ($content->{'raw_command'}) { # not considered as in a paragraph even if in a paragraph $text_added .= $content->{'text'}; goto new_text; } elsif ($content->{'command'} eq 'multitable' and $content->{'content'}) { my $indent_length_kept = $indent_level * $info_default_indent_length; my $multitable = { 'offset_in_file_kept' => $info_state->{'offset_in_file'}, 'line_count_kept' => $info_state->{'line_count'}, 'columns_size' => [ @{$content->{'columns_size'}} ], 'result' => '', 'length' => 0, 'line_count' => 0, 'result_kept' => $result, 'length_kept' => $length, 'all_line_passed_kept' => $all_line_passed, 'line_char_counter_kept' => $line_char_counter, 'max_column_kept' => $max_column, 'indent_level_kept' => $indent_level, 'indent_length_kept' => $indent_length_kept, }; push @{$info_state->{'multitable_stack'}}, $multitable; $info_state->{'offset_in_file'} = 0; $info_state->{'line_count'} = 0; $result = ''; $length = 0; $all_line_passed = 0; $line_char_counter = 0; $indent_level = 0; $indent_length = 0; } elsif ($content->{'command'} eq 'multitable_row') { my $multitable = $info_state->{'multitable_stack'}->[-1]; if (!defined($multitable->{'cells'}) and ($result ne '')) { $multitable->{'result'} .= $result; $multitable->{'length'} += $length; $multitable->{'line_count'} += $all_line_passed; $multitable->{'cells'} = []; } $multitable->{'cell_index'} = -1; } elsif ($content->{'command'} eq 'multitable_cell') { my $multitable = $info_state->{'multitable_stack'}->[-1]; $multitable->{'cell_index'}++; my $cell_width = $content->{'parent'}->{'parent'}->{'columns_size'}->[$multitable->{'cell_index'}]; #$max_column = $cell_width-1; $max_column = $cell_width -2; my $cell = {'cell_width' => $cell_width, 'index_entries' => [], 'anchors' => []}; push @{$multitable->{'cells'}}, $cell; $result = ''; $length = 0; $all_line_passed = 0; $line_char_counter = 0; $indent_level = 0; $indent_length = 0; if (!$content->{'content'}) {# empty cell $cell->{'result'} = $result; $cell->{'length'} = $length; $cell->{'line_passed'} = $all_line_passed; } #info_default_skip_spaces($current, $index, $close); } if ($info_default_indented_commands{$content->{'command'}}) { if ($content->{'command'} =~ /^deff_item/) { info_default_skip_spaces($current, $index, $close); } #elsif (!$info_state->{'blank_line'} and $info_state->{'only_spaces'} and ($indent_level != 0) and !$preformatted) elsif (!$info_state->{'blank_line'} and $info_state->{'only_spaces'} and ($indent_level != 0)) { $text_added .= "\n"; } # there is no close if !$content->{'content'} $indent_level++ if ($content->{'content'}); $indent_length = $indent_level * $info_default_indent_length; } elsif (($complex_format_map{$content->{'command'}} and $content->{'command'} ne 'menu') or $content->{'command'} eq 'cartouche') { #if (!$info_state->{'blank_line'} and $info_state->{'only_spaces'} and ($indent_level != 0) and !$preformatted_format) if (!$info_state->{'blank_line'} and $info_state->{'only_spaces'} and ($indent_level != 0)) { $text_added .= "\n"; } } if ($complex_format_map{$content->{'command'}} and $content->{'content'}) { $preformatted_format++; } } if (defined($content->{'text'})) { if ($in_para and !$in_exdent) { #print STDERR "IN_PARA text\n"; my $new_text = $content->{'text'}; # first find if in a context of no puncutation related # modification: code style command or @var, @cite, @math # acceptable for punctuation related modifications: # asis b dfn emph i slanted sansserif r sc strong t w my $current_tested = $content; my $no_punctation_munging_command; while ($current_tested) { if (defined($current_tested->{'command'}) and (($style_map{$current_tested->{'command'}} and $style_map{$current_tested->{'command'}}->{'args'} and $style_map{$current_tested->{'command'}}->{'args'}->[0] and $style_map{$current_tested->{'command'}}->{'args'}->[0] eq 'code') or $current_tested->{'command'} eq 'var' or $current_tested->{'command'} eq 'cite' or $current_tested->{'command'} eq 'math')) { $no_punctation_munging_command = 1; last; } $current_tested = $current_tested->{'parent'}; } # a punctuation at the end of line in a command is treated # like a punctuation in plain text, except for @:, # accent commands, @dots, 'simple_style' command, and if in # a command as found out just above. if (!$no_punctation_munging_command and (!defined($content->{'command'}) or ($content->{'command'} ne ':' and $content->{'command'} ne 'accents_commands' and $content->{'command'} ne 'dots') and !$info_default_leaf_command{$content->{'command'}}) and get_conf('frenchspacing') ne 'on' and $new_text =~ /([$info_default_end_sentence_character])([$info_default_after_punctuation_characters]*)(\s*)$/) { my $spaces = $3; if (chomp($new_text)) { $new_text =~ s/(\s*)$/ /; } else { # these variables hold the place where the end # of line characters are normalized. my ($current_start_from, $index_start_from, $close_start_from) = ($current, $index, $close); my $only_after_punctuation_characters = 1; my $spaces_to_normalize = 0; # first find whether there are only # after_punctuation_characters followed by spaces # and find the place where the # after_punctuation_characters end my ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current, $index, $close); # go through the text as long as there are after_punctuation_characters if (!$spaces) { while (1) { # !defined($text_next) catches many special # commands, like anchor, index. Not sure if # it is right or wrong. # Also a style_map command never stops # the search, so that @emph{ or @strong{ # begin and end are not taken into account if (!defined($current_next) or (!defined($text_next)) or (defined($command_next) and $command_next eq '*') or ($text_next !~ /^[$info_default_after_punctuation_characters]*(\s*)$/ and (!defined($command_next) or !$style_map{$command_next}))) { $only_after_punctuation_characters = 0; last; } my $text_next_kept = $text_next; # begin normalizing spaces at the last place # where there are after_punctuation_characters $current_start_from = $current_next; $index_start_from = $index_next; $index_start_from = $index_next; ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current_next, $index_next, $close_next); if ($text_next_kept =~ /^[$info_default_after_punctuation_characters]*\s+$/) { if (chomp($text_next_kept)) { $spaces_to_normalize = 1; } last; } } } # check if there are only spaces until end of line if ($only_after_punctuation_characters and !$spaces_to_normalize) { while (1) { # !defined($text_next) catches many special # commands, like anchor, index. Not sure if # it is right or wrong. last if (!defined($current_next) or (!defined($text_next)) or (defined($command_next) and $command_next eq '*')); if ($text_next =~ /\S/ and (!defined($command_next) or !$style_map{$command_next})) { last; } else { if (chomp($text_next)) { $spaces_to_normalize = 1; last; } } ($current_next, $index_next, $close_next, $text_next, $command_next) = info_default_next ($current_next, $index_next, $close_next); } } if ($spaces_to_normalize) { # now do the spaces normalization info_default_skip_spaces($current_start_from, $index_start_from, $close_start_from); my $content = $current_start_from->{'content'}->[$index_start_from]; $content->{'text'} =~ s/(\s*)$/ /; } } } elsif (chomp($new_text)) { $new_text =~ s/(\s*)$/ /; } $text_added .= $new_text; } # ignore spaces outside of paragraphs and preformatted elsif ($preformatted or $info_state->{'align_stack'}->[-1]->{'command'} ne 'normal') { #print STDERR "IN_PREFORMATTED or ALIGN text\n"; $text_added .= $content->{'text'}; } else { my $chomped_text = $content->{'text'}; if ($chomped_text !~ /\S/ and chomp($chomped_text) and !$item_pending) { if ($in_table_item and $info_state->{'only_spaces'}) { # in a blank_line #print STDERR "IN_ITEM ignored: `$content->{'text'}'\n"; } elsif (!$info_state->{'blank_line'} or !$info_state->{'only_spaces'}) { #print STDERR "IN_ADDING_BLANK_LINE because no line before or text before\n"; $text_added .= "\n"; } else { #print STDERR "IN_NOT_ADDING_BLANK_LINE\n"; } } else { # exdent, item not in paragraph nor in preformatted #print STDERR "NOWHERE and not end of line (or item_pending) `$content->{'text'}'\n"; $text_added .= $content->{'text'}; } } } else { if (defined($content->{'begin'})) { $text_added .= $content->{'begin'}; } # command that won't be closed, so the end has to be added # here. It should mostly happen for empty style @-commands. if (defined($content->{'end'}) and !defined($content->{'content'})) { $text_added .= $content->{'end'}; } } } if (!$preformatted and !$in_exdent and $info_state->{'align_stack'}->[-1]->{'command'} eq 'normal' and $in_para) { # the line_passed returned here are not used, since they are computed # below. $indentation_done = 1; my $dummy_line_passed; ($line_char_counter, $pending_spaces_word, $dummy_line_passed, $text_added) = info_default_process_para_text($text_added, $line_char_counter, $pending_spaces_word, $indent_length, $max_column, $in_w, $indent_level * $info_default_indent_length) if ($text_added ne ''); } elsif ($info_state->{'align_stack'}->[-1]->{'command'} ne 'center' and $info_state->{'align_stack'}->[-1]->{'command'} ne 'flushright') { my $dummy_line_passed; ($line_char_counter, $dummy_line_passed, $text_added) = info_default_process_line_text($text_added, $line_char_counter, $indent_length); #$indentation_done = 1; } else { $line_char_counter += t2h_default_string_width($text_added); } new_text: if ($text_added ne '') { if ($item_pending and !$line_added_before_item) { #info_default_skip_spaces($current, $index, $close); $item_pending = undef; } } if ($text_added =~ /\S/) { $in_table_item = 0 if ($in_table_item); $info_state->{'blank_line'} = 0; } $text_added = "\n" . $text_added if ($prepend_newline); print STDERR "TEXT_ADDED($indent_length) `$text_added'\n" if ($text_added ne '' and get_conf('DEBUG')); #print STDERR "TEXT_ADDED($indent_length) `$text_added'\n"; # from here, the next cmmand is available ($current, $index, $close) = info_default_iterator_next($current, $index, $close); my ($line_passed, $end_of_line, $last_line, $text_indented, $blank_line) = info_default_count_lines($text_added, $indent_length, ($indentation_done or $info_state->{'align_stack'}->[-1]->{'command'} eq 'center' or $info_state->{'align_stack'}->[-1]->{'command'} eq 'flushright')); $info_state->{'blank_line'} = 1 if ($blank_line); print STDERR "ADDING `$text_indented'\n" if ($text_indented ne '' and get_conf('DEBUG')); # only_space is set in all the conditionals if ($end_of_line) { $line_char_counter = 0; if ($in_exdent) { $in_exdent--; } if ($table_item_line) { $table_item_line--; $in_table_item = 1; } if (!$table_item_line and !$in_exdent and !$item_pending) { $indent_length = $indent_level * $info_default_indent_length; } $info_state->{'blank_line'} = 1 if ($info_state->{'only_spaces'} and ($last_line !~ /\S/)); $info_state->{'only_spaces'} = 1; } else { if ($line_passed) {# in that case we added more than one line, the $line_char_counter # is reset to the last line length. $line_char_counter = t2h_default_string_width($last_line); $in_exdent = 0; $indent_length = $indent_level * $info_default_indent_length; if ($last_line !~ /\S/) { $info_state->{'only_spaces'} = 1; } else { $info_state->{'only_spaces'} = 0; } } else { $info_state->{'only_spaces'} = 0 if ($last_line =~ /\S/); } } if ($info_state->{'align_stack'}->[-1]->{'command'} eq 'center' or $info_state->{'align_stack'}->[-1]->{'command'} eq 'flushright') { if (defined($current_line)) { $text_added = $current_line . $text_added; } $text_indented = ''; $current_line = undef; my $spaces_prepended = undef; foreach my $line (split /^/, $text_added) { my $chomped_line = $line; if (chomp($chomped_line)) { $line =~ s/^\s*//; $line =~ s/\s*$//; if (t2h_default_string_width($line) eq 0) { $spaces_prepended = 0; $text_indented .= "\n"; } else { my $line_width = t2h_default_string_width($line); if ($line_width > $max_column) { $spaces_prepended = 0; } elsif ($info_state->{'align_stack'}->[-1]->{'command'} eq 'center') { $spaces_prepended = (($max_column -1 - $line_width) /2); } else { $spaces_prepended = ($max_column -1 - $line_width); } $text_indented .= ' ' x$spaces_prepended . $line ."\n"; } } else { $current_line = $line; } } if (defined ($spaces_prepended) and defined($info_state->{'align_stack'}->[-1]->{'anchors'})) { while (@{$info_state->{'align_stack'}->[-1]->{'anchors'}}) { my $anchor = shift @{$info_state->{'align_stack'}->[-1]->{'anchors'}}; $anchor->{'info_offset'} += info_default_byte_count(' ' x$spaces_prepended); } } } if ($direntry) { $info_default_dir_specification .= $text_indented; } else { $result .= $text_indented; $length += info_default_byte_count($text_indented); $all_line_passed += $line_passed; } } return ($length, $result, $all_line_passed); } sub info_default_open_command($$;$) { my $state = shift; my $command = shift; my $additional_entries = shift; my $index = 0; my $info_state = info_default_get_state($state); # index in the parent content list $index = scalar(@{$info_state->{'current'}->{'content'}}) if (defined($info_state->{'current'}->{'content'})); my $new_command = {'command' => $command, 'parent' => $info_state->{'current'}, 'index_in_parent' => $index }; if (defined($additional_entries)) { foreach my $key (keys(%$additional_entries)) { $new_command->{$key} = $additional_entries->{$key}; } } push @{$info_state->{'current'}->{'content'}}, $new_command; $info_state->{'current'} = $new_command; print STDERR "TREE($info_state->{'nr'}): Opened $command\n" if (get_conf('DEBUG')); } sub info_default_close_command($$;$$$) { my $state = shift; my $command = shift; my $begin = shift; my $end = shift; my $command_entries = shift; $state = $Texi2HTML::THISDOC{'state'} if (!defined($state)); my $info_state = info_default_get_state($state); print STDERR "TREE($info_state->{'nr'}): Closing $command\n" if (get_conf('DEBUG')); if (!defined($info_state->{'current'})) { print STDERR "info_state->{'current'} not defined (closing $command)\n"; } elsif (!defined($info_state->{'current'}->{'command'})) { print STDERR "info_state->{'current'}->{'command'} not defined (closing $command)\n"; } elsif ($command ne $info_state->{'current'}->{'command'}) { print STDERR "Was waiting for $info_state->{'current'}->{'command'} (closing $command)\n"; } #return if $no_close; $command_entries->{'begin'} = $begin; $command_entries->{'end'} = $end; $command_entries->{'line_nr'} = $Texi2HTML::THISDOC{'line_nr'}; foreach my $key (keys(%$command_entries)) { $info_state->{'current'}->{$key} = $command_entries->{$key} if (defined($command_entries->{$key})); } $info_state->{'current'} = $info_state->{'current'}->{'parent'}; return info_default_output($info_state) if (!defined($info_state->{'current'}->{'command'})); return ''; } sub info_default_store_text($$;$$) { my $state = shift; my $text = shift; my $command = shift; my $text_entries = shift; $state = $Texi2HTML::THISDOC{'state'} if (!defined($state)); my $info_state = info_default_get_state($state); return '' if ((!defined($text) or $text eq '') and !defined($command)); ################################## debug my $command_text = ''; $command_text = "\[$command\]" if (defined($command)); $command_text .= $text if (defined($text)); print STDERR "TREE($info_state->{'nr'}) Storing: ${command_text}\n" if (get_conf('DEBUG')); ################################## end debug $text_entries->{'text'} = $text if (defined($text)); $text_entries->{'command'} = $command if (defined($command)); $text_entries->{'parent'} = $info_state->{'current'}; $text_entries->{'line_nr'} = $Texi2HTML::THISDOC{'line_nr'}; push @{$info_state->{'current'}->{'content'}}, $text_entries; return info_default_output($info_state) if (!defined($info_state->{'current'}->{'command'})); return ''; } sub info_default_increment_paragraph ($$$;$) { my $in_format = shift; my $parent_format = shift; my $info_state = shift; my $command = shift; if ($in_format) { $parent_format->{'paragraph_in_format_nr'} = 0 if (!defined($parent_format->{'paragraph_in_format_nr'})); $parent_format->{'paragraph_in_format_nr'}++; } else { $info_state->{'paragraph_in_element_nr'}++; } ####################### debug $command = 'PARA' if (!defined($command)); if (0) #if (1) { my $format_info = ''; if ($in_format) { $format_info = "format: [$parent_format->{'command'}],$parent_format->{'paragraph_in_format_nr'}" } print STDERR "INCREMENT_PARA($command) $info_state->{'paragraph_in_element_nr'} $format_info\n"; } ####################### end debug } sub info_default_begin_format_texi($$$) { my $command = shift; my $line = shift; my $state = shift; my $info_state = info_default_get_state ($state); my ($parent_format, $in_format); ($parent_format, $in_format) = info_default_parent_format($info_state->{'current'}); info_default_increment_paragraph ($in_format, $parent_format, $info_state, $command); # remove space in front of center, unless it removes the end of line! $line =~ s/^\s*// if ($command eq 'center' and $line =~ /\S/); # don't open a format if it is a @def*x command and we are already in the # corresponding @def* command info_default_open_command($state,$command) unless ($def_map{$command} and $command =~ /x$/ and defined($info_state->{'current'}->{'command'}) and "$info_state->{'current'}->{'command'}x" eq $command); return $line; } sub info_default_begin_style_texi($$$$$) { my $command = shift; my $state = shift; my $stack = shift; my $real_style_command = shift; my $remove_texi = shift; info_default_open_command($state,$command) unless ($info_default_accent_commands{$command} or exists $things_map{$command} or $command =~ /^special_(\w+)_(\d+)$/); # if ($real_style_command); } sub info_default_begin_paragraph_texi($$$) { my $command = shift; my $paragraph_macros = shift; my $paragraph_command = shift; #print STDERR "begin_paragraph $command\n"; my $state = shift; my $stack = shift; info_default_open_command($state,$command); foreach my $style_command (@$paragraph_macros) { #print STDERR "para stack: $style_command->{'style'}\n"; info_default_open_command($state,$style_command->{'style'}); } } sub info_default_simple_command($$$$$) { my $command = shift; my $in_preformatted = shift; my $in_math = shift; my $line_nr = shift; my $state = shift; my $result = $simple_map{$command}; $result = $simple_map_math{$command} if ($in_math and defined($simple_map_math{$command})); # discards '-' '|' '/' and ':'. If ':' is associated with a punctuation # character it is added to the tree in info_default_colon_command return info_default_store_text($state,$result,$command) if ($result ne ''); return ''; } sub info_default_colon_command($) { my $punctuation_character = shift; if (defined($colon_command_punctuation_characters{$punctuation_character}) and $punctuation_character =~ /^[$punctuation_characters]$/) { return info_default_store_text(undef,$colon_command_punctuation_characters{$punctuation_character}, ':'); } else { return info_default_store_text(undef,$punctuation_character); } } sub info_default_thing_command($$$$$$) { my $command = shift; my $text = shift; my $in_preformatted = shift; my $in_math = shift; my $line_nr = shift; my $state = shift; my $result = $things_map{$command}; #return info_default_close_command($state, $command, $result, $text, ''); return info_default_store_text($state, $result, $command); # return $result . $text; } sub info_default_style($$$$$$$$$$) { my $style = shift; my $command = shift; my $text = shift; my $args = shift; my $no_close = shift; my $no_open = shift; my $line_nr = shift; my $state = shift; my $command_stack = shift; my $kept_line_nrs = shift; my $begin = ''; my $end = ''; # note that the $text is always discarded for closed commands # the formatting is done right here, and the result is entered as text below. if ($info_default_leaf_command{$command}) { my $style_index = 0; my @formatted_args = (); foreach my $arg (@$args) { # we don't use style, since we only set 'orig_args' in style_map # and not in style_map_pre. my $arg_style = $style_map{$command}->{'orig_args'}->[$style_index]; my $new_state = main::duplicate_formatting_state($state); if ($arg_style eq 'normal') { push @formatted_args, main::substitute_line($arg, "\@$command", $new_state); } elsif ($arg_style eq 'code') { $new_state->{'code_style'} = 1; push @formatted_args, main::substitute_line($arg, "\@$command", $new_state); } else { print STDERR "Unknown arg style($style_index) $arg_style for $command, $state->{'remove_texi'}\n"; } $style_index++; } $args = \@formatted_args; } if (defined($style->{'function'})) { # in case of an accent, some text is returned here if there are still # more accents on the command_stack, otherwise it is put in the tree. # Other commands text results are put in the tree below. $text = &{$style->{'function'}}($command, $args, $command_stack, $state, $line_nr, $kept_line_nrs); } elsif ($info_default_leaf_command{$command}) { # no formatting function but a leaf command, it is just replaced # by the formatted argument, and put in the tree below. $text = $args->[0]; } if (defined($style->{'begin'}) and !$no_open) { $begin = $style->{'begin'}; } if (defined($style->{'end'}) and !$no_close) { $end = $style->{'end'}; } # normal style commands unless($special_style{$command} or $info_default_accent_commands{$command} or ($command eq 'hyphenation') or $info_default_leaf_command{$command}) { return info_default_close_command($state, $command, $begin, $end); } # this is for *ref, images and footnotes text registering and putting # in the tree. # anchor is already in the tree, from anchor_label. if (($special_style{$command} or $info_default_leaf_command{$command}) and $command ne 'anchor') { return info_default_store_text ($state, $begin.$text.$end, $command); } # for accents, hyphenation and anchor # (though the result for anchor is always an empty string). return $begin.$text.$end; } sub info_default_header () { return $Texi2HTML::THISDOC{'info_header'} if (defined($Texi2HTML::THISDOC{'info_header'})); # $Texi2HTML::THISDOC{'program'} my $input_basename = $Texi2HTML::THISDOC{'input_file_name'}; $input_basename =~ s/^.*\///; $input_basename = $STDIN_DOCU_NAME if ($input_basename eq '-'); my $output_basename = $Texi2HTML::THISDOC{'filename'}->{'top'}; $output_basename =~ s/^.*\///; my $result = "This is $output_basename, produced by makeinfo version 4.13 from $input_basename. "; my $dummy; ($dummy, $dummy, $dummy, $result) = info_default_process_para_text($result, 0, {'spaces' => ''}, undef, get_conf('FILLCOLUMN')); $result .= "\n\n"; $result .= "$Texi2HTML::THISDOC{'copying_comment'}"; if ($info_default_dir_specification) { $result .= "$info_default_dir_specification\n"; } $Texi2HTML::THISDOC{'info_header'} = $result; return $result; } sub info_default_print_page_head($) { my $fh = shift; my $header = info_default_header(); print $fh "".$header; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); $info_state->{'offset_in_file'} += info_default_byte_count($header); $info_state->{'blank_line'} = 1 if ($Texi2HTML::THISDOC{'copying_comment'} eq ''); } sub info_default_parent_format($) { my $parent_format = shift; my $in_format = 0; while (1) { if (defined($parent_format->{'command'}) and $info_default_format{$parent_format->{'command'}}) { $in_format = 1; last; } last if (!defined($parent_format->{'parent'})); $parent_format = $parent_format->{'parent'}; } return ($parent_format, $in_format); } sub info_default_paragraph($$$$$$$$$$$$) { my $text = shift; my $align = shift; my $indent = shift; my $paragraph_command = shift; my $paragraph_command_formatted = shift; my $paragraph_number = shift; my $format = shift; my $item_nr = shift; my $enumerate_style = shift; my $number = shift; my $command_stack_at_end = shift; my $command_stack_at_begin = shift; my $top_stack = ''; $top_stack = $command_stack_at_begin->[-1] if (scalar (@$command_stack_at_begin)); my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); my ($parent_format, $in_format); ($parent_format, $in_format) = info_default_parent_format($info_state->{'current'}->{'parent'}); info_default_increment_paragraph ($in_format, $parent_format, $info_state); my $additional_args = {'top_stack' => $top_stack, 'parent_format' => $parent_format}; $additional_args->{'paragraph_in_element_nr'} = ($info_state->{'paragraph_in_element_nr'} - 1) if (!$in_format); return info_default_close_command(undef, 'paragraph', undef, undef, $additional_args); } # currently not used, but could be used if info_default_preformatted # return something that is not 'false', for example spaces, though we # want the preformatted to be ignored. Though it is not sure that if there # are spaces we want to ignore the preformatted. sub info_default_empty_preformatted($) { my $text = shift; my $result = info_default_preformatted($text, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef, undef); return 0; } sub info_default_preformatted($$$$$$$$$$$$) { my $text = shift; my $pre_style = shift; my $class = shift; my $leading_command = shift; my $leading_command_formatted = shift; my $preformatted_number = shift; my $format = shift; my $item_nr = shift; my $enumerate_style = shift; my $number = shift; my $command_stack_at_end = shift; my $command_stack_at_begin = shift; return info_default_close_command(undef, 'preformatted'); } sub info_default_node_line($$) { my $element = shift; my $info_state = shift; my $outfile = $Texi2HTML::THISDOC{'filename'}->{'top'}; $outfile = $STDOUT_DOCU_NAME if ($outfile eq '-'); my $result = "\x{1F}\nFile: $outfile, Node: $element->{'text'}"; if (defined($element->{'NodeNext'})) { # This is not translatable $result .= ", Next: $element->{'NodeNext'}->{'text'}"; } if (defined($element->{'NodePrev'})) { $result .= ", Prev: $element->{'NodePrev'}->{'text'}"; } if (defined($element->{'NodeUp'})) { $result .= ", Up: $element->{'NodeUp'}->{'text'}"; } $result .= "\n\n"; # the line_count is ahead from the number of lines by one. $info_state->{'line_count'} = 3; $info_state->{'offset_in_file'} += info_default_byte_count($result); $info_state->{'blank_line'} = 1; return $result; } sub info_default_element_heading($$$$$$$$$$$$) { my $element = shift; my $command = shift; my $texi_line = shift; my $line = shift; my $in_preformatted = shift; my $one_section = shift; my $element_heading = shift; my $first_in_page = shift; my $is_top = shift; my $previous_is_top = shift; my $command_line = shift; my $element_id = shift; my $new_element = shift; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); # FIXME use $element or $Texi2HTML::THIS_ELEMENT? Main program should # ensure they are the same. if ($new_element and ($element ne $new_element and $element->{'node'})) { die "There is a new element, but element `$element->{'texi'}' is not the new element\n"; } # FIXME # non node element may appear if the element appears before the first # node/section element. For example `element not associated with a node' # won't be associated with a node. # @unnumbered element not associated with a node # @node Top # @top Top element if (!$element->{'node'}) { return &$heading($element, $command, $texi_line, $line, $in_preformatted, $one_section, $element_heading); } my $before = ''; $element->{'info_offset'} = $info_state->{'offset_in_file'}; push @{$info_state->{'pending_tags'}}, $element; my $result = info_default_node_line($element, $info_state); $info_default_footnote_index = 0; $info_default_current_node = $element; return $before.$result; } sub info_default_heading($$$$$;$$) { my $element = shift; my $command = shift; my $texi_line = shift; my $line = shift; my $in_preformatted = shift; my $one_section = shift; my $element_heading = shift; die "Heading called for a node\n" if ($element->{'node'}); my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); $info_state->{'paragraph_in_element_nr'} = 0; if (!defined($element->{'texi'})) { main::msg_debug("for $element, element->{'texi'} not defined, texi_line: $texi_line"); } elsif (!defined($element->{'text'})) { main::msg_debug("for $element, $element->{'texi'}, element->{'text'} not defined"); } return '' if ($element->{'tag'} eq 'part'); my $text = "$element->{'text'}"; # when @top is empty, use settitle $text = $Texi2HTML::THISDOC{'settitle'} if (!length($text) and $element->{'tag'} eq 'top' and defined ($Texi2HTML::THISDOC{'settitle'}) and length($Texi2HTML::THISDOC{'settitle'})); my $result = &$heading_text ("\@$command", $text, $element->{'level'}); $result .= "\n"; return info_default_store_text($state, $result, $command, {'heading_command' => 1}); } sub info_default_normal_text($$$$$$$;$) { my @initial_args = @_; my $text = shift; my $in_raw_text = shift; # remove_texi my $in_preformatted = shift; my $in_code = shift; my $in_math = shift; my $in_simple = shift; my $style_stack = shift; my $state = shift; # This is always done here since it is not done in t2h_utf8_normal_text $text = uc($text) if (in_cmd($style_stack, 'var')); # ENCODING_NAME should be defined, but maybe # not when parsing commands in first or second pass, and removing texi # like what is done for @setfilename. if (get_conf('ENABLE_ENCODING') and defined(get_conf('ENCODING_NAME')) and (get_conf('ENCODING_NAME') eq 'utf-8') and get_conf('USE_UNICODE')) { $text = &t2h_utf8_normal_text(@initial_args); } else { #print STDERR "info_default_normal_text $text $in_preformatted $in_code \n"; $text = uc($text) if (in_cmd($style_stack, 'sc')); if (! $in_code and !$in_preformatted) { $text =~ s/---/\x{1F}/g; $text =~ s/--/-/g; $text =~ s/\x{1F}/--/g; $text =~ s/``/"/g; $text =~ s/\'\'/"/g; } } # accented characters are not handled as normal text, but when the last # accent command on the stack is closed. if ($style_stack and @$style_stack and $info_default_accent_commands{$style_stack->[-1]}) { return $text; } #print STDERR "NORMAL\n"; return info_default_store_text($state,$text); } # this is not called in preformatted sub info_default_empty_line($$) { my $text = shift; my $state = shift; #ignore the line if it just follows a deff #return '' if ($state->{'deff_line'}); return info_default_store_text($state,$text); # return ''; } # change interface? sub info_default_anchor_label($$$$) { my $id = shift; my $anchor_text = shift; my $anchor_reference = shift; my $in_special_region = shift; return '' if ($in_special_region); #print STDERR "Storing anchor $anchor_reference->{'text'}\n"; main::line_warn(__("anchor outside of any node, it won't be registered"), $Texi2HTML::THISDOC{'line_nr'}) if (!defined($info_default_current_node)); return info_default_store_text(undef,undef,'anchor',{'anchor_reference' => $anchor_reference}); } sub info_default_acronym_like($$$$$$) { my $command = shift; my $acronym_texi = shift; my $acronym_text = shift; my $with_explanation = shift; my $explanation_lines = shift; my $explanation_text = shift; my $explanation_simply_formatted = shift; if ($with_explanation) { return "$acronym_text ($explanation_text)"; } else { return "$acronym_text"; } } sub info_default_print_page_foot($) { my $fh = shift; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); my $indirect = 0; if (!defined ($info_state->{'pending_tags'})) { # i18n main::document_warn ("Document without nodes."); } else { $indirect = 1 if ($info_default_out_file_nr > 1); if ($indirect) { close ($Texi2HTML::THISDOC{'FH'}); unless (rename ("$Texi2HTML::THISDOC{'destination_directory'}$Texi2HTML::THISDOC{'filename'}->{'top'}", "$Texi2HTML::THISDOC{'destination_directory'}$Texi2HTML::THISDOC{'filename'}->{'top'}-1")) { main::document_warn ("Rename $Texi2HTML::THISDOC{'destination_directory'}$Texi2HTML::THISDOC{'filename'}->{'top'} failed: $!"); } my $INDIRECT = main::open_out("$Texi2HTML::THISDOC{'destination_directory'}$Texi2HTML::THISDOC{'filename'}->{'top'}"); print $INDIRECT "".info_default_header(); print $INDIRECT "\x{1F}\nIndirect:"; foreach my $indirect (@info_default_pending_indirect) { print $INDIRECT "\n$indirect->{'file'}: $indirect->{'offset'}"; } $fh = $INDIRECT; } # makeinfo seems to add systematically an additional \n, done just below print $fh "\n\x{1F}\nTag Table:\n"; if ($indirect) { print $fh "(Indirect)\n"; } my $Top_seen; foreach my $element (@{$info_state->{'pending_tags'}}) { my $prefix; $prefix = 'Node' if ($element->{'node'}); $prefix = 'Ref' if ($element->{'anchor'} or $element->{'float'}); print $fh "$prefix: $element->{'text'}\x{7F}$element->{'info_offset'}\n"; $Top_seen = 1 if ($element->{'text'} =~ /^top$/i); } if (!$Top_seen) {# i18n main::document_warn ("Document without Top node."); } print $fh "\x{1F}\nEnd Tag Table\n"; } # IN_ENCODING is the documentencoding transformed to the encoding names # usually seen in html. This is what the info readers should understand. my $coding = get_conf('IN_ENCODING'); $coding = get_conf('documentencoding') if (!defined($coding)); if (defined($coding)) { print $fh "\n\x{1F}\nLocal Variables:\ncoding: $coding\nEnd:\n"; } } sub info_default_print_Top_footer($$$) { my $fh = shift; my $end_page = shift; my $element = shift; if ($end_page) { &$print_page_foot($fh); } info_default_end_section($fh, $end_page, $element); } sub info_default_footnote_texi($$$) { my $text = shift; my $state = shift; my $style_stack = shift; unless ($state->{'outside_document'} or (defined($state->{'multiple_pass'}) and $state->{'multiple_pass'} > 0)) { $info_default_footnote_index++; } my $footnote_number = $info_default_footnote_index; $footnote_number = $NO_NUMBER_FOOTNOTE_SYMBOL if (!get_conf('NUMBER_FOOTNOTES')); return "($footnote_number) $text"; #return undef; } sub info_default_print_section { my $fh = shift; my $first_in_page = shift; my $previous_is_top = shift; my $element = shift; my $nw = main::print_lines($fh); my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); if (!$info_state->{'blank_line'}) { my $end = "\n"; $info_state->{'offset_in_file'} += info_default_byte_count($end); $info_state->{'line_count'}++; print $fh "$end"; } if (@info_default_pending_footnotes) { my $footnote_text; my $footnote_element; if (get_conf('footnotestyle') eq 'separate') { my $node_ref = $info_default_current_node; # thee is a warning when processing the footnote, like # "Footnote defined without parent node" $node_ref = {'text' => 'no node', 'file' => ''} if (!defined($node_ref)); $footnote_element = { 'NodeUp' => $node_ref, 'text' => $node_ref->{'text'} . "-Footnotes", 'file' => $node_ref->{'file'}, 'info_offset' => $info_state->{'offset_in_file'}, 'node' => 1, }; $footnote_element->{'element_ref'} = $footnote_element; push @{$info_state->{'pending_tags'}}, $footnote_element; $footnote_text = info_default_node_line($footnote_element, $info_state); } else { # FIXME i18n? $footnote_text = " ---------- Footnotes ----------\n\n"; $info_state->{'offset_in_file'} += info_default_byte_count($footnote_text); $info_state->{'line_count'} += 2; #print STDERR "MMMMMMMMMMMMMMMMMM $info_state->{'line_count'}\n"; } while (@info_default_pending_footnotes) { #push @info_default_pending_footnotes, [$lines, $footnote_text, ${info_default_footnote_index}, $node_name, $footnote_info_state]; my $footnote = shift @info_default_pending_footnotes; my $foot_nr = $footnote->{'footnote_index'}; my $node_name = $footnote->{'node_name'}; my $lines = $footnote->{'lines'}; push @{$info_state->{'pending_tags'}}, {'anchor' => 1, 'text' => "${node_name}-Footnote-${foot_nr}", 'info_offset' => $info_state->{'offset_in_file'} }; my $footnote_info_state = $footnote->{'footnote_info_state'}; my $footnote_result = shift @{$lines}; # this is used to keep track of the size when there were # leading spaces that will be removed below. This is only used # to get the difference, the value itself is not of use. my $initial_length = info_default_byte_count($footnote_result); $footnote_result =~ s/^\s*//; #$footnote_result = " ($foot_nr) " . $footnote_result; $footnote_result = ' ' x get_conf('paragraphindent') . $footnote_result; foreach my $footnote_pending_tags(@{$footnote_info_state->{'pending_tags'}}) { $footnote_pending_tags->{'info_offset'} += $info_state->{'offset_in_file'} + info_default_byte_count($footnote_result) - $initial_length; push @{$info_state->{'pending_tags'}}, $footnote_pending_tags; } foreach my $footnote_pending_index_entry(@{$footnote_info_state->{'pending_index_entries'}}) { #print STDERR "TTTTTTTTTTT($footnote_pending_index_entry->{'index_entry_reference'}->{'entry'}) $footnote_pending_index_entry->{'line_nr'} $info_state->{'line_count'}\n"; $footnote_pending_index_entry->{'line_nr'} += $info_state->{'line_count'}; $footnote_pending_index_entry->{'index_entry_reference'}->{'real_element'} = $footnote_element if (get_conf('footnotestyle') eq 'separate'); } my $line; while (@$lines) { $line = shift @$lines; $footnote_result .= $line; } my ($line_passed, $end_of_line, $last_line, $text_indented, $blank_line) = info_default_count_lines($footnote_result); if ($line_passed == 0) {# certainly out of paragraph commands $footnote_result =~ s/\s*$//; $footnote_result .= "\n"; $line_passed = 1; } unless (($last_line !~ /\S/ and $end_of_line) or ($blank_line)) { $footnote_result .= "\n"; $line_passed += 1; } $info_state->{'offset_in_file'} += info_default_byte_count($footnote_result); $info_state->{'line_count'} += $line_passed; $footnote_text .= $footnote_result; } print $fh "$footnote_text"; } } sub info_default_end_section($$$) { my $fh = shift; my $end_foot_navigation = shift; my $element = shift; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); if (defined(get_conf('SPLIT_SIZE')) and $info_state->{'offset_in_file'} > ($info_default_out_file_nr) * get_conf('SPLIT_SIZE')) { if ($info_default_out_file_nr == 1) { # push also the first node, which is always the first pending_tags push @info_default_pending_indirect, {'file'=>"$Texi2HTML::THISDOC{'filename'}->{'top'}-$info_default_out_file_nr", 'offset' => $info_state->{'pending_tags'}->[0]->{'info_offset'} }; } $info_default_out_file_nr++; # these file descriptors leak, but this allows the user to write a # foot navigation himself, otherwise he would write on a closed file # descriptor #close($Texi2HTML::THISDOC{'FH'}); if (!$end_foot_navigation) { main::open_out_file("$Texi2HTML::THISDOC{'filename'}->{'top'}-$info_default_out_file_nr"); #print STDERR "X-$info_default_out_file_nr: $info_state->{'offset_in_file'}\n"; &$print_page_head($Texi2HTML::THISDOC{'FH'}); push @info_default_pending_indirect, {'file'=>"$Texi2HTML::THISDOC{'filename'}->{'top'}-$info_default_out_file_nr", 'offset' => $info_state->{'offset_in_file'}}; } } } sub info_default_one_section($$) { my $fh = shift; my $element = shift; &$print_section($fh, 1, 0, $element); &$print_page_foot($fh); } sub info_default_begin_special_region($$$) { my $region = shift; my $state = shift; my $lines = shift; my $info_state = info_default_get_state ($state); # reset paragraph_in_element_nr if out ofdocument formatting if ($state->{'outside_document'}) { $info_state->{'paragraph_in_element_nr'} = 0; } } sub info_default_end_special_region($$$) { my $region = shift; my $state = shift; my $text = shift; my $info_state = info_default_get_state ($state); my $end = ''; if (!$info_state->{'blank_line'}) { $end = "\n"; $info_state->{'offset_in_file'} += info_default_byte_count($end); } return $text.$end; } sub info_default_menu_link($$$$$$$$) { my $entry = shift; my $state = shift; my $href = shift; my $node = shift; my $title = shift; my $ending = shift; my $has_title = shift; my $command_stack = shift; my $preformatted = shift; $title = '' unless ($has_title); $title .= ':' if ($title ne ''); my $result = "$MENU_SYMBOL$title$node$ending"; return info_default_store_text($state,$result,'menu_entry'); } # not used, menu is a normal preformatted command #sub info_default_menu_command($$$) #{ # my $format = shift; # my $text = shift; # my $in_preformatted = shift; # return info_default_close_command(undef, $format, "* Menu:\n", undef, "\n"); #} sub info_default_complex_format($$) { my $name = shift; my $text = shift; my ($begin, $end); if ($name eq 'menu') { main::line_warn(__("\@menu before first node"), $Texi2HTML::THISDOC{'line_nr'}) if (!defined($info_default_current_node)); $begin = "* Menu:\n\n"; } elsif ($name eq 'direntry') { main::line_warn(__("\@direntry after first node"), $Texi2HTML::THISDOC{'line_nr'}) if (defined($info_default_current_node)); $begin = "START-INFO-DIR-ENTRY\n"; $end = "END-INFO-DIR-ENTRY\n"; } return info_default_close_command(undef, $name, $begin, $end); } sub info_default_quotation($$$$$) { my $command = shift; my $text = shift; my $argument_text = shift; my $argument_text_texi = shift; my $authors = shift; my $attribution; if ($authors) { $attribution = ''; foreach my $author (@$authors) { my $author_texi = $author->{'author_texi'}; chomp($author_texi); $attribution .= gdt("\@center --- \@emph{{author}}\n", {'author' => $author_texi}, {'duplicate' => 1, 'allow_paragraph' => 1}); } } return info_default_close_command(undef, $command, undef, $attribution); } sub info_default_misc_commands($$$$$) { my $command = shift; my $line = shift; my $args = shift; my $stack = shift; my $state = shift; info_default_store_text($state,undef,$command) if ($command eq 'exdent' or $command eq 'noindent' or $command eq 'indent'); return ($command, $line, undef); } sub info_default_external_ref($$$$$$$$$) { my $type = shift; my $section = shift; my $book = shift; my $file = shift; my $href = shift; my $cross_ref = shift; my $args_texi = shift; my $formatted_args = shift; my $node = shift; return info_default_inforef($formatted_args) if ($type eq 'inforef'); return info_default_normal_reference($type, $formatted_args); } sub info_default_internal_ref($$$$$$$$) { my $type = shift; my $href = shift; my $short_name = shift; my $name = shift; my $is_section = shift; my $args_texi = shift; my $formatted_args = shift; my $element = shift; $formatted_args->[1] = $name if ($element->{'float'} and (!defined($formatted_args->[1]) or $formatted_args->[1] eq '')); return info_default_inforef($formatted_args) if ($type eq 'inforef'); return info_default_normal_reference($type, $formatted_args); } sub info_default_normal_reference($$) { my $command = shift; my $formatted_args = shift; for (my $i = 0; $i < scalar(@$formatted_args); $i++) { $formatted_args->[$i] = undef if (defined($formatted_args->[$i]) and $formatted_args->[$i] =~ /^\s*$/); } my $node = $formatted_args->[0]; # an error, should trigger the message: Undefined node `' in @ref. # avoid undef value and use an empty string instead. $node = '' if (!defined($node)); my $name = $formatted_args->[1]; $name = $formatted_args->[2] if (!defined($name)); my $file = $formatted_args->[3]; $file = '' if (!defined($file) and defined($formatted_args->[4])); $name = $node if (!defined($name) and defined($file)); my $result = '*note '; $result = '*Note ' if ($command eq 'xref'); if (defined($name)) { $result .= "${name}: "; $result .= "($file)" if (defined($file)); $result .= "$node"; $result .= '.' if ($command eq 'pxref'); } else { $result .= "${node}::"; } return $result; } sub info_default_inforef($) { my $formatted_args = shift; return info_default_normal_reference('ref', [$formatted_args->[0], $formatted_args->[1], undef, $formatted_args->[2], 'dumb manual name']); } sub info_default_image_files($$$$) { my $base = shift; my $extension = shift; my $texi_base = shift; my $texi_extension = shift; my @files = (); return @files if (!defined($base) or ($base eq '')); if (defined($extension) and ($extension ne '')) { push @files, ["${base}$extension", "${texi_base}$extension"]; push @files, ["$base.$extension", "$texi_base.$extension"]; } foreach my $ext (@IMAGE_EXTENSIONS) { push @files, ["$base.$ext", "$texi_base.$ext"]; } return @files; } sub info_default_image($$$$$$$$$$$$$$$$$) { my $file = shift; my $base = shift; my $preformatted = shift; my $file_name = shift; my $alt = shift; my $width = shift; my $height = shift; my $raw_alt = shift; my $extension = shift; my $working_dir = shift; my $file_path = shift; my $in_paragraph = shift; my $file_locations = shift; my $base_simple_format = shift; my $extension_simple_format = shift; my $file_name_simple_format = shift; my $line_nr = shift; my $txt_path; my $found_file; my @extensions = @IMAGE_EXTENSIONS; if (defined($extension) and ($extension ne '')) { unshift @extensions, ".$extension"; unshift @extensions, "$extension"; } else { $extension = undef; } my $file_found_index = undef; my $file_index = 0; foreach my $file_location (@$file_locations) { my ($file_located, $path, $file_simple_format) = @$file_location; my $extension = shift @extensions; if (defined($path)) { if ($extension eq 'txt' and !defined($txt_path)) { $txt_path = $path; } elsif (!defined($found_file)) { $found_file = [$file_located, $extension, $file_simple_format]; $file_found_index = $file_index; } } $file_index++; } my $text; if (defined($txt_path)) { if (open(TXT, "<$txt_path")) { my $in_encoding = get_conf('IN_ENCODING'); if (defined($in_encoding) and get_conf('USE_UNICODE')) { binmode(TXT, ":encoding($in_encoding)"); } $text='[' if ($in_paragraph or $preformatted); while (my $img_txt = ) { $text .= $img_txt; } # remove last end of line chomp ($text); $text .= ']' if ($in_paragraph or $preformatted); close(TXT); } else { main::line_warn (sprintf(__("\@image file `%s' unreadable: %s"), $txt_path, $!), $line_nr); } } elsif (!defined($found_file)) { main::line_warn (sprintf(__("Cannot find \@image file `%s.txt'"), $base), $line_nr); } if (defined($found_file) and (!defined($extension) or $file_found_index <= 1)) { my $filename = $found_file->[2]; $filename =~ s/\\/\\\\/g; $filename =~ s/\"/\\\"/g; my $result = "\x{00}\x{08}[image src=\"$filename\""; if (defined($alt)) { $alt =~ s/\\/\\\\/g; $alt =~ s/\"/\\\"/g; $result .= " alt=\"$alt\""; } if (defined($text)) { $text =~ s/\\/\\\\/g; $text =~ s/\"/\\\"/g; $result .= " text=\"$text\""; } $result .= "\x{00}\x{08}]"; return $result; } return $text if (defined($text)); return ''; } sub info_default_printindex($$) { my $index_name = shift; my $printindex = shift; %info_default_index_entries_counts = (); return info_default_store_text(undef,t2h_GPL_default_printindex($index_name,$printindex),'printindex'); } sub info_default_print_index($$) { my $text = shift; my $name = shift; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); my $before = ''; if (!$info_state->{'blank_line'}) { $before = "\n"; } return $before if (!defined($text)); my $result = "\x{00}\x{08}[index\x{00}\x{08}]\n* Menu:\n\n" .$text."\n"; return $before.$result; } sub info_default_index_letter($$$) { my $letter = shift; my $id = shift; my $text = shift; return $text; } sub info_default_index_entry_label($$$$$$$$$) { my $identifier = shift; my $preformatted = shift; my $entry = shift; my $index_name = shift; my $index_command = shift; my $texi_entry = shift; my $formatted_entry = shift; my $in_region_not_in_output = shift; my $index_entry_ref = shift; #return '' if ($index_entry_ref->{'hidden'}); #return '' if (!$index_entry_ref->{'seen_in_output'} and defined($index_entry_ref->{'region'})); return '' if ($in_region_not_in_output or !defined($index_entry_ref->{'index_name'})); main::line_warn(sprintf(__("Entry for index `%s' outside of any node"), $index_entry_ref->{'index_name'}), $Texi2HTML::THISDOC{'line_nr'}) if (!defined($info_default_current_node) and !$Texi2HTML::THISDOC{'state'}->{'outside_document'}); my $index_entry_stored = {'index_entry_reference' => $index_entry_ref, 'index_command' => $index_command, 'texi_entry' => $texi_entry}; return info_default_store_text(undef, undef, 'index_label', $index_entry_stored); } sub info_default_index_entry($$$$$$$$$$) { my $text_href = shift; my $entry = shift; my $element_href = shift; my $element_text = shift; my $entry_file = shift; my $current_element_file = shift; my $entry_target = shift; my $entry_element_target = shift; my $in_region_not_in_output = shift; my $index_entry_ref = shift; #return '' if ($index_entry_ref->{'hidden'}); #return '' if (!$index_entry_ref->{'seen_in_output'} and defined($index_entry_ref->{'region'})); return '' if ($in_region_not_in_output); my $state = {}; $state = { 'code_style' => 1 } if ($index_entry_ref->{'in_code'}); $entry = main::substitute_line($index_entry_ref->{'texi'}, "index entry in \@printindex", $state); return '' if ($entry =~ /^\s*$/); my $entry_nr = ''; if (!defined($info_default_index_entries_counts{$entry})) { $info_default_index_entries_counts{$entry} = 0; } else { $info_default_index_entries_counts{$entry} ++; $entry_nr = ' <'.$info_default_index_entries_counts{$entry}.'>'; } my $result = "* $entry${entry_nr}: "; if (t2h_default_string_width($result) < $info_default_index_length_to_node) { $result .= ' ' x($info_default_index_length_to_node - t2h_default_string_width($result)); } #print STDERR "DDDDDDDDDDD $index_entry_ref `$index_entry_ref->{'texi'}'\n"; my $info_index_entry_ref = $info_default_index_entries{$index_entry_ref}; my $line_nr = $info_index_entry_ref->{'line_nr'}; my $real_element_text; my $element = $index_entry_ref->{'real_element'}; # in case $element->{'text'} is not defined, it certainly means that we # are n a special elemet, most likely the virtual element appearing # before anything else if (!defined($element->{'text'})) { $real_element_text = gdt('(outside of any node)'); $line_nr = 0; } else { $element = $element->{'element_ref'} if ($element->{'element_ref'}); $real_element_text = $element->{'text'}; # this happens for index entries appearing after @printindex. In that case # it is considered that they are at the beginning of the node. $line_nr = 3 if (defined($line_nr) and $line_nr < 3); $line_nr = 4 if (!defined($line_nr)); } $result .= $real_element_text . '.'; my $max_len = $info_default_index_line_string_length{$index_entry_ref->{'index_name'}}; $max_len = t2h_default_string_width($line_nr) if (!defined($max_len)); my $line_nr_spaces = sprintf("%${max_len}d", $line_nr); my $line_part = "(line ${line_nr_spaces})"; #print STDERR "GGGGGGGGGG name: $index_entry_ref->{'index_name'} max: ${max_len} line_nr: `$line_nr' line_nr_spaces `$line_nr_spaces' $line_part \n"; if (t2h_default_string_width($result)+t2h_default_string_width($line_part) +1 > get_conf('FILLCOLUMN')) { $result .= "\n" . ' ' x (get_conf('FILLCOLUMN') - t2h_default_string_width($line_part)) ; } else { $result .= ' ' x (get_conf('FILLCOLUMN') - t2h_default_string_width($line_part) - t2h_default_string_width($result)); } $result .= "$line_part\n"; return $result; } sub info_default_index_summary($$) { my $alpha = shift; my $nonalpha = shift; return ''; } sub info_default_summary_letter { return ''; } sub info_default_foot_line_and_ref($$$$$$$$) { my $foot_num = shift; my $relative_num = shift; my $footid = shift; my $docid = shift; my $from_file = shift; my $footnote_file = shift; my $lines = shift; my $state = shift; my $footnote_state = $Texi2HTML::THISDOC{'state'}; my $footnote_info_state = info_default_get_state ($footnote_state); my $footnote_text = "($info_default_footnote_index)"; $footnote_text = "($NO_NUMBER_FOOTNOTE_SYMBOL)" if (!get_conf('NUMBER_FOOTNOTES')); my $node_name; $node_name = ''; if (defined($info_default_current_node)) { $node_name = $info_default_current_node->{'text'}; } else { # i18n # no warning when outside of document, for footnotes in # titlepage and copying main::line_error(__("Footnote defined without parent node"), $Texi2HTML::THISDOC{'line_nr'}) unless ($footnote_state->{'outside_document'}); #print STDERR "".main::context_string()."\n"; } if (get_conf('footnotestyle') eq 'separate') { $footnote_text .= ' (' . info_default_normal_reference('pxref', ["${node_name}-Footnote-${info_default_footnote_index}"]) . ')'; } push @info_default_pending_footnotes, {'lines' => $lines, 'footnote_text' => $footnote_text, 'footnote_index' => ${info_default_footnote_index}, 'node_name' => $node_name, 'footnote_info_state' => $footnote_info_state} unless ($state->{'outside_document'} or (defined($state->{'multiple_pass'}) and $state->{'multiple_pass'} > 0)); return ([], $footnote_text); } sub info_default_foot_lines($) { my $lines = shift; #my $state = $Texi2HTML::THISDOC{'state'}; #my $info_state = info_default_get_state ($state); @$lines = (); } # remark: table_item is the html one, but it gets added to the table text # on the stack, and is ignored there (in info_default_format). sub info_default_format_list_item_texi($$$$) { my $format = shift; my $line = shift; my $prepended = shift; my $command = shift; my $number = shift; my $open_command = 0; my $result_line; $command = 'bullet' if ((!defined($command) or $command eq '') and (!defined($prepended) or $prepended eq '') and $format eq 'itemize'); $prepended = "\@$command\{\}" if (defined($command) and $command ne ''); $prepended = "$number." if (defined($number) and $number ne ''); $line =~ s/^\s*//; if (defined($command) and $command ne '' and $format ne 'itemize') { #@*table $line =~ s/\s*$//; if (exists ($style_map{$command})) { $result_line = "\@$command\{$line\}\n"; } elsif (exists ($things_map{$command})) { $result_line = "\@$command\{\} $line\n"; } else { $result_line = "\@$command $line\n"; } } # elsif (defined($prepended) and $prepended ne '') # { # @enumerate and @itemize # $prepended =~ s/^\s*//; # $prepended =~ s/\s*$//; # $result_line = $prepended . ' ' . $line; # } return ($result_line, $open_command); } sub info_default_list_item($$$$$$$$$$$$) { my $text = shift; my $format = shift; my $command = shift; my $formatted_command = shift; my $item_nr = shift; my $enumerate_style = shift; my $number = shift; my $prepended = shift; my $prepended_formatted = shift; my $only_inter_item_commands = shift; my $before_items = shift; my $item_command = shift; # my $prepend = ''; # if (defined($formatted_command) and $formatted_command ne '') # { # $prepend = $formatted_command; # } # return $prepend . $text; # $command = 'bullet' if ((!defined($command) or $command eq '') and (!defined($prepended) or $prepended eq '') and $format eq 'itemize'); $formatted_command = $things_map{'bullet'} if ((!defined($command) or $command eq '') and (!defined($prepended) or $prepended eq '') and $format eq 'itemize'); if ($format !~ /table$/) { my $result = ''; if ($format eq 'enumerate') { $result = $number.'.'; } elsif ($format eq 'itemize') { if (defined($formatted_command) and $formatted_command ne '') { $result = $formatted_command; } elsif (defined ($prepended_formatted) and $prepended_formatted ne '') { $prepended_formatted =~ s/^\s*//; $prepended_formatted =~ s/\s*$//; $result = $prepended_formatted; } } else { $result = ''; } $result .= ' ' if ($result ne ''); return info_default_close_command (undef, $item_command, $result); } return $text; } sub info_default_format($$$) { my $tag = shift; my $element = shift; my $text = shift; # currently no command has something else than '' as $element. # notice that any text is discarded $element = undef if ($element eq ''); my $element_end = $element; if (defined($element) and $element =~ /^(\w+)(\s+)(.+)/) { $element = $1; $element_end = $2; } return info_default_close_command(undef, $tag, $element, $element_end); } sub info_default_tab_item_texi($$$$$$) { my $command = shift; my $commands_stack = shift; my $stack = shift; my $state = shift; my $line = shift; my $line_nr = shift; $line =~ s/^\s*//; my $format; my $info_state = info_default_get_state ($state); #$format = $commands_stack->[-1] if (defined($commands_stack) and @$commands_stack and $commands_stack->[-1]); my ($parent_format, $in_format) = info_default_parent_format($info_state->{'current'}); print STDERR "Not in_format in info_default_tab_item_texi\n" if (!$in_format); $format = $parent_format->{'command'}; # in case of an @item or @tab outside of any format $format will be # undefined, or not multitable for a @tab. # however the main program still do as if something was opened, plus # it is checked there that the nesting is correct #return $line if (!defined($format) or $command eq 'tab' and $format ne 'multitable'); #print STDERR "tab_item_texi $format $command $commands_stack, $stack, $state, $line, ".main::format_line_number($line_nr)."\n"; if ($format eq 'multitable') { # even if it is a tab, if it is not already in a multitable_row, one # should be started if ($command ne 'tab' or $info_state->{'current'}->{'command'} ne 'multitable_row') { info_default_open_command($state, 'multitable_row'); } info_default_open_command($state, 'multitable_cell'); } elsif ($format =~ /table$/) { info_default_store_text ($state, undef, $command, {'format_name' => $format}); } else { info_default_open_command ($state, $command, {'format_name' => $format}); } # this allows removing blank space in front of the item or tab argument return $line; } sub info_default_sp($$) { my $number = shift; my $preformatted = shift; my $result = "\n" x $number; return info_default_store_text(undef,$result,'sp'); } sub info_default_paragraph_style_command($$) { my $format = shift; my $text = shift; return info_default_close_command(undef, $format); } sub info_default_row($$$$$$$$) { my $text = shift; my $macro = shift; my $columnfractions = shift; my $prototype_row = shift; my $prototype_lengths = shift; my $column_number = shift; my $only_inter_item_commands = shift; my $before_items = shift; #print STDERR "info_default_row: $text\n"; return info_default_close_command(undef, 'multitable_row', undef, undef, {'item_command' => $macro}); } sub info_default_cell($$$$$$$$) { my $text = shift; my $row_macro = shift; my $columnfractions = shift; my $prototype_row = shift; my $prototype_lengths = shift; my $column_number = shift; my $only_inter_item_commands = shift; my $before_items = shift; # in general, when before_items, there will be no call to the function # since there should never be a text sent back, so that this # function will not be called for the first row (the multitable title). # However, if there is a @tab before the first @item, the main program # is less careful and closes the cell in any case, so before_items # has to be checked for that case. return info_default_close_command(undef, 'multitable_cell') unless ($before_items); } sub info_default_table_list($$$$$$$$$) { my $format_command = shift; my $text = shift; my $command = shift; my $formatted_command = shift; # enumerate my $item_nr = shift; my $enumerate_style = shift; # itemize my $prepended = shift; my $prepended_formatted = shift; # multitable my $columnfractions = shift; my $prototype_row = shift; my $prototype_lengths = shift; my $column_number = shift; die "BUG: $format_command item_nr undef\n" if (!defined($item_nr)); return info_default_close_command(undef, $format_command, undef, undef, {'total_item_nr' => $item_nr}) if ($format_command ne 'multitable'); my $columnsize = []; if (defined($prototype_lengths) and @$prototype_lengths) { $columnsize = [ @$prototype_lengths ]; } elsif (defined($columnfractions) and @$columnfractions) { foreach my $fraction (@$columnfractions) { push @$columnsize, int($fraction * get_conf('FILLCOLUMN') +0.5); } } else { # empty multitable #print STDERR "Empty multitable?\n"; } return info_default_close_command(undef, $format_command, undef, undef, {'columns_size' => $columnsize, 'total_item_nr' => $item_nr}); } sub info_default_def_item($$$) { my $text = shift; my $only_inter_item_commands = shift; my $command = shift; my $format = 'deff_item'; $format = 'deff_itemx' if ($command =~ /x$/); return info_default_close_command(undef, $format); } sub info_default_def_line($$$$$$$$$$$$$$$$) { my $category_prepared = shift; my $name = shift; my $type = shift; my $arguments = shift; my $index_label = shift; my $arguments_array = shift; my $arguments_type_array = shift; my $unformatted_arguments_array = shift; my $command = shift; my $class_name = shift; my $category = shift; my $class = shift; my $style = shift; my $original_command = shift; $name = '' if (!defined($name) or ($name =~ /^\s*$/)); $type = '' if (!defined($type) or $type =~ /^\s*$/); $arguments = '' if (!defined($arguments) or $arguments =~ /^\s*$/); my $type_name = ''; $type_name .= "$type " if ($type ne ''); $type_name .= $name if ($name ne ''); my $result = " -- $category_prepared: ${type_name}$arguments"; $result =~ s/\s*$//; $result .= "\n"; my $state = $Texi2HTML::THISDOC{'state'}; info_default_store_text(undef,$result,"${command}_line",{'definition_line' => 1}); my $format = 'deff_item'; $format = 'deff_itemx' if ($original_command =~ /x$/); return info_default_open_command($state, $format); } sub info_default_def($$) { my $text = shift; my $command = shift; return info_default_close_command(undef, $command); } sub info_default_float($$$$$) { my $text = shift; my $float = shift; my $caption = shift; my $shortcaption = shift; my $additional_arguments; if (exists($float->{'id'})) { $additional_arguments->{'anchor_reference'} = $float; main::line_warn(__("float reference outside of any node, it won't be registered"), $Texi2HTML::THISDOC{'line_nr'}) if (!defined($info_default_current_node) and !$Texi2HTML::THISDOC{'state'}->{'outside_document'}); } my $caption_text = ''; if (defined($float->{'caption_texi'})) { $caption_text = $caption; } elsif (defined($float->{'shortcaption_texi'})) { $caption_text = $shortcaption; } elsif (defined($caption)) { $caption_text = $caption; } #return $caption_text; return info_default_close_command(undef, 'float', undef, $caption_text, $additional_arguments); } sub info_default_listoffloats_entry($$$$) { my $style_texi = shift; my $float = shift; my $float_style = shift; my $caption = shift; my $href = shift; my @lines = split /^/, $caption; $caption = $lines[0]; $caption = '' if (!defined($caption)); chomp ($caption); my $result = ''; #$caption .= ':' if ($caption ne ''); my $caption_entry = "* $float_style: $float->{'text'}."; if (t2h_default_string_width($caption_entry) > $info_default_listoffloat_caption_entry_length) { $caption_entry .= "\n" . ' ' x $info_default_listoffloat_caption_entry_length; } else { $caption_entry .= ' ' x ($info_default_listoffloat_caption_entry_length - length($caption_entry)); } my $width = $info_default_listoffloat_caption_entry_length; while ($caption =~ s/^(\S+\s*)//) { my $new_word = $1; if ((t2h_default_string_width($new_word) + $width) > get_conf('FILLCOLUMN') - 3) { $caption_entry .= $info_default_listoffloat_append; last; } else { $caption_entry .= $new_word; $width += t2h_default_string_width($new_word); } } return $caption_entry. "\n"; } sub info_default_listoffloats($$$) { my $style_texi = shift; my $style = shift; my $float_entries = shift; my $state = $Texi2HTML::THISDOC{'state'}; my $info_state = info_default_get_state ($state); my $result = "* Menu:\n\n"; foreach my $float_entry (@$float_entries) { $result .= $float_entry; } my ($parent_format, $in_format); ($parent_format, $in_format) = info_default_parent_format($info_state->{'current'}); #print STDERR "\@listoffloats not at top level\n" if ($in_format); info_default_increment_paragraph ($in_format, $parent_format, $info_state, 'listoffloats'); return info_default_store_text($state,$result,'listoffloats'); } sub info_default_raw($$) { my $style = shift; my $text = shift; my $expanded = 1 if (grep {$style eq $_} @EXPAND); # no warning for unknown raw formats if ($style eq 'verbatim' or $style eq 'verbatiminclude' or $expanded) { return info_default_store_text(undef, $text, $style, {'raw_command' => 1}); } return ''; } sub info_default_line_command($$$$) { my $command = shift; my $arg_text = shift; my $arg_texi = shift; my $state = shift; main::line_warn(__("\@dircategory after first node"), $Texi2HTML::THISDOC{'line_nr'}) if (defined($info_default_current_node)); return '' if ($arg_text eq ''); $info_default_dir_specification .= "INFO-DIR-SECTION $arg_text\n"; return ''; } sub info_default_unknown_style($$$$$) { my $command = shift; my $text = shift; my $state = shift; my $no_close = shift; my $no_open = shift; my ($result, $result_text, $message); $result_text = info_default_close_command(undef, $command, undef, undef, undef); $message = "Unknown command with braces `\@$command'" if (!$no_open); return (1, $result_text, $message); } 1;