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