home *** CD-ROM | disk | FTP | other *** search
/ AMIGA PD 1 / AMIGA-PD-1.iso / NetBSD / docs-netbsd / Networking-FAQ / texi2html < prev   
Text File  |  1994-11-27  |  41KB  |  1,559 lines

  1. #!/home2/rrzc1/fh/c9020/bin/perl
  2. 'di';
  3. 'ig00';
  4. #+##############################################################################
  5. #                                                                              #
  6. # File: texi2html                                                              #
  7. #                                                                              #
  8. # Description: Program to transform most Texinfo documents to HTML             #
  9. #                                                                              #
  10. #-##############################################################################
  11.  
  12. # @(#)texi2html    1.29 04/21/94    Written by Lionel Cons, Lionel.Cons@cern.ch
  13.  
  14. # The man page for this program is included at the end of this file and can be
  15. # viewed using the command 'nroff -man texi2html'.
  16. # Please read the copyright at the end of the man page.
  17.  
  18. #+++############################################################################
  19. #                                                                              #
  20. # Constants                                                                    #
  21. #                                                                              #
  22. #---############################################################################
  23.  
  24. $DEBUG_TOC = 1;
  25. $DEBUG_INDEX = 2;
  26. $DEBUG_BIB = 4;
  27. $DEBUG_GLOSS = 8;
  28. $DEBUG_DEF = 16;
  29.  
  30. $BIBRE = '\[[\w\/]+\]';            # RE for a bibliography reference
  31. $FILERE = '[\/\w.+-]+';            # RE for a file name
  32. $VARRE = '[^\s\{\}]+';            # RE for a variable name
  33. $NODERE = '[^@{}:\'`",]+';        # RE for a node name
  34. $NODESRE = '[^@{}:\'`"]+';        # RE for a list of node names
  35. $XREFRE = '[^@{}]+';            # RE for a xref (should use NODERE)
  36.  
  37. $THISPROG = "texi2html 1.29";            # program name and version
  38. $TODAY = &pretty_date;            # like "20 September 1993"
  39. $SPLITTAG = "<!-- SPLIT HERE -->\n";    # tag to know where to split
  40. $PROTECTTAG = "_ThisIsProtected_";    # tag to recognize protected sections
  41.  
  42. #
  43. # language dependent constants
  44. #
  45. $LDC_SEE = 'see';
  46. $LDC_SECTION = 'section';
  47. $LDC_IN = 'in';
  48. $LDC_TOC = 'Table of Contents';
  49. $LDC_GOTO = 'Go to the';
  50. $LDC_FOOT = 'Footnotes';
  51. # TODO: @def* shortcuts
  52.  
  53. #
  54. # texinfo section names to level
  55. #
  56. %sec2level = (
  57.           'top', 0,
  58.           'chapter', 1,
  59.           'unnumbered', 1,
  60.           'majorheading', 1,
  61.           'chapheading', 1,
  62.           'appendix', 1,
  63.           'section', 2,
  64.           'unnumberedsec', 2,
  65.           'heading', 2,
  66.           'appendixsec', 2,
  67.           'appendixsection', 2,
  68.           'subsection', 3,
  69.           'unnumberedsubsec', 3,
  70.           'subheading', 3,
  71.           'appendixsubsec', 3,
  72.           'subsubsection', 4,
  73.           'unnumberedsubsubsec', 4,
  74.           'subsubheading', 4,
  75.           'appendixsubsubsec', 4,
  76.           );
  77.  
  78. #
  79. # texinfo "simple things" (@foo) to HTML ones
  80. #
  81. %simple_map = (
  82.            # cf. makeinfo.c
  83.            "*", "<BR>",        # HTML+
  84.            " ", " ",
  85.            "\n", "\n",
  86.            "|", "",
  87.            # spacing commands
  88.            ":", "",
  89.            "!", "!",
  90.            "?", "?",
  91.            ".", ".",
  92.            );
  93.  
  94. #
  95. # texinfo "things" (@foo{}) to HTML ones
  96. #
  97. %things_map = (
  98.            'TeX', 'TeX',
  99.            'br', '<P>',        # paragraph break
  100.            'bullet', '*',
  101.            'copyright', '(C)',
  102.            'dots', '...',
  103.            'equiv', '==',
  104.            'error', 'error-->',
  105.            'expansion', '==>',
  106.            'minus', '-',
  107.            'point', '-!-',
  108.            'print', '-|',
  109.            'result', '=>',
  110.            'today', $TODAY,
  111.            );
  112.  
  113. #
  114. # texinfo styles (@foo{bar}) to HTML ones
  115. #
  116. %style_map = (
  117.           'asis', '',
  118.           'b', 'B',
  119.           'cite', 'CITE',
  120.           'code', 'CODE',
  121.           'ctrl', '&do_ctrl',    # special case
  122.           'dfn', 'DFN',
  123.           'dmn', '',        # useless
  124.           'emph', 'EM',
  125.           'file', '"TT',        # will put quotes, cf. &apply_style
  126.           'i', 'I',
  127.           'kbd', 'KBD',
  128.           'key', 'KBD',
  129.           'r', '',            # unsupported
  130.           'samp', '"SAMP',        # will put quotes, cf. &apply_style
  131.           'sc', '&do_sc',        # special case
  132.           'strong', 'STRONG',
  133.           't', 'TT',
  134.           'titlefont', '',        # useless
  135.           'var', 'VAR',
  136.           'w', '',            # unsupported
  137.           );
  138.  
  139. #
  140. # texinfo format (@foo/@end foo) to HTML ones
  141. #
  142. %format_map = (
  143.            'display', 'PRE',
  144.            'example', 'PRE',
  145.            'format', 'PRE',
  146.            'lisp', 'PRE',
  147.            'quotation', 'BLOCKQUOTE',
  148.            'smallexample', 'PRE',
  149.            'smalllisp', 'PRE',
  150.            # lists
  151.            'itemize', 'UL',
  152.            'enumerate', 'OL',
  153.            # poorly supported
  154.            'flushleft', 'PRE',
  155.            'flushright', 'PRE',
  156.            );
  157.  
  158. #
  159. # texinfo definition shortcuts to real ones
  160. #
  161. %def_map = (
  162.         # basic commands
  163.         'deffn', 0,
  164.         'defvr', 0,
  165.         'deftypefn', 0,
  166.         'deftypevr', 0,
  167.         'defcv', 0,
  168.         'defop', 0,
  169.         'deftp', 0,
  170.         # basic x commands
  171.         'deffnx', 0,
  172.         'defvrx', 0,
  173.         'deftypefnx', 0,
  174.         'deftypevrx', 0,
  175.         'defcvx', 0,
  176.         'defopx', 0,
  177.         'deftpx', 0,
  178.         # shortcuts
  179.         'defun', 'deffn Function',
  180.         'defmac', 'deffn Macro',
  181.         'defspec', 'deffn {Special Form}',
  182.         'defvar', 'defvr Variable',
  183.         'defopt', 'defvr {User Option}',
  184.         'deftypefun', 'deftypefn Function',
  185.         'deftypevar', 'deftypevr Variable',
  186.         'defivar', 'defcv {Instance Variable}',
  187.         'defmethod', 'defop Method',
  188.         # x shortcuts
  189.         'defunx', 'deffnx Function',
  190.         'defmacx', 'deffnx Macro',
  191.         'defspecx', 'deffnx {Special Form}',
  192.         'defvarx', 'defvrx Variable',
  193.         'defoptx', 'defvrx {User Option}',
  194.         'deftypefunx', 'deftypefnx Function',
  195.         'deftypevarx', 'deftypevrx Variable',
  196.         'defivarx', 'defcvx {Instance Variable}',
  197.         'defmethodx', 'defopx Method',
  198.         );
  199.  
  200. #
  201. # things to skip
  202. #
  203. %to_skip = (
  204.         # comments
  205.         'c', 1,
  206.         'comment', 1,
  207.         # useless
  208.         'contents', 1,
  209.         'shortcontents', 1,
  210.         'summarycontents', 1,
  211.         'footnotestyle', 1,
  212.         'end ifclear', 1,
  213.         'end ifset', 1,
  214.         'iftex', 1,
  215.         'end iftex', 1,
  216.         'titlepage', 1,
  217.         'end titlepage', 1,
  218.         # unsupported commands (formatting)
  219.         'afourpaper', 1,
  220.         'cropmarks', 1,
  221.         'finalout', 1,
  222.         'headings', 1,
  223.         'need', 1,
  224.         'page', 1,
  225.         'setchapternewpage', 1,
  226.         'everyheading', 1,
  227.         'everyfooting', 1,
  228.         'evenheading', 1,
  229.         'evenfooting', 1,
  230.         'oddheading', 1,
  231.         'oddfooting', 1,
  232.         'smallbook', 1,
  233.         'vskip', 1,
  234.         # unsupported formats
  235.         'cartouche', 1,
  236.         'end cartouche', 1,
  237.         'group', 1,
  238.         'end group', 1,
  239.         # misc unsupported commands
  240.             'defindex', 1,
  241.         );
  242.  
  243. #+++############################################################################
  244. #                                                                              #
  245. # Argument parsing, initialisation                                             #
  246. #                                                                              #
  247. #---############################################################################
  248.  
  249. $invisible_mark = '';
  250. $use_bibliography = 1;
  251. $usage = <<EOT;
  252. This is $THISPROG
  253. To convert a Texinfo file to HMTL: $0 [options] file
  254.   where options can be:
  255.     -glossary      : handle a glossary
  256.     -invisible name: use name as an invisible anchor
  257.     -menu          : handle menus
  258.     -split_chapter : split on main sections
  259.     -split_node    : split on nodes
  260.     -usage         : print usage instructions
  261.     -verbose       : verbose output
  262. To check converted files: $0 -check [-verbose] files
  263. EOT
  264.  
  265. while ($_ = $ARGV[0], /^-/) {
  266.     shift;
  267.     if (/^-d(ebug)?(\d+)?$/) { $debug = $2 || shift; next; }
  268.     if (/^-c(heck)?$/)       { $check = 1; next; }
  269.     if (/^-g(lossary)?$/)    { $use_glossary = 1; next; }
  270.     if (/^-i(nvisible)?$/)   { $invisible_mark = shift; next; }
  271.     if (/^-iso$/)            { $use_iso = 1; next; }
  272.     if (/^-m(enu)?$/)        { $show_menu = 1; next; }
  273.     if (/^-s(plit)?_?(n(ode)?|c(hapter)?)?$/) {
  274.     if ($2 =~ /^n/) {
  275.         $split_node = 1;
  276.     } else {
  277.         $split_chapter = 1;
  278.     }
  279.     next;
  280.     }
  281.     if (/^-v(erbose)?$/)     { $verbose = 1; next; }
  282.     die $usage;
  283. }
  284.  
  285. if ($check) {
  286.     die $usage unless @ARGV > 0;
  287.     ✓
  288.     exit;
  289. }
  290.  
  291. $invisible_mark = '<IMG SRC="invisible.xbm">' if $invisible_mark eq 'xbm';
  292. die $usage unless @ARGV == 1;
  293. $docu = shift(@ARGV);
  294. if ($docu =~ /.*\//) {
  295.     chop($docu_dir = $&);
  296.     $docu_name = $';
  297. } else {
  298.     $docu_dir = '.';
  299.     $docu_name = $docu;
  300. }
  301. $docu_name =~ s/\.te?x(i|info)?$//;    # basename of the document
  302.  
  303. $docu_toc = $docu_doc = $docu_foot = $docu_name;
  304. $docu_toc  .= '_toc.html';        # document's table of contents
  305. $docu_doc  .= '.html';            # document's contents
  306. $docu_foot .= '_foot.html';        # document's footnotes
  307.  
  308. #
  309. # variables
  310. #
  311. %value = ();                # hold texinfo variables
  312. $value{'html'} = 1;            # predefine html (the output format)
  313. $value{'texi2html'} = '1.29';        # predefine texi2html (the translator)
  314. %node2sec = ();                # node to section name
  315. %node2href = ();            # node to HREF
  316. %bib2href = ();                # bibliography reference to HREF
  317. %gloss2href = ();            # glossary term to HREF
  318. @sections = ();                # list of sections
  319. %tag2pro = ();                # protected sections
  320.  
  321. #
  322. # initial indexes
  323. #
  324. $bib_num = 0;
  325. $foot_num = 0;
  326. $gloss_num = 0;
  327. $idx_num = 0;
  328. $sec_num = 0;
  329. $doc_num = 0;
  330. $html_num = 0;
  331.  
  332. #
  333. # can I use ISO8879 characters? (HTML+)
  334. #
  335. if ($use_iso) {
  336.     $things_map{'bullet'} = "•";
  337.     $things_map{'copyright'} = "©";
  338.     $things_map{'dots'} = "…";
  339.     $things_map{'equiv'} = "≡";
  340.     $things_map{'expansion'} = "→";
  341.     $things_map{'point'} = "∗";
  342.     $things_map{'result'} = "⇒";
  343. }
  344.  
  345. #
  346. # read texi2html extensions (if any)
  347. #
  348. $extensions = 'texi2html.ext'; # extensions in working directory
  349. if (-f $extensions) {
  350.     print "# reading extensions from $extensions\n" if $verbose;
  351.     require($extensions);
  352. }
  353. ($progdir = $0) =~ s/[^\/]+$//;
  354. if ($progdir && ($progdir ne './')) {
  355.     $extensions = "${progdir}texi2html.ext"; # extensions in texi2html directory
  356.     if (-f $extensions) {
  357.     print "# reading extensions from $extensions\n" if $verbose;
  358.     require($extensions);
  359.     }
  360. }
  361.  
  362. print "# reading from $docu\n" if $verbose;
  363.  
  364. #+++############################################################################
  365. #                                                                              #
  366. # Pass 1: read source, handle command, variable, simple substitution           #
  367. #                                                                              #
  368. #---############################################################################
  369.  
  370. @lines = ();                # whole document
  371. @toc_lines = ();            # table of contents
  372. $curlevel = 0;                # current level in TOC
  373. $node = '';                # current node name
  374. $in_table = 0;                # am I inside a table
  375. $table_type = '';            # type of table ('', 'f', 'v')
  376. $in_bibliography = 0;            # am I inside a bibliography
  377. $in_glossary = 0;            # am I inside a glossary
  378. $in_top = 0;                # am I inside the top node
  379. $in_pre = 0;                # am I inside a preformatted section
  380. $in_html = 0;                # am I inside an HTML section
  381. $first_line = 1;                # is it the first line
  382. $dont_html = 0;                # don't protect HTML on this line
  383. $split_num = 0;                # split index
  384.  
  385. # build code for simple substitutions
  386. # the maps used (%simple_map and %things_map) MUST be aware of this
  387. # watch out for regexps, / and escaped characters!
  388. $subst_code = '';
  389. for (keys(%simple_map)) {
  390.     ($re = $_) =~ s/(\W)/\\$1/g; # protect regexp chars
  391.     $subst_code .= "s/\\@$re/$simple_map{$_}/g && study;\n";
  392. }
  393. for (keys(%things_map)) {
  394.     $subst_code .= "s/\\@$_\\{\\}/$things_map{$_}/g && study;\n";
  395. }
  396.  
  397. &init_input;
  398. while ($_ = &next_line) {
  399.     #
  400.     # remove \input on the first line
  401.     #
  402.     if ($first_line) {
  403.     $first_line = 0;
  404.     next if /^\\input/;
  405.     }
  406.     #
  407.     # parse texinfo tags
  408.     #
  409.     $tag = '';
  410.     $end_tag = '';
  411.     if (/^\@end\s+(\w+)\b/) {
  412.     $end_tag = $1;
  413.     } elsif (/^\@(\w+)\b/) {
  414.     $tag = $1;
  415.     }
  416.     #
  417.     # handle @ifhtml / @end ifhtml
  418.     #
  419.     if ($in_html) {
  420.     if ($end_tag eq 'ifhtml') {
  421.         $in_html = 0;
  422.     } else {
  423.         $tag2pro{$in_html} .= $_;
  424.     }
  425.     next;
  426.     } elsif ($tag eq 'ifhtml') {
  427.     $in_html = $PROTECTTAG . ++$html_num;
  428.     push(@lines, $in_html);
  429.     next;
  430.     }
  431.     #
  432.     # try to skip the line
  433.     #
  434.     if ($end_tag) {
  435.     next if $to_skip{"end $end_tag"};
  436.     } elsif ($tag) {
  437.     next if $to_skip{$tag};
  438.     last if $tag eq 'bye';
  439.     }
  440.     if ($in_top) {
  441.     # parsing the top node
  442.     if ($tag eq 'node' || $tag eq 'include' || $sec2level{$tag}) {
  443.         # no more in top
  444.         $in_top = 0;
  445.     } else {
  446.         # skip it
  447.         next;
  448.     }
  449.     }
  450.     #
  451.     # try to remove inlined comments
  452.     # syntax from tex-mode.el comment-start-skip
  453.     #
  454.     s/((^|[^\@])(\@\@)*)\@c(omment)? .*/\1/;
  455.     #
  456.     # analyze the tag
  457.     #
  458.     if ($tag) {
  459.     # skip lines
  460.     &skip_until($tag), next if $tag eq 'ignore';
  461.     &skip_until($tag), next if $tag eq 'ifinfo';
  462.     &skip_until($tag), next if $tag eq 'tex';
  463.     # handle special tables
  464.     if ($tag eq 'table') {
  465.         $table_type = '';
  466.     } elsif ($tag eq 'ftable') {
  467.         $tag = 'table';
  468.         $table_type = 'f';
  469.     } elsif ($tag eq 'vtable') {
  470.         $tag = 'table';
  471.         $table_type = 'v';
  472.     }
  473.     # special cases
  474.     if ($tag eq 'top' || ($tag eq 'node' && /^\@node\s+top\s*,/i)) {
  475.         $in_top = 1;
  476.         @lines = (); # ignore all lines before top (title page garbage)
  477.         next;
  478.     } elsif ($tag eq 'node') {
  479.         $in_top = 0;
  480.         &protect_html;    # if node contains '&' for instance
  481.         warn "Bad node line: $_" unless $_ =~ /^\@node\s$NODESRE$/o;
  482.         s/^\@node\s+//;
  483.         ($node) = split(/,/);
  484.         $node =~ s/\s+/ /g; # normalize
  485.         $node =~ s/ $//;
  486.         if ($split_node) {
  487.         &next_doc;
  488.         push(@lines, $SPLITTAG) if $split_num++;
  489.         push(@sections, $node);
  490.         }
  491.         next;
  492.     } elsif ($tag eq 'include') {
  493.         if (/^\@include\s+($FILERE)\s*$/o) {
  494.         $file = $1;
  495.         $file = "$docu_dir/$file" unless -e $file;
  496.         if (-e $file) {
  497.             &open($file);
  498.             print "# including $file\n" if $verbose;
  499.         } else {
  500.             warn "Can't find $file, skipping";
  501.         }
  502.         } else {
  503.         warn "Bad include line: $_";
  504.         }
  505.         next;
  506.     } elsif ($tag eq 'ifclear') {
  507.         if (/^\@ifclear\s+($VARRE)\s*$/o) {
  508.         next unless defined($value{$1});
  509.         &skip_until($tag);
  510.         } else {
  511.         warn "Bad ifclear line: $_";
  512.         }
  513.         next;
  514.     } elsif ($tag eq 'ifset') {
  515.         if (/^\@ifset\s+($VARRE)\s*$/o) {
  516.         next if defined($value{$1});
  517.         &skip_until($tag);
  518.         } else {
  519.         warn "Bad ifset line: $_";
  520.         }
  521.         next;
  522.     } elsif ($tag eq 'menu' && ! $show_menu) {
  523.         &skip_until($tag);
  524.         next;
  525.     } elsif ($format_map{$tag}) {
  526.         $in_pre = 1 if $format_map{$tag} eq 'PRE';
  527.         push(@lines, "<$format_map{$tag}>\n");
  528.         next;
  529.     } elsif ($tag eq 'table') {
  530.         if (/^\@[fv]?table\s+\@(\w+)\s*$/) {
  531.         $in_table = $1;
  532.         push(@lines, "<DL COMPACT>\n");
  533.         } else {
  534.         warn "Bad table line: $_";
  535.         }
  536.         next;
  537.     } elsif ($tag eq 'synindex' || $tag eq 'syncodeindex') {
  538.         if (/^\@$tag\s+(\w)\w\s+(\w)\w\s*$/) {
  539.         eval("*${1}index = *${2}index");
  540.         } else {
  541.         warn "Bad syn*index line: $_";
  542.         }
  543.         next;
  544.     } elsif ($tag eq 'sp') {
  545.         push(@lines, "<P>\n");
  546.         next;
  547.     } elsif (defined($def_map{$tag})) {
  548.         if ($def_map{$tag}) {
  549.         s/^\@$tag\s+//;
  550.         $tag = $def_map{$tag};
  551.         $_ = "\@$tag $_";
  552.         $tag =~ s/\s.*//;
  553.         }
  554.     }
  555.     if (defined($def_map{$tag})) {
  556.         s/^\@$tag\s+//;
  557.         $tag =~ s/x$//;
  558.         1 while s/(\{[^\}]*)\s+([^\{]*\})/$1$;9$2/; # protect spaces inside {}
  559.         @args = split(/\s+/, $_);
  560.         for (@args) {s/$;9/ /g;} # unprotect spaces
  561.         $type = shift(@args);
  562.         $type =~ s/^\{(.*)\}$/$1/;
  563.         print "# def ($tag): {$type} ", join(', ', @args), "\n"
  564.         if $debug & $DEBUG_DEF;
  565.         $type .= ':'; # it's nicer like this
  566.         $name = shift(@args);
  567.         $name =~ s/^\{(.*)\}$/$1/;
  568.         if ($tag eq 'deffn' || $tag eq 'defvr' || $tag eq 'deftp') {
  569.         $_ = "<U>$type</U> <B>$name</B>";
  570.         $_ .= " <I>@args</I>" if @args;
  571.         $_ .= "<P>\n";
  572.         } elsif ($tag eq 'deftypefn' || $tag eq 'deftypevr'
  573.              || $tag eq 'defcv' || $tag eq 'defop') {
  574.         $ftype = $name;
  575.         $name = shift(@args);
  576.         $name =~ s/^\{(.*)\}$/$1/;
  577.         $_ = "<U>$type</U> $ftype <B>$name</B>";
  578.         $_ .= " <I>@args</I>" if @args;
  579.         $_ .= "<P>\n";
  580.         } else {
  581.         warn "Unknown definition type: $tag\n";
  582.         $_ = "<U>$type</U> <B>$name</B>";
  583.         $_ .= " <I>@args</I>" if @args;
  584.         $_ .= "<P>\n";
  585.         }
  586.         if ($tag eq 'deffn' || $tag eq 'deftypefn' || $tag eq 'defop') {
  587.         unshift(@input_spool, "\@findex $name\n");
  588.         } elsif ($tag eq 'defvr' || $tag eq 'deftypevr' || $tag eq 'defcv') {
  589.         unshift(@input_spool, "\@vindex $name\n");
  590.         } else {
  591.         unshift(@input_spool, "\@tindex $name\n");
  592.         }
  593.         $dont_html = 1;
  594.     }
  595.     } elsif ($end_tag) {
  596.     if ($format_map{$end_tag}) {
  597.         $in_pre = 0 if $format_map{$end_tag} eq 'PRE';
  598.         push(@lines, "</$format_map{$end_tag}>\n");
  599.     } elsif ($end_tag eq 'table' ||
  600.          $end_tag eq 'ftable' ||
  601.          $end_tag eq 'vtable') {
  602.         $in_table = 0;
  603.         push(@lines, "</DL>\n");
  604.     } elsif (defined($def_map{$end_tag})) {
  605.         push(@lines, "<P>\n");
  606.     } elsif ($end_tag eq 'menu') {
  607.         push(@lines, $_); # must keep it for pass 2
  608.     }
  609.     next;
  610.     }
  611.     #
  612.     # misc things
  613.     #
  614.     # protect texi and HTML things
  615.     &protect_texi;
  616.     &protect_html unless $dont_html;
  617.     $dont_html = 0;
  618.     # non-@ substitutions cf. texinfmt.el
  619.     s/``/"/g && study;
  620.     s/''/"/g && study;
  621.     s/([\w ])---([\w ])/$1--$2/g && study;
  622.     # substitution (unsupported things)
  623.     s/^\@center\s+//g && study;
  624.     s/^\@exdent\s+//g && study;
  625.     s/\@noindent\s+//g && study;
  626.     s/\@refill\s+//g && study;
  627.     # other substitutions
  628.     eval($subst_code);
  629.     s/\@value{($VARRE)}/$value{$1}/eg;
  630.     s/\@footnote\{/\@footnote$docu_doc\{/g; # mark footnotes, cf. pass 4
  631.     #
  632.     # analyze the tag again
  633.     #
  634.     if ($tag) {
  635.     if ($sec2level{$tag} > 0) {
  636.         if (/^\@$tag\s+(.+)$/) {
  637.         $name = $1;
  638.         $name =~ s/\s+$//;
  639.         $level = $sec2level{$tag};
  640.         if ($tag =~ /heading$/) {
  641.             $_ = "<H$level>$name</H$level>\n";
  642.             print "# heading, section $name, level $level\n"
  643.             if $debug & $DEBUG_TOC;
  644.         } else {
  645.             if ($split_chapter && $level == 1) {
  646.             &next_doc;
  647.             push(@lines, $SPLITTAG) if $split_num++;
  648.             push(@sections, $name);
  649.             }
  650.             $id = 'SEC' . ++$sec_num;
  651.             # check biblio and glossary
  652.             $in_bibliography = ($name =~ /^bibliography$/i);
  653.             $in_glossary = ($name =~ /^glossary$/i);
  654.             # check node
  655.             if ($node) {
  656.             if ($node2sec{$node}) {
  657.                 warn "Duplicate node found: $node\n";
  658.             } else {
  659.                 $node2sec{$node} = $name;
  660.                 $node2href{$node} = "$docu_doc#$id";
  661.                 print "# node $node, section $name, level $level\n"
  662.                 if $debug & $DEBUG_TOC;
  663.             }
  664.             $node = '';
  665.             } else {
  666.             print "# no node, section $name, level $level\n"
  667.                 if $debug & $DEBUG_TOC;
  668.             }
  669.             # update TOC
  670.             while ($level > $curlevel) {
  671.             $curlevel++;
  672.             push(@toc_lines, "<UL>\n");
  673.             }
  674.             while ($level < $curlevel) {
  675.             $curlevel--;
  676.             push(@toc_lines, "</UL>\n");
  677.             }
  678.             $_ = "<LI>" . &anchor($id, "$docu_doc#$id", $name, 1);
  679.             push(@toc_lines, &substitute_style($_));
  680.             # update DOC
  681.             $_ =  "<H$level>" . &anchor($id, "$docu_toc#$id", $name) . "</H$level>\n";
  682.         }
  683.         # update DOC
  684.         push(@lines, $_);
  685.         next;
  686.         } else {
  687.         warn "Bad section line: $_";
  688.         }
  689.     } else {
  690.         # track variables
  691.         $value{$1} = $2, next if /^\@set\s+($VARRE)\s+(.*)$/o;
  692.         delete $value{$1}, next if /^\@clear\s+($VARRE)\s*$/o;
  693.         # store things
  694.         $value{'filename'} = $1, next if /^\@setfilename\s+(.*)$/;
  695.         $value{'author'} .= "$1\n", next if /^\@author\s+(.*)$/;
  696.         $value{'subtitle'} .= "$1\n", next if /^\@subtitle\s+(.*)$/;
  697.         $value{'title'} = $1, next if /^\@settitle\s+(.*)$/;
  698.         $value{'title'} = $1, next if /^\@title\s+(.*)$/;
  699.         # index
  700.         if (/^\@(.index)\s+/) {
  701.         $id = 'IDX' . ++$idx_num;
  702.         $index = $1;
  703.         $what = &substitute_style($');
  704.         $what =~ s/\s+$//;
  705.         print "# found $index for '$what' id $id\n"
  706.             if $debug & $DEBUG_INDEX;
  707.         eval("\$$index\{\$what\} = \"$docu_doc#$id\"");
  708.         # check last line
  709.         if ($lines[$#lines] =~ /^\</) {
  710.             # we can safely (?) insert the index before the last line
  711.             splice(@lines, -1, 0, &anchor($id, '', $invisible_mark, 1));
  712.         } else {
  713.             # it's safer to append the index
  714.             push(@lines, &anchor($id, '', $invisible_mark, !$in_pre));
  715.         }
  716.         next;
  717.         }
  718.         # list item
  719.         if (/^\@itemx?\s+/) {
  720.         $what = $';
  721.         $what =~ s/\s+$//;
  722.         if ($in_bibliography && $use_bibliography) {
  723.             if ($what =~ /^$BIBRE$/o) {
  724.             $id = 'BIB' . ++$bib_num;
  725.             $bib2href{$what} = "$docu_doc#$id";
  726.             print "# found bibliography for '$what' id $id\n"
  727.                 if $debug & $DEBUG_BIB;
  728.             $what = &anchor($id, '', $what);
  729.             }
  730.         } elsif ($in_glossary && $use_glossary) {
  731.             $id = 'GLOSS' . ++$gloss_num;
  732.             $entry = $what;
  733.             $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  734.             $gloss2href{$entry} = "$docu_doc#$id";
  735.             print "# found glossary for '$entry' id $id\n"
  736.             if $debug & $DEBUG_GLOSS;
  737.             $what = &anchor($id, '', $what);
  738.         }
  739.         if ($in_table) {
  740.             if ($things_map{$in_table} && !$what) {
  741.             # special case to allow @table @bullet for instance
  742.             push(@lines, "<DT>$things_map{$in_table}\n<DD>");
  743.             } else {
  744.             push(@lines, "<DT>\@$in_table{$what}\n<DD>");
  745.             }
  746.             if ($table_type) { # add also an index
  747.             unshift(@input_spool, "\@${table_type}index $what\n");
  748.             }
  749.         } else {
  750.             push(@lines, "<LI>$what\n");
  751.         }
  752.         next;
  753.         }
  754.     }
  755.     }
  756.     # paragraph separator
  757.     $_ = "<P>\n" if $_ eq "\n" && ! $in_pre;
  758.     # otherwise
  759.     push(@lines, $_);
  760. }
  761.  
  762. # finish TOC
  763. $level = 0;
  764. while ($level < $curlevel) {
  765.     $curlevel--;
  766.     push(@toc_lines, "</UL>\n");
  767. }
  768.  
  769. print "# end of pass 1\n" if $verbose;
  770.  
  771. #+++############################################################################
  772. #                                                                              #
  773. # Pass 2/3: handle style, menu, index, cross-reference                         #
  774. #                                                                              #
  775. #---############################################################################
  776.  
  777. @lines2 = ();                # whole document (2nd pass)
  778. @lines3 = ();                # whole document (3rd pass)
  779. $in_menu = 0;                # am I inside a menu
  780.  
  781. while (@lines) {
  782.     $_ = shift(@lines);
  783.     #
  784.     # special case (protected sections)
  785.     #
  786.     if (/^$PROTECTTAG/o) {
  787.     push(@lines2, $_);
  788.     next;
  789.     }
  790.     #
  791.     # menu
  792.     #
  793.     $in_menu = 1, push(@lines2, "<UL>\n"), next if /^\@menu\b/;
  794.     $in_menu = 0, push(@lines2, "</UL>\n"), next if /^\@end\s+menu\b/;
  795.     if ($in_menu) {
  796.     if (/^\*\s+($NODERE)::/o) {
  797.         $descr = $';
  798.         chop($descr);
  799.         &menu_entry($1, $1, $descr);
  800.     } elsif (/^\*\s+(.+):\s+([^\t,\.\n]+)[\t,\.\n]/) {
  801.         $descr = $';
  802.         chop($descr);
  803.         &menu_entry($1, $2, $descr);
  804.     } elsif (/^\*/) {
  805.         warn "Bad menu line: $_";
  806.     } else { # description continued?
  807.         push(@lines2, $_);
  808.     }
  809.     next;
  810.     }
  811.     #
  812.     # printindex
  813.     #
  814.     if (/^\@printindex\s+(\w)\w\b/) {
  815.     eval("*ary = *$1index");
  816.     @keys = keys(%ary);
  817.     for $key (@keys) {
  818.         $_ = $key;
  819.         1 while s/<(\w+)>\`(.*)\'<\/\1>/$2/; # remove HTML tags with quotes
  820.         1 while s/<(\w+)>(.*)<\/\1>/$2/; # remove HTML tags
  821.         &unprotect_html;
  822.         &unprotect_texi;
  823.         tr/A-Z/a-z/; # lowercase
  824.         $key2alpha{$key} = $_;
  825.         print "# index $key sorted as $_\n"
  826.         if $key ne $_ && $debug & $DEBUG_INDEX;
  827.     }
  828.     $last_letter = '';
  829.     push(@lines2, "<DIR>\n");
  830.     for (sort(byalpha @keys)) {
  831.         $letter = substr($key2alpha{$_}, 0, 1);
  832.         $letter = substr($key2alpha{$_}, 0, 2) if $letter eq $;;
  833.         if ($letter ne $last_letter) {
  834.         local($_) = $letter;
  835.         &protect_html;
  836.         push(@lines2, "<H2>$_</H2>\n");
  837.         $last_letter = $letter;
  838.         }
  839.         push(@lines2, "<LI>" . &anchor('', $ary{$_}, $_, 1));
  840.     }
  841.     push(@lines2, "</DIR>\n");
  842.     next;
  843.     }
  844.     #
  845.     # simple style substitutions
  846.     #
  847.     $_ = &substitute_style($_);
  848.     #
  849.     # xref
  850.     #
  851.     while (/\@(x|px|info|)ref{($XREFRE)(}?)/o) {
  852.     # note: Texinfo may accept other characters
  853.     ($type, $nodes, $full) = ($1, $2, $3);
  854.     ($before, $after) = ($`, $');
  855.     if (! $full && $after) {
  856.         warn "* Bad xref (no ending } on line): $_";
  857.         $_ = "$before$;0${type}ref\{$nodes$after";
  858.         next; # while xref
  859.     }
  860.     if ($type eq 'x') {
  861.         $type = 'See ';
  862.     } elsif ($type eq 'px') {
  863.         $type = 'see ';
  864.     } elsif ($type eq 'info') {
  865.         $type = 'See Info';
  866.     } else {
  867.         $type = '';
  868.     }
  869.     unless ($full) {
  870.         $next = shift(@lines);
  871.         chop($nodes); # remove final newline
  872.         if ($next =~ /\}/) { # splitted on 2 lines
  873.         $nodes .= " $`";
  874.         $after = $';
  875.         } else {
  876.         $nodes .= " $next";
  877.         $next = shift(@lines);
  878.         chop($nodes);
  879.         if ($next =~ /\}/) { # splitted on 3 lines
  880.             $nodes .= " $`";
  881.             $after = $';
  882.         } else {
  883.             warn "* Bad xref (no ending }): $_";
  884.             $_ = "$before$;0xref\{$nodes$after";
  885.             unshift(@lines, $next);
  886.             next; # while xref
  887.         }
  888.         }
  889.     }
  890.     $nodes =~ s/\s+/ /g; # normalize
  891.     @args = split(/\s*,\s*/, $nodes);
  892.     $node = $args[0]; # the node is always the first arg
  893.     $sec = $node2sec{$node};
  894.     if (@args == 5) { # reference to another manual
  895.         $sec = $args[2] || $node;
  896.         $man = $args[4] || $args[3];
  897.         $_ = "${before}${type}section `$sec' in \@cite{$man}$after";
  898.     } elsif ($type =~ /Info/) { # inforef
  899.         warn "Wrong number of arguments: $_" unless @args == 3;
  900.         ($nn, $rn, $in) = @args;
  901.         $_ = "${before}${type} file `$in', node `$nn'$after";
  902.     } elsif ($sec) {
  903.         $href = $node2href{$node};
  904.         $_ = "${before}${type}section " . &anchor('', $href, $sec) . $after;
  905.     } else {
  906.         warn "Undefined node ($node): $_";
  907.         $_ = "$before$;0xref{$nodes}$after";
  908.     }
  909.     }
  910.     #
  911.     # try to guess bibliography references or glossary terms
  912.     #
  913.     unless (/^<H\d><A NAME=\"SEC\d/) {
  914.     if ($use_bibliography) {
  915.         $done = '';
  916.         while (/$BIBRE/o) {
  917.         ($pre, $what, $post) = ($`, $&, $');
  918.         $href = $bib2href{$what};
  919.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  920.             $done .= $pre . &anchor('', $href, $what);
  921.         } else {
  922.             $done .= "$pre$what";
  923.         }
  924.         $_ = $post;
  925.         }
  926.         $_ = $done . $_;
  927.     }
  928.     if ($use_glossary) {
  929.         $done = '';
  930.         while (/\b\w+\b/) {
  931.         ($pre, $what, $post) = ($`, $&, $');
  932.         $entry = $what;
  933.         $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  934.         $href = $gloss2href{$entry};
  935.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  936.             $done .= $pre . &anchor('', $href, $what);
  937.         } else {
  938.             $done .= "$pre$what";
  939.         }
  940.         $_ = $post;
  941.         }
  942.         $_ = $done . $_;
  943.     }
  944.     }
  945.     # otherwise
  946.     push(@lines2, $_);
  947. }
  948. print "# end of pass 2\n" if $verbose;
  949.  
  950. #
  951. # splitted style substitutions
  952. #
  953. while (@lines2) {
  954.     $_ = shift(@lines2);
  955.     #
  956.     # special case (protected sections)
  957.     #
  958.     if (/^$PROTECTTAG/o) {
  959.     push(@lines3, $_);
  960.     next;
  961.     }
  962.     #
  963.     # splitted style substitutions
  964.     #
  965.     $old = '';
  966.     while ($old ne $_) {
  967.         $old = $_;
  968.     if (/\@(\w+)\{/) {
  969.         ($before, $style, $after) = ($`, $1, $');
  970.         if (defined($style_map{$style})) {
  971.         $_ = $after;
  972.         $text = '';
  973.         $after = '';
  974.         $failed = 1;
  975.         while (@lines2) {
  976.             if (/\}/) {
  977.             $text .= $`;
  978.             $after = $';
  979.             $failed = 0;
  980.             last;
  981.             } else {
  982.             $text .= $_;
  983.             $_ = shift(@lines2);
  984.             }
  985.         }
  986.         if ($failed) {
  987.             die "* Bad syntax (\@$style) after: $before\n";
  988.         } else {
  989.             $text = &apply_style($style, $text);
  990.             $_ = "$before$text$after";
  991.         }
  992.         }
  993.     }
  994.     }
  995.     # otherwise
  996.     push(@lines3, $_);
  997. }
  998. print "# end of pass 3\n" if $verbose;
  999.  
  1000. #+++############################################################################
  1001. #                                                                              #
  1002. # Pass 4: foot notes, final cleanup                                            #
  1003. #                                                                              #
  1004. #---############################################################################
  1005.  
  1006. @lines4 = ();                # whole document (4th pass)
  1007. @foot_lines = ();            # footnotes
  1008. @doc_lines = ();            # final document
  1009. $end_of_para = 0;            # true if last line is <P>
  1010.  
  1011. while (@lines3) {
  1012.     $_ = shift(@lines3);
  1013.     #
  1014.     # special case (protected sections)
  1015.     #
  1016.     if (/^$PROTECTTAG/o) {
  1017.     push(@doc_lines, $_);
  1018.     $end_of_para = 0;
  1019.     next;
  1020.     }
  1021.     #
  1022.     # footnotes
  1023.     #
  1024.     while (/\@footnote([^\{\s]+)\{/) {
  1025.     ($before, $d, $after) = ($`, $1, $');
  1026.     $_ = $after;
  1027.     $text = '';
  1028.     $after = '';
  1029.     $failed = 1;
  1030.     while (@lines3) {
  1031.         if (/\}/) {
  1032.         $text .= $`;
  1033.         $after = $';
  1034.         $failed = 0;
  1035.         last;
  1036.         } else {
  1037.         $text .= $_;
  1038.         $_ = shift(@lines3);
  1039.         }
  1040.     }
  1041.     if ($failed) {
  1042.         die "* Bad syntax (@footnote) after: $before\n";
  1043.     } else {
  1044.         $id = 'FOOT' . ++$foot_num;
  1045.         $foot = "($foot_num)";
  1046.         push(@foot_lines, "<H3>" . &anchor($id, "$d#$id", $foot) . "</H3>\n");
  1047.         push(@foot_lines, "$text\n");
  1048.         $_ = $before . &anchor($id, "$docu_foot#$id", $foot) . $after;
  1049.     }
  1050.     }
  1051.     #
  1052.     # remove unnecessary <P>
  1053.     #
  1054.     if ($_ eq "<P>\n") {
  1055.     next if $end_of_para++;
  1056.     } else {
  1057.     $end_of_para = 0;
  1058.     }
  1059.     # otherwise
  1060.     push(@doc_lines, $_);
  1061. }
  1062. print "# end of pass 4\n" if $verbose;
  1063.  
  1064. #+++############################################################################
  1065. #                                                                              #
  1066. # Pass 5: print things                                                         #
  1067. #                                                                              #
  1068. #---############################################################################
  1069.  
  1070. $header = <<EOT;
  1071. <!-- This HTML file has been created by $THISPROG
  1072.      from $docu on $TODAY -->
  1073. EOT
  1074.  
  1075. $_ = &substitute_style($value{'title'} || "Untitled Document");
  1076. &unprotect_texi;
  1077. $title = $_;
  1078.  
  1079. #
  1080. # print TOC
  1081. #
  1082. if (open(FILE, "> $docu_toc")) {
  1083.     print "# creating $docu_toc...\n" if $verbose;
  1084.     print FILE "$header\n";
  1085.     print FILE "<TITLE>$title - Table of Contents</TITLE>\n";
  1086.     print FILE "<H1>$title</H1>\n";
  1087.     if ($value{'subtitle'}) {
  1088.     chop($value{'subtitle'}); # rmv last \n
  1089.     foreach (split(/\n/, $value{'subtitle'})) {
  1090.         $_ = &substitute_style($_);
  1091.         &unprotect_texi;
  1092.         print FILE "<H2>$_</H2>\n";
  1093.     }
  1094.     }
  1095.     if ($value{'author'}) {
  1096.     chop($value{'author'}); # rmv last \n
  1097.     foreach (split(/\n/, $value{'author'})) {
  1098.         $_ = &substitute_style($_);
  1099.         &unprotect_texi;
  1100.         print FILE "<ADDRESS>$_</ADDRESS>\n";
  1101.     }
  1102.     }
  1103.     print FILE "<P>\n";
  1104.     &print(*toc_lines, FILE);
  1105.     close(FILE);
  1106. } else {
  1107.     warn "Can't write to $docu_toc: $!\n";
  1108. }
  1109.  
  1110. #
  1111. # print document
  1112. #
  1113. if ($split_chapter || $split_node) {
  1114.     $doc_num = 0;
  1115.     while (@sections) {
  1116.     $section = shift(@sections);
  1117.     &next_doc;
  1118.     if (open(FILE, "> $docu_doc")) {
  1119.         print "# creating $docu_doc...\n" if $verbose;
  1120.         $prev_doc = &doc_name($doc_num - 1);
  1121.         $prev_doc = '' if $doc_num == 1;
  1122.         $next_doc = &doc_name($doc_num + 1);
  1123.         $next_doc = '' unless @sections;
  1124.         print FILE "$header\n";
  1125.         print FILE "<TITLE>$title - $section</TITLE>\n";
  1126.         $navigation = '';
  1127.         $navigation .= "<P>Go to the " if $prev_doc || $next_doc;
  1128.         $navigation .= &anchor('', $prev_doc, "previous") if $prev_doc;
  1129.         $navigation .= ", " if $prev_doc && $next_doc;
  1130.         $navigation .= &anchor('', $next_doc, "next") if $next_doc;
  1131.         $navigation .= " section.<P>\n" if $prev_doc || $next_doc;
  1132.         print FILE $navigation;
  1133.         # find corresponding lines
  1134.             @tmp_lines = ();
  1135.             while (@doc_lines) {
  1136.         $_ = shift(@doc_lines);
  1137.         last if ($_ eq $SPLITTAG);
  1138.         push(@tmp_lines, $_);
  1139.         }
  1140.             &print(*tmp_lines, FILE);
  1141.         print FILE $navigation;
  1142.         close(FILE);
  1143.     } else {
  1144.         warn "Can't write to $docu_doc: $!\n";
  1145.     }
  1146.     }
  1147. } else {
  1148.     if (open(FILE, "> $docu_doc")) {
  1149.     print "# creating $docu_doc...\n" if $verbose;
  1150.     print FILE <<EOT;
  1151. $header
  1152. <TITLE>$title</TITLE>
  1153. <H1>$title</H1>
  1154. EOT
  1155.         &print(*doc_lines, FILE);
  1156.     close(FILE);
  1157.     } else {
  1158.     warn "Can't write to $docu_doc: $!\n";
  1159.     }
  1160. }
  1161.  
  1162. #
  1163. # print footnotes
  1164. #
  1165. if (@foot_lines) {
  1166.     if (open(FILE, "> $docu_foot")) {
  1167.     print "# creating $docu_foot...\n" if $verbose;
  1168.     print FILE <<EOT;
  1169. $header
  1170. <TITLE>$title - Footnotes</TITLE>
  1171. <H1>$title</H1>
  1172. EOT
  1173.         &print(*foot_lines, FILE);
  1174.     close(FILE);
  1175.     } else {
  1176.     warn "Can't write to $docu_foot: $!\n";
  1177.     }
  1178. }
  1179.  
  1180. print "# that's all folks\n" if $verbose;
  1181.  
  1182. #+++############################################################################
  1183. #                                                                              #
  1184. # Low level functions                                                          #
  1185. #                                                                              #
  1186. #---############################################################################
  1187.  
  1188. sub check {
  1189.     local($_, %seen, %context, $before, $match, $after);
  1190.  
  1191.     while (<>) {
  1192.     if (/\@(\*|\.|\:|\@|\{|\})/) {
  1193.         $seen{$&}++;
  1194.         $context{$&} .= "> $_" if $verbose;
  1195.         $_ = "$`XX$'";
  1196.         redo;
  1197.     }
  1198.     if (/\@(\w+)/) {
  1199.         ($before, $match, $after) = ($`, $&, $');
  1200.         if ($before =~ /\b[\w-]+$/ && $after =~ /^[\w-.]*\b/) { # e-mail address
  1201.         $seen{'e-mail address'}++;
  1202.         $context{'e-mail address'} .= "> $_" if $verbose;
  1203.         } else {
  1204.         $seen{$match}++;
  1205.         $context{$match} .= "> $_" if $verbose;
  1206.         }
  1207.         $match =~ s/^\@/X/;
  1208.         $_ = "$before$match$after";
  1209.         redo;
  1210.     }
  1211.     }
  1212.     
  1213.     for (sort(keys(%seen))) {
  1214.     if ($verbose) {
  1215.         print "$_\n";
  1216.         print $context{$_};
  1217.     } else {
  1218.         print "$_ ($seen{$_})\n";
  1219.     }
  1220.     }
  1221. }
  1222.  
  1223. sub open {
  1224.     local($name) = @_;
  1225.  
  1226.     ++$fh_name;
  1227.     if (open($fh_name, $name)) {
  1228.     unshift(@fhs, $fh_name);
  1229.     } else {
  1230.     warn "Can't read file $name: $!\n";
  1231.     }
  1232. }
  1233.  
  1234. sub init_input {
  1235.     @fhs = ();            # hold the file handles to read
  1236.     @input_spool = ();        # spooled lines to read
  1237.     $fh_name = 'FH000';
  1238.     &open($docu);
  1239. }
  1240.  
  1241. sub next_line {
  1242.     local($fh, $line);
  1243.  
  1244.     if (@input_spool) {
  1245.     $line = shift(@input_spool);
  1246.     return($line);
  1247.     }
  1248.     while (@fhs) {
  1249.     $fh = $fhs[0];
  1250.     $line = <$fh>;
  1251.     return($line) if $line;
  1252.     close($fh);
  1253.     shift(@fhs);
  1254.     }
  1255.     return(undef);
  1256. }
  1257.  
  1258. # used in pass 1, use &next_line
  1259. sub skip_until {
  1260.     local($tag) = @_;
  1261.     local($_);
  1262.  
  1263.     while ($_ = &next_line) {
  1264.     return if /^\@end\s+$tag\s*$/;
  1265.     }
  1266.     die "* Failed to find '$tag' after: " . $lines[$#lines];
  1267. }
  1268.  
  1269. sub menu_entry {
  1270.     local($entry, $node, $descr) = @_;
  1271.     local($href);
  1272.  
  1273.     $href = $node2href{$node};
  1274.     if ($href) {
  1275.     $descr =~ s/^\s+//;
  1276.     $descr = ": $descr" if $descr;
  1277.     push(@lines2, "<LI>" . &anchor('', $href, $entry) . "$descr\n");
  1278.     } else {
  1279.     warn "Undefined node ($node): $_";
  1280.     }
  1281. }
  1282.  
  1283. sub do_ctrl { "^$_[0]" }
  1284.  
  1285. sub do_sc { "\U$_[0]\E" }
  1286.  
  1287. sub apply_style {
  1288.     local($texi_style, $text) = @_;
  1289.     local($style);
  1290.  
  1291.     $style = $style_map{$texi_style};
  1292.     if (defined($style)) { # known style
  1293.     if ($style =~ /^\"/) { # add quotes
  1294.         $style = $';
  1295.         $text = "\`$text\'";
  1296.     }
  1297.     if ($style =~ /^\&/) { # custom
  1298.         $style = $';
  1299.         $text = &$style($text);
  1300.     } elsif ($style) { # good style
  1301.         $text = "<$style>$text</$style>";
  1302.     } else { # no style
  1303.     }
  1304.     } else { # unknown style
  1305.     $text = undef;
  1306.     }
  1307.     return($text);
  1308. }
  1309.  
  1310. sub substitute_style {
  1311.     local($_) = @_;
  1312.     local($changed, $done, $style, $text);
  1313.  
  1314.     $changed = 1;
  1315.     while ($changed) {
  1316.     $changed = 0;
  1317.     $done = '';
  1318.     while (/\@(\w+){([^\{\}]+)}/) {
  1319.         $text = &apply_style($1, $2);
  1320.         if ($text) {
  1321.         $_ = "$`$text$'";
  1322.         $changed = 1;
  1323.         } else {
  1324.         $done .= "$`\@$1";
  1325.         $_ = "{$2}$'";
  1326.         }
  1327.     }
  1328.         $_ = $done . $_;
  1329.     }
  1330.     return($_);
  1331. }
  1332.  
  1333. sub anchor {
  1334.     local($name, $href, $text, $newline) = @_;
  1335.     local($result);
  1336.  
  1337.     $result = "<A";
  1338.     $result .= " NAME=\"$name\"" if $name;
  1339.     $result .= " HREF=\"$href\"" if $href;
  1340.     $result .= ">$text</A>";
  1341.     $result .= "\n" if $newline;
  1342.     return($result);
  1343. }
  1344.  
  1345. sub pretty_date {
  1346.     local(@MoY, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
  1347.  
  1348.     @MoY = ('January', 'Febuary', 'March', 'April', 'May', 'June',
  1349.         'July', 'August', 'September', 'October', 'November', 'December');
  1350.     ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
  1351.     $year += ($year < 70) ? 2000 : 1900;
  1352.     return("$mday $MoY[$mon] $year");
  1353. }
  1354.  
  1355. sub doc_name {
  1356.     local($num) = @_;
  1357.  
  1358.     return("${docu_name}_$num.html");
  1359. }
  1360.  
  1361. sub next_doc {
  1362.     $docu_doc = &doc_name(++$doc_num);
  1363. }
  1364.  
  1365. sub print {
  1366.     local(*lines, $fh) = @_;
  1367.     local($_);
  1368.  
  1369.     while (@lines) {
  1370.     $_ = shift(@lines);
  1371.     if (/^$PROTECTTAG/o) {
  1372.         $_ = $tag2pro{$_};
  1373.     } else {
  1374.         &unprotect_texi;
  1375.     }
  1376.     print $fh $_;
  1377.     }
  1378. }
  1379.  
  1380. sub protect_texi {
  1381.     # protect @ { } ` '
  1382.     s/\@\@/$;0/go && study;
  1383.     s/\@\{/$;1/go && study;
  1384.     s/\@\}/$;2/go && study;
  1385.     s/\@\`/$;3/go && study;
  1386.     s/\@\'/$;4/go && study;
  1387. }
  1388.  
  1389. sub protect_html {
  1390.     # protect & < >
  1391.     s/\&/\&\#38;/g && study;
  1392.     s/\</\&\#60;/g && study;
  1393.     s/\>/\&\#62;/g && study;
  1394.     s/\&\#60;\/A\&\#62;/<\/A>/g && study; # assume </A> is HTML
  1395.     s/\&\#60;A ([^\&]+)\&\#62;/<A $1>/g && study; # assume <A [^&]+> is HTML
  1396. }
  1397.  
  1398. # we should use this:
  1399. #
  1400. #  sub unprotect_texi {
  1401. #      s/$;0/\@/go && study;
  1402. #      s/$;1/\{/go && study;
  1403. #      s/$;2/\}/go && study;
  1404. #      s/$;3/\`/go && study;
  1405. #      s/$;4/\'/go && study;
  1406. #  }
  1407. #  
  1408. #  sub unprotect_html {
  1409. #      s/\&\#38;/\&/g && study;
  1410. #      s/\&\#60;/\</g && study;
  1411. #      s/\&\#62;/\>/g && study;
  1412. #  }
  1413. #
  1414. # but Perl 4.036 bugs on it (Perl 5 is OK)
  1415.  
  1416. sub unprotect_texi {
  1417.     s/$;0/\@/go;
  1418.     s/$;1/\{/go;
  1419.     s/$;2/\}/go;
  1420.     s/$;3/\`/go;
  1421.     s/$;4/\'/go;
  1422. }
  1423.  
  1424. sub unprotect_html {
  1425.     s/\&\#38;/\&/g;
  1426.     s/\&\#60;/\</g;
  1427.     s/\&\#62;/\>/g;
  1428. }
  1429.  
  1430. sub byalpha {
  1431.     $key2alpha{$a} cmp $key2alpha{$b};
  1432. }
  1433.  
  1434. ##############################################################################
  1435.  
  1436.     # These next few lines are legal in both Perl and nroff.
  1437.  
  1438. .00;            # finish .ig
  1439.  
  1440. 'di            \" finish diversion--previous line must be blank
  1441. .nr nl 0-1        \" fake up transition to first page again
  1442. .nr % 0            \" start at page 1
  1443. '; __END__ ############# From here on it's a standard manual page ############
  1444. .TH TEXI2HTML 1 "04/21/94"
  1445. .AT 3
  1446. .SH NAME
  1447. texi2html \- a Texinfo to HTML converter
  1448. .SH SYNOPSIS
  1449. .B texi2html [options] file
  1450. .PP
  1451. .B texi2html -check [-verbose] files
  1452. .SH DESCRIPTION
  1453. .I Texi2html
  1454. converts the given Texinfo file to a set of HTML files. It tries to handle
  1455. most of Texinfo commands. It creates hypertext links for cross-references,
  1456. footnotes...
  1457. .PP
  1458. It also tries to add links from a reference to its corresponding entry in the
  1459. bibliography (if any). It may also handle a glossary (see the
  1460. .B \-glossary
  1461. option).
  1462. .PP
  1463. .I Texi2html
  1464. creates several files depending on the contents of the Texinfo file and on
  1465. the chosen options (see FILES).
  1466. .PP
  1467. The HTML files created by
  1468. .I texi2html
  1469. are closer to TeX than to Info, what's why
  1470. .I texi2html
  1471. convert @iftex sections and not @ifinfo ones.
  1472. .SH OPTIONS
  1473. .TP 12
  1474. .B \-check
  1475. Check the given file and give the list of all things that may be Texinfo commands.
  1476. This may be used to check the output of
  1477. .I texi2html
  1478. to find the Texinfo commands that have been left in the HTML file.
  1479. .TP
  1480. .B \-glossary
  1481. Use the section named 'Glossary' to build a list of terms and put links in the HTML
  1482. document from each term toward its definition.
  1483. .TP
  1484. .B \-invisible \fIname\fP
  1485. Use \fIname\fP to create invisible destination anchors for index links. This is a workaround
  1486. for a known bug of many WWW browsers, including xmosaic.
  1487. .TP
  1488. .B \-menu
  1489. Show the Texinfo menus; by default they are ignored.
  1490. .TP
  1491. .B \-split_chapter
  1492. Split the output into several HTML files (one per main section:
  1493. chapter, appendix...).
  1494. .TP
  1495. .B \-split_node
  1496. Split the output into several HTML files (one per node).
  1497. .TP
  1498. .B \-usage
  1499. Print usage instructions, listing the current available command-line options.
  1500. .TP
  1501. .B \-verbose
  1502. Give a verbose output. Can be used with the
  1503. .B \-check
  1504. option.
  1505. .PP
  1506. .SH FILES
  1507. By default
  1508. .I texi2html
  1509. creates the following files (foo being the name of the Texinfo file):
  1510. .TP 16
  1511. .B foo_toc.html
  1512. The table of contents.
  1513. .TP
  1514. .B foo.html
  1515. The document's contents.
  1516. .TP
  1517. .B foo_foot.html
  1518. The footnotes (if any).
  1519. .PP
  1520. When used with the
  1521. .B \-split
  1522. option, it creates several files (one per chapter or node), named
  1523. .B foo_n.html
  1524. (n being the indice of the chapter or node), instead of the single
  1525. .B foo.html
  1526. file.
  1527. .SH VARIABLES
  1528. .I texi2html
  1529. predefines the following variables: \fBhtml\fP, \fBtexi2html\fP.
  1530. .SH ADDITIONAL COMMANDS
  1531. .I texi2html
  1532. implements the following non-Texinfo commands:
  1533. .TP 16
  1534. .B @ifhtml
  1535. This indicates the start of an HTML section, this section will passed through
  1536. without any modofication.
  1537. .TP
  1538. .B @end ifhtml
  1539. This indcates the end of an HTML section.
  1540. .SH VERSION
  1541. This is \fItexi2html\fP version 1.29, 04/21/94.
  1542. .SH AUTHOR
  1543. Lionel Cons, CERN CN/DCI/UWS, Lionel.Cons@cern.ch
  1544. .SH COPYRIGHT
  1545. This program is the intellectual property of the European
  1546. Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is
  1547. provided by CERN. No liability whatsoever is accepted for any loss or damage
  1548. of any kind resulting from any defect or inaccuracy in this information or
  1549. code.
  1550. .PP
  1551. CERN, 1211 Geneva 23, Switzerland
  1552. .SH "SEE ALSO"
  1553. GNU Texinfo Documentation Format,
  1554. HyperText Markup Language (HTML),
  1555. World Wide Web (WWW).
  1556. .SH BUGS
  1557. This program does not understand all Texinfo commands (yet).
  1558. .ex
  1559.