1# HTML.pm: output tree as HTML.
2#
3# Copyright 2011-2020 Free Software Foundation, Inc.
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License,
8# or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <http://www.gnu.org/licenses/>.
17#
18#
19# There are three categories of formatting functions that can be
20# replaced by the user, together with the hash with default functions:
21#  * command tree element formatting functions registered in
22#    %default_commands_conversion
23#  * type tree element (element without @-command) formatting
24#    functions, registered in %default_types_conversion
25#  * other formatting functions, registered in
26#    %default_formatting_references
27#
28# The functions used in the default case for all the functions
29# that may be replaced should not have side effects, such that
30# users can overrides them independently without risking unwanted
31# results.  Also in formatting functions, the state of the
32# converter should only be accessed through functions, such
33# as in_math, in_preformatted, preformatted_classes_stack and
34# similar functions.
35#
36# In most formatting functions, the case where $self->in_string() is
37# true should be handled explicitely and the simplest formatting should be
38# done in that case, without any HTML element such that the result
39# can be in an attribute or in a comment.
40#
41# FIXME: there is already a case with a side effect, with the
42# variable $html_menu_entry_index.
43#
44# Original author: Patrice Dumas <pertusus@free.fr>
45
46package Texinfo::Convert::HTML;
47
48use 5.00405;
49
50# See 'The "Unicode Bug"' under 'perlunicode' man page.  This means
51# that regular expressions will treat characters 128-255 in a Perl string
52# the same regardless of whether the string is using a UTF-8 encoding.
53#  For older Perls, you can use utf8::upgrade on the strings, where the
54# difference matters.
55use if $] >= 5.012, feature => 'unicode_strings';
56
57use strict;
58
59use Texinfo::Convert::Converter;
60use Texinfo::Common;
61use Texinfo::Convert::Texinfo;
62use Texinfo::Convert::Text;
63use Texinfo::Convert::Unicode;
64use Texinfo::Convert::NodeNameNormalization;
65
66use Carp qw(cluck confess);
67
68use File::Copy qw(copy);
69
70require Exporter;
71use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
72@ISA = qw(Exporter Texinfo::Convert::Converter);
73
74%EXPORT_TAGS = ( 'all' => [ qw(
75  convert
76  convert_tree
77  output
78  output_internal_links
79) ] );
80
81@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
82
83@EXPORT = qw(
84);
85
86$VERSION = '6.8';
87
88# misc commands that are of use for formatting.
89my %formatting_misc_commands = %Texinfo::Convert::Text::formatting_misc_commands;
90my %no_brace_commands = %Texinfo::Common::no_brace_commands;
91my %accent_commands = %Texinfo::Common::accent_commands;
92my %misc_commands = %Texinfo::Common::misc_commands;
93my %sectioning_commands = %Texinfo::Common::sectioning_commands;
94my %def_commands = %Texinfo::Common::def_commands;
95my %ref_commands = %Texinfo::Common::ref_commands;
96my %brace_commands = %Texinfo::Common::brace_commands;
97my %block_commands = %Texinfo::Common::block_commands;
98my %menu_commands = %Texinfo::Common::menu_commands;
99my %root_commands = %Texinfo::Common::root_commands;
100my %preformatted_commands = %Texinfo::Common::preformatted_commands;
101my %math_commands = %Texinfo::Common::math_commands;
102my %explained_commands = %Texinfo::Common::explained_commands;
103my %item_container_commands = %Texinfo::Common::item_container_commands;
104my %raw_commands = %Texinfo::Common::raw_commands;
105my %format_raw_commands = %Texinfo::Common::format_raw_commands;
106my %inline_commands = %Texinfo::Common::inline_commands;
107my %inline_format_commands = %Texinfo::Common::inline_format_commands;
108my %code_style_commands       = %Texinfo::Common::code_style_commands;
109my %regular_font_style_commands = %Texinfo::Common::regular_font_style_commands;
110my %preformatted_code_commands = %Texinfo::Common::preformatted_code_commands;
111my %default_index_commands = %Texinfo::Common::default_index_commands;
112my %style_commands = %Texinfo::Common::style_commands;
113my %align_commands = %Texinfo::Common::align_commands;
114my %region_commands = %Texinfo::Common::region_commands;
115my %context_brace_commands = %Texinfo::Common::context_brace_commands;
116my %letter_no_arg_commands = %Texinfo::Common::letter_no_arg_commands;
117
118my %small_alias;
119for my $cmd ('example', 'display', 'format', 'lisp', 'quotation',
120             'indentedblock') {
121  $small_alias{'small'.$cmd} = $cmd;
122};
123
124foreach my $def_command (keys(%def_commands)) {
125  $formatting_misc_commands{$def_command} = 1 if ($misc_commands{$def_command});
126}
127
128# FIXME remove raw commands?
129my %format_context_commands = (%block_commands, %root_commands);
130
131foreach my $misc_context_command('tab', 'item', 'itemx', 'headitem') {
132  $format_context_commands{$misc_context_command} = 1;
133}
134
135my %composition_context_commands = (%preformatted_commands, %root_commands,
136  %menu_commands, %align_commands);
137$composition_context_commands{'float'} = 1;
138
139my %pre_class_types;
140
141# FIXME allow customization? (also in DocBook)
142my %upper_case_commands = ( 'sc' => 1 );
143
144sub in_math($)
145{
146  my $self = shift;
147  return $self->{'document_context'}->[-1]->{'math'};
148}
149
150# set if in menu or preformatted command
151sub in_preformatted($)
152{
153  my $self = shift;
154  my $context = $self->{'document_context'}->[-1]->{'composition_context'}->[-1];
155  if ($preformatted_commands{$context}
156      or $pre_class_types{$context}
157      or ($menu_commands{$context} and $self->_in_preformatted_in_menu())) {
158    return $context;
159  } else {
160    return undef;
161  }
162}
163
164sub in_upper_case($)
165{
166  my $self = shift;
167  return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'};
168}
169
170sub in_space_protected($)
171{
172  my $self = shift;
173  return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'};
174}
175
176sub in_code($)
177{
178  my $self = shift;
179  return $self->{'document_context'}->[-1]->{'monospace'}->[-1];
180}
181
182sub in_string($)
183{
184  my $self = shift;
185  return $self->{'document_context'}->[-1]->{'string'};
186}
187
188sub in_verbatim($)
189{
190  my $self = shift;
191  return $self->{'document_context'}->[-1]->{'verbatim'};
192}
193
194sub in_raw($)
195{
196  my $self = shift;
197  return $self->{'document_context'}->[-1]->{'raw'};
198}
199
200sub paragraph_number($)
201{
202  my $self = shift;
203  return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'};
204}
205
206sub preformatted_number($)
207{
208  my $self = shift;
209  return $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'};
210}
211
212sub count_elements_in_filename($$)
213{
214  my $self = shift;
215  my $filename = shift;
216
217  if (defined($self->{'elements_in_file_count'}->{$filename})) {
218    return $self->{'elements_in_file_count'}->{$filename};
219  }
220  return undef;
221}
222
223sub top_format($)
224{
225  my $self = shift;
226  return $self->{'document_context'}->[-1]->{'formats'}->[-1];
227}
228
229sub commands_stack($)
230{
231  my $self = shift;
232  return @{$self->{'document_context'}->[-1]->{'commands'}};
233}
234
235sub preformatted_classes_stack($)
236{
237  my $self = shift;
238  return @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
239}
240
241sub in_align($)
242{
243  my $self = shift;
244  my $context
245       = $self->{'document_context'}->[-1]->{'composition_context'}->[-1];
246  if ($align_commands{$context}) {
247    return $context;
248  } else {
249    return undef;
250  }
251}
252
253# $COMMAND should be a tree element which is a possible target of a link.
254#
255# Returns a hash that may have these keys set:
256# 'target': A unique string representing the target.  Used as argument to
257#           'id' attribute.
258# 'node_filename', 'section_filename',
259# 'misc_filename', 'filename'.  Possibly others.
260#
261# Some functions cache their results in these hashes.
262sub _get_target($$)
263{
264  my $self = shift;
265  my $command = shift;
266  my $target;
267  if (!defined($command)) {
268    cluck("_get_target command not defined");
269  }
270  if ($self->{'targets'}->{$command}) {
271    $target = $self->{'targets'}->{$command};
272  } elsif ($command->{'cmdname'}
273    # This should only happen for @*heading*, root_commands targets should
274    # already be set.
275            and $sectioning_commands{$command->{'cmdname'}}
276            and !$root_commands{$command->{'cmdname'}}) {
277    $target = $self->_new_sectioning_command_target($command);
278  }
279  return $target;
280}
281
282# API for the elements formatting
283sub command_id($$)
284{
285  my $self = shift;
286  my $command = shift;
287  my $target = $self->_get_target($command);
288  if ($target) {
289    return $target->{'target'};
290  } else {
291    return undef;
292  }
293}
294
295sub command_contents_target($$$)
296{
297  my $self = shift;
298  my $command = shift;
299  my $contents_or_shortcontents = shift;
300  $contents_or_shortcontents = 'shortcontents'
301    if ($contents_or_shortcontents eq 'summarycontents');
302
303  my $target = $self->_get_target($command);
304  if ($target) {
305    return $target->{$contents_or_shortcontents .'_target'};
306  } else {
307    return undef;
308  }
309}
310
311# Return href target for linking to this command
312sub command_target($$)
313{
314  my $self = shift;
315  my $command = shift;
316
317  if ($command->{'extra'}
318      and $command->{'extra'}->{'associated_node'}) {
319    $command = $command->{'extra'}->{'associated_node'};
320  }
321  my $target = $self->_get_target($command);
322  if ($target) {
323    return $target->{'target'};
324  } else {
325    return undef;
326  }
327}
328
329sub command_filename($$)
330{
331  my $self = shift;
332  my $command = shift;
333
334  my $target = $self->_get_target($command);
335  if ($target) {
336    if (defined($target->{'filename'})) {
337      return $target->{'filename'};
338    }
339    my ($element, $root_command) = $self->_get_element($command, 1);
340
341    if (defined($root_command)) {
342      $target->{'root_command'} = $root_command;
343    }
344    if (defined($element)) {
345      $target->{'element'} = $element;
346      $target->{'filename'} = $element->{'filename'};
347      return $element->{'filename'};
348    }
349  }
350  return undef;
351}
352
353sub command_element($$)
354{
355  my $self = shift;
356  my $command = shift;
357
358  my $target = $self->_get_target($command);
359  if ($target) {
360    $self->command_filename($command);
361    return $target->{'element'};
362  }
363  return undef;
364}
365
366sub command_element_command($$)
367{
368  my $self = shift;
369  my $command = shift;
370
371  my ($element, $root_command) = $self->_get_element($command);
372  #my $element = $self->command_element($command);
373  if ($element and $element->{'extra'}) {
374    return $element->{'extra'}->{'element_command'};
375  }
376  return undef;
377}
378
379sub element_command($$)
380{
381  my $self = shift;
382  my $element = shift;
383
384  if ($element and $element->{'extra'}) {
385    if ($element->{'extra'}->{'element_command'}) {
386      return $element->{'extra'}->{'element_command'};
387    } elsif ($element->{'extra'}->{'special_element'}) {
388      return $element;
389    }
390  }
391  return undef;
392}
393
394sub command_node($$)
395{
396  my $self = shift;
397  my $command = shift;
398
399  my $target = $self->_get_target($command);
400  if ($target) {
401    $self->command_filename($command);
402    my $root_command = $target->{'root_command'};
403    if (defined($root_command)) {
404      if ($root_command->{'cmdname'} and $root_command->{'cmdname'} eq 'node') {
405        return $root_command;
406      }
407      if ($root_command->{'extra'} and $root_command->{'extra'}->{'associated_node'}) {
408        return $root_command->{'extra'}->{'associated_node'};
409      }
410    }
411  }
412  return undef;
413}
414
415# Return string for linking to $COMMAND with <a href>
416sub command_href($$;$$)
417{
418  my $self = shift;
419  my $command = shift;
420  my $filename = shift;
421  my $link_command = shift;
422
423  $filename = $self->{'current_filename'} if (!defined($filename));
424
425  if ($command->{'manual_content'}) {
426    return $self->_external_node_href($command, $filename, $link_command);
427  }
428
429  my $target = $self->command_target($command);
430  return '' if (!defined($target));
431  my $href = '';
432
433  my $target_filename = $self->command_filename($command);
434  if (!defined($target_filename)) {
435    # Happens if there are no pages, for example if OUTPUT is set to ''
436    # as in the test cases.  Also for things in @titlepage when
437    # titlepage is not output.
438    if ($self->{'elements'} and $self->{'elements'}->[0]
439       and defined($self->{'elements'}->[0]->{'filename'})) {
440      # In that case use the first page.
441      $target_filename = $self->{'elements'}->[0]->{'filename'};
442    }
443  }
444  if (defined($target_filename)) {
445    if (!defined($filename)
446         or $filename ne $target_filename) {
447      $href .= $target_filename;
448      # omit target if the command is an element command, there is only
449      # one element in file and there is a file in the href
450      my $command_element_command = $self->command_element_command($command);
451      if (defined($filename)
452          and defined($command_element_command)
453          and ($command_element_command eq $command
454            or (defined($command_element_command->{'extra'})
455              and defined($command_element_command->{'extra'}->{'associated_section'})
456              and $command_element_command->{'extra'}->{'associated_section'}
457                    eq $command))) {
458        my $count_elements_in_file = $self->count_elements_in_filename($target_filename);
459        if (defined($count_elements_in_file) and $count_elements_in_file == 1) {
460          $target = '';
461        }
462      }
463    }
464  }
465  $href .= '#' . $target if ($target ne '');
466  return $href;
467}
468
469my %contents_command_element_name = (
470  'contents' => 'Contents',
471  'shortcontents' => 'Overview',
472  'summarycontents' => 'Overview',
473);
474
475# Return string for linking to $CONTENTS_OR_SHORTCONTENTS associated
476# element from $COMMAND with <a href>
477sub command_contents_href($$$$)
478{
479  my $self = shift;
480  my $command = shift;
481  my $contents_or_shortcontents = shift;
482  my $filename = shift;
483
484  my $href;
485  my $name = $contents_command_element_name{$contents_or_shortcontents};
486
487  my $target = $self->command_contents_target($command, $contents_or_shortcontents);
488
489  my $target_element = $self->special_element($name);
490  my $target_filename;
491  # !defined happens when called as convert() and not output()
492  if (defined($target_element)) {
493    $target_filename = $self->command_filename($target_element);
494  }
495  if (defined($target_filename) and
496      (!defined($filename)
497       or $filename ne $target_filename)) {
498    $href .= $target_filename;
499  }
500  $href .= '#' . $target if ($target ne '');
501  return $href;
502}
503
504# Return text to be used for a hyperlink to $COMMAND.
505# $TYPE refers to the type of value returned from this function:
506#  'text' - return text
507#  'tree' - return a tree
508#  'tree_nonumber' - return tree representing text without a chapter number
509#                    being included.
510#  'string' - return simpler text that can be used in element attributes
511sub command_text($$;$)
512{
513  my $self = shift;
514  my $command = shift;
515  my $type = shift;
516
517  if (!defined($type)) {
518    $type = 'text';
519  }
520  if (!defined($command)) {
521    cluck "in command_text($type) command not defined";
522  }
523
524  if ($command->{'manual_content'}) {
525    my $node_content = [];
526    $node_content = $command->{'node_content'}
527      if (defined($command->{'node_content'}));
528    my $tree;
529    if ($command->{'manual_content'}) {
530      $tree = {'type' => '_code',
531          'contents' => [{'text' => '('}, @{$command->{'manual_content'}},
532                         {'text' => ')'}, @$node_content]};
533    } else {
534      $tree = {'type' => '_code',
535          'contents' => $node_content};
536    }
537    if ($type eq 'tree') {
538      return $tree;
539    } else {
540      if ($type eq 'string') {
541        $tree = {'type' => '_string',
542                 'contents' => [$tree]};
543      }
544      my $result = $self->convert_tree_new_formatting_context(
545            $tree, $command->{'cmdname'});
546      return $result;
547    }
548  }
549
550  my $target = $self->_get_target($command);
551  if ($target) {
552    my $explanation;
553    $explanation = "command_text \@$command->{'cmdname'}"
554       if ($command->{'cmdname'});
555    if (defined($target->{$type})) {
556      return $target->{$type};
557    }
558    my $tree;
559    if (!$target->{'tree'}) {
560      if ($command->{'extra'}
561               and $command->{'extra'}->{'special_element'}) {
562        my $special_element = $command->{'extra'}->{'special_element'};
563        $tree = $self->get_conf('SPECIAL_ELEMENTS_NAME')->{$special_element};
564        $explanation = "command_text $special_element";
565      } elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'node'
566                                          or $command->{'cmdname'} eq 'anchor')) {
567        $tree = {'type' => '_code',
568                 'contents' => $command->{'extra'}->{'node_content'}};
569      } elsif ($command->{'cmdname'} and ($command->{'cmdname'} eq 'float')) {
570        $tree = $self->_float_type_number($command);
571      } elsif ($command->{'extra'}->{'missing_argument'}) {
572        if ($type eq 'tree' or $type eq 'tree_nonumber') {
573          return {};
574        } else {
575          return '';
576        }
577      } else {
578        if (!$command->{'args'}->[0]->{'contents'}) {
579          cluck "No misc_content: "
580            .Texinfo::Common::_print_current($command);
581        }
582        if (defined($command->{'number'})
583            and ($self->get_conf('NUMBER_SECTIONS')
584                 or !defined($self->get_conf('NUMBER_SECTIONS')))) {
585          if ($command->{'cmdname'} eq 'appendix' and $command->{'level'} == 1) {
586            $tree = $self->gdt('Appendix {number} {section_title}',
587                             {'number' => {'text' => $command->{'number'}},
588                              'section_title'
589                                => {'contents'
590                                    => $command->{'args'}->[0]->{'contents'}}});
591          } else {
592            $tree = $self->gdt('{number} {section_title}',
593                             {'number' => {'text' => $command->{'number'}},
594                              'section_title'
595                                => {'contents'
596                                    => $command->{'args'}->[0]->{'contents'}}});
597          }
598        } else {
599          $tree = {'contents' => [@{$command->{'args'}->[0]->{'contents'}}]};
600        }
601
602        $target->{'tree_nonumber'}
603          = {'contents' => $command->{'args'}->[0]->{'contents'}};
604      }
605      $target->{'tree'} = $tree;
606    } else {
607      $tree = $target->{'tree'};
608    }
609    return $target->{'tree_nonumber'} if ($type eq 'tree_nonumber'
610                                          and $target->{'tree_nonumber'});
611    return $tree if ($type eq 'tree' or $type eq 'tree_nonumber');
612
613    $self->_new_document_context($command->{'cmdname'});
614
615    if ($type eq 'string') {
616      $tree = {'type' => '_string',
617               'contents' => [$tree]};
618    }
619
620    if ($type =~ /^(.*)_nonumber$/) {
621      $tree = $target->{'tree_nonumber'}
622        if (defined($target->{'tree_nonumber'}));
623    }
624    $self->{'ignore_notice'}++;
625    $target->{$type} = $self->_convert($tree, $explanation);
626    $self->{'ignore_notice'}--;
627
628    pop @{$self->{'document_context'}};
629    return $target->{$type};
630  }
631  return undef;
632}
633
634# Return the element in the tree that $LABEL refers to.
635sub label_command($$)
636{
637  my $self = shift;
638  my $label = shift;
639  return $self->{'labels'}->{$label};
640}
641
642sub special_element($$)
643{
644  my $self = shift;
645  my $type = shift;
646  return $self->{'special_elements_types'}->{$type};
647}
648
649sub global_element($$)
650{
651  my $self = shift;
652  my $type = shift;
653  return $self->{'global_target_elements'}->{$type};
654}
655
656# it is considered 'top' only if element corresponds to @top or
657# element is a node
658sub element_is_top($$)
659{
660  my $self = shift;
661  my $element = shift;
662  return ($self->{'global_target_elements'}->{'Top'}
663    and $self->{'global_target_elements'}->{'Top'} eq $element
664    and $element->{'extra'}
665    and (($element->{'extra'}->{'section'}
666         and $element->{'extra'}->{'section'}->{'cmdname'} eq 'top')
667         or ($element->{'extra'}->{'element_command'}
668             and $element->{'extra'}->{'element_command'}->{'cmdname'} eq 'node')));
669}
670
671sub default_formatting_function($$)
672{
673  my $self = shift;
674  my $format = shift;
675  return $self->{'default_formatting_functions'}->{$format};
676}
677
678# used for customization only (in t2h_singular.init)
679sub get_value($$)
680{
681  my $self = shift;
682  my $value = shift;
683  if (defined($self->{'parser'})
684      and exists ($self->{'parser'}->{'values'}->{$value})) {
685    return $self->{'parser'}->{'values'}->{$value};
686  } else {
687    return undef;
688  }
689}
690
691# This function should be used in formatting functions when some
692# Texinfo tree need to be converted.
693sub convert_tree_new_formatting_context($$;$$)
694{
695  my $self = shift;
696  my $tree = shift;
697  my $context_string = shift;
698  my $multiple_pass = shift;
699  if (defined($context_string)) {
700    $self->_new_document_context($context_string);
701  }
702  if ($multiple_pass) {
703    $self->{'ignore_notice'}++;
704    push @{$self->{'multiple_pass'}}, $multiple_pass;
705  }
706  my $result = $self->convert_tree($tree);
707  if (defined($context_string)) {
708    pop @{$self->{'document_context'}};
709  }
710  if ($multiple_pass) {
711    $self->{'ignore_notice'}--;
712    pop @{$self->{'multiple_pass'}};
713  }
714  return $result;
715}
716
717# see http://www.w3.org/TR/REC-html40/types.html#type-links
718my %BUTTONS_REL =
719(
720 'Top',         'start',
721 'Contents',    'contents',
722 'Overview',    '',
723 'Index',       'index',
724 'This',        '',
725 'Back',        'prev',
726 'FastBack',    '',
727 'Prev',        'prev',
728 'Up',          'up',
729 'Next',        'next',
730 'NodeUp',      'up',
731 'NodeNext',    'next',
732 'NodePrev',    'prev',
733 'NodeForward', '',
734 'NodeBack',    '',
735 'Forward',     'next',
736 'FastForward', '',
737 'About' ,      'help',
738 'First',       '',
739 'Last',        '',
740 'NextFile',    'next',
741 'PrevFile',    'prev',
742);
743
744my %BUTTONS_ACCESSKEY =
745(
746 'Top',         '',
747 'Contents',    '',
748 'Overview',    '',
749 'Index',       '',
750 'This',        '',
751 'Back',        'p',
752 'FastBack',    '',
753 'Prev',        'p',
754 'Up',          'u',
755 'Next',        'n',
756 'NodeUp',      'u',
757 'NodeNext',    'n',
758 'NodePrev',    'p',
759 'NodeForward', '',
760 'NodeBack',    '',
761 'Forward',     'n',
762 'FastForward', '',
763 'About' ,      '',
764 'First',       '',
765 'Last',        '',
766 'NextFile',    '',
767 'PrevFile',    '',
768);
769
770my %BUTTONS_EXAMPLE =
771    (
772     'Top',         ' &nbsp; ',
773     'Contents',    ' &nbsp; ',
774     'Overview',    ' &nbsp; ',
775     'Index',       ' &nbsp; ',
776     'This',        '1.2.3',
777     'Back',        '1.2.2',
778     'FastBack',    '1',
779     'Prev',        '1.2.2',
780     'Up',          '1.2',
781     'Next',        '1.2.4',
782     'NodeUp',      '1.2',
783     'NodeNext',    '1.2.4',
784     'NodePrev',    '1.2.2',
785     'NodeForward', '1.2.4',
786     'NodeBack',    '1.2.2',
787     'Forward',     '1.2.4',
788     'FastForward', '2',
789     'About',       ' &nbsp; ',
790     'First',       '1.',
791     'Last',        '1.2.4',
792     'NextFile',    ' &nbsp; ',
793     'PrevFile',    ' &nbsp; ',
794    );
795
796
797# insert here name of icon images for buttons
798# Icons are used, if ICONS and resp. value are set
799my %ACTIVE_ICONS = (
800     'Top',         '',
801     'Contents',    '',
802     'Overview',    '',
803     'Index',       '',
804     'This',        '',
805     'Back',        '',
806     'FastBack',    '',
807     'Prev',        '',
808     'Up',          '',
809     'Next',        '',
810     'NodeUp',      '',
811     'NodeNext',    '',
812     'NodePrev',    '',
813     'NodeForward', '',
814     'NodeBack',    '',
815     'Forward',     '',
816     'FastForward', '',
817     'About' ,      '',
818     'First',       '',
819     'Last',        '',
820     'NextFile',    '',
821     'PrevFile',    '',
822     ' ',           '',
823);
824
825# insert here name of icon images for these, if button is inactive
826my %PASSIVE_ICONS = (
827     'Top',         '',
828     'Contents',    '',
829     'Overview',    '',
830     'Index',       '',
831     'This',        '',
832     'Back',        '',
833     'FastBack',    '',
834     'Prev',        '',
835     'Up',          '',
836     'Next',        '',
837     'NodeUp',      '',
838     'NodeNext',    '',
839     'NodePrev',    '',
840     'NodeForward', '',
841     'NodeBack',    '',
842     'Forward',     '',
843     'FastForward', '',
844     'About',       '',
845     'First',       '',
846     'Last',        '',
847     'NextFile',    '',
848     'PrevFile',    '',
849);
850
851my (%BUTTONS_TEXT, %BUTTONS_GOTO, %BUTTONS_NAME, %SPECIAL_ELEMENTS_NAME);
852
853my %defaults = (
854  'ENABLE_ENCODING'      => 0,
855  'FORMAT_MENU'           => 'sectiontoc',
856  'OUTPUT_ENCODING_NAME'  => 'utf-8',
857  'OUTFILE'              => undef,
858  'SUBDIR'               => undef,
859  'USE_NODES'            => 1,
860  'USE_NODE_DIRECTIONS'  => undef,
861  'CONTENTS_OUTPUT_LOCATION' => 'after_top',
862  'SPLIT'                => 'node',
863# if set style is added in attribute.
864  'INLINE_CSS_STYLE'     => 0,
865# if set, no css is used.
866  'NO_CSS'               => 0,
867  'JS_WEBLABELS'	 => 'generate',
868  'JS_WEBLABELS_FILE'	 => 'js_licenses.html', # no clash with node name
869  'OPEN_QUOTE_SYMBOL'    => '&lsquo;',
870  'CLOSE_QUOTE_SYMBOL'   => '&rsquo;',
871  'USE_ISO'              => 1,
872  'TOP_FILE'             => 'index.html',
873  'EXTENSION'            => 'html',
874  'TOP_NODE_FILE_TARGET' => 'index.html',
875  'TRANSLITERATE_FILE_NAMES' => 1,
876  'USE_LINKS'            => 1,
877  'USE_NUMERIC_ENTITY'   => 1,
878  'ENABLE_ENCODING_USE_ENTITY'   => 1,
879  'DATE_IN_HEADER'       => 0,
880  'AVOID_MENU_REDUNDANCY' => 0,
881  'HEADERS'              => 1,
882  'DO_ABOUT'             => 0,
883  'USE_ACCESSKEY'        => 1,
884  'USE_REL_REV'          => 1,
885  'NODE_NAME_IN_MENU'    => 1,
886  'NODE_NAME_IN_INDEX'   => 1,
887  'XREF_USE_NODE_NAME_ARG' => undef,
888  'XREF_USE_FLOAT_LABEL'   => 0,
889  'OVERVIEW_LINK_TO_TOC' => 1,
890  'COMPLEX_FORMAT_IN_TABLE' => 0,
891  'WORDS_IN_PAGE'        => 300,
892  # _default_panel_button_dynamic_direction use nodes direction based on USE_NODE_DIRECTIONS
893  # or USE_NODES if USE_NODE_DIRECTIONS is undefined
894  'SECTION_BUTTONS'      => [[ 'Next', \&_default_panel_button_dynamic_direction ],
895                             [ 'Prev', \&_default_panel_button_dynamic_direction ],
896                             [ 'Up', \&_default_panel_button_dynamic_direction ], ' ',
897                             'Contents', 'Index'],
898  'SECTION_FOOTER_BUTTONS' => [[ 'Next', \&_default_panel_button_dynamic_direction_section_footer ],
899                              [ 'Prev', \&_default_panel_button_dynamic_direction_section_footer ],
900                              [ 'Up', \&_default_panel_button_dynamic_direction_section_footer ], ' ',
901                              'Contents', 'Index'],
902  'LINKS_BUTTONS'        => ['Top', 'Index', 'Contents', 'About',
903                              'NodeUp', 'NodeNext', 'NodePrev'],
904  'NODE_FOOTER_BUTTONS'  => [[ 'Next', \&_default_panel_button_dynamic_direction_node_footer ],
905                             [ 'Prev', \&_default_panel_button_dynamic_direction_node_footer ],
906                             [ 'Up', \&_default_panel_button_dynamic_direction_node_footer ],
907                             ' ', 'Contents', 'Index'],
908  'misc_elements_targets'   => {
909                             'Overview' => 'SEC_Overview',
910                             'Contents' => 'SEC_Contents',
911                             'Footnotes' => 'SEC_Foot',
912                             'About' => 'SEC_About',
913                             'Top' => 'SEC_Top',
914                            },
915  'misc_pages_file_string' => {
916                              'Contents' => '_toc',
917                              'Overview' => '_ovr',
918                              'Footnotes' => '_fot',
919                              'About' => '_abt',
920                            },
921  'frame_pages_file_string' => {
922                              'Frame' => '_frame',
923                              'Toc_Frame' => '_toc_frame',
924                              },
925  'misc_elements_order'  => ['Footnotes', 'Contents', 'Overview', 'About'],
926  'DOCTYPE'              => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
927  'FRAMESET_DOCTYPE'     => '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
928  'DEFAULT_RULE'         => '<hr>',
929  'BIG_RULE'             => '<hr>',
930  'MENU_SYMBOL'          => '&bull;',
931  'MENU_ENTRY_COLON'     => ':',
932  'INDEX_ENTRY_COLON'    => ':',
933  'BODYTEXT'             => undef,
934  'documentlanguage'     => 'en',
935  'xrefautomaticsectiontitle' => 'on',
936  'SHOW_TITLE'           => 1,
937  'SECTION_NAME_IN_TITLE' => 0,
938  'USE_TITLEPAGE_FOR_TITLE' => 0,
939  'MONOLITHIC'           => 1,
940  'CHAPTER_HEADER_LEVEL' => 2,
941  'MAX_HEADER_LEVEL'     => 4,
942  'FOOTNOTE_END_HEADER_LEVEL' => 4,
943  'FOOTNOTE_SEPARATE_HEADER_LEVEL' => 4,
944
945  'BUTTONS_REL'          => \%BUTTONS_REL,
946  'BUTTONS_ACCESSKEY'    => \%BUTTONS_ACCESSKEY,
947  'BUTTONS_EXAMPLE'      => \%BUTTONS_EXAMPLE,
948  'BUTTONS_GOTO'         => \%BUTTONS_GOTO,
949  'BUTTONS_NAME'         => \%BUTTONS_NAME,
950  'BUTTONS_TEXT'         => \%BUTTONS_TEXT,
951  'ACTIVE_ICONS'         => \%ACTIVE_ICONS,
952  'PASSIVE_ICONS'        => \%PASSIVE_ICONS,
953  'SPECIAL_ELEMENTS_NAME' => \%SPECIAL_ELEMENTS_NAME,
954  'SPECIAL_ELEMENTS_CLASS' => {
955    'About'       => 'about',
956    'Contents'    => 'contents',
957    'Overview'    => 'shortcontents',
958    'Footnotes'   => 'footnotes',
959  },
960  'jslicenses' => {},         # for outputting licences file
961  'jslicenses_element' => {}, # scripts used in current output file
962  'jslicenses_math' => {},    # MathJax scripts
963  'jslicenses_infojs' => {},  # info.js scripts
964  'element_math' => 0,        # whether math has been seen in current file
965  'COPIABLE_ANCHORS' => 1,
966
967  'output_format'        => 'html',
968);
969
970foreach my $buttons ('CHAPTER_BUTTONS', 'MISC_BUTTONS', 'TOP_BUTTONS') {
971  $defaults{$buttons} = [@{$defaults{'SECTION_BUTTONS'}}];
972}
973
974foreach my $buttons ('SECTION_FOOTER_BUTTONS', 'CHAPTER_FOOTER_BUTTONS') {
975  $defaults{$buttons} = [@{$defaults{'SECTION_FOOTER_BUTTONS'}}];
976}
977
978
979my @global_directions = ('First', 'Last', 'Index', 'Top');
980my %global_and_special_directions;
981foreach my $global_direction (@global_directions) {
982  $global_and_special_directions{$global_direction} = 1;
983}
984foreach my $special_element (keys %{$defaults{'SPECIAL_ELEMENTS_CLASS'}}) {
985  $global_and_special_directions{$special_element} = 1;
986}
987
988foreach my $hash (\%BUTTONS_REL, \%BUTTONS_ACCESSKEY,
989                  \%ACTIVE_ICONS, \%PASSIVE_ICONS) {
990  foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %$hash) {
991    $hash->{'FirstInFile'.$button} = $hash->{$button};
992  }
993}
994
995sub _translate_names($)
996{
997  my $self = shift;
998  #print STDERR "encoding_name: ".$self->get_conf('OUTPUT_ENCODING_NAME')." documentlanguage: ".$self->get_conf('documentlanguage')."\n";
999
1000
1001  %BUTTONS_TEXT = (
1002     'Top',         $self->gdt('Top'),
1003     'Contents',    $self->gdt('Contents'),
1004     'Overview',    $self->gdt('Overview'),
1005     'Index',       $self->gdt('Index'),
1006     ' ',           ' &nbsp; ',
1007     'This',        $self->gdt('current'),
1008     'Back',        ' &lt; ',
1009     'FastBack',    ' &lt;&lt; ',
1010     'Prev',        $self->gdt('Prev'),
1011     'Up',          $self->gdt(' Up '),
1012     'Next',        $self->gdt('Next'),
1013     #'NodeUp',      $self->gdt('Node up'),
1014     'NodeUp',      $self->gdt('Up'),
1015     #'NodeNext',    $self->gdt('Next node'),
1016     'NodeNext',    $self->gdt('Next'),
1017     #'NodePrev',    $self->gdt('Previous node'),
1018     'NodePrev',    $self->gdt('Previous'),
1019     'NodeForward', $self->gdt('Forward node'),
1020     'NodeBack',    $self->gdt('Back node'),
1021     'Forward',     ' &gt; ',
1022     'FastForward', ' &gt;&gt; ',
1023     'About',       ' ? ',
1024     'First',       ' |&lt; ',
1025     'Last',        ' &gt;| ',
1026     'NextFile',    $self->gdt('Next file'),
1027     'PrevFile',    $self->gdt('Previous file'),
1028  );
1029
1030  foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_TEXT) {
1031    $BUTTONS_TEXT{'FirstInFile'.$button} = $BUTTONS_TEXT{$button};
1032  }
1033
1034  #%BUTTONS_TEXT = %NAVIGATION_TEXT;
1035
1036  %BUTTONS_GOTO = (
1037     'Top',         $self->gdt('Cover (top) of document'),
1038     'Contents',    $self->gdt('Table of contents'),
1039     'Overview',    $self->gdt('Short table of contents'),
1040     'Index',       $self->gdt('Index'),
1041     'This',        $self->gdt('Current section'),
1042     'Back',        $self->gdt('Previous section in reading order'),
1043     'FastBack',    $self->gdt('Beginning of this chapter or previous chapter'),
1044     'Prev',        $self->gdt('Previous section on same level'),
1045     'Up',          $self->gdt('Up section'),
1046     'Next',        $self->gdt('Next section on same level'),
1047     'NodeUp',      $self->gdt('Up node'),
1048     'NodeNext',    $self->gdt('Next node'),
1049     'NodePrev',    $self->gdt('Previous node'),
1050     'NodeForward', $self->gdt('Next node in node reading order'),
1051     'NodeBack',    $self->gdt('Previous node in node reading order'),
1052     'Forward',     $self->gdt('Next section in reading order'),
1053     'FastForward', $self->gdt('Next chapter'),
1054     'About' ,      $self->gdt('About (help)'),
1055     'First',       $self->gdt('First section in reading order'),
1056     'Last',        $self->gdt('Last section in reading order'),
1057     'NextFile',    $self->gdt('Forward section in next file'),
1058     'PrevFile',    $self->gdt('Back section in previous file'),
1059  );
1060
1061  foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_GOTO) {
1062    $BUTTONS_GOTO{'FirstInFile'.$button} = $BUTTONS_GOTO{$button};
1063  }
1064
1065  %BUTTONS_NAME = (
1066     'Top',         $self->gdt('Top'),
1067     'Contents',    $self->gdt('Contents'),
1068     'Overview',    $self->gdt('Overview'),
1069     'Index',       $self->gdt('Index'),
1070     ' ',           ' ',
1071     'This',        $self->gdt('This'),
1072     'Back',        $self->gdt('Back'),
1073     'FastBack',    $self->gdt('FastBack'),
1074     'Prev',        $self->gdt('Prev'),
1075     'Up',          $self->gdt('Up'),
1076     'Next',        $self->gdt('Next'),
1077     'NodeUp',      $self->gdt('NodeUp'),
1078     'NodeNext',    $self->gdt('NodeNext'),
1079     'NodePrev',    $self->gdt('NodePrev'),
1080     'NodeForward', $self->gdt('NodeForward'),
1081     'NodeBack',    $self->gdt('NodeBack'),
1082     'Forward',     $self->gdt('Forward'),
1083     'FastForward', $self->gdt('FastForward'),
1084     'About',       $self->gdt('About'),
1085     'First',       $self->gdt('First'),
1086     'Last',        $self->gdt('Last'),
1087     'NextFile',    $self->gdt('NextFile'),
1088     'PrevFile',    $self->gdt('PrevFile'),
1089  );
1090
1091  foreach my $button (grep {not exists($global_and_special_directions{$_}) and $_ ne ' '} keys %BUTTONS_NAME) {
1092    $BUTTONS_NAME{'FirstInFile'.$button} = $BUTTONS_NAME{$button};
1093  }
1094
1095  %SPECIAL_ELEMENTS_NAME = (
1096    'About'       => $self->gdt('About This Document'),
1097    'Contents'    => $self->gdt('Table of Contents'),
1098    'Overview'    => $self->gdt('Short Table of Contents'),
1099    'Footnotes'   => $self->gdt('Footnotes'),
1100  );
1101
1102  # delete the tree and formatted results for special elements
1103  # such that they are redone with the new tree when needed.
1104  foreach my $special_element (keys (%SPECIAL_ELEMENTS_NAME)) {
1105    if ($self->{'special_elements_types'}->{$special_element} and
1106        $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}}) {
1107      my $target
1108        = $self->{'targets'}->{$self->{'special_elements_types'}->{$special_element}};
1109      foreach my $key ('text', 'string', 'tree') {
1110        delete $target->{$key};
1111      }
1112    }
1113  }
1114
1115  foreach my $hash (\%BUTTONS_TEXT, \%BUTTONS_GOTO, \%BUTTONS_NAME) {
1116    foreach my $button (keys (%$hash)) {
1117      if (ref($hash->{$button})) {
1118        $hash->{$button} = $self->convert_tree_new_formatting_context(
1119                                       $hash->{$button}, "button $button");
1120      }
1121    }
1122  }
1123  if ($self->{'commands_translation'}) {
1124    my %translated_commands;
1125    foreach my $context ('normal', 'preformatted', 'string') {
1126      foreach my $command (keys(%{$self->{'commands_translation'}->{$context}})) {
1127        $translated_commands{$command} = 1;
1128        delete $self->{'commands_formatting'}->{$context}->{$command};
1129        if (defined($self->{'commands_translation'}->{$context}->{$command})) {
1130          $self->{'commands_formatting'}->{$context}->{$command}
1131           = $self->gdt($self->{'commands_translation'}->{$context}->{$command},
1132                        undef, 'translated_text');
1133        }
1134      }
1135    }
1136    foreach my $command(keys(%translated_commands)) {
1137      $self->_complete_commands_formatting($command);
1138    }
1139  }
1140}
1141
1142
1143sub converter_defaults($$)
1144{
1145  my $self = shift;
1146  my $conf = shift;
1147  if (defined($conf->{'TEXI2HTML'})) {
1148    _set_variables_texi2html();
1149  }
1150  return %defaults;
1151}
1152
1153my $NO_BULLET_LIST_STYLE = 'list-style: none';
1154my $NO_BULLET_LIST_CLASS = 'no-bullet';
1155my $NO_BULLET_LIST_ATTRIBUTE = ' class="'.$NO_BULLET_LIST_CLASS.'"';
1156
1157my $MENU_PRE_STYLE = 'font-family: serif';
1158
1159my %css_map = (
1160     "ul.$NO_BULLET_LIST_CLASS" => "$NO_BULLET_LIST_STYLE",
1161     'pre.menu-comment'       => "$MENU_PRE_STYLE",
1162     'pre.menu-preformatted'  => "$MENU_PRE_STYLE",
1163     'a.summary-letter'       => 'text-decoration: none',
1164     'pre.display'            => 'font-family: inherit',
1165     'span.sansserif'     => 'font-family: sans-serif; font-weight: normal',
1166     'span.roman'         => 'font-family: initial; font-weight: normal',
1167     'span.nolinebreak'   => 'white-space: nowrap',
1168     'kbd'                => 'font-style: oblique',
1169
1170     # The anchor element is wrapped in a <span> rather than a block level
1171     # element to avoid it appearing unless the mouse pointer is directly
1172     # over the text, as it is annoying for anchors to flicker when
1173     # you are moving your pointer elsewhere. "line-height: 0em" stops the
1174     # invisible text from changing vertical spacing.
1175     'a.copiable-anchor' => 'visibility: hidden; '
1176                           .'text-decoration: none; line-height: 0em',
1177     'span:hover a.copiable-anchor'         => 'visibility: visible',
1178);
1179
1180$css_map{'pre.format'} = $css_map{'pre.display'};
1181
1182my %preformatted_commands_context = %preformatted_commands;
1183$preformatted_commands_context{'verbatim'} = 1;
1184
1185my %pre_class_commands;
1186foreach my $preformatted_command (keys(%preformatted_commands_context)) {
1187  # no class for the @small* variants
1188  if ($small_alias{$preformatted_command}) {
1189    $pre_class_commands{$preformatted_command} = $small_alias{$preformatted_command};
1190  } else {
1191    $pre_class_commands{$preformatted_command} = $preformatted_command;
1192  }
1193}
1194$pre_class_commands{'menu'} = 'menu-preformatted';
1195$pre_class_types{'menu_comment'} = 'menu-comment';
1196
1197my %indented_preformatted_commands;
1198foreach my $indented_format ('example', 'display', 'lisp') {
1199  $indented_preformatted_commands{$indented_format} = 1;
1200  $indented_preformatted_commands{"small$indented_format"} = 1;
1201
1202  $css_map{"div.$indented_format"} = 'margin-left: 3.2em';
1203}
1204delete $css_map{"div.lisp"}; # output as div.example instead
1205
1206$css_map{"blockquote.indentedblock"} = 'margin-right: 0em';
1207
1208# types that are in code style in the default case.  '_code' is not
1209# a type that can appear in the tree built from Texinfo code, it is used
1210# to format a tree fragment as if it was in a @code @-command.
1211my %default_code_types = (
1212 '_code' => 1,
1213);
1214
1215# default specification of arguments formatting
1216my %default_commands_args = (
1217  'email' => [['monospace', 'monospacestring'], ['normal']],
1218  'anchor' => [['monospacestring']],
1219  'uref' => [['monospacestring'], ['normal'], ['normal']],
1220  'url' => [['monospacestring'], ['normal'], ['normal']],
1221  'printindex' => [[]],
1222  'sp' => [[]],
1223  'inforef' => [['monospace'],['normal'],['monospacetext']],
1224  'xref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
1225  'pxref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
1226  'ref' => [['monospace'],['normal'],['normal'],['monospacetext'],['normal']],
1227  'image' => [['monospacetext'],['monospacetext'],['monospacetext'],['string', 'normal'],['monospacetext']],
1228  # FIXME shouldn't it better not to convert if later ignored?
1229  'inlinefmt' => [['monospacetext'],['normal']],
1230  'inlinefmtifelse' => [['monospacetext'],['normal'],['normal']],
1231  'inlineraw' => [['monospacetext'],['raw']],
1232  'inlineifclear' => [['monospacetext'],['normal']],
1233  'inlineifset' => [['monospacetext'],['normal']],
1234  'item' => [[]],
1235  'itemx' => [[]],
1236);
1237
1238foreach my $explained_command (keys(%explained_commands)) {
1239  $default_commands_args{$explained_command}
1240     = [['normal'], ['string']];
1241}
1242
1243# Return the default for the function references used for
1244# the formatting of commands, in case a user still wants to call
1245# default @-commands formatting functions when replacing functions,
1246# using code along
1247# &{$self->default_commands_conversion($cmdname)}($self, $cmdname, $command, $content)
1248my %default_commands_conversion;
1249
1250sub default_commands_conversion($$)
1251{
1252  my $self = shift;
1253  my $command = shift;
1254  return $default_commands_conversion{$command};
1255}
1256
1257my %kept_misc_commands;
1258
1259my @informative_global_commands = ('contents', 'shortcontents',
1260  'summarycontents', 'allowcodebreaks', 'documentlanguage',
1261  'footnotestyle', 'documentencoding',
1262  'xrefautomaticsectiontitle', 'deftypefnnewline');
1263# taken from global
1264# 'documentencoding'
1265# 'novalidate'
1266foreach my $misc_command(@informative_global_commands,
1267        'verbatiminclude', 'insertcopying', 'printindex', 'listoffloats',
1268        'author', 'subtitle',
1269        'title', keys(%default_index_commands),
1270        keys(%formatting_misc_commands)) {
1271  $kept_misc_commands{$misc_command} = 1;
1272}
1273
1274sub converter_global_commands($)
1275{
1276  return @informative_global_commands;
1277}
1278
1279foreach my $misc_command (keys(%misc_commands)) {
1280  $default_commands_conversion{$misc_command} = undef
1281    unless ($kept_misc_commands{$misc_command});
1282}
1283
1284foreach my $ignored_brace_commands ('caption', 'shortcaption',
1285  'hyphenation', 'sortas') {
1286  $default_commands_conversion{$ignored_brace_commands} = undef;
1287}
1288
1289# commands that leads to advancing the paragraph number.  This is mostly
1290# used to determine the first line, in fact.
1291my %advance_paragraph_count_commands;
1292foreach my $command (keys(%block_commands)) {
1293  next if ($menu_commands{$command}
1294            or $block_commands{$command} eq 'raw');
1295  $advance_paragraph_count_commands{$command} = 1;
1296}
1297
1298foreach my $ignored_block_commands ('ignore', 'macro', 'rmacro', 'copying',
1299  'documentdescription', 'titlepage', 'direntry') {
1300  $default_commands_conversion{$ignored_block_commands} = undef;
1301};
1302
1303# Formatting of commands without args
1304
1305# The hash holding the defaults for the formatting of
1306# most commands without args.  It has three contexts as keys,
1307# 'normal' in normal text, 'preformatted' in @example and similar
1308# commands, and 'string' for contexts where HTML elements should not
1309# be used.
1310my %default_commands_formatting;
1311
1312foreach my $command (keys(%{$Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}})) {
1313  $default_commands_formatting{'normal'}->{$command} =
1314    $Texinfo::Convert::Converter::default_xml_commands_formatting{'normal'}->{$command};
1315}
1316
1317$default_commands_formatting{'normal'}->{' '} = '&nbsp;';
1318$default_commands_formatting{'normal'}->{"\t"} = '&nbsp;';
1319$default_commands_formatting{'normal'}->{"\n"} = '&nbsp;';
1320
1321my %default_commands_translation;
1322# possible example of use, right now not used, as the generic
1323# translated command with gdt tree is used.
1324#$default_commands_translation{'normal'}->{'error'} = 'error--&gt;';
1325## This is used to have gettext pick up the chain to be translated
1326#if (0) {
1327#  my $not_existing;
1328#  $not_existing->gdt('error--&gt;');
1329#}
1330
1331$default_commands_formatting{'normal'}->{'enddots'}
1332    = '<small class="enddots">...</small>';
1333$default_commands_formatting{'preformatted'}->{'enddots'} = '...';
1334$default_commands_formatting{'normal'}->{'*'} = '<br>';
1335$default_commands_formatting{'preformatted'}->{'*'} = "\n";
1336
1337
1338sub _convert_no_arg_command($$$)
1339{
1340  my $self = shift;
1341  my $cmdname = shift;
1342  my $command = shift;
1343
1344  if ($cmdname eq 'click' and $command->{'extra'}
1345      and exists($command->{'extra'}->{'clickstyle'})) {
1346    my $click_cmdname = $command->{'extra'}->{'clickstyle'};
1347    if (($self->in_preformatted() or $self->in_math()
1348         and $self->{'commands_formatting'}->{'preformatted'}->{$click_cmdname})
1349        or ($self->in_string() and
1350            $self->{'commands_formatting'}->{'string'}->{$click_cmdname})
1351        or ($self->{'commands_formatting'}->{'normal'}->{$click_cmdname})) {
1352      $cmdname = $click_cmdname;
1353    }
1354  }
1355  if ($self->in_upper_case() and $letter_no_arg_commands{$cmdname}
1356      and $self->{'commands_formatting'}->{'normal'}->{uc($cmdname)}) {
1357    $cmdname = uc($cmdname);
1358  }
1359
1360  my $result;
1361  if ($self->{'translated_commands'}->{$cmdname}) {
1362    return $self->convert_tree(
1363         $self->gdt($self->{'translated_commands'}->{$cmdname}));
1364  }
1365  if ($self->in_preformatted() or $self->in_math()) {
1366    $result = $self->{'commands_formatting'}->{'preformatted'}->{$cmdname};
1367  } elsif ($self->in_string()) {
1368    $result = $self->{'commands_formatting'}->{'string'}->{$cmdname};
1369  } else {
1370    $result = $self->{'commands_formatting'}->{'normal'}->{$cmdname};
1371  }
1372  return $result;
1373}
1374
1375foreach my $command(keys(%{$default_commands_formatting{'normal'}})) {
1376  $default_commands_conversion{$command} = \&_convert_no_arg_command;
1377}
1378
1379sub _convert_today_command($$$)
1380{
1381  my $self = shift;
1382  my $cmdname = shift;
1383  my $command = shift;
1384
1385  my $tree = $self->Texinfo::Common::expand_today();
1386  return $self->convert_tree($tree);
1387}
1388
1389$default_commands_conversion{'today'} = \&_convert_today_command;
1390
1391# style commands
1392
1393my %quoted_style_commands;
1394foreach my $quoted_command ('samp') {
1395  $quoted_style_commands{$quoted_command} = 1;
1396}
1397
1398my %style_attribute_commands;
1399$style_attribute_commands{'normal'} = {
1400      'b'           => 'b',
1401      'cite'        => 'cite',
1402      'code'        => 'code',
1403      'command'     => 'code',
1404      'dfn'         => 'em',
1405      'emph'        => 'em',
1406      'env'         => 'code',
1407      'file'        => 'samp',
1408      'headitemfont' => 'b', # not really that, in fact it is
1409                             # in <th> rather than <td>
1410      'i'           => 'i',
1411      'slanted'     => 'i',
1412      'sansserif'   => 'span class="sansserif"',
1413      'kbd'         => 'kbd',
1414      'option'      => 'samp',
1415      'r'           => 'span class="roman"',
1416      'samp'        => 'samp',
1417      'sc'          => 'small',
1418      'strong'      => 'strong',
1419      'sub'         => 'sub',
1420      'sup'         => 'sup',
1421      't'           => 'tt',
1422      'var'         => 'var',
1423      'verb'        => 'tt',
1424};
1425
1426my %style_commands_formatting;
1427
1428# this weird construct does like uniq, it avoids duplicates.
1429# it is required since math is not in the %style_commands as it is
1430# in context command.
1431my @all_style_commands = keys %{{ map { $_ => 1 }
1432    (keys(%style_commands), keys(%{$style_attribute_commands{'normal'}}),
1433     'dmn') }};
1434
1435foreach my $command(@all_style_commands) {
1436  # default is no attribute.
1437  if ($style_attribute_commands{'normal'}->{$command}) {
1438    $style_commands_formatting{'normal'}->{$command}->{'attribute'}
1439     = $style_attribute_commands{'normal'}->{$command};
1440    $style_commands_formatting{'preformatted'}->{$command}->{'attribute'}
1441     = $style_attribute_commands{'normal'}->{$command};
1442  }
1443  if ($style_attribute_commands{'preformatted'}->{$command}) {
1444    $style_commands_formatting{'preformatted'}->{$command}->{'attribute'} =
1445      $style_attribute_commands{'preformatted'}->{$command};
1446  }
1447  if ($quoted_style_commands{$command}) {
1448    foreach my $context ('normal', 'string', 'preformatted') {
1449      $style_commands_formatting{$context}->{$command}->{'quote'} = 1;
1450    }
1451  }
1452  $default_commands_conversion{$command} = \&_convert_style_command;
1453}
1454
1455delete $style_commands_formatting{'preformatted'}->{'sc'}->{'attribute'};
1456delete $style_commands_formatting{'preformatted'}->{'sc'};
1457
1458sub _parse_attribute($)
1459{
1460  my $element = shift;
1461  return ('', '', '') if (!defined($element));
1462  my ($class, $attributes) = ('', '');
1463  if ($element =~ /^(\w+)(\s+.*)/)
1464  {
1465    $element = $1;
1466    $attributes = $2;
1467    if ($attributes =~ s/^\s+class=\"([^\"]+)\"//) {
1468      $class = $1;
1469    }
1470  }
1471  return ($element, $class, $attributes);
1472}
1473
1474sub _convert_style_command($$$$)
1475{
1476  my $self = shift;
1477  my $cmdname = shift;
1478  my $command = shift;
1479  my $args = shift;
1480
1481  my $text = $args->[0]->{'normal'};
1482  if (!defined($text)) {
1483    # happens with bogus @-commands without argument, like @strong something
1484    #cluck "text not defined in _convert_style_command";
1485    return '';
1486  }
1487  # handle the effect of kbdinputstyle
1488  if ($cmdname eq 'kbd' and $command->{'extra'}
1489      and $command->{'extra'}->{'code'}) {
1490    $cmdname = 'code';
1491  }
1492
1493  my $attribute_hash = {};
1494  if ($self->in_preformatted()) {
1495    $attribute_hash = $self->{'style_commands_formatting'}->{'preformatted'};
1496  } elsif (!$self->in_string()) {
1497    $attribute_hash = $self->{'style_commands_formatting'}->{'normal'};
1498  }
1499  if (defined($attribute_hash->{$cmdname})) {
1500    if (defined($attribute_hash->{$cmdname}->{'attribute'})) {
1501      my ($style, $class, $attribute_text)
1502        = _parse_attribute ($attribute_hash->{$cmdname}->{'attribute'});
1503      my $open = $self->_attribute_class($style, $class);
1504      if ($open ne '') {
1505        $text = $open . "$attribute_text>"
1506              . $text . "</$style>";
1507      } elsif ($attribute_text ne '') {
1508        $text = "<$style $attribute_text>". $text . "</$style>";
1509      }
1510    }
1511    if (defined($attribute_hash->{$cmdname}->{'quote'})) {
1512      $text = $self->get_conf('OPEN_QUOTE_SYMBOL') . $text
1513                . $self->get_conf('CLOSE_QUOTE_SYMBOL');
1514    }
1515  }
1516  return $text;
1517}
1518
1519sub _convert_w_command($$$$)
1520{
1521  my $self = shift;
1522  my $cmdname = shift;
1523  my $command = shift;
1524  my $args = shift;
1525  my $text = $args->[0]->{'normal'};
1526  if (!defined($text)) {
1527    $text = '';
1528  }
1529  if ($self->in_string) {
1530    return $text;
1531  } else {
1532    return $text . '<!-- /@w -->';
1533  }
1534}
1535$default_commands_conversion{'w'} = \&_convert_w_command;
1536
1537sub _convert_value_command($$$$)
1538{
1539  my $self = shift;
1540  my $cmdname = shift;
1541  my $command = shift;
1542  my $args = shift;
1543
1544  return $self->convert_tree($self->gdt('@{No value for `{value}\'@}',
1545                                       {'value' => $command->{'type'}}));
1546}
1547
1548$default_commands_conversion{'value'} = \&_convert_value_command;
1549
1550sub _convert_email_command($$$$)
1551{
1552  my $self = shift;
1553  my $cmdname = shift;
1554  my $command = shift;
1555  my $args = shift;
1556
1557  my $mail_arg = shift @$args;
1558  my $text_arg = shift @$args;
1559  my $mail = '';
1560  my $mail_string = '';
1561  if (defined($mail_arg)) {
1562    $mail = $mail_arg->{'monospace'};
1563    $mail_string = $mail_arg->{'monospacestring'};
1564  }
1565  my $text = '';
1566  if (defined($text_arg)) {
1567    $text = $text_arg->{'normal'};
1568  }
1569  $text = $mail unless ($text ne '');
1570  return $text if ($mail eq '');
1571  if ($self->in_string()) {
1572    return "$mail_string ($text)";
1573  } else {
1574    return "<a href=\"mailto:$mail_string\">$text</a>";
1575  }
1576}
1577
1578$default_commands_conversion{'email'} = \&_convert_email_command;
1579
1580sub _convert_explained_command($$$$)
1581{
1582  my $self = shift;
1583  my $cmdname = shift;
1584  my $command = shift;
1585  my $args = shift;
1586
1587  my $with_explanation;
1588  my $explanation_result;
1589  my $explanation_string;
1590  my $normalized_type
1591    = Texinfo::Convert::NodeNameNormalization::normalize_node(
1592    {'contents' => $command->{'args'}->[0]->{'contents'}});
1593
1594  if ($args->[1] and defined($args->[1]->{'string'})
1595                 and $args->[1]->{'string'} =~ /\S/) {
1596    $with_explanation = 1;
1597    $explanation_string = $args->[1]->{'string'};
1598
1599    # Convert the expanation of the acronym.  Must do this before we save
1600    # the explanation for the future, otherwise we get infinite recursion
1601    # for recursively-defined acronyms.
1602    $explanation_result = $self->convert_tree( $args->[1]->{'tree'} );
1603
1604    $self->{'explained_commands'}->{$cmdname}->{$normalized_type} =
1605       $command->{'args'}->[1]->{'contents'};
1606  } elsif ($command->{'extra'}->{'explanation_contents'}) {
1607    if (@{$command->{'extra'}->{'explanation_contents'}}) {
1608      $explanation_string = $self->convert_tree_new_formatting_context(
1609        {'type' => '_string',
1610         'contents' => $command->{'extra'}->{'explanation_contents'}},
1611        $cmdname, $cmdname);
1612    }
1613  } elsif ($self->{'explained_commands'}->{$cmdname}->{$normalized_type}) {
1614    $explanation_string = $self->convert_tree_new_formatting_context(
1615      {'type' => '_string',
1616       'contents' => $self->{'explained_commands'}
1617                       ->{$cmdname}->{$normalized_type}},
1618    $cmdname, $cmdname);
1619
1620    $command->{'extra'}->{'explanation_contents'}
1621       = $self->{'explained_commands'}->{$cmdname}->{$normalized_type};
1622  } else {
1623    # Avoid ever giving an explanation for this element.  This prevents
1624    # infinite recursion for a recursively-defined acronym, when an
1625    # @acronym within the explanation could end up referring to the
1626    # containing @acronym.
1627
1628    $command->{'extra'}->{'explanation_contents'} = [];
1629  }
1630  my $result = $args->[0]->{'normal'};
1631  if (!$self->in_string()) {
1632    if (defined($explanation_string)) {
1633      $result = "<$cmdname title=\"$explanation_string\">".$result;
1634    } else {
1635      $result = "<$cmdname>".$result;
1636    }
1637    $result .= "</$cmdname>";
1638  }
1639  if ($with_explanation) {
1640    $result = $self->convert_tree($self->gdt('{explained_string} ({explanation})',
1641          {'explained_string' => {'type' => '_converted',
1642                   'text' => $result},
1643           'explanation' => {'type' => '_converted',
1644                   'text' => $explanation_result}}));
1645  }
1646
1647  return $result;
1648}
1649
1650foreach my $explained_command (keys(%explained_commands)) {
1651  $default_commands_conversion{$explained_command}
1652    = \&_convert_explained_command;
1653}
1654
1655sub _convert_anchor_command($$$$)
1656{
1657  my $self = shift;
1658  my $cmdname = shift;
1659  my $command = shift;
1660  my $args = shift;
1661
1662  my $id = $self->command_id($command);
1663  if (defined($id) and $id ne '' and !@{$self->{'multiple_pass'}}
1664      and !$self->in_string()) {
1665    return "<span id=\"$id\"></span>";
1666  }
1667  return '';
1668}
1669
1670$default_commands_conversion{'anchor'} = \&_convert_anchor_command;
1671
1672my $foot_num;
1673my $foot_lines;
1674my $NO_NUMBER_FOOTNOTE_SYMBOL = '*';
1675
1676my $footid_base = 'FOOT';
1677my $docid_base = 'DOCF';
1678
1679# to avoid duplicate names, use a prefix that cannot happen in anchors
1680my $target_prefix = "t_h";
1681my %footnote_id_numbers;
1682sub _convert_footnote_command($$$$)
1683{
1684  my $self = shift;
1685  my $cmdname = shift;
1686  my $command = shift;
1687  my $args = shift;
1688
1689  my $number_in_doc;
1690  $foot_num++;
1691  if ($self->get_conf('NUMBER_FOOTNOTES')) {
1692    $number_in_doc = $foot_num;
1693  } else {
1694    $number_in_doc = $NO_NUMBER_FOOTNOTE_SYMBOL;
1695  }
1696
1697  return "($number_in_doc)" if ($self->in_string());
1698  #print STDERR "FOOTNOTE $command\n";
1699  my $footid = $self->command_target($command);
1700
1701  # happens for bogus footnotes
1702  if (!defined($footid)) {
1703    return '';
1704  }
1705
1706  # ID for linking back to the main text from the footnote.
1707  my $docid = $footid;
1708  $docid =~ s/^$footid_base/$docid_base/;
1709
1710  my $document_filename;
1711  my $footnote_filename;
1712  if ($self->get_conf('footnotestyle') eq 'separate') {
1713    $footnote_filename = $self->command_filename($command);
1714    $document_filename = $self->{'current_filename'};
1715    $footnote_filename = '' if (!defined($footnote_filename));
1716    $document_filename = '' if (!defined($document_filename));
1717
1718    if ($document_filename eq $footnote_filename) {
1719      $document_filename = $footnote_filename = '';
1720    }
1721  } else {
1722    $document_filename = $footnote_filename = '';
1723  }
1724  my $footnote_text;
1725  if ($args->[0]) {
1726    $footnote_text = $args->[0]->{'normal'};
1727  } else {
1728    $footnote_text = '';
1729  }
1730  chomp ($footnote_text);
1731  $footnote_text .= "\n";
1732
1733  if (@{$self->{'multiple_pass'}}) {
1734    $footid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$footid.'_'.$foot_num;
1735    $docid = $target_prefix.$self->{'multiple_pass'}->[-1].'_'.$docid.'_'.$foot_num;
1736  } else {
1737    if (!defined($footnote_id_numbers{$footid})) {
1738      $footnote_id_numbers{$footid} = $foot_num;
1739    } else {
1740      # This should rarely happen, except for @footnote is @copying and
1741      # multiple @insertcopying...
1742      # Here it is not checked that there is no clash with another anchor.
1743      # However, unless there are more than 1000 footnotes this should not
1744      # happen.
1745      $footid .= '_'.$foot_num;
1746      $docid .= '_'.$foot_num;
1747    }
1748  }
1749
1750  $foot_lines .= '<h5>' .
1751   "<a id=\"$footid\" href=\"$document_filename#$docid\">($number_in_doc)</a></h5>\n"
1752   . $footnote_text;
1753
1754  my $footnote_number_text;
1755  if ($self->in_preformatted()) {
1756    $footnote_number_text = "($number_in_doc)";
1757  } else {
1758    $footnote_number_text = "<sup>$number_in_doc</sup>";
1759  }
1760  return "<a id=\"$docid\" href=\"$footnote_filename#$footid\">$footnote_number_text</a>";
1761}
1762$default_commands_conversion{'footnote'} = \&_convert_footnote_command;
1763
1764sub _convert_uref_command($$$$)
1765{
1766  my $self = shift;
1767  my $cmdname = shift;
1768  my $command = shift;
1769  my $args = shift;
1770
1771  my @args = @$args;
1772  my $url_arg = shift @args;
1773  my $text_arg = shift @args;
1774  my $replacement_arg = shift @args;
1775
1776  my ($url, $text, $replacement);
1777  $url = $url_arg->{'monospacestring'} if defined($url_arg);
1778  $text = $text_arg->{'normal'} if defined($text_arg);
1779  $replacement = $replacement_arg->{'normal'} if defined($replacement_arg);
1780
1781  $text = $replacement if (defined($replacement) and $replacement ne '');
1782  $text = $url if (!defined($text) or $text eq '');
1783  return $text if (!defined($url) or $url eq '');
1784  return "$text ($url)" if ($self->in_string());
1785  return "<a href=\"$url\">$text</a>";
1786}
1787
1788$default_commands_conversion{'uref'} = \&_convert_uref_command;
1789$default_commands_conversion{'url'} = \&_convert_uref_command;
1790
1791my @image_files_extensions = ('.png', '.jpg', '.jpeg', '.gif');
1792sub _convert_image_command($$$$)
1793{
1794  my $self = shift;
1795  my $cmdname = shift;
1796  my $command = shift;
1797  my $args = shift;
1798
1799  my @extensions = @image_files_extensions;
1800
1801  if (defined($args->[0]->{'monospacetext'}) and $args->[0]->{'monospacetext'} ne '') {
1802    my $basefile = $args->[0]->{'monospacetext'};
1803    return $basefile if ($self->in_string());
1804    my $extension;
1805    if (defined($args->[4]) and defined($args->[4]->{'monospacetext'})) {
1806      $extension = $args->[4]->{'monospacetext'};
1807      unshift @extensions, ("$extension", ".$extension");
1808    }
1809    my $image_file;
1810    foreach my $extension (@extensions) {
1811      if ($self->Texinfo::Common::locate_include_file ($basefile.$extension)) {
1812        # use the basename and not the file found.  It is agreed that it is
1813        # better, since in any case the files are moved.
1814        $image_file = $basefile.$extension;
1815        last;
1816      }
1817    }
1818    if (!defined($image_file) or $image_file eq '') {
1819      if (defined($extension) and $extension ne '') {
1820        $image_file = $basefile.$extension;
1821      } else {
1822        $image_file = "$basefile.jpg";
1823      }
1824      #cluck "err ($self->{'ignore_notice'})";
1825      $self->line_warn(sprintf(
1826              __("\@image file `%s' (for HTML) not found, using `%s'"),
1827                               $basefile, $image_file), $command->{'line_nr'});
1828    }
1829    if (defined($self->get_conf('IMAGE_LINK_PREFIX'))) {
1830      $image_file = $self->get_conf('IMAGE_LINK_PREFIX') . $image_file;
1831    }
1832    my $alt_string;
1833    if (defined($args->[3]) and defined($args->[3]->{'string'})) {
1834      $alt_string = $args->[3]->{'string'};
1835    }
1836    if (!defined($alt_string) or ($alt_string eq '')) {
1837      $alt_string = $self->protect_text($basefile);
1838    }
1839    return "<img src=\"".$self->protect_text($image_file)."\" alt=\"$alt_string\">";
1840  }
1841  return '';
1842}
1843
1844$default_commands_conversion{'image'} = \&_convert_image_command;
1845
1846sub _convert_math_command($$$$)
1847{
1848  my $self = shift;
1849  my $cmdname = shift;
1850  my $command = shift;
1851  my $args = shift;
1852
1853  my $arg = $args->[0]->{'normal'};
1854
1855  my $math_type = $self->get_conf('HTML_MATH');
1856  if ($math_type and $math_type eq 'mathjax') {
1857    # MathJax won't handle tags in code
1858    # TODO: instead convert inside $command to LaTeX, when such a conversion
1859    # becomes possible
1860    if ($arg !~ /</) {
1861      $self->{'element_math'} = 1;
1862      return "<em class=\'tex2jax_process\'>\\($arg\\)</em>";
1863    }
1864  }
1865  return "<em class=\'math\'>$arg</em>";
1866}
1867
1868$default_commands_conversion{'math'} = \&_convert_math_command;
1869
1870sub _convert_accent_command($$$$)
1871{
1872  my $self = shift;
1873  my $cmdname = shift;
1874  my $command = shift;
1875  my $args = shift;
1876
1877  return $self->xml_accents($command, $self->in_upper_case());
1878}
1879
1880foreach my $command (keys(%accent_commands)) {
1881  $default_commands_conversion{$command} = \&_convert_accent_command;
1882}
1883
1884# key is formatted as code since it is in code_style_commands
1885sub _convert_key_command($$$$)
1886{
1887  my $self = shift;
1888  my $cmdname = shift;
1889  my $command = shift;
1890  my $args = shift;
1891
1892  my $text = $args->[0]->{'normal'};
1893  if (!defined($text)) {
1894    # happens with bogus @-commands without argument, like @strong something
1895    return '';
1896  }
1897  if ($self->in_string()) {
1898    return $text;
1899  }
1900  #return $self->protect_text('<') .$text .$self->protect_text('>');
1901  my $class = $cmdname;
1902  if (!$self->in_code()) {
1903    return $self->_attribute_class('tt', $class).'>'.$text .'</tt>';;
1904  } else {
1905    my $open = $self->_attribute_class('span', $class);
1906    if ($open ne '') {
1907      return $open.'>'.$text.'</span>';
1908    } else {
1909      return $text;
1910    }
1911  }
1912}
1913
1914$default_commands_conversion{'key'} = \&_convert_key_command;
1915
1916# argument is formatted as code since indicateurl is in code_style_commands
1917sub _convert_indicateurl_command($$$$)
1918{
1919  my $self = shift;
1920  my $cmdname = shift;
1921  my $command = shift;
1922  my $args = shift;
1923
1924  my $text = $args->[0]->{'normal'};
1925  if (!defined($text)) {
1926    # happens with bogus @-commands without argument, like @strong something
1927    return '';
1928  }
1929  if (!$self->in_string()) {
1930    return $self->get_conf('OPEN_QUOTE_SYMBOL').'<code>' .$text
1931                .'</code>'.$self->get_conf('CLOSE_QUOTE_SYMBOL');
1932  } else {
1933    return $self->get_conf('OPEN_QUOTE_SYMBOL').$text.
1934              $self->get_conf('CLOSE_QUOTE_SYMBOL');
1935  }
1936}
1937
1938$default_commands_conversion{'indicateurl'} = \&_convert_indicateurl_command;
1939
1940
1941sub _convert_titlefont_command($$$$)
1942{
1943  my $self = shift;
1944  my $cmdname = shift;
1945  my $command = shift;
1946  my $args = shift;
1947
1948  my $text = $args->[0]->{'normal'};
1949  if (!defined($text)) {
1950    # happens with bogus @-commands without argument, like @strong something
1951    return '';
1952  }
1953  return &{$self->{'format_heading_text'}}($self, 'titlefont', $text, 0, $command);
1954}
1955$default_commands_conversion{'titlefont'} = \&_convert_titlefont_command;
1956
1957sub _convert_U_command($$$$)
1958{
1959  my $self = shift;
1960  my $cmdname = shift;
1961  my $command = shift;
1962  my $args = shift;
1963
1964  my $arg = $args->[0]->{'normal'};
1965  my $res;
1966  if (defined($arg) && $arg) {
1967    # checks on the value already done in Parser, just output it here.
1968    $res = "&#x$arg;";
1969  } else {
1970    $res = '';
1971  }
1972  return $res;
1973}
1974$default_commands_conversion{'U'} = \&_convert_U_command;
1975
1976sub _default_format_comment($$) {
1977  my $self = shift;
1978  my $text = shift;
1979  return $self->xml_comment(' '.$text);
1980}
1981
1982sub protect_text($$) {
1983  my $self = shift;
1984  my $text = shift;
1985  return &{$self->{'format_protect_text'}}($self, $text);
1986}
1987
1988sub _default_format_protect_text($$) {
1989  my $self = shift;
1990  my $text = shift;
1991  my $result = $self->xml_protect_text($text);
1992  $result =~ s/\f/&#12;/g;
1993  return $result;
1994}
1995
1996sub _default_format_heading_text($$$$$)
1997{
1998  my $self = shift;
1999  my $cmdname = shift;
2000  my $text = shift;
2001  my $level = shift;
2002  my $command = shift;
2003
2004  return '' if ($text !~ /\S/);
2005
2006  # This should seldom happen.
2007  if ($self->in_string()) {
2008    $text .= "\n" unless ($cmdname eq 'titlefont');
2009    return $text;
2010  }
2011
2012  my $class;
2013  if ($cmdname eq 'node') {
2014    $class = 'node-heading';
2015  } else {
2016    $class = $cmdname;
2017  }
2018
2019  my $align = '';
2020  $align = ' align="center"' if ($cmdname eq 'centerchap' or $cmdname eq 'settitle');
2021  if ($level < 1) {
2022    $level = 1;
2023  } elsif ($level > $self->get_conf('MAX_HEADER_LEVEL')) {
2024    $level = $self->get_conf('MAX_HEADER_LEVEL');
2025  }
2026  my $result = $self->_attribute_class("h$level", $class) ."$align>$text</h$level>";
2027  # titlefont appears inline in text, so no end of line is
2028  # added. The end of line should be added by the user if needed.
2029  $result .= "\n" unless ($cmdname eq 'titlefont');
2030  $result .= $self->get_conf('DEFAULT_RULE') . "\n"
2031     if ($cmdname eq 'part'
2032         and defined($self->get_conf('DEFAULT_RULE'))
2033         and $self->get_conf('DEFAULT_RULE') ne '');
2034  return $result;
2035}
2036
2037# Associated to a button.  Return text to use for a link in button bar.
2038# Depending on USE_NODE_DIRECTIONS and xrefautomaticsectiontitle
2039# use section or node for link direction and string.
2040sub _default_panel_button_dynamic_direction($$;$$)
2041{
2042  my $self = shift;
2043  my $direction = shift;
2044  my $omit_rel = shift;
2045  my $use_first_element_in_file_directions = shift;
2046
2047  my $result = undef;
2048
2049  if ((defined($self->get_conf('USE_NODE_DIRECTIONS'))
2050       and $self->get_conf('USE_NODE_DIRECTIONS'))
2051      or (not defined($self->get_conf('USE_NODE_DIRECTIONS'))
2052          and $self->get_conf('USE_NODES'))) {
2053    $direction = 'Node'.$direction;
2054  }
2055
2056  if ($use_first_element_in_file_directions) {
2057    $direction = 'FirstInFile'.$direction;
2058  }
2059
2060  my $href = $self->_element_direction($self->{'current_element'},
2061                                           $direction, 'href');
2062  my $node;
2063
2064
2065  if ($self->get_conf('xrefautomaticsectiontitle') eq 'on') {
2066    $node = $self->_element_direction($self->{'current_element'},
2067                                             $direction, 'section');
2068  }
2069
2070  if (!defined($node)) {
2071    $node = $self->_element_direction($self->{'current_element'},
2072                                      $direction, 'node');
2073  }
2074
2075  my $anchor;
2076  if (defined($href) and defined($node) and $node =~ /\S/) {
2077    my $anchor_attributes = $omit_rel ? ''
2078      : $self->_direction_href_attributes($direction);
2079    $anchor = "<a href=\"$href\"${anchor_attributes}>$node</a>";
2080  }
2081  if (defined($anchor)) {
2082    # i18n
2083    $result = $self->get_conf('BUTTONS_TEXT')->{$direction}.": $anchor";
2084  }
2085  # 1 to communicate that a delimiter is needed for that button
2086  return ($result, 1);
2087}
2088
2089# Used for button bar at the foot of a node, with "rel" and "accesskey"
2090# attributes omitted.
2091sub _default_panel_button_dynamic_direction_node_footer($$)
2092{
2093  my $self = shift;
2094  my $direction = shift;
2095
2096  return _default_panel_button_dynamic_direction($self, $direction, 1);
2097}
2098
2099# used for button bar at the foot of a section or chapter with
2100# directions of first element in file used instead of the last
2101# element directions.
2102sub _default_panel_button_dynamic_direction_section_footer($$) {
2103  my $self = shift;
2104  my $direction = shift;
2105
2106  return _default_panel_button_dynamic_direction($self, $direction, undef, 1);
2107}
2108
2109# how to create IMG tag
2110# this is only used in html, and only if ICONS is set and the button
2111# is active.
2112sub _default_format_button_icon_img($$$;$)
2113{
2114  my $self = shift;
2115  my $button = shift;
2116  my $icon = shift;
2117  my $name = shift;
2118  return '' if (!defined($icon));
2119  $button = "" if (!defined ($button));
2120  $name = '' if (!defined($name));
2121  my $alt = '';
2122  if ($name ne '') {
2123    if ($button ne '') {
2124      $alt = "$button: $name";
2125    } else {
2126      $alt = $name;
2127    }
2128  } else {
2129    $alt = $button;
2130  }
2131  return qq{<img src="$icon" border="0" alt="$alt" align="middle">};
2132}
2133
2134sub _direction_href_attributes($$)
2135{
2136  my $self = shift;
2137  my $direction = shift;
2138
2139  my $href_attributes = '';
2140  if ($self->get_conf('USE_ACCESSKEY')
2141      and $self->get_conf('BUTTONS_ACCESSKEY')) {
2142    my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$direction};
2143    if (defined($accesskey) and ($accesskey ne '')) {
2144      $href_attributes = " accesskey=\"$accesskey\"";
2145    }
2146  }
2147  if ($self->get_conf('USE_REL_REV') and $self->get_conf('BUTTONS_REL')) {
2148    my $button_rel = $self->get_conf('BUTTONS_REL')->{$direction};
2149    if (defined($button_rel) and ($button_rel ne '')) {
2150      $href_attributes .= " rel=\"$button_rel\"";
2151    }
2152  }
2153  return $href_attributes;
2154}
2155
2156my %html_default_node_directions;
2157foreach my $node_directions ('NodeNext', 'NodePrev', 'NodeUp') {
2158  $html_default_node_directions{$node_directions} = 1;
2159}
2160
2161sub _default_format_button($$)
2162{
2163  my $self = shift;
2164  my $button = shift;
2165
2166  my ($active, $passive, $need_delimiter);
2167  if (ref($button) eq 'CODE') {
2168    ($active, $need_delimiter) = &$button($self);
2169  } elsif (ref($button) eq 'SCALAR') {
2170    $active = "$$button" if defined($$button);
2171    $need_delimiter = 1;
2172  } elsif (ref($button) eq 'ARRAY' and scalar(@$button == 2)) {
2173    my $text = $button->[1];
2174    my $button_href = $button->[0];
2175    # $button_href is simple text and $text is a reference
2176    if (defined($button_href) and !ref($button_href)
2177        and defined($text) and (ref($text) eq 'SCALAR') and defined($$text)) {
2178      # use given text
2179      my $href = $self->_element_direction($self->{'current_element'},
2180                                           $button_href, 'href');
2181      if ($href) {
2182        my $anchor_attributes = $self->_direction_href_attributes($button_href);
2183        $active = "<a href=\"$href\"${anchor_attributes}>$$text</a>";
2184      } else {
2185        $passive = $$text;
2186      }
2187      $need_delimiter = 1;
2188    # $button_href is simple text and $text is a reference on code
2189    } elsif (defined($button_href) and !ref($button_href)
2190             and defined($text) and (ref($text) eq 'CODE')) {
2191      ($active, $need_delimiter) = &$text($self, $button_href);
2192    # $button_href is simple text and $text is also a simple text
2193    } elsif (defined($button_href) and !ref($button_href)
2194             and defined($text) and !ref($text)) {
2195      if ($text =~ s/^->\s*//) {
2196        $active = $self->_element_direction($self->{'current_element'},
2197                                           $button_href, $text);
2198      } else {
2199        my $href = $self->_element_direction($self->{'current_element'},
2200                                             $button_href, 'href');
2201        my $text_formatted = $self->_element_direction($self->{'current_element'},
2202                                           $button_href, $text);
2203        if ($href) {
2204          my $anchor_attributes = $self->_direction_href_attributes($button_href);
2205          $active = "<a href=\"$href\"${anchor_attributes}>$text_formatted</a>";
2206        } else {
2207          $passive = $text_formatted;
2208        }
2209      }
2210      $need_delimiter = 1;
2211    }
2212  } elsif ($button eq ' ') {
2213    # handle space button
2214    if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS')
2215        and defined($self->get_conf('ACTIVE_ICONS')->{$button})
2216        and $self->get_conf('ACTIVE_ICONS')->{$button} ne '') {
2217      my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
2218      $active = &{$self->{'format_button_icon_img'}}($self, $button_name,
2219                                       $self->get_conf('ACTIVE_ICONS')->{' '});
2220    } else {
2221      $active = $self->get_conf('BUTTONS_TEXT')->{$button};
2222    }
2223    $need_delimiter = 0;
2224  } else {
2225    my $href = $self->_element_direction($self->{'current_element'},
2226                                         $button, 'href');
2227    if ($href) {
2228      # button is active
2229      my $btitle = '';
2230      if ($self->get_conf('BUTTONS_GOTO')
2231          and defined($self->get_conf('BUTTONS_GOTO')->{$button})) {
2232        $btitle = ' title="' . $self->get_conf('BUTTONS_GOTO')->{$button} . '"';
2233      }
2234      if ($self->get_conf('USE_ACCESSKEY') and $self->get_conf('BUTTONS_ACCESSKEY')) {
2235        my $accesskey = $self->get_conf('BUTTONS_ACCESSKEY')->{$button};
2236        if (defined($accesskey) and $accesskey ne '') {
2237          $btitle .= " accesskey=\"$accesskey\"";
2238        }
2239      }
2240      if ($self->get_conf('USE_REL_REV') and ($self->get_conf('BUTTONS_REL'))) {
2241        my $button_rel = $self->get_conf('BUTTONS_REL')->{$button};
2242        if (defined($button_rel) and $button_rel ne '') {
2243          $btitle .= " rel=\"$button_rel\"";
2244        }
2245      }
2246      my $use_icon;
2247      if ($self->get_conf('ICONS') and $self->get_conf('ACTIVE_ICONS')
2248          and $self->get_conf('BUTTONS_NAME')) {
2249        my $active_icon = $self->get_conf('ACTIVE_ICONS')->{$button};
2250        my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
2251        if (defined($active_icon) and $active_icon ne ''
2252            and defined($button_name)) {
2253          # use icon
2254          $active = "<a href=\"$href\"${btitle}>".
2255             &{$self->{'format_button_icon_img'}}($self, $button_name, $active_icon,
2256                      $self->_element_direction($self->{'current_element'},
2257                                       $button, 'string')) ."</a>";
2258          $use_icon = 1;
2259        }
2260      }
2261      if (!$use_icon) {
2262        # use text
2263        $active = '[' . "<a href=\"$href\"${btitle}>".
2264          $self->get_conf('BUTTONS_TEXT')->{$button}."</a>" . ']';
2265      }
2266    } else {
2267      # button is passive
2268      my $use_icon;
2269      if ($self->get_conf('ICONS') and $self->get_conf('PASSIVE_ICONS')
2270          and $self->get_conf('BUTTONS_NAME')) {
2271        my $passive_icon = $self->get_conf('PASSIVE_ICONS')->{$button};
2272        my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
2273        if ($passive_icon and $passive_icon ne '') {
2274          $passive = &{$self->{'format_button_icon_img'}}($self, $button_name,
2275                                                   $passive_icon,
2276                      $self->_element_direction($self->{'current_element'},
2277                                       $button, 'string'));
2278          $use_icon = 1;
2279        }
2280      }
2281      if (!$use_icon) {
2282        $passive =  '[' . $self->get_conf('BUTTONS_TEXT')->{$button} . ']';
2283      }
2284    }
2285    $need_delimiter = 0;
2286  }
2287  # FIXME chose another option among those proposed in comments below?
2288  if (not defined($need_delimiter)) {
2289    # option 1: be forgiving if $need_delimiter is not set
2290    # if ($html_default_node_directions{$button}) {
2291    #   $need_delimiter = 1;
2292    # } else {
2293    #   $need_delimiter = 0;
2294    # }
2295    # option 2: be somewhat forgiving but show a backtrace
2296    #cluck ("need_delimiter not defined");
2297    # $need_delimiter = 0;
2298    # option3: no pity
2299    confess ("need_delimiter not defined");
2300  }
2301  return ($active, $passive, $need_delimiter);
2302}
2303
2304sub _default_format_navigation_header_panel($$$$;$)
2305{
2306  my $self = shift;
2307  my $buttons = shift;
2308  my $cmdname = shift;
2309  my $command = shift;
2310  my $vertical = shift;
2311
2312  # if VERTICAL_HEAD_NAVIGATION, the buttons are in a vertical table which
2313  # is itself in the first column of a table opened in header_navigation
2314  #my $vertical = $self->get_conf('VERTICAL_HEAD_NAVIGATION');
2315
2316  my $first_button = 1;
2317  my $result = '';
2318  if ($self->get_conf('HEADER_IN_TABLE')) {
2319    $result .= $self->_attribute_class('table', 'header')
2320        .' cellpadding="1" cellspacing="1" border="0">'."\n";
2321    $result .= "<tr>" unless $vertical;
2322  } else {
2323    $result .= $self->_attribute_class('div', 'header').">\n<p>\n";
2324  }
2325  foreach my $button (@$buttons) {
2326    if ($self->get_conf('HEADER_IN_TABLE')) {
2327      $result .= qq{<tr valign="top" align="left">\n} if $vertical;
2328      $result .=  qq{<td valign="middle" align="left">};
2329    }
2330    my $direction;
2331    if (ref($button) eq 'ARRAY'
2332        and defined($button->[0]) and !ref($button->[0])) {
2333      $direction = $button->[0];
2334    } elsif (defined($button) and !ref($button)) {
2335      $direction = $button;
2336    }
2337
2338    my ($active, $passive, $need_delimiter) = &{$self->{'format_button'}}($self, $button);
2339    if ($self->get_conf('HEADER_IN_TABLE')) {
2340      if (defined($active)) {
2341        $first_button = 0 if ($first_button);
2342        $result .= $active;
2343      } elsif (defined($passive)) {
2344        $first_button = 0 if ($first_button);
2345        $result .= $passive;
2346      }
2347      $result .= "</td>\n";
2348      $result .= "</tr>\n" if $vertical;
2349    } elsif (defined($active)) {
2350      # only active buttons are print out when not in table
2351      if ($need_delimiter and !$first_button) {
2352        $active = ', ' .$active;
2353      }
2354      $result .= $active;
2355      $first_button = 0 if ($first_button);
2356    }
2357  }
2358  if ($self->get_conf('HEADER_IN_TABLE')) {
2359    $result .= "</tr>" unless $vertical;
2360    $result .= "</table>\n";
2361  } else {
2362     $result .= "</p>\n</div>\n";
2363  }
2364  return $result;
2365}
2366
2367sub _default_format_navigation_header($$$$)
2368{
2369  my $self = shift;
2370  my $buttons = shift;
2371  my $cmdname = shift;
2372  my $command = shift;
2373
2374  my $result = '';
2375  if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) {
2376    $result .= '<table border="0" cellpadding="0" cellspacing="0">
2377<tr valign="top">
2378<td align="left">
2379';
2380  }
2381  $result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons,
2382                                                   $cmdname, $command,
2383                                   $self->get_conf('VERTICAL_HEAD_NAVIGATION'));
2384  if ($self->get_conf('VERTICAL_HEAD_NAVIGATION')) {
2385    $result .= '</td>
2386<td align="left">
2387';
2388  } elsif ($self->get_conf('SPLIT') eq 'node') {
2389    $result .= $self->get_conf('DEFAULT_RULE')."\n";
2390  }
2391  return $result;
2392}
2393
2394sub _default_format_element_header($$$$)
2395{
2396  my $self = shift;
2397  my $cmdname = shift;
2398  my $command = shift;
2399  my $element = shift;
2400
2401  my $result = '';
2402
2403  print STDERR "Element $element (@{$element->{'contents'}}) ".
2404     Texinfo::Structuring::_print_element_command_texi($element) ."\n"
2405        if ($self->get_conf('DEBUG'));
2406
2407  # Do the heading if the command is the first command in the element
2408  if (($element->{'contents'}->[0] eq $command
2409       or (!$element->{'contents'}->[0]->{'cmdname'}
2410            and $element->{'contents'}->[1] eq $command))
2411      # and there is more than one element
2412      and ($element->{'element_next'} or $element->{'element_prev'})) {
2413    my $is_top = $self->element_is_top($element);
2414    my $first_in_page = (defined($element->{'filename'})
2415           and $self->{'counter_in_file'}->{$element->{'filename'}} == 1);
2416    my $previous_is_top = ($element->{'element_prev'}
2417                   and $self->element_is_top($element->{'element_prev'}));
2418
2419    print STDERR "Header ($previous_is_top, $is_top, $first_in_page): "
2420      .Texinfo::Structuring::_print_root_command_texi($command)."\n"
2421        if ($self->get_conf('DEBUG'));
2422
2423    if ($is_top) {
2424      # use TOP_BUTTONS for top.
2425      $result .= &{$self->{'format_navigation_header'}}($self,
2426               $self->get_conf('TOP_BUTTONS'), $cmdname, $command)
2427        if ($self->get_conf('SPLIT') or $self->get_conf('HEADERS'));
2428    } else {
2429      if ($first_in_page and !$self->get_conf('HEADERS')) {
2430        if ($self->get_conf('SPLIT') eq 'chapter') {
2431          $result .= &{$self->{'format_navigation_header'}}($self,
2432                $self->get_conf('CHAPTER_BUTTONS'), $cmdname, $command);
2433
2434          $result .= $self->get_conf('DEFAULT_RULE') ."\n"
2435            if (defined($self->get_conf('DEFAULT_RULE'))
2436                and !$self->get_conf('VERTICAL_HEAD_NAVIGATION'));
2437        } elsif ($self->get_conf('SPLIT') eq 'section') {
2438          $result .= &{$self->{'format_navigation_header'}}($self,
2439                $self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
2440        }
2441      }
2442      if (($first_in_page or $previous_is_top)
2443           and $self->get_conf('HEADERS')) {
2444        $result .= &{$self->{'format_navigation_header'}}($self,
2445                $self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
2446      } elsif($self->get_conf('HEADERS') or $self->get_conf('SPLIT') eq 'node') {
2447        # got to do this here, as it isn't done otherwise since
2448        # navigation_header is not called
2449        $result .= &{$self->{'format_navigation_header_panel'}}($self,
2450                $self->get_conf('SECTION_BUTTONS'), $cmdname, $command);
2451      }
2452    }
2453  }
2454  return $result;
2455}
2456
2457sub register_opened_section_level($$$)
2458{
2459  my $self = shift;
2460  my $level = shift;
2461  my $close = shift;
2462  while (@{$self->{'pending_closes'}} < $level) {
2463      push(@{$self->{'pending_closes'}}, "");
2464  }
2465  push(@{$self->{'pending_closes'}}, $close);
2466}
2467
2468sub close_registered_sections_level($$)
2469{
2470  my $self = shift;
2471  my $level = shift;
2472  if (not defined($level)) {
2473    cluck 'close_registered_sections_level $level not defined';
2474  }
2475  my @closed_elements;
2476  my $result = '';
2477  while (@{$self->{'pending_closes'}} > $level) {
2478      my $close = pop @{$self->{'pending_closes'}};
2479      push(@closed_elements, $close)
2480        if ($close);
2481  }
2482  return @closed_elements;
2483}
2484
2485sub _convert_heading_command($$$$$)
2486{
2487  my $self = shift;
2488  my $cmdname = shift;
2489  my $command = shift;
2490  my $args = shift;
2491  my $content = shift;
2492
2493  my $result = '';
2494
2495  # not clear that it may really happen
2496  if ($self->in_string) {
2497    $result .= $self->command_string($command) ."\n" if ($cmdname ne 'node');
2498    $result .= $content if (defined($content));
2499    return $result;
2500  }
2501
2502  my $element_id = $self->command_id($command);
2503  my $section;
2504  if ($cmdname eq 'node' and $command->{'extra'}->{'associated_section'}) {
2505    $section = $command->{'extra'}->{'associated_section'};
2506  } elsif ($cmdname ne 'node'
2507           and not $command->{'extra'}->{'associated_node'}
2508           # to avoid *heading* @-commands
2509           and $Texinfo::Common::root_commands{$cmdname}) {
2510    $section = $command;
2511  }
2512
2513  if ($section) {
2514    my $level = $section->{'level'};
2515    $result .= join('', $self->close_registered_sections_level($level));
2516    $self->register_opened_section_level($level, "</div>\n");
2517
2518    $result .= '<div class="' . $section->{'cmdname'} . '"';
2519
2520    $result .= " id=\"$element_id\""
2521        if (defined($element_id) and $element_id ne '');
2522    $result .= ">\n";
2523  } else {
2524    $result .= "<span id=\"$element_id\"></span>"
2525        if (defined($element_id) and $element_id ne '');
2526  }
2527
2528  print STDERR "Process $command "
2529        .Texinfo::Structuring::_print_root_command_texi($command)."\n"
2530          if ($self->get_conf('DEBUG'));
2531  my $element;
2532  if ($Texinfo::Common::root_commands{$command->{'cmdname'}}
2533      and $command->{'parent'}
2534      and $command->{'parent'}->{'type'}
2535      and $command->{'parent'}->{'type'} eq 'element') {
2536    $element = $command->{'parent'};
2537  }
2538  if ($element) {
2539    $result .= &{$self->{'format_element_header'}}($self, $cmdname,
2540                                            $command, $element);
2541  }
2542
2543  my $heading_level;
2544  # node is used as heading if there is nothing else.
2545  if ($cmdname eq 'node') {
2546    if (!$element or (!$element->{'extra'}->{'section'}
2547                      and $element->{'extra'}->{'node'}
2548                      and $element->{'extra'}->{'node'} eq $command
2549                      # bogus node may not have been normalized
2550                      and defined($command->{'extra'}->{'normalized'}))) {
2551      if ($command->{'extra'}->{'normalized'} eq 'Top') {
2552        $heading_level = 0;
2553      } else {
2554        $heading_level = 3;
2555      }
2556    }
2557  } elsif (defined $command->{'level'}) {
2558    $heading_level = $command->{'level'};
2559  } else {
2560    # for *heading* @-commands which do not have a level
2561    # in the document as they are not associated with the
2562    # sectioning tree, but still have a $heading_level
2563    $heading_level = Texinfo::Structuring::section_level($command);
2564  }
2565
2566  my $heading = $self->command_text($command);
2567  # $heading not defined may happen if the command is a @node, for example
2568  # if there is an error in the node.
2569  if (defined($heading) and $heading ne '' and defined($heading_level)) {
2570
2571    if ($self->get_conf('TOC_LINKS')
2572        and $Texinfo::Common::root_commands{$cmdname}
2573        and $Texinfo::Common::sectioning_commands{$cmdname}) {
2574      my $content_href = $self->command_contents_href($command, 'contents',
2575                                        $self->{'current_filename'});
2576      if ($content_href) {
2577        $heading = "<a href=\"$content_href\">$heading</a>";
2578      }
2579    }
2580
2581    if ($self->in_preformatted()) {
2582      $result .= '<strong>'.$heading.'</strong>'."\n";
2583    } else {
2584      # if the level was changed, set the command name right
2585      if ($cmdname ne 'node'
2586          and $heading_level ne $Texinfo::Common::command_structuring_level{$cmdname}) {
2587        $cmdname
2588          = $Texinfo::Common::level_to_structuring_command{$cmdname}->[$heading_level];
2589      }
2590      $result .= &{$self->{'format_heading_text'}}($self, $cmdname, $heading,
2591                 $heading_level +$self->get_conf('CHAPTER_HEADER_LEVEL') -1, $command);
2592    }
2593  }
2594  $result .= $content if (defined($content));
2595
2596  my $table_of_contents_was_output = 0.;
2597  if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top'
2598      and $cmdname eq 'top'
2599      and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
2600      and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
2601    foreach my $content_command_name ('contents', 'shortcontents') {
2602      if ($self->get_conf($content_command_name)) {
2603        my $contents_text
2604          = $self->_contents_inline_element($content_command_name, undef);
2605        if ($contents_text ne '') {
2606          $result .= $contents_text;
2607          #$result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n";
2608          $table_of_contents_was_output = 1;
2609        }
2610      }
2611    }
2612  }
2613  if (not $table_of_contents_was_output
2614      and $self->get_conf('FORMAT_MENU') eq 'sectiontoc'
2615      and $sectioning_commands{$cmdname}
2616      and ($cmdname ne 'top'
2617           or (not ($self->_has_contents_or_shortcontents()
2618                   and $self->get_conf('CONTENTS_OUTPUT_LOCATION')
2619                       eq 'inline')))) {
2620    $result .= _mini_toc($self, $command);
2621  }
2622  return $result;
2623}
2624
2625foreach my $command (keys(%sectioning_commands), 'node') {
2626  $default_commands_conversion{$command} = \&_convert_heading_command;
2627}
2628
2629sub _convert_raw_command($$$$)
2630{
2631  my $self = shift;
2632  my $cmdname = shift;
2633  my $command = shift;
2634  my $content = shift;
2635
2636  if ($cmdname eq $self->{'output_format'}) {
2637    return $content;
2638  }
2639  $self->line_warn(sprintf(__("raw format %s is not converted"),
2640                           $cmdname), $command->{'line_nr'});
2641  return $self->protect_text($content);
2642}
2643
2644foreach my $command (keys(%format_raw_commands)) {
2645  $default_commands_conversion{$command} = \&_convert_raw_command;
2646}
2647
2648sub _convert_inline_command($$$$)
2649{
2650  my $self = shift;
2651  my $cmdname = shift;
2652  my $command = shift;
2653  my $args = shift;
2654
2655  my $format_arg = shift @$args;
2656
2657  my $format;
2658  if (defined($format_arg)) {
2659    $format = $format_arg->{'monospacetext'};
2660  }
2661  return '' if (!defined($format) or $format eq '');
2662
2663  my $arg_index = undef;
2664  if ($inline_format_commands{$cmdname}) {
2665    if ($cmdname eq 'inlinefmtifelse'
2666        and ! $self->{'expanded_formats_hash'}->{$format}) {
2667      $arg_index = 1;
2668    } elsif ($self->{'expanded_formats_hash'}->{$format}) {
2669      $arg_index = 0;
2670    }
2671  } elsif (defined($command->{'extra'}->{'expand_index'})) {
2672    $arg_index = 0;
2673  }
2674  if (defined($arg_index) and $arg_index < scalar(@$args)) {
2675    my $text_arg = $args->[$arg_index];
2676    if ($text_arg) {
2677      if ($text_arg->{'normal'}) {
2678        return $text_arg->{'normal'};
2679      } elsif ($text_arg->{'raw'}) {
2680        return $text_arg->{'raw'};
2681      }
2682    }
2683  }
2684  return '';
2685}
2686
2687foreach my $command (keys(%inline_commands)) {
2688  $default_commands_conversion{$command} = \&_convert_inline_command;
2689}
2690
2691sub _indent_with_table ($)
2692{
2693  my $content = shift;
2694
2695  return '<table><tr><td>&nbsp;</td><td>'.$content."</td></tr></table>\n";
2696}
2697
2698my $html_menu_entry_index = 0;
2699sub _convert_preformatted_command($$$$)
2700{
2701  my $self = shift;
2702  my $cmdname = shift;
2703  my $command = shift;
2704  my $content = shift;
2705  my $extra_classes;
2706
2707  # this is mainly for classes as there are purprosely no classes
2708  # for small*
2709  my $main_cmdname;
2710  if ($small_alias{$cmdname}) {
2711    $main_cmdname = $small_alias{$cmdname};
2712  } else {
2713    $main_cmdname = $cmdname;
2714  }
2715
2716  if ($cmdname eq 'menu') {
2717    $html_menu_entry_index = 0;
2718  } elsif ($cmdname eq 'example') {
2719    if ($command->{'args'}) {
2720      $extra_classes = [];
2721      for my $example_arg (@{$command->{'args'}}) {
2722        # convert or remove all @-commands, using simple ascii and unicode
2723        # characters
2724        my $converted_arg = Texinfo::Convert::NodeNameNormalization::convert($example_arg);
2725        if ($converted_arg ne '') {
2726          push @$extra_classes, $converted_arg;
2727        }
2728      }
2729    }
2730  } elsif ($main_cmdname eq 'lisp') {
2731    $main_cmdname = 'example';
2732    $extra_classes = ['lisp'];
2733  }
2734
2735  if ($content ne '' and !$self->in_string()) {
2736    if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) {
2737      if ($indented_preformatted_commands{$cmdname}) {
2738        return _indent_with_table ($content);
2739      } else {
2740        return $content."\n";
2741      }
2742    } else {
2743      return $self->_attribute_class('div', $main_cmdname, $extra_classes).">\n".$content.'</div>'."\n";
2744    }
2745  } else {
2746    return $content;
2747  }
2748}
2749
2750foreach my $preformatted_command (keys(%preformatted_commands)) {
2751  $default_commands_conversion{$preformatted_command}
2752    = \&_convert_preformatted_command;
2753}
2754
2755sub _convert_indented_command($$$$)
2756{
2757  my $self = shift;
2758  my $cmdname = shift;
2759  my $command = shift;
2760  my $content = shift;
2761
2762  # no class for @small* variants
2763  $cmdname = $small_alias{$cmdname}
2764    if $small_alias{$cmdname};
2765
2766  if ($content ne '' and !$self->in_string()) {
2767    if ($self->get_conf('COMPLEX_FORMAT_IN_TABLE')) {
2768      return _indent_with_table ($content);
2769    } else {
2770      return $self->_attribute_class('blockquote', $cmdname).">\n"
2771             .$content.'</blockquote>'."\n";
2772    }
2773  } else {
2774    return $content;
2775  }
2776}
2777
2778$default_commands_conversion{'indentedblock'} = \&_convert_indented_command;
2779
2780sub _convert_verbatim_command($$$$)
2781{
2782  my $self = shift;
2783  my $cmdname = shift;
2784  my $command = shift;
2785  my $content = shift;
2786
2787  if (!$self->in_string) {
2788    return $self->_attribute_class('pre', $cmdname).'>'
2789          .$content . '</pre>';
2790  } else {
2791    return $content;
2792  }
2793}
2794
2795$default_commands_conversion{'verbatim'} = \&_convert_verbatim_command;
2796
2797sub _convert_displaymath_command($$$$)
2798{
2799  my $self = shift;
2800  my $cmdname = shift;
2801  my $command = shift;
2802  my $content = shift;
2803
2804  if ($self->in_string) {
2805    return $content;
2806  }
2807
2808  my $result = '';
2809  $result .= $self->_attribute_class('div', 'displaymath').'>';
2810  if ($self->get_conf('HTML_MATH')
2811        and $self->get_conf('HTML_MATH') eq 'mathjax') {
2812    $self->{'element_math'} = 1;
2813    $result .= $self->_attribute_class('em', 'tex2jax_process').'>'
2814          ."\\[$content\\]".'</em>';
2815  } else {
2816    $result .= $self->_attribute_class('em').'>'."$content".'</em>';
2817  }
2818  $result .= '</div>';
2819  return $result;
2820}
2821
2822$default_commands_conversion{'displaymath'} = \&_convert_displaymath_command;
2823
2824sub _convert_verbatiminclude_command($$$$)
2825{
2826  my $self = shift;
2827  my $cmdname = shift;
2828  my $command = shift;
2829  my $args = shift;
2830
2831  my $verbatim_include_verbatim
2832    = $self->Texinfo::Common::expand_verbatiminclude($command);
2833  if (defined($verbatim_include_verbatim)) {
2834    return $self->convert_tree($verbatim_include_verbatim);
2835  } else {
2836    return '';
2837  }
2838}
2839
2840$default_commands_conversion{'verbatiminclude'}
2841  = \&_convert_verbatiminclude_command;
2842
2843sub _convert_command_noop($$$$)
2844{
2845  my $self = shift;
2846  my $cmdname = shift;
2847  my $command = shift;
2848  my $content = shift;
2849
2850  return $content;
2851}
2852
2853$default_commands_conversion{'raggedright'} = \&_convert_command_noop;
2854$default_commands_conversion{'flushleft'} = \&_convert_command_noop;
2855$default_commands_conversion{'flushright'} = \&_convert_command_noop;
2856$default_commands_conversion{'group'} = \&_convert_command_noop;
2857
2858
2859sub _convert_sp_command($$$$)
2860{
2861  my $self = shift;
2862  my $cmdname = shift;
2863  my $command = shift;
2864  my $args = shift;
2865
2866  if (defined($command->{'extra'}->{'misc_args'}->[0])) {
2867    my $sp_nr = $command->{'extra'}->{'misc_args'}->[0];
2868    if ($self->in_preformatted() or $self->in_string()) {
2869      return "\n" x $sp_nr;
2870    } else {
2871      return "<br>\n" x $sp_nr;
2872    }
2873  }
2874}
2875
2876$default_commands_conversion{'sp'} = \&_convert_sp_command;
2877
2878sub _convert_exdent_command($$$$)
2879{
2880  my $self = shift;
2881  my $cmdname = shift;
2882  my $command = shift;
2883  my $args = shift;
2884
2885  # FIXME do something better with css and span?
2886  my $preformatted = $self->in_preformatted();
2887
2888  if ($self->in_preformatted() or $self->in_string()) {
2889    return $self->_convert_preformatted_type($cmdname, $command,
2890                                             $args->[0]->{'normal'} ."\n");
2891  } else {
2892    # ignore alignment information
2893    return "<p>".$args->[0]->{'normal'} ."\n</p>";
2894  }
2895}
2896
2897$default_commands_conversion{'exdent'} = \&_convert_exdent_command;
2898
2899sub _convert_center_command($$$$)
2900{
2901  my $self = shift;
2902  my $cmdname = shift;
2903  my $command = shift;
2904  my $args = shift;
2905
2906  if ($self->in_string()) {
2907    return $self->_convert_preformatted_type($cmdname, $command,
2908                                             $args->[0]->{'normal'}."\n");
2909  } else {
2910    return "<div align=\"center\">".$args->[0]->{'normal'}."\n</div>";
2911  }
2912}
2913
2914$default_commands_conversion{'center'} = \&_convert_center_command;
2915
2916sub _convert_author_command($$$$)
2917{
2918  my $self = shift;
2919  my $cmdname = shift;
2920  my $command = shift;
2921  my $args = shift;
2922
2923  return '' if (!$args->[0] or !$command->{'extra'}->{'titlepage'});
2924  if (!$self->in_string()) {
2925    return "<strong>$args->[0]->{'normal'}</strong><br>\n";
2926  } else {
2927    return $args->[0]->{'normal'}."\n";
2928  }
2929}
2930
2931$default_commands_conversion{'author'} = \&_convert_author_command;
2932
2933sub _convert_title_command($$$$)
2934{
2935  my $self = shift;
2936  my $cmdname = shift;
2937  my $command = shift;
2938  my $args = shift;
2939  return '' if (!$args->[0]);
2940  if (!$self->in_string()) {
2941    return "<h1>$args->[0]->{'normal'}</h1>\n";
2942  } else {
2943    return $args->[0]->{'normal'};
2944  }
2945}
2946$default_commands_conversion{'title'} = \&_convert_title_command;
2947
2948sub _convert_subtitle_command($$$$)
2949{
2950  my $self = shift;
2951  my $cmdname = shift;
2952  my $command = shift;
2953  my $args = shift;
2954  return '' if (!$args->[0]);
2955  if (!$self->in_string()) {
2956    return "<h3 align=\"right\">$args->[0]->{'normal'}</h3>\n";
2957  } else {
2958    return $args->[0]->{'normal'};
2959  }
2960}
2961$default_commands_conversion{'subtitle'} = \&_convert_subtitle_command;
2962
2963sub _convert_insertcopying_command($$$$)
2964{
2965  my $self = shift;
2966  my $cmdname = shift;
2967  my $command = shift;
2968
2969  if ($self->{'extra'} and $self->{'extra'}->{'copying'}) {
2970    return $self->convert_tree({'contents'
2971               => $self->{'extra'}->{'copying'}->{'contents'}});
2972  }
2973  return '';
2974}
2975$default_commands_conversion{'insertcopying'}
2976   = \&_convert_insertcopying_command;
2977
2978sub _convert_listoffloats_command($$$$)
2979{
2980  my $self = shift;
2981  my $cmdname = shift;
2982  my $command = shift;
2983  my $args = shift;
2984
2985  if (!$self->in_string()
2986      and $command->{'extra'} and $command->{'extra'}->{'type'}
2987      and defined($command->{'extra'}->{'type'}->{'normalized'})
2988      and $self->{'floats'}
2989      and $self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}}
2990      and @{$self->{'floats'}->{$command->{'extra'}->{'type'}->{'normalized'}}}) {
2991   my $listoffloats_name = $command->{'extra'}->{'type'}->{'normalized'};
2992   my $result = $self->_attribute_class('dl', 'listoffloats').">\n" ;
2993   foreach my $float (@{$self->{'floats'}->{$listoffloats_name}}) {
2994     my $float_href = $self->command_href($float);
2995     next if (!$float_href);
2996     $result .= '<dt>';
2997     my $float_text = $self->command_text($float);
2998     if (defined($float_text) and $float_text ne '') {
2999       if ($float_href) {
3000         $result .= "<a href=\"$float_href\">$float_text</a>";
3001       } else {
3002         $result .= $float_text;
3003       }
3004     }
3005     $result .= '</dt>';
3006     my $caption;
3007     if ($float->{'extra'}->{'shortcaption'}) {
3008       $caption = $float->{'extra'}->{'shortcaption'};
3009     } elsif ($float->{'extra'}->{'caption'}) {
3010       $caption = $float->{'extra'}->{'caption'};
3011     }
3012
3013     my $caption_text;
3014     if ($caption) {
3015       $caption_text = $self->convert_tree_new_formatting_context(
3016         $caption->{'args'}->[0], $cmdname, 'listoffloats');
3017     } else {
3018       $caption_text = '';
3019     }
3020     $result .= '<dd>'.$caption_text.'</dd>'."\n";
3021   }
3022   return $result . "</dl>\n";
3023 } else {
3024   return '';
3025 }
3026}
3027$default_commands_conversion{'listoffloats'} = \&_convert_listoffloats_command;
3028
3029sub _in_preformatted_in_menu($)
3030{
3031  my $self = shift;
3032  return 1 if ($self->get_conf('SIMPLE_MENU'));
3033  my @pre_classes = $self->preformatted_classes_stack();
3034  foreach my $pre_class (@pre_classes) {
3035    return 1 if ($preformatted_commands{$pre_class});
3036  }
3037  return 0;
3038}
3039
3040sub _convert_menu_command($$$$)
3041{
3042  my $self = shift;
3043  my $cmdname = shift;
3044  my $command = shift;
3045  my $content = shift;
3046
3047  return $content if ($cmdname eq 'detailmenu');
3048
3049  $html_menu_entry_index = 0;
3050  if ($content !~ /\S/) {
3051    return '';
3052  }
3053  # This can probably only happen with incorrect input,
3054  # for instance menu in copying
3055  # FIXME check?
3056  if ($self->in_string()) {
3057    return $content;
3058  }
3059  my $begin_row = '';
3060  my $end_row = '';
3061  if ($self->_in_preformatted_in_menu()) {
3062    $begin_row = '<tr><td>';
3063    $end_row = '</td></tr>';
3064  }
3065  return $self->_attribute_class('table', 'menu')
3066    ." border=\"0\" cellspacing=\"0\">${begin_row}\n"
3067      . $content . "${end_row}</table>\n";
3068}
3069$default_commands_conversion{'menu'} = \&_convert_menu_command;
3070$default_commands_conversion{'detailmenu'} = \&_convert_menu_command;
3071
3072sub _convert_float_command($$$$$)
3073{
3074  my $self = shift;
3075  my $cmdname = shift;
3076  my $command = shift;
3077  my $args = shift;
3078  my $content = shift;
3079
3080  my ($caption, $prepended) = Texinfo::Common::float_name_caption($self,
3081                                                                   $command);
3082  my $caption_text = '';
3083  my $prepended_text;
3084  if ($self->in_string()) {
3085    if ($prepended) {
3086      $prepended_text = $self->convert_tree_new_formatting_context(
3087        $prepended, 'float prepended');
3088    } else {
3089      $prepended_text = '';
3090    }
3091    if ($caption) {
3092      $caption_text = $self->convert_tree_new_formatting_context(
3093        {'contents' => $caption->{'args'}->[0]->{'contents'}},
3094        'float caption');
3095    }
3096    return $prepended.$content.$caption_text;
3097  }
3098
3099  my $id = $self->command_id($command);
3100  my $label;
3101  if (defined($id) and $id ne '') {
3102    $label = "<span id=\"$id\"></span>";
3103  } else {
3104    $label = '';
3105  }
3106
3107  if ($prepended) {
3108    if ($caption) {
3109      # prepend the prepended tree to the first paragraph
3110      my @caption_original_contents = @{$caption->{'args'}->[0]->{'contents'}};
3111      my @caption_contents;
3112      my $new_paragraph;
3113      while (@caption_original_contents) {
3114        my $content = shift @caption_original_contents;
3115        if ($content->{'type'} and $content->{'type'} eq 'paragraph') {
3116          %{$new_paragraph} = %{$content};
3117          $new_paragraph->{'contents'} = [@{$content->{'contents'}}];
3118          unshift (@{$new_paragraph->{'contents'}}, {'cmdname' => 'strong',
3119               'args' => [{'type' => 'brace_command_arg',
3120                          'contents' => [$prepended]}]});
3121          push @caption_contents, $new_paragraph;
3122          last;
3123        } else {
3124          push @caption_contents, $content;
3125        }
3126      }
3127      push @caption_contents, @caption_original_contents;
3128      if ($new_paragraph) {
3129        $caption_text = $self->convert_tree_new_formatting_context(
3130         {'contents' => \@caption_contents}, 'float caption');
3131        $prepended_text = '';
3132      }
3133    }
3134    if ($caption_text eq '') {
3135      $prepended_text = $self->convert_tree_new_formatting_context(
3136        $prepended, 'float prepended');
3137      if ($prepended_text ne '') {
3138        $prepended_text = '<p><strong>'.$prepended_text.'</strong></p>';
3139      }
3140    }
3141  } else {
3142    $prepended_text = '';
3143  }
3144
3145  if ($caption and $caption_text eq '') {
3146    $caption_text = $self->convert_tree_new_formatting_context(
3147      $caption->{'args'}->[0], 'float caption');
3148  }
3149  if ($prepended_text.$caption_text ne '') {
3150    $prepended_text = $self->_attribute_class('div','float-caption'). '>'
3151        . $prepended_text;
3152    $caption_text .= '</div>';
3153  }
3154  return $self->_attribute_class('div','float'). '>' .$label."\n".$content.
3155     $prepended_text.$caption_text . '</div>';
3156}
3157$default_commands_conversion{'float'} = \&_convert_float_command;
3158
3159sub _convert_quotation_command($$$$$)
3160{
3161  my $self = shift;
3162  my $cmdname = shift;
3163  my $command = shift;
3164  my $args = shift;
3165  my $content = shift;
3166
3167  #$cmdname = $small_alias{$cmdname}
3168  #  if $small_alias{$cmdname};
3169
3170  my $attribution = '';
3171  if ($command->{'extra'} and $command->{'extra'}->{'authors'}) {
3172    foreach my $author (@{$command->{'extra'}->{'authors'}}) {
3173      my $centered_author = $self->gdt("\@center --- \@emph{{author}}\n",
3174         {'author' => $author->{'args'}->[0]->{'contents'}});
3175      $centered_author->{'parent'} = $command;
3176      $attribution .= $self->convert_tree($centered_author);
3177    }
3178  }
3179  if (!$self->in_string()) {
3180    return "<blockquote>\n" . $content . "</blockquote>\n" . $attribution;
3181  } else {
3182    return $content.$attribution;
3183  }
3184}
3185$default_commands_conversion{'quotation'} = \&_convert_quotation_command;
3186
3187sub _convert_cartouche_command($$$$)
3188{
3189  my $self = shift;
3190  my $cmdname = shift;
3191  my $command = shift;
3192  my $content = shift;
3193
3194  if ($content =~ /\S/ and !$self->in_string()) {
3195    return $self->_attribute_class('table', 'cartouche')
3196       ." border=\"1\"><tr><td>\n". $content ."</td></tr></table>\n";
3197  }
3198  return $content;
3199}
3200
3201$default_commands_conversion{'cartouche'} = \&_convert_cartouche_command;
3202
3203sub _convert_itemize_command($$$$)
3204{
3205  my $self = shift;
3206  my $cmdname = shift;
3207  my $command = shift;
3208  my $content = shift;
3209
3210  if ($self->in_string()) {
3211    return $content;
3212  }
3213  if ($command->{'extra'}->{'command_as_argument'}
3214     and $command->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') {
3215    return "<ul>\n" . $content. "</ul>\n";
3216  } else {
3217    return $self->_attribute_class('ul',$NO_BULLET_LIST_CLASS).">\n"
3218            . $content . "</ul>\n";
3219  }
3220}
3221
3222$default_commands_conversion{'itemize'} = \&_convert_itemize_command;
3223
3224sub _convert_enumerate_command($$$$)
3225{
3226  my $self = shift;
3227  my $cmdname = shift;
3228  my $command = shift;
3229  my $content = shift;
3230
3231  if ($self->in_string()) {
3232    return $content;
3233  }
3234  if ($content eq '') {
3235    return '';
3236  }
3237  my $specification = $command->{'extra'}->{'enumerate_specification'};
3238  if (defined $specification) {
3239    my ($start, $type);
3240    if ($specification =~ /^\d*$/ and $specification ne '1') {
3241      $start = $specification;
3242    } elsif ($specification =~ /^[A-Z]$/) {
3243      $start = 1 + ord($specification) - ord('A');
3244      $type = 'A';
3245    } elsif ($specification =~ /^[a-z]$/) {
3246      $start = 1 + ord($specification) - ord('a');
3247      $type = 'a';
3248    }
3249    if (defined $type and defined $start) {
3250      return "<ol type=\"$type\" start=\"$start\">\n" . $content . "</ol>\n";
3251    } elsif (defined $start) {
3252      return "<ol start=\"$start\">\n" . $content . "</ol>\n";
3253    }
3254  }
3255  return "<ol>\n" . $content . "</ol>\n";
3256}
3257
3258$default_commands_conversion{'enumerate'} = \&_convert_enumerate_command;
3259
3260sub _convert_multitable_command($$$$)
3261{
3262  my $self = shift;
3263  my $cmdname = shift;
3264  my $command = shift;
3265  my $content = shift;
3266
3267  if ($self->in_string()) {
3268    return $content;
3269  }
3270  if ($content =~ /\S/) {
3271    return "<table>\n" . $content . "</table>\n";
3272  } else {
3273    return '';
3274  }
3275}
3276
3277$default_commands_conversion{'multitable'} = \&_convert_multitable_command;
3278
3279sub _convert_xtable_command($$$$)
3280{
3281  my $self = shift;
3282  my $cmdname = shift;
3283  my $command = shift;
3284  my $content = shift;
3285
3286  if ($self->in_string()) {
3287    return $content;
3288  }
3289  if ($content ne '') {
3290    return "<dl compact=\"compact\">\n" . $content . "</dl>\n";
3291  } else {
3292    return '';
3293  }
3294}
3295$default_commands_conversion{'table'} = \&_convert_xtable_command;
3296$default_commands_conversion{'ftable'} = \&_convert_xtable_command;
3297$default_commands_conversion{'vtable'} = \&_convert_xtable_command;
3298
3299sub _convert_item_command($$$$)
3300{
3301  my $self = shift;
3302  my $cmdname = shift;
3303  my $command = shift;
3304  my $content = shift;
3305
3306  if ($self->in_string()) {
3307    return $content;
3308  }
3309  if ($command->{'parent'}->{'cmdname'}
3310      and $command->{'parent'}->{'cmdname'} eq 'itemize') {
3311    my $prepend ;
3312    my $itemize = $command->{'parent'};
3313    if ($itemize->{'extra'}->{'command_as_argument'}
3314       and $itemize->{'extra'}->{'command_as_argument'}->{'cmdname'} eq 'bullet') {
3315      $prepend = '';
3316    } else {
3317      # Setting multiple expansion should not be needed, except in
3318      # case of invalid constructs
3319      $prepend = $self->convert_tree_new_formatting_context(
3320        $itemize->{'args'}->[0],
3321        $command->{'cmdname'}, 'item_prepended');
3322    }
3323    if ($content =~ /\S/) {
3324      return '<li>' . $prepend .' '. $content . '</li>';
3325    } else {
3326      return '';
3327    }
3328  } elsif ($command->{'parent'}->{'cmdname'}
3329      and $command->{'parent'}->{'cmdname'} eq 'enumerate') {
3330    if ($content =~ /\S/) {
3331      return '<li>' . ' ' . $content . '</li>';
3332    } else {
3333      return '';
3334    }
3335  } elsif ($command->{'parent'}->{'type'}
3336           and $command->{'parent'}->{'type'} eq 'table_term') {
3337    # FIXME instead use the code of Plaintext or DocBook.
3338    my $args = $content;
3339    if ($args->[0]) {
3340      my $tree = $self->_table_item_content_tree($command,
3341                                                [$args->[0]->{'tree'}]);
3342      my $result = $self->convert_tree ($tree);
3343      foreach my $command_name (reverse($self->commands_stack())) {
3344        if ($preformatted_code_commands{$command_name}) {
3345          $result = '<tt>' .$result. '</tt>';
3346          last;
3347        }
3348      }
3349      my $index_id = $self->command_id ($command);
3350      my $anchor;
3351      if (defined($index_id)) {
3352        $anchor = $self->_get_copiable_anchor($index_id);
3353        $index_id = " id='$index_id'";
3354      } else {
3355        $anchor = '';
3356        $index_id = '';
3357      }
3358
3359      return "<dt${index_id}><span>$result$anchor</span></dt>\n";
3360    } else {
3361      return '';
3362    }
3363  } elsif ($command->{'parent'}->{'type'}
3364           and $command->{'parent'}->{'type'} eq 'row') {
3365    return $self->_convert_tab_command ($cmdname, $command, $content);
3366  }
3367  return '';
3368}
3369$default_commands_conversion{'item'} = \&_convert_item_command;
3370$default_commands_conversion{'headitem'} = \&_convert_item_command;
3371$default_commands_conversion{'itemx'} = \&_convert_item_command;
3372
3373sub _convert_tab_command ($$$$)
3374{
3375  my $self = shift;
3376  my $cmdname = shift;
3377  my $command = shift;
3378  my $content = shift;
3379
3380  my $cell_nr = $command->{'extra'}->{'cell_number'};
3381  my $row = $command->{'parent'};
3382  my $row_cmdname = $row->{'contents'}->[0]->{'cmdname'};
3383  my $multitable = $row->{'parent'}->{'parent'};
3384
3385  my $fractions = '';
3386  my $cf = $multitable->{'extra'}->{'columnfractions'};
3387  if ($cf) {
3388    if (exists($cf->{'extra'}->{'misc_args'}->[$cell_nr-1])) {
3389      my $fraction = sprintf('%d',
3390                             100*$cf->{'extra'}->{'misc_args'}->[$cell_nr-1]);
3391      $fractions = " width=\"$fraction%\"";
3392    }
3393  }
3394
3395  $content =~ s/^\s*//;
3396  $content =~ s/\s*$//;
3397
3398  if ($self->in_string()) {
3399    return $content;
3400  }
3401  if ($row_cmdname eq 'headitem') {
3402    return "<th${fractions}>" . $content . '</th>';
3403  } else {
3404    return "<td${fractions}>" . $content . '</td>';
3405  }
3406}
3407$default_commands_conversion{'tab'} = \&_convert_tab_command;
3408
3409sub _convert_xref_commands($$$$)
3410{
3411  my $self = shift;
3412  my $cmdname = shift;
3413  my $root = shift;
3414  my $args = shift;
3415
3416  my $tree;
3417  my $name;
3418  if ($cmdname ne 'inforef'
3419      and defined($args->[2]->{'normal'}) and $args->[2]->{'normal'} ne '') {
3420    $name = $args->[2]->{'normal'};
3421  } elsif (defined($args->[1]->{'normal'}) and $args->[1]->{'normal'} ne '') {
3422    $name = $args->[1]->{'normal'}
3423  }
3424
3425  if ($cmdname eq 'inforef') {
3426    $args->[3] = $args->[2];
3427    $args->[2] = undef;
3428  }
3429
3430  my $file_arg_tree;
3431  my $file = '';
3432  if (defined($args->[3]->{'monospacetext'})
3433              and $args->[3]->{'monospacetext'} ne '') {
3434    $file_arg_tree = $args->[3]->{'tree'};
3435    $file = $args->[3]->{'monospacetext'};
3436  }
3437
3438  my $book = '';
3439  $book = $args->[4]->{'normal'} if (defined($args->[4]->{'normal'}));
3440
3441  # internal reference
3442  if ($cmdname ne 'inforef' and $book eq '' and $file eq ''
3443      and $root->{'extra'}->{'node_argument'}
3444      and defined($root->{'extra'}->{'node_argument'}->{'normalized'})
3445      and !$root->{'extra'}->{'node_argument'}->{'manual_content'}
3446      and $self->{'labels'}
3447      and $self->{'labels'}->{$root->{'extra'}->{'node_argument'}->{'normalized'}}) {
3448    my $node
3449     = $self->label_command($root->{'extra'}->{'node_argument'}->{'normalized'});
3450    # This is the node if USE_NODES, otherwise this may be the sectioning
3451    # command (if the sectioning command is really associated to the node)
3452    my $command = $self->command_element_command($node);
3453    $command = $node if (!$node->{'extra'}->{'associated_section'}
3454                         or $node->{'extra'}->{'associated_section'} ne $command);
3455
3456    my $href = $self->command_href($command, undef, $root);
3457
3458    if (!defined($name)) {
3459      if ($self->get_conf('xrefautomaticsectiontitle') eq 'on'
3460         and $node->{'extra'}->{'associated_section'}) {
3461        $command = $node->{'extra'}->{'associated_section'};
3462        $name = $self->command_text($command, 'text_nonumber');
3463      } elsif ($node->{'cmdname'} eq 'float') {
3464        if (!$self->get_conf('XREF_USE_FLOAT_LABEL')) {
3465          $name = $self->command_text($command);
3466        }
3467        if (!defined($name) or $name eq '') {
3468          if (defined($args->[0]->{'monospace'})) {
3469            $name = $args->[0]->{'monospace'};
3470          } else {
3471            $name = '';
3472          }
3473        }
3474      } elsif (!$self->get_conf('XREF_USE_NODE_NAME_ARG')
3475               and (defined($self->get_conf('XREF_USE_NODE_NAME_ARG'))
3476                    or !$self->in_preformatted())) {
3477        $name = $self->command_text($command, 'text_nonumber');
3478        #die "$command $command->{'normalized'}" if (!defined($name));
3479      } elsif (defined($args->[0]->{'monospace'})) {
3480        $name = $args->[0]->{'monospace'};
3481      } else {
3482        $name = '';
3483      }
3484    }
3485    my $reference = $name;
3486    $reference = "<a href=\"$href\">$name</a>" if ($href ne ''
3487                                                   and !$self->in_string());
3488
3489    # maybe use {'extra'}->{'node_argument'}?
3490    my $is_section = ($command->{'cmdname'} ne 'node'
3491                      and $command->{'cmdname'} ne 'anchor'
3492                      and $command->{'cmdname'} ne 'float');
3493    if ($cmdname eq 'pxref') {
3494      $tree = $self->gdt('see {reference_name}',
3495        { 'reference_name' => {'type' => '_converted', 'text' => $reference} });
3496    } elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') {
3497      $tree = $self->gdt('See {reference_name}',
3498        { 'reference_name' => {'type' => '_converted', 'text' => $reference} });
3499    } elsif ($cmdname eq 'ref') {
3500      $tree = $self->gdt('{reference_name}',
3501         { 'reference_name' => {'type' => '_converted', 'text' => $reference} });
3502    }
3503  } else {
3504    # external reference
3505    my $node_entry = {};
3506    $node_entry->{'node_content'} = $root->{'extra'}->{'node_argument'}->{'node_content'}
3507      if ($root->{'extra'}->{'node_argument'}
3508          and $root->{'extra'}->{'node_argument'}->{'node_content'});
3509    $node_entry->{'normalized'} = $root->{'extra'}->{'node_argument'}->{'normalized'}
3510      if ($root->{'extra'}->{'node_argument'}
3511          and exists($root->{'extra'}->{'node_argument'}->{'normalized'}));
3512
3513    # file argument takes precedence over the file in the node (file)node entry
3514    if (defined($file_arg_tree) and $file ne '') {
3515      $node_entry->{'manual_content'} = $file_arg_tree->{'contents'};
3516    } elsif ($root->{'extra'}->{'node_argument'}
3517             and $root->{'extra'}->{'node_argument'}->{'manual_content'}) {
3518      $node_entry->{'manual_content'}
3519        = $root->{'extra'}->{'node_argument'}->{'manual_content'};
3520      my $file_with_node_tree = {'type' => '_code',
3521                                  'contents' => [@{$node_entry->{'manual_content'}}]};
3522      $file = $self->convert_tree($file_with_node_tree, 'node file in ref');
3523    }
3524    my $href = $self->command_href($node_entry, undef, $root);
3525
3526    if ($book eq '') {
3527      if (!defined($name)) {
3528        my $node_name = $self->command_text($node_entry);
3529        $name = $node_name;
3530      } elsif ($file ne '') {
3531        $name = "($file)$name";
3532      }
3533    } elsif (!defined($name) and $node_entry->{'node_content'}) {
3534      my $node_no_file_tree = {'type' => '_code',
3535                               'contents' => [@{$node_entry->{'node_content'}}]};
3536      my $node_name = $self->convert_tree($node_no_file_tree, 'node in ref');
3537      if (defined($node_name) and ($self->get_conf('KEEP_TOP_EXTERNAL_REF')
3538                                   or $node_name ne 'Top')) {
3539        $name = $node_name;
3540      }
3541    }
3542
3543    # not exactly sure when it happens.  Something like @ref{(file),,,Manual}?
3544    $name = $args->[0]->{'monospace'}
3545       if (!defined($name)
3546           # FIXME could it really be Top?
3547           and ($self->get_conf('KEEP_TOP_EXTERNAL_REF')
3548                or $args->[0]->{'monospace'} ne 'Top'));
3549
3550    $name = '' if (!defined($name));
3551    my $reference = $name;
3552    my $book_reference = '';
3553    if (!$self->in_string() and $href ne '') {
3554      # attribute to distiguish links to Texinfo manuals from other links
3555      # and to provide manual name of target
3556      my $attribute = '';
3557      if ($file) {
3558        $attribute = "data-manual=\"".$self->protect_text($file)."\" ";
3559      }
3560      if ($name ne '') {
3561        $reference = "<a ${attribute}href=\"$href\">$name</a>";
3562      } elsif ($book ne '') {
3563        $book_reference = "<a ${attribute}href=\"$href\">$book</a>";
3564      }
3565    }
3566    if ($cmdname eq 'pxref') {
3567      if (($book ne '') and ($href ne '') and ($reference ne '')) {
3568        $tree = $self->gdt('see {reference} in @cite{{book}}',
3569            { 'reference' => {'type' => '_converted', 'text' => $reference},
3570              'book' => {'type' => '_converted', 'text' => $book }});
3571      } elsif ($book_reference ne '') {
3572        $tree = $self->gdt('see @cite{{book_reference}}',
3573            { 'book_reference' => {'type' => '_converted',
3574                                   'text' => $book_reference }});
3575      } elsif (($book ne '') and ($reference ne '')) {
3576        $tree = $self->gdt('see `{section}\' in @cite{{book}}',
3577            { 'section' => {'type' => '_converted', 'text' => $reference},
3578              'book' => {'type' => '_converted', 'text' => $book }});
3579      } elsif ($book ne '') { # should seldom or even never happen
3580        $tree = $self->gdt('see @cite{{book}}',
3581              {'book' => {'type' => '_converted', 'text' => $book }});
3582      } elsif ($href ne '') {
3583        $tree = $self->gdt('see {reference}',
3584             { 'reference' => {'type' => '_converted', 'text' => $reference} });
3585      } elsif ($reference ne '') {
3586        $tree = $self->gdt('see `{section}\'', {
3587              'section' => {'type' => '_converted', 'text' => $reference} });
3588      }
3589    } elsif ($cmdname eq 'xref' or $cmdname eq 'inforef') {
3590      if (($book ne '') and ($href ne '') and ($reference ne '')) {
3591        $tree = $self->gdt('See {reference} in @cite{{book}}',
3592            { 'reference' => {'type' => '_converted', 'text' => $reference},
3593              'book' => {'type' => '_converted', 'text' => $book }});
3594      } elsif ($book_reference ne '') {
3595        $tree = $self->gdt('See @cite{{book_reference}}',
3596            { 'book_reference' => {'type' => '_converted',
3597                                   'text' => $book_reference }});
3598      } elsif (($book ne '') and ($reference ne '')) {
3599        $tree = $self->gdt('See `{section}\' in @cite{{book}}',
3600            { 'section' => {'type' => '_converted', 'text' => $reference},
3601              'book' => {'type' => '_converted', 'text' => $book }});
3602      } elsif ($book ne '') { # should seldom or even never happen
3603        $tree = $self->gdt('See @cite{{book}}',
3604              {'book' => {'type' => '_converted', 'text' => $book }});
3605      } elsif ($href ne '') {
3606        $tree = $self->gdt('See {reference}',
3607             { 'reference' => {'type' => '_converted', 'text' => $reference} });
3608      } elsif ($reference ne '') {
3609        $tree = $self->gdt('See `{section}\'', {
3610              'section' => {'type' => '_converted', 'text' => $reference} });
3611      }
3612    } else {
3613      if (($book ne '') and ($href ne '') and ($reference ne '')) {
3614        $tree = $self->gdt('{reference} in @cite{{book}}',
3615            { 'reference' => {'type' => '_converted', 'text' => $reference},
3616              'book' => {'type' => '_converted', 'text' => $book }});
3617      } elsif ($book_reference ne '') {
3618        $tree = $self->gdt('@cite{{book_reference}}',
3619            { 'book_reference' => {'type' => '_converted',
3620                                   'text' => $book_reference }});
3621      } elsif (($book ne '') and ($reference ne '')) {
3622        $tree = $self->gdt('`{section}\' in @cite{{book}}',
3623            { 'section' => {'type' => '_converted', 'text' => $reference},
3624              'book' => {'type' => '_converted', 'text' => $book }});
3625      } elsif ($book ne '') { # should seldom or even never happen
3626        $tree = $self->gdt('@cite{{book}}',
3627              {'book' => {'type' => '_converted', 'text' => $book }});
3628      } elsif ($href ne '') {
3629        $tree = $self->gdt('{reference}',
3630             { 'reference' => {'type' => '_converted', 'text' => $reference} });
3631      } elsif ($reference ne '') {
3632        $tree = $self->gdt('`{section}\'', {
3633              'section' => {'type' => '_converted', 'text' => $reference} });
3634      }
3635    }
3636    if (!defined($tree)) {
3637      # May happen if there is no argument
3638      #die "external: $cmdname, ($args), '$name' '$file' '$book' '$href' '$reference'. tree undef";
3639      return '';
3640    }
3641  }
3642  return $self->convert_tree($tree);
3643}
3644foreach my $command(keys(%ref_commands)) {
3645  $default_commands_conversion{$command} = \&_convert_xref_commands;
3646}
3647
3648sub _convert_index_command($$$$)
3649{
3650  my $self = shift;
3651  my $cmdname = shift;
3652  my $command = shift;
3653  my $args = shift;
3654
3655  my $index_id = $self->command_id($command);
3656  if (defined($index_id) and $index_id ne ''
3657      and !@{$self->{'multiple_pass'}}
3658      and !$self->in_string()) {
3659    my $result = "<span id=\"$index_id\"></span>";
3660    $result .= "\n" unless ($self->in_preformatted());
3661    return $result;
3662  }
3663  return '';
3664}
3665$default_commands_conversion{'cindex'} = \&_convert_index_command;
3666
3667my %formatted_index_entries;
3668
3669sub _convert_printindex_command($$$$)
3670{
3671  my $self = shift;
3672  my $cmdname = shift;
3673  my $command = shift;
3674  my $args = shift;
3675
3676  my $index_name;
3677  if ($command->{'extra'} and $command->{'extra'}->{'misc_args'}
3678      and defined($command->{'extra'}->{'misc_args'}->[0])) {
3679    $index_name = $command->{'extra'}->{'misc_args'}->[0];
3680  } else {
3681    return '';
3682  }
3683  if (!$self->{'index_entries_by_letter'}
3684      or !$self->{'index_entries_by_letter'}->{$index_name}
3685      or !@{$self->{'index_entries_by_letter'}->{$index_name}}) {
3686    return '';
3687  }
3688
3689  #foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
3690  #  print STDERR "IIIIIII $letter_entry->{'letter'}\n";
3691  #  foreach my $index_entry (@{$letter_entry->{'entries'}}) {
3692  #    print STDERR "   ".join('|', keys(%$index_entry))."||| $index_entry->{'key'}\n";
3693  #  }
3694  #}
3695  return '' if ($self->in_string());
3696
3697  $self->_new_document_context($cmdname);
3698
3699  my $result = '';
3700
3701  # First do the summary letters linking to the letters done below
3702  my %letter_id;
3703  my @non_alpha = ();
3704  my @alpha = ();
3705  # collect the links
3706  my $symbol_idx = 0;
3707  foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
3708    my $letter = $letter_entry->{'letter'};
3709    my $index_element_id = $self->_element_direction($self->{'current_element'},
3710                                                     'This', 'target');
3711    if (!defined($index_element_id)) {
3712      $index_element_id = $target_prefix;
3713    }
3714    my $is_symbol = $letter !~ /^[[:alpha:]]/;
3715    my $identifier;
3716    if ($is_symbol) {
3717      $symbol_idx++;
3718      $identifier = $index_element_id . "_${index_name}_symbol-$symbol_idx";
3719    } else {
3720      $identifier = $index_element_id . "_${index_name}_letter-${letter}";
3721    }
3722    $letter_id{$letter} = $identifier;
3723
3724    my $summary_letter_link = $self->_attribute_class('a', 'summary-letter')
3725       ." href=\"#$identifier\"><b>".$self->protect_text($letter).'</b></a>';
3726    if ($is_symbol) {
3727      push @non_alpha, $summary_letter_link;
3728    } else {
3729      push @alpha, $summary_letter_link;
3730    }
3731  }
3732  # Format the summary letters
3733  my $join = '';
3734  my $non_alpha_text = '';
3735  my $alpha_text = '';
3736  $join = " &nbsp; \n<br>\n" if (@non_alpha and @alpha);
3737  if (@non_alpha) {
3738    $non_alpha_text = join("\n &nbsp; \n", @non_alpha) . "\n";
3739  }
3740  if (@alpha) {
3741    $alpha_text = join("\n &nbsp; \n", @alpha) . "\n &nbsp; \n";
3742  }
3743  # format the summary
3744  my $summary = "<table><tr><th valign=\"top\">"
3745    . $self->convert_tree($self->gdt('Jump to')) .": &nbsp; </th><td>" .
3746    $non_alpha_text . $join . $alpha_text . "</td></tr></table>\n";
3747
3748  $result .= $summary;
3749
3750  # now format the index entries
3751  $result .= $self->_attribute_class('table', "index-$index_name")
3752    ." border=\"0\">\n" . "<tr><td></td><th align=\"left\">"
3753    . $self->convert_tree($self->gdt('Index Entry'))
3754    . "</th><td>&nbsp;</td><th align=\"left\"> "
3755    .  $self->convert_tree($self->gdt('Section'))
3756    ."</th></tr>\n" . "<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE')
3757    ."</td></tr>\n";
3758  foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
3759    my $letter = $letter_entry->{'letter'};
3760    my $entries_text = '';
3761    foreach my $index_entry_ref (@{$letter_entry->{'entries'}}) {
3762      # to avoid double error messages set ignore_notice if an entry was
3763      # already formatted once, for example if there are multiple printindex.
3764      my $already_formatted;
3765      if (!$formatted_index_entries{$index_entry_ref}) {
3766        $formatted_index_entries{$index_entry_ref} = 1;
3767      } else {
3768        $already_formatted = 1;
3769        $self->{'ignore_notice'}++;
3770      }
3771
3772      my $entry;
3773      if ($index_entry_ref->{'in_code'}) {
3774        $entry = $self->convert_tree({'type' => '_code',
3775                                      'contents' => $index_entry_ref->{'content'}});
3776      } else {
3777        $entry = $self->convert_tree({'contents' => $index_entry_ref->{'content'}});
3778      }
3779      $entry .= $self->convert_index_subentries($index_entry_ref);
3780      if ($already_formatted) {
3781        $self->{'ignore_notice'}--;
3782      }
3783
3784      next if ($entry !~ /\S/);
3785      $entry = '<code>' .$entry .'</code>' if ($index_entry_ref->{'in_code'});
3786      my $entry_href = $self->command_href($index_entry_ref->{'command'});
3787      my $associated_command;
3788      if ($self->get_conf('NODE_NAME_IN_INDEX')) {
3789        $associated_command = $index_entry_ref->{'node'};
3790        if (!defined($associated_command)) {
3791          $associated_command
3792            = $self->command_node($index_entry_ref->{'command'});
3793        }
3794      }
3795      if (!$associated_command) {
3796        $associated_command
3797          = $self->command_element_command($index_entry_ref->{'command'});
3798        if (!$associated_command) {
3799          # Use Top if not associated command found
3800          $associated_command
3801            = $self->element_command($self->global_element('Top'));
3802        }
3803      }
3804      my ($associated_command_href, $associated_command_text);
3805      if ($associated_command) {
3806        $associated_command_href = $self->command_href($associated_command);
3807        $associated_command_text = $self->command_text($associated_command);
3808      }
3809
3810      $entries_text .= '<tr><td></td><td valign="top">'
3811         . "<a href=\"$entry_href\">$entry</a>" .
3812          $self->get_conf('INDEX_ENTRY_COLON') .
3813        '</td><td>&nbsp;</td><td valign="top">';
3814      $entries_text .= "<a href=\"$associated_command_href\">$associated_command_text</a>"
3815         if ($associated_command_href);
3816       $entries_text .= "</td></tr>\n";
3817    }
3818    # a letter and associated indice entries
3819    $result .= '<tr>' .
3820    "<th id=\"$letter_id{$letter}\">".$self->protect_text($letter)
3821        .  "</th><td></td><td></td></tr>\n" . $entries_text .
3822       "<tr><td colspan=\"4\"> ".$self->get_conf('DEFAULT_RULE')."</td></tr>\n";
3823
3824  }
3825  $result .= "</table>\n";
3826
3827  pop @{$self->{'document_context'}};
3828
3829  return $result .$summary;
3830}
3831$default_commands_conversion{'printindex'} = \&_convert_printindex_command;
3832
3833sub _contents_inline_element($$$)
3834{
3835  my $self = shift;
3836  my $cmdname = shift;
3837  my $command = shift;
3838
3839  my $content = &{$self->{'format_contents'}}($self, $cmdname, $command);
3840  if ($content) {
3841    my $element_name = $contents_command_element_name{$cmdname};
3842    my $special_element
3843      = $self->special_element($element_name);
3844    my $heading;
3845    my $result = "<div class=\"${element_name}_element\"";
3846    if ($special_element) {
3847      my $id = $self->command_id($special_element);
3848      if ($id ne '') {
3849        $result .= " id=\"$id\"";
3850      }
3851      $heading = $self->command_text($special_element);
3852    } else {
3853      # happens when called as convert() and not output()
3854      #cluck "$cmdname special element not defined";
3855      $heading
3856        = $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{$element_name});
3857    }
3858    $result .= ">\n";
3859    my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name};
3860    $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
3861                       $heading, $self->get_conf('CHAPTER_HEADER_LEVEL'))."\n";
3862    $result .= $content . "</div>\n";
3863    return $result;
3864  }
3865  return '';
3866}
3867
3868sub _convert_informative_command($$$$)
3869{
3870  my $self = shift;
3871  my $cmdname = shift;
3872  my $command = shift;
3873
3874  return '' if ($self->in_string());
3875  $cmdname = 'shortcontents' if ($cmdname eq 'summarycontents');
3876
3877  $self->_informative_command($command);
3878  if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'inline'
3879      and ($cmdname eq 'contents' or $cmdname eq 'shortcontents')
3880      and $self->get_conf($cmdname)
3881      and $self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
3882      and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
3883    return $self->_contents_inline_element($cmdname, $command);
3884  }
3885  if ($cmdname eq 'documentlanguage') {
3886    $self->_translate_names();
3887  }
3888  return '';
3889}
3890
3891foreach my $informative_command (@informative_global_commands) {
3892  $default_commands_conversion{$informative_command}
3893    = \&_convert_informative_command;
3894}
3895
3896# associate same formatting function for @small* command
3897# as for the associated @-command
3898foreach my $small_command (keys(%small_alias)) {
3899  $default_commands_conversion{$small_command}
3900    = $default_commands_conversion{$small_alias{$small_command}};
3901}
3902
3903# Keys are tree element types, values are function references to convert
3904# elements of that type.  Can be overridden with
3905# Texinfo::Config::texinfo_types_conversion, setup by
3906# Texinfo::Config::texinfo_register_type_formatting()
3907my %default_types_conversion;
3908
3909sub default_types_conversion($$)
3910{
3911  my $self = shift;
3912  my $type = shift;
3913  return $default_types_conversion{$type};
3914}
3915
3916# Ignored commands
3917foreach my $type ('empty_line_after_command', 'preamble',
3918            'preamble_before_setfilename',
3919            'empty_spaces_after_command', 'spaces_at_end',
3920            'empty_spaces_before_argument', 'empty_spaces_before_paragraph',
3921            'empty_spaces_after_close_brace') {
3922  $default_types_conversion{$type} = undef;
3923}
3924
3925my %paragraph_style = (
3926      'center'     => 'center',
3927      'flushleft'  => 'left',
3928      'flushright' => 'right',
3929      );
3930
3931sub _quotation_arg_to_prepend($$)
3932{
3933  my $self = shift;
3934  my $command = shift;
3935  if ($command->{'parent'} and $command->{'parent'}->{'cmdname'}
3936      and ($command->{'parent'}->{'cmdname'} eq 'quotation'
3937           or $command->{'parent'}->{'cmdname'} eq 'smallquotation')
3938      and $command->{'parent'}->{'args'}
3939      and $command->{'parent'}->{'args'}->[0]
3940      and $command->{'parent'}->{'args'}->[0]->{'contents'}
3941      and @{$command->{'parent'}->{'args'}->[0]->{'contents'}}) {
3942    return $self->convert_tree($self->gdt('@b{{quotation_arg}:} ',
3943     {'quotation_arg' =>
3944      $command->{'parent'}->{'args'}->[0]->{'contents'}}));
3945
3946  }
3947  return undef;
3948}
3949
3950sub _convert_paragraph_type($$$$)
3951{
3952  my $self = shift;
3953  my $type = shift;
3954  my $command = shift;
3955  my $content = shift;
3956
3957  if ($self->paragraph_number() == 1) {
3958    my $in_format = $self->top_format();
3959    if ($in_format) {
3960      # no first paragraph in those environment to avoid extra spacing
3961      if ($in_format eq 'itemize'
3962          or $in_format eq 'enumerate'
3963          or $in_format eq 'multitable') {
3964        return $content;
3965      } else {
3966        my $prepended = $self->_quotation_arg_to_prepend($command);
3967        $content = $prepended.$content if (defined($prepended));
3968      }
3969    }
3970  }
3971  return $content if ($self->in_string());
3972
3973  if ($content =~ /\S/) {
3974    my $align = $self->in_align();
3975    if ($align and $paragraph_style{$align}) {
3976      return "<p align=\"$paragraph_style{$align}\">".$content."</p>";
3977    } else {
3978      return "<p>".$content."</p>";
3979    }
3980  } else {
3981    return '';
3982  }
3983}
3984
3985$default_types_conversion{'paragraph'} = \&_convert_paragraph_type;
3986
3987sub _preformatted_class()
3988{
3989  my $self = shift;
3990  my $pre_class;
3991  my @pre_classes = $self->preformatted_classes_stack();
3992  foreach my $class (@pre_classes) {
3993    # FIXME maybe add   or $pre_class eq 'menu-preformatted'  to override
3994    # 'menu-preformatted' with 'menu-comment'?
3995    $pre_class = $class unless ($pre_class
3996                           and $preformatted_code_commands{$pre_class}
3997                           and !($preformatted_code_commands{$class}
3998                                 or $class eq 'menu-preformatted'));
3999  }
4000  return $pre_class;
4001}
4002
4003sub _convert_preformatted_type($$$$)
4004{
4005  my $self = shift;
4006  my $type = shift;
4007  my $command = shift;
4008  my $content = shift;
4009
4010  if (!defined($content)) {
4011    cluck "content undef in _convert_preformatted_type "
4012       .Texinfo::Common::_print_current($command);
4013  }
4014
4015  my $current = $command;
4016
4017  # !defined preformatted_number may happen if there is something before the
4018  # first preformatted.  For example an @exdent.
4019  if ($self->preformatted_number() and $self->preformatted_number() == 1) {
4020    my $prepended = $self->_quotation_arg_to_prepend($command);
4021    $content = $prepended.$content if (defined($prepended));
4022  }
4023
4024  return '' if ($content eq '');
4025  return $content if ($type eq 'rawpreformatted');
4026
4027  my $pre_class = $self->_preformatted_class();
4028
4029  if ($self->top_format() eq 'multitable') {
4030    $content =~ s/^\s*//;
4031    $content =~ s/\s*$//;
4032  }
4033
4034  # menu_entry_description is always in a preformatted container
4035  # in the tree, as the whole menu is meant to be an
4036  # environment where spaces and newlines are preserved.
4037  #
4038  # However, if not in preformatted block command (nor in SIMPLE_MENU),
4039  # we don't preserve spaces and newlines in menu_entry_description,
4040  # instead the whole menu_entry is in a table, so here, not <pre>
4041  if ($command->{'parent'}->{'type'}
4042      and $command->{'parent'}->{'type'} eq 'menu_entry_description'
4043      and !$self->_in_preformatted_in_menu()) {
4044    return $content;
4045  }
4046
4047  if ($self->in_string()) {
4048    return $content;
4049  }
4050  $content =~ s/^\n/\n\n/; # a newline immediately after a <pre> is ignored.
4051  my $result = $self->_attribute_class('pre', $pre_class).">".$content."</pre>";
4052
4053  # this may happen with lines without textual content
4054  # between a def* and def*x.
4055  if ($command->{'parent'}->{'cmdname'}
4056      and $command->{'parent'}->{'cmdname'} =~ /^def/) {
4057    $result = '<dd>'.$result.'</dd>';
4058  }
4059  return $result;
4060}
4061
4062$default_types_conversion{'preformatted'} = \&_convert_preformatted_type;
4063$default_types_conversion{'rawpreformatted'} = \&_convert_preformatted_type;
4064
4065sub _convert_bracketed_type($$$$) {
4066  my $self = shift;
4067  my $type = shift;
4068  my $command = shift;
4069  my $content = shift;
4070#print STDERR "$self $type $command $content\n";
4071
4072  return '{'.$content.'}';
4073}
4074
4075$default_types_conversion{'bracketed'} = \&_convert_bracketed_type;
4076
4077sub _convert_definfoenclose_type($$$$) {
4078  my $self = shift;
4079  my $type = shift;
4080  my $command = shift;
4081  my $content = shift;
4082
4083  return $self->protect_text($command->{'extra'}->{'begin'}) . $content
4084         .$self->protect_text($command->{'extra'}->{'end'});
4085}
4086
4087$default_types_conversion{'definfoenclose_command'}
4088  = \&_convert_definfoenclose_type;
4089
4090sub _convert_text($$$)
4091{
4092  my $self = shift;
4093  my $type = shift;
4094  my $command = shift;
4095  my $text = shift;
4096
4097  if ($self->in_verbatim()) {
4098    return $self->protect_text($text);
4099  }
4100  return $text if ($self->in_raw());
4101  $text = uc($text) if ($self->in_upper_case());
4102  $text = $self->protect_text($text);
4103  if ($self->get_conf('ENABLE_ENCODING') and
4104      !$self->get_conf('ENABLE_ENCODING_USE_ENTITY')
4105      and $self->get_conf('OUTPUT_ENCODING_NAME')
4106      and $self->get_conf('OUTPUT_ENCODING_NAME') eq 'utf-8') {
4107    $text = Texinfo::Convert::Unicode::unicode_text($text,
4108                                        ($self->in_code() or $self->in_math()));
4109  } elsif (!$self->in_code() and !$self->in_math()) {
4110    if ($self->get_conf('USE_ISO')) {
4111      $text =~ s/---/\&mdash\;/g;
4112      $text =~ s/--/\&ndash\;/g;
4113      $text =~ s/``/\&ldquo\;/g;
4114      $text =~ s/''/\&rdquo\;/g;
4115      $text =~ s/'/\&rsquo\;/g;
4116      $text =~ s/`/\&lsquo\;/g;
4117    } else {
4118      $text =~ s/``/&quot;/g;
4119      $text =~ s/''/&quot;/g;
4120      $text =~ s/---/\x{1F}/g;
4121      $text =~ s/--/-/g;
4122      $text =~ s/\x{1F}/--/g;
4123    }
4124  }
4125  $text = $self->_protect_space($text);
4126  return $text;
4127}
4128
4129$default_types_conversion{'text'} = \&_convert_text;
4130
4131sub _simplify_text_for_comparison($)
4132{
4133  my $text = shift;
4134  $text =~ s/[^\w]//g;
4135  return $text;
4136}
4137
4138sub _convert_row_type($$$$) {
4139  my $self = shift;
4140  my $type = shift;
4141  my $command = shift;
4142  my $content = shift;
4143
4144  return $content if ($self->in_string());
4145  if ($content =~ /\S/) {
4146    my $row_cmdname = $command->{'contents'}->[0]->{'cmdname'};
4147    if ($row_cmdname eq 'headitem') {
4148      return '<thead><tr>' . $content . '</tr></thead>' . "\n";
4149    } else {
4150      return '<tr>' . $content . '</tr>' . "\n";
4151    }
4152  } else {
4153    return '';
4154  }
4155}
4156$default_types_conversion{'row'} = \&_convert_row_type;
4157
4158sub _convert_menu_entry_type($$$)
4159{
4160  my $self = shift;
4161  my $type = shift;
4162  my $command = shift;
4163
4164  my $href;
4165  my $rel = '';
4166  my $section;
4167  my $node_entry = $command->{'extra'}->{'menu_entry_node'};
4168  # external node
4169  my $external_node;
4170  if ($node_entry->{'manual_content'}) {
4171    $href = $self->command_href($node_entry, undef, $command);
4172    $external_node = 1;
4173  } else {
4174    my $node = $self->label_command($node_entry->{'normalized'});
4175    # if !NODE_NAME_IN_MENU, we pick the associated section, except if
4176    # the node is the element command
4177    if ($node->{'extra'}->{'associated_section'}
4178      and !$self->get_conf('NODE_NAME_IN_MENU')
4179      and !($self->command_element_command($node) eq $node)) {
4180      $section = $node->{'extra'}->{'associated_section'};
4181      $href = $self->command_href($section, undef, $command);
4182    } else {
4183      $href = $self->command_href($node, undef, $command);
4184    }
4185    if ($node->{'extra'}->{'isindex'}) {
4186      # Mark the target as an index.  See
4187      # http://microformats.org/wiki/existing-rel-values#HTML5_link_type_extensions
4188      $rel = ' rel="index"';
4189    }
4190  }
4191
4192  $html_menu_entry_index++;
4193  my $accesskey = '';
4194  $accesskey = " accesskey=\"$html_menu_entry_index\""
4195    if ($self->get_conf('USE_ACCESSKEY') and $html_menu_entry_index < 10);
4196
4197  my $MENU_SYMBOL = $self->get_conf('MENU_SYMBOL');
4198  my $MENU_ENTRY_COLON = $self->get_conf('MENU_ENTRY_COLON');
4199
4200  if ($self->_in_preformatted_in_menu() or $self->in_string()) {
4201    my $result = '';
4202    my $i = 0;
4203    my @args = @{$command->{'args'}};
4204    while (@args) {
4205      last if ($args[0]->{'type'}
4206               and $args[0]->{'type'} eq 'menu_entry_description');
4207      my $arg = shift @args;
4208      if ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_node') {
4209        my $name = $self->convert_tree(
4210           {'type' => '_code', 'contents' => $arg->{'contents'}});
4211        if ($href ne '' and !$self->in_string()) {
4212          $result .= "<a href=\"$href\"$rel$accesskey>".$name."</a>";
4213        } else {
4214          $result .= $name;
4215        }
4216      } elsif ($arg->{'type'} and $arg->{'type'} eq 'menu_entry_leading_text') {
4217        my $text = $arg->{'text'};
4218
4219        $text =~ s/\*/$MENU_SYMBOL/;
4220        $result .= $text;
4221      } else {
4222        $result .= $self->convert_tree($arg, "menu_arg preformatted [$i]");
4223      }
4224      $i++;
4225    }
4226    my $description = '';
4227    foreach my $arg (@args) {
4228      $description .= $self->convert_tree($arg, "menu_arg preformatted [$i]");
4229      $i++;
4230    }
4231
4232    if (!$self->get_conf('SIMPLE_MENU')) {
4233      $description =~ s/^<pre[^>]*>//;
4234      $description =~ s/<\/pre>$//;
4235    }
4236
4237    $result = $result . $description;
4238
4239    if (!$self->get_conf('SIMPLE_MENU')) {
4240      my $pre_class = $self->_preformatted_class();
4241      $result = $self->_attribute_class('pre', $pre_class).">".$result."</pre>";
4242    }
4243    return $result;
4244  }
4245
4246  my $name;
4247  my $name_no_number;
4248  if ($section) {
4249    $name = $self->command_text($section);
4250    $name_no_number = $self->command_text($section, 'text_nonumber');
4251    if ($href ne '' and $name ne '') {
4252      $name = "<a href=\"$href\"$rel$accesskey>".$name."</a>";
4253    }
4254  }
4255  if (!defined($name) or $name eq '') {
4256    if ($command->{'extra'}->{'menu_entry_name'}) {
4257      $name = $self->convert_tree($command->{'extra'}->{'menu_entry_name'});
4258    }
4259    if (!defined($name) or $name eq '') {
4260      if ($node_entry->{'manual_content'}) {
4261        $name = $self->command_text($node_entry);
4262      } else {
4263        $name = $self->convert_tree({'type' => '_code',
4264                          'contents' => $node_entry->{'node_content'}},
4265                          "menu_arg name");
4266      }
4267    }
4268    $name =~ s/^\s*//;
4269    $name_no_number = $name;
4270    if ($href ne '') {
4271      $name = "<a href=\"$href\"$rel$accesskey>".$name."</a>";
4272    }
4273    $name = "$MENU_SYMBOL ".$name;
4274  }
4275  my $description = '';
4276  if ($command->{'extra'}->{'menu_entry_description'}) {
4277    $description = $self->convert_tree ($command->{'extra'}->{'menu_entry_description'},
4278                                        "menu_arg description");
4279    if ($self->get_conf('AVOID_MENU_REDUNDANCY')) {
4280      $description = '' if (_simplify_text_for_comparison($name_no_number)
4281                           eq _simplify_text_for_comparison($description));
4282    }
4283  }
4284  return "<tr><td align=\"left\" valign=\"top\">$name$MENU_ENTRY_COLON</td><td>&nbsp;&nbsp;</td><td align=\"left\" valign=\"top\">$description</td></tr>\n";
4285}
4286
4287$default_types_conversion{'menu_entry'} = \&_convert_menu_entry_type;
4288
4289sub _convert_menu_comment_type($$$$)
4290{
4291  my $self = shift;
4292  my $type = shift;
4293  my $command = shift;
4294  my $content = shift;
4295
4296  if ($self->_in_preformatted_in_menu() or $self->in_string()) {
4297    return $content;
4298  } else {
4299    return "<tr><th colspan=\"3\" align=\"left\" valign=\"top\">".$content
4300       ."</th></tr>";
4301  }
4302}
4303
4304$default_types_conversion{'menu_comment'} = \&_convert_menu_comment_type;
4305
4306sub _convert_before_item_type($$$$)
4307{
4308  my $self = shift;
4309  my $type = shift;
4310  my $command = shift;
4311  my $content = shift;
4312
4313  return '' if ($content !~ /\S/);
4314  return $content if ($self->in_string());
4315  my $top_format = $self->top_format();
4316  if ($top_format eq 'itemize' or $top_format eq 'enumerate') {
4317    return '<li>'. $content .'</li>';
4318  } elsif ($top_format eq 'table' or $top_format eq 'vtable'
4319           or $top_format eq 'ftable') {
4320    return '<dd>'. $content .'</dd>'."\n";
4321  } elsif ($top_format eq 'multitable') {
4322    $content =~ s/^\s*//;
4323    $content =~ s/\s*$//;
4324
4325    return '<tr><td>'.$content.'</td></tr>'."\n";
4326  }
4327}
4328
4329$default_types_conversion{'before_item'} = \&_convert_before_item_type;
4330
4331sub _convert_def_line_type($$$$)
4332{
4333  my $self = shift;
4334  my $type = shift;
4335  my $command = shift;
4336  my $content = shift;
4337
4338  if ($self->in_string()) {
4339    return $self->protect_text(Texinfo::Convert::Text::convert(
4340       $command, Texinfo::Common::_convert_text_options($self)));
4341  }
4342
4343  my $index_label = '';
4344  my $index_id = $self->command_id($command);
4345  if (defined($index_id) and $index_id ne '' and !@{$self->{'multiple_pass'}}) {
4346    $index_label = " id=\"$index_id\"";
4347  }
4348  my $arguments
4349    = Texinfo::Common::definition_arguments_content($command);
4350
4351  if (!$self->get_conf('DEF_TABLE')) {
4352    my $tree;
4353    my $command_name;
4354    if ($Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}}) {
4355      $command_name = $Texinfo::Common::def_aliases{$command->{'extra'}->{'def_command'}};
4356    } else {
4357      $command_name = $command->{'extra'}->{'def_command'};
4358    }
4359    my $name;
4360    if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) {
4361      $name = $command->{'extra'}->{'def_parsed_hash'}->{'name'};
4362    } else {
4363      $name = '';
4364    }
4365    my $category = $command->{'extra'}->{'def_parsed_hash'}->{'category'};
4366    my $category_result = '';
4367    my $category_tree;
4368    if ($category) {
4369      $category_tree
4370        = {'type' => '_code',
4371           'contents'=>[$self->gdt("{category}: ", {'category' => $category})]
4372          };
4373      # NB perhaps the category shouldn't be in_code.
4374    } else {
4375      $category = '';
4376    }
4377    # no type
4378    if ($command_name eq 'deffn'
4379        or $command_name eq 'defvr'
4380        or $command_name eq 'deftp'
4381        or (($command_name eq 'deftypefn'
4382             or $command_name eq 'deftypevr')
4383            and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})
4384        or (($command_name eq 'defop'
4385             or ($command_name eq 'deftypeop'
4386                 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})
4387             or $command_name eq 'defcv'
4388             or ($command_name eq 'deftypecv'
4389                 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'}))
4390            and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) {
4391      $category_result = $self->convert_tree($category_tree);
4392      if ($arguments) {
4393        $tree = $self->gdt("\@strong{{name}} \@emph{{arguments}}", {
4394                'name' => $name,
4395                'arguments' => $arguments});
4396      } else {
4397        $tree = $self->gdt("\@strong{{name}}", {'name' => $name});
4398      }
4399    # with a type
4400    } elsif ($command_name eq 'deftypefn'
4401             or $command_name eq 'deftypevr'
4402             or (($command_name eq 'deftypeop'
4403                  or $command_name eq 'deftypecv')
4404                 and !$command->{'extra'}->{'def_parsed_hash'}->{'class'})) {
4405      if ($arguments) {
4406        my $strings = {
4407                'name' => $name,
4408                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4409                'arguments' => $arguments};
4410        if ($self->get_conf('deftypefnnewline') eq 'on') {
4411          $category_tree
4412            = {'type' => '_code',
4413               'contents'
4414                  => [$self->gdt("{category}:\@* ", {'category' => $category})]
4415              };
4416          $tree
4417             = $self->gdt("\@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
4418                          $strings);
4419        } else {
4420          $tree
4421             = $self->gdt("\@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
4422                          $strings);
4423        }
4424      } else {
4425        my $strings = {
4426                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4427                'name' => $name};
4428        if ($self->get_conf('deftypefnnewline') eq 'on') {
4429          $category_tree
4430            = {'type' => '_code',
4431               'contents'
4432                  => [$self->gdt("{category}:\@* ", {'category' => $category})]
4433              };
4434        } else {
4435          $tree = $self->gdt("\@emph{{type}} \@strong{{name}}",
4436                  $strings);
4437        }
4438      }
4439      $category_result = $self->convert_tree($category_tree);
4440    # with a class, no type
4441    } elsif ($command_name eq 'defcv'
4442             or ($command_name eq 'deftypecv'
4443                 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) {
4444      if ($arguments) {
4445        $tree = $self->gdt("{category} of {class}: \@strong{{name}} \@emph{{arguments}}", {
4446                'category' => $category,
4447                'name' => $name,
4448                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4449                'arguments' => $arguments});
4450      } else {
4451        $tree = $self->gdt("{category} of {class}: \@strong{{name}}", {
4452                'category' => $category,
4453                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4454                'name' => $name});
4455      }
4456    } elsif ($command_name eq 'defop'
4457             or ($command_name eq 'deftypeop'
4458                 and !$command->{'extra'}->{'def_parsed_hash'}->{'type'})) {
4459      if ($arguments) {
4460        $tree = $self->gdt("{category} on {class}: \@strong{{name}} \@emph{{arguments}}", {
4461                'category' => $category,
4462                'name' => $name,
4463                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4464                'arguments' => $arguments});
4465      } else {
4466        $tree = $self->gdt("{category} on {class}: \@strong{{name}}", {
4467                'category' => $category,
4468                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4469                'name' => $name});
4470      }
4471    # with a class and a type
4472    } elsif ($command_name eq 'deftypeop') {
4473      if ($arguments) {
4474        my $strings = {
4475                'category' => $category,
4476                'name' => $name,
4477                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4478                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4479                'arguments' => $arguments};
4480        if ($self->get_conf('deftypefnnewline') eq 'on') {
4481          $tree
4482            = $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
4483                         $strings);
4484        } else {
4485          $tree
4486            = $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
4487                         $strings);
4488        }
4489      } else {
4490        my $strings = {
4491                'category' => $category,
4492                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4493                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4494                'name' => $name};
4495        if ($self->get_conf('deftypefnnewline') eq 'on') {
4496          $tree
4497            = $self->gdt("{category} on {class}:\@* \@emph{{type}}\@* \@strong{{name}}",
4498                         $strings);
4499        } else {
4500          $tree
4501            = $self->gdt("{category} on {class}: \@emph{{type}} \@strong{{name}}",
4502                         $strings);
4503        }
4504      }
4505    } elsif ($command_name eq 'deftypecv') {
4506      if ($arguments) {
4507        my $strings = {
4508                'category' => $category,
4509                'name' => $name,
4510                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4511                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4512                'arguments' => $arguments};
4513        if ($self->get_conf('deftypefnnewline') eq 'on') {
4514          $tree
4515            = $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}} \@emph{{arguments}}",
4516                         $strings);
4517        } else {
4518          $tree
4519            = $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}} \@emph{{arguments}}",
4520                         $strings);
4521        }
4522      } else {
4523        my $strings = {
4524                'category' => $category,
4525                'type' => $command->{'extra'}->{'def_parsed_hash'}->{'type'},
4526                'class' => $command->{'extra'}->{'def_parsed_hash'}->{'class'},
4527                'name' => $name};
4528        if ($self->get_conf('deftypefnnewline') eq 'on') {
4529          $tree
4530            = $self->gdt("{category} of {class}:\@* \@emph{{type}}\@* \@strong{{name}}",
4531                         $strings);
4532        } else {
4533          $tree
4534            = $self->gdt("{category} of {class}: \@emph{{type}} \@strong{{name}}",
4535                         $strings);
4536        }
4537      }
4538    }
4539
4540    if ($category_result ne '') {
4541      $category_result = $self->_attribute_class('span', 'category')
4542                            .">$category_result</span>";
4543    }
4544    my $anchor = $self->_get_copiable_anchor($index_id);
4545    return "<dt$index_label>".$category_result
4546              ."<span>".$self->convert_tree({'type' => '_code',
4547                           'contents' => [$tree]}) . "$anchor</span></dt>\n";
4548  } else {
4549    my $category_prepared = '';
4550    if ($command->{'extra'} and $command->{'extra'}->{'def_parsed_hash'}
4551        and %{$command->{'extra'}->{'def_parsed_hash'}}) {
4552      my $parsed_definition_category
4553         = Texinfo::Common::definition_category ($self, $command);
4554      if ($parsed_definition_category) {
4555        $category_prepared = $self->convert_tree({'type' => '_code',
4556                   'contents' => [$parsed_definition_category]});
4557      }
4558    }
4559
4560    my $arguments_text = '';
4561    if ($arguments) {
4562      $arguments_text = $self->convert_tree({'type' => '_code',
4563                   'contents' => $arguments});
4564      $arguments_text = '<em> ' . $arguments_text . '</em>'
4565        if ($arguments_text =~ /\S/);
4566    }
4567
4568
4569    my $def_type = '';
4570    my $type_name = '';
4571    if ($command->{'extra'}->{'def_parsed_hash'}->{'type'}) {
4572      $def_type = $self->convert_tree({'type' => '_code',
4573          'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'type'}]});
4574    }
4575    $type_name = " <em>$def_type</em>" if ($def_type ne '');
4576    my $name = '';
4577    if ($command->{'extra'}->{'def_parsed_hash'}->{'name'}) {
4578      $name = $self->convert_tree({'type' => '_code',
4579          'contents' => [$command->{'extra'}->{'def_parsed_hash'}->{'name'}]});
4580    }
4581    $type_name .= ' <strong>' . $name . '</strong>' if ($name ne '');
4582    $type_name .= $arguments_text;
4583
4584    return "<tr$index_label><td align=\"left\">" . $type_name .
4585       "</td><td align=\"right\">" . $category_prepared . "</td></tr>\n";
4586  }
4587}
4588
4589sub _get_copiable_anchor {
4590  my ($self, $id) = @_;
4591  my $result = '';
4592  if ($id and $self->get_conf('COPIABLE_ANCHORS')) {
4593    $result = "<a href='#$id' class='copiable-anchor'> &para;</a>";
4594  }
4595  return $result;
4596}
4597
4598$default_types_conversion{'def_line'} = \&_convert_def_line_type;
4599
4600sub _convert_def_item_type($$$$)
4601{
4602  my $self = shift;
4603  my $type = shift;
4604  my $command = shift;
4605  my $content = shift;
4606
4607  return $content if ($self->in_string());
4608  if ($content =~ /\S/) {
4609    if (! $self->get_conf('DEF_TABLE')) {
4610      return '<dd>' . $content . '</dd>';
4611    } else {
4612      return '<tr><td colspan="2">' . $content . '</td></tr>';
4613    }
4614  }
4615}
4616
4617$default_types_conversion{'def_item'} = \&_convert_def_item_type;
4618$default_types_conversion{'inter_def_item'} = \&_convert_def_item_type;
4619
4620sub _convert_def_command($$$$) {
4621  my $self = shift;
4622  my $cmdname = shift;
4623  my $command = shift;
4624  my $content = shift;
4625
4626  return $content if ($self->in_string());
4627  if (!$self->get_conf('DEF_TABLE')) {
4628    return $self->_attribute_class('dl', 'def').">\n". $content ."</dl>\n";
4629  } else {
4630    return "<table width=\"100%\">\n" . $content . "</table>\n";
4631  }
4632}
4633
4634foreach my $command (keys(%def_commands)) {
4635  $default_commands_conversion{$command} = \&_convert_def_command;
4636}
4637
4638sub _convert_table_item_type($$$$)
4639{
4640  my $self = shift;
4641  my $type = shift;
4642  my $command = shift;
4643  my $content = shift;
4644
4645  return $content if ($self->in_string());
4646  if ($content =~ /\S/) {
4647    return '<dd>' . $content . '</dd>'."\n";
4648  }
4649}
4650
4651$default_types_conversion{'table_item'} = \&_convert_table_item_type;
4652$default_types_conversion{'inter_item'} = \&_convert_table_item_type;
4653
4654# This type is the only one present if there are no elements.  It is
4655# therefore used to do the formatting of the element in case there are no
4656# element.
4657sub _convert_root_text_type($$$$)
4658{
4659  my $self = shift;
4660  my $type = shift;
4661  my $command = shift;
4662  my $content = shift;
4663
4664  my $result = $content;
4665  #$result =~ s/^\s*//;
4666  # if there is no element, the parent should not be an element
4667  if (!$command->{'parent'}
4668      or !$command->{'parent'}->{'type'}
4669      or $command->{'parent'}->{'type'} ne 'element') {
4670    $result .= &{$self->{'format_footnotes_text'}}($self);
4671    $result .= $self->get_conf('DEFAULT_RULE') ."\n",
4672      if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')
4673          and defined($self->get_conf('DEFAULT_RULE'))
4674          and !$self->in_string());
4675  }
4676  return $result;
4677}
4678
4679$default_types_conversion{'text_root'} = \&_convert_root_text_type;
4680
4681sub _contents_shortcontents_in_title($)
4682{
4683  my $self = shift;
4684
4685  my $result = '';
4686
4687  if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
4688      and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1
4689      and $self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_title') {
4690    foreach my $command ('contents', 'shortcontents') {
4691      if ($self->get_conf($command)) {
4692        my $contents_text = $self->_contents_inline_element($command, undef);
4693        if ($contents_text ne '') {
4694          $result .= $contents_text . $self->get_conf('DEFAULT_RULE')."\n";
4695        }
4696      }
4697    }
4698  }
4699  return $result;
4700}
4701
4702# Convert @titlepage.  Falls back to simpletitle.
4703sub _default_format_titlepage($)
4704{
4705  my $self = shift;
4706
4707  my $titlepage_text;
4708  if ($self->{'extra'}->{'titlepage'}) {
4709    $titlepage_text = $self->convert_tree({'contents'
4710               => $self->{'extra'}->{'titlepage'}->{'contents'}});
4711  } elsif ($self->{'simpletitle_tree'}) {
4712    my $title_text = $self->convert_tree_new_formatting_context(
4713                   $self->{'simpletitle_tree'}, 'simpletitle_string');
4714    $titlepage_text = &{$self->{'format_heading_text'}}($self, 'settitle', $title_text,
4715                                            0, {'cmdname' => 'settitle',
4716                     'contents' => $self->{'simpletitle_tree'}->{'contents'}});
4717  }
4718  my $result = '';
4719  $result .= $titlepage_text.$self->get_conf('DEFAULT_RULE')."\n"
4720    if (defined($titlepage_text));
4721  $result .= $self->_contents_shortcontents_in_title();
4722  return $result;
4723}
4724
4725sub _print_title($)
4726{
4727  my $self = shift;
4728
4729  my $result = '';
4730  if ($self->get_conf('SHOW_TITLE')) {
4731    if ($self->get_conf('USE_TITLEPAGE_FOR_TITLE')) {
4732      $result .= &{$self->{'format_titlepage'}}($self);
4733    } else {
4734      if ($self->{'simpletitle_tree'}) {
4735        my $title_text = $self->convert_tree_new_formatting_context(
4736                   $self->{'simpletitle_tree'}, 'simpletitle_string');
4737        $result .= &{$self->{'format_heading_text'}}($self, 'settitle', $title_text,
4738                                            0, {'cmdname' => 'settitle',
4739                     'contents' => $self->{'simpletitle_tree'}->{'contents'}});
4740      }
4741      $result .= $self->_contents_shortcontents_in_title();
4742    }
4743  }
4744  return $result;
4745}
4746
4747# Function for converting the top-level elements in the conversion: a section
4748# or a node.  $ELEMENT was created in this module (in _prepare_elements), with
4749# type 'element' (it's not a tree element created by the parser).  $CONTENT
4750# is the contents of the node/section, already converted.
4751sub _convert_element_type($$$$)
4752{
4753  my $self = shift;
4754  my $type = shift;
4755  my $element = shift;
4756  my $content = shift;
4757
4758  if ($self->in_string()) {
4759    if (defined($content)) {
4760      return $content;
4761    } else {
4762      return '';
4763    }
4764  }
4765
4766  my $result = '';
4767  my $special_element;
4768
4769  if ($element->{'extra'}->{'special_element'}) {
4770    $special_element = $element->{'extra'}->{'special_element'};
4771    $result .= join('', $self->close_registered_sections_level(0));
4772    my $id = $self->command_id($element);
4773    $result .= "<div class=\"${special_element}_element\"";
4774    if ($id ne '') {
4775      $result .= " id=\"$id\"";
4776    }
4777    $result .= ">\n";
4778    if ($self->get_conf('HEADERS')
4779        # first in page
4780        or $self->{'counter_in_file'}->{$element->{'filename'}} == 1) {
4781      $result .= &{$self->{'format_navigation_header'}}($self,
4782                 $self->get_conf('MISC_BUTTONS'), undef, $element);
4783    }
4784    my $heading = $self->command_text($element);
4785    my $element_name = $element->{'extra'}->{'special_element'};
4786    my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{$element_name};
4787    my $level = $self->get_conf('CHAPTER_HEADER_LEVEL');
4788    if ($element_name eq 'Footnotes') {
4789      $level = $self->get_conf('FOOTNOTE_SEPARATE_HEADER_LEVEL');
4790    }
4791    $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
4792                       $heading, $level)."\n";
4793
4794    my $special_element_body .= &{$self->{'format_special_element_body'}}
4795                                    ($self, $special_element, $element);
4796
4797    # This may happen with footnotes in regions that are not expanded,
4798    # like @copying or @titlepage
4799    if ($special_element_body eq '') {
4800      return '';
4801    }
4802    $result .= $special_element_body . '</div>';
4803  } elsif (!$element->{'element_prev'}) {
4804    $result .= $self->_print_title();
4805    if (!$element->{'element_next'}) {
4806      # only one element
4807      $result .= $content;
4808      $result .= &{$self->{'format_footnotes_text'}}($self);
4809      $result .= $self->get_conf('DEFAULT_RULE');
4810      $result .= join('', $self->close_registered_sections_level(0));
4811      return $result;
4812    }
4813  }
4814  $result .= $content unless ($special_element);
4815  $result .= &{$self->{'format_element_footer'}}($self, $type,
4816                                                 $element, $content);
4817  return $result;
4818}
4819
4820sub _default_format_element_footer($$$$)
4821{
4822  my $self = shift;
4823  my $type = shift;
4824  my $element = shift;
4825  my $content = shift;
4826
4827  my $result = '';
4828  my $is_top = $self->element_is_top($element);
4829  my $next_is_top = ($element->{'element_next'}
4830                     and $self->element_is_top($element->{'element_next'}));
4831  my $next_is_special = (defined($element->{'element_next'})
4832    and $element->{'element_next'}->{'extra'}->{'special_element'});
4833
4834  my $end_page = (!$element->{'element_next'}
4835       or (defined($element->{'filename'})
4836           and $element->{'filename'} ne $element->{'element_next'}->{'filename'}
4837           and $self->{'file_counters'}->{$element->{'filename'}} == 1));
4838
4839  my $is_special = $element->{'extra'}->{'special_element'};
4840
4841  if (($end_page or $next_is_top or $next_is_special or $is_top)
4842       and $self->get_conf('VERTICAL_HEAD_NAVIGATION')
4843       and ($self->get_conf('SPLIT') ne 'node'
4844            or $self->get_conf('HEADERS') or $is_special or $is_top)) {
4845   $result .= "</td>
4846</tr>
4847</table>"."\n";
4848  }
4849
4850  my $rule = '';
4851  my $buttons;
4852
4853  if ($end_page) {
4854    $result .= join('', $self->close_registered_sections_level(0));
4855
4856    # setup buttons for navigation footer
4857    if (($is_top or $is_special)
4858        and ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC'))
4859        and (($self->get_conf('HEADERS')
4860                or ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'node')))) {
4861      if ($is_top) {
4862        $buttons = $self->get_conf('TOP_BUTTONS');
4863      } else {
4864        $buttons = $self->get_conf('MISC_BUTTONS');
4865      }
4866    } elsif ($self->get_conf('SPLIT') eq 'section') {
4867      $buttons = $self->get_conf('SECTION_FOOTER_BUTTONS');
4868    } elsif ($self->get_conf('SPLIT') eq 'chapter') {
4869      $buttons = $self->get_conf('CHAPTER_FOOTER_BUTTONS');
4870    } elsif ($self->get_conf('SPLIT') eq 'node') {
4871      if ($self->get_conf('HEADERS')) {
4872        my $no_footer_word_count;
4873        if ($self->get_conf('WORDS_IN_PAGE')) {
4874          my @cnt = split(/\W*\s+\W*/, $content);
4875          if (scalar(@cnt) < $self->get_conf('WORDS_IN_PAGE')) {
4876            $no_footer_word_count = 1;
4877          }
4878        }
4879        $buttons = $self->get_conf('NODE_FOOTER_BUTTONS')
4880           unless ($no_footer_word_count);
4881      }
4882    }
4883  }
4884  # FIXME the following condition is almost a duplication of end_page
4885  # except that the file counter needs not be 1
4886  if ((!$element->{'element_next'}
4887       or (defined($element->{'filename'})
4888           and $element->{'filename'} ne $element->{'element_next'}->{'filename'}))
4889      and $self->get_conf('footnotestyle') eq 'end') {
4890    $result .= &{$self->{'format_footnotes_text'}}($self);
4891  }
4892
4893  if (!$buttons or $is_top or $is_special
4894     or ($end_page and ($self->get_conf('SPLIT') eq 'chapter'
4895                       or $self->get_conf('SPLIT') eq 'section'))
4896     or ($self->get_conf('SPLIT') eq 'node' and $self->get_conf('HEADERS'))) {
4897    $rule = $self->get_conf('DEFAULT_RULE');
4898  }
4899
4900  if (!$end_page and ($is_top or $next_is_top or ($next_is_special
4901                                                 and !$is_special))) {
4902    $rule = $self->get_conf('BIG_RULE');
4903  }
4904
4905  if ($buttons or !$end_page or $self->get_conf('PROGRAM_NAME_IN_FOOTER')) {
4906    $result .= "$rule\n" if ($rule);
4907  }
4908  if ($buttons) {
4909    $result .= &{$self->{'format_navigation_header_panel'}}($self, $buttons,
4910                                                     undef, $element);
4911  }
4912
4913  return $result;
4914}
4915
4916$default_types_conversion{'element'} = \&_convert_element_type;
4917
4918sub _new_document_context($$)
4919{
4920  my $self = shift;
4921  my $cmdname = shift;
4922
4923  push @{$self->{'document_context'}},
4924          {'cmdname' => $cmdname,
4925           'formatting_context' => [{'cmdname' => $cmdname}],
4926           'composition_context' => ['raggedright'],
4927           'formats' => [],
4928           'monospace' => [0],
4929          };
4930}
4931
4932# Functions accessed with e.g. 'format_heading_text'.
4933my %default_formatting_references = (
4934     'format_heading_text' => \&_default_format_heading_text,
4935     'format_comment' => \&_default_format_comment,
4936     'format_protect_text' => \&_default_format_protect_text,
4937     'format_css_lines' => \&_default_format_css_lines,
4938     'format_begin_file' => \&_default_format_begin_file,
4939     'format_node_redirection_page' => \&_default_format_node_redirection_page,
4940     'format_end_file' => \&_default_format_end_file,
4941     'format_special_element_body' => \&_default_format_special_element_body,
4942     'format_footnotes_text' => \&_default_format_footnotes_text,
4943     'format_program_string' => \&_default_format_program_string,
4944     'format_titlepage' => \&_default_format_titlepage,
4945     'format_navigation_header' => \&_default_format_navigation_header,
4946     'format_navigation_header_panel' => \&_default_format_navigation_header_panel,
4947     'format_element_header' => \&_default_format_element_header,
4948     'format_element_footer' => \&_default_format_element_footer,
4949     'format_button' => \&_default_format_button,
4950     'format_button_icon_img' => \&_default_format_button_icon_img,
4951     'format_contents' => \&_default_format_contents,
4952     'format_frame_files' => \&_default_format_frame_files,
4953);
4954
4955sub _use_entity_is_entity($$)
4956{
4957  my $self = shift;
4958  my $text = shift;
4959  return 0 if (!$self->get_conf('ENABLE_ENCODING_USE_ENTITY'));
4960  return 1 if ($text =~ /^&/ and $text =~ /;$/);
4961}
4962
4963sub _complete_commands_formatting($$)
4964{
4965  my $self = shift;
4966  my $command = shift;
4967  if (!defined ($self->{'commands_formatting'}->{'normal'}->{$command})) {
4968    $self->{'commands_formatting'}->{'normal'}->{$command} = '';
4969  }
4970  if (!defined ($self->{'commands_formatting'}->{'preformatted'}->{$command})) {
4971    $self->{'commands_formatting'}->{'preformatted'}->{$command} =
4972      $self->{'commands_formatting'}->{'normal'}->{$command};
4973  }
4974  if (!defined ($self->{'commands_formatting'}->{'string'}->{$command})) {
4975   $self->{'commands_formatting'}->{'string'}->{$command} =
4976      $self->{'commands_formatting'}->{'preformatted'}->{$command};
4977  }
4978}
4979
4980my %htmlxref_entries = (
4981 'node' => [ 'node', 'section', 'chapter', 'mono' ],
4982 'section' => [ 'section', 'chapter','node', 'mono' ],
4983 'chapter' => [ 'chapter', 'section', 'node', 'mono' ],
4984 'mono' => [ 'mono', 'chapter', 'section', 'node' ],
4985);
4986
4987sub _parse_htmlxref_files($$)
4988{
4989  my $self = shift;
4990  my $files = shift;
4991  my $htmlxref;
4992
4993  foreach my $file (@$files) {
4994    my ($fname) = $file;
4995    if ($self->get_conf('TEST')) {
4996      $fname =~ s/([^\/]+\/)*//; # strip directories for out-of-source builds
4997    }
4998    print STDERR "html refs config file: $file\n" if ($self->get_conf('DEBUG'));
4999    unless (open (HTMLXREF, $file)) {
5000      $self->document_warn(
5001        sprintf(__("could not open html refs config file %s: %s"),
5002          $file, $!));
5003      next;
5004    }
5005    my $line_nr = 0;
5006    my %variables;
5007    while (my $hline = <HTMLXREF>) {
5008      my $line = $hline;
5009      $line_nr++;
5010      next if $hline =~ /^\s*#/;
5011      #$hline =~ s/[#]\s.*//;
5012      $hline =~ s/^\s*//;
5013      next if $hline =~ /^\s*$/;
5014      chomp ($hline);
5015      if ($hline =~ s/^\s*(\w+)\s*=\s*//) {
5016        # handle variables
5017        my $var = $1;
5018        my $re = join '|', map { quotemeta $_ } keys %variables;
5019        $hline =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
5020                                                       : "\${$1}"/ge;
5021        $variables{$var} = $hline;
5022        next;
5023      }
5024      my @htmlxref = split /\s+/, $hline;
5025      my $manual = shift @htmlxref;
5026      my $split_or_mono = shift @htmlxref;
5027      #print STDERR "$split_or_mono $Texi2HTML::Config::htmlxref_entries{$split_or_mono} $line_nr\n";
5028      if (!defined($split_or_mono)) {
5029        $self->file_line_warn(__("missing type"), $fname, $line_nr);
5030        next;
5031      } elsif (!defined($htmlxref_entries{$split_or_mono})) {
5032        $self->file_line_warn(sprintf(__("unrecognized type: %s"),
5033                               $split_or_mono), $fname, $line_nr);
5034        next;
5035      }
5036      my $href = shift @htmlxref;
5037      next if (exists($htmlxref->{$manual}->{$split_or_mono}));
5038
5039      if (defined($href)) { # substitute 'variables'
5040        my $re = join '|', map { quotemeta $_ } keys %variables;
5041        $href =~ s/\$\{($re)\}/defined $variables{$1} ? $variables{$1}
5042                                                      : "\${$1}"/ge;
5043        $href =~ s/\/*$// if ($split_or_mono ne 'mono');
5044      }
5045      $htmlxref->{$manual}->{$split_or_mono} = $href;
5046    }
5047    if (!close (HTMLXREF)) {
5048      $self->document_warn(sprintf(__(
5049                       "error on closing html refs config file %s: %s"),
5050                             $file, $!));
5051    }
5052  }
5053  return $htmlxref;
5054}
5055
5056sub _load_htmlxref_files {
5057  my ($self) = @_;
5058
5059  my @htmlxref_dirs = ();
5060  if ($self->get_conf('TEST')) {
5061    my $curdir = File::Spec->curdir();
5062    # to have reproducible tests, do not use system or user
5063    # directories if TEST is set.
5064    @htmlxref_dirs = File::Spec->catdir($curdir, '.texinfo');
5065
5066    my $input_directory = $self->{'info'}->{'input_directory'};
5067    if (defined($input_directory)
5068        and $input_directory ne '.' and $input_directory ne '') {
5069      unshift @htmlxref_dirs, $input_directory;
5070    }
5071  } elsif ($self->{'language_config_dirs'}
5072            and @{$self->{'language_config_dirs'}}) {
5073    @htmlxref_dirs = @{$self->{'language_config_dirs'}};
5074  }
5075  unshift @htmlxref_dirs, '.';
5076
5077  my @texinfo_htmlxref_files;
5078  my $init_file_from_conf = $self->get_conf('HTMLXREF');
5079  if ($init_file_from_conf) {
5080    if (!$self->get_conf('TEST')) {
5081      @texinfo_htmlxref_files = ( $init_file_from_conf );
5082    } else {
5083      @texinfo_htmlxref_files
5084      = Texinfo::Common::locate_init_file ($init_file_from_conf,
5085        \@htmlxref_dirs, 1);
5086    }
5087  } elsif (!$self->get_conf('TEST')) {
5088    @texinfo_htmlxref_files
5089      = Texinfo::Common::locate_init_file ('htmlxref.cnf',
5090                                           \@htmlxref_dirs, 1);
5091  }
5092  $self->{'htmlxref_files'} = \@texinfo_htmlxref_files;
5093
5094  $self->{'htmlxref'} = {};
5095  if ($self->{'htmlxref_files'}) {
5096    $self->{'htmlxref'} = _parse_htmlxref_files($self,
5097                                                $self->{'htmlxref_files'});
5098  }
5099}
5100
5101
5102sub converter_initialize($)
5103{
5104  my $self = shift;
5105
5106  $foot_num = 0;
5107  $foot_lines = '';
5108  %formatted_index_entries = ();
5109  %footnote_id_numbers = ();
5110
5111  %{$self->{'css_map'}} = %css_map;
5112
5113  _load_htmlxref_files($self);
5114
5115  foreach my $type (keys(%default_types_conversion)) {
5116    if (exists($Texinfo::Config::texinfo_types_conversion{$type})) {
5117      $self->{'types_conversion'}->{$type}
5118          = $Texinfo::Config::texinfo_types_conversion{$type};
5119    } else {
5120      $self->{'types_conversion'}->{$type}
5121          = $default_types_conversion{$type};
5122    }
5123  }
5124  # FIXME API with a function call?  Used in cvs.init.
5125  foreach my $type (keys(%default_code_types)) {
5126    $self->{'code_types'}->{$type} = $default_code_types{$type};
5127  }
5128  if ($Texinfo::Config::texinfo_code_types) {
5129    foreach my $type (keys(%$Texinfo::Config::texinfo_code_types)) {
5130      $self->{'code_types'}->{$type}
5131        = $Texinfo::Config::texinfo_code_types->{$type};
5132    }
5133  }
5134
5135  # FIXME put value in a category in Texinfo::Common?
5136  foreach my $command (keys(%misc_commands), keys(%brace_commands),
5137     keys (%block_commands), keys(%no_brace_commands), 'value') {
5138    if (exists($Texinfo::Config::texinfo_commands_conversion{$command})) {
5139      $self->{'commands_conversion'}->{$command}
5140          = $Texinfo::Config::texinfo_commands_conversion{$command};
5141    } else {
5142      if ($self->get_conf('FORMAT_MENU') ne 'menu'
5143           and ($command eq 'menu' or $command eq 'detailmenu')) {
5144        $self->{'commands_conversion'}->{$command} = undef;
5145      } elsif ($format_raw_commands{$command}
5146               and !$self->{'expanded_formats_hash'}->{$command}) {
5147      } elsif (exists($default_commands_conversion{$command})) {
5148        $self->{'commands_conversion'}->{$command}
5149           = $default_commands_conversion{$command};
5150        if ($command eq 'menu' and $self->get_conf('SIMPLE_MENU')) {
5151          $self->{'commands_conversion'}->{$command}
5152            = $default_commands_conversion{'example'};
5153        }
5154      }
5155    }
5156  }
5157
5158  foreach my $context ('normal', 'preformatted', 'string') {
5159    foreach my $command (keys(%{$default_commands_formatting{'normal'}})) {
5160      if (exists ($Texinfo::Config::commands_formatting{$context}->{$command})) {
5161        $self->{'commands_formatting'}->{$context}->{$command}
5162           = $Texinfo::Config::commands_formatting{$context}->{$command};
5163      } else {
5164        if (defined($default_commands_formatting{$context}->{$command})) {
5165          if ($self->get_conf('ENABLE_ENCODING')
5166              and Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command(
5167                             $command, $self->get_conf('OUTPUT_ENCODING_NAME'))
5168              and !$self->_use_entity_is_entity($default_commands_formatting{$context}->{$command})) {
5169            $self->{'commands_formatting'}->{$context}->{$command}
5170              = Texinfo::Convert::Unicode::unicode_for_brace_no_arg_command(
5171                           $command, $self->get_conf('OUTPUT_ENCODING_NAME'));
5172          } else {
5173            $self->{'commands_formatting'}->{$context}->{$command}
5174              = $default_commands_formatting{$context}->{$command};
5175          }
5176        }
5177      }
5178      if (exists ($Texinfo::Config::commands_translation{$context}->{$command})) {
5179        $self->{'commands_translation'}->{$context}->{$command}
5180           = $Texinfo::Config::commands_translation{$context}->{$command};
5181        delete $self->{'translated_commands'}->{$command};
5182      } elsif (defined($default_commands_translation{$context}->{$command})) {
5183        $self->{'commands_translation'}->{$context}->{$command}
5184          = $default_commands_translation{$context}->{$command};
5185        delete $self->{'translated_commands'}->{$command};
5186      }
5187    }
5188  }
5189
5190  # set sane defaults in case there is none and the default formatting
5191  # function is used
5192  foreach my $command (keys(%{$default_commands_formatting{'normal'}})) {
5193    if ($self->{'commands_conversion'}->{$command}
5194        and $self->{'commands_conversion'}->{$command}
5195            eq $default_commands_conversion{$command}) {
5196      $self->_complete_commands_formatting($command);
5197    }
5198  }
5199
5200  foreach my $context (keys(%style_commands_formatting)) {
5201    foreach my $command (keys(%{$style_commands_formatting{$context}})) {
5202      if (exists ($Texinfo::Config::style_commands_formatting{$context}->{$command})) {
5203        $self->{'style_commands_formatting'}->{$context}->{$command}
5204           = $Texinfo::Config::style_commands_formatting{$context}->{$command};
5205      } elsif (exists($style_commands_formatting{$context}->{$command})) {
5206        $self->{'style_commands_formatting'}->{$context}->{$command}
5207           = $style_commands_formatting{$context}->{$command};
5208      }
5209    }
5210  }
5211
5212  foreach my $command (keys %{$self->{'commands_conversion'}}) {
5213    if (exists($Texinfo::Config::commands_args{$command})) {
5214      $self->{'commands_args'}->{$command}
5215         = $Texinfo::Config::commands_args{$command};
5216    } elsif (exists($default_commands_args{$command})) {
5217      $self->{'commands_args'}->{$command} = $default_commands_args{$command};
5218    }
5219  }
5220
5221  foreach my $formatting_reference (keys(%default_formatting_references)) {
5222    $self->{'default_formatting_functions'}->{$formatting_reference}
5223       = $default_formatting_references{$formatting_reference};
5224    if (defined($Texinfo::Config::texinfo_formatting_references{$formatting_reference})) {
5225      $self->{$formatting_reference}
5226       =  $Texinfo::Config::texinfo_formatting_references{$formatting_reference};
5227    } else {
5228      $self->{$formatting_reference}
5229       = $default_formatting_references{$formatting_reference};
5230    }
5231  }
5232
5233  $self->{'document_context'} = [];
5234  $self->{'multiple_pass'} = [];
5235  $self->{'pending_closes'} = [];
5236  $self->_new_document_context('_toplevel_context');
5237
5238  if ($self->get_conf('SPLIT') and $self->get_conf('SPLIT') ne 'chapter'
5239      and $self->get_conf('SPLIT') ne 'section'
5240      and $self->get_conf('SPLIT') ne 'node') {
5241    $self->force_conf('SPLIT', 'node');
5242  }
5243
5244  return $self;
5245}
5246
5247# the entry point for _convert
5248sub convert_tree($$;$)
5249{
5250  my $self = shift;
5251  my $element = shift;
5252  my $explanation = shift;
5253
5254  return $self->_convert($element, $explanation);
5255}
5256
5257sub _normalized_to_id($)
5258{
5259  my $id = shift;
5260  if (!defined($id)) {
5261    cluck "_normalized_to_id id not defined";
5262    return '';
5263  }
5264  $id =~ s/^([0-9_])/g_t$1/;
5265  return $id;
5266}
5267
5268sub _default_format_css_lines($)
5269{
5270  my $self = shift;
5271
5272  return if ($self->get_conf('NO_CSS'));
5273
5274  my $css_refs = $self->get_conf('CSS_REFS');
5275
5276  return if (!@{$self->{'css_import_lines'}} and !@{$self->{'css_rule_lines'}}
5277             and !keys(%{$self->{'css_map'}}) and !@$css_refs);
5278
5279  my $css_text = "<style type=\"text/css\">\n<!--\n";
5280  $css_text .= join('',@{$self->{'css_import_lines'}}) . "\n"
5281    if (@{$self->{'css_import_lines'}});
5282  foreach my $css_rule (sort(keys(%{$self->{'css_map'}}))) {
5283    next unless ($self->{'css_map'}->{$css_rule});
5284    $css_text .= "$css_rule {$self->{'css_map'}->{$css_rule}}\n";
5285  }
5286  $css_text .= join('',@{$self->{'css_rule_lines'}}) . "\n"
5287    if (@{$self->{'css_rule_lines'}});
5288  $css_text .= "-->\n</style>\n";
5289  foreach my $ref (@$css_refs) {
5290    $css_text .= "<link rel=\"stylesheet\" type=\"text/css\" href=\"$ref\">\n";
5291  }
5292  $self->set_conf('CSS_LINES', $css_text);
5293}
5294
5295sub _process_css_file($$$)
5296{
5297  my $self = shift;
5298  my $fh =shift;
5299  my $file = shift;
5300  my $in_rules = 0;
5301  my $in_comment = 0;
5302  my $in_import = 0;
5303  my $in_string = 0;
5304  my $rules = [];
5305  my $imports = [];
5306  my $line_nr = 0;
5307  while (my $line = <$fh>) {
5308    $line_nr++;
5309    #print STDERR "Line: $line";
5310    if ($in_rules) {
5311      push @$rules, $line;
5312      next;
5313    }
5314    my $text = '';
5315    while (1) {
5316      #sleep 1;
5317      #print STDERR "${text}!in_comment $in_comment in_rules $in_rules in_import $in_import in_string $in_string: $line";
5318      if ($in_comment) {
5319        if ($line =~ s/^(.*?\*\/)//) {
5320          $text .= $1;
5321          $in_comment = 0;
5322        } else {
5323          push @$imports, $text . $line;
5324          last;
5325        }
5326      } elsif (!$in_string and $line =~ s/^\///) {
5327        if ($line =~ s/^\*//) {
5328          $text .= '/*';
5329          $in_comment = 1;
5330        } else {
5331          push (@$imports, $text. "\n") if ($text ne '');
5332          push (@$rules, '/' . $line);
5333          $in_rules = 1;
5334          last;
5335        }
5336      } elsif (!$in_string and $in_import and $line =~ s/^([\"\'])//) {
5337        # strings outside of import start rules
5338        $text .= "$1";
5339        $in_string = quotemeta("$1");
5340      } elsif ($in_string and $line =~ s/^(\\$in_string)//) {
5341        $text .= $1;
5342      } elsif ($in_string and $line =~ s/^($in_string)//) {
5343        $text .= $1;
5344        $in_string = 0;
5345      } elsif ((! $in_string and !$in_import)
5346              and ($line =~ s/^([\\]?\@import)$//
5347                   or $line =~ s/^([\\]?\@import\s+)//)) {
5348        $text .= $1;
5349        $in_import = 1;
5350      } elsif (!$in_string and $in_import and $line =~ s/^\;//) {
5351        $text .= ';';
5352        $in_import = 0;
5353      } elsif (($in_import or $in_string) and $line =~ s/^(.)//) {
5354        $text .= $1;
5355      } elsif (!$in_import and $line =~ s/^([^\s])//) {
5356        push (@$imports, $text. "\n") if ($text ne '');
5357        push (@$rules, $1 . $line);
5358        $in_rules = 1;
5359        last;
5360      } elsif ($line =~ s/^(\s)//) {
5361        $text .= $1;
5362      } elsif ($line eq '') {
5363        push (@$imports, $text);
5364        last;
5365      }
5366    }
5367  }
5368  #file_line_warn (__("string not closed in css file"), $file) if ($in_string);
5369  #file_line_warn (__("--css-file ended in comment"), $file) if ($in_comment);
5370  #file_line_warn (__("\@import not finished in css file"), $file)  if ($in_import and !$in_comment and !$in_string);
5371  $self->file_line_warn(sprintf(__("string not closed in css file"),
5372                        $file, $line_nr)) if ($in_string);
5373  $self->file_line_warn(sprintf(__("--css-include ended in comment"),
5374                        $file, $line_nr)) if ($in_comment);
5375  $self->file_line_warn(sprintf(__("\@import not finished in css file"),
5376                        $file, $line_nr))
5377    if ($in_import and !$in_comment and !$in_string);
5378  return ($imports, $rules);
5379}
5380
5381sub _prepare_css($)
5382{
5383  my $self = shift;
5384
5385  return if ($self->get_conf('NO_CSS'));
5386
5387  my @css_import_lines;
5388  my @css_rule_lines;
5389
5390  my $css_files = $self->get_conf('CSS_FILES');
5391  foreach my $file (@$css_files) {
5392    my $css_file_fh;
5393    my $css_file;
5394    if ($file eq '-') {
5395      $css_file_fh = \*STDIN;
5396      $css_file = '-';
5397    } else {
5398      $css_file = $self->Texinfo::Common::locate_include_file($file);
5399      unless (defined($css_file)) {
5400        $self->document_warn(sprintf(
5401               __("CSS file %s not found"), $file));
5402        next;
5403      }
5404      # FIXME use open_out?
5405      unless (open (CSSFILE, $css_file)) {
5406        $self->document_warn(sprintf(__(
5407             "could not open --include-file %s: %s"),
5408              $css_file, $!));
5409        next;
5410      }
5411      $css_file_fh = \*CSSFILE;
5412    }
5413    my ($import_lines, $rules_lines);
5414    ($import_lines, $rules_lines)
5415      = $self->_process_css_file ($css_file_fh, $css_file);
5416    if (!close($css_file_fh)) {
5417      $self->document_warn(sprintf(__("error on closing CSS file %s: %s"),
5418                                   $css_file, $!));
5419    }
5420    push @css_import_lines, @$import_lines;
5421    push @css_rule_lines, @$rules_lines;
5422
5423  }
5424  if ($self->get_conf('DEBUG')) {
5425    if (@css_import_lines) {
5426      print STDERR "# css import lines\n";
5427      foreach my $line (@css_import_lines) {
5428        print STDERR "$line";
5429      }
5430    }
5431    if (@css_rule_lines) {
5432      print STDERR "# css rule lines\n";
5433      foreach my $line (@css_rule_lines) {
5434        print STDERR "$line";
5435      }
5436    }
5437  }
5438  $self->{'css_import_lines'} = \@css_import_lines;
5439  $self->{'css_rule_lines'} = \@css_rule_lines;
5440}
5441
5442# Get the name of a file containing a node, as well as the anchor within
5443# that file to link to that node.  Argument is the 'extra' value on
5444# an element hash, or something that looks like it.
5445sub _node_id_file($$)
5446{
5447  my $self = shift;
5448  my $node_info = shift;
5449
5450  my $target;
5451  my $normalized;
5452  if ($node_info->{'normalized'}) {
5453    $normalized = $node_info->{'normalized'};
5454  } elsif ($node_info->{'node_content'}) {
5455    $normalized = Texinfo::Convert::NodeNameNormalization::normalize_node (
5456      { 'contents' => $node_info->{'node_content'} });
5457  }
5458
5459  if (defined($normalized)) {
5460    $target = _normalized_to_id($normalized);
5461  } else {
5462    $target = '';
5463  }
5464  # to find out the Top node, one could check $node_info->{'normalized'}
5465  if (defined($Texinfo::Config::node_target_name)) {
5466    $target = &$Texinfo::Config::node_target_name($node_info, $target);
5467  }
5468
5469  my $filename = $self->_node_filename($node_info);
5470
5471  return ($filename, $target);
5472}
5473
5474sub _new_sectioning_command_target($$)
5475{
5476  my $self = shift;
5477  my $command = shift;
5478
5479  my ($normalized_name, $filename)
5480    = $self->_sectioning_command_normalized_filename($command);
5481
5482  my $target_base = _normalized_to_id($normalized_name);
5483  if ($target_base !~ /\S/ and $command->{'cmdname'} eq 'top'
5484      and defined($self->{'misc_elements_targets'}->{'Top'})) {
5485    $target_base = $self->{'misc_elements_targets'}->{'Top'};
5486  }
5487  my $nr=1;
5488  my $target = $target_base;
5489  if ($target ne '') {
5490    while ($self->{'seen_ids'}->{$target}) {
5491      $target = $target_base.'-'.$nr;
5492      $nr++;
5493      # Avoid integer overflow
5494      die if ($nr == 0);
5495    }
5496  }
5497
5498  # These are undefined if the $target is set to ''.
5499  my $target_contents;
5500  my $target_shortcontents;
5501  if ($Texinfo::Common::sectioning_commands{$command->{'cmdname'}}) {
5502    if ($target ne '') {
5503      my $target_base_contents = $target;
5504      $target_base_contents =~ s/^g_t//;
5505      $target_contents = 'toc-'.$target_base_contents;
5506      my $toc_nr = $nr -1;
5507      while ($self->{'seen_ids'}->{$target_contents}) {
5508        $target_contents = 'toc-'.$target_base_contents.'-'.$toc_nr;
5509        $toc_nr++;
5510        # Avoid integer overflow
5511        die if ($toc_nr == 0);
5512      }
5513
5514      $target_shortcontents = 'stoc-'.$target_base_contents;
5515      my $target_base_shortcontents = $target_base;
5516      $target_base_shortcontents =~ s/^g_t//;
5517      my $stoc_nr = $nr -1;
5518      while ($self->{'seen_ids'}->{$target_shortcontents}) {
5519        $target_shortcontents = 'stoc-'.$target_base_shortcontents
5520                                   .'-'.$stoc_nr;
5521        $stoc_nr++;
5522        # Avoid integer overflow
5523        die if ($stoc_nr == 0);
5524      }
5525    }
5526  }
5527
5528  if (defined($Texinfo::Config::sectioning_command_target_name)) {
5529    ($target, $target_contents,
5530     $target_shortcontents, $filename)
5531        = &$Texinfo::Config::sectioning_command_target_name($self,
5532                                     $command, $target,
5533                                     $target_contents,
5534                                     $target_shortcontents,
5535                                     $filename);
5536  }
5537  if ($self->get_conf('DEBUG')) {
5538    print STDERR "Register $command->{'cmdname'} $target\n";
5539  }
5540  $self->{'targets'}->{$command} = {
5541                           'target' => $target,
5542                           'section_filename' => $filename,
5543                          };
5544  $self->{'seen_ids'}->{$target} = 1;
5545  if (defined($target_contents)) {
5546    $self->{'targets'}->{$command}->{'contents_target'} = $target_contents;
5547  } else {
5548    $self->{'targets'}->{$command}->{'contents_target'} = '';
5549  }
5550  if (defined($target_shortcontents)) {
5551    $self->{'targets'}->{$command}->{'shortcontents_target'}
5552       = $target_shortcontents;
5553  } else {
5554    $self->{'targets'}->{$command}->{'shortcontents_target'} = '';
5555  }
5556  return $self->{'targets'}->{$command};
5557}
5558
5559# This set 2 unrelated things.
5560#  * The targets and id of sectioning elements
5561#  * the target, id and normalized filename of 'labels', ie everything that
5562#    may be the target of a ref, like @node, @float, @anchor...
5563# conversion to HTML is done on-demand, upon call to command_text.
5564# Note that 'node_filename', which is set here for Top too, is not
5565# used later for Top, see the NOTE below.
5566sub _set_root_commands_targets_node_files($$)
5567{
5568  my $self = shift;
5569  my $elements = shift;
5570
5571  my $no_unidecode;
5572  $no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE'))
5573                        and !$self->get_conf('USE_UNIDECODE'));
5574
5575  my $extension = '';
5576  $extension = '.'.$self->get_conf('EXTENSION')
5577            if (defined($self->get_conf('EXTENSION'))
5578                and $self->get_conf('EXTENSION') ne '');
5579  if ($self->{'labels'}) {
5580    foreach my $root_command (values(%{$self->{'labels'}})) {
5581      my ($filename, $target) = $self->_node_id_file($root_command->{'extra'});
5582      $filename .= $extension;
5583      if (defined($Texinfo::Config::node_file_name)) {
5584        $filename = &$Texinfo::Config::node_file_name($self, $root_command,
5585                                                     $filename);
5586      }
5587      if ($self->get_conf('DEBUG')) {
5588        print STDERR "Register label($root_command) $target, $filename\n";
5589      }
5590      $self->{'targets'}->{$root_command} = {'target' => $target,
5591                                             'node_filename' => $filename};
5592      $self->{'seen_ids'}->{$target} = 1;
5593    }
5594  }
5595
5596  if ($elements) {
5597    foreach my $element (@$elements) {
5598      foreach my $root_command(@{$element->{'contents'}}) {
5599        # this happens for type 'text_root' which precedes the
5600        # root commands.  The target may also already be set for top node.
5601        next if (!defined($root_command->{'cmdname'})
5602                 or $self->{'targets'}->{$root_command});
5603        if ($Texinfo::Common::sectioning_commands{$root_command->{'cmdname'}}) {
5604          $self->_new_sectioning_command_target($root_command);
5605        }
5606      }
5607    }
5608  }
5609}
5610
5611sub _get_element($$;$);
5612
5613# If $find_container is set, the element that holds the command is found,
5614# otherwise the element that holds the command content is found.  This is
5615# mostly relevant for footnote only.
5616sub _get_element($$;$)
5617{
5618  my $self = shift;
5619  my $command = shift;
5620  my $find_container = shift;
5621
5622  my $current = $command;
5623
5624  my ($element, $root_command);
5625  while (1) {
5626    if ($current->{'type'}) {
5627      if ($current->{'type'} eq 'element') {
5628        return ($current, $root_command);
5629      }
5630    }
5631    if ($current->{'cmdname'}) {
5632      if ($root_commands{$current->{'cmdname'}}) {
5633        $root_command = $current;
5634        return ($element, $root_command) if defined($element);
5635      } elsif ($region_commands{$current->{'cmdname'}}) {
5636        if ($current->{'cmdname'} eq 'copying'
5637            and $self->{'extra'} and $self->{'extra'}->{'insertcopying'}) {
5638          foreach my $insertcopying(@{$self->{'extra'}->{'insertcopying'}}) {
5639            my ($element, $root_command)
5640              = $self->_get_element($insertcopying, $find_container);
5641            return ($element, $root_command)
5642              if (defined($element) or defined($root_command));
5643          }
5644        } elsif ($current->{'cmdname'} eq 'titlepage'
5645                 and $self->get_conf('USE_TITLEPAGE_FOR_TITLE')
5646                 and $self->get_conf('SHOW_TITLE')
5647                 and $self->{'elements'}->[0]) {
5648          return ($self->{'elements'}->[0],
5649                  $self->{'elements'}->[0]->{'extra'}->{'element_command'});
5650        }
5651        die "Problem $element, $root_command" if (defined($element)
5652                                                  or defined($root_command));
5653        return (undef, undef);
5654      } elsif ($current->{'cmdname'} eq 'footnote'
5655           and $self->{'special_elements_types'}->{'Footnotes'}
5656           and $find_container) {
5657           # in that case there is no root_command
5658          $element = $self->{'special_elements_types'}->{'Footnotes'};
5659          return ($element);
5660      }
5661    }
5662    if ($current->{'parent'}) {
5663      $current = $current->{'parent'};
5664    } else {
5665      return ($element, $root_command);
5666    }
5667  }
5668}
5669
5670sub _set_pages_files($$)
5671{
5672  my $self = shift;
5673  my $elements = shift;
5674  my $special_elements = shift;
5675
5676  # Ensure that the document has pages
5677  return undef if (!defined($elements) or !@$elements);
5678
5679  my $extension = '';
5680  $extension = '.'.$self->get_conf('EXTENSION')
5681            if (defined($self->get_conf('EXTENSION'))
5682                and $self->get_conf('EXTENSION') ne '');
5683
5684  if (!$self->get_conf('SPLIT')) {
5685    foreach my $element (@$elements) {
5686      if (!defined($element->{'filename'})) {
5687        $element->{'filename'} = $self->{'output_filename'};
5688        $element->{'out_filename'} = $self->{'output_file'};
5689      }
5690    }
5691  } else {
5692    my $node_top;
5693    #my $section_top;
5694    $node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'});
5695    #$section_top = $self->{'extra'}->{'top'} if ($self->{'extra'});
5696
5697    my $top_node_filename = $self->_top_node_filename();
5698    # first determine the top node file name.
5699    if ($node_top and defined($top_node_filename)) {
5700      my ($node_top_element) = $self->_get_element($node_top);
5701      die "BUG: No element for top node" if (!defined($node_top));
5702      $self->_set_element_file($node_top_element, $top_node_filename);
5703    }
5704    my $file_nr = 0;
5705    my $previous_page;
5706    foreach my $element(@$elements) {
5707      # For Top node.
5708      next if (defined($element->{'filename'}));
5709      if (!$element->{'extra'}->{'first_in_page'}) {
5710        cluck ("No first_in_page for $element\n");
5711      }
5712      if (!defined($element->{'extra'}->{'first_in_page'}->{'filename'})) {
5713        my $file_element = $element->{'extra'}->{'first_in_page'};
5714        foreach my $root_command (@{$file_element->{'contents'}}) {
5715          if ($root_command->{'cmdname'}
5716              and $root_command->{'cmdname'} eq 'node') {
5717            my $node_filename;
5718            # double node are not normalized, they are handled here
5719            if (!defined($root_command->{'extra'}->{'normalized'})
5720                or !defined($self->{'labels'}->{$root_command->{'extra'}->{'normalized'}})) {
5721              $node_filename = 'unknown_node';
5722              $node_filename .= $extension;
5723            } else {
5724              if (!defined($self->{'targets'}->{$root_command})
5725                  or !defined($self->{'targets'}->{$root_command}->{'node_filename'})) {
5726                # Could have been a double node, thus use equivalent node.
5727                # However since double nodes are not normalized, in fact it
5728                # never happens.
5729                $root_command
5730                  = $self->{'labels'}->{$root_command->{'extra'}->{'normalized'}};
5731              }
5732              $node_filename
5733                = $self->{'targets'}->{$root_command}->{'node_filename'};
5734            }
5735            $self->_set_element_file($file_element, $node_filename);
5736            last;
5737          }
5738        }
5739        if (!defined($file_element->{'filename'})) {
5740          # use section to do the file name if there is no node
5741          my $command = $self->element_command($file_element);
5742          if ($command) {
5743            if ($command->{'cmdname'} eq 'top' and !$node_top
5744                and defined($top_node_filename)) {
5745              $self->_set_element_file($file_element, $top_node_filename);
5746            } else {
5747              $self->_set_element_file($file_element,
5748                 $self->{'targets'}->{$command}->{'section_filename'});
5749            }
5750          } else {
5751            # when everything else has failed
5752            if ($file_nr == 0 and !$node_top
5753                and defined($top_node_filename)) {
5754              $self->_set_element_file($file_element, $top_node_filename);
5755            } else {
5756              my $filename = $self->{'document_name'} . "_$file_nr";
5757              $filename .= $extension;
5758              $self->_set_element_file($element, $filename);
5759            }
5760            $file_nr++;
5761          }
5762        }
5763      }
5764      $element->{'filename'}
5765         = $element->{'extra'}->{'first_in_page'}->{'filename'};
5766      $element->{'out_filename'}
5767         = $element->{'extra'}->{'first_in_page'}->{'out_filename'};
5768    }
5769  }
5770
5771  foreach my $element (@$elements) {
5772    if (defined($Texinfo::Config::element_file_name)) {
5773      # NOTE the information that it is associated with @top or @node Top
5774      # may be determined with $self->element_is_top($element);
5775      my $filename = &$Texinfo::Config::element_file_name($self, $element,
5776                                                          $element->{'filename'});
5777      $self->_set_element_file($element, $filename) if (defined($filename));
5778    }
5779    $self->{'file_counters'}->{$element->{'filename'}}++;
5780    print STDERR "Page $element ".Texinfo::Structuring::_print_element_command_texi($element).": $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n"
5781      if ($self->get_conf('DEBUG'));
5782  }
5783  if ($special_elements) {
5784    my $previous_element = $elements->[-1];
5785    foreach my $element (@$special_elements) {
5786      my $filename
5787       = $self->{'targets'}->{$element}->{'misc_filename'};
5788      if (defined($filename)) {
5789        $self->_set_element_file($element, $filename);
5790        $self->{'file_counters'}->{$element->{'filename'}}++;
5791        print STDERR "Special page $element: $element->{'filename'}($self->{'file_counters'}->{$element->{'filename'}})\n"
5792          if ($self->get_conf('DEBUG'));
5793      }
5794      $element->{'element_prev'} = $previous_element;
5795      $previous_element->{'element_next'} = $element;
5796      $previous_element = $element;
5797    }
5798  }
5799}
5800
5801# $ROOT is a parsed Texinfo tree.  Return a list of the "elements" we need to
5802# output in the HTML file(s).  Each "element" is what can go in one HTML file,
5803# such as the content between @node lines in the Texinfo source.
5804sub _prepare_elements($$)
5805{
5806  my $self = shift;
5807  my $root = shift;
5808
5809  my $elements;
5810
5811  # do that now to have it available for formatting
5812  # NOTE this calls Convert::Converter::_informative_command on all the
5813  # @informative_global commands.
5814  # Thus sets among others language and encodings.
5815  $self->_set_global_multiple_commands(-1);
5816  $self->_translate_names();
5817
5818  if ($self->get_conf('USE_NODES')) {
5819    $elements = Texinfo::Structuring::split_by_node($root);
5820  } else {
5821    $elements = Texinfo::Structuring::split_by_section($root);
5822  }
5823
5824  $self->{'elements'} = $elements
5825    if (defined($elements));
5826
5827  # This may be done as soon as elements are available.
5828  $self->_prepare_global_targets($elements);
5829
5830  # Do that before the other elements, to be sure that special page ids
5831  # are registered before elements id are.
5832  my $special_elements
5833    = $self->_prepare_special_elements($elements);
5834
5835  $self->{'special_elements'} = $special_elements
5836    if (defined($special_elements));
5837
5838  #if ($elements) {
5839  #  foreach my $element(@{$elements}) {
5840  #    print STDERR "ELEMENT $element->{'type'}: $element\n";
5841  #  }
5842  #}
5843
5844  $self->_set_root_commands_targets_node_files($elements);
5845
5846  return ($elements, $special_elements);
5847}
5848
5849sub _prepare_special_elements($$)
5850{
5851  my $self = shift;
5852  my $elements = shift;
5853
5854  my %do_special;
5855  # FIXME let the user decide how @*contents are treated?
5856  if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
5857      and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
5858    foreach my $cmdname ('contents', 'shortcontents') {
5859      my $type = $contents_command_element_name{$cmdname};
5860      if ($self->get_conf($cmdname)) {
5861        if ($self->get_conf('CONTENTS_OUTPUT_LOCATION')
5862            eq 'separate_element') {
5863          $do_special{$type} = 1;
5864        }
5865      }
5866    }
5867  }
5868  if ($self->{'extra'}->{'footnote'}
5869      and $self->get_conf('footnotestyle') eq 'separate'
5870      and $elements and scalar(@$elements) > 1) {
5871    $do_special{'Footnotes'} = 1;
5872  }
5873
5874  if ((!defined($self->get_conf('DO_ABOUT'))
5875       and $elements and scalar(@$elements) > 1
5876           and ($self->get_conf('SPLIT') or $self->get_conf('HEADERS')))
5877       or ($self->get_conf('DO_ABOUT'))) {
5878    $do_special{'About'} = 1;
5879  }
5880
5881  my $extension = '';
5882  $extension = $self->get_conf('EXTENSION')
5883    if (defined($self->get_conf('EXTENSION')));
5884
5885  my $special_elements = [];
5886  foreach my $type (@{$self->{'misc_elements_order'}}) {
5887    next unless ($do_special{$type});
5888
5889    my $element = {'type' => 'element',
5890                   'extra' => {'special_element' => $type,
5891                               }};
5892    $element->{'extra'}->{'directions'}->{'This'} = $element;
5893    $self->{'special_elements_types'}->{$type} = $element;
5894    push @$special_elements, $element;
5895
5896    my $target = $self->{'misc_elements_targets'}->{$type};
5897    my $default_filename;
5898    if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')) {
5899      $default_filename = $self->{'document_name'}.
5900        $self->{'misc_pages_file_string'}->{$type};
5901      $default_filename .= '.'.$extension if (defined($extension));
5902    } else {
5903      $default_filename = undef;
5904    }
5905
5906    my $filename;
5907    if (defined($Texinfo::Config::special_element_target_file_name)) {
5908      ($target, $filename)
5909                 = &$Texinfo::Config::special_element_target_file_name(
5910                                                            $self,
5911                                                            $element,
5912                                                            $target,
5913                                                            $default_filename);
5914    }
5915    $filename = $default_filename if (!defined($filename));
5916
5917    if ($self->get_conf('DEBUG')) {
5918      my $fileout = $filename;
5919      $fileout = 'UNDEF' if (!defined($fileout));
5920      print STDERR "Add special $element $type: target $target,\n".
5921        "    filename $fileout\n"
5922    }
5923    if ($self->get_conf('SPLIT') or !$self->get_conf('MONOLITHIC')
5924        or (defined($filename) ne defined($default_filename))
5925        or (defined($filename) and $filename ne $default_filename)) {
5926      $self->_set_element_file($element, $filename);
5927      print STDERR "NEW page for $type ($filename)\n" if ($self->get_conf('DEBUG'));
5928    }
5929    $self->{'targets'}->{$element} = {'target' => $target,
5930                                      'misc_filename' => $filename,
5931                                     };
5932    $self->{'seen_ids'}->{$target} = 1;
5933  }
5934  if ($self->get_conf('FRAMES')) {
5935    foreach my $type (keys(%{$self->{'frame_pages_file_string'}})) {
5936      my $default_filename;
5937      $default_filename = $self->{'document_name'}.
5938        $self->{'frame_pages_file_string'}->{$type};
5939      $default_filename .= '.'.$extension if (defined($extension));
5940
5941      my $element = {'type' => 'element',
5942                   'extra' => {'special_element' => $type,
5943                               }};
5944
5945      # only the filename is used
5946      my ($target, $filename);
5947      if (defined($Texinfo::Config::special_element_target_file_name)) {
5948        ($target, $filename)
5949                 = &$Texinfo::Config::special_element_target_file_name(
5950                                                            $self,
5951                                                            $element,
5952                                                            $target,
5953                                                            $default_filename);
5954      }
5955      $filename = $default_filename if (!defined($filename));
5956      $self->{'frame_pages_filenames'}->{$type} = $filename;
5957    }
5958  }
5959  return $special_elements;
5960}
5961
5962sub _prepare_contents_elements($)
5963{
5964  my $self = shift;
5965
5966  if ($self->{'structuring'} and $self->{'structuring'}->{'sectioning_root'}
5967      and scalar(@{$self->{'structuring'}->{'sections_list'}}) > 1) {
5968    foreach my $cmdname ('contents', 'shortcontents') {
5969      my $type = $contents_command_element_name{$cmdname};
5970      if ($self->get_conf($cmdname)) {
5971        my $default_filename;
5972        if ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_title') {
5973          if ($self->{'elements'}) {
5974            $default_filename = $self->{'elements'}->[0]->{'filename'};
5975          }
5976        } elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'after_top') {
5977          my $section_top = undef;
5978          if ($self->{'extra'} and $self->{'extra'}->{'top'}) {
5979             $section_top = $self->{'extra'}->{'top'};
5980             $default_filename = $self->command_filename($section_top)
5981          }
5982        } elsif ($self->get_conf('CONTENTS_OUTPUT_LOCATION') eq 'inline') {
5983          if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) {
5984            foreach my $command(@{$self->{'extra'}->{$cmdname}}) {
5985              my ($element, $root_command)
5986                = $self->_get_element($command);
5987              if (defined($element)) {
5988                $default_filename = $element->{'filename'};
5989                last;
5990              }
5991            }
5992          } else {
5993            next;
5994          }
5995        } else { # in this case, there should already be a special element
5996                 # if needed, done together with the other special elements.
5997          next;
5998        }
5999
6000        my $element = {'type' => 'element',
6001                       'extra' => {'special_element' => $type}};
6002        $self->{'special_elements_types'}->{$type} = $element;
6003        my $target = $self->{'misc_elements_targets'}->{$type};
6004        my $filename;
6005        if (defined($Texinfo::Config::special_element_target_file_name)) {
6006          ($target, $filename)
6007               = &$Texinfo::Config::special_element_target_file_name(
6008                                                          $self,
6009                                                          $element,
6010                                                          $target,
6011                                                          $default_filename);
6012        }
6013        $filename = $default_filename if (!defined($filename));
6014        print STDERR "Add content $element $type: target $target,\n".
6015           "    filename $filename\n" if ($self->get_conf('DEBUG'));
6016        $self->{'targets'}->{$element} = {'target' => $target,
6017                                          'misc_filename' => $filename,
6018                                          'filename' => $filename,
6019                                          };
6020      }
6021    }
6022  }
6023}
6024
6025# Associate elements with the global targets, First, Last, Top, Index.
6026sub _prepare_global_targets($$)
6027{
6028  my $self = shift;
6029  my $elements = shift;
6030
6031  $self->{'global_target_elements'}->{'First'} = $elements->[0];
6032  $self->{'global_target_elements'}->{'Last'} = $elements->[-1];
6033  # It is always the first printindex, even if it is not output (for example
6034  # it is in @copying and @titlepage, which are certainly wrong constructs).
6035  if ($self->{'extra'} and $self->{'extra'}->{'printindex'}) {
6036    my ($element, $root_command)
6037     = $self->_get_element($self->{'extra'}->{'printindex'}->[0]);
6038    if (defined($element)) {
6039      if ($root_command and $root_command->{'cmdname'} eq 'node'
6040          and $element->{'extra'}->{'section'}) {
6041        $root_command = $element->{'extra'}->{'section'};
6042      }
6043      if ($root_command and $root_command->{'cmdname'} ne 'node') {
6044        while ($root_command->{'level'} > 1
6045               and $root_command->{'section_up'}
6046               and $root_command->{'section_up'}->{'parent'}) {
6047          $root_command = $root_command->{'section_up'};
6048          $element = $root_command->{'parent'};
6049        }
6050      }
6051      $self->{'global_target_elements'}->{'Index'} = $element;
6052    }
6053  }
6054
6055  my $node_top;
6056  $node_top = $self->{'labels'}->{'Top'} if ($self->{'labels'});
6057  my $section_top;
6058  $section_top = $self->{'extra'}->{'top'} if ($self->{'extra'});
6059  if ($section_top) {
6060    $self->{'global_target_elements'}->{'Top'} = $section_top->{'parent'};
6061  } elsif ($node_top) {
6062    my $element_top = $node_top->{'parent'};
6063    if (!$element_top) {
6064      die "No parent for node_top: ".Texinfo::Common::_print_current($node_top);
6065    }
6066    $self->{'global_target_elements'}->{'Top'} = $element_top;
6067  } else {
6068    $self->{'global_target_elements'}->{'Top'} = $elements->[0];
6069  }
6070
6071  if ($self->get_conf('DEBUG')) {
6072    print STDERR "GLOBAL DIRECTIONS:\n";
6073    foreach my $global_direction (@global_directions) {
6074      if (defined($self->{'global_target_elements'}->{$global_direction})) {
6075        print STDERR "$global_direction($self->{'global_target_elements'}->{$global_direction}): ".
6076          Texinfo::Structuring::_print_element_command_texi(
6077             $self->{'global_target_elements'}->{$global_direction})."\n";
6078      }
6079    }
6080  }
6081}
6082
6083sub _prepare_index_entries($)
6084{
6085  my $self = shift;
6086
6087  if ($self->{'parser'}) {
6088    my $no_unidecode;
6089    $no_unidecode = 1 if (defined($self->get_conf('USE_UNIDECODE'))
6090                          and !$self->get_conf('USE_UNIDECODE'));
6091
6092    my $index_names = $self->{'parser'}->indices_information();
6093    $self->{'index_names'} = $index_names;
6094    my $merged_index_entries
6095        = Texinfo::Structuring::merge_indices($index_names);
6096    $self->{'index_entries_by_letter'}
6097      = Texinfo::Structuring::sort_indices_by_letter ($self->{'parser'},
6098                          $merged_index_entries, $index_names);
6099    $self->{'index_entries'} = $merged_index_entries;
6100
6101    foreach my $index_name (sort(keys(%$index_names))) {
6102      foreach my $index_entry (@{$index_names->{$index_name}->{'index_entries'}}) {
6103        my $region = '';
6104        $region = "$index_entry->{'region'}->{'cmdname'}-"
6105          if (defined($index_entry->{'region'}));
6106        my @contents = @{$index_entry->{'content_normalized'}};
6107        my $trimmed_contents
6108          = Texinfo::Common::trim_spaces_comment_from_content(\@contents);
6109        my $normalized_index =
6110          Texinfo::Convert::NodeNameNormalization::transliterate_texinfo(
6111            {'contents' => \@contents}, $no_unidecode);
6112        my $target_base = "index-" . $region .$normalized_index;
6113        my $nr=1;
6114        my $target = $target_base;
6115        while ($self->{'seen_ids'}->{$target}) {
6116          $target = $target_base.'-'.$nr;
6117          $nr++;
6118          # Avoid integer overflow
6119          die if ($nr == 0);
6120        }
6121        $self->{'seen_ids'}->{$target} = 1;
6122        $self->{'targets'}->{$index_entry->{'command'}} = {'target' => $target,
6123                                                        };
6124      }
6125    }
6126  }
6127}
6128
6129sub _prepare_footnotes($)
6130{
6131  my $self = shift;
6132
6133  if ($self->{'extra'}->{'footnote'}) {
6134    my $footnote_nr = 0;
6135    foreach my $footnote (@{$self->{'extra'}->{'footnote'}}) {
6136      $footnote_nr++;
6137      my $nr = $footnote_nr;
6138      my $footid = $footid_base.$nr;
6139      my $docid = $docid_base.$nr;
6140      while ($self->{'seen_ids'}->{$docid} or $self->{'seen_ids'}->{$footid}) {
6141        $nr++;
6142        $footid = $footid_base.$nr;
6143        $docid = $docid_base.$nr;
6144        # Avoid integer overflow
6145        die if ($nr == 0);
6146      }
6147      $self->{'seen_ids'}->{$footid} = 1;
6148      $self->{'seen_ids'}->{$docid} = 1;
6149      $self->{'targets'}->{$footnote} = { 'target' => $footid };
6150      print STDERR "Enter footnote $footnote: target $footid, nr $footnote_nr\n"
6151       .Texinfo::Convert::Texinfo::convert($footnote)."\n"
6152        if ($self->get_conf('DEBUG'));
6153    }
6154  }
6155}
6156
6157# TODO this encapsulates some information.
6158# The encapsulation and API should be more consistent for
6159# the overall module.
6160sub _htmlxref($$)
6161{
6162  my $self = shift;
6163  my $file = shift;
6164
6165  return $self->{'htmlxref'}->{$file};
6166}
6167
6168sub _external_node_href($$$$)
6169{
6170  my $self = shift;
6171  my $external_node = shift;
6172  my $filename = shift;
6173  my $link_command = shift;
6174
6175  #print STDERR "external_node: ".join('|', keys(%$external_node))."\n";
6176  my ($target_filebase, $target) = $self->_node_id_file($external_node);
6177
6178  my $xml_target = _normalized_to_id($target);
6179
6180  my $default_target_split = $self->get_conf('EXTERNAL_CROSSREF_SPLIT');
6181
6182  my $external_file_extension = '.html';
6183
6184  my $target_split;
6185  my $file;
6186  if ($external_node->{'manual_content'}) {
6187    my $manual_name = Texinfo::Convert::Text::convert(
6188       {'contents' => $external_node->{'manual_content'}},
6189       { 'code' => 1,
6190         Texinfo::Common::_convert_text_options($self)});
6191    my $manual_base = $manual_name;
6192    $manual_base =~ s/\.info*$//;
6193    $manual_base =~ s/^.*\///;
6194    my $document_split = $self->get_conf('SPLIT');
6195    $document_split = 'mono' if (!$document_split);
6196    my $split_found;
6197    my $href;
6198    my $htmlxref_info = $self->_htmlxref($manual_base);
6199    if ($htmlxref_info) {
6200      foreach my $split_ordered (@{$htmlxref_entries{$document_split}}) {
6201        if (defined($htmlxref_info->{$split_ordered})) {
6202          $split_found = $split_ordered;
6203          $href = $htmlxref_info->{$split_ordered};
6204          last;
6205        }
6206      }
6207    }
6208    if (defined($split_found)) {
6209      $target_split = 1 unless ($split_found eq 'mono');
6210    } else { # nothing specified for that manual, use default
6211      $target_split = $default_target_split;
6212      if ($self->get_conf('CHECK_HTMLXREF')) {
6213        if (defined($link_command) and $link_command->{'line_nr'}) {
6214          $self->line_warn(sprintf(__(
6215              "no htmlxref.cnf entry found for `%s'"), $manual_name),
6216            $link_command->{'line_nr'});
6217        } elsif (!$self->{'check_htmlxref_already_warned'}->{$manual_name}) {
6218          $self->document_warn(sprintf(__(
6219            "no htmlxref.cnf entry found for `%s'"), $manual_name),
6220            );
6221        }
6222        $self->{'check_htmlxref_already_warned'}->{$manual_name} = 1;
6223      }
6224    }
6225
6226    if ($target_split) {
6227      if (defined($href)) {
6228        $file = $href;
6229      } elsif (defined($self->get_conf('EXTERNAL_DIR'))) {
6230        $file = $self->get_conf('EXTERNAL_DIR')."/$manual_base";
6231      } elsif ($self->get_conf('SPLIT')) {
6232        $file = "../$manual_base";
6233      }
6234      $file .= "/";
6235    } else {# target not split
6236      if (defined($href)) {
6237        $file = $href;
6238      } else {
6239        if (defined($self->get_conf('EXTERNAL_DIR'))) {
6240          $file = $self->get_conf('EXTERNAL_DIR')."/$manual_base";
6241        } elsif ($self->get_conf('SPLIT')) {
6242          $file = "../$manual_base";
6243        } else {
6244          $file = $manual_base;
6245        }
6246        $file .= $external_file_extension;
6247      }
6248    }
6249  } else {
6250    $file = '';
6251    $target_split = $default_target_split;
6252  }
6253
6254  if ($target eq '') {
6255    if ($target_split) {
6256      if (defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
6257        return $file . $self->get_conf('TOP_NODE_FILE_TARGET');
6258      } else {
6259        return $file;# . '#Top';
6260      }
6261    } else {
6262      return $file . '#Top';
6263    }
6264  }
6265
6266  if (! $target_split) {
6267    return $file . '#' . $xml_target;
6268  } else {
6269    my $file_name;
6270    if ($target eq 'Top' and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
6271      $file_name = $self->get_conf('TOP_NODE_FILE_TARGET');
6272    } else {
6273      $file_name = $target_filebase . $external_file_extension;
6274    }
6275    return $file . $file_name . '#' . $xml_target;
6276  }
6277}
6278
6279my %valid_types = (
6280  'href' => 1,
6281  'string' => 1,
6282  'text' => 1,
6283  'tree' => 1,
6284  'target' => 1,
6285  'node' => 1,
6286  'section' => 1
6287);
6288
6289foreach my $no_number_type ('text', 'tree', 'string') {
6290  $valid_types{$no_number_type .'_nonumber'} = 1;
6291}
6292
6293# sub _element_direction($SELF, $ELEMENT, $DIRECTION, $TYPE, $FILENAME)
6294#
6295# Return text used for linking from $ELEMENT in direction $DIRECTION.  The
6296# text returned depends on $TYPE.
6297#
6298# $element can be undef.
6299# $element undef happens at least when there is no output file, or for
6300# the table of content when frames are used.  That call would result
6301# for instance from _element_direction being called from _get_links,
6302# itself called from 'format_begin_file' which, in the default case
6303# points to _default_format_begin_file.
6304# TODO are there other cases?
6305sub _element_direction($$$$;$)
6306{
6307  my $self = shift;
6308  my $element = shift;
6309  my $direction = shift;
6310  my $type = shift;
6311  my $filename = shift;
6312
6313  my $element_target;
6314  my $command;
6315  my $target;
6316
6317  $filename = $self->{'current_filename'} if (!defined($filename));
6318
6319  if (!$valid_types{$type}) {
6320    print STDERR "Incorrect type $type in _element_direction call\n";
6321    return undef;
6322  }
6323  if ($self->{'global_target_elements'}->{$direction}) {
6324    $element_target = $self->{'global_target_elements'}->{$direction};
6325  } elsif ($element and $element->{'extra'}
6326      and $element->{'extra'}->{'directions'}
6327      and $element->{'extra'}->{'directions'}->{$direction}) {
6328    $element_target
6329      = $element->{'extra'}->{'directions'}->{$direction};
6330  # output TOP_NODE_UP related infos even if element is not
6331  # defined which should mostly correspond to cases when there is no
6332  # output file, for example in the tests.
6333  } elsif ((not defined($element)
6334             or ($element and $self->element_is_top($element)))
6335            and defined($self->get_conf('TOP_NODE_UP_URL'))
6336            and ($direction eq 'Up' or $direction eq 'NodeUp')) {
6337    if ($type eq 'href') {
6338      return $self->get_conf('TOP_NODE_UP_URL');
6339    } elsif ($type eq 'text' or $type eq 'node' or $type eq 'string' or $type eq 'section') {
6340      return $self->get_conf('TOP_NODE_UP');
6341    } else {
6342      cluck("type $type not available for TOP_NODE_UP\n");
6343      return '';
6344    }
6345  }
6346
6347  if ($element_target) {
6348    ######## debug
6349    if (!$element_target->{'type'}) {
6350      die "No type for element_target $direction $element_target: "
6351        . Texinfo::Common::_print_current_keys($element_target)
6352        . "directions :". Texinfo::Structuring::_print_directions($element);
6353    }
6354    ########
6355    if ($element_target->{'type'} eq 'external_node') {
6356      my $external_node = $element_target->{'extra'};
6357      if ($type eq 'href') {
6358        return $self->command_href($external_node, $filename);
6359      } elsif ($type eq 'text' or $type eq 'node') {
6360        return $self->command_text($external_node);
6361      } elsif ($type eq 'string') {
6362        return $self->command_text($external_node, $type);
6363      }
6364    } elsif ($type eq 'node') {
6365      $command = $element_target->{'extra'}->{'node'};
6366      $target = $self->{'targets'}->{$command} if ($command);
6367      $type = 'text';
6368    } elsif ($type eq 'section') {
6369      $command = $element_target->{'extra'}->{'section'};
6370      $target = $self->{'targets'}->{$command} if ($command);
6371      $type = 'text_nonumber';
6372    } else {
6373      if ($element_target->{'extra'}->{'special_element'}) {
6374        $command = $element_target;
6375      } else {
6376        $command = $element_target->{'extra'}->{'element_command'};
6377      }
6378      if ($type eq 'href') {
6379        if (defined($command)) {
6380          return $self->command_href($command, $filename);
6381        } else {
6382          return '';
6383        }
6384      }
6385      $target = $self->{'targets'}->{$command} if ($command);
6386    }
6387  } elsif ($self->special_element($direction)) {
6388    $element_target = $self->special_element($direction);
6389    $command = $element_target;
6390    if ($type eq 'href') {
6391      return $self->command_href($element_target, $filename);
6392    }
6393    $target = $self->{'targets'}->{$element_target};
6394  } else {
6395    return undef;
6396  }
6397
6398  if (exists($target->{$type})) {
6399    return $target->{$type};
6400  } elsif ($type eq 'target') {
6401    return undef;
6402  } elsif ($command) {
6403    return $self->command_text($command, $type);
6404  }
6405}
6406
6407# Output a list of the nodes immediately below this one
6408sub _mini_toc
6409{
6410  my ($self, $command) = @_;
6411
6412  my $filename = $self->{'current_filename'};
6413  my $result = '';
6414  my $entry_index = 0;
6415  my $accesskey;
6416
6417  if ($command->{'section_childs'} and @{$command->{'section_childs'}}) {
6418    $result .= $self->_attribute_class('ul', 'section-toc').">\n";
6419
6420    foreach my $section (@{$command->{'section_childs'}}) {
6421      my $tree = $self->command_text($section, 'tree_nonumber');
6422      my $text = $self->_convert($tree);
6423
6424      $entry_index++;
6425      $accesskey = '';
6426      $accesskey = " accesskey=\"$entry_index\""
6427        if ($self->get_conf('USE_ACCESSKEY') and $entry_index < 10);
6428
6429      my $href = $self->command_href($section, $filename);
6430      if ($text ne '') {
6431        if ($href ne '') {
6432          my $href_attribute = '';
6433          if ($href ne '') {
6434            $href_attribute = " href=\"$href\"";
6435          }
6436          $result .= "<li><a${href_attribute}$accesskey>$text</a>";
6437        } else {
6438          $result .= "<li>$text";
6439        }
6440        $result .= "</li>\n";
6441      }
6442    }
6443    $result .= "</ul>\n";
6444  }
6445  return $result;
6446}
6447
6448sub _default_format_contents($$;$$)
6449{
6450  my $self = shift;
6451  my $cmdname = shift;
6452  my $command = shift;
6453  my $filename = shift;
6454  $filename = $self->{'current_filename'} if (!defined($filename));
6455
6456  return ''
6457   if (!$self->{'structuring'} or !$self->{'structuring'}->{'sectioning_root'});
6458
6459  my $section_root = $self->{'structuring'}->{'sectioning_root'};
6460  my $contents;
6461  $contents = 1 if ($cmdname eq 'contents');
6462
6463  my $min_root_level = $section_root->{'section_childs'}->[0]->{'level'};
6464  my $max_root_level = $section_root->{'section_childs'}->[0]->{'level'};
6465  foreach my $top_section(@{$section_root->{'section_childs'}}) {
6466    $min_root_level = $top_section->{'level'}
6467      if ($top_section->{'level'} < $min_root_level);
6468    $max_root_level = $top_section->{'level'}
6469      if ($top_section->{'level'} > $max_root_level);
6470  }
6471  # chapter level elements are considered top-level here.
6472  $max_root_level = 1 if ($max_root_level < 1);
6473  #print STDERR "ROOT_LEVEL Max: $max_root_level, Min: $min_root_level\n";
6474  my $ul_class = '';
6475  $ul_class = $NO_BULLET_LIST_CLASS if ($self->get_conf('NUMBER_SECTIONS'));
6476
6477  my $result = '';
6478  if ($contents and !defined($self->get_conf('BEFORE_TOC_LINES'))
6479      or (!$contents and !defined($self->get_conf('BEFORE_OVERVIEW')))) {
6480    $result .= $self->_attribute_class('div', $cmdname).">\n";
6481  } elsif($contents) {
6482    $result .= $self->get_conf('BEFORE_TOC_LINES');
6483  } else {
6484    $result .= $self->get_conf('BEFORE_OVERVIEW');
6485  }
6486
6487  my $toplevel_contents;
6488  if (@{$section_root->{'section_childs'}} > 1) {
6489  #    or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') {
6490    $result .= $self->_attribute_class('ul', $ul_class) .">\n";
6491    $toplevel_contents = 1;
6492  }
6493  foreach my $top_section (@{$section_root->{'section_childs'}}) {
6494    my $section = $top_section;
6495 SECTION:
6496    while ($section) {
6497      if ($section->{'cmdname'} ne 'top') {
6498        my $text = $self->command_text($section);
6499        my $href;
6500        if (!$contents and $self->get_conf('OVERVIEW_LINK_TO_TOC')) {
6501          $href = $self->command_contents_href($section, 'contents', $filename);
6502        } else {
6503          $href = $self->command_href($section, $filename);
6504        }
6505        my $toc_id = $self->command_contents_target($section, $cmdname);
6506        if ($text ne '') {
6507          # no indenting for shortcontents
6508          $result .= (' ' x (2*($section->{'level'} - $min_root_level)))
6509            if ($contents);
6510          if ($toc_id ne '' or $href ne '') {
6511            my $toc_name_attribute = '';
6512            if ($toc_id ne '') {
6513              $toc_name_attribute = " id=\"$toc_id\"";
6514            }
6515            my $href_attribute = '';
6516            if ($href ne '') {
6517              $href_attribute = " href=\"$href\"";
6518            }
6519            my $rel = '';
6520            if ($section->{'extra'}
6521                and $section->{'extra'}->{'associated_node'}
6522                and $section->{'extra'}->{'associated_node'}->{'extra'}->{'isindex'}) {
6523              $rel = ' rel="index"';
6524            }
6525            $result .= "<li><a${toc_name_attribute}${href_attribute}$rel>$text</a>";
6526          } else {
6527            $result .= "<li>$text";
6528          }
6529        }
6530      } elsif ($section->{'section_childs'} and @{$section->{'section_childs'}}
6531               and $toplevel_contents) {
6532        $result .= "<li>";
6533      }
6534      # for shortcontents don't do child if child is not toplevel
6535      if ($section->{'section_childs'}
6536          and ($contents or $section->{'level'} < $max_root_level)) {
6537        # no indenting for shortcontents
6538        $result .= "\n". ' ' x (2*($section->{'level'} - $min_root_level))
6539          if ($contents);
6540        $result .= $self->_attribute_class('ul', $ul_class) .">\n";
6541        $section = $section->{'section_childs'}->[0];
6542      } elsif ($section->{'section_next'} and $section->{'cmdname'} ne 'top') {
6543        $result .= "</li>\n";
6544        last if ($section eq $top_section);
6545        $section = $section->{'section_next'};
6546      } else {
6547        #last if ($section eq $top_section);
6548        if ($section eq $top_section) {
6549          $result .= "</li>\n" unless ($section->{'cmdname'} eq 'top');
6550          last;
6551        }
6552        while ($section->{'section_up'}) {
6553          $section = $section->{'section_up'};
6554          $result .= "</li>\n". ' ' x (2*($section->{'level'} - $min_root_level))
6555            . "</ul>";
6556          if ($section eq $top_section) {
6557            $result .= "</li>\n" if ($toplevel_contents);
6558            last SECTION;
6559          }
6560          if ($section->{'section_next'}) {
6561            $result .= "</li>\n";
6562            $section = $section->{'section_next'};
6563            last;
6564          }
6565        }
6566      }
6567    }
6568  }
6569  if (@{$section_root->{'section_childs'}} > 1) {
6570   #   or $section_root->{'section_childs'}->[0]->{'cmdname'} ne 'top') {
6571    $result .= "\n</ul>";
6572  }
6573  if ($contents and !defined($self->get_conf('AFTER_TOC_LINES'))
6574      or (!$contents and !defined($self->get_conf('AFTER_OVERVIEW')))) {
6575    $result .= "\n</div>\n";
6576  } elsif($contents) {
6577    $result .= $self->get_conf('AFTER_TOC_LINES');
6578  } else {
6579    $result .= $self->get_conf('AFTER_OVERVIEW');
6580  }
6581  return $result;
6582}
6583
6584sub _default_format_program_string($)
6585{
6586  my $self = shift;
6587  if (defined($self->get_conf('PROGRAM'))
6588      and $self->get_conf('PROGRAM') ne ''
6589      and defined($self->get_conf('PACKAGE_URL'))) {
6590    return $self->convert_tree(
6591      $self->gdt('This document was generated on @emph{@today{}} using @uref{{program_homepage}, @emph{{program}}}.',
6592         { 'program_homepage' => $self->get_conf('PACKAGE_URL'),
6593           'program' => $self->get_conf('PROGRAM') }));
6594  } else {
6595    return $self->convert_tree(
6596      $self->gdt('This document was generated on @emph{@today{}}.'));
6597  }
6598}
6599
6600sub _default_format_end_file($)
6601{
6602  my $self = shift;
6603  my $program_text = '';
6604  if ($self->get_conf('PROGRAM_NAME_IN_FOOTER')) {
6605    my $program_string = &{$self->{'format_program_string'}}($self);
6606    $program_text .= "<p><font size=\"-1\">
6607  $program_string
6608</font></p>";
6609  }
6610  my $pre_body_close = $self->get_conf('PRE_BODY_CLOSE');
6611  $pre_body_close = '' if (!defined($pre_body_close));
6612
6613  my $setting = $self->get_conf('JS_WEBLABELS');
6614  my $path = $self->get_conf('JS_WEBLABELS_FILE');
6615  if ($setting and $path
6616        and ($setting eq 'generate' or $setting eq 'reference')
6617             and %{$self->{'jslicenses_element'}}) {
6618    $pre_body_close .=
6619"<a href='$path' rel='jslicense'><small>"
6620.$self->convert_tree($self->gdt('JavaScript license information'))
6621.'</small></a>';
6622  }
6623
6624  return "${program_text}
6625
6626$pre_body_close
6627</body>
6628</html>
6629";
6630}
6631
6632# This is used for normal output files and other files, like
6633# redirection file headers.  $COMMAND is the tree element for
6634# a @node that is being output in the file.
6635sub _file_header_informations($$)
6636{
6637  my $self = shift;
6638  my $command = shift;
6639
6640  my $title;
6641  if ($command) {
6642    my $command_string =
6643      $self->command_text($command, 'string');
6644    if (defined($command_string)
6645        and $command_string ne $self->{'title_string'}) {
6646      my $element_tree;
6647      if ($self->get_conf('SECTION_NAME_IN_TITLE')
6648          and $command->{'extra'}
6649          and $command->{'extra'}->{'associated_section'}
6650          and $command->{'extra'}->{'associated_section'}->{'args'}
6651          and $command->{'extra'}->{'associated_section'}->{'args'}->[0]) {
6652        $element_tree = $command->{'extra'}->{'associated_section'}->{'args'}->[0];
6653      } else {
6654        $element_tree = $self->command_text($command, 'tree');
6655      }
6656      my $title_tree = $self->gdt('{element_text} ({title})',
6657                   { 'title' => $self->{'title_tree'},
6658                     'element_text' => $element_tree });
6659      $title = $self->convert_tree_new_formatting_context(
6660          {'type' => '_string', 'contents' => [$title_tree]},
6661          $command->{'cmdname'}, 'element_title');
6662    }
6663  }
6664  $title = $self->{'title_string'} if (!defined($title));
6665
6666  my $description;
6667  if ($self->{'documentdescription_string'}) {
6668    $description = $self->{'documentdescription_string'};
6669  } else {
6670    $description = $title;
6671  }
6672  $description = "<meta name=\"description\" content=\"$description\">"
6673    if ($description ne '');
6674  my $encoding = '';
6675  $encoding
6676     = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=".
6677       $self->get_conf('OUTPUT_ENCODING_NAME')."\">"
6678    if (defined($self->get_conf('OUTPUT_ENCODING_NAME'))
6679        and ($self->get_conf('OUTPUT_ENCODING_NAME') ne ''));
6680
6681  my $date = '';
6682  if ($self->get_conf('DATE_IN_HEADER')) {
6683    my $today = $self->convert_tree_new_formatting_context(
6684            {'cmdname' => 'today'}, 'DATE_IN_HEADER');
6685    $date = "\n<meta name=\"date\" content=\"$today\">";
6686  }
6687
6688  my $css_lines;
6689  if (defined($self->get_conf('CSS_LINES'))) {
6690    $css_lines = $self->get_conf('CSS_LINES');
6691  } else {
6692    $css_lines = '';
6693  }
6694  my $doctype = $self->get_conf('DOCTYPE');
6695  my $bodytext = $self->get_conf('BODYTEXT');
6696  if ($self->{'element_math'} and $self->get_conf('HTML_MATH')) {
6697    if ($self->get_conf('HTML_MATH') eq 'mathjax') {
6698      $bodytext .= ' class="tex2jax_ignore"';
6699    }
6700  }
6701  my $copying_comment = '';
6702  $copying_comment = $self->{'copying_comment'}
6703    if (defined($self->{'copying_comment'}));
6704  my $after_body_open = '';
6705  $after_body_open = $self->get_conf('AFTER_BODY_OPEN')
6706    if (defined($self->get_conf('AFTER_BODY_OPEN')));
6707  my $extra_head = '';
6708  $extra_head = $self->get_conf('EXTRA_HEAD')
6709    if (defined($self->get_conf('EXTRA_HEAD')));
6710  my $program_and_version = $self->get_conf('PACKAGE_AND_VERSION');
6711  my $program_homepage = $self->get_conf('PACKAGE_URL');
6712  my $program = $self->get_conf('PROGRAM');
6713  my $generator = '';
6714  if (defined($program) and $program ne '') {
6715    $generator = "\n<meta name=\"Generator\" content=\"$program\">";
6716  }
6717
6718  if (defined($self->get_conf('INFO_JS_DIR'))) {
6719    if (!$self->get_conf('SPLIT')) {
6720      $self->document_error(
6721        sprintf(__("%s not meaningful for non-split output"),
6722                   'INFO_JS_DIR'));
6723    } else {
6724      my $jsdir = $self->get_conf('INFO_JS_DIR');
6725      if ($jsdir eq '.') {
6726        $jsdir = '';
6727      } else {
6728        $jsdir =~ s,/*$,/,; # append a single slash
6729      }
6730
6731      $extra_head .=
6732'<link rel="stylesheet" type="text/css" href="'.$jsdir.'info.css"/>
6733<script src="'.$jsdir.'modernizr.js" type="text/javascript"></script>
6734<script src="'.$jsdir.'info.js" type="text/javascript"></script>';
6735    }
6736    for my $key (keys %{$self->{'jslicenses_infojs'}}) {
6737      $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_infojs'}->{$key};
6738    }
6739  }
6740  if (($self->{'element_math'} or !$self->get_conf('SPLIT'))
6741        and defined($self->get_conf('HTML_MATH'))
6742        and $self->get_conf('HTML_MATH') eq 'mathjax') {
6743    my $mathjax_script = $self->get_conf('MATHJAX_SCRIPT');
6744
6745    $extra_head .=
6746"<script type='text/javascript'>
6747MathJax = {
6748  options: {
6749    skipHtmlTags: {'[-]': ['pre']},
6750    ignoreHtmlClass: 'tex2jax_ignore',
6751    processHtmlClass: 'tex2jax_process'
6752  },
6753};
6754</script>"
6755.'<script type="text/javascript" id="MathJax-script" async
6756  src="'.$mathjax_script.'">
6757</script>';
6758
6759    for my $key (keys %{$self->{'jslicenses_math'}}) {
6760      $self->{'jslicenses_element'}->{$key} = $self->{'jslicenses_math'}->{$key};
6761    }
6762  }
6763
6764  return ($title, $description, $encoding, $date, $css_lines,
6765          $doctype, $bodytext, $copying_comment, $after_body_open,
6766          $extra_head, $program_and_version, $program_homepage,
6767          $program, $generator);
6768}
6769
6770sub _get_links ($$$)
6771{
6772  my $self = shift;
6773  my $filename = shift;
6774  my $element = shift;
6775
6776  my $links = '';
6777  if ($self->get_conf('USE_LINKS')) {
6778    my $link_buttons = $self->get_conf('LINKS_BUTTONS');
6779    foreach my $link (@$link_buttons) {
6780      my $link_href = $self->_element_direction($element,
6781                                          $link, 'href', $filename);
6782      #print STDERR "$link -> $link_href \n";
6783      if ($link_href and $link_href ne '') {
6784        my $link_string = $self->_element_direction($element,
6785                                          $link, 'string');
6786        my $link_title = '';
6787        $link_title = " title=\"$link_string\"" if (defined($link_string));
6788        my $rel = '';
6789        $rel = " rel=\"".$self->get_conf('BUTTONS_REL')->{$link}.'"'
6790           if (defined($self->get_conf('BUTTONS_REL')->{$link}));
6791        $links .= "<link href=\"$link_href\"${rel}${link_title}>\n";
6792      }
6793    }
6794  }
6795  return $links;
6796}
6797
6798sub _default_format_begin_file($$$)
6799{
6800  my $self = shift;
6801  my $filename = shift;
6802  my $element = shift;
6803
6804  my $command;
6805  if ($element and $self->get_conf('SPLIT')) {
6806    $command = $self->element_command($element);
6807  }
6808
6809  my ($title, $description, $encoding, $date, $css_lines,
6810          $doctype, $bodytext, $copying_comment, $after_body_open,
6811          $extra_head, $program_and_version, $program_homepage,
6812          $program, $generator) = $self->_file_header_informations($command);
6813
6814  my $links = $self->_get_links ($filename, $element);
6815
6816  my $result = "$doctype
6817<html>
6818<!-- Created by $program_and_version, $program_homepage -->
6819<head>
6820$encoding
6821$copying_comment<title>$title</title>
6822
6823$description
6824<meta name=\"keywords\" content=\"$title\">
6825<meta name=\"resource-type\" content=\"document\">
6826<meta name=\"distribution\" content=\"global\">${generator}$date
6827<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">
6828
6829${links}$css_lines
6830$extra_head
6831</head>
6832
6833<body $bodytext>
6834$after_body_open";
6835
6836  return $result;
6837}
6838
6839sub _default_format_node_redirection_page($$)
6840{
6841  my $self = shift;
6842  my $command = shift;
6843
6844  my ($title, $description, $encoding, $date, $css_lines,
6845          $doctype, $bodytext, $copying_comment, $after_body_open,
6846          $extra_head, $program_and_version, $program_homepage,
6847          $program, $generator) = $self->_file_header_informations($command);
6848
6849  my $name = $self->command_text($command);
6850  my $href = $self->command_href($command);
6851  my $direction = "<a href=\"$href\">$name</a>";
6852  my $string = $self->convert_tree (
6853    $self->gdt('The node you are looking for is at {href}.',
6854      { 'href' => {'type' => '_converted', 'text' => $direction }}));
6855  my $result = "$doctype
6856<html>
6857<!-- Created by $program_and_version, $program_homepage -->
6858<!-- This file redirects to the location of a node or anchor -->
6859<head>
6860$encoding
6861$copying_comment<title>$title</title>
6862
6863$description
6864<meta name=\"keywords\" content=\"$title\">
6865<meta name=\"resource-type\" content=\"document\">
6866<meta name=\"distribution\" content=\"global\">${generator}$date
6867$css_lines
6868<meta http-equiv=\"Refresh\" content=\"0; url=$href\">
6869<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">
6870$extra_head
6871</head>
6872
6873<body $bodytext>
6874$after_body_open
6875<p>$string</p>
6876</body>
6877";
6878  return $result;
6879}
6880
6881sub _default_format_footnotes_text($)
6882{
6883  my $self = shift;
6884  return '' if (!$foot_lines);
6885  my $result = $self->_attribute_class('div', 'footnote').">\n";
6886  $result .= $self->get_conf('DEFAULT_RULE') . "\n"
6887     if (defined($self->get_conf('DEFAULT_RULE'))
6888         and $self->get_conf('DEFAULT_RULE') ne '');
6889  my $footnote_heading
6890    = $self->convert_tree ($self->get_conf('SPECIAL_ELEMENTS_NAME')->{'Footnotes'});
6891  my $class = $self->get_conf('SPECIAL_ELEMENTS_CLASS')->{'Footnotes'};
6892  my $level = $self->get_conf('FOOTNOTE_END_HEADER_LEVEL');
6893  $result .= &{$self->{'format_heading_text'}}($self, $class.'-heading',
6894                                        $footnote_heading, $level)."\n";
6895  $result .= &{$self->{'format_special_element_body'}}($self, 'Footnotes',
6896                                               $self->{'current_element'});
6897  $result .= "</div>\n";
6898  return $result;
6899}
6900
6901sub _default_format_special_element_body($$$)
6902{
6903  my $self = shift;
6904  my $special_type = shift;
6905  my $element = shift;
6906
6907  if ($special_type eq 'About') {
6908    my $about = "<p>\n";
6909    my $PRE_ABOUT = $self->get_conf('PRE_ABOUT');
6910    if (defined($PRE_ABOUT)) {
6911      if (ref($PRE_ABOUT) eq 'CODE') {
6912        $about .= &$PRE_ABOUT($self, $element);
6913      } else {
6914        $about .= $PRE_ABOUT;
6915      }
6916    } else {
6917      $about .= '  '.&{$self->{'format_program_string'}}($self) ."\n";
6918    }
6919    $about .= <<EOT;
6920</p>
6921<p>
6922EOT
6923    $about .= $self->convert_tree($self->gdt('  The buttons in the navigation panels have the following meaning:')) . "\n";
6924    $about .= <<EOT;
6925</p>
6926<table border="1">
6927  <tr>
6928EOT
6929    $about .= '    <th> ' . $self->convert_tree($self->gdt('Button')) . " </th>\n" .
6930     '    <th> ' . $self->convert_tree($self->gdt('Name')) . " </th>\n" .
6931     '    <th> ' . $self->convert_tree($self->gdt('Go to')) . " </th>\n" .
6932     '    <th> ' . $self->convert_tree($self->gdt('From 1.2.3 go to')) . "</th>\n" . "  </tr>\n";
6933
6934    foreach my $button (@{$self->get_conf('SECTION_BUTTONS')}) {
6935      next if ($button eq ' ' or ref($button) eq 'CODE' or ref($button) eq 'SCALAR'
6936                or ref($button) eq 'ARRAY');
6937      my $button_name = $self->get_conf('BUTTONS_NAME')->{$button};
6938      $about .= "  <tr>\n    <td align=\"center\">";
6939      $about .=
6940            ($self->get_conf('ICONS') && $self->get_conf('ACTIVE_ICONS')->{$button} ?
6941             &{$self->{'format_button_icon_img'}}($self, $button_name,
6942                                       $self->get_conf('ACTIVE_ICONS')->{$button}) :
6943             ' [' . $self->get_conf('BUTTONS_TEXT')->{$button} . '] ');
6944      $about .= "</td>\n";
6945      $about .=
6946"    <td align=\"center\">".$button_name."</td>
6947    <td>".$self->get_conf('BUTTONS_GOTO')->{$button}."</td>
6948    <td>".$self->get_conf('BUTTONS_EXAMPLE')->{$button}."</td>
6949  </tr>
6950";
6951    }
6952
6953    $about .= <<EOT;
6954</table>
6955
6956<p>
6957EOT
6958    $about .= $self->convert_tree($self->gdt('  where the @strong{ Example } assumes that the current position is at @strong{ Subsubsection One-Two-Three } of a document of the following structure:')) . "\n";
6959
6960#  where the <strong> Example </strong> assumes that the current position
6961#  is at <strong> Subsubsection One-Two-Three </strong> of a document of
6962#  the following structure:
6963    $about .= <<EOT;
6964</p>
6965
6966<ul>
6967EOT
6968    $about .= '  <li> 1. ' . $self->convert_tree($self->gdt('Section One')) . "\n" .
6969"    <ul>\n" .
6970'      <li>1.1 ' . $self->convert_tree($self->gdt('Subsection One-One')) . "\n";
6971    $about .= <<EOT;
6972        <ul>
6973          <li>...</li>
6974        </ul>
6975      </li>
6976EOT
6977    $about .= '      <li>1.2 ' . $self->convert_tree($self->gdt('Subsection One-Two')) . "\n" .
6978"        <ul>\n" .
6979'          <li>1.2.1 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-One')) . "</li>\n" .
6980'          <li>1.2.2 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Two')) . "</li>\n" .
6981'          <li>1.2.3 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Three')) . " &nbsp; &nbsp;\n"
6982.
6983'            <strong>&lt;== ' . $self->convert_tree($self->gdt('Current Position')) . " </strong></li>\n" .
6984'          <li>1.2.4 ' . $self->convert_tree($self->gdt('Subsubsection One-Two-Four')) . "</li>\n" .
6985"        </ul>\n" .
6986"      </li>\n" .
6987'      <li>1.3 ' . $self->convert_tree($self->gdt('Subsection One-Three')) . "\n";
6988    $about .= <<EOT;
6989        <ul>
6990          <li>...</li>
6991        </ul>
6992      </li>
6993EOT
6994    $about .= '      <li>1.4 ' . $self->convert_tree($self->gdt('Subsection One-Four')) . "</li>\n";
6995
6996    my $AFTER_ABOUT = '';
6997    if (defined($self->get_conf('AFTER_ABOUT'))) {
6998      $AFTER_ABOUT = $self->get_conf('AFTER_ABOUT');
6999    }
7000    $about .= <<EOT;
7001    </ul>
7002  </li>
7003</ul>
7004$AFTER_ABOUT
7005EOT
7006    return $about;
7007  } elsif ($special_type eq 'Contents') {
7008    return &{$self->{'format_contents'}}($self, 'contents', undef);
7009  } elsif ($special_type eq 'Overview') {
7010    return &{$self->{'format_contents'}}($self, 'shortcontents', undef);
7011  } elsif ($special_type eq 'Footnotes') {
7012    my $result = $foot_lines;
7013    $foot_lines = '';
7014    return $result;
7015  }
7016}
7017
7018sub _do_jslicenses_file {
7019  my $self = shift;
7020
7021  my $setting = $self->get_conf('JS_WEBLABELS');
7022  my $path = $self->get_conf('JS_WEBLABELS_FILE');
7023
7024  # Possible settings:
7025  #   'generate' - create file at JS_WEBLABELS_FILE
7026  #   'reference' - reference file at JS_WEBLABELS_FILE but do not create it
7027  #   'omit' - do nothing
7028  return if (!$setting or $setting ne 'generate');
7029
7030  my $a = '';
7031  $a .= '<!DOCTYPE html>
7032<html><head><title>jslicense labels</title></head>
7033<body>
7034<table id="jslicense-labels1">
7035';
7036  my $h = $self->{'jslicenses'};
7037
7038  for my $file (keys %{$self->{'jslicenses'}}) {
7039    $a .= "<tr>\n";
7040    $a .= "<td><a href=\"$file\">$file</a></td>\n";
7041    $a .= "<td><a href=\"$h->{$file}->[1]\">$h->{$file}->[0]</a></td>\n";
7042    $a .= "<td><a href=\"$h->{$file}->[2]\">$h->{$file}->[2]</a></td>\n";
7043    $a .= "</tr>\n";
7044  }
7045
7046  $a .= "</body></html>\n";
7047
7048  if (File::Spec->file_name_is_absolute($path) or $path =~ /^[A-Za-z]*:/) {
7049    $self->document_warn(sprintf(
7050__("cannot use absolute path or URL `%s' for JS_WEBLABELS_FILE when generating web labels file"), $path));
7051    return;
7052  }
7053  my $license_file = File::Spec->catdir($self->{'destination_directory'},
7054                                        $path);
7055  my $fh = $self->Texinfo::Common::open_out($license_file);
7056  if (defined($fh)) {
7057    print $fh $a;
7058    $self->register_close_file($license_file);
7059    if (!close ($fh)) {
7060      $self->document_error(sprintf(__("error on closing %s: %s"),
7061                                    $license_file, $!));
7062    }
7063  } else {
7064    $self->document_error(sprintf(__("could not open %s for writing: %s"),
7065                                  $license_file, $!));
7066  }
7067}
7068
7069sub _default_format_frame_files($)
7070{
7071  my $self = shift;
7072
7073  my $frame_file = $self->{'frame_pages_filenames'}->{'Frame'};
7074  my $frame_outfile;
7075  if (defined($self->{'destination_directory'})
7076      and $self->{'destination_directory'} ne '') {
7077    $frame_outfile = File::Spec->catfile($self->{'destination_directory'},
7078                                         $frame_file);
7079  } else {
7080    $frame_outfile = $frame_file;
7081  }
7082
7083  my $toc_frame_file = $self->{'frame_pages_filenames'}->{'Toc_Frame'};
7084  my $toc_frame_outfile;
7085  if (defined($self->{'destination_directory'})
7086      and $self->{'destination_directory'} ne '') {
7087    $toc_frame_outfile = File::Spec->catfile($self->{'destination_directory'},
7088                                             $toc_frame_file);
7089  } else {
7090    $toc_frame_outfile = $toc_frame_file;
7091  }
7092
7093  my $frame_fh = $self->Texinfo::Common::open_out($frame_outfile);
7094  if (defined($frame_fh)) {
7095    my $doctype = $self->get_conf('FRAMESET_DOCTYPE');
7096    my $top_file = '';
7097    if ($self->global_element('Top')) {
7098      my $top_element = $self->global_element('Top');
7099      $top_file = $top_element->{'filename'};
7100    }
7101    my $title = $self->{'title_string'};
7102    print $frame_fh <<EOT;
7103$doctype
7104<html>
7105<head><title>$title</title></head>
7106<frameset cols="140,*">
7107  <frame name="toc" src="$toc_frame_file">
7108  <frame name="main" src="$top_file">
7109</frameset>
7110</html>
7111EOT
7112
7113    $self->register_close_file($frame_outfile);
7114    if (!close ($frame_fh)) {
7115      $self->document_error(sprintf(__("error on closing frame file %s: %s"),
7116                                    $frame_outfile, $!));
7117      return 0;
7118    }
7119  } else {
7120    $self->document_error(sprintf(__("could not open %s for writing: %s"),
7121                                  $frame_outfile, $!));
7122    return 0;
7123  }
7124
7125  my $toc_frame_fh = $self->Texinfo::Common::open_out($toc_frame_outfile);
7126  if (defined($toc_frame_fh)) {
7127
7128    my $header = &{$self->{'format_begin_file'}}($self, $toc_frame_file, undef);
7129    print $toc_frame_fh $header;
7130    print $toc_frame_fh '<h2>Content</h2>'."\n";
7131    my $shortcontents =
7132      &{$self->{'format_contents'}}($self, 'shortcontents', undef);
7133    $shortcontents =~ s/\bhref=/target="main" href=/g;
7134    print $toc_frame_fh $shortcontents;
7135    print $toc_frame_fh "</body></html>\n";
7136
7137    $self->register_close_file($toc_frame_outfile);
7138    if (!close ($toc_frame_fh)) {
7139      $self->document_error(sprintf(__("error on closing TOC frame file %s: %s"),
7140                                    $toc_frame_outfile, $!));
7141      return 0;
7142    }
7143  } else {
7144    $self->document_error(sprintf(__("could not open %s for writing: %s"),
7145                                  $toc_frame_outfile, $!));
7146    return 0;
7147  }
7148  return 1;
7149}
7150
7151sub _has_contents_or_shortcontents($)
7152{
7153  my $self = shift;
7154  foreach my $cmdname ('contents', 'shortcontents') {
7155    if ($self->{'extra'} and $self->{'extra'}->{$cmdname}) {
7156      return 1;
7157    }
7158  }
7159  return 0;
7160}
7161
7162sub convert($$)
7163{
7164  my $self = shift;
7165  my $root = shift;
7166
7167  my $result = '';
7168
7169  # This should return undef if called on a tree without node or sections.
7170  my ($elements, $special_elements)
7171    = $self->_prepare_elements($root);
7172  $self->_prepare_index_entries();
7173  $self->_prepare_footnotes();
7174
7175  if (!defined($elements)) {
7176    $result = $self->_convert($root);
7177  } else {
7178    foreach my $element (@$elements) {
7179      my $element_text = $self->_convert($element);
7180      $result .= $element_text;
7181    }
7182  }
7183
7184  return $result;
7185}
7186
7187# This is called from the main program on the converter.
7188sub output_internal_links($)
7189{
7190  my $self = shift;
7191  my $out_string = '';
7192  if ($self->{'elements'}) {
7193    foreach my $element (@{$self->{'elements'}}) {
7194      my $text;
7195      my $href;
7196      my $command = $self->element_command($element);
7197      if (defined($command)) {
7198        # Use '' for filename, to force a filename in href.
7199        $href = $self->command_href($command, '');
7200        my $tree = $self->command_text($command, 'tree');
7201        if ($tree) {
7202          $text = Texinfo::Convert::Text::convert($tree,
7203                             {Texinfo::Common::_convert_text_options($self)});
7204        }
7205      }
7206      if (defined($href) or defined($text)) {
7207        $out_string .= $href if (defined($href));
7208        $out_string .= "\ttoc\t";
7209        $out_string .= $text if (defined($text));
7210        $out_string .= "\n";
7211      }
7212    }
7213  }
7214  if ($self->{'parser'}) {
7215    foreach my $index_name (sort(keys (%{$self->{'index_entries_by_letter'}}))) {
7216      foreach my $letter_entry (@{$self->{'index_entries_by_letter'}->{$index_name}}) {
7217        foreach my $index_entry (@{$letter_entry->{'entries'}}) {
7218          my $href;
7219          my $key;
7220          $href = $self->command_href($index_entry->{'command'}, '');
7221          $key = $index_entry->{'key'};
7222          if (defined($key) and $key =~ /\S/) {
7223            $out_string .= $href if (defined($href));
7224            $out_string .= "\t$index_name\t";
7225            $out_string .= $key;
7226            $out_string .= "\n";
7227          }
7228        }
7229      }
7230    }
7231  }
7232  if ($out_string ne '') {
7233    return $out_string;
7234  } else {
7235    return undef;
7236  }
7237}
7238
7239my @possible_stages = ('setup', 'structure', 'init', 'finish');
7240my %possible_stages;
7241foreach my $stage (@possible_stages) {
7242  $possible_stages{$stage} = 1;
7243}
7244
7245sub run_stage_handlers($$$)
7246{
7247  my $converter = shift;
7248  my $root = shift;
7249  my $stage = shift;
7250  die if (!$possible_stages{$stage});
7251
7252  return 1 if (!defined($Texinfo::Config::texinfo_default_stage_handlers{$stage}));
7253
7254  my @sorted_priorities = sort keys(%{$Texinfo::Config::texinfo_default_stage_handlers{$stage}});
7255  foreach my $priority (@sorted_priorities) {
7256    foreach my $handler (@{$Texinfo::Config::texinfo_default_stage_handlers{$stage}->{$priority}}) {
7257      if ($converter->get_conf('DEBUG')) {
7258        print STDERR "HANDLER($stage) , priority $priority: $handler\n";
7259      }
7260      my $status = &{$handler}($converter, $root, $stage);
7261      if (!$status) {
7262        #if ($converter->get_conf('VERBOSE')) {
7263        #  print STDERR "Handler $handler of $stage($priority) failed\n";
7264        #}
7265        $converter->document_error(sprintf(__(
7266                 "handler %s of stage %s priority %s failed"),
7267                 $handler, $stage, $priority));
7268        return $status;
7269      }
7270    }
7271  }
7272  return 1;
7273}
7274
7275my $default_priority = 'default';
7276
7277{
7278package Texinfo::Config;
7279
7280# Note that these variables are available for the Texinfo modules
7281# but, in general should not be accessed directly by the users who
7282# customize formatting and should use the associated functions,
7283# such as texinfo_register_handler(), texinfo_register_formatting_function(),
7284# texinfo_register_command_formatting() or texinfo_register_type_formatting().
7285use vars qw(%texinfo_default_stage_handlers %texinfo_formatting_references
7286            %texinfo_commands_conversion %texinfo_types_conversion);
7287
7288sub texinfo_register_handler($$;$)
7289{
7290  my $stage = shift;
7291  my $handler = shift;
7292  my $priority = shift;
7293
7294  if (!$possible_stages{$stage}) {
7295    carp ("Unknown stage $stage\n");
7296    return 0;
7297  }
7298  $priority = $default_priority if (!defined($priority));
7299  push @{$texinfo_default_stage_handlers{$stage}->{$priority}}, $handler;
7300  return 1;
7301}
7302
7303sub texinfo_register_formatting_function($$)
7304{
7305  my $thing = shift;
7306  my $handler = shift;
7307  if (!$default_formatting_references{$thing}) {
7308    carp ("Unknown formatting type $thing\n");
7309    return 0;
7310  }
7311  $texinfo_formatting_references{$thing} = $handler;
7312}
7313
7314sub texinfo_register_command_formatting($$)
7315{
7316  my $command = shift;
7317  my $reference = shift;
7318  $texinfo_commands_conversion{$command} = $reference;
7319}
7320
7321sub texinfo_register_type_formatting($$)
7322{
7323  my $command = shift;
7324  my $reference = shift;
7325  $texinfo_types_conversion{$command} = $reference;
7326}
7327
7328
7329}
7330
7331# Main function for outputting a manual in HTML.
7332# $SELF is the output converter object of class Texinfo::Convert::HTML (this
7333# module), and $ROOT is the Texinfo tree from the parser.
7334sub output($$)
7335{
7336  my $self = shift;
7337  my $root = shift;
7338
7339  # no splitting when writing to the null device or to stdout or returning
7340  # a string
7341  if (defined($self->get_conf('OUTFILE'))
7342      and ($Texinfo::Common::null_device_file{$self->get_conf('OUTFILE')}
7343           or $self->get_conf('OUTFILE') eq '-'
7344           or $self->get_conf('OUTFILE') eq '')) {
7345    $self->force_conf('SPLIT', 0);
7346    $self->force_conf('MONOLITHIC', 1);
7347    $self->force_conf('FRAMES', 0);
7348  }
7349  if ($self->get_conf('SPLIT')) {
7350    $self->set_conf('NODE_FILES', 1);
7351  }
7352  if ($self->get_conf('FRAMES')) {
7353    $self->set_conf('shortcontents', 1);
7354  }
7355  $self->set_conf('EXTERNAL_CROSSREF_SPLIT', $self->get_conf('SPLIT'));
7356
7357  my $setup_status = $self->run_stage_handlers($root, 'setup');
7358  return undef unless($setup_status);
7359
7360  $self->_prepare_css();
7361
7362  # this sets OUTFILE, to be used if not split, but also
7363  # 'destination_directory' and 'output_filename' that are useful when split.
7364  $self->_set_outfile();
7365  return undef unless $self->_create_destination_directory();
7366
7367  # Get the list of "elements" to be processed, i.e. nodes or sections.
7368  # This should return undef if called on a tree without node or sections.
7369  my ($elements, $special_elements) = $self->_prepare_elements($root);
7370
7371  Texinfo::Structuring::split_pages($elements, $self->get_conf('SPLIT'));
7372
7373  # determine file names associated with the different pages, and setup
7374  # the counters for special element pages.
7375  if ($self->{'output_file'} ne '') {
7376    $self->_set_pages_files($elements, $special_elements);
7377  }
7378
7379  $self->_prepare_contents_elements();
7380
7381  # do element directions.
7382  Texinfo::Structuring::elements_directions($self, $elements);
7383
7384  # do element directions related to files.
7385  # FIXME do it here or before?  Here it means that
7386  # PrevFile and NextFile can be set.
7387  Texinfo::Structuring::elements_file_directions($self, $elements);
7388
7389  # Associate the special elements that have no page with the main page.
7390  # This may only happen if not split.
7391  if ($special_elements
7392      and $elements and $elements->[0]
7393      and defined($elements->[0]->{'filename'})) {
7394    foreach my $special_element (@$special_elements) {
7395      if (!defined($special_element->{'filename'})) {
7396        $special_element->{'filename'} = $elements->[0]->{'filename'};
7397        $special_element->{'out_filename'} = $elements->[0]->{'out_filename'};
7398        $self->{'file_counters'}->{$special_element->{'filename'}}++;
7399      }
7400    }
7401  }
7402
7403  $self->_prepare_index_entries();
7404  $self->_prepare_footnotes();
7405
7406  # 'file_counters' is dynamic, decreased when the element is encountered
7407  # 'elements_in_file_count' is not modified afterwards
7408  foreach my $filename (keys(%{$self->{'file_counters'}})) {
7409    $self->{'elements_in_file_count'}->{$filename} = $self->{'file_counters'}->{$filename};
7410  }
7411
7412  my $structure_status = $self->run_stage_handlers($root, 'structure');
7413  return undef unless($structure_status);
7414
7415  &{$self->{'format_css_lines'}}($self);
7416
7417  $self->set_conf('BODYTEXT',
7418                  'lang="' . $self->get_conf('documentlanguage') . '"');
7419
7420  # prepare title.  fulltitle uses more possibility than simpletitle for
7421  # title, including @-commands found in @titlepage only.  Therefore
7422  # simpletitle is more in line with what makeinfo in C does.
7423  my $fulltitle;
7424  foreach my $fulltitle_command('settitle', 'title',
7425     'shorttitlepage', 'top') {
7426    if ($self->{'extra'}->{$fulltitle_command}) {
7427      my $command = $self->{'extra'}->{$fulltitle_command};
7428      next if (!$command->{'args'}
7429               or (!$command->{'args'}->[0]->{'contents'}
7430                   or $command->{'extra'}->{'missing_argument'}));
7431      print STDERR "Using $fulltitle_command as title\n"
7432        if ($self->get_conf('DEBUG'));
7433      $fulltitle = {'contents' => $command->{'args'}->[0]->{'contents'}};
7434      last;
7435    }
7436  }
7437  if (!$fulltitle and $self->{'extra'}->{'titlefont'}
7438      and $self->{'extra'}->{'titlefont'}->[0]->{'args'}
7439      and defined($self->{'extra'}->{'titlefont'}->[0]->{'args'}->[0])
7440      and @{$self->{'extra'}->{'titlefont'}->[0]->{'args'}->[0]->{'contents'}}) {
7441    $fulltitle = $self->{'extra'}->{'titlefont'}->[0];
7442  }
7443  # prepare simpletitle
7444  foreach my $simpletitle_command('settitle', 'shorttitlepage') {
7445    if ($self->{'extra'}->{$simpletitle_command}) {
7446      my $command = $self->{'extra'}->{$simpletitle_command};
7447      next if ($command->{'extra'}
7448               and $command->{'extra'}->{'missing_argument'});
7449      $self->{'simpletitle_tree'} =
7450         {'contents' => $command->{'args'}->[0]->{'contents'}};
7451      last;
7452    }
7453  }
7454
7455  my $html_title_string;
7456  if ($fulltitle) {
7457    $self->{'title_tree'} = $fulltitle;
7458    $html_title_string = $self->convert_tree_new_formatting_context(
7459          {'type' => '_string', 'contents' => [$self->{'title_tree'}]},
7460          'title_string');
7461  }
7462  if (!defined($html_title_string) or $html_title_string !~ /\S/) {
7463    my $default_title = $self->gdt('Untitled Document');
7464    $self->{'title_tree'} = $default_title;
7465    $self->{'title_string'} = $self->convert_tree_new_formatting_context(
7466          {'type' => '_string', 'contents' => [$self->{'title_tree'}]},
7467          'title_string');
7468    $self->file_line_warn(__(
7469                         "must specify a title with a title command or \@top"),
7470                         $self->{'info'}->{'input_file_name'});
7471  } else {
7472    $self->{'title_string'} = $html_title_string;
7473  }
7474
7475  # copying comment
7476  if ($self->{'extra'}->{'copying'}) {
7477    my $copying_comment = Texinfo::Convert::Text::convert(
7478     {'contents' => $self->{'extra'}->{'copying'}->{'contents'}},
7479     {Texinfo::Common::_convert_text_options($self)});
7480    if ($copying_comment ne '') {
7481      $self->{'copying_comment'} = &{$self->{'format_comment'}}($self, $copying_comment);
7482    }
7483  }
7484
7485  # documentdescription
7486  if (defined($self->get_conf('documentdescription'))) {
7487    $self->{'documentdescription_string'}
7488      = $self->get_conf('documentdescription');
7489  } elsif ($self->{'extra'}->{'documentdescription'}) {
7490    $self->{'documentdescription_string'}
7491      = $self->convert_tree_new_formatting_context(
7492       {'type' => '_string',
7493        'contents' => $self->{'extra'}->{'documentdescription'}->{'contents'}},
7494       'documentdescription');
7495    chomp($self->{'documentdescription_string'});
7496  }
7497
7498  my $init_status = $self->run_stage_handlers($root, 'init');
7499  return undef unless($init_status);
7500
7501  if ($self->get_conf('FRAMES')) {
7502    my $status = &{$self->{'format_frame_files'}}($self);
7503    return undef if (!$status);
7504  }
7505
7506  if ($self->get_conf('HTML_MATH')
7507        and $self->get_conf('HTML_MATH') eq 'mathjax') {
7508    # See https://www.gnu.org/licenses/javascript-labels.html
7509    #
7510    # The link to the source for mathjax does not strictly follow the advice
7511    # there: instead we link to instructions for obtaining the full source in
7512    # its preferred form of modification.
7513
7514    my ($mathjax_script, $mathjax_source);
7515
7516    $mathjax_script = $self->get_conf('MATHJAX_SCRIPT');
7517    if (!$mathjax_script) {
7518      $mathjax_script = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js';
7519      $self->set_conf('MATHJAX_SCRIPT', $mathjax_script);
7520    }
7521
7522    $mathjax_source = $self->get_conf('MATHJAX_SOURCE');
7523    if (!$mathjax_source) {
7524      $mathjax_source = 'http://docs.mathjax.org/en/latest/web/hosting.html#getting-mathjax-via-git';
7525      $self->set_conf('MATHJAX_SOURCE', $mathjax_source);
7526    }
7527
7528    $self->{'jslicenses_math'}->{$mathjax_script} =
7529        [ 'Apache License, Version 2.0.',
7530          'https://www.apache.org/licenses/LICENSE-2.0',
7531          $mathjax_source ];
7532    # append to hash
7533    %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}},
7534                                 %{$self->{'jslicenses_math'}} );
7535
7536  }
7537  if ($self->get_conf('INFO_JS_DIR')) {
7538    $self->{'jslicenses_infojs'}->{'js/info.js'} =
7539      [ 'GNU General Public License 3.0 or later',
7540        'http://www.gnu.org/licenses/gpl-3.0.html',
7541        'js/info.js' ];
7542
7543    $self->{'jslicenses_infojs'}->{'js/modernizr.js'} =
7544      [ 'Expat',
7545        'http://www.jclark.com/xml/copying.txt',
7546        'js/modernizr.js' ];
7547    %{$self->{'jslicenses'}} = ( %{$self->{'jslicenses'}},
7548                                 %{$self->{'jslicenses_infojs'}} );
7549  }
7550
7551  # FIXME here call _unset_global_multiple_commands?  Problem is
7552  # that some conversion, for instance for page header requires
7553  # that the correct language is set, for instance.  The @-command
7554  # will necessarily appear later on -- even if it appears a the
7555  # beginning of the file.
7556
7557  my $fh;
7558  my $output = '';
7559
7560  if (!$elements or !defined($elements->[0]->{'filename'})) {
7561    # no page
7562    my $outfile;
7563    if ($self->{'output_file'} ne '') {
7564      if ($self->get_conf('SPLIT')) {
7565        $outfile = $self->_top_node_filename();
7566        if (defined($self->{'destination_directory'})
7567            and $self->{'destination_directory'} ne '') {
7568          $outfile = File::Spec->catfile($self->{'destination_directory'},
7569                                         $outfile);
7570        }
7571      } else {
7572        $outfile = $self->{'output_file'};
7573      }
7574      $fh = $self->Texinfo::Common::open_out($outfile);
7575      if (!$fh) {
7576        $self->document_error(sprintf(__("could not open %s for writing: %s"),
7577                                      $outfile, $!));
7578        return undef;
7579      }
7580    }
7581    $self->{'current_filename'} = $self->{'output_filename'};
7582
7583    my $body = '';
7584    if ($elements and @$elements) {
7585      foreach my $element (@$elements) {
7586        my $element_text = $self->_convert($element);
7587        $body .= $element_text;
7588      }
7589    } else {
7590      $body .= $self->_print_title();
7591      $body .= $self->_convert($root);
7592    }
7593
7594    my $header = &{$self->{'format_begin_file'}}($self,
7595                                           $self->{'output_filename'}, undef);
7596    $output .= $self->_output_text($header, $fh);
7597    $output .= $self->_output_text($body, $fh);
7598    $output .= $self->_output_text(&{$self->{'format_end_file'}}($self), $fh);
7599
7600    # NOTE do not close STDOUT now to avoid a perl warning.
7601    if ($fh and $outfile ne '-') {
7602      $self->register_close_file($outfile);
7603      if (!close($fh)) {
7604        $self->document_error(sprintf(__("error on closing %s: %s"),
7605                                      $outfile, $!));
7606      }
7607    }
7608    return $output if ($self->{'output_file'} eq '');
7609  } else {
7610    # output with pages
7611    print STDERR "DO Elements with filenames\n"
7612      if ($self->get_conf('DEBUG'));
7613    my %files;
7614
7615    # Now do the output, converting each member in @$elements in turn.
7616    $special_elements = [] if (!defined($special_elements));
7617    foreach my $element (@$elements, @$special_elements) {
7618      my $file_fh;
7619      $self->{'current_filename'} = $element->{'filename'};
7620      $self->{'counter_in_file'}->{$element->{'filename'}}++;
7621      if ($self->{'counter_in_file'}->{$element->{'filename'}} == 1) {
7622        $self->{'jslicenses_element'} = {};
7623        $self->{'element_math'} = 0;
7624      }
7625
7626      # First do the special pages, to avoid outputting these if they are
7627      # empty.
7628      my $special_element_content;
7629      if ($element->{'extra'} and $element->{'extra'}->{'special_element'}) {
7630        $special_element_content .= $self->_convert($element);
7631        if ($special_element_content eq '') {
7632          $self->{'file_counters'}->{$element->{'filename'}}--;
7633          next ;
7634        }
7635      }
7636
7637      # convert body before header in case this affects the header
7638      my $body = '';
7639      if (defined($special_element_content)) {
7640        $body = $special_element_content;
7641      } else {
7642        $body = $self->_convert($element);
7643      }
7644
7645      if (!$files{$element->{'filename'}}->{'fh'}) {
7646        $file_fh = $self->Texinfo::Common::open_out($element->{'out_filename'});
7647        if (!$file_fh) {
7648          $self->document_error(sprintf(__("could not open %s for writing: %s"),
7649                                    $element->{'out_filename'}, $!));
7650          return undef;
7651        }
7652        print $file_fh "".&{$self->{'format_begin_file'}}($self,
7653                                           $element->{'filename'},
7654                                           $element);
7655        $files{$element->{'filename'}}->{'fh'} = $file_fh;
7656      } else {
7657        $file_fh = $files{$element->{'filename'}}->{'fh'};
7658      }
7659      print $file_fh $body;
7660      $self->{'file_counters'}->{$element->{'filename'}}--;
7661      if ($self->{'file_counters'}->{$element->{'filename'}} == 0) {
7662        # end file
7663        print $file_fh "". &{$self->{'format_end_file'}}($self);
7664
7665        # NOTE do not close STDOUT here to avoid a perl warning
7666        if ($element->{'out_filename'} ne '-') {
7667          $self->register_close_file($element->{'out_filename'});
7668          if (!close($file_fh)) {
7669            $self->document_error(sprintf(__("error on closing %s: %s"),
7670                                  $element->{'out_filename'}, $!));
7671            return undef;
7672          }
7673        }
7674      }
7675    }
7676    if ($self->get_conf('INFO_JS_DIR')) {
7677      my $jsdir = File::Spec->catdir($self->{'destination_directory'},
7678                                     $self->get_conf('INFO_JS_DIR'));
7679      if (!-d $jsdir) {
7680        if (-f $jsdir) {
7681          $self->document_error(
7682            sprintf(__("%s already exists but is not a directory"), $jsdir));
7683        } else {
7684          mkdir $jsdir;
7685        }
7686      }
7687      if (-d $jsdir) {
7688        my $jssrcdir;
7689        if (!$Texinfo::ModulePath::texinfo_uninstalled) {
7690          $jssrcdir = File::Spec->catdir(
7691            $Texinfo::ModulePath::lib_dir, 'js');
7692        } else {
7693          $jssrcdir = File::Spec->catdir(
7694            $Texinfo::ModulePath::top_srcdir, 'js');
7695        }
7696        for my $f ('info.js', 'modernizr.js', 'info.css') {
7697          my $from = File::Spec->catfile($jssrcdir, $f);
7698
7699          if (!copy($from, $jsdir)) {
7700            $self->document_error(
7701              sprintf(__("error on copying %s into %s"), $from, $jsdir));
7702          }
7703        }
7704      }
7705    }
7706  }
7707
7708  if (%{$self->{'jslicenses'}}) {
7709    $self->_do_jslicenses_file();
7710  }
7711
7712  my $finish_status = $self->run_stage_handlers($root, 'finish');
7713  return undef unless($finish_status);
7714
7715  my $extension = '';
7716  $extension = '.'.$self->get_conf('EXTENSION')
7717            if (defined($self->get_conf('EXTENSION'))
7718                and $self->get_conf('EXTENSION') ne '');
7719  # do node redirection pages
7720  $self->{'current_filename'} = undef;
7721  if ($self->get_conf('NODE_FILES')
7722      and $self->{'labels'} and $self->{'output_file'} ne '') {
7723    foreach my $label (sort(keys (%{$self->{'labels'}}))) {
7724      my $node = $self->{'labels'}->{$label};
7725      my $target = $self->_get_target($node);
7726      # filename may not be defined in case of an @anchor or similar in
7727      # @titlepage, and @titlepage is not used.
7728      my $filename = $self->command_filename($node);
7729      my $node_filename;
7730      # NOTE 'node_filename' is not used for Top, so the other manual
7731      # must use the same convention to get it right.  We avoid doing
7732      # also 'node_filename' to avoid unneeded redirection files.
7733      if ($node->{'extra'} and $node->{'extra'}->{'normalized'}
7734          and $node->{'extra'}->{'normalized'} eq 'Top'
7735          and defined($self->get_conf('TOP_NODE_FILE_TARGET'))) {
7736        $node_filename = $self->get_conf('TOP_NODE_FILE_TARGET');
7737      } else {
7738        $node_filename = $target->{'node_filename'};
7739      }
7740
7741      if (defined($filename) and $node_filename ne $filename) {
7742        my $redirection_page
7743          = &{$self->{'format_node_redirection_page'}}($self, $node);
7744        my $out_filename;
7745        if (defined($self->{'destination_directory'})
7746            and $self->{'destination_directory'} ne '') {
7747          $out_filename = File::Spec->catfile($self->{'destination_directory'},
7748                                              $node_filename);
7749        } else {
7750          $out_filename = $node_filename;
7751        }
7752        my $file_fh = $self->Texinfo::Common::open_out($out_filename);
7753        if (!$file_fh) {
7754         $self->document_error(sprintf(__(
7755                                    "could not open %s for writing: %s"),
7756                                    $out_filename, $!));
7757        } else {
7758          print $file_fh $redirection_page;
7759          $self->register_close_file($out_filename);
7760          if (!close ($file_fh)) {
7761            $self->document_error(sprintf(__(
7762                             "error on closing redirection node file %s: %s"),
7763                                    $out_filename, $!));
7764            return undef;
7765          }
7766        }
7767      }
7768    }
7769  }
7770  return undef;
7771}
7772
7773# Convert the 'contents' of a tree element.
7774sub _convert_contents($$$)
7775{
7776  my $self = shift;
7777  my $root = shift;
7778  my $command_type = shift;
7779  my $content_formatted = '';
7780  if (ref($root->{'contents'}) ne 'ARRAY') {
7781    cluck "for $root contents not an array: $root->{'contents'}";
7782    print STDERR Texinfo::Common::_print_current($root);
7783  }
7784
7785  my $content_idx = 0;
7786  foreach my $content (@{$root->{'contents'}}) {
7787    my $new_content = $self->_convert($content, "$command_type [$content_idx]");
7788    if (!defined($new_content)) {
7789      cluck "content not defined for $command_type [$content_idx]\n";
7790      print STDERR "root is: ".Texinfo::Common::_print_current ($root);
7791      print STDERR "content is: ".Texinfo::Common::_print_current ($content);
7792    } else {
7793      $content_formatted .= $new_content;
7794    }
7795    $content_idx++;
7796  }
7797  return $content_formatted;
7798}
7799
7800#my $characters_replaced_from_class_names = quotemeta('[](),~#:/\\@+=!;.,?* ');
7801# FIXME not clear what character should be allowed and which ones replaced besides space
7802my $characters_replaced_from_class_names = quotemeta(' ');
7803sub _protect_class_name($$)
7804{
7805  my $self = shift;
7806  my $class_name = shift;
7807  $class_name =~ s/[$characters_replaced_from_class_names]/-/g;
7808  return $self->protect_text($class_name);
7809}
7810
7811# $extra_classes should be an array reference or undef
7812sub _attribute_class($$$;$)
7813{
7814  my $self = shift;
7815  my $element = shift;
7816  my $class = shift;
7817  my $extra_classes = shift;
7818
7819  if (!defined($class) or $class eq '' or $self->get_conf('NO_CSS')) {
7820    if ($element eq 'span') {
7821      return '';
7822    } else {
7823      return "<$element";
7824    }
7825  }
7826
7827  my $style = '';
7828
7829  if ($self->get_conf('INLINE_CSS_STYLE')
7830      and defined($self->{'css_map'}->{"$element.$class"})) {
7831    $style = ' style="'.$self->{'css_map'}->{"$element.$class"}.'"';
7832  }
7833  my $extra_class_str = '';
7834  if (defined($extra_classes)) {
7835    my $extra_class_conversion = join(' ', map {$self->_protect_class_name($_)} @$extra_classes);
7836    if ($extra_class_conversion ne '') {
7837      $extra_class_str = ' '.$extra_class_conversion;
7838    }
7839  }
7840  return "<$element class=\"$class$extra_class_str\"$style";
7841}
7842
7843sub _protect_space($$)
7844{
7845  my $self = shift;
7846  my $text = shift;
7847
7848  return $text if ($self->in_preformatted());
7849
7850  if ($self->in_space_protected()) {
7851    my $open = $self->_attribute_class('span', 'nolinebreak');
7852    if ($open ne '') {
7853      $open .= '>';
7854      # Protect spaces in the html leading attribute in case we are in 'w'
7855      $open =~ s/ /\x{1F}/g;
7856      # Special span to avoid breaking at _-
7857      $text =~ s/(\S*[_-]\S*)/${open}$1<\/span>/g;
7858    }
7859    $text .= '&nbsp;' if (chomp($text));
7860    # Protect spaces within text
7861    $text =~ s/ /&nbsp;/g;
7862    # Revert protected spaces in leading html attribute
7863    $text =~ s/\x{1F}/ /g;
7864  }
7865  return $text;
7866}
7867
7868# Convert tree element $ROOT, and return HTML text for the output files.
7869sub _convert($$;$);
7870
7871sub _convert($$;$)
7872{
7873  my $self = shift;
7874  my $root = shift;
7875  # only used for debug
7876  my $explanation = shift;
7877
7878  # to help debug and trace
7879  my $command_type = '';
7880  if ($root->{'cmdname'}) {
7881    $command_type = "\@$root->{'cmdname'} ";
7882  }
7883  if (defined($root->{'type'})) {
7884    $command_type .= $root->{'type'};
7885  }
7886
7887  if ($self->get_conf('DEBUG')) {
7888    $explanation = 'NO EXPLANATION' if (!defined($explanation));
7889    print STDERR "ROOT($explanation):$root (".join('|',@{$self->{'document_context'}->[-1]->{'formatting_context'}})."), ->";
7890    print STDERR " cmd: $root->{'cmdname'}," if ($root->{'cmdname'});
7891    print STDERR " type: $root->{'type'}" if ($root->{'type'});
7892    my $text = $root->{'text'};
7893    if (defined($text)) {
7894      $text =~ s/\n/\\n/;
7895      print STDERR " text: $text";
7896    }
7897    print STDERR "\n";
7898  }
7899
7900  if (ref($root) ne 'HASH') {
7901    cluck "_convert: root not a HASH\n";
7902    return '';
7903  }
7904
7905  if (($root->{'type'}
7906        and exists ($self->{'types_conversion'}->{$root->{'type'}})
7907        and !defined($self->{'types_conversion'}->{$root->{'type'}}))
7908       or ($root->{'cmdname'}
7909            and exists($self->{'commands_conversion'}->{$root->{'cmdname'}})
7910            and !defined($self->{'commands_conversion'}->{$root->{'cmdname'}}))) {
7911    if ($self->get_conf('DEBUG')) {
7912      my $string = 'IGNORED';
7913      $string .= " \@$root->{'cmdname'}" if ($root->{'cmdname'});
7914      $string .= " $root->{'type'}" if ($root->{'type'});
7915      print STDERR "$string\n";
7916    }
7917    return '';
7918  }
7919
7920  # Process text
7921  if (defined($root->{'text'})) {
7922    # already converted to html, keep it as is
7923    if ($root->{'type'} and $root->{'type'} eq '_converted') {
7924      return $root->{'text'};
7925    }
7926    if ($root->{'type'} and $root->{'type'} eq 'untranslated') {
7927      my $translated = $self->gdt($root->{'text'});
7928      my $result = $self->_convert($translated);
7929      return $result;
7930    }
7931    my $result = &{$self->{'types_conversion'}->{'text'}} ($self,
7932                                                      $root->{'type'},
7933                                                      $root,
7934                                                      $root->{'text'});
7935    print STDERR "DO TEXT => `$result'\n" if ($self->get_conf('DEBUG'));
7936    return $result;
7937  }
7938
7939  if ($root->{'extra'} and $root->{'extra'}->{'missing_argument'}
7940             and (!$root->{'contents'} or !@{$root->{'contents'}})) {
7941    print STDERR "MISSING_ARGUMENT\n" if ($self->get_conf('DEBUG'));
7942    return '';
7943  }
7944
7945  # commands like @deffnx have both a cmdname and a def_line type.  It is
7946  # better to consider them as a def_line type, as the whole point of the
7947  # def_line type is to handle the same the def*x and def* line formatting.
7948  if ($root->{'cmdname'}
7949      and !($root->{'type'} and $root->{'type'} eq 'def_line'
7950            or $root->{'type'} and $root->{'type'} eq 'definfoenclose_command')) {
7951    my $command_name = $root->{'cmdname'};
7952    # use the same command name for all the index entry commands
7953    if ($root->{'extra'} and $root->{'extra'}->{'index_entry'}
7954      and $root->{'cmdname'} and $root->{'cmdname'} =~ /index$/) {
7955      $command_name = 'cindex';
7956    }
7957    if ($root_commands{$command_name}) {
7958      $self->{'current_root_command'} = $root;
7959    }
7960    if (exists($self->{'commands_conversion'}->{$command_name})) {
7961      if (exists($context_brace_commands{$command_name})) {
7962        $self->_new_document_context($command_name);
7963      }
7964      push @{$self->{'document_context'}->[-1]->{'commands'}},
7965        $root->{'cmdname'};
7966      if (exists($format_context_commands{$command_name})) {
7967        push @{$self->{'document_context'}->[-1]->{'formatting_context'}},
7968                                              {'cmdname' => $command_name};
7969      }
7970      if (exists($block_commands{$command_name})) {
7971        push @{$self->{'document_context'}->[-1]->{'formats'}}, $command_name;
7972      }
7973      if (exists ($composition_context_commands{$command_name})) {
7974        push @{$self->{'document_context'}->[-1]->{'composition_context'}}, $command_name;
7975      }
7976      if ($pre_class_commands{$command_name}) {
7977        push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}},
7978          $pre_class_commands{$command_name};
7979      }
7980      if ($format_raw_commands{$command_name}) {
7981        $self->{'document_context'}->[-1]->{'raw'}++;
7982      } elsif ($command_name eq 'verb' or $command_name eq 'verbatim') {
7983        $self->{'document_context'}->[-1]->{'verbatim'}++;
7984      }
7985      if ($code_style_commands{$command_name} or
7986          $preformatted_code_commands{$command_name}) {
7987        push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
7988      } elsif ($regular_font_style_commands{$command_name}) {
7989        push @{$self->{'document_context'}->[-1]->{'monospace'}}, 0;
7990      } elsif ($upper_case_commands{$command_name}) {
7991        $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}++;
7992      } elsif ($math_commands{$command_name}) {
7993        $self->{'document_context'}->[-1]->{'math'}++;
7994      } elsif ($command_name eq 'w') {
7995        $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}++;
7996      }
7997      my $content_formatted;
7998      if ($root->{'contents'}) {
7999        $content_formatted = $self->_convert_contents($root, $command_type);
8000      }
8001      my $args_formatted;
8002      if ($brace_commands{$command_name}
8003          or ($misc_commands{$command_name}
8004              and $misc_commands{$command_name} eq 'line')
8005          or (($command_name eq 'item' or $command_name eq 'itemx')
8006               and ($root->{'parent'}->{'type'}
8007                    and $root->{'parent'}->{'type'} eq 'table_term'))
8008          or ($command_name eq 'quotation'
8009              or $command_name eq 'smallquotation')
8010              or ($command_name eq 'float')) {
8011        $args_formatted = [];
8012        if ($root->{'args'}) {
8013          my @args_specification;
8014          @args_specification = @{$self->{'commands_args'}->{$command_name}}
8015            if (defined($self->{'commands_args'}->{$command_name}));
8016          my $arg_idx = 0;
8017          foreach my $arg (@{$root->{'args'}}) {
8018            my $arg_spec = shift @args_specification;
8019            $arg_spec = ['normal'] if (!defined($arg_spec));
8020            my $arg_formatted = {'tree' => $arg};
8021            foreach my $arg_type (@$arg_spec) {
8022              my $explanation = "$command_type \[$arg_idx\]$arg_type";
8023              if ($arg_type eq 'normal') {
8024                $arg_formatted->{'normal'} = $self->_convert($arg, $explanation);
8025              } elsif ($arg_type eq 'monospace') {
8026                push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
8027                #$self->{'document_context'}->[-1]->{'code'}++;
8028                $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
8029                #$self->{'document_context'}->[-1]->{'code'}--;
8030                pop @{$self->{'document_context'}->[-1]->{'monospace'}};
8031              } elsif ($arg_type eq 'string') {
8032                $self->_new_document_context($command_type);
8033                $self->{'document_context'}->[-1]->{'string'}++;
8034                $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
8035                pop @{$self->{'document_context'}};
8036              } elsif ($arg_type eq 'monospacestring') {
8037                $self->_new_document_context($command_type);
8038                $self->{'document_context'}->[-1]->{'monospace'}->[-1] = 1;
8039                $self->{'document_context'}->[-1]->{'string'}++;
8040                $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
8041                pop @{$self->{'document_context'}};
8042              } elsif ($arg_type eq 'monospacetext') {
8043                $arg_formatted->{$arg_type}
8044                  = Texinfo::Convert::Text::convert($arg, {'code' => 1,
8045                            Texinfo::Common::_convert_text_options($self)});
8046              } elsif ($arg_type eq 'raw') {
8047                $self->{'document_context'}->[-1]->{'raw'}++;
8048                $arg_formatted->{$arg_type} = $self->_convert($arg, $explanation);
8049                $self->{'document_context'}->[-1]->{'raw'}--;
8050              }
8051            }
8052
8053            push @$args_formatted, $arg_formatted;
8054            $arg_idx++;
8055          }
8056        }
8057      }
8058      if (exists ($composition_context_commands{$command_name})) {
8059        pop @{$self->{'document_context'}->[-1]->{'composition_context'}};
8060      }
8061      if ($pre_class_commands{$command_name}) {
8062        pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
8063      }
8064      if ($code_style_commands{$command_name}
8065          or $preformatted_code_commands{$command_name}
8066          or $regular_font_style_commands{$command_name}) {
8067        #$self->{'document_context'}->[-1]->{'code'}--;
8068        pop @{$self->{'document_context'}->[-1]->{'monospace'}};
8069      } elsif ($upper_case_commands{$command_name}) {
8070        $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'upper_case'}--;
8071      } elsif ($math_commands{$command_name}) {
8072        $self->{'document_context'}->[-1]->{'math'}--;
8073      } elsif ($command_name eq 'w') {
8074        $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'space_protected'}--;
8075      }
8076      if ($format_raw_commands{$command_name}) {
8077        $self->{'document_context'}->[-1]->{'raw'}--;
8078      } elsif ($command_name eq 'verb' or $command_name eq 'verbatim') {
8079        $self->{'document_context'}->[-1]->{'verbatim'}--;
8080      }
8081      if (exists($block_commands{$command_name})) {
8082        pop @{$self->{'document_context'}->[-1]->{'formats'}};
8083      }
8084      if (exists($format_context_commands{$command_name})) {
8085        pop @{$self->{'document_context'}->[-1]->{'formatting_context'}};
8086      }
8087      pop @{$self->{'document_context'}->[-1]->{'commands'}};
8088      if (exists($context_brace_commands{$command_name})) {
8089        pop @{$self->{'document_context'}};
8090      }
8091
8092      if ($root->{'cmdname'} eq 'node') {
8093        $self->{'current_node'} = $root;
8094      }
8095      elsif ($root->{'cmdname'} eq 'menu' and $self->{'current_node'}) {
8096        $self->{'seenmenus'}->{$self->{'current_node'}} = 1;
8097      }
8098      # args are formatted, now format the command itself
8099      my $result;
8100      if ($args_formatted) {
8101        if (!defined($self->{'commands_conversion'}->{$command_name})) {
8102          print STDERR "No command_conversion for $command_name\n";
8103          $result = '';
8104        } else {
8105          $result = &{$self->{'commands_conversion'}->{$command_name}}($self,
8106                  $command_name, $root, $args_formatted, $content_formatted);
8107        }
8108      } else {
8109        $result = &{$self->{'commands_conversion'}->{$command_name}}($self,
8110                $command_name, $root, $content_formatted);
8111      }
8112      return $result;
8113    } else {
8114      print STDERR "Unknown command `$command_name'\n"
8115       if ($self->get_conf('VERBOSE') or $self->get_conf('DEBUG'));
8116      return '';
8117    }
8118    if ($root_commands{$command_name}) {
8119      delete $self->{'current_root_command'};
8120    }
8121  } elsif ($root->{'type'}) {
8122    push @{$self->{'document_context'}->[-1]->{'commands'}},
8123      $root->{'cmdname'}
8124        if ($root->{'cmdname'});
8125
8126    if ($root->{'type'} eq 'paragraph') {
8127      $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'paragraph_number'}++;
8128    } elsif ($root->{'type'} eq 'preformatted'
8129             or $root->{'type'} eq 'rawpreformatted') {
8130      $self->{'document_context'}->[-1]->{'formatting_context'}->[-1]->{'preformatted_number'}++;
8131    } elsif ($root->{'type'} eq 'element') {
8132      $self->{'current_element'} = $root;
8133      $self->{'current_filename'} = $root->{'filename'};
8134    } elsif ($pre_class_types{$root->{'type'}}) {
8135      push @{$self->{'document_context'}->[-1]->{'preformatted_classes'}},
8136        $pre_class_types{$root->{'type'}};
8137      push @{$self->{'document_context'}->[-1]->{'composition_context'}},
8138        $root->{'type'};
8139    }
8140
8141    if ($self->{'code_types'}->{$root->{'type'}}) {
8142      #$self->{'document_context'}->[-1]->{'code'}++;
8143      push @{$self->{'document_context'}->[-1]->{'monospace'}}, 1;
8144    }
8145    if ($root->{'type'} eq '_string') {
8146      $self->{'document_context'}->[-1]->{'string'}++;
8147    }
8148
8149    my $content_formatted;
8150    if ($root->{'type'} eq 'definfoenclose_command') {
8151      if ($root->{'args'}) {
8152        $content_formatted = $self->_convert($root->{'args'}->[0]);
8153      }
8154    } elsif ($root->{'contents'}) {
8155      $content_formatted = $self->_convert_contents($root, $command_type);
8156    }
8157
8158    my $result = '';
8159    if (exists($self->{'types_conversion'}->{$root->{'type'}})) {
8160      $result = &{$self->{'types_conversion'}->{$root->{'type'}}} ($self,
8161                                                 $root->{'type'},
8162                                                 $root,
8163                                                 $content_formatted);
8164      #print STDERR "Converting type $root->{'type'} -> $result\n";
8165    } elsif (defined($content_formatted)) {
8166      $result = $content_formatted;
8167    }
8168    if ($self->{'code_types'}->{$root->{'type'}}) {
8169      #$self->{'document_context'}->[-1]->{'code'}--;
8170      pop @{$self->{'document_context'}->[-1]->{'monospace'}};
8171    }
8172    if ($root->{'type'} eq '_string') {
8173      $self->{'document_context'}->[-1]->{'string'}--;
8174    }
8175    if ($root->{'type'} eq 'element') {
8176      delete $self->{'current_element'};
8177      delete $self->{'current_filename'};
8178    } elsif ($pre_class_types{$root->{'type'}}) {
8179      pop @{$self->{'document_context'}->[-1]->{'preformatted_classes'}};
8180      pop @{$self->{'document_context'}->[-1]->{'composition_context'}};
8181    }
8182    print STDERR "DO type ($root->{'type'}) => `$result'\n"
8183      if ($self->get_conf('DEBUG'));
8184    pop @{$self->{'document_context'}->[-1]->{'commands'}}
8185        if ($root->{'cmdname'});
8186    return $result;
8187    # no type, no cmdname, but contents.
8188  } elsif ($root->{'contents'}) {
8189    # this happens inside accents, for section/node names, for @images.
8190    my $content_formatted = '';
8191    my $i = 0;
8192    foreach my $content (@{$root->{'contents'}}) {
8193      $content_formatted .= $self->_convert($content, "$command_type [$i]");
8194      $i++;
8195    }
8196    print STDERR "UNNAMED HOLDER => `$content_formatted'\n"
8197      if ($self->get_conf('DEBUG'));
8198    return $content_formatted;
8199  } else {
8200    print STDERR "UNNAMED empty\n" if ($self->get_conf('DEBUG'));
8201    if ($self->{'types_conversion'}->{''}) {
8202      return &{$self->{'types_conversion'}->{''}} ($self, $root);
8203    } else {
8204      return '';
8205    }
8206  }
8207  print STDERR "DEBUG: HERE!($root)\n";
8208}
8209
8210sub _set_variables_texi2html()
8211{
8212  my @texi2html_options = (
8213  ['FORMAT_MENU', 'menu'],
8214  ['NO_USE_SETFILENAME', 1],
8215  ['USE_SETFILENAME_EXTENSION', 0],
8216  ['footnotestyle', 'separate'],
8217  ['CONTENTS_OUTPUT_LOCATION', 'separate_element'],
8218  ['FORCE', 1],
8219  ['AVOID_MENU_REDUNDANCY', 1],
8220  ['USE_ACCESSKEY', 0],
8221  ['NODE_NAME_IN_MENU', 0],
8222  ['OVERVIEW_LINK_TO_TOC', 0],
8223  ['USE_UP_NODE_FOR_ELEMENT_UP', 1],
8224  ['USE_REL_REV', 0],
8225  ['USE_LINKS', 0],
8226  ['USE_NODES', 0],
8227  ['USE_NUMERIC_ENTITY', 1],
8228  ['SPLIT', ''],
8229  ['PROGRAM_NAME_IN_FOOTER', 1],
8230  ['HEADER_IN_TABLE', 1],
8231  ['USE_TITLEPAGE_FOR_TITLE', 1],
8232  ['MENU_ENTRY_COLON', ''],
8233  ['INDEX_ENTRY_COLON', ''],
8234  ['ENABLE_ENCODING_USE_ENTITY', 1],
8235  ['DO_ABOUT', undef],
8236  ['NODE_NAME_IN_INDEX', 0],
8237  ['CHAPTER_HEADER_LEVEL', 1],
8238  ['BIG_RULE', '<hr size="6">'],
8239  ['FOOTNOTE_END_HEADER_LEVEL', 3],
8240  ['FOOTNOTE_SEPARATE_HEADER_LEVEL', 1],
8241  ['KEEP_TOP_EXTERNAL_REF', 1],
8242  ['SECTION_BUTTONS', ['FastBack', 'Back', 'Up', 'Forward', 'FastForward',
8243                             ' ', ' ', ' ', ' ',
8244                             'Top', 'Contents', 'Index', 'About' ]],
8245  ['TOP_BUTTONS', ['Back', 'Forward', ' ',
8246                             'Contents', 'Index', 'About']],
8247
8248  ['MISC_BUTTONS', [ 'Top', 'Contents', 'Index', 'About' ]],
8249  ['CHAPTER_BUTTONS', [ 'FastBack', 'FastForward', ' ',
8250                              ' ', ' ', ' ', ' ',
8251                              'Top', 'Contents', 'Index', 'About', ]],
8252  ['SECTION_FOOTER_BUTTONS', [ 'FastBack', 'FirstInFileBack', 'FirstInFileUp',
8253                                               'Forward', 'FastForward' ]],
8254  ['CHAPTER_FOOTER_BUTTONS', [ 'FastBack', 'FastForward', ' ',
8255                              ' ', ' ', ' ', ' ',
8256                              'Top', 'Contents', 'Index', 'About', ]],
8257  ['NODE_FOOTER_BUTTONS', [ 'FastBack', 'Back',
8258                                            'Up', 'Forward', 'FastForward',
8259                             ' ', ' ', ' ', ' ',
8260                             'Top', 'Contents', 'Index', 'About' ]],
8261  );
8262  foreach my $option (@texi2html_options) {
8263    #no warnings 'once';
8264    $defaults{$option->[0]} = $option->[1];
8265  }
8266}
8267
82681;
8269
8270__END__
8271# $Id$
8272# Automatically generated from maintain/template.pod
8273
8274=head1 NAME
8275
8276Texinfo::Convert::HTML - Convert Texinfo tree to HTML
8277
8278=head1 SYNOPSIS
8279
8280  my $converter
8281    = Texinfo::Convert::HTML->converter({'parser' => $parser});
8282
8283  $converter->output($tree);
8284  $converter->convert($tree);
8285  $converter->convert_tree($tree);
8286  $converter->output_internal_links(); # HTML only
8287
8288=head1 DESCRIPTION
8289
8290Texinfo::Convert::HTML converts a Texinfo tree to HTML.
8291
8292=head1 METHODS
8293
8294=over
8295
8296=item $converter = Texinfo::Convert::HTML->converter($options)
8297
8298Initialize converter from Texinfo to HTML.
8299
8300The I<$options> hash reference holds options for the converter.  In
8301this option hash reference a parser object may be associated with the
8302I<parser> key.  The other options should be configuration options
8303described in the Texinfo manual.  Those options, when appropriate,
8304override the document content.
8305
8306See L<Texinfo::Convert::Converter> for more informations.
8307
8308=item $converter->output($tree)
8309
8310Convert a Texinfo tree I<$tree> and output the result in files as
8311described in the Texinfo manual.
8312
8313=item $result = $converter->convert($tree)
8314
8315Convert a Texinfo tree I<$tree> or tree portion and return
8316the resulting output.
8317
8318=item $result = $converter->convert_tree($tree)
8319
8320Convert a Texinfo tree portion I<$tree> and return the resulting
8321output.  This function does not try to output a full document but only
8322portions.  For a full document use C<convert>.
8323
8324=item $result = $converter->output_internal_links()
8325
8326Returns text representing the links in the document.  The format should
8327follow the C<--internal-links> option of the texi2any/makeinfo
8328specification.  This is only supported in (and relevant for) HTML.
8329
8330=back
8331
8332=head1 AUTHOR
8333
8334Patrice Dumas, E<lt>pertusus@free.frE<gt>
8335
8336=cut
8337