# Leave the first line of this file blank!
# This is a Perl script; the following two lines allow us to avoid
# embedding the path of the perl interpreter in the script.
eval "exec perl -S $0 $*"
if $running_under_some_shell;
'di';
'ig00';
#+##############################################################################
# #
# File: texi2html #
# #
# Description: Program to transform most Texinfo documents to HTML #
# #
#-##############################################################################
# @(#)texi2html 1.31 12/08/94 Written by Lionel Cons, Lionel.Cons@cern.ch
# The man page for this program is included at the end of this file and can be
# viewed using the command 'nroff -man texi2html'.
# Please read the copyright at the end of the man page.
#+++############################################################################
# #
# Constants #
# #
#---############################################################################
$DEBUG_TOC = 1;
$DEBUG_INDEX = 2;
$DEBUG_BIB = 4;
$DEBUG_GLOSS = 8;
$DEBUG_DEF = 16;
$BIBRE = '\[[\w\/]+\]'; # RE for a bibliography reference
$FILERE = '[\/\w.+-]+'; # RE for a file name
$VARRE = '[^\s\{\}]+'; # RE for a variable name
$NODERE = '[^@{}:\'`",]+'; # RE for a node name
$NODESRE = '[^@{}:\'`"]+'; # RE for a list of node names
$XREFRE = '[^@{}]+'; # RE for a xref (should use NODERE)
$THISPROG = "texi2html 1.31"; # program name and version
$HOMEPAGE = "http://asis01.cern.ch/infohtml/texi2html.html"; # program home page
$TODAY = &pretty_date; # like "20 September 1993"
$SPLITTAG = "\n"; # tag to know where to split
$PROTECTTAG = "_ThisIsProtected_"; # tag to recognize protected sections
#
# language dependent constants
#
$LDC_SEE = 'see';
$LDC_SECTION = 'section';
$LDC_IN = 'in';
$LDC_TOC = 'Table of Contents';
$LDC_GOTO = 'Go to the';
$LDC_FOOT = 'Footnotes';
# TODO: @def* shortcuts
#
# texinfo section names to level
#
%sec2level = (
'top', 0,
'chapter', 1,
'unnumbered', 1,
'majorheading', 1,
'chapheading', 1,
'appendix', 1,
'section', 2,
'unnumberedsec', 2,
'heading', 2,
'appendixsec', 2,
'appendixsection', 2,
'subsection', 3,
'unnumberedsubsec', 3,
'subheading', 3,
'appendixsubsec', 3,
'subsubsection', 4,
'unnumberedsubsubsec', 4,
'subsubheading', 4,
'appendixsubsubsec', 4,
);
#
# accent map, TeX command to ISO name
#
%accent_map = (
'"', 'uml',
'~', 'tilde',
'^', 'circ',
);
#
# texinfo "simple things" (@foo) to HTML ones
#
%simple_map = (
# cf. makeinfo.c
"*", "
", # HTML+
" ", " ",
"\n", "\n",
"|", "",
# spacing commands
":", "",
"!", "!",
"?", "?",
".", ".",
);
#
# texinfo "things" (@foo{}) to HTML ones
#
%things_map = (
'TeX', 'TeX',
'br', '
', # paragraph break
'bullet', '*',
'copyright', '(C)',
'dots', '...',
'equiv', '==',
'error', 'error-->',
'expansion', '==>',
'minus', '-',
'point', '-!-',
'print', '-|',
'result', '=>',
'today', $TODAY,
);
#
# texinfo styles (@foo{bar}) to HTML ones
#
%style_map = (
'asis', '',
'b', 'B',
'cite', 'CITE',
'code', 'CODE',
'ctrl', '&do_ctrl', # special case
'dfn', 'DFN',
'dmn', '', # useless
'emph', 'EM',
'file', '"TT', # will put quotes, cf. &apply_style
'i', 'I',
'kbd', 'KBD',
'key', 'KBD',
'r', '', # unsupported
'samp', '"SAMP', # will put quotes, cf. &apply_style
'sc', '&do_sc', # special case
'strong', 'STRONG',
't', 'TT',
'titlefont', '', # useless
'var', 'VAR',
'w', '', # unsupported
);
#
# texinfo format (@foo/@end foo) to HTML ones
#
%format_map = (
'display', 'PRE',
'example', 'PRE',
'format', 'PRE',
'lisp', 'PRE',
'quotation', 'BLOCKQUOTE',
'smallexample', 'PRE',
'smalllisp', 'PRE',
# lists
'itemize', 'UL',
'enumerate', 'OL',
# poorly supported
'flushleft', 'PRE',
'flushright', 'PRE',
);
#
# texinfo definition shortcuts to real ones
#
%def_map = (
# basic commands
'deffn', 0,
'defvr', 0,
'deftypefn', 0,
'deftypevr', 0,
'defcv', 0,
'defop', 0,
'deftp', 0,
# basic x commands
'deffnx', 0,
'defvrx', 0,
'deftypefnx', 0,
'deftypevrx', 0,
'defcvx', 0,
'defopx', 0,
'deftpx', 0,
# shortcuts
'defun', 'deffn Function',
'defmac', 'deffn Macro',
'defspec', 'deffn {Special Form}',
'defvar', 'defvr Variable',
'defopt', 'defvr {User Option}',
'deftypefun', 'deftypefn Function',
'deftypevar', 'deftypevr Variable',
'defivar', 'defcv {Instance Variable}',
'defmethod', 'defop Method',
# x shortcuts
'defunx', 'deffnx Function',
'defmacx', 'deffnx Macro',
'defspecx', 'deffnx {Special Form}',
'defvarx', 'defvrx Variable',
'defoptx', 'defvrx {User Option}',
'deftypefunx', 'deftypefnx Function',
'deftypevarx', 'deftypevrx Variable',
'defivarx', 'defcvx {Instance Variable}',
'defmethodx', 'defopx Method',
);
#
# things to skip
#
%to_skip = (
# comments
'c', 1,
'comment', 1,
# useless
'contents', 1,
'shortcontents', 1,
'summarycontents', 1,
'footnotestyle', 1,
'end ifclear', 1,
'end ifset', 1,
'iftex', 1,
'end iftex', 1,
'titlepage', 1,
'end titlepage', 1,
# unsupported commands (formatting)
'afourpaper', 1,
'cropmarks', 1,
'finalout', 1,
'headings', 1,
'need', 1,
'page', 1,
'setchapternewpage', 1,
'everyheading', 1,
'everyfooting', 1,
'evenheading', 1,
'evenfooting', 1,
'oddheading', 1,
'oddfooting', 1,
'smallbook', 1,
'vskip', 1,
# unsupported formats
'cartouche', 1,
'end cartouche', 1,
'group', 1,
'end group', 1,
# misc unsupported commands
'defindex', 1,
);
#+++############################################################################
# #
# Argument parsing, initialisation #
# #
#---############################################################################
$invisible_mark = '';
$use_bibliography = 1;
$usage = < \n");
next;
} elsif (defined($def_map{$tag})) {
if ($def_map{$tag}) {
s/^\@$tag\s+//;
$tag = $def_map{$tag};
$_ = "\@$tag $_";
$tag =~ s/\s.*//;
}
}
if (defined($def_map{$tag})) {
s/^\@$tag\s+//;
$tag =~ s/x$//;
1 while s/(\{[^\}]*)\s+([^\{]*\})/$1$;9$2/; # protect spaces inside {}
&protect_html;
@args = split(/\s+/, $_);
foreach(@args) {s/$;9/ /g;} # unprotect spaces
$type = shift(@args);
$type =~ s/^\{(.*)\}$/$1/;
print "# def ($tag): {$type} ", join(', ', @args), "\n"
if $debug & $DEBUG_DEF;
$type .= ':'; # it's nicer like this
$name = shift(@args);
$name =~ s/^\{(.*)\}$/$1/;
if ($tag eq 'deffn' || $tag eq 'defvr' || $tag eq 'deftp') {
$_ = "$type $name";
$_ .= " @args" if @args;
$_ .= " \n";
} elsif ($tag eq 'deftypefn' || $tag eq 'deftypevr'
|| $tag eq 'defcv' || $tag eq 'defop') {
$ftype = $name;
$name = shift(@args);
$name =~ s/^\{(.*)\}$/$1/;
$_ = "$type $ftype $name";
$_ .= " @args" if @args;
$_ .= " \n";
} else {
warn "Unknown definition type: $tag\n";
$_ = "$type $name";
$_ .= " @args" if @args;
$_ .= " \n";
}
if ($tag eq 'deffn' || $tag eq 'deftypefn' || $tag eq 'defop') {
unshift(@input_spool, "\@findex $name\n");
} elsif ($tag eq 'defvr' || $tag eq 'deftypevr' || $tag eq 'defcv') {
unshift(@input_spool, "\@vindex $name\n");
} else {
unshift(@input_spool, "\@tindex $name\n");
}
$dont_html = 1;
}
} elsif ($end_tag) {
if ($format_map{$end_tag}) {
$in_pre = 0 if $format_map{$end_tag} eq 'PRE';
$in_list-- if $format_map{$end_tag} eq 'UL' || $format_map{$end_tag} eq 'OL' ;
push(@lines, "$format_map{$end_tag}>\n");
} elsif ($end_tag eq 'table' ||
$end_tag eq 'ftable' ||
$end_tag eq 'vtable') {
shift(@tables);
if (@tables) {
($table_type, $in_table) = split($;, $tables[0]);
} else {
$in_table = 0;
}
push(@lines, " \n");
} elsif ($end_tag eq 'menu') {
push(@lines, $_); # must keep it for pass 2
}
next;
}
#
# misc things
#
# protect texi and HTML things
&protect_texi;
&protect_html unless $dont_html;
$dont_html = 0;
# substitution (unsupported things)
s/^\@center\s+//g && study;
s/^\@exdent\s+//g && study;
s/\@noindent\s+//g && study;
s/\@refill\s+//g && study;
# other substitutions
eval($subst_code);
s/\@value{($VARRE)}/$value{$1}/eg;
s/\@footnote\{/\@footnote$docu_doc\{/g; # mark footnotes, cf. pass 4
#
# analyze the tag again
#
if ($tag) {
if ($sec2level{$tag} > 0) {
if (/^\@$tag\s+(.+)$/) {
$name = $1;
$name =~ s/\s+$//;
$level = $sec2level{$tag};
if ($tag =~ /heading$/) {
$_ = " \n" if $_ eq "\n" && !$in_pre && !$in_list && !$in_table;
# otherwise
push(@lines, $_);
}
# finish TOC
$level = 0;
while ($level < $curlevel) {
$curlevel--;
push(@toc_lines, "\n");
}
print "# end of pass 1\n" if $verbose;
#+++############################################################################
# #
# Pass 2/3: handle style, menu, index, cross-reference #
# #
#---############################################################################
@lines2 = (); # whole document (2nd pass)
@lines3 = (); # whole document (3rd pass)
$in_menu = 0; # am I inside a menu
while (@lines) {
$_ = shift(@lines);
#
# special case (protected sections)
#
if (/^$PROTECTTAG/o) {
push(@lines2, $_);
next;
}
#
# menu
#
$in_menu = 1, push(@lines2, "
while (@lines3) {
$_ = shift(@lines3);
#
# special case (protected sections)
#
if (/^$PROTECTTAG/o) {
push(@doc_lines, $_);
$end_of_para = 0;
next;
}
#
# footnotes
#
while (/\@footnote([^\{\s]+)\{/) {
($before, $d, $after) = ($`, $1, $');
$_ = $after;
$text = '';
$after = '';
$failed = 1;
while (@lines3) {
if (/\}/) {
$text .= $`;
$after = $';
$failed = 0;
last;
} else {
$text .= $_;
$_ = shift(@lines3);
}
}
if ($failed) {
die "* Bad syntax (\@footnote) after: $before\n";
} else {
$id = 'FOOT' . ++$foot_num;
$foot = "($foot_num)";
push(@foot_lines, "
#
if ($_ eq " \n") {
next if $end_of_para++;
} else {
$end_of_para = 0;
}
# otherwise
push(@doc_lines, $_);
}
print "# end of pass 4\n" if $verbose;
#+++############################################################################
# #
# Pass 5: print things #
# #
#---############################################################################
$header = < \n";
&print(*toc_lines, FILE);
print FILE <' if $invisible_mark eq 'xbm';
die $usage unless @ARGV == 1;
$docu = shift(@ARGV);
if ($docu =~ /.*\//) {
chop($docu_dir = $&);
$docu_name = $';
} else {
$docu_dir = '.';
$docu_name = $docu;
}
$docu_name =~ s/\.te?x(i|info|i_pp)?$//;# basename of the document
$docu_toc = $docu_doc = $docu_foot = $docu_name;
$docu_toc .= '_toc.html'; # document's table of contents
$docu_doc .= '.html'; # document's contents
$docu_foot .= '_foot.html'; # document's footnotes
#
# variables
#
%value = (); # hold texinfo variables
$value{'html'} = 1; # predefine html (the output format)
$value{'texi2html'} = '1.31'; # predefine texi2html (the translator)
%node2sec = (); # node to section name
%node2href = (); # node to HREF
%bib2href = (); # bibliography reference to HREF
%gloss2href = (); # glossary term to HREF
@sections = (); # list of sections
%tag2pro = (); # protected sections
#
# initial indexes
#
$bib_num = 0;
$foot_num = 0;
$gloss_num = 0;
$idx_num = 0;
$sec_num = 0;
$doc_num = 0;
$html_num = 0;
#
# can I use ISO8879 characters? (HTML+)
#
if ($use_iso) {
$things_map{'bullet'} = "•";
$things_map{'copyright'} = "©";
$things_map{'dots'} = "…";
$things_map{'equiv'} = "≡";
$things_map{'expansion'} = "→";
$things_map{'point'} = "∗";
$things_map{'result'} = "⇒";
}
#
# read texi2html extensions (if any)
#
$extensions = 'texi2html.ext'; # extensions in working directory
if (-f $extensions) {
print "# reading extensions from $extensions\n" if $verbose;
require($extensions);
}
($progdir = $0) =~ s/[^\/]+$//;
if ($progdir && ($progdir ne './')) {
$extensions = "${progdir}texi2html.ext"; # extensions in texi2html directory
if (-f $extensions) {
print "# reading extensions from $extensions\n" if $verbose;
require($extensions);
}
}
print "# reading from $docu\n" if $verbose;
#+++############################################################################
# #
# Pass 1: read source, handle command, variable, simple substitution #
# #
#---############################################################################
@lines = (); # whole document
@toc_lines = (); # table of contents
$curlevel = 0; # current level in TOC
$node = ''; # current node name
$in_table = 0; # am I inside a table
$table_type = ''; # type of table ('', 'f', 'v')
@tables = (); # nested table support
$in_bibliography = 0; # am I inside a bibliography
$in_glossary = 0; # am I inside a glossary
$in_top = 0; # am I inside the top node
$in_pre = 0; # am I inside a preformatted section
$in_list = 0; # am I inside a list
$in_html = 0; # am I inside an HTML section
$first_line = 1; # is it the first line
$dont_html = 0; # don't protect HTML on this line
$split_num = 0; # split index
# build code for simple substitutions
# the maps used (%simple_map and %things_map) MUST be aware of this
# watch out for regexps, / and escaped characters!
$subst_code = '';
foreach (keys(%simple_map)) {
($re = $_) =~ s/(\W)/\\$1/g; # protect regexp chars
$subst_code .= "s/\\\@$re/$simple_map{$_}/g && study;\n";
}
foreach (keys(%things_map)) {
$subst_code .= "s/\\\@$_\\{\\}/$things_map{$_}/g && study;\n";
}
if ($use_acc) {
# accentuated characters
foreach(keys(%accent_map)) {
$subst_code .= "s/\\\@\\$_([aeiou])/&\${1}$accent_map{$_};/g && study;\n";
}
}
&init_input;
while ($_ = &next_line) {
#
# remove \input on the first lines only
#
if ($first_line) {
next if /^\\input/;
$first_line = 0;
}
#
# parse texinfo tags
#
$tag = '';
$end_tag = '';
if (/^\@end\s+(\w+)\b/) {
$end_tag = $1;
} elsif (/^\@(\w+)\b/) {
$tag = $1;
}
#
# handle @ifhtml / @end ifhtml
#
if ($in_html) {
if ($end_tag eq 'ifhtml') {
$in_html = 0;
} else {
$tag2pro{$in_html} .= $_;
}
next;
} elsif ($tag eq 'ifhtml') {
$in_html = $PROTECTTAG . ++$html_num;
push(@lines, $in_html);
next;
}
#
# try to skip the line
#
if ($end_tag) {
next if $to_skip{"end $end_tag"};
} elsif ($tag) {
next if $to_skip{$tag};
last if $tag eq 'bye';
}
if ($in_top) {
# parsing the top node
if ($tag eq 'node' || $tag eq 'include' || $sec2level{$tag}) {
# no more in top
$in_top = 0;
} else {
# skip it
next;
}
}
#
# try to remove inlined comments
# syntax from tex-mode.el comment-start-skip
#
s/((^|[^\@])(\@\@)*)\@c(omment)? .*/$1/;
# non-@ substitutions cf. texinfmt.el
s/``/"/g && study;
s/''/"/g && study;
s/([\w ])---([\w ])/$1--$2/g && study;
#
# analyze the tag
#
if ($tag) {
# skip lines
&skip_until($tag), next if $tag eq 'ignore';
&skip_until($tag), next if $tag eq 'ifinfo';
&skip_until($tag), next if $tag eq 'tex';
# handle special tables
if ($tag eq 'table') {
$table_type = '';
} elsif ($tag eq 'ftable') {
$tag = 'table';
$table_type = 'f';
} elsif ($tag eq 'vtable') {
$tag = 'table';
$table_type = 'v';
}
# special cases
if (0 && ($tag eq 'top' || ($tag eq 'node' && /^\@node\s+top\s*,/i))) {
$in_top = 1;
@lines = (); # ignore all lines before top (title page garbage)
next;
} elsif ($tag eq 'node') {
$in_top = 0;
&protect_html; # if node contains '&' for instance
warn "Bad node line: $_" unless $_ =~ /^\@node\s$NODESRE$/o;
s/^\@node\s+//;
($node) = split(/,/);
$node =~ s/\s+/ /g; # normalize
$node =~ s/ $//;
if ($split_node) {
&next_doc;
push(@lines, $SPLITTAG) if $split_num++;
push(@sections, $node);
}
next;
} elsif ($tag eq 'include') {
if (/^\@include\s+($FILERE)\s*$/o) {
$file = $1;
$file = "$docu_dir/$file" unless -e $file;
if (-e $file) {
&open($file);
print "# including $file\n" if $verbose;
} else {
warn "Can't find $file, skipping";
}
} else {
warn "Bad include line: $_";
}
next;
} elsif ($tag eq 'ifclear') {
if (/^\@ifclear\s+($VARRE)\s*$/o) {
next unless defined($value{$1});
&skip_until($tag);
} else {
warn "Bad ifclear line: $_";
}
next;
} elsif ($tag eq 'ifset') {
if (/^\@ifset\s+($VARRE)\s*$/o) {
next if defined($value{$1});
&skip_until($tag);
} else {
warn "Bad ifset line: $_";
}
next;
} elsif ($tag eq 'menu' && ! $show_menu) {
&skip_until($tag);
next;
} elsif ($format_map{$tag}) {
$in_pre = 1 if $format_map{$tag} eq 'PRE';
$in_list++ if $format_map{$tag} eq 'UL' || $format_map{$tag} eq 'OL' ;
push(@lines, "<$format_map{$tag}>\n");
next;
} elsif ($tag eq 'table') {
if (/^\@[fv]?table\s+\@(\w+)\s*$/) {
$in_table = $1;
unshift(@tables, join($;, $table_type, $in_table));
push(@lines, "
\n");
} else {
warn "Bad table line: $_";
}
next;
} elsif ($tag eq 'synindex' || $tag eq 'syncodeindex') {
if (/^\@$tag\s+(\w)\w\s+(\w)\w\s*$/) {
eval("*${1}index = *${2}index");
} else {
warn "Bad syn*index line: $_";
}
next;
} elsif ($tag eq 'sp') {
push(@lines, "
\n");
} elsif (defined($def_map{$end_tag})) {
push(@lines, "\n");
}
while ($level < $curlevel) {
$curlevel--;
push(@toc_lines, "
\n");
}
$_ = "\n"), next if /^\@menu\b/;
$in_menu = 0, push(@lines2, "
\n"), next if /^\@end\s+menu\b/;
if ($in_menu) {
if (/^\*\s+($NODERE)::/o) {
$descr = $';
chop($descr);
&menu_entry($1, $1, $descr);
} elsif (/^\*\s+(.+):\s+([^\t,\.\n]+)[\t,\.\n]/) {
$descr = $';
chop($descr);
&menu_entry($1, $2, $descr);
} elsif (/^\*/) {
warn "Bad menu line: $_";
} else { # description continued?
push(@lines2, $_);
}
next;
}
#
# printindex
#
if (/^\@printindex\s+(\w)\w\b/) {
eval("*ary = *$1index");
@keys = keys(%ary);
foreach$key (@keys) {
$_ = $key;
1 while s/<(\w+)>\`(.*)\'<\/\1>/$2/; # remove HTML tags with quotes
1 while s/<(\w+)>(.*)<\/\1>/$2/; # remove HTML tags
&unprotect_html;
&unprotect_texi;
tr/A-Z/a-z/; # lowercase
$key2alpha{$key} = $_;
print "# index $key sorted as $_\n"
if $key ne $_ && $debug & $DEBUG_INDEX;
}
$last_letter = '';
foreach(sort(byalpha @keys)) {
$letter = substr($key2alpha{$_}, 0, 1);
$letter = substr($key2alpha{$_}, 0, 2) if $letter eq $;;
if ($letter ne $last_letter) {
local($_) = $letter;
&protect_html;
push(@lines2, "\n") if $last_letter;
push(@lines2, "$_
\n");
push(@lines2, "" . &anchor($id, "$d#$id", $foot) . "
\n");
push(@foot_lines, "$text\n");
$_ = $before . &anchor($id, "$docu_foot#$id", $foot) . $after;
}
}
#
# remove unnecessary $title
\n";
if ($value{'subtitle'}) {
chop($value{'subtitle'}); # rmv last \n
foreach (split(/\n/, $value{'subtitle'})) {
$_ = &substitute_style($_);
&unprotect_texi;
print FILE "$_
\n";
}
}
if ($value{'author'}) {
chop($value{'author'}); # rmv last \n
foreach (split(/\n/, $value{'author'})) {
$_ = &substitute_style($_);
&unprotect_texi;
print FILE "$_\n";
}
}
print FILE "
\n";
# find corresponding lines
@tmp_lines = ();
while (@doc_lines) {
$_ = shift(@doc_lines);
last if ($_ eq $SPLITTAG);
push(@tmp_lines, $_);
}
&print(*tmp_lines, FILE);
print FILE "
\n$navigation";
&print_footer;
close(FILE);
} else {
warn "Can't write to $docu_doc: $!\n";
}
}
} else {
if (open(FILE, "> $docu_doc")) {
print "# creating $docu_doc...\n" if $verbose;
&print_header($title);
print FILE "$title
\n";
&print(*doc_lines, FILE);
&print_footer;
close(FILE);
} else {
warn "Can't write to $docu_doc: $!\n";
}
}
#
# print footnotes
#
if (@foot_lines) {
if (open(FILE, "> $docu_foot")) {
print "# creating $docu_foot...\n" if $verbose;
&print_header("$title - Footnotes");
print FILE "$title
\n";
&print(*foot_lines, FILE);
&print_footer;
close(FILE);
} else {
warn "Can't write to $docu_foot: $!\n";
}
}
print "# that's all folks\n" if $verbose;
#+++############################################################################
# #
# Low level functions #
# #
#---############################################################################
sub check {
local($_, %seen, %context, $before, $match, $after);
while (<>) {
if (/\@(\*|\.|\:|\@|\{|\})/) {
$seen{$&}++;
$context{$&} .= "> $_" if $verbose;
$_ = "$`XX$'";
redo;
}
if (/\@(\w+)/) {
($before, $match, $after) = ($`, $&, $');
if ($before =~ /\b[\w-]+$/ && $after =~ /^[\w-.]*\b/) { # e-mail address
$seen{'e-mail address'}++;
$context{'e-mail address'} .= "> $_" if $verbose;
} else {
$seen{$match}++;
$context{$match} .= "> $_" if $verbose;
}
$match =~ s/^\@/X/;
$_ = "$before$match$after";
redo;
}
}
foreach(sort(keys(%seen))) {
if ($verbose) {
print "$_\n";
print $context{$_};
} else {
print "$_ ($seen{$_})\n";
}
}
}
sub open {
local($name) = @_;
++$fh_name;
if (open($fh_name, $name)) {
unshift(@fhs, $fh_name);
} else {
warn "Can't read file $name: $!\n";
}
}
sub init_input {
@fhs = (); # hold the file handles to read
@input_spool = (); # spooled lines to read
$fh_name = 'FH000';
&open($docu);
}
sub next_line {
local($fh, $line);
if (@input_spool) {
$line = shift(@input_spool);
return($line);
}
while (@fhs) {
$fh = $fhs[0];
$line = <$fh>;
return($line) if $line;
close($fh);
shift(@fhs);
}
return(undef);
}
# used in pass 1, use &next_line
sub skip_until {
local($tag) = @_;
local($_);
while ($_ = &next_line) {
return if /^\@end\s+$tag\s*$/;
}
die "* Failed to find '$tag' after: " . $lines[$#lines];
}
sub menu_entry {
local($entry, $node, $descr) = @_;
local($href);
$href = $node2href{$node};
if ($href) {
$descr =~ s/^\s+//;
$descr = ": $descr" if $descr;
push(@lines2, "