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