4beec01b341f0898f1e97725dd1cbd02c83da1a4
[platform/upstream/bash.git] / support / texi2html
1 #!/usr/local/bin/perl
2 'di ';
3 'ig 00 ';
4 #+##############################################################################
5 #                                                                              #
6 # File: texi2html                                                              #
7 #                                                                              #
8 # Description: Program to transform most Texinfo documents to HTML             #
9 #                                                                              #
10 #-##############################################################################
11
12 # @(#)texi2html 1.52 01/05/98   Written (mainly) 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 $DEBUG_HTML  = 32;
30 $DEBUG_USER  = 64;
31
32 $BIBRE = '\[[\w\/-]+\]';                # RE for a bibliography reference
33 $FILERE = '[\/\w.+-]+';                 # RE for a file name
34 $VARRE = '[^\s\{\}]+';                  # RE for a variable name
35 $NODERE = '[^@{}:\'`",]+';              # RE for a node name
36 $NODESRE = '[^@{}:\'`"]+';              # RE for a list of node names
37 $XREFRE = '[^@{}]+';                    # RE for a xref (should use NODERE)
38
39 $ERROR = "***";                         # prefix for errors and warnings
40 $THISPROG = "texi2html 1.52";                   # program name and version
41 $HOMEPAGE = "http://wwwinfo.cern.ch/dis/texi2html/"; # program home page
42 $TODAY = &pretty_date;                  # like "20 September 1993"
43 $SPLITTAG = "<!-- SPLIT HERE -->\n";    # tag to know where to split
44 $PROTECTTAG = "_ThisIsProtected_";      # tag to recognize protected sections
45 $html2_doctype = '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0 Strict Level 2//EN">';
46
47 #
48 # language dependent constants
49 #
50 #$LDC_SEE = 'see';
51 #$LDC_SECTION = 'section';
52 #$LDC_IN = 'in';
53 #$LDC_TOC = 'Table of Contents';
54 #$LDC_GOTO = 'Go to the';
55 #$LDC_FOOT = 'Footnotes';
56 # TODO: @def* shortcuts
57
58 #
59 # pre-defined indices
60 #
61 %predefined_index = (
62                     'cp', 'c',
63                     'fn', 'f',
64                     'vr', 'v',
65                     'ky', 'k',
66                     'pg', 'p',
67                     'tp', 't',
68                     );
69
70 #
71 # valid indices
72 #
73 %valid_index = (
74                     'c', 1,
75                     'f', 1,
76                     'v', 1,
77                     'k', 1,
78                     'p', 1,
79                     't', 1,
80                 );
81
82 #
83 # texinfo section names to level
84 #
85 %sec2level = (
86               'top', 0,
87               'chapter', 1,
88               'unnumbered', 1,
89               'majorheading', 1,
90               'chapheading', 1,
91               'appendix', 1,
92               'section', 2,
93               'unnumberedsec', 2,
94               'heading', 2,
95               'appendixsec', 2,
96               'appendixsection', 2,
97               'subsection', 3,
98               'unnumberedsubsec', 3,
99               'subheading', 3,
100               'appendixsubsec', 3,
101               'subsubsection', 4,
102               'unnumberedsubsubsec', 4,
103               'subsubheading', 4,
104               'appendixsubsubsec', 4,
105               );
106
107 #
108 # accent map, TeX command to ISO name
109 #
110 %accent_map = (
111                '"',  'uml',
112                '~',  'tilde',
113                '^',  'circ',
114                '`',  'grave',
115                '\'', 'acute',
116                );
117
118 #
119 # texinfo "simple things" (@foo) to HTML ones
120 #
121 %simple_map = (
122                # cf. makeinfo.c
123                "*", "<BR>",             # HTML+
124                " ", " ",
125                "\n", "\n",
126                "|", "",
127                # spacing commands
128                ":", "",
129                "!", "!",
130                "?", "?",
131                ".", ".",
132                "-", "",
133                );
134
135 #
136 # texinfo "things" (@foo{}) to HTML ones
137 #
138 %things_map = (
139                'TeX', 'TeX',
140                'br', '<P>',             # paragraph break
141                'bullet', '*',
142                'copyright', '(C)',
143                'dots', '...',
144                'equiv', '==',
145                'error', 'error-->',
146                'expansion', '==>',
147                'minus', '-',
148                'point', '-!-',
149                'print', '-|',
150                'result', '=>',
151                'today', $TODAY,
152                );
153
154 #
155 # texinfo styles (@foo{bar}) to HTML ones
156 #
157 %style_map = (
158               'asis', '',
159               'b', 'B',
160               'cite', 'CITE',
161               'code', 'CODE',
162               'ctrl', '&do_ctrl',       # special case
163               'dfn', 'EM',              # DFN tag is illegal in the standard
164               'dmn', '',                # useless
165               'email', '&do_email',     # insert a clickable email address
166               'emph', 'EM',
167               'file', '"TT',            # will put quotes, cf. &apply_style
168               'i', 'I',
169               'kbd', 'KBD',
170               'key', 'KBD',
171               'math', 'EM',
172               'r', '',                  # unsupported
173               'samp', '"SAMP',          # will put quotes, cf. &apply_style
174               'sc', '&do_sc',           # special case
175               'strong', 'STRONG',
176               't', 'TT',
177               'titlefont', '',          # useless
178               'uref', '&do_uref',       # insert a clickable URL
179               'url', '&do_url',         # insert a clickable URL
180               'var', 'VAR',
181               'w', '',                  # unsupported
182               );
183
184 #
185 # texinfo format (@foo/@end foo) to HTML ones
186 #
187 %format_map = (
188                'display', 'PRE',
189                'example', 'PRE',
190                'format', 'PRE',
191                'lisp', 'PRE',
192                'quotation', 'BLOCKQUOTE',
193                'smallexample', 'PRE',
194                'smalllisp', 'PRE',
195                # lists
196                'itemize', 'UL',
197                'enumerate', 'OL',
198                # poorly supported
199                'flushleft', 'PRE',
200                'flushright', 'PRE',
201                );
202
203 #
204 # texinfo definition shortcuts to real ones
205 #
206 %def_map = (
207             # basic commands
208             'deffn', 0,
209             'defvr', 0,
210             'deftypefn', 0,
211             'deftypevr', 0,
212             'defcv', 0,
213             'defop', 0,
214             'deftp', 0,
215             # basic x commands
216             'deffnx', 0,
217             'defvrx', 0,
218             'deftypefnx', 0,
219             'deftypevrx', 0,
220             'defcvx', 0,
221             'defopx', 0,
222             'deftpx', 0,
223             # shortcuts
224             'defun', 'deffn Function',
225             'defmac', 'deffn Macro',
226             'defspec', 'deffn {Special Form}',
227             'defvar', 'defvr Variable',
228             'defopt', 'defvr {User Option}',
229             'deftypefun', 'deftypefn Function',
230             'deftypevar', 'deftypevr Variable',
231             'defivar', 'defcv {Instance Variable}',
232             'defmethod', 'defop Method',
233             # x shortcuts
234             'defunx', 'deffnx Function',
235             'defmacx', 'deffnx Macro',
236             'defspecx', 'deffnx {Special Form}',
237             'defvarx', 'defvrx Variable',
238             'defoptx', 'defvrx {User Option}',
239             'deftypefunx', 'deftypefnx Function',
240             'deftypevarx', 'deftypevrx Variable',
241             'defivarx', 'defcvx {Instance Variable}',
242             'defmethodx', 'defopx Method',
243             );
244
245 #
246 # things to skip
247 #
248 %to_skip = (
249             # comments
250             'c', 1,
251             'comment', 1,
252             # useless
253             'contents', 1,
254             'shortcontents', 1,
255             'summarycontents', 1,
256             'footnotestyle', 1,
257             'end ifclear', 1,
258             'end ifset', 1,
259             'titlepage', 1,
260             'end titlepage', 1,
261             # unsupported commands (formatting)
262             'afourpaper', 1,
263             'cropmarks', 1,
264             'finalout', 1,
265             'headings', 1,
266             'need', 1,
267             'page', 1,
268             'setchapternewpage', 1,
269             'everyheading', 1,
270             'everyfooting', 1,
271             'evenheading', 1,
272             'evenfooting', 1,
273             'oddheading', 1,
274             'oddfooting', 1,
275             'smallbook', 1,
276             'vskip', 1,
277             'filbreak', 1,
278             'paragraphindent', 1,
279             # unsupported formats
280             'cartouche', 1,
281             'end cartouche', 1,
282             'group', 1,
283             'end group', 1,
284             );
285
286 #+++############################################################################
287 #                                                                              #
288 # Argument parsing, initialisation                                             #
289 #                                                                              #
290 #---############################################################################
291
292 %value = ();                            # hold texinfo variables, see also -D
293
294 $use_bibliography = 1;
295 $use_acc = 0;
296 $debug = 0;
297 $doctype = '';
298 $check = 0;
299 $expandinfo = 0;
300 $use_glossary = 0;
301 $invisible_mark = '';
302 $use_iso = 0;
303 @include_dirs = ();
304 $show_menu = 0;
305 $number_sections = 0;
306 $split_node = 0;
307 $split_chapter = 0;
308 $monolithic = 0;
309 $verbose = 0;
310 $usage = <<EOT;
311 This is $THISPROG
312 To convert a Texinfo file to HMTL: $0 [options] file
313   where options can be:
314     -expandinfo    : use \@ifinfo sections, not \@iftex
315     -glossary      : handle a glossary
316     -invisible name: use 'name' as an invisible anchor
317     -Dname         : define name like with \@set
318     -I dir         : search also for files in 'dir'
319     -menu          : handle menus
320     -monolithic    : output only one file including ToC
321     -number        : number sections
322     -split_chapter : split on main sections
323     -split_node    : split on nodes
324     -usage         : print usage instructions
325     -verbose       : verbose output
326 To check converted files: $0 -check [-verbose] files
327 EOT
328
329 while (@ARGV && $ARGV[0] =~ /^-/) {
330     $_ = shift(@ARGV);
331     if (/^-acc$/)            { $use_acc = 1; next; }
332     if (/^-d(ebug)?(\d+)?$/) { $debug = $2 || shift(@ARGV); next; }
333     if (/^-doctype$/)        { $doctype = shift(@ARGV); next; }
334     if (/^-c(heck)?$/)       { $check = 1; next; }
335     if (/^-e(xpandinfo)?$/)  { $expandinfo = 1; next; }
336     if (/^-g(lossary)?$/)    { $use_glossary = 1; next; }
337     if (/^-i(nvisible)?$/)   { $invisible_mark = shift(@ARGV); next; }
338     if (/^-iso$/)            { $use_iso = 1; next; }
339     if (/^-D(.+)?$/)         { $value{$1 || shift(@ARGV)} = 1; next; }
340     if (/^-I(.+)?$/)         { push(@include_dirs, $1 || shift(@ARGV)); next; }
341     if (/^-m(enu)?$/)        { $show_menu = 1; next; }
342     if (/^-mono(lithic)?$/)  { $monolithic = 1; next; }
343     if (/^-n(umber)?$/)      { $number_sections = 1; next; }
344     if (/^-s(plit)?_?(n(ode)?|c(hapter)?)?$/) {
345         if ($2 =~ /^n/) {
346             $split_node = 1;
347         } else {
348             $split_chapter = 1;
349         }
350         next;
351     }
352     if (/^-v(erbose)?$/)     { $verbose = 1; next; }
353     die $usage;
354 }
355 if ($check) {
356     die $usage unless @ARGV > 0;
357     &check;
358     exit;
359 }
360
361 if (($split_node || $split_chapter) && $monolithic) {
362     warn "Can't use -monolithic with -split, -monolithic ignored.\n";
363     $monolithic = 0;
364 }
365 if ($expandinfo) {
366     $to_skip{'ifinfo'}++;
367     $to_skip{'end ifinfo'}++;
368 } else {
369     $to_skip{'iftex'}++;
370     $to_skip{'end iftex'}++;
371 }
372 $invisible_mark = '<IMG SRC="invisible.xbm">' if $invisible_mark eq 'xbm';
373 die $usage unless @ARGV == 1;
374 $docu = shift(@ARGV);
375 if ($docu =~ /.*\//) {
376     chop($docu_dir = $&);
377     $docu_name = $';
378 } else {
379     $docu_dir = '.';
380     $docu_name = $docu;
381 }
382 unshift(@include_dirs, $docu_dir);
383 $docu_name =~ s/\.te?x(i|info)?$//;     # basename of the document
384
385 $docu_doc = "$docu_name.html";          # document's contents
386 if ($monolithic) {
387     $docu_toc = $docu_foot = $docu_doc;
388 } else {
389     $docu_toc  = "${docu_name}_toc.html";  # document's table of contents
390     $docu_foot = "${docu_name}_foot.html"; # document's footnotes
391 }
392
393 #
394 # variables
395 #
396 $value{'html'} = 1;                     # predefine html (the output format)
397 $value{'texi2html'} = '1.52';           # predefine texi2html (the translator)
398 # _foo: internal to track @foo
399 foreach ('_author', '_title', '_subtitle',
400          '_settitle', '_setfilename') {
401     $value{$_} = '';                    # prevent -w warnings
402 }
403 %node2sec = ();                         # node to section name
404 %node2href = ();                        # node to HREF
405 %bib2href = ();                         # bibliography reference to HREF
406 %gloss2href = ();                       # glossary term to HREF
407 @sections = ();                         # list of sections
408 %tag2pro = ();                          # protected sections
409
410 #
411 # initial indexes
412 #
413 $bib_num = 0;
414 $foot_num = 0;
415 $gloss_num = 0;
416 $idx_num = 0;
417 $sec_num = 0;
418 $doc_num = 0;
419 $html_num = 0;
420
421 #
422 # can I use ISO8879 characters? (HTML+)
423 #
424 if ($use_iso) {
425     $things_map{'bullet'} = "&bull;";
426     $things_map{'copyright'} = "&copy;";
427     $things_map{'dots'} = "&hellip;";
428     $things_map{'equiv'} = "&equiv;";
429     $things_map{'expansion'} = "&rarr;";
430     $things_map{'point'} = "&lowast;";
431     $things_map{'result'} = "&rArr;";
432 }
433
434 #
435 # read texi2html extensions (if any)
436 #
437 $extensions = 'texi2html.ext'; # extensions in working directory
438 if (-f $extensions) {
439     print "# reading extensions from $extensions\n" if $verbose;
440     require($extensions);
441 }
442 ($progdir = $0) =~ s/[^\/]+$//;
443 if ($progdir && ($progdir ne './')) {
444     $extensions = "${progdir}texi2html.ext"; # extensions in texi2html directory
445     if (-f $extensions) {
446         print "# reading extensions from $extensions\n" if $verbose;
447         require($extensions);
448     }
449 }
450
451 print "# reading from $docu\n" if $verbose;
452
453 #+++############################################################################
454 #                                                                              #
455 # Pass 1: read source, handle command, variable, simple substitution           #
456 #                                                                              #
457 #---############################################################################
458
459 @lines = ();                            # whole document
460 @toc_lines = ();                        # table of contents
461 $toplevel = 0;                          # top level seen in hierarchy
462 $curlevel = 0;                          # current level in TOC
463 $node = '';                             # current node name
464 $in_table = 0;                          # am I inside a table
465 $table_type = '';                       # type of table ('', 'f', 'v', 'multi')
466 @tables = ();                           # nested table support
467 $in_bibliography = 0;                   # am I inside a bibliography
468 $in_glossary = 0;                       # am I inside a glossary
469 $in_top = 0;                            # am I inside the top node
470 $in_pre = 0;                            # am I inside a preformatted section
471 $in_list = 0;                           # am I inside a list
472 $in_html = 0;                           # am I inside an HTML section
473 $first_line = 1;                        # is it the first line
474 $dont_html = 0;                         # don't protect HTML on this line
475 $split_num = 0;                         # split index
476 $deferred_ref = '';                     # deferred reference for indexes
477 @html_stack = ();                       # HTML elements stack
478 $html_element = '';                     # current HTML element
479 &html_reset;
480
481 # build code for simple substitutions
482 # the maps used (%simple_map and %things_map) MUST be aware of this
483 # watch out for regexps, / and escaped characters!
484 $subst_code = '';
485 foreach (keys(%simple_map)) {
486     ($re = $_) =~ s/(\W)/\\$1/g; # protect regexp chars
487     $subst_code .= "s/\\\@$re/$simple_map{$_}/g;\n";
488 }
489 foreach (keys(%things_map)) {
490     $subst_code .= "s/\\\@$_\\{\\}/$things_map{$_}/g;\n";
491 }
492 if ($use_acc) {
493     # accentuated characters
494     foreach (keys(%accent_map)) {
495         if ($_ eq "`") {
496             $subst_code .= "s/$;3";
497         } elsif ($_ eq "'") {
498             $subst_code .= "s/$;4";
499         } else {
500             $subst_code .= "s/\\\@\\$_";
501         }
502         $subst_code .= "([aeiou])/&\${1}$accent_map{$_};/gi;\n";
503     }
504 }
505 eval("sub simple_substitutions { $subst_code }");
506
507 &init_input;
508 while ($_ = &next_line) {
509     #
510     # remove \input on the first lines only
511     #
512     if ($first_line) {
513         next if /^\\input/;
514         $first_line = 0;
515     }
516     #
517     # parse texinfo tags
518     #
519     $tag = '';
520     $end_tag = '';
521     if (/^\@end\s+(\w+)\b/) {
522         $end_tag = $1;
523     } elsif (/^\@(\w+)\b/) {
524         $tag = $1;
525     }
526     #
527     # handle @ifhtml / @end ifhtml
528     #
529     if ($in_html) {
530         if ($end_tag eq 'ifhtml') {
531             $in_html = 0;
532         } else {
533             $tag2pro{$in_html} .= $_;
534         }
535         next;
536     } elsif ($tag eq 'ifhtml') {
537         $in_html = $PROTECTTAG . ++$html_num;
538         push(@lines, $in_html);
539         next;
540     }
541     #
542     # try to skip the line
543     #
544     if ($end_tag) {
545         next if $to_skip{"end $end_tag"};
546     } elsif ($tag) {
547         next if $to_skip{$tag};
548         last if $tag eq 'bye';
549     }
550     if ($in_top) {
551         # parsing the top node
552         if ($tag eq 'node' || $tag eq 'include' || $sec2level{$tag}) {
553             # no more in top
554             $in_top = 0;
555         } else {
556             # skip it
557             next;
558         }
559     }
560     #
561     # try to remove inlined comments
562     # syntax from tex-mode.el comment-start-skip
563     #
564     s/((^|[^\@])(\@\@)*)\@c(omment)? .*/$1/;
565     # non-@ substitutions cf. texinfmt.el
566     unless ($in_pre) {
567         s/``/\"/g;
568         s/''/\"/g;
569         s/([\w ])---([\w ])/$1--$2/g;
570     }
571     #
572     # analyze the tag
573     #
574     if ($tag) {
575         # skip lines
576         &skip_until($tag), next if $tag eq 'ignore';
577         if ($expandinfo) {
578             &skip_until($tag), next if $tag eq 'iftex';
579         } else {
580             &skip_until($tag), next if $tag eq 'ifinfo';
581         }
582         &skip_until($tag), next if $tag eq 'tex';
583         # handle special tables
584         if ($tag =~ /^(|f|v|multi)table$/) {
585             $table_type = $1;
586             $tag = 'table';
587         }
588         # special cases
589         if ($tag eq 'top' || ($tag eq 'node' && /^\@node\s+top\s*,/i)) {
590             $in_top = 1;
591             @lines = (); # ignore all lines before top (title page garbage)
592             next;
593         } elsif ($tag eq 'node') {
594             $in_top = 0;
595             warn "$ERROR Bad node line: $_" unless $_ =~ /^\@node\s$NODESRE$/o;
596             $_ = &protect_html($_); # if node contains '&' for instance
597             s/^\@node\s+//;
598             ($node) = split(/,/);
599             &normalise_node($node);
600             if ($split_node) {
601                 &next_doc;
602                 push(@lines, $SPLITTAG) if $split_num++;
603                 push(@sections, $node);
604             }
605             next;
606         } elsif ($tag eq 'include') {
607             if (/^\@include\s+($FILERE)\s*$/o) {
608                 $file = $1;
609                 unless (-e $file) {
610                     foreach $dir (@include_dirs) {
611                         $file = "$dir/$1";
612                         last if -e $file;
613                     }
614                 }
615                 if (-e $file) {
616                     &open($file);
617                     print "# including $file\n" if $verbose;
618                 } else {
619                     warn "$ERROR Can't find $file, skipping";
620                 }
621             } else {
622                 warn "$ERROR Bad include line: $_";
623             }
624             next;
625         } elsif ($tag eq 'ifclear') {
626             if (/^\@ifclear\s+($VARRE)\s*$/o) {
627                 next unless defined($value{$1});
628                 &skip_until($tag);
629             } else {
630                 warn "$ERROR Bad ifclear line: $_";
631             }
632             next;
633         } elsif ($tag eq 'ifset') {
634             if (/^\@ifset\s+($VARRE)\s*$/o) {
635                 next if defined($value{$1});
636                 &skip_until($tag);
637             } else {
638                 warn "$ERROR Bad ifset line: $_";
639             }
640             next;
641         } elsif ($tag eq 'menu') {
642             unless ($show_menu) {
643                 &skip_until($tag);
644                 next;
645             }
646             &html_push_if($tag);
647             push(@lines, &html_debug("\n", __LINE__));
648         } elsif ($format_map{$tag}) {
649             $in_pre = 1 if $format_map{$tag} eq 'PRE';
650             &html_push_if($format_map{$tag});
651             push(@lines, &html_debug("\n", __LINE__));
652             $in_list++ if $format_map{$tag} eq 'UL' || $format_map{$tag} eq 'OL' ;
653             push(@lines, &debug("<$format_map{$tag}>\n", __LINE__));
654             next;
655         } elsif ($tag eq 'table') {
656             if (/^\@(|f|v|multi)table\s+\@(\w+)/) {
657                 $in_table = $2;
658                 unshift(@tables, join($;, $table_type, $in_table));
659                 if ($table_type eq "multi") {
660                     push(@lines, &debug("<TABLE BORDER>\n", __LINE__));
661                     &html_push_if('TABLE');
662                 } else {
663                     push(@lines, &debug("<DL COMPACT>\n", __LINE__));
664                     &html_push_if('DL');
665                 }
666                 push(@lines, &html_debug("\n", __LINE__));
667             } else {
668                 warn "$ERROR Bad table line: $_";
669             }
670             next;
671         } elsif ($tag eq 'synindex' || $tag eq 'syncodeindex') {
672             if (/^\@$tag\s+(\w)\w\s+(\w)\w\s*$/) {
673                 eval("*${1}index = *${2}index");
674             } else {
675                 warn "$ERROR Bad syn*index line: $_";
676             }
677             next;
678         } elsif ($tag eq 'sp') {
679             push(@lines, &debug("<P>\n", __LINE__));
680             next;
681         } elsif ($tag eq 'setref') {
682             &protect_html; # if setref contains '&' for instance
683             if (/^\@$tag\s*{($NODERE)}\s*$/) {
684                 $setref = $1;
685                 $setref =~ s/\s+/ /g; # normalize
686                 $setref =~ s/ $//;
687                 $node2sec{$setref} = $name;
688                 $node2href{$setref} = "$docu_doc#$docid";
689             } else {
690                 warn "$ERROR Bad setref line: $_";
691             }
692             next;
693         } elsif ($tag eq 'defindex' || $tag eq 'defcodeindex') {
694             if (/^\@$tag\s+(\w\w)\s*$/) {
695                 $valid_index{$1} = 1;
696             } else {
697                 warn "$ERROR Bad defindex line: $_";
698             }
699             next;
700         } elsif (defined($def_map{$tag})) {
701             if ($def_map{$tag}) {
702                 s/^\@$tag\s+//;
703                 $tag = $def_map{$tag};
704                 $_ = "\@$tag $_";
705                 $tag =~ s/\s.*//;
706             }
707         } elsif (defined($user_sub{$tag})) {
708             s/^\@$tag\s+//;
709             $sub = $user_sub{$tag};
710             print "# user $tag = $sub, arg: $_" if $debug & $DEBUG_USER;
711             if (defined(&$sub)) {
712                 chop($_);
713                 &$sub($_);
714             } else {
715                 warn "$ERROR Bad user sub for $tag: $sub\n";
716             }
717             next;
718         }
719         if (defined($def_map{$tag})) {
720             s/^\@$tag\s+//;
721             if ($tag =~ /x$/) {
722                 # extra definition line
723                 $tag = $`;
724                 $is_extra = 1;
725             } else {
726                 $is_extra = 0;
727             }
728             while (/\{([^\{\}]*)\}/) {
729                 # this is a {} construct
730                 ($before, $contents, $after) = ($`, $1, $');
731                 # protect spaces
732                 $contents =~ s/\s+/$;9/g;
733                 # restore $_ protecting {}
734                 $_ = "$before$;7$contents$;8$after";
735             }
736             @args = split(/\s+/, &protect_html($_));
737             foreach (@args) {
738                 s/$;9/ /g;      # unprotect spaces
739                 s/$;7/\{/g;     # ... {
740                 s/$;8/\}/g;     # ... }
741             }
742             $type = shift(@args);
743             $type =~ s/^\{(.*)\}$/$1/;
744             print "# def ($tag): {$type} ", join(', ', @args), "\n"
745                 if $debug & $DEBUG_DEF;
746             $type .= ':'; # it's nicer like this
747             $name = shift(@args);
748             $name =~ s/^\{(.*)\}$/$1/;
749             if ($is_extra) {
750                 $_ = &debug("<DT>", __LINE__);
751             } else {
752                 $_ = &debug("<DL>\n<DT>", __LINE__);
753             }
754             if ($tag eq 'deffn' || $tag eq 'defvr' || $tag eq 'deftp') {
755                 $_ .= "<U>$type</U> <B>$name</B>";
756                 $_ .= " <I>@args</I>" if @args;
757             } elsif ($tag eq 'deftypefn' || $tag eq 'deftypevr'
758                      || $tag eq 'defcv' || $tag eq 'defop') {
759                 $ftype = $name;
760                 $name = shift(@args);
761                 $name =~ s/^\{(.*)\}$/$1/;
762                 $_ .= "<U>$type</U> $ftype <B>$name</B>";
763                 $_ .= " <I>@args</I>" if @args;
764             } else {
765                 warn "$ERROR Unknown definition type: $tag\n";
766                 $_ .= "<U>$type</U> <B>$name</B>";
767                 $_ .= " <I>@args</I>" if @args;
768             }
769             $_ .= &debug("\n<DD>", __LINE__);
770             $name = &unprotect_html($name);
771             if ($tag eq 'deffn' || $tag eq 'deftypefn') {
772                 unshift(@input_spool, "\@findex $name\n");
773             } elsif ($tag eq 'defop') {
774                 unshift(@input_spool, "\@findex $name on $ftype\n");
775             } elsif ($tag eq 'defvr' || $tag eq 'deftypevr' || $tag eq 'defcv') {
776                 unshift(@input_spool, "\@vindex $name\n");
777             } else {
778                 unshift(@input_spool, "\@tindex $name\n");
779             }
780             $dont_html = 1;
781         }
782     } elsif ($end_tag) {
783         if ($format_map{$end_tag}) {
784             $in_pre = 0 if $format_map{$end_tag} eq 'PRE';
785             $in_list-- if $format_map{$end_tag} eq 'UL' || $format_map{$end_tag} eq 'OL' ;
786             &html_pop_if('LI', 'P');
787             &html_pop_if();
788             push(@lines, &debug("</$format_map{$end_tag}>\n", __LINE__));
789             push(@lines, &html_debug("\n", __LINE__));
790         } elsif ($end_tag =~ /^(|f|v|multi)table$/) {
791             unless (@tables) {
792                 warn "$ERROR \@end $end_tag without \@*table\n";
793                 next;
794             }
795             ($table_type, $in_table) = split($;, shift(@tables));
796             unless ($1 eq $table_type) {
797                 warn "$ERROR \@end $end_tag without matching \@$end_tag\n";
798                 next;
799             }
800             if ($table_type eq "multi") {
801                 push(@lines, "</TR></TABLE>\n");
802                 &html_pop_if('TR');
803             } else {
804                 push(@lines, "</DL>\n");
805                 &html_pop_if('DD');
806             }
807             &html_pop_if();
808             if (@tables) {
809                 ($table_type, $in_table) = split($;, $tables[0]);
810             } else {
811                 $in_table = 0;
812             }
813         } elsif (defined($def_map{$end_tag})) {
814             push(@lines, &debug("</DL>\n", __LINE__));
815         } elsif ($end_tag eq 'menu') {
816             &html_pop_if();
817             push(@lines, $_); # must keep it for pass 2
818         }
819         next;
820     }
821     #
822     # misc things
823     #
824     # protect texi and HTML things
825     &protect_texi;
826     $_ = &protect_html($_) unless $dont_html;
827     $dont_html = 0;
828     # substitution (unsupported things)
829     s/^\@center\s+//g;
830     s/^\@exdent\s+//g;
831     s/\@noindent\s+//g;
832     s/\@refill\s+//g;
833     # other substitutions
834     &simple_substitutions;
835     s/\@value{($VARRE)}/$value{$1}/eg;
836     s/\@footnote\{/\@footnote$docu_doc\{/g; # mark footnotes, cf. pass 4
837     #
838     # analyze the tag again
839     #
840     if ($tag) {
841         if (defined($sec2level{$tag}) && $sec2level{$tag} > 0) {
842             if (/^\@$tag\s+(.+)$/) {
843                 $name = $1;
844                 $name =~ s/\s+$//;
845                 $level = $sec2level{$tag};
846                 $name = &update_sec_num($tag, $level) . "  $name"
847                     if $number_sections && $tag !~ /^unnumbered/;
848                 if ($tag =~ /heading$/) {
849                     push(@lines, &html_debug("\n", __LINE__));
850                     if ($html_element ne 'body') {
851                         # We are in a nice pickle here. We are trying to get a H? heading
852                         # even though we are not in the body level. So, we convert it to a
853                         # nice, bold, line by itself.
854                         $_ = &debug("\n\n<P><STRONG>$name</STRONG></P>\n\n", __LINE__);
855                     } else {
856                         $_ = &debug("<H$level>$name</H$level>\n", __LINE__);
857                         &html_push_if('body');
858                     }
859                     print "# heading, section $name, level $level\n"
860                         if $debug & $DEBUG_TOC;
861                 } else {
862                     if ($split_chapter) {
863                         unless ($toplevel) {
864                             # first time we see a "section"
865                             unless ($level == 1) {
866                                 warn "$ERROR The first section found is not of level 1: $_";
867                                 warn "$ERROR I'll split on sections of level $level...\n";
868                             }
869                             $toplevel = $level;
870                         }
871                         if ($level == $toplevel) {
872                             &next_doc;
873                             push(@lines, $SPLITTAG) if $split_num++;
874                             push(@sections, $name);
875                         }
876                     }
877                     $sec_num++;
878                     $docid = "SEC$sec_num";
879                     $tocid = "TOC$sec_num";
880                     # check biblio and glossary
881                     $in_bibliography = ($name =~ /^([A-Z]|\d+)?(\.\d+)*\s*bibliography$/i);
882                     $in_glossary = ($name =~ /^([A-Z]|\d+)?(\.\d+)*\s*glossary$/i);
883                     # check node
884                     if ($node) {
885                         if ($node2sec{$node}) {
886                             warn "$ERROR Duplicate node found: $node\n";
887                         } else {
888                             $node2sec{$node} = $name;
889                             $node2href{$node} = "$docu_doc#$docid";
890                             print "# node $node, section $name, level $level\n"
891                                 if $debug & $DEBUG_TOC;
892                         }
893                         $node = '';
894                     } else {
895                         print "# no node, section $name, level $level\n"
896                             if $debug & $DEBUG_TOC;
897                     }
898                     # update TOC
899                     while ($level > $curlevel) {
900                         $curlevel++;
901                         push(@toc_lines, "<UL>\n");
902                     }
903                     while ($level < $curlevel) {
904                         $curlevel--;
905                         push(@toc_lines, "</UL>\n");
906                     }
907                     $_ = "<LI>" . &anchor($tocid, "$docu_doc#$docid", $name, 1);
908                     push(@toc_lines, &substitute_style($_));
909                     # update DOC
910                     push(@lines, &html_debug("\n", __LINE__));
911                     &html_reset;
912                     $_ =  "<H$level>".&anchor($docid, "$docu_toc#$tocid", $name)."</H$level>\n";
913                     $_ = &debug($_, __LINE__);
914                     push(@lines, &html_debug("\n", __LINE__));
915                 }
916                 # update DOC
917                 foreach $line (split(/\n+/, $_)) {
918                     push(@lines, "$line\n");
919                 }
920                 next;
921             } else {
922                 warn "$ERROR Bad section line: $_";
923             }
924         } else {
925             # track variables
926             $value{$1} = $2, next if /^\@set\s+($VARRE)\s+(.*)$/o;
927             delete $value{$1}, next if /^\@clear\s+($VARRE)\s*$/o;
928             # store things
929             $value{'_setfilename'}   = $1, next if /^\@setfilename\s+(.*)$/;
930             $value{'_settitle'}      = $1, next if /^\@settitle\s+(.*)$/;
931             $value{'_author'}   .= "$1\n", next if /^\@author\s+(.*)$/;
932             $value{'_subtitle'} .= "$1\n", next if /^\@subtitle\s+(.*)$/;
933             $value{'_title'}    .= "$1\n", next if /^\@title\s+(.*)$/;
934             # index
935             if (/^\@(..?)index\s+/) {
936                 unless ($valid_index{$1}) {
937                     warn "$ERROR Undefined index command: $_";
938                     next;
939                 }
940                 $id = 'IDX' . ++$idx_num;
941                 $index = $1 . 'index';
942                 $what = &substitute_style($');
943                 $what =~ s/\s+$//;
944                 print "# found $index for '$what' id $id\n"
945                     if $debug & $DEBUG_INDEX;
946                 eval(<<EOC);
947                 if (defined(\$$index\{\$what\})) {
948                     \$$index\{\$what\} .= "$;$docu_doc#$id";
949                 } else {
950                     \$$index\{\$what\} = "$docu_doc#$id";
951                 }
952 EOC
953                 #
954                 # dirty hack to see if I can put an invisible anchor...
955                 #
956                 if ($html_element eq 'P' ||
957                     $html_element eq 'LI' ||
958                     $html_element eq 'DT' ||
959                     $html_element eq 'DD' ||
960                     $html_element eq 'ADDRESS' ||
961                     $html_element eq 'B' ||
962                     $html_element eq 'BLOCKQUOTE' ||
963                     $html_element eq 'PRE' ||
964                     $html_element eq 'SAMP') {
965                     push(@lines, &anchor($id, '', $invisible_mark, !$in_pre));
966                 } elsif ($html_element eq 'body') {
967                     push(@lines, &debug("<P>\n", __LINE__));
968                     push(@lines, &anchor($id, '', $invisible_mark, !$in_pre));
969                     &html_push('P');
970                 } elsif ($html_element eq 'DL' ||
971                          $html_element eq 'UL' ||
972                          $html_element eq 'OL' ) {
973                     $deferred_ref .= &anchor($id, '', $invisible_mark, !$in_pre) . " ";
974                 }
975                 next;
976             }
977             # list item
978             if (/^\@itemx?\s+/) {
979                 $what = $';
980                 $what =~ s/\s+$//;
981                 if ($in_bibliography && $use_bibliography) {
982                     if ($what =~ /^$BIBRE$/o) {
983                         $id = 'BIB' . ++$bib_num;
984                         $bib2href{$what} = "$docu_doc#$id";
985                         print "# found bibliography for '$what' id $id\n"
986                             if $debug & $DEBUG_BIB;
987                         $what = &anchor($id, '', $what);
988                     }
989                 } elsif ($in_glossary && $use_glossary) {
990                     $id = 'GLOSS' . ++$gloss_num;
991                     $entry = $what;
992                     $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
993                     $gloss2href{$entry} = "$docu_doc#$id";
994                     print "# found glossary for '$entry' id $id\n"
995                         if $debug & $DEBUG_GLOSS;
996                     $what = &anchor($id, '', $what);
997                 }
998                 &html_pop_if('P');
999                 if ($html_element eq 'DL' || $html_element eq 'DD') {
1000                     if ($things_map{$in_table} && !$what) {
1001                         # special case to allow @table @bullet for instance
1002                         push(@lines, &debug("<DT>$things_map{$in_table}\n", __LINE__));
1003                     } else {
1004                         push(@lines, &debug("<DT>\@$in_table\{$what\}\n", __LINE__));
1005                     }
1006                     push(@lines, "<DD>");
1007                     &html_push('DD') unless $html_element eq 'DD';
1008                     if ($table_type) { # add also an index
1009                         unshift(@input_spool, "\@${table_type}index $what\n");
1010                     }
1011                 } elsif ($html_element eq 'TABLE') {
1012                     push(@lines, &debug("<TR><TD>$what</TD>\n", __LINE__));
1013                     &html_push('TR');
1014                 } elsif ($html_element eq 'TR') {
1015                     push(@lines, &debug("</TR>\n", __LINE__));
1016                     push(@lines, &debug("<TR><TD>$what</TD>\n", __LINE__));
1017                 } else {
1018                     push(@lines, &debug("<LI>$what\n", __LINE__));
1019                     &html_push('LI') unless $html_element eq 'LI';
1020                 }
1021                 push(@lines, &html_debug("\n", __LINE__));
1022                 if ($deferred_ref) {
1023                     push(@lines, &debug("$deferred_ref\n", __LINE__));
1024                     $deferred_ref = '';
1025                 }
1026                 next;
1027             } elsif (/^\@tab\s+(.*)$/) {
1028                 push(@lines, "<TD>$1</TD>\n");
1029                 next;
1030             }
1031         }
1032     }
1033     # paragraph separator
1034     if ($_ eq "\n") {
1035         next if $#lines >= 0 && $lines[$#lines] eq "\n";
1036         if ($html_element eq 'P') {
1037             push(@lines, "\n");
1038             $_ = &debug("</P>\n", __LINE__);
1039             &html_pop;
1040         }
1041     } elsif ($html_element eq 'body' || $html_element eq 'BLOCKQUOTE') {
1042         push(@lines, "<P>\n");
1043         &html_push('P');
1044         $_ = &debug($_, __LINE__);
1045     }
1046     # otherwise
1047     push(@lines, $_);
1048 }
1049
1050 # finish TOC
1051 $level = 0;
1052 while ($level < $curlevel) {
1053     $curlevel--;
1054     push(@toc_lines, "</UL>\n");
1055 }
1056
1057 print "# end of pass 1\n" if $verbose;
1058
1059 #+++############################################################################
1060 #                                                                              #
1061 # Pass 2/3: handle style, menu, index, cross-reference                         #
1062 #                                                                              #
1063 #---############################################################################
1064
1065 @lines2 = ();                           # whole document (2nd pass)
1066 @lines3 = ();                           # whole document (3rd pass)
1067 $in_menu = 0;                           # am I inside a menu
1068
1069 while (@lines) {
1070     $_ = shift(@lines);
1071     #
1072     # special case (protected sections)
1073     #
1074     if (/^$PROTECTTAG/o) {
1075         push(@lines2, $_);
1076         next;
1077     }
1078     #
1079     # menu
1080     #
1081     $in_menu = 1, push(@lines2, &debug("<UL>\n", __LINE__)), next if /^\@menu\b/;
1082     $in_menu = 0, push(@lines2, &debug("</UL>\n", __LINE__)), next if /^\@end\s+menu\b/;
1083     if ($in_menu) {
1084         if (/^\*\s+($NODERE)::/o) {
1085             $descr = $';
1086             chop($descr);
1087             &menu_entry($1, $1, $descr);
1088         } elsif (/^\*\s+(.+):\s+([^\t,\.\n]+)[\t,\.\n]/) {
1089             $descr = $';
1090             chop($descr);
1091             &menu_entry($1, $2, $descr);
1092         } elsif (/^\*/) {
1093             warn "$ERROR Bad menu line: $_";
1094         } else { # description continued?
1095             push(@lines2, $_);
1096         }
1097         next;
1098     }
1099     #
1100     # printindex
1101     #
1102     if (/^\@printindex\s+(\w\w)\b/) {
1103         local($index, *ary, @keys, $key, $letter, $last_letter, @refs);
1104         if ($predefined_index{$1}) {
1105             $index = $predefined_index{$1} . 'index';
1106         } else {
1107             $index = $1 . 'index';
1108         }
1109         eval("*ary = *$index");
1110         @keys = keys(%ary);
1111         foreach $key (@keys) {
1112             $_ = $key;
1113             1 while s/<(\w+)>\`(.*)\'<\/\1>/$2/; # remove HTML tags with quotes
1114             1 while s/<(\w+)>(.*)<\/\1>/$2/;     # remove HTML tags
1115             $_ = &unprotect_html($_);
1116             &unprotect_texi;
1117             tr/A-Z/a-z/; # lowercase
1118             $key2alpha{$key} = $_;
1119             print "# index $key sorted as $_\n"
1120                 if $key ne $_ && $debug & $DEBUG_INDEX;
1121         }
1122         push(@lines2, "Jump to:\n");
1123         $last_letter = undef;
1124         foreach $key (sort byalpha @keys) {
1125             $letter = substr($key2alpha{$key}, 0, 1);
1126             $letter = substr($key2alpha{$key}, 0, 2) if $letter eq $;;
1127             if (!defined($last_letter) || $letter ne $last_letter) {
1128                 push(@lines2, "-\n") if defined($last_letter);
1129                 push(@lines2, "<A HREF=\"#$index\_$letter\">" . &protect_html($letter) . "</A>\n");
1130                 $last_letter = $letter;
1131             }
1132         }
1133         push(@lines2, "<P>\n");
1134         $last_letter = undef;
1135         foreach $key (sort byalpha @keys) {
1136             $letter = substr($key2alpha{$key}, 0, 1);
1137             $letter = substr($key2alpha{$key}, 0, 2) if $letter eq $;;
1138             if (!defined($last_letter) || $letter ne $last_letter) {
1139                 push(@lines2, "</DIR>\n") if defined($last_letter);
1140                 push(@lines2, "<H2><A NAME=\"$index\_$letter\">" . &protect_html($letter) . "</A></H2>\n");
1141                 push(@lines2, "<DIR>\n");
1142                 $last_letter = $letter;
1143             }
1144             @refs = ();
1145             foreach (split(/$;/, $ary{$key})) {
1146                 push(@refs, &anchor('', $_, $key, 0));
1147             }
1148             push(@lines2, "<LI>" . join(", ", @refs) . "\n");
1149         }
1150         push(@lines2, "</DIR>\n") if defined($last_letter);
1151         next;
1152     }
1153     #
1154     # simple style substitutions
1155     #
1156     $_ = &substitute_style($_);
1157     #
1158     # xref
1159     #
1160     while (/\@(x|px|info|)ref{($XREFRE)(}?)/o) {
1161         # note: Texinfo may accept other characters
1162         ($type, $nodes, $full) = ($1, $2, $3);
1163         ($before, $after) = ($`, $');
1164         if (! $full && $after) {
1165             warn "$ERROR Bad xref (no ending } on line): $_";
1166             $_ = "$before$;0${type}ref\{$nodes$after";
1167             next; # while xref
1168         }
1169         if ($type eq 'x') {
1170             $type = 'See ';
1171         } elsif ($type eq 'px') {
1172             $type = 'see ';
1173         } elsif ($type eq 'info') {
1174             $type = 'See Info';
1175         } else {
1176             $type = '';
1177         }
1178         unless ($full) {
1179             $next = shift(@lines);
1180             $next = &substitute_style($next);
1181             chop($nodes); # remove final newline
1182             if ($next =~ /\}/) { # split on 2 lines
1183                 $nodes .= " $`";
1184                 $after = $';
1185             } else {
1186                 $nodes .= " $next";
1187                 $next = shift(@lines);
1188                 $next = &substitute_style($next);
1189                 chop($nodes);
1190                 if ($next =~ /\}/) { # split on 3 lines
1191                     $nodes .= " $`";
1192                     $after = $';
1193                 } else {
1194                     warn "$ERROR Bad xref (no ending }): $_";
1195                     $_ = "$before$;0xref\{$nodes$after";
1196                     unshift(@lines, $next);
1197                     next; # while xref
1198                 }
1199             }
1200         }
1201         $nodes =~ s/\s+/ /g; # remove useless spaces
1202         @args = split(/\s*,\s*/, $nodes);
1203         $node = $args[0]; # the node is always the first arg
1204         &normalise_node($node);
1205         $sec = $node2sec{$node};
1206         if (@args == 5) { # reference to another manual
1207             $sec = $args[2] || $node;
1208             $man = $args[4] || $args[3];
1209             $_ = "${before}${type}section `$sec' in \@cite{$man}$after";
1210         } elsif ($type =~ /Info/) { # inforef
1211             warn "$ERROR Wrong number of arguments: $_" unless @args == 3;
1212             ($nn, $_, $in) = @args;
1213             $_ = "${before}${type} file `$in', node `$nn'$after";
1214         } elsif ($sec) {
1215             $href = $node2href{$node};
1216             $_ = "${before}${type}section " . &anchor('', $href, $sec) . $after;
1217         } else {
1218             warn "$ERROR Undefined node ($node): $_";
1219             $_ = "$before$;0xref{$nodes}$after";
1220         }
1221     }
1222     #
1223     # try to guess bibliography references or glossary terms
1224     #
1225     unless (/^<H\d><A NAME=\"SEC\d/) {
1226         if ($use_bibliography) {
1227             $done = '';
1228             while (/$BIBRE/o) {
1229                 ($pre, $what, $post) = ($`, $&, $');
1230                 $href = $bib2href{$what};
1231                 if (defined($href) && $post !~ /^[^<]*<\/A>/) {
1232                     $done .= $pre . &anchor('', $href, $what);
1233                 } else {
1234                     $done .= "$pre$what";
1235                 }
1236                 $_ = $post;
1237             }
1238             $_ = $done . $_;
1239         }
1240         if ($use_glossary) {
1241             $done = '';
1242             while (/\b\w+\b/) {
1243                 ($pre, $what, $post) = ($`, $&, $');
1244                 $entry = $what;
1245                 $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
1246                 $href = $gloss2href{$entry};
1247                 if (defined($href) && $post !~ /^[^<]*<\/A>/) {
1248                     $done .= $pre . &anchor('', $href, $what);
1249                 } else {
1250                     $done .= "$pre$what";
1251                 }
1252                 $_ = $post;
1253             }
1254             $_ = $done . $_;
1255         }
1256     }
1257     # otherwise
1258     push(@lines2, $_);
1259 }
1260 print "# end of pass 2\n" if $verbose;
1261
1262 #
1263 # split style substitutions
1264 #
1265 while (@lines2) {
1266     $_ = shift(@lines2);
1267     #
1268     # special case (protected sections)
1269     #
1270     if (/^$PROTECTTAG/o) {
1271         push(@lines3, $_);
1272         next;
1273     }
1274     #
1275     # split style substitutions
1276     #
1277     $old = '';
1278     while ($old ne $_) {
1279         $old = $_;
1280         if (/\@(\w+)\{/) {
1281             ($before, $style, $after) = ($`, $1, $');
1282             if (defined($style_map{$style})) {
1283                 $_ = $after;
1284                 $text = '';
1285                 $after = '';
1286                 $failed = 1;
1287                 while (@lines2) {
1288                     if (/\}/) {
1289                         $text .= $`;
1290                         $after = $';
1291                         $failed = 0;
1292                         last;
1293                     } else {
1294                         $text .= $_;
1295                         $_ = shift(@lines2);
1296                     }
1297                 }
1298                 if ($failed) {
1299                     die "* Bad syntax (\@$style) after: $before\n";
1300                 } else {
1301                     $text = &apply_style($style, $text);
1302                     $_ = "$before$text$after";
1303                 }
1304             }
1305         }
1306     }
1307     # otherwise
1308     push(@lines3, $_);
1309 }
1310 print "# end of pass 3\n" if $verbose;
1311
1312 #+++############################################################################
1313 #                                                                              #
1314 # Pass 4: foot notes, final cleanup                                            #
1315 #                                                                              #
1316 #---############################################################################
1317
1318 @foot_lines = ();                       # footnotes
1319 @doc_lines = ();                        # final document
1320 $end_of_para = 0;                       # true if last line is <P>
1321
1322 while (@lines3) {
1323     $_ = shift(@lines3);
1324     #
1325     # special case (protected sections)
1326     #
1327     if (/^$PROTECTTAG/o) {
1328         push(@doc_lines, $_);
1329         $end_of_para = 0;
1330         next;
1331     }
1332     #
1333     # footnotes
1334     #
1335     while (/\@footnote([^\{\s]+)\{/) {
1336         ($before, $d, $after) = ($`, $1, $');
1337         $_ = $after;
1338         $text = '';
1339         $after = '';
1340         $failed = 1;
1341         while (@lines3) {
1342             if (/\}/) {
1343                 $text .= $`;
1344                 $after = $';
1345                 $failed = 0;
1346                 last;
1347             } else {
1348                 $text .= $_;
1349                 $_ = shift(@lines3);
1350             }
1351         }
1352         if ($failed) {
1353             die "* Bad syntax (\@footnote) after: $before\n";
1354         } else {
1355             $foot_num++;
1356             $docid  = "DOCF$foot_num";
1357             $footid = "FOOT$foot_num";
1358             $foot = "($foot_num)";
1359             push(@foot_lines, "<H3>" . &anchor($footid, "$d#$docid", $foot) . "</H3>\n");
1360             $text = "<P>$text" unless $text =~ /^\s*<P>/;
1361             push(@foot_lines, "$text\n");
1362             $_ = $before . &anchor($docid, "$docu_foot#$footid", $foot) . $after;
1363         }
1364     }
1365     #
1366     # remove unnecessary <P>
1367     #
1368     if (/^\s*<P>\s*$/) {
1369         next if $end_of_para++;
1370     } else {
1371         $end_of_para = 0;
1372     }
1373     # otherwise
1374     push(@doc_lines, $_);
1375 }
1376 print "# end of pass 4\n" if $verbose;
1377
1378 #+++############################################################################
1379 #                                                                              #
1380 # Pass 5: print things                                                         #
1381 #                                                                              #
1382 #---############################################################################
1383
1384 $header = <<EOT;
1385 <!-- This HTML file has been created by $THISPROG
1386      from $docu on $TODAY -->
1387 EOT
1388
1389 $full_title = $value{'_title'} || $value{'_settitle'} || "Untitled Document";
1390 $title = $value{'_settitle'} || $full_title;
1391 $_ = &substitute_style($full_title);
1392 &unprotect_texi;
1393 s/\n$//; # rmv last \n (if any)
1394 $full_title = "<H1>" . join("</H1>\n<H1>", split(/\n/, $_)) . "</H1>\n";
1395
1396 #
1397 # print ToC
1398 #
1399 if (!$monolithic && @toc_lines) {
1400     if (open(FILE, "> $docu_toc")) {
1401         print "# creating $docu_toc...\n" if $verbose;
1402         &print_toplevel_header("$title - Table of Contents");
1403         &print_ruler;
1404         &print(*toc_lines, FILE);
1405         &print_toplevel_footer;
1406         close(FILE);
1407     } else {
1408         warn "$ERROR Can't write to $docu_toc: $!\n";
1409     }
1410 }
1411
1412 #
1413 # print footnotes
1414 #
1415 if (!$monolithic && @foot_lines) {
1416     if (open(FILE, "> $docu_foot")) {
1417         print "# creating $docu_foot...\n" if $verbose;
1418         &print_toplevel_header("$title - Footnotes");
1419         &print_ruler;
1420         &print(*foot_lines, FILE);
1421         &print_toplevel_footer;
1422         close(FILE);
1423     } else {
1424         warn "$ERROR Can't write to $docu_foot: $!\n";
1425     }
1426 }
1427
1428 #
1429 # print document
1430 #
1431 if ($split_chapter || $split_node) { # split
1432     $doc_num = 0;
1433     $last_num = scalar(@sections);
1434     $first_doc = &doc_name(1);
1435     $last_doc = &doc_name($last_num);
1436     while (@sections) {
1437         $section = shift(@sections);
1438         &next_doc;
1439         if (open(FILE, "> $docu_doc")) {
1440             print "# creating $docu_doc...\n" if $verbose;
1441             &print_header("$title - $section");
1442             $prev_doc = ($doc_num == 1 ? undef : &doc_name($doc_num - 1));
1443             $next_doc = ($doc_num == $last_num ? undef : &doc_name($doc_num + 1));
1444             $navigation = "Go to the ";
1445             $navigation .= ($prev_doc ? &anchor('', $first_doc, "first") : "first");
1446             $navigation .= ", ";
1447             $navigation .= ($prev_doc ? &anchor('', $prev_doc, "previous") : "previous");
1448             $navigation .= ", ";
1449             $navigation .= ($next_doc ? &anchor('', $next_doc, "next") : "next");
1450             $navigation .= ", ";
1451             $navigation .= ($next_doc ? &anchor('', $last_doc, "last") : "last");
1452             $navigation .= " section, " . &anchor('', $docu_toc, "table of contents") . ".\n";
1453             print FILE $navigation;
1454             &print_ruler;
1455             # find corresponding lines
1456             @tmp_lines = ();
1457             while (@doc_lines) {
1458                 $_ = shift(@doc_lines);
1459                 last if ($_ eq $SPLITTAG);
1460                 push(@tmp_lines, $_);
1461             }
1462             &print(*tmp_lines, FILE);
1463             &print_ruler;
1464             print FILE $navigation;
1465             &print_footer;
1466             close(FILE);
1467         } else {
1468             warn "$ERROR Can't write to $docu_doc: $!\n";
1469         }
1470     }
1471 } else { # not split
1472     if (open(FILE, "> $docu_doc")) {
1473         print "# creating $docu_doc...\n" if $verbose;
1474         if ($monolithic || !@toc_lines) {
1475             &print_toplevel_header($title);
1476         } else {
1477             &print_header($title);
1478             print FILE $full_title;
1479         }
1480         if ($monolithic && @toc_lines) {
1481             &print_ruler;
1482             print FILE "<H1>Table of Contents</H1>\n";
1483             &print(*toc_lines, FILE);
1484         }
1485         &print_ruler;
1486         &print(*doc_lines, FILE);
1487         if ($monolithic && @foot_lines) {
1488             &print_ruler;
1489             print FILE "<H1>Footnotes</H1>\n";
1490             &print(*foot_lines, FILE);
1491         }
1492         if ($monolithic || !@toc_lines) {
1493             &print_toplevel_footer;
1494         } else {
1495             &print_footer;
1496         }
1497         close(FILE);
1498     } else {
1499         warn "$ERROR Can't write to $docu_doc: $!\n";
1500     }
1501 }
1502
1503 print "# that's all folks\n" if $verbose;
1504
1505 #+++############################################################################
1506 #                                                                              #
1507 # Low level functions                                                          #
1508 #                                                                              #
1509 #---############################################################################
1510
1511 sub update_sec_num {
1512     local($name, $level) = @_;
1513
1514     $level--; # here we start at 0
1515     if ($name =~ /^appendix/) {
1516         # appendix style
1517         if (defined(@appendix_sec_num)) {
1518             &incr_sec_num($level, @appendix_sec_num);
1519         } else {
1520             @appendix_sec_num = ('A', 0, 0, 0);
1521         }
1522         return(join('.', @appendix_sec_num[0..$level]));
1523     } else {
1524         # normal style
1525         if (defined(@normal_sec_num)) {
1526             &incr_sec_num($level, @normal_sec_num);
1527         } else {
1528             @normal_sec_num = (1, 0, 0, 0);
1529         }
1530         return(join('.', @normal_sec_num[0..$level]));
1531     }
1532 }
1533
1534 sub incr_sec_num {
1535     local($level, $l);
1536     $level = shift(@_);
1537     $_[$level]++;
1538     foreach $l ($level+1 .. 3) {
1539         $_[$l] = 0;
1540     }
1541 }
1542
1543 sub check {
1544     local($_, %seen, %context, $before, $match, $after);
1545
1546     while (<>) {
1547         if (/\@(\*|\.|\:|\@|\{|\})/) {
1548             $seen{$&}++;
1549             $context{$&} .= "> $_" if $verbose;
1550             $_ = "$`XX$'";
1551             redo;
1552         }
1553         if (/\@(\w+)/) {
1554             ($before, $match, $after) = ($`, $&, $');
1555             if ($before =~ /\b[\w-]+$/ && $after =~ /^[\w-.]*\b/) { # e-mail address
1556                 $seen{'e-mail address'}++;
1557                 $context{'e-mail address'} .= "> $_" if $verbose;
1558             } else {
1559                 $seen{$match}++;
1560                 $context{$match} .= "> $_" if $verbose;
1561             }
1562             $match =~ s/^\@/X/;
1563             $_ = "$before$match$after";
1564             redo;
1565         }
1566     }
1567     
1568     foreach (sort(keys(%seen))) {
1569         if ($verbose) {
1570             print "$_\n";
1571             print $context{$_};
1572         } else {
1573             print "$_ ($seen{$_})\n";
1574         }
1575     }
1576 }
1577
1578 sub open {
1579     local($name) = @_;
1580
1581     ++$fh_name;
1582     if (open($fh_name, $name)) {
1583         unshift(@fhs, $fh_name);
1584     } else {
1585         warn "$ERROR Can't read file $name: $!\n";
1586     }
1587 }
1588
1589 sub init_input {
1590     @fhs = ();                  # hold the file handles to read
1591     @input_spool = ();          # spooled lines to read
1592     $fh_name = 'FH000';
1593     &open($docu);
1594 }
1595
1596 sub next_line {
1597     local($fh, $line);
1598
1599     if (@input_spool) {
1600         $line = shift(@input_spool);
1601         return($line);
1602     }
1603     while (@fhs) {
1604         $fh = $fhs[0];
1605         $line = <$fh>;
1606         return($line) if $line;
1607         close($fh);
1608         shift(@fhs);
1609     }
1610     return(undef);
1611 }
1612
1613 # used in pass 1, use &next_line
1614 sub skip_until {
1615     local($tag) = @_;
1616     local($_);
1617
1618     while ($_ = &next_line) {
1619         return if /^\@end\s+$tag\s*$/;
1620     }
1621     die "* Failed to find '$tag' after: " . $lines[$#lines];
1622 }
1623
1624 #
1625 # HTML stacking to have a better HTML output
1626 #
1627
1628 sub html_reset {
1629     @html_stack = ('html');
1630     $html_element = 'body';
1631 }
1632
1633 sub html_push {
1634     local($what) = @_;
1635     push(@html_stack, $html_element);
1636     $html_element = $what;
1637 }
1638
1639 sub html_push_if {
1640     local($what) = @_;
1641     push(@html_stack, $html_element)
1642         if ($html_element && $html_element ne 'P');
1643     $html_element = $what;
1644 }
1645
1646 sub html_pop {
1647     $html_element = pop(@html_stack);
1648 }
1649
1650 sub html_pop_if {
1651     local($elt);
1652
1653     if (@_) {
1654         foreach $elt (@_) {
1655             if ($elt eq $html_element) {
1656                 $html_element = pop(@html_stack) if @html_stack;
1657                 last;
1658             }
1659         }
1660     } else {
1661         $html_element = pop(@html_stack) if @html_stack;
1662     }
1663 }
1664
1665 sub html_debug {
1666     local($what, $line) = @_;
1667     return("<!-- $line @html_stack, $html_element -->$what")
1668         if $debug & $DEBUG_HTML;
1669     return($what);
1670 }
1671
1672 # to debug the output...
1673 sub debug {
1674     local($what, $line) = @_;
1675     return("<!-- $line -->$what")
1676         if $debug & $DEBUG_HTML;
1677     return($what);
1678 }
1679
1680 sub normalise_node {
1681     $_[0] =~ s/\s+/ /g;
1682     $_[0] =~ s/ $//;
1683     $_[0] =~ s/^ //;
1684 }
1685
1686 sub menu_entry {
1687     local($entry, $node, $descr) = @_;
1688     local($href);
1689
1690     &normalise_node($node);
1691     $href = $node2href{$node};
1692     if ($href) {
1693         $descr =~ s/^\s+//;
1694         $descr = ": $descr" if $descr;
1695         push(@lines2, "<LI>" . &anchor('', $href, $entry) . "$descr\n");
1696     } else {
1697         warn "$ERROR Undefined node ($node): $_";
1698     }
1699 }
1700
1701 sub do_ctrl { "^$_[0]" }
1702
1703 sub do_email {
1704     local($addr, $text) = split(/,\s*/, $_[0]);
1705
1706     $text = $addr unless $text;
1707     &anchor('', "mailto:$addr", $text);
1708 }
1709
1710 sub do_sc { "\U$_[0]\E" }
1711
1712 sub do_uref {
1713     local($url, $text) = split(/,\s*/, $_[0]);
1714
1715     $text = $url unless $text;
1716     &anchor('', $url, $text);
1717 }
1718
1719 sub do_url { &anchor('', $_[0], $_[0]) }
1720
1721 sub apply_style {
1722     local($texi_style, $text) = @_;
1723     local($style);
1724
1725     $style = $style_map{$texi_style};
1726     if (defined($style)) { # known style
1727         if ($style =~ /^\"/) { # add quotes
1728             $style = $';
1729             $text = "\`$text\'";
1730         }
1731         if ($style =~ /^\&/) { # custom
1732             $style = $';
1733             $text = &$style($text);
1734         } elsif ($style) { # good style
1735             $text = "<$style>$text</$style>";
1736         } else { # no style
1737         }
1738     } else { # unknown style
1739         $text = undef;
1740     }
1741     return($text);
1742 }
1743
1744 # remove Texinfo styles
1745 sub remove_style {
1746     local($_) = @_;
1747     s/\@\w+{([^\{\}]+)}/$1/g;
1748     return($_);
1749 }
1750
1751 sub substitute_style {
1752     local($_) = @_;
1753     local($changed, $done, $style, $text);
1754
1755     $changed = 1;
1756     while ($changed) {
1757         $changed = 0;
1758         $done = '';
1759         while (/\@(\w+){([^\{\}]+)}/) {
1760             $text = &apply_style($1, $2);
1761             if ($text) {
1762                 $_ = "$`$text$'";
1763                 $changed = 1;
1764             } else {
1765                 $done .= "$`\@$1";
1766                 $_ = "{$2}$'";
1767             }
1768         }
1769         $_ = $done . $_;
1770     }
1771     return($_);
1772 }
1773
1774 sub anchor {
1775     local($name, $href, $text, $newline) = @_;
1776     local($result);
1777
1778     $result = "<A";
1779     $result .= " NAME=\"$name\"" if $name;
1780     $result .= " HREF=\"$href\"" if $href;
1781     $result .= ">$text</A>";
1782     $result .= "\n" if $newline;
1783     return($result);
1784 }
1785
1786 sub pretty_date {
1787     local(@MoY, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
1788
1789     @MoY = ('January', 'Febuary', 'March', 'April', 'May', 'June',
1790             'July', 'August', 'September', 'October', 'November', 'December');
1791     ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
1792     $year += ($year < 70) ? 2000 : 1900;
1793     return("$mday $MoY[$mon] $year");
1794 }
1795
1796 sub doc_name {
1797     local($num) = @_;
1798
1799     return("${docu_name}_$num.html");
1800 }
1801
1802 sub next_doc {
1803     $docu_doc = &doc_name(++$doc_num);
1804 }
1805
1806 sub print {
1807     local(*lines, $fh) = @_;
1808     local($_);
1809
1810     while (@lines) {
1811         $_ = shift(@lines);
1812         if (/^$PROTECTTAG/o) {
1813             $_ = $tag2pro{$_};
1814         } else {
1815             &unprotect_texi;
1816         }
1817         print $fh $_;
1818     }
1819 }
1820
1821 sub print_ruler {
1822     print FILE "<P><HR><P>\n";
1823 }
1824
1825 sub print_header {
1826     local($_);
1827
1828     # clean the title
1829     $_ = &remove_style($_[0]);
1830     &unprotect_texi;
1831     # print the header
1832     if ($doctype eq 'html2') {
1833         print FILE $html2_doctype;
1834     } elsif ($doctype) {
1835         print FILE $doctype;
1836     }
1837     print FILE <<EOT;
1838 <HTML>
1839 <HEAD>
1840 $header
1841 <TITLE>$_</TITLE>
1842 </HEAD>
1843 <BODY>
1844 EOT
1845 }
1846
1847 sub print_toplevel_header {
1848     local($_);
1849
1850     &print_header; # pass given arg...
1851     print FILE $full_title;
1852     if ($value{'_subtitle'}) {
1853         $value{'_subtitle'} =~ s/\n+$//;
1854         foreach (split(/\n/, $value{'_subtitle'})) {
1855             $_ = &substitute_style($_);
1856             &unprotect_texi;
1857             print FILE "<H2>$_</H2>\n";
1858         }
1859     }
1860     if ($value{'_author'}) {
1861         $value{'_author'} =~ s/\n+$//;
1862         foreach (split(/\n/, $value{'_author'})) {
1863             $_ = &substitute_style($_);
1864             &unprotect_texi;
1865             s/[\w.-]+\@[\w.-]+/<A HREF="mailto:$&">$&<\/A>/g;
1866             print FILE "<ADDRESS>$_</ADDRESS>\n";
1867         }
1868     }
1869     print FILE "<P>\n";
1870 }
1871
1872 sub print_footer {
1873     print FILE <<EOT;
1874 </BODY>
1875 </HTML>
1876 EOT
1877 }
1878
1879 sub print_toplevel_footer {
1880     &print_ruler;
1881     print FILE <<EOT;
1882 This document was generated on $TODAY using the
1883 <A HREF=\"$HOMEPAGE\">texi2html</A>
1884 translator version 1.52.</P>
1885 EOT
1886     &print_footer;
1887 }
1888
1889 sub protect_texi {
1890     # protect @ { } ` '
1891     s/\@\@/$;0/go;
1892     s/\@\{/$;1/go;
1893     s/\@\}/$;2/go;
1894     s/\@\`/$;3/go;
1895     s/\@\'/$;4/go;
1896 }
1897
1898 sub protect_html {
1899     local($what) = @_;
1900     # protect & < >
1901     $what =~ s/\&/\&\#38;/g;
1902     $what =~ s/\</\&\#60;/g;
1903     $what =~ s/\>/\&\#62;/g;
1904     # but recognize some HTML things
1905     $what =~ s/\&\#60;\/A\&\#62;/<\/A>/g;             # </A>
1906     $what =~ s/\&\#60;A ([^\&]+)\&\#62;/<A $1>/g;     # <A [^&]+>
1907     $what =~ s/\&\#60;IMG ([^\&]+)\&\#62;/<IMG $1>/g; # <IMG [^&]+>
1908     return($what);
1909 }
1910
1911 sub unprotect_texi {
1912     s/$;0/\@/go;
1913     s/$;1/\{/go;
1914     s/$;2/\}/go;
1915     s/$;3/\`/go;
1916     s/$;4/\'/go;
1917 }
1918
1919 sub unprotect_html {
1920     local($what) = @_;
1921     $what =~ s/\&\#38;/\&/g;
1922     $what =~ s/\&\#60;/\</g;
1923     $what =~ s/\&\#62;/\>/g;
1924     return($what);
1925 }
1926
1927 sub byalpha {
1928     $key2alpha{$a} cmp $key2alpha{$b};
1929 }
1930
1931 ##############################################################################
1932
1933         # These next few lines are legal in both Perl and nroff.
1934
1935 .00 ;                   # finish .ig
1936  
1937 'di                     \" finish diversion--previous line must be blank
1938 .nr nl 0-1              \" fake up transition to first page again
1939 .nr % 0                 \" start at page 1
1940 '; __END__ ############# From here on it's a standard manual page ############
1941 .TH TEXI2HTML 1 "01/05/98"
1942 .AT 3
1943 .SH NAME
1944 texi2html \- a Texinfo to HTML converter
1945 .SH SYNOPSIS
1946 .B texi2html [options] file
1947 .PP
1948 .B texi2html -check [-verbose] files
1949 .SH DESCRIPTION
1950 .I Texi2html
1951 converts the given Texinfo file to a set of HTML files. It tries to handle
1952 most of the Texinfo commands. It creates hypertext links for cross-references,
1953 footnotes...
1954 .PP
1955 It also tries to add links from a reference to its corresponding entry in the
1956 bibliography (if any). It may also handle a glossary (see the
1957 .B \-glossary
1958 option).
1959 .PP
1960 .I Texi2html
1961 creates several files depending on the contents of the Texinfo file and on
1962 the chosen options (see FILES).
1963 .PP
1964 The HTML files created by
1965 .I texi2html
1966 are closer to TeX than to Info, that's why
1967 .I texi2html
1968 converts @iftex sections and not @ifinfo ones by default. You can reverse
1969 this with the \-expandinfo option.
1970 .SH OPTIONS
1971 .TP 12
1972 .B \-check
1973 Check the given file and give the list of all things that may be Texinfo commands.
1974 This may be used to check the output of
1975 .I texi2html
1976 to find the Texinfo commands that have been left in the HTML file.
1977 .TP
1978 .B \-expandinfo
1979 Expand @ifinfo sections, not @iftex ones.
1980 .TP
1981 .B \-glossary
1982 Use the section named 'Glossary' to build a list of terms and put links in the HTML
1983 document from each term toward its definition.
1984 .TP
1985 .B \-invisible \fIname\fP
1986 Use \fIname\fP to create invisible destination anchors for index links
1987 (you can for instance use the invisible.xbm file shipped with this program).
1988 This is a workaround for a known bug of many WWW browsers, including netscape.
1989 .TP
1990 .B \-I \fIdir\fP
1991 Look also in \fIdir\fP to find included files.
1992 .TP
1993 .B \-menu
1994 Show the Texinfo menus; by default they are ignored.
1995 .TP
1996 .B \-monolithic
1997 Output only one file, including the table of contents and footnotes.
1998 .TP
1999 .B \-number
2000 Number the sections.
2001 .TP
2002 .B \-split_chapter
2003 Split the output into several HTML files (one per main section:
2004 chapter, appendix...).
2005 .TP
2006 .B \-split_node
2007 Split the output into several HTML files (one per node).
2008 .TP
2009 .B \-usage
2010 Print usage instructions, listing the current available command-line options.
2011 .TP
2012 .B \-verbose
2013 Give a verbose output. Can be used with the
2014 .B \-check
2015 option.
2016 .PP
2017 .SH FILES
2018 By default
2019 .I texi2html
2020 creates the following files (foo being the name of the Texinfo file):
2021 .TP 16
2022 .B foo_toc.html
2023 The table of contents.
2024 .TP
2025 .B foo.html
2026 The document's contents.
2027 .TP
2028 .B foo_foot.html
2029 The footnotes (if any).
2030 .PP
2031 When used with the
2032 .B \-split
2033 option, it creates several files (one per chapter or node), named
2034 .B foo_n.html
2035 (n being the indice of the chapter or node), instead of the single
2036 .B foo.html
2037 file.
2038 .PP
2039 When used with the
2040 .B \-monolithic
2041 option, it creates only one file:
2042 .B foo.html
2043 .SH VARIABLES
2044 .I texi2html
2045 predefines the following variables: \fBhtml\fP, \fBtexi2html\fP.
2046 .SH ADDITIONAL COMMANDS
2047 .I texi2html
2048 implements the following non-Texinfo commands (maybe they are in Texinfo now...):
2049 .TP 16
2050 .B @ifhtml
2051 This indicates the start of an HTML section, this section will passed through
2052 without any modification.
2053 .TP
2054 .B @end ifhtml
2055 This indicates the end of an HTML section.
2056 .SH VERSION
2057 This is \fItexi2html\fP version 1.52, 01/05/98.
2058 .PP
2059 The latest version of \fItexi2html\fP can be found in WWW, cf. URL
2060 http://wwwinfo.cern.ch/dis/texi2html/
2061 .SH AUTHOR
2062 The main author is Lionel Cons, CERN IT/DIS/OSE, Lionel.Cons@cern.ch.
2063 Many other people around the net contributed to this program.
2064 .SH COPYRIGHT
2065 This program is the intellectual property of the European
2066 Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is
2067 provided by CERN. No liability whatsoever is accepted for any loss or damage
2068 of any kind resulting from any defect or inaccuracy in this information or
2069 code.
2070 .PP
2071 CERN, 1211 Geneva 23, Switzerland
2072 .SH "SEE ALSO"
2073 GNU Texinfo Documentation Format,
2074 HyperText Markup Language (HTML),
2075 World Wide Web (WWW).
2076 .SH BUGS
2077 This program does not understand all Texinfo commands (yet).
2078 .PP
2079 TeX specific commands (normally enclosed in @iftex) will be
2080 passed unmodified.
2081 .ex