58510024be
BIRD uses hacked LinuxDocTools for building documentation, keeping some parts locally and using remaining parts from system-installed one. This setup breaks when LinuxDocTools makes some internal changes and is hard to keep consistent. Just include full LinuxDocTools code (both hacked and unmodified parts) to avoid consistency issues. Note that we still need some binaries from LinuxDocTools, so it still needs to be installed to build documentation.
357 lines
11 KiB
Perl
357 lines
11 KiB
Perl
# InfoUtils.pm
|
|
#
|
|
# Some utils for the linuxdoc info backend.
|
|
#
|
|
# * Create menus
|
|
# * Normalize node names and associated text
|
|
# * Point references to the associated node as needed
|
|
#
|
|
# Copyright (C) 2009 Agustín Martín Domingo, agmartin at debian org
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
# --------------------------------------------------------------------
|
|
|
|
|
|
package LinuxDocTools::InfoUtils;
|
|
|
|
use base qw(Exporter);
|
|
|
|
# List all exported symbols here.
|
|
our @EXPORT_OK = qw(info_process_texi);
|
|
|
|
# Import :all to get everything.
|
|
our %EXPORT_TAGS = (all => [@EXPORT_OK]);
|
|
|
|
=head1 NAME
|
|
|
|
InfoUtils - Some utils for the linuxdoc info backend.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use InfoUtils q{:all};
|
|
|
|
info_process_texi($infile, $outfile, $infoname)
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This module contains some utils to process the raw texinfo file
|
|
creating menus, normalizing node names and associated text and
|
|
pointing references to the associated node as needed.
|
|
|
|
=head1 FUNCTIONS
|
|
|
|
=over 4
|
|
|
|
=cut
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_normalize_node_text {
|
|
# -------------------------------------------------------------------------
|
|
# Filter characters not allowed in section names
|
|
# -------------------------------------------------------------------------
|
|
my $text = shift;
|
|
|
|
$text =~ s/\s+/ /g;
|
|
$text =~ s/\@[A-Za-z][A-Za-z0-9]*//g;
|
|
$text =~ s/(\{|\})//g;
|
|
$text =~ s/\,//g;
|
|
# $text =~ s/\.+$//g;
|
|
$text =~ s/\./-/g;
|
|
$text =~ s/\s+$//g;
|
|
|
|
return $text;
|
|
}
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_normalize_node_name {
|
|
# -------------------------------------------------------------------------
|
|
# Filter characters not allowed in node names. Previous filtering of
|
|
# characters not allowed in section names is supposed.
|
|
# -------------------------------------------------------------------------
|
|
my $text = shift;
|
|
# my $tmpnodedata = shift;
|
|
|
|
$text =~ s/\://g;
|
|
$text =~ s/\;//g;
|
|
|
|
# die "Error: Reference \"$text\" already used"
|
|
# if defined $tmpnodedata->{$text};
|
|
|
|
return $text;
|
|
}
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_parse_raw_file {
|
|
# -------------------------------------------------------------------------
|
|
# Parse raw texinfo file. It does not yet contain section names, menus,
|
|
# correct references or title.
|
|
# -------------------------------------------------------------------------
|
|
my $inputfile = shift;
|
|
my $INPUT;
|
|
|
|
my @inputtext = (); # Copy of input file with some preprocessing
|
|
my %nodedata = # A hash of hashes with all node info
|
|
( 'Top' =>
|
|
{ 'text' => "Top",
|
|
'depth' => 0,
|
|
'up' => "",
|
|
'next' => '',
|
|
'previous' => "",
|
|
'sort' => 0,
|
|
'debug' => "",
|
|
'menu' => []}
|
|
);
|
|
|
|
my %levellast = (0 => "Top");
|
|
my %labels = ();
|
|
my %docdata = # Some misc data for the document
|
|
( 'title' => "",
|
|
'author' => "",
|
|
'subtitle' => ""
|
|
);
|
|
|
|
my $depth = my $lastdepth = 0;
|
|
my $lastnode = "";
|
|
my $sort = 0;
|
|
|
|
my $inauthor;
|
|
my $authorline;
|
|
|
|
open ($INPUT, "< $inputfile")
|
|
or die "info-postASP: Could not open $inputfile for read. Aborting ...\n";
|
|
|
|
while (<$INPUT>){
|
|
chomp;
|
|
if ( s/^\@SUB\s+// ){
|
|
my $updepth = $depth;
|
|
my $uppernode = $levellast{$updepth};
|
|
$depth++;
|
|
$sort++;
|
|
|
|
my @levelmenu = ();
|
|
|
|
if ( defined $nodedata{$uppernode}->{'menu'} ){
|
|
@levelmenu = @{ $nodedata{$uppernode}->{'menu'} };
|
|
}
|
|
|
|
my $nodetext = info_normalize_node_text($_);
|
|
my $nodename = info_normalize_node_name($nodetext,\%nodedata);
|
|
|
|
# Make first appearing node the next node for top node
|
|
$nodedata{'Top'}->{'next'} = $nodename if ( $lastdepth eq 0);
|
|
|
|
# Fill info for current node (and 'next' for last one in level)
|
|
$nodedata{$nodename}->{'orig'} = $_;
|
|
$nodedata{$nodename}->{'text'} = $nodetext;
|
|
$nodedata{$nodename}->{'depth'} = $depth;
|
|
$nodedata{$nodename}->{'previous'} =
|
|
defined $levellast{$depth} ? $levellast{$depth} : "";
|
|
$nodedata{$levellast{$depth}}->{'next'} = $nodename
|
|
if defined $levellast{$depth};
|
|
$nodedata{$nodename}->{'up'} = $uppernode;
|
|
$nodedata{$nodename}->{'sort'} = $sort;
|
|
$nodedata{$nodename}->{'debug'} =
|
|
"updepth: $updepth, lastdepth: $lastdepth, up: $uppernode";
|
|
|
|
# Keep this defined in case tbere is no next node in the same level.
|
|
$nodedata{$nodename}->{'next'} = "";
|
|
|
|
push @inputtext, "\@SUB $nodename"; # Rewrite @SUB with the new name
|
|
push @levelmenu, $nodename; # Add $nodename to the level menu list
|
|
|
|
# Prepare things for next @SUB entry found
|
|
$levellast{$depth} = $lastnode = $nodename;
|
|
$lastdepth = $depth;
|
|
$nodedata{$uppernode}->{'menu'} = \@levelmenu;
|
|
|
|
} elsif ( s/^\@ENDSUB// ){
|
|
$depth--;
|
|
push @inputtext, $_;
|
|
} elsif (s/^\@LABEL\s+//){
|
|
# Keep record of node labels vs nodenames. Will use the last.
|
|
$labels{$_} = $lastnode;
|
|
} elsif (s/^\@title\s+//){
|
|
$docdata{'title'} = $_;
|
|
} elsif (/^\@ldt_endauthor/){
|
|
$inauthor = '';
|
|
my @authors;
|
|
if ( @$docdata{'authors'} ){
|
|
@authors = @$docdata{'authors'};
|
|
}
|
|
push @authors, $authorline;
|
|
$docdata{'authors'} = \@authors;
|
|
$authorline = "";
|
|
} elsif ( s/^\@author\s+// ){
|
|
$inauthor = 1;
|
|
$authorline = $_;
|
|
} elsif ( $inauthor ){
|
|
next if m/^\s*$/;
|
|
s/^\s+//;
|
|
$authorline .= " $_ ";
|
|
} elsif (s/^\@subtitle\s+//){
|
|
$docdata{'subtitle'} = $_;
|
|
} elsif (s/^\@ldt_translator\s+//){
|
|
$docdata{'translator'} = $_;
|
|
} elsif (s/^\@ldt_tdate\s+//){
|
|
$docdata{'tdate'} = $_;
|
|
} else {
|
|
push @inputtext, $_;
|
|
}
|
|
}
|
|
close $INPUT;
|
|
|
|
$docdata{'nodedata'} = \%nodedata;
|
|
$docdata{'labels'} = \%labels;
|
|
$docdata{'inputtext'} = \@inputtext;
|
|
|
|
return \%docdata;
|
|
}
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_write_preprocessed_file {
|
|
# -------------------------------------------------------------------------
|
|
# Write processed texinfo file. Add section names, menus, correct
|
|
# references and title.
|
|
# -------------------------------------------------------------------------
|
|
my $docdata = shift;
|
|
my $infoname = shift;
|
|
my $texiout = shift;
|
|
|
|
die "InfoUtils.pm: No info file name $infoname.\n" unless $infoname;
|
|
die "InfoUtils.pm: No output texi file $texiout\n" unless $texiout;
|
|
|
|
my $nodedata = $docdata->{'nodedata'};
|
|
my $labels = $docdata->{'labels'};
|
|
my $inputtext = $docdata->{'inputtext'};
|
|
|
|
my $OUTFILE;
|
|
|
|
# info_check_parsed_data($nodedata);
|
|
|
|
my %sections = ( 1 => "\@chapter",
|
|
2 => "\@section",
|
|
3 => "\@subsection",
|
|
4 => "\@subsubsection");
|
|
|
|
my $lastdepth = 0;
|
|
my $lastnode = "Top";
|
|
my $texinfo = "\@c %** START OF HEADER
|
|
\@setfilename $infoname
|
|
\@c %** END OF HEADER\n";
|
|
|
|
foreach ( @$inputtext ) {
|
|
if ( s/^\@SUB\s+// ){
|
|
my $key = $_;
|
|
my $depth = $nodedata->{$key}->{'depth'};
|
|
my $name = $nodedata->{$key}->{'text'};
|
|
|
|
if ( $depth le 4 ){
|
|
my $next = $nodedata->{$key}->{'next'};
|
|
my $previous = $nodedata->{$key}->{'previous'};
|
|
my $up = $nodedata->{$key}->{'up'};
|
|
# my $txt = "\@comment nodename, next, previous, up\n";
|
|
my $txt = "";
|
|
|
|
# $txt .= "\@node $key, $previous, $next, $up\n";
|
|
$txt .= "\@node $key\n";
|
|
$txt .= "$sections{$depth} $name\n";
|
|
|
|
if ( $depth gt $lastdepth && defined $nodedata->{$lastnode}->{'menu'}){
|
|
$txt = "\n\@menu\n\* "
|
|
. join("::\n\* ",@{$nodedata->{$lastnode}->{'menu'}})
|
|
. "::\n\@end menu\n"
|
|
. "\n$txt";
|
|
}
|
|
|
|
$texinfo .= $txt;
|
|
$lastdepth = $depth;
|
|
$lastnode = $key;
|
|
} elsif ( $depth eq 5 ){
|
|
$texinfo .= "\@subsubheading $nodedata->{$key}->{'text'}\n";
|
|
} else {
|
|
die "info-postASP: Entry \"$key\" has wrong depth $depth\n";
|
|
}
|
|
} elsif (s/^\@REF\s+//){
|
|
if ( defined $labels->{$_} ){
|
|
# If this reference is to a node, use its nodename
|
|
$texinfo .= "\@ref{" . $labels->{$_} . "}\n";
|
|
} else {
|
|
$texinfo .= "\@ref{$_}\n";
|
|
}
|
|
} elsif (s/^\@TOP//){
|
|
$texinfo .= "\@node top\n"
|
|
. "\@top " . $docdata->{'title'} . "\n"
|
|
. "\@example\n";
|
|
|
|
$texinfo .= join(' and ',@{$docdata->{'authors'}}) . "\n"
|
|
if ( @{$docdata->{'authors'}} );
|
|
|
|
$texinfo .= $docdata->{'subtitle'} . "\n"
|
|
if ( defined $docdata->{'subtitle'} );
|
|
|
|
$texinfo .= $docdata->{'translator'} . "\n"
|
|
if ( defined $docdata->{'translator'} );
|
|
|
|
$texinfo .= $docdata->{'tdate'} . "\n"
|
|
if ( defined $docdata->{'tdate'} );
|
|
|
|
$texinfo .= "\@end example\n";
|
|
} else {
|
|
$texinfo .= "$_\n";
|
|
}
|
|
}
|
|
|
|
open ($OUTFILE, "> $texiout")
|
|
or die "Could not open \"$texiout\" for write. Aborting ...\n";
|
|
print $OUTFILE $texinfo;
|
|
close $OUTFILE;
|
|
}
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_check_parsed_data {
|
|
# -------------------------------------------------------------------------
|
|
# -------------------------------------------------------------------------
|
|
my $tmpnodedata = shift;
|
|
my @sections = sort {
|
|
$tmpnodedata->{$a}->{'sort'} <=> $tmpnodedata->{$b}->{'sort'}
|
|
} keys %$tmpnodedata;
|
|
|
|
foreach ( @sections ){
|
|
my $ref = $tmpnodedata->{$_};
|
|
print STDERR "Node: $_\n";
|
|
print STDERR " orig: $ref->{'orig'}\n";
|
|
print STDERR " text: $ref->{'text'}\n";
|
|
print STDERR " debug: $ref->{'debug'}\n";
|
|
print STDERR " up: $ref->{'up'}\n";
|
|
print STDERR " depth: $ref->{'depth'}\n";
|
|
print STDERR " previous: $ref->{'previous'}\n";
|
|
print STDERR " next: $ref->{'next'}\n";
|
|
print STDERR " sort: $ref->{'sort'}\n";
|
|
print STDERR " menu:\n * " . join("\n * ",@{$ref->{'menu'}}) . "\n" if defined $ref->{'menu'};
|
|
}
|
|
}
|
|
|
|
# -------------------------------------------------------------------------
|
|
sub info_process_texi {
|
|
# -------------------------------------------------------------------------
|
|
# info_process_texi($infile, $outfile, $infoname)
|
|
#
|
|
# Call the other functions.
|
|
# -------------------------------------------------------------------------
|
|
my $infile = shift;
|
|
my $outfile = shift;
|
|
my $infoname = shift;
|
|
|
|
info_write_preprocessed_file(info_parse_raw_file($infile),$infoname,$outfile);
|
|
}
|