1# Structuring.pm: extract informations about a document structure based on the
2#                 document tree.
3#
4# Copyright 2010-2019 Free Software Foundation, Inc.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 3 of the License,
9# or (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18#
19# Original author: Patrice Dumas <pertusus@free.fr>
20# Parts (also from Patrice Dumas) come from texi2html.pl.
21
22package Texinfo::Structuring;
23
24use 5.00405;
25
26# See comment at start of HTML.pm
27use if $] >= 5.012, feature => 'unicode_strings';
28
29use strict;
30
31use Texinfo::Common;
32
33# for debugging.  Also for index entries sorting.
34use Texinfo::Convert::Text;
35# for error messages
36use Texinfo::Convert::Texinfo;
37
38*node_extra_to_texi = \&Texinfo::Convert::Texinfo::node_extra_to_texi;
39
40use Carp qw(cluck);
41
42require Exporter;
43use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
44@ISA = qw(Exporter);
45
46%EXPORT_TAGS = ( 'all' => [ qw(
47  associate_internal_references
48  elements_directions
49  elements_file_directions
50  merge_indices
51  nodes_tree
52  number_floats
53  sectioning_structure
54  set_menus_node_directions
55  sort_indices
56  sort_indices_by_letter
57  split_by_node
58  split_by_section
59  split_pages
60  warn_non_empty_parts
61) ] );
62
63@EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
64
65@EXPORT = qw(
66);
67
68$VERSION = '6.8';
69
70
71my %types_to_enter;
72foreach my $type_to_enter ('brace_command_arg', 'line_arg',
73    'paragraph') {
74  $types_to_enter{$type_to_enter} = 1;
75}
76
77
78my %command_structuring_level = %Texinfo::Common::command_structuring_level;
79
80my %appendix_commands;
81my %unnumbered_commands;
82foreach my $command (keys(%command_structuring_level)) {
83  if ($command =~ /appendix/) {
84    $appendix_commands{$command} = 1;
85  } elsif ($command =~ /unnumbered/) {
86    $unnumbered_commands{$command} = 1;
87  }
88}
89$unnumbered_commands{'top'} = 1;
90$unnumbered_commands{'centerchap'} = 1;
91$unnumbered_commands{'part'} = 1;
92
93my $min_level = $command_structuring_level{'chapter'};
94my $max_level = $command_structuring_level{'subsubsection'};
95
96# Return numbered level of an element
97sub section_level($)
98{
99  my $section = shift;
100  my $level = $command_structuring_level{$section->{'cmdname'}};
101  # correct level according to raise/lowersections
102  if ($section->{'extra'} and $section->{'extra'}->{'sections_level'}) {
103    $level -= $section->{'extra'}->{'sections_level'};
104    if ($level < $min_level) {
105      if ($command_structuring_level{$section->{'cmdname'}} < $min_level) {
106        $level = $command_structuring_level{$section->{'cmdname'}};
107      } else {
108        $level = $min_level;
109      }
110    } elsif ($level > $max_level) {
111      $level = $max_level;
112    }
113  }
114  return $level;
115}
116
117
118# Go through the sectioning commands (e.g. @chapter, not @node), and
119# set:
120# 'number'
121# 'section_childs'
122# 'section_up'
123# 'section_prev'
124# 'section_next'
125# 'toplevel_next'
126# 'toplevel_prev'
127# 'toplevel_up'
128sub sectioning_structure($$)
129{
130  my $self = shift;
131  my $root = shift;
132  if (!$root->{'type'} or $root->{'type'} ne 'document_root'
133      or !$root->{'contents'}) {
134    return undef;
135  }
136
137  my $sec_root = {};
138  my $previous_section;
139  my $previous_toplevel;
140
141  my $in_appendix = 0;
142  # lowest level with a number.  This is the lowest level above 0.
143  my $number_top_level;
144
145  my $section_top;
146  my @sections_list;
147
148  # holds the current number for all the levels.  It is not possible to use
149  # something like the last child index, because of @unnumbered.
150  my @command_numbers;
151  # keep track of the unnumbered
152  my @command_unnumbered;
153  foreach my $content (@{$root->{'contents'}}) {
154    if (!$content->{'cmdname'} or $content->{'cmdname'} eq 'node'
155        or $content->{'cmdname'} eq 'bye') {
156      next;
157    }
158    push @sections_list, $content;
159    if ($content->{'cmdname'} eq 'top') {
160      if (! $section_top) {
161        $section_top = $content;
162      }
163    }
164    my $level;
165    $level = $content->{'level'} = section_level($content);
166    if (!defined($level)) {
167      warn "bug: level not defined for $content->{'cmdname'}\n";
168      $level = $content->{'level'} = 0;
169    }
170
171    if ($previous_section) {
172      # new command is below
173      if ($previous_section->{'level'} < $level) {
174        if ($level - $previous_section->{'level'} > 1) {
175          $self->line_error(sprintf(
176              __("raising the section level of \@%s which is too low"),
177              $content->{'cmdname'}), $content->{'line_nr'});
178          $content->{'level'} = $previous_section->{'level'} + 1;
179        }
180        $previous_section->{'section_childs'} = [$content];
181        $content->{'section_up'} = $previous_section;
182
183        # if the up is unnumbered, the number information has to be kept,
184        # to avoid reusing an already used number.
185        if (!$unnumbered_commands{$previous_section->{'cmdname'}}) {
186          $command_numbers[$content->{'level'}] = undef;
187        } elsif (!$unnumbered_commands{$content->{'cmdname'}}) {
188          $command_numbers[$content->{'level'}]++;
189        }
190        if ($unnumbered_commands{$content->{'cmdname'}}) {
191          $command_unnumbered[$content->{'level'}] = 1;
192        } else {
193          $command_unnumbered[$content->{'level'}] = 0;
194        }
195      } else {
196        my $up = $previous_section->{'section_up'};
197        my $new_upper_part_element;
198        if ($previous_section->{'level'} != $level) {
199          # means it is above the previous command, the up is to be found
200          while ($up->{'section_up'} and $up->{'level'} >= $level) {
201            $up = $up->{'section_up'};
202          }
203          if ($level <= $up->{'level'}) {
204            if ($content->{'cmdname'} eq 'part') {
205              $new_upper_part_element = 1;
206              if ($level < $up->{'level'}) {
207                $self->line_warn(sprintf(__(
208                      "no chapter-level command before \@%s"),
209                    $content->{'cmdname'}), $content->{'line_nr'});
210              }
211            } else {
212              $self->line_warn(sprintf(__(
213                    "lowering the section level of \@%s appearing after a lower element"),
214                  $content->{'cmdname'}), $content->{'line_nr'});
215              $content->{'level'} = $up->{'level'} + 1;
216            }
217          }
218        }
219        if ($appendix_commands{$content->{'cmdname'}} and !$in_appendix
220            and $content->{'level'} <= $number_top_level
221            and $up->{'cmdname'} and $up->{'cmdname'} eq 'part') {
222          $up = $up->{'section_up'};
223        }
224        if ($new_upper_part_element) {
225          # In that case the root has to be updated because the first
226          # 'part' just appeared
227          $content->{'section_up'} = $sec_root;
228          $sec_root->{'level'} = $level - 1;
229          push @{$sec_root->{'section_childs'}}, $content;
230          $number_top_level = $level;
231          $number_top_level++ if (!$number_top_level);
232        } else {
233          push @{$up->{'section_childs'}}, $content;
234          $content->{'section_up'} = $up;
235          $content->{'section_prev'} = $up->{'section_childs'}->[-2];
236          $content->{'section_prev'}->{'section_next'} = $content;
237        }
238        if (!$unnumbered_commands{$content->{'cmdname'}}) {
239          $command_numbers[$content->{'level'}]++;
240          $command_unnumbered[$content->{'level'}] = 0;
241        } else {
242          $command_unnumbered[$content->{'level'}] = 1;
243        }
244      }
245    } else { # first section determines the level of the root.  It is
246      # typically -1 when there is a @top.
247      $content->{'section_up'} = $sec_root;
248      $sec_root->{'level'} = $level - 1;
249      $sec_root->{'section_childs'} = [$content];
250      $number_top_level = $level;
251      # if $level of top sectioning element is 0, which means that
252      # it is a @top, $number_top_level is 1 as it is associated to
253      # the level of chapter/unnumbered...
254      $number_top_level++ if (!$number_top_level);
255      if ($content->{'cmdname'} ne 'top') {
256        if (!$unnumbered_commands{$content->{'cmdname'}}) {
257          $command_unnumbered[$content->{'level'}] = 0;
258        } else {
259          $command_unnumbered[$content->{'level'}] = 1;
260        }
261      }
262    }
263    if (!defined($command_numbers[$content->{'level'}])) {
264      if ($unnumbered_commands{$content->{'cmdname'}}) {
265        $command_numbers[$content->{'level'}] = 0;
266      } else {
267        $command_numbers[$content->{'level'}] = 1;
268      }
269    }
270    if ($appendix_commands{$content->{'cmdname'}} and !$in_appendix) {
271      $in_appendix = 1;
272      $command_numbers[$content->{'level'}] = 'A';
273    }
274    if (!$unnumbered_commands{$content->{'cmdname'}}) {
275      # construct the number, if not below an unnumbered
276      if (!$command_unnumbered[$number_top_level]) {
277        $content->{'number'} = $command_numbers[$number_top_level];
278        for (my $i = $number_top_level+1; $i <= $content->{'level'}; $i++) {
279          $content->{'number'} .= ".$command_numbers[$i]";
280          # If there is an unnumbered above, then no number is added.
281          if ($command_unnumbered[$i]) {
282            delete $content->{'number'};
283            last;
284          }
285        }
286      }
287    }
288    $previous_section = $content;
289    if ($content->{'cmdname'} ne 'part'
290        and $content->{'level'} <= $number_top_level) {
291      if ($previous_toplevel) {
292        $previous_toplevel->{'toplevel_next'} = $content;
293        $content->{'toplevel_prev'} = $previous_toplevel;
294      }
295      $previous_toplevel = $content;
296      if ($section_top and $content ne $section_top) {
297        $content->{'toplevel_up'} = $section_top;
298      }
299    } elsif ($content->{'cmdname'} eq 'part'
300        and !$content->{'extra'}->{'part_associated_section'}) {
301      $self->line_warn(sprintf(__(
302            "no sectioning command associated with \@%s"),
303          $content->{'cmdname'}), $content->{'line_nr'});
304    }
305  }
306  $self->{'structuring'}->{'sectioning_root'} = $sec_root;
307  $self->{'structuring'}->{'sections_list'} = \@sections_list;
308  return $sec_root;
309}
310
311sub _print_sectioning_tree($);
312sub _print_sectioning_tree($)
313{
314  my $current = shift;
315  my $result = ' ' x $current->{'level'} . _print_root_command_texi($current)."\n";
316  foreach my $child (@{$current->{'section_childs'}}) {
317    $result .= _print_sectioning_tree($child);
318  }
319  return $result;
320}
321
322
323sub warn_non_empty_parts($)
324{
325  my $self = shift;
326  my $global_commands = $self->global_commands_information();
327  if ($global_commands->{'part'}) {
328    foreach my $part (@{$global_commands->{'part'}}) {
329      if (!Texinfo::Common::is_content_empty($part)) {
330        $self->line_warn(sprintf(__("\@%s not empty"),
331                         $part->{'cmdname'}), $part->{'line_nr'});
332      }
333    }
334  }
335}
336
337sub _check_node_same_texinfo_code($$)
338{
339  my $reference_node = shift;
340  my $node_extra = shift;
341
342  my $reference_node_texi;
343  if ($reference_node->{'extra'}->{'node_content'}) {
344    $reference_node_texi = Texinfo::Convert::Texinfo::convert (
345        {'contents' => $reference_node->{'extra'}->{'node_content'}});
346    $reference_node_texi =~ s/\s+/ /g;
347  } else {
348    $reference_node_texi = '';
349  }
350
351  my $node_texi;
352  if ($node_extra and $node_extra->{'node_content'}) {
353    my @contents_node = @{$node_extra->{'node_content'}};
354    pop @contents_node if ($contents_node[-1]->{'type'}
355               and $contents_node[-1]->{'type'} eq 'space_at_end_menu_node');
356    $node_texi = Texinfo::Convert::Texinfo::convert (
357      {'contents' => \@contents_node});
358    $node_texi =~ s/\s+/ /g;
359  } else {
360    $node_texi = '';
361  }
362  return ($reference_node_texi eq $node_texi);
363}
364
365
366my @node_directions = ('next', 'prev', 'up');
367# No translation of those special Info keywords.
368my %direction_texts = (
369 'prev' => 'Prev',
370 'next' => 'Next',
371 'up' => 'Up'
372);
373
374sub _check_menu_entry($$$)
375{
376  my $self = shift;
377  my $command = shift;
378  my $menu_content = shift;
379
380  my $normalized_menu_node
381      = $menu_content->{'extra'}->{'menu_entry_node'}->{'normalized'};
382
383  my $menu_node = $self->{'labels'}->{$normalized_menu_node};
384
385  if (!$menu_node) {
386    $self->line_error(sprintf(
387     __("\@%s reference to nonexistent node `%s'"), $command,
388        node_extra_to_texi($menu_content->{'extra'}->{'menu_entry_node'})),
389     $menu_content->{'line_nr'});
390  } else {
391    if (!_check_node_same_texinfo_code($menu_node,
392                           $menu_content->{'extra'}->{'menu_entry_node'})) {
393      $self->line_warn(sprintf(
394       __("\@%s entry node name `%s' different from %s name `%s'"),
395         $command,
396         node_extra_to_texi($menu_content->{'extra'}->{'menu_entry_node'}),
397         $menu_node->{'cmdname'},
398         node_extra_to_texi($menu_node->{'extra'})),
399                            $menu_content->{'line_nr'});
400    }
401  }
402}
403
404sub _check_referenced_nodes
405{
406  my ($self, $top_node) = @_;
407
408  my %referenced_nodes = ($top_node => 1);
409  foreach my $node (@{$self->{'nodes'}}) {
410    # gather referenced nodes based on node pointers
411    foreach my $direction (@node_directions) {
412      if ($node->{'node_'.$direction}
413          and not $node->{'node_'.$direction}->{'extra'}->{'manual_content'}) {
414        $referenced_nodes{$node->{'node_'.$direction}} = 1;
415      }
416    }
417    if ($node->{'menu_up_hash'}) {
418      $referenced_nodes{$node} = 1;
419    }
420  }
421
422  # consider nodes in @*ref commands to be referenced
423  my $labels = $self->labels_information();
424  my $refs = $self->internal_references_information();
425  if (defined($refs)) {
426    foreach my $ref (@$refs) {
427      my $node_arg = $ref->{'extra'}{'node_argument'};
428      if ($node_arg->{'node_content'}) {
429        my $normalized =
430           Texinfo::Convert::NodeNameNormalization::normalize_node(
431              {'contents' => $node_arg->{'node_content'} });
432        my $node_target = $labels->{$normalized};
433        if ($node_target) {
434          $referenced_nodes{$node_target} = 1;
435        }
436      }
437    }
438  }
439
440  foreach my $node (@{$self->{'nodes'}}) {
441    if (not exists($referenced_nodes{$node})) {
442      $self->line_warn(sprintf(__("node `%s' unreferenced"),
443          node_extra_to_texi($node->{'extra'})),
444           $node->{'line_nr'});
445    }
446  }
447}
448
449# set menu directions
450sub set_menus_node_directions($)
451{
452  my $self = shift;
453  return undef unless ($self->{'nodes'} and @{$self->{'nodes'}});
454
455  my $check_menu_entries = (!$self->{'info'}->{'novalidate'}
456                      and $self->get_conf('FORMAT_MENU') eq 'menu');
457
458  # First go through all the menus and set menu_up, menu_next and menu_prev,
459  # and warn for unknown nodes.
460  # Remark: since the @menu are only checked if they are in @node,
461  # menu entries before the first node, or @menu nested inside
462  # another command such as @format, may be treated slightly
463  # differently; at least, there are no error messages for them.
464  #
465  foreach my $node (@{$self->{'nodes'}}) {
466    if ($node->{'menus'}) {
467      if (@{$node->{'menus'}} > 1) {
468        foreach my $menu (@{$node->{'menus'}}[1 .. $#{$node->{'menus'}}]) {
469          $self->line_warn(sprintf(__("multiple \@%s"),
470                        $menu->{'cmdname'}), $menu->{'line_nr'});
471        }
472      }
473      foreach my $menu (@{$node->{'menus'}}) {
474        my $previous_node;
475        foreach my $menu_content (@{$menu->{'contents'}}) {
476          if ($menu_content->{'extra'}
477             and $menu_content->{'extra'}->{'menu_entry_node'}) {
478            my $menu_node;
479            my $external_node;
480            if (!$menu_content->{'extra'}->{'menu_entry_node'}->{'manual_content'}) {
481              $menu_node = $self->{'labels'}->{
482                      $menu_content->{'extra'}
483                                   ->{'menu_entry_node'}->{'normalized'}};
484
485              if ($check_menu_entries) {
486                _check_menu_entry($self, 'menu', $menu_content);
487              }
488              # this may happen more than once for a given node if the node
489              # is in more than one menu.  Therefore all the menu up node
490              # are kept in $menu_node->{'menu_up_hash'}
491              if ($menu_node) {
492                $menu_node->{'menu_up'} = $node;
493                $menu_node->{'menu_up_hash'}->{$node->{'extra'}->{'normalized'}} = 1;
494              }
495            } else {
496              $external_node = 1;
497              $menu_node = {'extra' => $menu_content->{'extra'}->{'menu_entry_node'}};
498            }
499            if ($menu_node) {
500              if ($previous_node) {
501                if (!$external_node) {
502                  $menu_node->{'menu_prev'} = $previous_node;
503                }
504                if (!$previous_node->{'extra'}->{'manual_content'}) {
505                  $previous_node->{'menu_next'} = $menu_node;
506                }
507              } else {
508                $node->{'menu_child'} = $menu_node;
509              }
510              $previous_node = $menu_node;
511            }
512          }
513        } # end menu
514      }
515    }
516  }
517  # Check @detailmenu
518  if ($check_menu_entries) {
519    my $global_commands = $self->global_commands_information();
520    if ($global_commands->{'detailmenu'}) {
521      foreach my $detailmenu (@{$global_commands->{'detailmenu'}}) {
522        foreach my $menu_content (@{$detailmenu->{'contents'}}) {
523          if ($menu_content->{'extra'}
524             and $menu_content->{'extra'}->{'menu_entry_node'}) {
525            if (!$menu_content->{'extra'}->{'menu_entry_node'}->{'manual_content'}) {
526              _check_menu_entry($self, 'detailmenu', $menu_content);
527            }
528          }
529        }
530      }
531    }
532  }
533}
534
535# determine node found through section directions, usually
536# from section_$direction.  It could also be from
537# toplevel_$direction if going through parts, except for @top
538# as prev or next.
539sub _section_direction_associated_node($$)
540{
541  my $section = shift;
542  my $direction = shift;
543
544  foreach my $direction_base ('section', 'toplevel') {
545    if ($section->{$direction_base.'_'.$direction}
546       and $section->{$direction_base.'_'.$direction}->{'extra'}
547       and ($direction_base ne 'toplevel'
548            or $direction eq 'up'
549            or $section->{$direction_base.'_'.$direction}->{'cmdname'} ne 'top')
550       and $section->{$direction_base.'_'.$direction}->{'extra'}->{'associated_node'}) {
551         return $section->{$direction_base.'_'.$direction}->{'extra'}->{'associated_node'};
552    }
553  }
554  return undef;
555}
556
557# complete automatic directions with menus (and first node
558# for Top node).
559# Checks on structure related to menus.
560sub complete_node_tree_with_menus($$)
561{
562  my $self = shift;
563  my $top_node = shift;
564
565  return undef unless ($self->{'nodes'} and @{$self->{'nodes'}});
566  # Go through all the nodes
567  foreach my $node (@{$self->{'nodes'}}) {
568    my $automatic_directions =
569      (scalar(@{$node->{'extra'}->{'nodes_manuals'}}) == 1);
570
571    if ($automatic_directions) {
572      if ($node->{'extra'}->{'normalized'} ne 'Top') {
573        foreach my $direction (@node_directions) {
574          # prev already defined for the node first Top node menu entry
575          if ($direction eq 'prev' and $node->{'node_'.$direction}
576              and $node->{'node_'.$direction}->{'extra'}
577              and $node->{'node_'.$direction}->{'extra'}->{'normalized'}
578              and $node->{'node_'.$direction}->{'extra'}->{'normalized'} eq 'Top') {
579            next;
580          }
581          if ($node->{'extra'}->{'associated_section'}) {
582            my $section = $node->{'extra'}->{'associated_section'};
583
584            # Prefer the section associated with a @part for node directions.
585            if ($section->{'extra'}->{'part_associated_section'}) {
586              $section = $section->{'extra'}->{'part_associated_section'};
587            }
588            # Check consistency with section and menu structure
589            my $direction_associated_node
590              = _section_direction_associated_node($section, $direction);
591            if ($direction_associated_node) {
592              if ($self->get_conf('CHECK_NORMAL_MENU_STRUCTURE')) {
593                if ($section->{'section_up'}{'extra'}
594          and $section->{'section_up'}{'extra'}{'associated_node'}
595          and $section->{'section_up'}{'extra'}{'associated_node'}{'menus'}
596          and @{$section->{'section_up'}{'extra'}{'associated_node'}{'menus'}}
597                    and !$node->{'menu_'.$direction}) {
598                  $self->line_warn(sprintf(
599               __("node %s for `%s' is `%s' in sectioning but not in menu"),
600                  $direction,
601                  node_extra_to_texi($node->{'extra'}),
602                  node_extra_to_texi($direction_associated_node->{'extra'})),
603                    $node->{'line_nr'});
604                }
605              }
606            }
607          }
608          # no direction was found using sections, use menus.  This allows
609          # using only automatic direction for manuals without sectioning
610          # commands.
611          if (!$node->{'node_'.$direction}
612              and $node->{'menu_'.$direction}
613              and !$node->{'menu_'.$direction}->{'extra'}->{'manual_content'}) {
614            if ($self->get_conf('CHECK_NORMAL_MENU_STRUCTURE')
615                  and $node->{'extra'}->{'associated_section'}) {
616              $self->line_warn(sprintf(
617                  __("node `%s' is %s for `%s' in menu but not in sectioning"),
618                node_extra_to_texi($node->{'menu_'.$direction}->{'extra'}),
619                                   $direction,
620                node_extra_to_texi($node->{'extra'}),
621                  ),
622                $node->{'line_nr'});
623            }
624            $node->{'node_'.$direction} = $node->{'menu_'.$direction};
625          }
626        }
627      } elsif (not $node->{'node_next'}) {
628        # use first menu entry if available as next for Top
629        if ($node->{'menu_child'}) {
630          $node->{'node_next'} = $node->{'menu_child'};
631          if (!$node->{'menu_child'}->{'extra'}->{'manual_content'}
632              and !$node->{'menu_child'}->{'node_prev'}) {
633            $node->{'menu_child'}->{'node_prev'} = $node;
634          }
635        } else {
636          # use the first non top node as next for Top
637          foreach my $first_non_top_node (@{$self->{'nodes'}}) {
638            if ($first_non_top_node ne $node) {
639              $node->{'node_next'} = $first_non_top_node;
640              if (scalar(@{$first_non_top_node->{'extra'}->{'nodes_manuals'}}) == 1) {
641                $first_non_top_node->{'node_prev'} = $node;
642              }
643              last;
644            }
645          }
646        }
647      }
648    }
649    # check consistency between node pointer and node entries menu order
650    if ($node->{'extra'}->{'normalized'} ne 'Top') {
651      foreach my $direction (@node_directions) {
652        if ($self->get_conf('CHECK_NORMAL_MENU_STRUCTURE')
653            and $node->{'node_'.$direction}
654            and $node->{'menu_'.$direction}
655            and $node->{'menu_'.$direction}
656               ne $node->{'node_'.$direction}
657            and not $node->{'menu_'.$direction}->{'extra'}->{'manual_content'}) {
658          $self->line_warn(sprintf(
659           __("node %s pointer for `%s' is `%s' but %s is `%s' in menu"),
660                  $direction,
661                  node_extra_to_texi($node->{'extra'}),
662                  node_extra_to_texi($node->{'node_'.$direction}->{'extra'}),
663                  $direction,
664                  node_extra_to_texi($node->{'menu_'.$direction}->{'extra'})),
665                 $node->{'line_nr'});
666        }
667      }
668    }
669
670    # check for node up / menu up mismatch
671    if ($self->get_conf('CHECK_NORMAL_MENU_STRUCTURE')
672        and $node->{'node_up'}
673        # No check if node up is an external manual
674        and (!$node->{'node_up'}->{'extra'}->{'manual_content'})
675        and (!$node->{'menu_up_hash'}
676          or !$node->{'menu_up_hash'}->{$node->{'node_up'}->{'extra'}->{'normalized'}})) {
677      # check if up node has a menu
678      if ($node->{'node_up'}->{'menus'} and @{$node->{'node_up'}->{'menus'}}) {
679        $self->line_warn(sprintf(
680           __("node `%s' lacks menu item for `%s' despite being its Up target"),
681           node_extra_to_texi($node->{'node_up'}->{'extra'}),
682           node_extra_to_texi($node->{'extra'})),
683           $node->{'node_up'}->{'line_nr'});
684      }
685      # FIXME check that the menu_up_hash is not empty (except for Top)?
686      # FIXME check that node_up is not an external node (except for Top)?
687    }
688  }
689  _check_referenced_nodes($self, $top_node);
690}
691
692
693# set node directions based on sectioning and @node explicit directions
694sub nodes_tree($)
695{
696  my $self = shift;
697  return undef unless ($self->{'nodes'} and @{$self->{'nodes'}});
698
699  my $top_node;
700  # Go through all the nodes and set directions.
701  foreach my $node (@{$self->{'nodes'}}) {
702    if ($node->{'extra'}->{'normalized'} eq 'Top') {
703      $top_node = $node;
704    }
705    my $automatic_directions =
706      (scalar(@{$node->{'extra'}->{'nodes_manuals'}}) == 1);
707
708    if ($automatic_directions) {
709      if ($node->{'extra'}->{'normalized'} ne 'Top') {
710        foreach my $direction (@node_directions) {
711          # prev already defined for the node first Top node menu entry
712          if ($direction eq 'prev' and $node->{'node_'.$direction}
713              and $node->{'node_'.$direction}->{'extra'}
714              and $node->{'node_'.$direction}->{'extra'}->{'normalized'}
715              and $node->{'node_'.$direction}->{'extra'}->{'normalized'} eq 'Top') {
716            next;
717          }
718          if ($node->{'extra'}->{'associated_section'}) {
719            my $section = $node->{'extra'}->{'associated_section'};
720
721            # Prefer the section associated with a @part for node directions.
722            if ($section->{'extra'}->{'part_associated_section'}) {
723              $section = $section->{'extra'}->{'part_associated_section'};
724            }
725
726            my $direction_associated_node
727              = _section_direction_associated_node($section, $direction);
728            if ($direction_associated_node) {
729              $node->{'node_'.$direction} = $direction_associated_node;
730            }
731          }
732        }
733      } else {
734        # Special case for Top node, use first section
735        if ($node->{'extra'}->{'associated_section'}
736            and $node->{'extra'}->{'associated_section'}->{'section_childs'}
737            and $node->{'extra'}->{'associated_section'}->{'section_childs'}->[0]
738            and $node->{'extra'}->{'associated_section'}->{'section_childs'}->[0]->{'extra'}->{'associated_node'}) {
739          my $top_node_section_child
740            = $node->{'extra'}->{'associated_section'}->{'section_childs'}->[0]->{'extra'}->{'associated_node'};
741          $node->{'node_next'} = $top_node_section_child;
742          if (scalar(@{$top_node_section_child->{'extra'}->{'nodes_manuals'}}) == 1) {
743            $top_node_section_child->{'node_prev'} = $node;
744          }
745        }
746      }
747    } else { # explicit directions
748      my @directions = @{$node->{'extra'}->{'nodes_manuals'}};
749      shift @directions;
750      foreach my $direction (@node_directions) {
751        my $node_direction = shift @directions;
752        next if (!defined($node_direction));
753        # external node
754        if ($node_direction->{'manual_content'}) {
755          $node->{'node_'.$direction} = { 'extra' => $node_direction };
756        } else {
757          if ($self->{'labels'}->{$node_direction->{'normalized'}}) {
758            my $node_target
759               = $self->{'labels'}->{$node_direction->{'normalized'}};
760            $node->{'node_'.$direction} = $node_target;
761
762            if (!$self->{'info'}->{'novalidate'}
763                and !_check_node_same_texinfo_code($node_target,
764                                                   $node_direction)) {
765              $self->line_warn(sprintf(
766                __("%s pointer `%s' (for node `%s') different from %s name `%s'"),
767                  $direction_texts{$direction},
768                  node_extra_to_texi($node_direction),
769                  node_extra_to_texi($node->{'extra'}),
770                                     $node_target->{'cmdname'},
771                  node_extra_to_texi($node_target->{'extra'})),
772                                     $node->{'line_nr'});
773            }
774          } else {
775            if ($self->{'info'}->{'novalidate'}) {
776              $node->{'node_'.$direction} = { 'extra' => $node_direction };
777            } else {
778              $self->line_error(sprintf(
779                                  __("%s reference to nonexistent `%s'"),
780                    $direction_texts{$direction},
781                    node_extra_to_texi($node_direction)), $node->{'line_nr'});
782            }
783          }
784        }
785      }
786    }
787  }
788  $top_node = $self->{'nodes'}->[0] if (!$top_node);
789  $self->{'structuring'}->{'top_node'} = $top_node;
790
791  return $top_node;
792}
793
794# Return a list of elements to be converted into pages.  Each element starts
795# with a @node as its first child (except possibly the first one).
796sub split_by_node($)
797{
798  my $root = shift;
799  if (!$root->{'type'} or $root->{'type'} ne 'document_root'
800      or !$root->{'contents'} or !@{$root->{'contents'}}) {
801    return undef;
802  }
803  my $elements;
804  my $current = { 'type' => 'element', 'extra' => {'no_node' => 1}};
805  push @$elements, $current;
806  my @pending_parts = ();
807  foreach my $content (@{$root->{'contents'}}) {
808    if ($content->{'cmdname'} and $content->{'cmdname'} eq 'part'
809        and $content->{'extra'}->{'part_associated_section'}) {
810      push @pending_parts, $content;
811      next;
812    }
813    if ($content->{'cmdname'} and $content->{'cmdname'} eq 'node') {
814      if ($current->{'extra'}->{'no_node'}) {
815        delete $current->{'extra'}->{'no_node'};
816        $current->{'extra'}->{'node'} = $content;
817      } else {
818        $current = { 'type' => 'element', 'extra' => {'node' => $content}};
819        $current->{'element_prev'} = $elements->[-1];
820        $elements->[-1]->{'element_next'} = $current;
821        push @$elements, $current;
822      }
823      $elements->[-1]->{'extra'}->{'element_command'} = $content;
824      if ($content->{'extra'}->{'associated_section'}) {
825        $elements->[-1]->{'extra'}->{'section'}
826          = $content->{'extra'}->{'associated_section'};
827      }
828    }
829    if (@pending_parts) {
830      foreach my $part (@pending_parts) {
831        push @{$current->{'contents'}}, $part;
832        $part->{'parent'} = $current;
833      }
834      @pending_parts = ();
835    }
836    push @{$current->{'contents'}}, $content;
837    $content->{'parent'} = $current;
838  }
839  return $elements;
840}
841
842# Return a list of elements to be converted into pages.  Each element starts
843# with the @node associated with a sectioning command or with the sectioning
844# command if there is no associated node
845sub split_by_section($)
846{
847  my $root = shift;
848  if (!$root->{'type'} or $root->{'type'} ne 'document_root'
849      or !$root->{'contents'} or !@{$root->{'contents'}}) {
850    return undef;
851  }
852  my $elements;
853  my $current = { 'type' => 'element', 'extra' => {'no_section' => 1}};
854  push @$elements, $current;
855  foreach my $content (@{$root->{'contents'}}) {
856    if ($content->{'cmdname'}
857        and (($content->{'cmdname'} eq 'node'
858              and $content->{'extra'}->{'associated_section'})
859             or ($content->{'cmdname'} eq 'part'
860                 and $content->{'extra'}->{'part_associated_section'}))) {
861      my $new_section;
862      if ($content->{'cmdname'} eq 'node') {
863        $new_section = $content->{'extra'}->{'associated_section'};
864      } else {
865        $new_section = $content->{'extra'}->{'part_associated_section'};
866      }
867      if (! $current->{'extra'}->{'section'}
868        or $new_section ne $current->{'extra'}->{'section'}) {
869        if ($current->{'extra'}->{'no_section'}) {
870          delete $current->{'extra'}->{'no_section'};
871          $current->{'extra'}->{'section'}
872            = $new_section;
873        } else {
874          $current = { 'type' => 'element',
875                       'extra' => {'section' => $new_section}};
876          $current->{'element_prev'} = $elements->[-1];
877          $elements->[-1]->{'element_next'} = $current;
878          push @$elements, $current;
879        }
880        $elements->[-1]->{'extra'}->{'element_command'}
881          = $new_section;
882      }
883    } elsif ($content->{'cmdname'} and $content->{'cmdname'} ne 'node'
884                                   and $content->{'cmdname'} ne 'bye') {
885      if ($current->{'extra'}->{'no_section'}) {
886        delete $current->{'extra'}->{'no_section'};
887        $current->{'extra'}->{'section'} = $content;
888        $current->{'extra'}->{'element_command'} = $content;
889      } elsif ($current->{'extra'}->{'section'} ne $content) {
890        $current = { 'type' => 'element', 'extra' => {'section' => $content,
891                                              'element_command' => $content}};
892        $current->{'element_prev'} = $elements->[-1];
893        $elements->[-1]->{'element_next'} = $current;
894        push @$elements, $current;
895      }
896    }
897    if ($content->{'cmdname'} and $content->{'cmdname'} eq 'node'
898        and $content->{'extra'}->{'associated_section'}) {
899      $current->{'extra'}->{'node'} = $content;
900    }
901    push @{$current->{'contents'}}, $content;
902    $content->{'parent'} = $current;
903  }
904  return $elements;
905}
906
907# Associate top-level elements with pages according to the splitting
908# specification.  Set 'first_in_page' on each top-level element to the element
909# that is the first in the output page.
910sub split_pages ($$)
911{
912  my $elements = shift;
913  my $split = shift;
914
915  return undef if (!$elements or !@$elements);
916
917  my $split_level;
918  if (!$split) {
919    foreach my $element (@$elements) {
920      $element->{'extra'}->{'first_in_page'} = $elements->[0];
921    }
922    return;
923  } elsif ($split eq 'chapter') {
924    $split_level = 1;
925  } elsif ($split eq 'section') {
926    $split_level = 2;
927  } elsif ($split ne 'node') {
928    warn "Unknown split specification: $split\n";
929  }
930
931  my $current_first_in_page;
932  foreach my $element (@$elements) {
933    my $level;
934    if ($element->{'extra'}->{'section'}) {
935      $level = $element->{'extra'}->{'section'}->{'level'};
936    } elsif ($element->{'extra'}->{'node'}
937             and $element->{'extra'}->{'node'}->{'associated_section'}) {
938      $level = $element->{'extra'}->{'node'}->{'associated_section'}->{'level'};
939    }
940    #print STDERR "level($split_level) $level "._print_element_command_texi($element)."\n";
941    if (!defined($split_level) or (defined($level) and $split_level >= $level)
942        or !$current_first_in_page) {
943      $current_first_in_page = $element;
944    }
945    $element->{'extra'}->{'first_in_page'} = $current_first_in_page;
946  }
947}
948
949# undef in argument should be an error.  Thus only node existing should be
950# passed to this function.  Even if not existing the value returned should
951# be undef.
952sub _node_element($)
953{
954  my $node = shift;
955  if ($node->{'extra'} and $node->{'extra'}->{'manual_content'}) {
956    my $external_node = { 'type' => 'external_node',
957      'extra' => {'manual_content' => $node->{'extra'}->{'manual_content'}}};
958
959    if ($node->{'extra'}->{'node_content'}) {
960      $external_node->{'extra'}->{'node_content'}
961        = $node->{'extra'}->{'node_content'};
962      $external_node->{'extra'}->{'normalized'} =
963        Texinfo::Convert::NodeNameNormalization::normalize_node(
964          {'contents' => $node->{'extra'}->{'node_content'}});
965    }
966    return $external_node;
967  } elsif ($node->{'cmdname'} and $node->{'cmdname'} eq 'node') {
968    return $node->{'parent'};
969  } else {
970    # case of a @float or an @anchor
971    return undef;
972  }
973}
974
975# Do element directions (like in texi2html) and store them
976# in 'extra'->'directions'.
977sub elements_directions($$)
978{
979  my $self = shift;
980  my $elements = shift;
981  return if (!$elements or !@$elements);
982
983  my $node_top = $self->{'labels'}->{'Top'};
984  foreach my $element (@$elements) {
985    my $directions;
986    $directions->{'This'} = $element;
987    $directions->{'Forward'} = $element->{'element_next'}
988      if ($element->{'element_next'}
989          and (($element->{'extra'}->{'special_element'}
990                and $element->{'element_next'}->{'extra'}->{'special_element'})
991               or (!$element->{'extra'}->{'special_element'}
992                and !$element->{'element_next'}->{'extra'}->{'special_element'})));
993    $directions->{'Back'} = $element->{'element_prev'}
994      if ($element->{'element_prev'}
995          and (($element->{'extra'}->{'special_element'}
996                and $element->{'element_prev'}->{'extra'}->{'special_element'})
997               or (!$element->{'extra'}->{'special_element'}
998                and !$element->{'element_prev'}->{'extra'}->{'special_element'})));
999    if ($element->{'extra'}->{'node'}) {
1000      my $node = $element->{'extra'}->{'node'};
1001      foreach my $direction(['NodeUp', 'node_up'], ['NodeNext', 'node_next'],
1002                            ['NodePrev', 'node_prev']) {
1003        $directions->{$direction->[0]} = _node_element($node->{$direction->[1]})
1004            if ($node->{$direction->[1]});
1005      }
1006      # Now do NodeForward which is something like the following node.
1007      my $automatic_directions =
1008        (scalar(@{$node->{'extra'}->{'nodes_manuals'}}) == 1);
1009      if ($node->{'menu_child'}) {
1010        $directions->{'NodeForward'} = _node_element($node->{'menu_child'});
1011      } elsif ($automatic_directions and $node->{'associated_section'}
1012               and $node->{'associated_section'}->{'section_childs'}
1013               and $node->{'associated_section'}->{'section_childs'}->[0]) {
1014        $directions->{'NodeForward'}
1015          = $node->{'associated_section'}->{'section_childs'}->[0]->{'parent'};
1016      } elsif ($node->{'node_next'}) {
1017        $directions->{'NodeForward'} = _node_element($node->{'node_next'});
1018      } else {
1019        my $up = $node->{'node_up'};
1020        my @up_list = ($node);
1021        # the condition with the up_list avoids infinite loops
1022        # the last condition stops when the Top node is reached.
1023        while (defined($up)
1024               and not (grep {$up eq $_} @up_list
1025                        or ($node_top and $up eq $node_top))) {
1026          if (defined($up->{'node_next'})) {
1027            $directions->{'NodeForward'} = _node_element($up->{'node_next'});
1028            last;
1029          }
1030          push @up_list, $up;
1031          $up = $up->{'node_up'};
1032        }
1033      }
1034
1035      $directions->{'NodeForward'}->{'extra'}->{'directions'}->{'NodeBack'} = $element
1036        if ($directions->{'NodeForward'}
1037            and $directions->{'NodeForward'}->{'type'} eq 'element'
1038            and !$directions->{'NodeForward'}->{'extra'}->{'directions'}->{'NodeBack'});
1039    }
1040
1041    if (!$element->{'extra'}->{'section'}) {
1042      # If there is no associated section, find the previous element section.
1043      # Use the FastForward of this element.
1044      # Use it as FastBack if the section is top level, or use the FastBack.
1045      my $section_element;
1046      my $current = $element;
1047      while ($current->{'element_prev'}) {
1048        $current = $current->{'element_prev'};
1049        if ($current->{'extra'}->{'section'}) {
1050          $section_element = $current;
1051          last;
1052        }
1053      }
1054      if ($section_element) {
1055        if ($section_element->{'extra'}->{'directions'}->{'FastForward'}) {
1056          $directions->{'FastForward'}
1057            = $section_element->{'extra'}->{'directions'}->{'FastForward'};
1058        }
1059        if ($section_element->{'extra'}->{'section'}->{'level'} <= 1) {
1060          $directions->{'FastBack'} = $section_element;
1061        } elsif ($section_element->{'extra'}->{'directions'}->{'Fastback'}) {
1062          $directions->{'FastBack'}
1063            = $section_element->{'extra'}->{'directions'}->{'Fastback'};
1064        }
1065      }
1066    } else {
1067      my $section = $element->{'extra'}->{'section'};
1068      foreach my $direction(['Up', 'section_up'], ['Next', 'section_next'],
1069                            ['Prev', 'section_prev']) {
1070        # in most cases $section->{$direction->[1]}->{'parent'} is defined
1071        # but it may not be the case for the up of @top.
1072        # The section may be its own up in cases like
1073        #  @part part
1074        #  @chapter chapter
1075        # in that cas the direction is not set up
1076        $directions->{$direction->[0]} = $section->{$direction->[1]}->{'parent'}
1077          if ($section->{$direction->[1]}
1078              and $section->{$direction->[1]}->{'parent'}
1079              and $section->{$direction->[1]}->{'parent'} ne $section->{'parent'});
1080      }
1081
1082      my $up = $section;
1083      while ($up->{'level'} > 1 and $up->{'section_up'}) {
1084        $up = $up->{'section_up'};
1085      }
1086
1087      # fastforward is the next element on same level than the upper parent
1088      # element.
1089      if ($up->{'level'} < 1 and $up->{'cmdname'} and $up->{'cmdname'} eq 'top'
1090          and $up->{'section_childs'} and @{$up->{'section_childs'}}) {
1091        $directions->{'FastForward'} = $up->{'section_childs'}->[0]->{'parent'};
1092      } elsif ($up->{'toplevel_next'}) {
1093        $directions->{'FastForward'} = $up->{'toplevel_next'}->{'parent'};
1094      } elsif ($up->{'section_next'}) {
1095        $directions->{'FastForward'} = $up->{'section_next'}->{'parent'};
1096      }
1097      # if the element isn't at the highest level, fastback is the
1098      # highest parent element
1099      if ($up and $up ne $section) {
1100        $directions->{'FastBack'} = $up->{'parent'};
1101      } elsif ($section->{'level'} <= 1) {
1102        # the element is a top level element, we adjust the next
1103        # toplevel element fastback
1104        $directions->{'FastForward'}->{'extra'}->{'directions'}->{'FastBack'}
1105          = $element if ($directions->{'FastForward'});
1106      }
1107    }
1108    # Use node up for Up if there is no section up.
1109    # Not done in the default case.
1110    if ($self->get_conf('USE_UP_NODE_FOR_ELEMENT_UP')
1111        and !$directions->{'Up'} and $element->{'extra'}->{'node'}
1112        and $element->{'extra'}->{'node'}->{'node_up'}
1113        and (!$node_top or ($element->{'extra'}->{'node'} ne $node_top))) {
1114      #print STDERR "Using node for up "._print_element_command_texi($element)."\n";
1115      my $up_node_element = _node_element($element->{'extra'}->{'node'}->{'node_up'});
1116      $directions->{'Up'} = $up_node_element if ($up_node_element);
1117    }
1118    if ($element->{'extra'}->{'directions'}) {
1119      %{$element->{'extra'}->{'directions'}} = (%{$element->{'extra'}->{'directions'}},
1120                                                %$directions)
1121    } else {
1122      $element->{'extra'}->{'directions'} = $directions;
1123    }
1124  }
1125  if ($self->get_conf('DEBUG')) {
1126    foreach my $element (@$elements) {
1127      print STDERR "Directions($element): "
1128         .Texinfo::Structuring::_print_directions($element)."\n";
1129    }
1130  }
1131}
1132
1133sub elements_file_directions($$)
1134{
1135  my $self = shift;
1136  my $elements = shift;
1137  return if (!$elements or !@$elements);
1138
1139  my $current_filename;
1140  my $first_element_in_file;
1141  # need to gather the directions before the FirstInFile* directions
1142  # are added to the first element in the file.
1143  my @first_element_in_file_directions;
1144  foreach my $element (@$elements) {
1145    my $directions;
1146    my $filename;
1147    if (defined($element->{'filename'})) {
1148      $filename = $element->{'filename'};
1149      my $current_element = $element;
1150      if (not defined($current_filename)
1151          or $filename ne $current_filename) {
1152        $first_element_in_file = $element;
1153        @first_element_in_file_directions = keys %{$element->{'extra'}->{'directions'}};
1154        $current_filename = $filename;
1155      }
1156      while ($current_element->{'element_prev'}) {
1157        $current_element = $current_element->{'element_prev'};
1158        if (defined($current_element->{'filename'})) {
1159          if ($current_element->{'filename'} ne $filename) {
1160            $element->{'extra'}->{'directions'}->{'PrevFile'} = $current_element;
1161            last;
1162          }
1163        } else {
1164          last;
1165        }
1166      }
1167      $current_element = $element;
1168      while ($current_element->{'element_next'}) {
1169        $current_element = $current_element->{'element_next'};
1170        if (defined($current_element->{'filename'})) {
1171          if ($current_element->{'filename'} ne $filename) {
1172            $element->{'extra'}->{'directions'}->{'NextFile'} = $current_element;
1173            last;
1174          }
1175        } else {
1176          last;
1177        }
1178      }
1179    }
1180    # set the directions of the first elements in file, to
1181    # be used in footers for example
1182    if (defined($first_element_in_file)) {
1183      foreach my $first_in_file_direction
1184                (@first_element_in_file_directions) {
1185        $element->{'extra'}->{'directions'}->{'FirstInFile'.$first_in_file_direction}
1186          = $first_element_in_file->{'extra'}->{'directions'}->{$first_in_file_direction};
1187      }
1188    }
1189  }
1190}
1191
1192my %sectioning_commands = %Texinfo::Common::sectioning_commands;
1193# for debugging
1194sub _print_root_command_texi($)
1195{
1196  my $command = shift;
1197  my $tree;
1198  if ($command->{'cmdname'}) {
1199    if ($command->{'cmdname'} eq 'node') {
1200      $tree = $command->{'extra'}->{'node_content'};
1201    } elsif ($sectioning_commands{$command->{'cmdname'}}) {
1202      $tree = $command->{'args'}->[0]->{'contents'};
1203    }
1204  } else {
1205    return "Not a root command";
1206  }
1207  return '@'.$command->{'cmdname'}. ' '
1208       .Texinfo::Convert::Texinfo::convert ({'contents' => $tree})
1209          if ($tree);
1210  return 'UNDEF @'.$command->{'cmdname'};
1211}
1212
1213# for debugging
1214sub _print_element_command_texi($)
1215{
1216  my $element = shift;
1217  if (!$element) {
1218    return "UNDEF ELEMENT";
1219  }
1220  if (!$element->{'type'}) {
1221    return "element $element without type: ".
1222       Texinfo::Parser::_print_current_keys($element);
1223  }
1224
1225  if ($element->{'type'} eq 'external_node') {
1226    my $command = {'contents' => [{'text' => '('},
1227                        @{$element->{'extra'}->{'manual_content'}},
1228                               {'text' => ')'}]};
1229    if ($element->{'extra'}->{'node_content'}) {
1230      unshift @{$command->{'contents'}}, @{$element->{'extra'}->{'node_content'}};
1231    }
1232    return Texinfo::Convert::Texinfo::convert($command);
1233  }
1234
1235  my $command = $element->{'extra'}->{'element_command'};
1236  if (!defined($command)) {
1237    # happens when there are only nodes and sections are used as elements
1238    my $result = "No associated command ";
1239    $result .= "(type $element->{'type'})" if (defined($element->{'type'}));
1240    return $result;
1241  }
1242  return _print_root_command_texi($command);
1243}
1244
1245# for debugging
1246sub _print_directions($)
1247{
1248  my $element = shift;
1249  my $result = 'element: '._print_element_command_texi($element)."\n";
1250
1251  if ($element->{'extra'} and $element->{'extra'}->{'directions'}) {
1252    foreach my $direction (sort(keys(%{$element->{'extra'}->{'directions'}}))) {
1253      $result .= "  $direction: ".
1254       _print_element_command_texi($element->{'extra'}->{'directions'}->{$direction})."\n";
1255    }
1256  } else {
1257    $result .= "  NO DIRECTION";
1258  }
1259  return $result;
1260}
1261
1262# this is used in the test suite, but not likely to be useful in real life.
1263sub _unsplit($)
1264{
1265  my $root = shift;
1266  if (!$root->{'type'} or $root->{'type'} ne 'document_root'
1267      or !$root->{'contents'}) {
1268    return $root;
1269  }
1270  foreach my $content (@{$root->{'contents'}}) {
1271    $content->{'parent'} = $root;
1272  }
1273  return $root;
1274}
1275
1276# For each internal reference command, set the 'label' key in the 'extra'
1277# hash of the reference tree element to the associated labeled tree element.
1278sub associate_internal_references($)
1279{
1280  my $self = shift;
1281
1282  my $labels = $self->labels_information();
1283  my $refs = $self->internal_references_information();
1284  return if (!defined($refs));
1285  foreach my $ref (@$refs) {
1286    my $node_arg;
1287    $node_arg = $ref->{'extra'}{'menu_entry_node'};
1288
1289    if (defined $node_arg) {
1290      if ($node_arg->{'node_content'}) {
1291        my $normalized =
1292             Texinfo::Convert::NodeNameNormalization::normalize_node(
1293                {'contents' => $node_arg->{'node_content'} });
1294        $node_arg->{'normalized'} = $normalized
1295          if (defined $normalized and $normalized ne '');
1296      }
1297      next;
1298    }
1299
1300    $node_arg = $ref->{'extra'}{'node_argument'};
1301    if ($node_arg->{'node_content'}) {
1302      my $normalized =
1303           Texinfo::Convert::NodeNameNormalization::normalize_node(
1304              {'contents' => $node_arg->{'node_content'} });
1305      $node_arg->{'normalized'} = $normalized;
1306    }
1307    if (!defined($labels->{$node_arg->{'normalized'}})) {
1308      if (!$self->{'info'}->{'novalidate'}) {
1309        $self->line_error(sprintf(__("\@%s reference to nonexistent node `%s'"),
1310                $ref->{'cmdname'}, node_extra_to_texi($node_arg)),
1311                $ref->{'line_nr'});
1312      }
1313    } else {
1314      my $node_target = $labels->{$node_arg->{'normalized'}};
1315      $ref->{'extra'}->{'label'} = $node_target;
1316      if (!$self->{'info'}->{'novalidate'}
1317          and !_check_node_same_texinfo_code($node_target, $node_arg)) {
1318        $self->line_warn(sprintf(
1319           __("\@%s to `%s', different from %s name `%s'"),
1320           $ref->{'cmdname'},
1321           node_extra_to_texi($node_arg),
1322           $node_target->{'cmdname'},
1323           node_extra_to_texi($node_target->{'extra'})), $ref->{'line_nr'});
1324      }
1325    }
1326  }
1327}
1328
1329sub number_floats($)
1330{
1331  my $floats = shift;
1332  return if (!defined($floats));
1333  foreach my $style (keys(%$floats)) {
1334    my %nr_in_chapter;
1335    my $float_index = 0;
1336    foreach my $float (@{$floats->{$style}}) {
1337      next if (!$float->{'extra'}
1338               or !defined($float->{'extra'}->{'node_content'}));
1339      $float_index++;
1340      my $number;
1341      if ($float->{'extra'}->{'float_section'}) {
1342        my $up = $float->{'extra'}->{'float_section'};
1343        while ($up->{'section_up'}
1344               #and $command_structuring_level{$up->{'cmdname'}}
1345               and defined($up->{'section_up'}->{'cmdname'})
1346               and $command_structuring_level{$up->{'section_up'}->{'cmdname'}}) {
1347          $up = $up->{'section_up'};
1348        }
1349        if (!$unnumbered_commands{$up->{'cmdname'}}) {
1350          $nr_in_chapter{$up->{'number'}}++;
1351          $number = $up->{'number'} . '.' . $nr_in_chapter{$up->{'number'}};
1352        }
1353      }
1354      $number = $float_index if (!defined($number));
1355      $float->{'number'} = $number;
1356    }
1357  }
1358}
1359
1360sub _copy_contents($)
1361{
1362  my $contents = shift;
1363  if (ref($contents) ne 'ARRAY') {
1364    cluck "$contents not an array";
1365    return undef;
1366  }
1367  my $copy = Texinfo::Common::copy_tree({'contents' => $contents});
1368  return $copy->{'contents'};
1369}
1370
1371sub get_node_node_childs
1372{
1373  my ($node) = @_;
1374
1375  my @node_childs;
1376
1377  if ($node->{'extra'}->{'associated_section'}->{'section_childs'}) {
1378    foreach my $child (@{$node->{'extra'}->{'associated_section'}->{'section_childs'}}) {
1379      if ($child->{'extra'} and $child->{'extra'}->{'associated_node'}) {
1380        push @node_childs, $child->{'extra'}->{'associated_node'};
1381      }
1382    }
1383  }
1384  # Special case for @top.  Gather all the children of the @part following
1385  # @top.
1386  if ($node->{'extra'}->{'associated_section'}->{'cmdname'} eq 'top') {
1387    my $current = $node->{'extra'}->{'associated_section'};
1388    while ($current->{'section_next'}) {
1389      $current = $current->{'section_next'};
1390      if ($current->{'cmdname'} and $current->{'cmdname'} eq 'part'
1391          and $current->{'section_childs'}) {
1392        foreach my $child (@{$current->{'section_childs'}}) {
1393          if ($child->{'extra'} and $child->{'extra'}->{'associated_node'}) {
1394            push @node_childs, $child->{'extra'}->{'associated_node'};
1395          }
1396        }
1397      } elsif ($current->{'extra'}->{'associated_node'}) {
1398        # for @appendix, and what follows, as it stops a @part, but is
1399        # not below @top
1400        push @node_childs, $current->{'extra'}->{'associated_node'};
1401      }
1402    }
1403  }
1404  return @node_childs;
1405}
1406
1407sub new_node_menu_entry
1408{
1409  my ($self, $node, $use_sections) = @_;
1410
1411  my $node_contents = $node->{'extra'}->{'node_content'};
1412
1413  my ($name_contents, $menu_entry_name);
1414  if ($use_sections) {
1415    if (defined $node->{'extra'}->{'associated_section'}) {
1416      $name_contents = $node->{'extra'}->{'associated_section'}->{'args'}->[0]->{'contents'};
1417    } else {
1418      $name_contents = $node_contents; # shouldn't happen
1419    }
1420  }
1421
1422  my $entry = {'type' => 'menu_entry'};
1423
1424  if ($use_sections) {
1425    $menu_entry_name = {'type' => 'menu_entry_name'};
1426    $menu_entry_name->{'contents'} = _copy_contents ($name_contents);
1427    foreach my $content (@{$menu_entry_name->{'contents'}}) {
1428      $content->{'parent'} = $menu_entry_name;
1429    }
1430  }
1431
1432  my $menu_entry_node = {'type' => 'menu_entry_node'};
1433  $menu_entry_node->{'contents'}
1434    = _copy_contents ($node_contents);
1435
1436  foreach my $content (@{$menu_entry_node->{'contents'}}) {
1437    $content->{'parent'} = $menu_entry_node;
1438  }
1439  Texinfo::Common::protect_colon_in_tree($menu_entry_node);
1440
1441  my $description = {'type' => 'menu_entry_description'};
1442  $description->{'contents'}->[0] = {'type' => 'preformatted',
1443                                     'parent' => $description};
1444  $description->{'contents'}->[0]->{'contents'}->[0] = {'text' =>"\n",
1445         'parent' => $description->{'contents'}->[0]};
1446
1447  if ($use_sections) {
1448    $entry->{'args'}
1449     = [{'text' => '* ', 'type' => 'menu_entry_leading_text'},
1450       $menu_entry_name,
1451       {'text' => ': ', 'type' => 'menu_entry_separator'},
1452       $menu_entry_node,
1453       {'text' => '.', 'type' => 'menu_entry_separator'},
1454       $description];
1455  } else {
1456    $entry->{'args'}
1457     = [{'text' => '* ', 'type' => 'menu_entry_leading_text'},
1458       $menu_entry_node,
1459       {'text' => '::', 'type' => 'menu_entry_separator'},
1460       $description];
1461  }
1462
1463  foreach my $arg(@{$entry->{'args'}}) {
1464    $arg->{'parent'} = $entry;
1465  }
1466  $entry->{'extra'}->{'menu_entry_name'} = $menu_entry_name;
1467
1468  $entry->{'extra'}->{'menu_entry_node'} =
1469    Texinfo::Common::parse_node_manual($menu_entry_node);
1470  my $content = $entry->{'extra'}->{'menu_entry_node'}->{'node_content'};
1471  if ($content) {
1472    $entry->{'extra'}->{'menu_entry_node'}->{'normalized'}
1473     = Texinfo::Convert::NodeNameNormalization::normalize_node(
1474         {'contents' => $content } );
1475  }
1476
1477  $entry->{'extra'}->{'menu_entry_description'} = $description;
1478
1479  return $entry;
1480}
1481
1482sub new_block_command($$$)
1483{
1484  my $block_contents = shift;
1485  my $parent = shift;
1486  my $command_name = shift;
1487
1488  my $end = {'cmdname' => 'end', 'extra' =>
1489                 {'command_argument' => $command_name,
1490                  'text_arg' => $command_name}};
1491  push @{$end->{'args'}},
1492    {'type' => 'line_arg', 'parent' => $end};
1493  push @{$end->{'args'}->[0]->{'contents'}},
1494         ({'text' => $command_name, 'parent' => $end->{'args'}->[0]},
1495          {'type' => 'spaces_at_end', 'text' => "\n",
1496           'parent' => $end->{'args'}->[0]});
1497  $end->{'args'}->[0]->{'extra'} = {'spaces_before_argument' => ' '};
1498  my $new_block = {'cmdname' => $command_name, 'parent' => $parent,
1499                  'extra'=>{'end_command' => $end}};
1500  $new_block->{'contents'} = [{'type' => 'empty_line_after_command',
1501                               'text' => "\n"},
1502                              @$block_contents, $end];
1503  foreach my $content (@{$new_block->{'contents'}}) {
1504    $content->{'parent'} = $new_block;
1505  }
1506  return $new_block;
1507}
1508
1509sub new_complete_node_menu
1510{
1511  my ($self, $node, $use_sections) = @_;
1512
1513  my @node_childs = get_node_node_childs($node);
1514
1515  if (not scalar(@node_childs)) {
1516    return;
1517  }
1518
1519  my @pending;
1520  for my $child (@node_childs) {
1521    my $entry = new_node_menu_entry($self, $child, $use_sections);
1522    push @pending, $entry;
1523  }
1524
1525  my $section = $node->{'extra'}->{'associated_section'};
1526  my $current_menu = new_block_command (\@pending, $section, 'menu');
1527
1528  return $current_menu;
1529}
1530
1531sub _sort_string($$)
1532{
1533  my $a = shift;
1534  my $b = shift;
1535  return (($a =~ /^[[:alpha:]]/ and $b =~ /^[[:alpha:]]/)
1536              or ($a !~ /^[[:alpha:]]/ and $b !~ /^[[:alpha:]]/))
1537              ? ($a cmp $b)
1538                : (($a =~ /^[[:alpha:]]/ && 1) || -1);
1539}
1540
1541sub _sort_index_entries($$)
1542{
1543  my $key1 = shift;
1544  my $key2 = shift;
1545  my $a = uc($key1->{'key'});
1546  my $b = uc($key2->{'key'});
1547  my $res = _sort_string($a, $b);
1548  if ($res == 0) {
1549    $res = ($key1->{'number'} <=> $key2->{'number'});
1550  }
1551  # This may happen if 2 indices are merged as the number is per
1552  # index name.  The @-command should be different though, for
1553  # index names to be different.
1554  if ($res == 0) {
1555    $res = ($key1->{'index_at_command'} cmp $key2->{'index_at_command'});
1556  }
1557  return $res;
1558}
1559
1560sub _sort_index_entries_in_letter($$)
1561{
1562  my $key1 = shift;
1563  my $key2 = shift;
1564  my $a = uc($key1->{'key'});
1565  my $b = uc($key2->{'key'});
1566  my $res = ($a cmp $b);
1567  if ($res == 0) {
1568    $res = ($key1->{'number'} <=> $key2->{'number'});
1569  }
1570  if ($res == 0) {
1571    $res = ($key1->{'index_at_command'} cmp $key2->{'index_at_command'});
1572  }
1573  return $res;
1574}
1575
1576# Go through all the index entries and set 'key', the sort key, on
1577# each one.
1578sub do_index_keys($$)
1579{
1580  my $self = shift;
1581  my $index_names = shift;
1582  my $ignore_chars = '';
1583
1584  # '-' must come first to avoid e.g. [<-@] looking like a character range
1585  $ignore_chars .= '-'
1586    if defined $self->{'values'}->{'txiindexhyphenignore'};
1587  $ignore_chars .= '\\\\' # string with 2 \s, for escaping inside regex
1588    if defined $self->{'values'}->{'txiindexbackslashignore'};
1589  $ignore_chars .= '<'
1590    if defined $self->{'values'}->{'txiindexlessthanignore'};
1591  $ignore_chars .= '@'
1592    if defined $self->{'values'}->{'txiindexatsignignore'};
1593
1594  my $options = {'sort_string' => 1};
1595  if ($self->get_conf('ENABLE_ENCODING')
1596      and $self->{'info'}->{'input_encoding_name'}) {
1597    $options->{'enabled_encoding'} = $self->{'info'}->{'input_encoding_name'};
1598  }
1599
1600  if ($self->get_conf('ENABLE_ENCODING')) {
1601    if ($self->get_conf('OUTPUT_ENCODING_NAME')) {
1602      $options->{'enabled_encoding'} = $self->get_conf('OUTPUT_ENCODING_NAME');
1603    }
1604  }
1605  $options->{'expanded_formats_hash'} = $self->{'expanded_formats_hash'};
1606
1607  foreach my $index_name (keys(%$index_names)) {
1608    foreach my $entry (@{$index_names->{$index_name}->{'index_entries'}}) {
1609      $options->{'code'} = $entry->{'in_code'};
1610      if (defined $entry->{'sortas'}) {
1611        $entry->{'key'} = $entry->{'sortas'};
1612      } else {
1613        $entry->{'key'} = Texinfo::Convert::Text::convert(
1614                              {'contents' => $entry->{'content'}}, $options);
1615        if ($ignore_chars) {
1616          $entry->{'key'} =~ s/[$ignore_chars]//g;
1617        }
1618      }
1619      if ($entry->{'key'} !~ /\S/) {
1620        $self->line_warn(sprintf(__("empty index key in \@%s"),
1621                                 $entry->{'index_at_command'}),
1622                        $entry->{'command'}->{'line_nr'});
1623      }
1624      # This avoids varying results depending on whether the string is
1625      # represented internally in UTF-8.  See "the Unicode bug" in the
1626      # "perlunicode" man page.
1627      utf8::upgrade($entry->{'key'});
1628    }
1629  }
1630}
1631
1632sub sort_indices($$$)
1633{
1634  my $self = shift;
1635  my $index_entries = shift;
1636  my $index_names = shift;
1637  my $sorted_index_entries;
1638  do_index_keys($self, $index_names);
1639  foreach my $index_name (keys(%$index_entries)) {
1640    @{$sorted_index_entries->{$index_name}} =
1641        sort _sort_index_entries
1642            grep {$_->{'key'} =~ /\S/} @{$index_entries->{$index_name}};
1643  }
1644  return $sorted_index_entries;
1645}
1646
1647sub sort_indices_by_letter($$$)
1648{
1649  my $self = shift;
1650  my $index_entries = shift;
1651  my $index_names = shift;
1652  my $indices_sorted_by_letters;
1653  do_index_keys($self, $index_names);
1654  foreach my $index_name (keys(%$index_entries)) {
1655    my $index_letter_hash;
1656    foreach my $index_entry (@{$index_entries->{$index_name}}) {
1657      next if ($index_entry->{'key'} !~ /\S/);
1658      my $letter = uc(substr($index_entry->{'key'}, 0, 1));
1659      push @{$index_letter_hash->{$letter}}, $index_entry;
1660    }
1661    foreach my $letter (sort _sort_string (keys %$index_letter_hash)) {
1662      my @sorted_letter_entries
1663         = sort _sort_index_entries_in_letter @{$index_letter_hash->{$letter}};
1664      push @{$indices_sorted_by_letters->{$index_name}},
1665        { 'letter' => $letter, 'entries' => \@sorted_letter_entries };
1666    }
1667  }
1668  return $indices_sorted_by_letters;
1669}
1670
1671sub merge_indices($)
1672{
1673  my $index_names = shift;
1674
1675  my $merged_index_entries;
1676  foreach my $index_name (keys(%$index_names)) {
1677    my $index_info = $index_names->{$index_name};
1678    next if ($index_info->{'merged_in'});
1679    foreach my $contained_index (keys (%{$index_info->{'contained_indices'}})) {
1680      if ($index_names->{$contained_index}->{'index_entries'}) {
1681        push @{$merged_index_entries->{$index_name}},
1682          @{$index_names->{$contained_index}->{'index_entries'}};
1683      }
1684    }
1685  }
1686  return $merged_index_entries;
1687}
1688
1689
16901;
1691
1692__END__
1693
1694#Last,
1695#C<output_internal_links> may be used to output element and
1696#index entries references, mostly for HTML output.
1697
1698=head1 NAME
1699
1700Texinfo::Structuring - information on Texinfo::Parser tree
1701
1702=head1 SYNOPSIS
1703
1704  use Texinfo::Structuring qw(sectioning_structure nodes_tree number_floats
1705    associate_internal_references split_by_node split_by_section split_pages
1706    merge_indices sort_indices_by_letter sort_indices elements_directions
1707    elements_file_directions);
1708  # $tree is a Texinfo document tree.  $parser is a Texinfo::Parser object.
1709  my $sections_root = sectioning_structure ($parser, $tree);
1710  set_menus_node_directions($parser);
1711  my $top_node = nodes_tree($parser);
1712  complete_node_tree_with_menus($parser, $top_node);
1713  number_floats($parser->floats_information());
1714  associate_internal_references($parser);
1715  my $elements;
1716  if ($split_at_nodes) {
1717    $elements = split_by_node($tree);
1718  } else {
1719    $elements = split_by_section($tree);
1720  }
1721  split_pages($elements, $split);
1722  elements_directions($parser, $elements);
1723  elements_file_directions($parser, $elements);
1724
1725  my $index_names = $parser->indices_information();
1726  my $merged_index_entries
1727     = merge_indices($index_names);
1728  my $index_entries_sorted;
1729  if ($sort_by_letter) {
1730    $index_entries_sorted = sort_indices_by_letter($parser,
1731                                       $merged_index_entries, $index_names);
1732  } else {
1733    $index_entries_sorted = sort_indices($parser, $merged_index_entries,
1734                                         $index_names);
1735  }
1736
1737
1738=head1 DESCRIPTION
1739
1740Texinfo::Structuring first allows to collect informations on a Texinfo tree.
1741In most case, it also requires a parser object to do that job.  Thanks to
1742C<sectioning_structure> the hierarchy of sectioning commands is determined.
1743The directions implied by menus are determined with
1744C<set_menus_node_directions>.  The node tree is analysed with C<nodes_tree>.
1745Nodes directions are completed with menu directions with
1746C<complete_node_tree_with_menus>.  Floats get their standard numbering with
1747C<number_floats> and internal references are matched up with nodes, floats or
1748anchors with C<associate_internal_references>.
1749
1750It is also possible to group the top-level contents of the tree, which consist
1751in nodes and sectioning commands into elements that group together a node and
1752the next sectioning element.  With C<split_by_node> nodes are considered
1753to be the main sectioning elements, while with C<split_by_section> the
1754sectioning command elements are the main elements.  The first mode is typical
1755of Info format, while the second correspond to a traditional book.
1756The elements may be further split in I<pages>, which are not pages as
1757in book pages, but more like web pages, and hold series of elements.
1758
1759The elements may have directions to other elements prepared
1760by C<elements_directions>.  C<elements_file_directions> should also
1761set direction related to files, provided files are associated with
1762elements by the user.
1763
1764C<merge_indices> may be used to merge indices, which may be sorted
1765with C<sort_indices> or C<sort_indices_by_letter> to sort by letters.
1766
1767Other miscellaneous methods include C<set_menus_to_simple_menu> and
1768C<menu_to_simple_menu> to change the menu texinfo tree, as well
1769as C<insert_nodes_for_sectioning_commands> that adds nodes for
1770sectioning commands without nodes and C<complete_tree_nodes_menus>
1771that completes the node menus based on the sectioning tree.
1772
1773
1774
1775=head1 METHODS
1776
1777No method is exported in the default case.
1778
1779Most of those function references takes a Texinfo::Parser object
1780as argument, see L<Texinfo::Parser>.
1781
1782=over
1783
1784=item $sections_root = sectioning_structure ($parser, $tree)
1785
1786This function goes through the tree and gather information on
1787the document structure for sectioning commands.  It returns the
1788root of the sectioning commands tree.
1789
1790For section elements, it sets:
1791
1792=over
1793
1794=item level
1795
1796The level in the sectioning tree hierarchy.  0 is for C<@top> or
1797C<@part>, 1 for C<@chapter>, C<@appendix>...  This level is corrected
1798by C<@raisesections> and C<@lowersections>.
1799
1800=item number
1801
1802The sectioning element number.
1803
1804=item section_childs
1805
1806An array holding sectioning elements children of the element.
1807
1808=item section_up
1809
1810=item section_prev
1811
1812=item section_next
1813
1814The up, previous and next sectioning elements.
1815
1816=item toplevel_next
1817
1818=item toplevel_prev
1819
1820=item toplevel_up
1821
1822The next and previous and up sectioning elements of toplevel sectioning
1823elements (like C<@top>, C<@chapter>, C<@appendix>), not taking into
1824account C<@part> elements.
1825
1826=back
1827
1828=item set_menus_node_directions($parser)
1829
1830Goes through menu and set directions.
1831
1832=over
1833
1834=item menu_child
1835
1836The first child in the menu of the node.
1837
1838=item menu_up
1839
1840=item menu_next
1841
1842=item menu_prev
1843
1844Up, next and previous directions as set in menus.
1845
1846=item node_up
1847
1848=back
1849
1850=item my $top_node = nodes_tree($parser)
1851
1852Goes through nodes and set directions.  Returns the top
1853node.
1854
1855This functions sets:
1856
1857=over
1858
1859=item node_up
1860
1861=item node_prev
1862
1863=item node_next
1864
1865Up, next and previous directions for the node.
1866
1867=back
1868
1869=item complete_node_tree_with_menus($parser, $top_node)
1870
1871Complete nodes directions with menu directions.  Check consistency
1872of menus, sectionning and nodes direction structures.  Check that
1873all the nodes are referenced (in menu, @*ref or node direction).
1874
1875=item number_floats($float_information)
1876
1877Number the floats as described in the Texinfo manual.  Sets
1878the I<number> key of the float tree elements.
1879
1880=item associate_internal_references($parser)
1881
1882Verify that internal references (C<@ref> and similar without
1883fourth of fifth argument) have an associated node, anchor or float.
1884Set the I<label> key in the I<extra> hash of the reference tree
1885element to the associated labeled tree element.
1886
1887=item warn_non_empty_parts($parser)
1888
1889Register a warning in C<$parser> for each C<@part> that is not empty.
1890
1891=item $elements = split_by_node($tree)
1892
1893Returns a reference array of elements where a node is associated to
1894the following sectioning commands.  Sectioning commands without nodes
1895are also with the previous node, while nodes without sectioning commands
1896are alone in their elements.
1897
1898Elements are regular tree items with type I<element>, the
1899associated nodes and sectioning tree items are in the array associated
1900with the I<contents> key.  They have directions, namely I<element_next>
1901and I<element_prev> pointing to the previous and the next element.
1902
1903In the I<extra> hash they have
1904
1905=over
1906
1907=item no_node
1908
1909A special case, if there are no nodes in the document, the value is set.
1910
1911=item node
1912
1913=item element_command
1914
1915The node command associated with the element.
1916
1917=item section
1918
1919The sectioning command associated with the element node.
1920
1921=back
1922
1923=item $elements = split_by_section($tree)
1924
1925Similarly with C<split_by_node>, returns an array of elements.  This time,
1926lone nodes are associated with the previous sections and lone sections
1927makes up an element.
1928
1929The extra hash keys set are the same, except that I<element_command> is
1930the sectioning command associated with the element, and I<no_node> is
1931replaced by I<no_section>.
1932
1933=item $pages = split_pages($elements, $split)
1934
1935The elements from the array reference argument have an extra I<first_in_page>
1936value set to the first element on the unit, and based on the
1937value of I<$split>.  The possible values for I<$split> are
1938
1939=over
1940
1941=item chapter
1942
1943The elements are split at chapter or other toplevel sectioning elements.
1944
1945=item node
1946
1947Each element has its own page.
1948
1949=item section
1950
1951The elements are split at sectioning commands below chapter.
1952
1953=item value evaluating to false
1954
1955No splitting, only one page is returned, holding all the elements.
1956
1957=back
1958
1959=item elements_directions($parser, $elements)
1960
1961Directions are set up for the elements in the array reference given in
1962argument.  The corresponding hash reference is in
1963C<< {'extra'}->{'directions'} >>
1964and keys correspond to directions while values are elements.
1965
1966The following directions are set up:
1967
1968=over
1969
1970=item This
1971
1972The element itself.
1973
1974=item Forward
1975
1976Element next.
1977
1978=item Back
1979
1980Previous element.
1981
1982=item NodeForward
1983
1984Following node element in reading order.  It is the next node, or the
1985first in menu or the next of the up node.
1986
1987=item NodeBack
1988
1989Preceding node element.
1990
1991=item NodeUp
1992
1993=item NodeNext
1994
1995=item NodePrev
1996
1997The up, next and previous node elements.
1998
1999=item Up
2000
2001=item Next
2002
2003=item Prev
2004
2005The up, next and previous section elements.
2006
2007=item FastForward
2008
2009The next top level section element.
2010
2011=item FastBack
2012
2013For top level elements, the previous top level element.  For other elements
2014the up top level element.  For example, for a chapter element it is the
2015previous chapter, for a subsection element it is the chapter element
2016that contains the subsection.
2017
2018=item FastForward
2019
2020The next top level element.
2021
2022=back
2023
2024=item elements_file_directions($parser, $elements)
2025
2026In the directions reference described above for C<elements_directions>, sets
2027the I<PrevFile> and C<NextFile> directions to the elements in previous and
2028following files.
2029
2030It also sets C<FirstInFile*> directions for all the elements by using
2031the directions of the first element in file.  So, for example,
2032C<FirstInFileNodeNext> is the next node of the first element in
2033the file of each element.
2034
2035The API for association of pages/elements to files is not defined yet.
2036
2037=item $merged_entries = merge_indices($index_names)
2038
2039Using informations returned by L<Texinfo::Parser/indices_information>,
2040a structure holding all the index entries by index name is returned,
2041with all the entries of merged indices merged with those of the indice
2042merged into.
2043
2044The I<$merged_entries> returned is a hash reference whose
2045keys are the index names and values arrays of index entry structures
2046described in details in L<Texinfo::Parser/index_entries>.
2047
2048=item $index_entries_sorted = sort_indices_by_letter($parser, $merged_index_entries, $index_names)
2049
2050=item $index_entries_sorted = sort_indices($parser, $merged_index_entries, $index_names)
2051
2052These functions first sets a plain text key for each index entry, used for
2053sorting.  In both cases, a hash reference with index names as keys is returned.
2054
2055When sorting by letter, an array reference of letter hash references is
2056associated with each index name.  Each letter hash reference has two
2057keys, a I<letter> key with the letter, and an I<entries> key with an array
2058reference of sorted index entries beginning with the letter.
2059
2060When simply sorting, the array of the sorted indes entries is associated
2061with the index name.
2062
2063=back
2064
2065=head1 SEE ALSO
2066
2067L<Texinfo manual|http://www.gnu.org/s/texinfo/manual/texinfo/>,
2068L<Texinfo::Parser>.
2069
2070=head1 AUTHOR
2071
2072Patrice Dumas, E<lt>pertusus@free.frE<gt>
2073
2074=cut
2075