1#- -*- perl -*-
2#
3# $Id: latex2html.pin,v 1.71 2004/01/06 23:49:54 RRM Exp $
4#
5# Comprises patches and revisions by various authors:
6#   See Changes, the log file of LaTeX2HTML.
7#
8# Original Copyright notice:
9#
10# LaTeX2HTML by Nikos Drakos <nikos@cbl.leeds.ac.uk>
11
12# ****************************************************************
13# LaTeX To HTML Translation **************************************
14# ****************************************************************
15# LaTeX2HTML is a Perl program that translates LaTeX source
16# files into HTML (HyperText Markup Language). For each source
17# file given as an argument the translator will create a
18# directory containing the corresponding HTML files.
19#
20# The man page for this program is included at the end of this file
21# and can be viewed using "perldoc latex2html"
22#
23# For more information on this program and some examples of its
24# capabilities visit
25#
26#          http://www.latex2html.org/
27#
28# or see the accompanying documentation in the docs/  directory
29#
30# or
31#
32#    http://www-texdev.ics.mq.edu.au/l2h/docs/manual/
33#
34# or
35#
36#    http://www.cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/
37#
38# Original code written by Nikos Drakos, July 1993.
39#
40# Address: Computer Based Learning Unit
41#          University of Leeds
42#          Leeds,  LS2 9JT
43#
44# Copyright (c) 1993-95. All rights reserved.
45#
46#
47# Extensively modified by Ross Moore, Herb Swan and others
48#
49# Address: Mathematics Department
50#          Macquarie University
51#          Sydney, Australia, 2109
52#
53# Copyright (c) 1996-2001. All rights reserved.
54#
55# See general license in the LICENSE file.
56#
57##########################################################################
58
59use 5.003; # refuse to work with old and buggy perl version
60#use strict;
61#use diagnostics;
62
63# include some perl packages; these come with the standard distribution
64use Getopt::Long;
65use Fcntl;
66use AnyDBM_File;
67
68# The following are global variables that also appear in some modules
69use vars qw($LATEX2HTMLDIR $LATEX2HTMLPLATDIR $SCRIPT
70            %Month %used_icons $inside_tabbing $TABLE_attribs
71            %mathentities $date_name $outer_math $TABLE__CELLPADDING_rx);
72
73#- NOTE: This file contains a sort of preprocessor information, similar
74#- to C's #define statement, so please be careful when removing comments!
75#-
76#- The (texlive) wrapper sets these values
77#- or it is stored in the enviroment
78#-
79#unless @wrapper@ || @texlive@
80BEGIN {
81  # print "scanning for l2hdir\n";
82  if($ENV{'LATEX2HTMLDIR'}) {
83    $LATEX2HTMLDIR = $ENV{'LATEX2HTMLDIR'};
84  } else {
85    $ENV{'LATEX2HTMLDIR'} = $LATEX2HTMLDIR = '@LATEX2HTMLDIR@';
86  }
87
88  if($ENV{'LATEX2HTMLPLATDIR'}) {
89    $LATEX2HTMLPLATDIR = $ENV{'LATEX2HTMLPLATDIR'};
90  } else {
91    $LATEX2HTMLPLATDIR = '@LATEX2HTMLPLATDIR@'||$LATEX2HTMLDIR;
92    $ENV{'LATEX2HTMLPLATDIR'} = $LATEX2HTMLPLATDIR;
93  }
94  if(-d $LATEX2HTMLPLATDIR) {
95    push(@INC,$LATEX2HTMLPLATDIR);
96  }
97
98  if(-d $LATEX2HTMLDIR) {
99    push(@INC,$LATEX2HTMLDIR);
100  } else {
101    die qq{Fatal: Directory "$LATEX2HTMLDIR" does not exist.\n};
102  }
103}
104#fi
105
106use L2hos; # Operating system dependent routines
107
108# $^W = 1; # turn on warnings
109
110my $RELEASE = '@distver@';
111my ($REVISION) = q$Revision: 1.71 $ =~ /:\s*(\S+)/;
112
113# The key, which delimts expressions defined in the environment
114# depends on the operating system.
115$envkey = L2hos->pathd();
116
117# $dd is the directory delimiter character
118$dd = L2hos->dd();
119
120# make sure the $LATEX2HTMLDIR is on the search-path for forked processes
121if($ENV{'PERL5LIB'}) {
122  $ENV{'PERL5LIB'} .= "$envkey$LATEX2HTMLDIR"
123    unless($ENV{'PERL5LIB'} =~ m|\Q$LATEX2HTMLDIR\E|o);
124} else {
125  $ENV{'PERL5LIB'} = $LATEX2HTMLDIR;
126}
127
128# Local configuration, read at runtime
129# Read the $CONFIG_FILE  (usually l2hconf.pm )
130if($ENV{'L2HCONFIG'}) {
131  require $ENV{'L2HCONFIG'} ||
132    die "Fatal (require $ENV{'L2HCONFIG'}): $!";
133} else {
134  eval 'use l2hconf';
135  if($@) {
136    die "Fatal (use l2hconf): $@\n";
137  }
138}
139
140# MRO: Changed this to global value in config/config.pl
141# change these whenever you do a patch to this program and then
142# name the resulting patch file accordingly
143# $TVERSION = "@distver@";
144#$TPATCHLEVEL = " beta";
145#$TPATCHLEVEL = " release";
146#$RELDATE = "(March 30, 1999)";
147#$TEX2HTMLV_SHORT = $TVERSION . $TPATCHLEVEL;
148
149$TEX2HTMLV_SHORT = $RELEASE;
150$TEX2HTMLVERSION = "$TEX2HTMLV_SHORT ($REVISION)";
151$TEX2HTMLADDRESS = "http://www.latex2html.org/";
152$AUTHORADDRESS = "http://cbl.leeds.ac.uk/nikos/personal.html";
153#$AUTHORADDRESS2 = "http://www-math.mpce.mq.edu.au/%7Eross/";
154$AUTHORADDRESS2 = "http://www.maths.mq.edu.au/&#126;ross/";
155
156# Set $HOME to what the system considers the home directory
157$HOME = L2hos->home();
158push(@INC,$HOME);
159
160# flush stdout with every print -- gives better feedback during
161# long computations
162$| = 1;
163
164# set Perl's subscript separator to LaTeX's illegal character.
165# (quite defensive but why not)
166$; = "\000";
167
168# No arguments!!
169unless(@ARGV) {
170  die "Error: No files to process!\n";
171}
172
173# Image prefix
174#if @texlive@
175$IMAGE_PREFIX = L2hos->plat() eq 'dos' ? 'ps' : '_image';
176#else
177  #if @plat@ eq 'dos'
178$IMAGE_PREFIX = 'ps';
179  #else
180$IMAGE_PREFIX = '_image';
181  #fi
182#fi - texlive
183
184# Partition prefix
185$PARTITION_PREFIX = 'part_' unless $PARTITION_PREFIX;
186
187# Author address
188@address_data = &address_data('ISO');
189$ADDRESS = "$address_data[0]\n$address_data[1]";
190
191# ensure non-zero defaults
192$MAX_SPLIT_DEPTH = 4 unless ($MAX_SPLIT_DEPTH);
193$MAX_LINK_DEPTH = 4 unless ($MAX_LINK_DEPTH);
194$TOC_DEPTH = 4 unless ($TOC_DEPTH);
195
196# A global value may already be set in the $CONFIG_FILE
197$INIT_FILE_NAME = $ENV{'L2HINIT_NAME'} || '.latex2html-init'
198   unless $INIT_FILE_NAME;
199
200# Read the $HOME/$INIT_FILE_NAME if one is found
201if (-f "$HOME$dd$INIT_FILE_NAME" && -r _) {
202    print "Note: Loading $HOME$dd$INIT_FILE_NAME\n";
203    require("$HOME$dd$INIT_FILE_NAME");
204    $INIT_FILE = "$HOME$dd$INIT_FILE_NAME";
205    # _MRO_TODO_: Introduce a version to be checked?
206    die "Error: You have an out-of-date " . $HOME .
207	"$dd$INIT_FILE_NAME file.\nPlease update or delete it.\n"
208	if ($DESTDIR eq '.');
209}
210
211# Read the $INIT_FILE_NAME file if one is found in current directory
212if ( L2hos->Cwd() ne $HOME && -f ".$dd$INIT_FILE_NAME" && -r _) {
213    print "Note: Loading .$dd$INIT_FILE_NAME\n";
214    require(".$dd$INIT_FILE_NAME");
215    $INIT_FILE = "$INIT_FILE_NAME";
216}
217die "Error: '.' is an incorrect setting for DESTDIR.\n" .
218    "Please check your $INIT_FILE_NAME file.\n"
219    if ($DESTDIR eq '.');
220
221# User home substitutions
222$LATEX2HTMLSTYLES =~ s/~([$dd$dd$envkey]|$)/$HOME$1/go;
223# the next line fails utterly on non-UNIX systems
224$LATEX2HTMLSTYLES =~ s/~([^$dd$dd$envkey]+)/L2hos->home($1)/geo;
225
226#absolutise the paths
227$LATEX2HTMLSTYLES = join($envkey,
228                        map(L2hos->Make_directory_absolute($_),
229                                split(/$envkey/o, $LATEX2HTMLSTYLES)));
230
231#HWS:  That was the last reference to HOME.  Now set HOME to $LATEX2HTMLDIR,
232#	to enable dvips to see that version of .dvipsrc!  But only if we
233#	have DVIPS_MODE not set - yes - this is a horrible nasty kludge
234# MRO: The file has to be updated by configure _MRO_TODO_
235
236if ($PK_GENERATION && ! $DVIPS_MODE) {
237    $ENV{HOME} =  $LATEX2HTMLDIR;
238    delete $ENV{PRINTER}; # Overrides .dvipsrc
239}
240
241# language of the DTD specified in the <DOCTYPE...> tag
242$ISO_LANGUAGE = 'EN' unless $ISO_LANGUAGE;
243
244# Save the command line arguments, quote where necessary
245$argv = join(' ', map {/[\s#*!\$%]/ ? "'$_'" : $_ } @ARGV);
246
247# Pre-process the command line for backward compatibility
248foreach(@ARGV) {
249  s/^--?no_/-no/; # replace e.g. no_fork by nofork
250  # s/^[+](\d+)$/$1/; # remove + in front of integers
251}
252
253# Process command line options
254my %opt;
255unless(GetOptions(\%opt, # all non-linked options go into %opt
256        # option                linkage (optional)
257        'help|h',
258        'version|V',
259        'split=s',
260        'link=s',
261        'toc_depth=i',          \$TOC_DEPTH,
262        'toc_stars!',           \$TOC_STARS,
263        'short_extn!',          \$SHORTEXTN,
264        'iso_language=s',       \$ISO_LANGUAGE,
265        'validate!',            \$HTML_VALIDATE,
266        'latex!',
267        'djgpp!',               \$DJGPP,
268        'fork!',                \$CAN_FORK,
269        'external_images!',     \$EXTERNAL_IMAGES,
270        'ascii_mode!',          \$ASCII_MODE,
271        'lcase_tags!',          \$LOWER_CASE_TAGS,
272        'ps_images!',           \$PS_IMAGES,
273        'font_size=s',          \$FONT_SIZE,
274        'tex_defs!',            \$TEXDEFS,
275        'navigation!',
276        'top_navigation!',      \$TOP_NAVIGATION,
277        'bottom_navigation!',   \$BOTTOM_NAVIGATION,
278        'auto_navigation!',     \$AUTO_NAVIGATION,
279        'index_in_navigation!', \$INDEX_IN_NAVIGATION,
280        'contents_in_navigation!', \$CONTENTS_IN_NAVIGATION,
281        'next_page_in_navigation!', \$NEXT_PAGE_IN_NAVIGATION,
282        'previous_page_in_navigation!', \$PREVIOUS_PAGE_IN_NAVIGATION,
283        'footnode!',
284        'numbered_footnotes!',  \$NUMBERED_FOOTNOTES,
285        'prefix=s',             \$PREFIX,
286        'auto_prefix!',         \$AUTO_PREFIX,
287        'long_titles=i',        \$LONG_TITLES,
288        'custom_titles!',       \$CUSTOM_TITLES,
289        'title|t=s',            \$TITLE,
290        'rooted!',              \$ROOTED,
291        'rootdir=s',
292        'dir=s',                \$FIXEDDIR,
293        'mkdir',                \$MKDIR,
294        'address=s',            \$ADDRESS,
295        'noaddress',
296        'subdir!',
297        'info=s',               \$INFO,
298        'noinfo',
299        'auto_link!',
300        'reuse=i',              \$REUSE,
301        'noreuse',
302        'antialias_text!',      \$ANTI_ALIAS_TEXT,
303        'antialias!',           \$ANTI_ALIAS,
304        'transparent!',         \$TRANSPARENT_FIGURES,
305        'white!',               \$WHITE_BACKGROUND,
306        'discard!',             \$DISCARD_PS,
307        'image_type=s',         \$IMAGE_TYPE,
308        'images!',
309        'accent_images=s',      \$ACCENT_IMAGES,
310        'noaccent_images',
311        'style=s',              \$STYLESHEET,
312        'parbox_images!',
313        'math!',
314        'math_parsing!',
315        'latin!',
316        'entities!',            \$USE_ENTITY_NAMES,
317        'local_icons!',         \$LOCAL_ICONS,
318        'scalable_fonts!',      \$SCALABLE_FONTS,
319        'images_only!',         \$IMAGES_ONLY,
320        'show_section_numbers!',\$SHOW_SECTION_NUMBERS,
321        'show_init!',           \$SHOW_INIT_FILE,
322        'init_file=s',          \$INIT_FILE,
323        'up_url=s',             \$EXTERNAL_UP_LINK,
324        'up_title=s',           \$EXTERNAL_UP_TITLE,
325        'down_url=s',           \$EXTERNAL_DOWN_LINK,
326        'down_title=s',         \$EXTERNAL_DOWN_TITLE,
327        'prev_url=s',           \$EXTERNAL_PREV_LINK,
328        'prev_title=s',         \$EXTERNAL_PREV_TITLE,
329        'index=s',              \$EXTERNAL_INDEX,
330        'biblio=s',             \$EXTERNAL_BIBLIO,
331        'contents=s',           \$EXTERNAL_CONTENTS,
332        'external_file=s',      \$EXTERNAL_FILE,
333        'short_index!',         \$SHORT_INDEX,
334        'unsegment!',           \$UNSEGMENT,
335        'debug!',               \$DEBUG,
336        'tmp=s',                \$TMP,
337        'ldump!',               \$LATEX_DUMP,
338        'timing!',              \$TIMING,
339        'verbosity=i',          \$VERBOSITY,
340        'html_version=s',       \$HTML_VERSION,
341        'strict!',              \$STRICT_HTML,
342        'xbit!',                \$XBIT_HACK,
343        'ssi!',                 \$ALLOW_SSI,
344        'php!',                 \$ALLOW_PHP,
345        'test_mode!' # undocumented switch
346       )) {
347    &usage();
348    exit 1;
349}
350
351# interpret options, check option consistency
352if(defined $opt{'split'}) {
353    if ($opt{'split'} =~ /^(\+?)(\d+)$/) {
354        $MAX_SPLIT_DEPTH = $2;
355        if ($1) { $MAX_SPLIT_DEPTH *= -1; $REL_DEPTH = 1; }
356    } else {
357        &usage;
358        die "Error: Unrecognised value for -split: $opt{'split'}\n";
359    }
360}
361if(defined $opt{'link'}) {
362    if ($opt{'link'} =~ /^(\+?)(\d+)$/) {
363        $MAX_LINK_DEPTH = $2;
364        if ($1) { $MAX_LINK_DEPTH *= -1 }
365    } else {
366        &usage;
367        die "Error: Unrecognised value for -link: $opt{'link'}\n";
368    }
369}
370unless ($ISO_LANGUAGE =~ /^[A-Z.]+$/) {
371    die "Error: Language (-iso_language) must be uppercase and dots only: $ISO_LANGUAGE\n";
372}
373if ($HTML_VALIDATE && !$HTML_VALIDATOR) {
374    die "Error: Need a HTML_VALIDATOR when -validate is specified.\n";
375}
376&set_if_false($NOLATEX,$opt{latex}); # negate the option...
377if ($ASCII_MODE || $PS_IMAGES) {
378    $EXTERNAL_IMAGES = 1;
379}
380if ($FONT_SIZE && $FONT_SIZE !~ /^\d+pt$/) {
381    die "Error: Font size (-font_size) must end with 'pt': $FONT_SIZE\n"
382}
383&set_if_false($NO_NAVIGATION,$opt{navigation});
384&set_if_false($NO_FOOTNODE,$opt{footnode});
385if (defined $TITLE && !length($TITLE)) {
386    die "Error: Empty title (-title).\n";
387}
388if ($opt{rootdir}) {
389    $ROOTED = 1;
390    $FIXEDDIR = $opt{rootdir};
391}
392if ($FIXEDDIR && !-d $FIXEDDIR) {
393    if ($MKDIR) {
394	print "\n *** creating directory: $FIXEDDIR ";
395	die "Failed: $!\n" unless (mkdir($FIXEDDIR, 0755));
396        # _TODO_ use File::Path to create a series of directories
397    } else {
398	&usage;
399	die "Error: Specified directory (-rootdir, -dir) does not exist.\n";
400    }
401}
402&set_if_false($NO_SUBDIR, $opt{subdir});
403&set_if_false($NO_AUTO_LINK, $opt{auto_link});
404if ($opt{noreuse}) {
405    $REUSE = 0;
406}
407unless(grep(/^\Q$IMAGE_TYPE\E$/o, @IMAGE_TYPES)) {
408    die <<"EOF";
409Error: No such image type '$IMAGE_TYPE'.
410       This installation supports (first is default): @IMAGE_TYPES
411EOF
412}
413&set_if_false($NO_IMAGES, $opt{images});
414if ($opt{noaccent_images}) {
415    $ACCENT_IMAGES = '';
416}
417if($opt{noaddress}) {
418    $ADDRESS = '';
419}
420if($opt{noinfo}) {
421    $INFO = 0;
422}
423if($ACCENT_IMAGES && $ACCENT_IMAGES !~ /^[a-zA-Z,]+$/) {
424    die "Error: Single word or comma-list of style words needed for -accent_images, not: $_\n";
425}
426&set_if_false($NO_PARBOX_IMAGES, $opt{parbox_images});
427&set_if_false($NO_SIMPLE_MATH, $opt{math});
428if (defined $opt{math_parsing}) {
429    $NO_MATH_PARSING = !$opt{math_parsing};
430    $NO_SIMPLE_MATH = !$opt{math_parsing} unless(defined $opt{math});
431}
432&set_if_false($NO_ISOLATIN, $opt{latin});
433if ($INIT_FILE) {
434    if (-f $INIT_FILE && -r _) {
435        print "Note: Initialising with file: $INIT_FILE\n"
436            if ($DEBUG || $VERBOSITY);
437        require($INIT_FILE);
438    } else {
439        die "Error: Could not find file (-init_file): $INIT_FILE\n";
440    }
441}
442foreach($EXTERNAL_UP_LINK, $EXTERNAL_DOWN_LINK, $EXTERNAL_PREV_LINK,
443        $EXTERNAL_INDEX, $EXTERNAL_BIBLIO, $EXTERNAL_CONTENTS) {
444    $_ ||= ''; # initialize
445    s/~/&#126;/g; # protect `~'
446}
447if($TMP && !(-d $TMP && -w _)) {
448    die "Error: '$TMP' not usable as temporary directory.\n";
449}
450if ($opt{help}) {
451    L2hos->perldoc($SCRIPT);
452    exit 0;
453}
454if ($opt{version}) {
455    &banner();
456    exit 0;
457}
458if ($opt{test_mode}) {
459    $TITLE = 'LaTeX2HTML Test Document';
460    $TEXEXPAND = "$PERL @srcdir@${dd}texexpand@scriptext@";
461    $PSTOIMG   = "$PERL @srcdir@${dd}pstoimg@scriptext@";
462    $ICONSERVER = L2hos->path2URL("@srcdir@${dd}icons");
463    $TEST_MODE  = 1;
464    $RGBCOLORFILE = "@srcdir@${dd}styles${dd}rgb.txt";
465    $CRAYOLAFILE = "@srcdir@${dd}styles${dd}crayola.txt";
466}
467if($DEBUG) {
468    # make the OS-dependent functions more chatty, too
469    $L2hos::Verbose = 1;
470}
471
472undef %opt; # not needed any more
473
474#unless @have_images@
475print "Warning: This system does not support generation of images\n"
476  unless($NO_IMAGES);
477$NO_IMAGES = 1;
478#fi
479
480$FIXEDDIR = $FIXEDDIR || $DESTDIR || '';  # for backward compatibility
481
482if ($EXTERNAL_UP_TITLE xor $EXTERNAL_UP_LINK) {
483    warn "Warning (-up_url, -up_title): Need to specify both a parent URL and a parent title!\n";
484    $EXTERNAL_UP_TITLE = $EXTERNAL_UP_LINK = "";
485}
486
487if ($EXTERNAL_DOWN_TITLE xor $EXTERNAL_DOWN_LINK) {
488    warn "Warning (-down_url, -down_title): Need to specify both a parent URL and a parent title!\n";
489    $EXTERNAL_DOWN_TITLE = $EXTERNAL_DOWN_LINK = "";
490}
491
492# $NO_NAVIGATION = 1 unless $MAX_SPLIT_DEPTH;	#  Martin Wilck
493
494if ($MAX_SPLIT_DEPTH && $MAX_SPLIT_DEPTH < 0) {
495    $MAX_SPLIT_DEPTH *= -1; $REL_DEPTH = 1;
496}
497if ($MAX_LINK_DEPTH && $MAX_LINK_DEPTH < 0) {
498    $MAX_LINK_DEPTH *= -1; $LEAF_LINKS = 1;
499}
500
501$FOOT_FILENAME = 'footnode' unless ($FOOT_FILENAME);
502$NO_FOOTNODE = 1 unless ($MAX_SPLIT_DEPTH || $NO_FOOTNODE);
503$NO_SPLIT = 1 unless $MAX_SPLIT_DEPTH; # _MRO_TODO_: is this needed at all?
504$SEGMENT = $SEGMENTED = 0;
505$NO_MATH_MARKUP = 1;
506
507# specify the filename extension to use with the generated HTML files
508if ($SHORTEXTN) { $EXTN = ".htm"; }	# for HTML files on CDROM
509elsif ($ALLOW_PHP) { $EXTN = ".php"; }  # has PHP dynamic includes
510	# with server-side includes (SSI) :
511elsif ($ALLOW_SSI && !$XBIT_HACK) { $EXTN = ".shtml"; }
512	# ordinary names, valid also for SSI with XBit hack :
513else { $EXTN = ".html"; }
514
515$NODE_NAME = 'node' unless (defined $NODE_NAME);
516
517# space for temporary files
518# different to the $TMPDIR for image-generation
519# MRO: No directory should end with $dd!
520$TMP_ = "TMP";
521
522$TMP_PREFIX = "l2h" unless ($TMP_PREFIX);
523
524# This can be set to 1 when using a version of dvips that is safe
525# from the "dot-in-name" bug.
526# _TODO_ this should be determined by configure
527$DVIPS_SAFE = 1;
528
529$CHARSET = $charset || 'iso-8859-1';
530
531####################################################################
532#
533# If possible, use icons of the same type as generated images
534#
535if ($IMAGE_TYPE && %{"icons_$IMAGE_TYPE"}) {
536    %icons = %{"icons_$IMAGE_TYPE"};
537}
538
539####################################################################
540#
541# Figure out what options we need to pass to DVIPS and store that in
542# the $DVIPSOPT variable.  Also, scaling is taken care of at the
543# dvips level if PK_GENERATION is set to 1, so adjust SCALE_FACTORs
544# accordingly.
545#
546if ($SCALABLE_FONTS) {
547    $PK_GENERATION = 0;
548    $DVIPS_MODE = '';
549}
550
551if ($PK_GENERATION) {
552    if ($MATH_SCALE_FACTOR <= 0) { $MATH_SCALE_FACTOR = 2; }
553    if ($FIGURE_SCALE_FACTOR <= 0) { $FIGURE_SCALE_FACTOR = 2; }
554    my $saveMSF = $MATH_SCALE_FACTOR;
555    my $saveFSF = $FIGURE_SCALE_FACTOR;
556    my $desired_dpi = int($MATH_SCALE_FACTOR*75);
557    $FIGURE_SCALE_FACTOR = ($METAFONT_DPI / 72) *
558	($FIGURE_SCALE_FACTOR / $MATH_SCALE_FACTOR) ;
559    $MATH_SCALE_FACTOR = $METAFONT_DPI / 72;
560    $dvi_mag = int(1000 * $desired_dpi / $METAFONT_DPI);
561    if ($dvi_mag > 1000) {
562	&write_warnings(
563	    "WARNING: Your SCALE FACTOR is too large for PK_GENERATION.\n" .
564	    "         See $CONFIG_FILE for more information.\n");
565    }
566
567    # RRM: over-sized scaling, using dvi-magnification
568    if ($EXTRA_IMAGE_SCALE) {
569	print "\n *** Images at $EXTRA_IMAGE_SCALE times resolution of displayed size ***\n";
570	$desired_dpi = int($EXTRA_IMAGE_SCALE * $desired_dpi+.5);
571	print "    desired_dpi = $desired_dpi  METAFONT_DPI = $METAFONT_DPI\n"
572            if $DEBUG;
573	$dvi_mag = int(1000 * $desired_dpi / $METAFONT_DPI);
574	$MATH_SCALE_FACTOR = $saveMSF;
575	$FIGURE_SCALE_FACTOR = $saveFSF;
576    }
577    # no space after "-y", "-D", "-e" --- required by DVIPS under DOS !
578    my $mode_switch = "-mode $DVIPS_MODE" if $DVIPS_MODE;
579    $DVIPSOPT .= " -y$dvi_mag -D$METAFONT_DPI $mode_switch -e5 ";
580} else { # no PK_GENERATION
581#    if ($EXTRA_IMAGE_SCALE) {
582#	&write_warnings(
583#	   "the \$EXTRA_IMAGE_SCALE feature requires either \$PK_GENERATION=1"
584#			. " or the '-scalable_fonts' option");
585#	$EXTRA_IMAGE_SCALE = '';
586#    }
587    # MRO: shifted to l2hconf
588    #$DVIPSOPT .= ' -M';
589} # end PK_GENERATION
590
591# The mapping from numbers to accents.
592# These are required to process the \accent command, which is found in
593# tables of contents whenever there is an accented character in a
594# caption or section title.  Processing the \accent command makes
595# $encoded_*_number work properly (see &extract_captions) with
596# captions that contain accented characters.
597# I got the numbers from the plain.tex file, version 3.141.
598
599# Missing entries should be looked up by a native speaker.
600# Have a look at generate_accent_commands and $iso_8859_1_character_map.
601
602# MEH: added more accent types
603# MRO: only uppercase needed!
604%accent_type = (
605   '18' => 'grave',		# \`
606   '19' => 'acute',		# `'
607   '20' => 'caron',		# \v
608   '21' => 'breve',		# \u
609   '22' => 'macr',		# \=
610   '23' => 'ring',		#
611   '24' => 'cedil',		# \c
612   '94' => 'circ',		# \^
613   '95' => 'dot',		# \.
614   '7D' => 'dblac',		# \H
615   '7E' => 'tilde',		# \~
616   '7F' => 'uml',		# \"
617);
618
619&driver;
620
621exit 0; # clean exit, no errors
622
623############################ Subroutines ##################################
624
625#check that $TMP is writable, if so create a subdirectory
626sub make_tmp_dir {
627    &close_dbm_database if $DJGPP; # to save file-handles
628
629#if @texlive@
630    $TMP = "$DESTDIR$dd$TMP_";
631    unless(-d $TMP) {
632        mkdir($TMP,0755);
633    }
634#fi
635    # determine a suitable temporary path
636    #
637    $TMPDIR = '';
638    my @tmp_try = ();
639    push(@tmp_try, $TMP) if($TMP);
640    push(@tmp_try, "$DESTDIR$dd$TMP_") if($TMP_);
641    push(@tmp_try, $DESTDIR) if($DESTDIR);
642    push(@tmp_try, L2hos->Cwd());
643
644    my $try;
645    TempTry: foreach $try (@tmp_try) {
646      next unless(-d $try && -w _);
647      my $tmp = "$try$dd$TMP_PREFIX$$";
648      if(mkdir($tmp,0755)) {
649        $TMPDIR=$tmp;
650	last TempTry;
651      } else {
652        warn "Warning: Cannot create temporary directory '$tmp': $!\n";
653      }
654    }
655
656    $dvips_warning = <<"EOF";
657
658Warning: There is a '.' in \$TMPDIR, $DVIPS will probably fail.
659Set \$TMP to use a /tmp directory, or rename the working directory.
660EOF
661    die ($dvips_warning . "\n\$TMPDIR=$TMPDIR  ***\n\n")
662	if ($TMPDIR =~ /\./ && $DVIPS =~ /dvips/ && !$DVIPS_SAFE);
663
664    &open_dbm_database if $DJGPP;
665}
666
667# MRO: set first parameter to the opposite of the second if second parameter is defined
668sub set_if_false {
669    $_[0] = !$_[1] if(defined $_[1]);
670}
671
672sub check_for_dots {
673    local($file) = @_;
674    if ($file =~ /\.[^.]*\./ && !$DVIPS_SAFE) {
675	die "\n\n\n *** Fatal Error --- but easy to fix ***\n"
676	    . "\nCannot have '.' in file-name prefix, else dvips fails on images"
677	    . "\nChange the name from  $file  and try again.\n\n";
678    }
679}
680
681# Process each file ...
682sub driver {
683    local($FILE, $orig_cwd, %unknown_commands, %dependent, %depends_on
684	  , %styleID, %env_style, $bbl_cnt, $dbg, %numbered_section);
685    # MRO: $texfilepath has to be global!
686    local(%styles_loaded);
687    $orig_cwd = L2hos->Cwd();
688
689    print "\n *** initialise *** " if ($VERBOSITY > 1);
690    &initialise;		# Initialise some global variables
691
692    print "\n *** check modes *** " if ($VERBOSITY > 1);
693    &ascii_mode if $ASCII_MODE;	# Must come after initialization
694    &titles_language($TITLES_LANGUAGE);
695    &make_numbered_footnotes if ($NUMBERED_FOOTNOTES);
696    $dbg = $DEBUG ? "-debug" : "";
697    $dbg .= (($VERBOSITY>2) ? " -verbose" : "");
698
699    #use the same hashes for all files in a batch
700    local(%cached_env_img, %id_map, %symbolic_labels, %latex_labels)
701	if ($FIXEDDIR && $NO_SUBDIR);
702
703    local($MULTIPLE_FILES,$THIS_FILE);
704    $MULTIPLE_FILES = 1+$#ARGV if $ROOTED;
705    print "\n *** $MULTIPLE_FILES file".($MULTIPLE_FILES ? 's: ' : ': ')
706    	. join(',',@ARGV) . " *** " if ($VERBOSITY > 1);
707
708    local(%section_info, %toc_section_info, %cite_info, %ref_files);
709
710    foreach $FILE (@ARGV) {
711	&check_for_dots($FILE) unless $DVIPS_SAFE;
712	++$THIS_FILE if $MULTIPLE_FILES;
713	do {
714	    %section_info = ();
715	    %toc_section_info = ();
716	    %cite_info = ();
717	    %ref_files = ();
718	} unless $MULTIPLE_FILES;
719	local($bbl_nr) = 1;
720
721	# The number of reused images and those in images.tex
722	local($global_page_num) = (0) unless($FIXEDDIR && $NO_SUBDIR);
723	# The number of images in images.tex
724	local($new_page_num) = (0); # unless($FIXEDDIR && $NO_SUBDIR);
725	local($pid, $sections_rx,
726	    , $outermost_level, %latex_body, $latex_body
727	    , %encoded_section_number
728	    , %verbatim, %new_command, %new_environment
729	    , %provide_command, %renew_command, %new_theorem
730	    , $preamble, $aux_preamble, $prelatex, @preamble);
731
732	# must retain these when all files are in the same directory
733	# else the images.pl and labels.pl files get clobbered
734	unless ($FIXEDDIR && $NO_SUBDIR) {
735	    print "\nResetting image-cache" if ($#ARGV);
736	    local(%cached_env_img, %id_map, %symbolic_labels, %latex_labels)
737	}
738
739## AYS: Allow extension other than .tex and make it optional
740	($EXT = $FILE) =~ s/.*\.([^\.]*)$/$1/;
741	if ( $EXT eq $FILE ) {
742	    $EXT = "tex";
743	    $FILE =~ s/$/.tex/;
744	}
745
746	#RRM: allow user-customisation, dependent on file-name
747	# e.g. add directories to $TEXINPUTS named for the file
748	# --- idea due to Fred Drake <fdrake@acm.org>
749	&custom_driver_hook($FILE) if (defined &custom_driver_hook);
750
751# JCL(jcl-dir)
752# We need absolute paths for TEXINPUTS here, because
753# we change the directory
754	if ($orig_cwd eq $texfilepath) {
755	    &deal_with_texinputs($orig_cwd);
756	} else {
757	    &deal_with_texinputs($orig_cwd, $texfilepath);
758	}
759
760	($texfilepath, $FILE) = &get_full_path($FILE);
761	$texfilepath = '.' unless($texfilepath);
762
763	die "Cannot read $texfilepath$dd$FILE \n"
764	    unless (-f "$texfilepath$dd$FILE");
765
766
767# Tell texexpand which files we *don't* want to look at.
768	$ENV{'TEXE_DONT_INCLUDE'} = $DONT_INCLUDE if $DONT_INCLUDE;
769# Tell texexpand which files we *do* want to look at, e.g.
770# home-brew style files
771	$ENV{'TEXE_DO_INCLUDE'} = $DO_INCLUDE if $DO_INCLUDE;
772
773	$FILE =~ s/\.[^\.]*$//; ## AYS
774	$DESTDIR = ''; # start at empty
775	if ($FIXEDDIR) {
776	    $DESTDIR = $FIXEDDIR unless ($FIXEDDIR eq '.');
777	    if (($ROOTED)&&!($texfilepath eq $orig_cwd)) {
778		$DESTDIR .= $dd . $FILE unless $NO_SUBDIR;
779	    };
780	} elsif ($texfilepath eq $orig_cwd) {
781	    $DESTDIR = ($NO_SUBDIR ? '.' : $FILE);
782	} else {
783	    $DESTDIR = $ROOTED ? '.' : $texfilepath;
784	    $DESTDIR .= $dd . $FILE unless $NO_SUBDIR;
785	}
786	$PREFIX  = "$FILE-" if $AUTO_PREFIX;
787
788	print "\nOPENING $texfilepath$dd$FILE.$EXT \n"; ## AYS
789
790	next unless (&new_dir($DESTDIR,''));
791        # establish absolute path to $DESTDIR
792	$DESTDIR = L2hos->Make_directory_absolute($DESTDIR);
793        &make_tmp_dir;
794        print "\nNote: Working directory is $DESTDIR\n";
795        print "Note: Images will be generated in $TMPDIR\n\n";
796
797# Need to clean up a bit in case there's garbage left
798# from former runs.
799	if ($DESTDIR) { chdir($DESTDIR) || die "$!\n"; }
800	if (opendir (TMP,$TMP_)) {
801	    foreach (readdir TMP) {
802		L2hos->Unlink("TMP_$dd$_") unless (/^\.\.?$/);
803	    }
804	    closedir TMP;
805	}
806	&cleanup(1);
807	unless(-d $TMP_) {
808	    mkdir($TMP_, 0755) ||
809	      die "Cannot create directory '$TMP_': $!\n";
810	}
811	chdir($orig_cwd);
812
813# RRM 14/5/98  moved this to occur earlier
814## JCL(jcl-dir)
815## We need absolute paths for TEXINPUTS here, because
816## we change the directory
817#	if ($orig_cwd eq $texfilepath) {
818#	    &deal_with_texinputs($orig_cwd);
819#	} else {
820#	    &deal_with_texinputs($orig_cwd, $texfilepath);
821#	}
822
823
824# This needs $DESTDIR to have been created ...
825	print " *** calling  `texexpand' ***" if ($VERBOSITY > 1);
826	local($unseg) = ($UNSEGMENT ? "-unsegment " : "");
827
828# does DOS need to check these here ?
829#	die "File $TEXEXPAND does not exist or is not executable\n"
830#	    unless (-x $TEXEXPAND);
831	L2hos->syswait("$TEXEXPAND $dbg -auto_exclude $unseg"
832		 . "-save_styles \"$DESTDIR$dd$TMP_${dd}styles\" "
833		 . ($TEXINPUTS ? "-texinputs \"$TEXINPUTS\" " : '' )
834		 . (($VERBOSITY >2) ? "-verbose " : '' )
835		 . "-out \"$DESTDIR$dd$TMP_$dd$FILE\" "
836		 . "\"$texfilepath$dd$FILE.$EXT\"")
837	    && die " texexpand  failed: $!\n";
838	print STDOUT "\n ***  `texexpand' done ***\n" if ($VERBOSITY > 1);
839
840	chdir($DESTDIR) if $DESTDIR;
841	$SIG{'INT'} = 'handler';
842
843	&open_dbm_database;
844	&initialise_sections;
845	print STDOUT "\n ***  database open ***\n" if ($VERBOSITY > 1);
846
847	if ($IMAGES_ONLY) {
848	    &make_off_line_images;
849	} else {
850	    &rename_image_files;
851	    &load_style_file_translations;
852	    &make_language_rx;
853	    &make_raw_arg_cmd_rx;
854#	    &make_isolatin1_rx unless ($NO_ISOLATIN);
855	    &translate_titles;
856	    &make_sections_rx;
857	    print "\nReading ...";
858	    if ($SHORT_FILENAME) {
859		L2hos->Rename ("$TMP_$dd$FILE" ,"$TMP_$dd$SHORT_FILENAME" );
860		&slurp_input_and_partition_and_pre_process(
861		      "$TMP_$dd$SHORT_FILENAME");
862	    } else {
863		&slurp_input_and_partition_and_pre_process("$TMP_$dd$FILE");
864	    }
865	    &add_preamble_head;
866	    # Create a regular expressions
867	    &set_depth_levels;
868	    &make_sections_rx;
869	    &make_order_sensitive_rx;
870	    &add_document_info_page if ($INFO && !(/\\htmlinfo/));
871	    &add_bbl_and_idx_dummy_commands;
872	    &translate;	# Destructive!
873	}
874	&style_sheet;
875	&close_dbm_database;
876	&cleanup();
877
878#JCL: read warnings from file to $warnings
879	local($warnings) = &get_warnings;
880	print "\n\n*********** WARNINGS ***********  \n$warnings"
881	    if ($warnings || $NO_IMAGES || $IMAGES_ONLY);
882	&image_cache_message if ($NO_IMAGES || $IMAGES_ONLY);
883	&image_message if ($warnings =~ /Failed to convert/io);
884	undef $warnings;
885
886# JCL - generate directory index entry.
887# Yet, a hard link, cause Perl lacks symlink() on some systems.
888	do {
889	    local($EXTN) = $EXTN;
890	    $EXTN =~ s/_\w+(\.html?)/$1/ if ($frame_main_name);
891	    local($from,$to) = (eval($LINKPOINT),eval($LINKNAME));
892	    if (length($from) && length($to) && ($from ne $to)) {
893		#frames may have altered $EXTN
894		$from =~ s/$frame_main_name(\.html?)/$1/ if ($frame_main_name);
895		$to =~ s/$frame_main_name(\.html?)/$1/ if ($frame_main_name);
896		L2hos->Unlink($to);
897		L2hos->Link($from,$to);
898	    }
899	} unless ($NO_AUTO_LINK || !($LINKPOINT) || !($LINKNAME));
900
901	&html_validate if ($HTML_VALIDATE && $HTML_VALIDATOR);
902
903# Go back to the source directory
904	chdir($orig_cwd);
905        $TEST_MODE = $DESTDIR if($TEST_MODE); # save path
906	$DESTDIR = '';
907	$OUT_NODE = 0 unless $FIXEDDIR;
908	$STYLESHEET = '' if ($STYLESHEET =~ /^\Q$FILE./);
909    }
910    print "\nUnknown commands: ". join(" ",keys %unknown_commands)
911	if %unknown_commands;
912###MEH -- math support
913    print "\nMath commands outside math: " .
914	join(" ",keys %commands_outside_math) .
915	    "\n  Output may look weird or may be faulty!\n"
916		if %commands_outside_math;
917    print "\nDone.\n";
918    if($TEST_MODE) {
919      $TEST_MODE =~ s:[$dd$dd]+$::;
920      print "\nTo view the results, point your browser at:\n",
921        L2hos->path2URL(L2hos->Make_directory_absolute($TEST_MODE).$dd.
922        "index$EXTN"),"\n";
923    }
924    $end_time = time;
925    $total_time = $end_time - $start_time;
926    print STDOUT join(' ',"Timing:",$total_time,"seconds\n")
927	if ($TIMING||$DEBUG||($VERBOSITY > 2));
928    $_;
929}
930
931sub open_dbm_database {
932    # These are DBM (unix DataBase Management) arrays which are actually
933    # stored in external files. They are used for communication between
934    # the main process and forked child processes;
935    print STDOUT "\n"; # this mysteriously prevents a core dump !
936
937    dbmopen(%verb, "$TMP_${dd}verb",0755);
938#    dbmopen(%verbatim, "$TMP_${dd}verbatim",0755);
939    dbmopen(%verb_delim, "$TMP_${dd}verb_delim",0755);
940    dbmopen(%expanded,"$TMP_${dd}expanded",0755);
941# Holds max_id, verb_counter, verbatim_counter, eqn_number
942    dbmopen(%global, "$TMP_${dd}global",0755);
943# Hold style sheet information
944    dbmopen(%env_style, "$TMP_${dd}envstyles",0755);
945    dbmopen(%txt_style, "$TMP_${dd}txtstyles",0755);
946    dbmopen(%styleID, "$TMP_${dd}styleIDs",0755);
947
948# These next two are used during off-line image conversion
949# %new_id_map maps image id's to page_numbers of the images in images.tex
950# %image_params maps image_ids to conversion parameters for that image
951    dbmopen(%new_id_map, "$TMP_${dd}ID_MAP",0755);
952    dbmopen(%img_params, "$TMP_${dd}IMG_PARAMS",0755);
953    dbmopen(%orig_name_map, "$TMP_${dd}ORIG_MAP",0755);
954
955    $global{'max_id'} = ($global{'max_id'} | 0);
956    &read_mydb(\%verbatim, "verbatim");
957    $global{'verb_counter'} = ($global{'verb_counter'} | 0);
958    $global{'verbatim_counter'} = ($global{'verbatim_counter'} | 0);
959
960    &read_mydb(\%new_command, "new_command");
961    &read_mydb(\%renew_command, "renew_command");
962    &read_mydb(\%provide_command, "provide_command");
963    &read_mydb(\%new_theorem, "new_theorem");
964    &read_mydb(\%new_environment, "new_environment");
965    &read_mydb(\%dependent, "dependent");
966#    &read_mydb(\%env_style, "env_style");
967#    &read_mydb(\%styleID, "styleID");
968    # MRO: Why should we use read_mydb instead of catfile?
969    $preamble = &catfile(&_dbname("preamble"),1) || '';
970    $prelatex = &catfile(&_dbname("prelatex"),1) || '';
971    $aux_preamble = &catfile(&_dbname("aux_preamble"),1) || '';
972    &restore_critical_variables;
973}
974
975sub close_dbm_database {
976    &save_critical_variables;
977    dbmclose(%verb); undef %verb;
978#    dbmclose(%verbatim); undef %verbatim;
979    dbmclose(%verb_delim); undef %verb_delim;
980    dbmclose(%expanded); undef %expanded;
981    dbmclose(%global); undef %global;
982    dbmclose(%env_style); undef %env_style;
983    dbmclose(%style_id); undef %style_id;
984    dbmclose(%new_id_map); undef %new_id_map;
985    dbmclose(%img_params); undef %img_params;
986    dbmclose(%orig_name_map); undef %orig_name_map;
987    dbmclose(%txt_style); undef %txt_style;
988    dbmclose(%styleID); undef %styleID;
989}
990
991sub clear_images_dbm_database {
992    # <Added calls to dbmclose dprhws>
993    # %new_id_map will be used by the off-line image conversion process
994    #
995    dbmclose(%new_id_map);
996    dbmclose(%img_params);
997    dbmclose(%orig_name_map);
998    undef %new_id_map;
999    undef %img_params;
1000    undef %orig_name_map;
1001    dbmopen(%new_id_map, "$TMP_${dd}ID_MAP",0755);
1002    dbmopen(%img_params, "$TMP_${dd}IMG_PARAMS",0755);
1003    dbmopen(%orig_name_map, "$TMP_${dd}ORIG_MAP",0755);
1004}
1005
1006sub initialise_sections {
1007    local($key);
1008    foreach $key (keys %numbered_section) {
1009	$global{$key} = $numbered_section{$key}}
1010}
1011
1012sub save_critical_variables {
1013    $global{'math_markup'} = $NO_MATH_MARKUP;
1014    $global{'charset'} = $CHARSET;
1015    $global{'charenc'} = $charset;
1016    $global{'language'} = $default_language;
1017    $global{'isolatin'} = $ISOLATIN_CHARS;
1018    $global{'unicode'} = $UNICODE_CHARS;
1019    if ($UNFINISHED_ENV) {
1020	$global{'unfinished_env'} = $UNFINISHED_ENV;
1021	$global{'replace_end_env'} = $REPLACE_END_ENV;
1022    }
1023    $global{'unfinished_comment'} = $UNFINISHED_COMMENT;
1024    if (@UNMATCHED_OPENING) {
1025	$global{'unmatched'} = join(',',@UNMATCHED_OPENING);
1026    }
1027}
1028
1029sub restore_critical_variables {
1030    $NO_MATH_MARKUP = ($global{'math_markup'}|
1031	(defined $NO_MATH_MARKUP ? $NO_MATH_MARKUP:1));
1032    $CHARSET = ($global{'charset'}| $CHARSET);
1033    $charset = ($global{'charenc'}| $charset);
1034    $default_language = ($global{'language'}|
1035	(defined $default_language ? $default_language:'english'));
1036    $ISOLATIN_CHARS = ($global{'isolatin'}|
1037	(defined $ISOLATIN_CHARS ? $ISOLATIN_CHARS:0));
1038    $UNICODE_CHARS = ($global{'unicode'}|
1039	(defined $UNICODE_CHARS ? $UNICODE_CHARS:0));
1040    if ($global{'unfinished_env'}) {
1041	$UNFINISHED_ENV = $global{'unfinished_env'};
1042	$REPLACE_END_ENV = $global{'replace_end_env'};
1043    }
1044    $UNFINISHED_COMMENT = $global{'unfinished_comment'};
1045    if ($global{'unmatched'}) {
1046	@UNMATCHED_OPENING = split(',',$global{'unmatched'});
1047    }
1048
1049    # undef any renewed-commands...
1050    # so the new defs are read from %new_command
1051    local($cmd,$key,$code);
1052    foreach $key (keys %renew_command) {
1053	$cmd = "do_cmd_$key";
1054	$code = "undef \&$cmd"; eval($code) if (defined &$cmd);
1055	if ($@) { print "\nundef \&do_cmd_$cmd failed"}
1056    }
1057}
1058
1059#JCL: The warnings should have been handled within the DBM database.
1060# Unfortunately if the contents of an array are more than ~900 (system
1061# dependent) chars long then dbm cannot handle it and gives error messages.
1062sub write_warnings { #clean
1063    my ($str) = @_;
1064    $str .= "\n" unless($str =~ /\n$/);
1065    print STDOUT "\n *** Warning: $str" if ($VERBOSITY > 1);
1066    my $warnings = '';
1067    if(-f 'WARNINGS') {
1068        $warnings = &catfile('WARNINGS') || '';
1069    }
1070    return () if ($warnings =~ /\Q$str\E/);
1071    if(open(OUT,">>WARNINGS")) {
1072        print OUT $str;
1073        close OUT;
1074    } else {
1075        print "\nError: Cannot append to 'WARNINGS': $!\n";
1076    }
1077}
1078
1079sub get_warnings {
1080    return &catfile('WARNINGS',1) || '';
1081}
1082
1083# MRO: Standardizing
1084sub catfile {
1085    my ($file,$ignore) = @_;
1086    unless(open(CATFILE,"<$file")) {
1087        print "\nError: Cannot read '$file': $!\n"
1088            unless($ignore);
1089        return undef;
1090    }
1091    local($/) = undef; # slurp in whole file
1092    my $contents = <CATFILE>;
1093    close(CATFILE);
1094    $contents;
1095}
1096
1097
1098sub html_validate {
1099    my ($extn) = $EXTN;
1100    if ($EXTN !~ /^\.html?$/i) {
1101	$extn =~ s/^[^\.]*(\.html?)$/$1/;
1102    }
1103    print "\n *** Validating ***\n";
1104    my @htmls = glob("*$extn");
1105    my $file;
1106    foreach $file (@htmls) {
1107      system("$HTML_VALIDATOR $file");
1108    }
1109}
1110
1111sub lost_argument {
1112    local($cmd) = @_;
1113    &write_warnings("\nincomplete argument to command: \\$cmd");
1114}
1115
1116#-----------------------------------------------------------------------------
1117
1118# These subroutines should have been handled within the DBM database.
1119# Unfortunately if the contents of an array are more than ~900 (system
1120# dependent) chars long then dbm cannot handle it and gives error messages.
1121# So here we save and then read the contents explicitly.
1122sub write_mydb {
1123    my ($db, $key, $str) = @_;
1124    &write_mydb_simple($db, "\n$mydb_mark#$key#$str");
1125}
1126
1127# generate the DB file name from the DB name
1128sub _dbname {
1129    "$TMP_$dd$_[0]";
1130}
1131
1132sub write_mydb_simple {
1133    my ($db, $str) = @_;
1134    my $file = &_dbname($db);
1135    if(open(DB,">>$file")) {
1136        print DB $str;
1137        close DB;
1138    } else {
1139        print "\nError: Cannot append to '$file': $!\n";
1140    }
1141}
1142
1143sub clear_mydb {
1144    my ($db) = @_;
1145    my $file = &_dbname($db);
1146    if(open(DB,">$file")) {
1147        close DB;
1148    } else {
1149        print "\nError: Cannot clear '$file': $!\n";
1150    }
1151}
1152
1153# Assumes the existence of a DB file which contains
1154# sequences of e.g. verbatim counters and verbatim contents.
1155sub read_mydb {
1156    my ($dbref,$name) = @_;
1157    my $contents = &catfile(&_dbname($name),1);
1158    return '' unless(defined $contents);
1159    my @tmp = split(/\n$mydb_mark#([^#]*)#/, $contents);
1160    my $i = 1;	# Ignore the first element at 0
1161    print "\nDBM: $name open..." if ($VERBOSITY > 2);
1162    while ($i < scalar(@tmp)) {
1163	my $tmp1 = $tmp[$i];
1164        my $tmp2 = $tmp[++$i];
1165	$$dbref{$tmp1} = defined $tmp2 ? $tmp2 : '';
1166	++$i;
1167    };
1168    $contents;
1169}
1170
1171#-----------------------------------------------------------------------------
1172
1173# Reads in a latex generated file (e.g. .bbl or .aux)
1174# It returns success or failure
1175# ****** and binds $_ in the caller as a side-effect ******
1176sub process_ext_file {
1177    local($ext) = @_;
1178    local($found, $extfile,$dum,$texpath);
1179    $extfile =  $EXTERNAL_FILE||$FILE;
1180    local($file) = &fulltexpath("$extfile.$ext");
1181    $found = 0;
1182    &write_warnings(
1183	    "\n$extfile.$EXT is newer than $extfile.$ext: Please rerun latex" . ## AYS
1184	    (($ext =~ /bbl/) ? " and bibtex.\n" : ".\n"))
1185	if ( ($found = (-f $file)) &&
1186	    &newer(&fulltexpath("$extfile.$EXT"), $file)); ## AYS
1187    if ((!$found)&&($extfile =~ /\.$EXT$/)) {
1188	$file = &fulltexpath("$extfile");
1189	&write_warnings(
1190	    "\n$extfile is newer than $extfile: Please rerun latex" . ## AYS
1191	    (($ext =~ /bbl/) ? " and bibtex.\n" : ".\n"))
1192	    if ( ($found = (-f $file)) &&
1193		&newer(&fulltexpath("$extfile"), $file)); ## AYS
1194    }
1195
1196    # check in other directories on the $TEXINPUTS paths
1197    if (!$found) {
1198	foreach $texpath (split /$envkey/, $TEXINPUTS ) {
1199	    $file = "$texpath$dd$extfile.$ext";
1200	    last if ($found = (-f $file));
1201	}
1202    }
1203    if ( $found ) {
1204	print "\nReading $ext file: $file ...";
1205	# must allow @ within control-sequence names
1206	$dum = &do_cmd_makeatletter();
1207	&slurp_input($file);
1208	if ($ext =~ /bbl/) {
1209	    # remove the \newcommand{\etalchar}{...} since not needed
1210	    s/^\\newcommand\{\\etalchar}[^\n\r]*[\n\r]+//s;
1211	}
1212	&pre_process;
1213	&substitute_meta_cmds if (%new_command || %new_environment);
1214	if ($ext eq "aux") {
1215            my $latex_pathname = L2hos->path2latex($file);
1216	    $aux_preamble .=
1217		"\\AtBeginDocument{\\makeatletter\n\\input $latex_pathname\n\\makeatother\n}\n";
1218	    local(@extlines) = split ("\n", $_);
1219	    print " translating ".(0+@extlines). " lines " if ($VERBOSITY >1);
1220	    local($eline,$skip_to); #$_ = '';
1221	    foreach $eline (@extlines) {
1222		if ($skip_to) { next unless ($eline =~ s/$O$skip_to$C//) }
1223		$skip_to = '';
1224		# skip lines added for pdfTeX/hyperref compatibility
1225		next if ($eline =~ /^\\(ifx|else|fi|global \\let|gdef|AtEndDocument|let )/);
1226		# remove \index and \label commands, else invalid links may result
1227		$eline =~ s/\\(index|label)\s*($O\d+$C).*\2//g;
1228		if ($eline =~ /\\(old)?contentsline/) {
1229		    do { local($_,$save_AUX) = ($eline,$AUX_FILE);
1230		    $AUX_FILE = 0;
1231		    &wrap_shorthand_environments;
1232		    #footnote markers upset the numbering
1233		    s/\\footnote(mark|text)?//g;
1234		    $eline = &translate_environments($_);
1235		    $AUX_FILE = $save_AUX;
1236		    undef $_ };
1237		} elsif ($eline =~ s/^\\\@input//) {
1238		    &do_cmd__at_input($eline);
1239		    $eline = '';
1240		} elsif ($eline =~ s/^\\\@setckpt$O(\d+)$C//) {
1241		    $skip_to = $1; next;
1242		}
1243#	    $eline =~ s/$image_mark#([^#]+)#/print "\nIMAGE:",$img_params{$1},"\n";''/e;
1244#		$_ .= &translate_commands(&translate_environments($eline));
1245		$_ .= &translate_commands($eline) if $eline;
1246	    }
1247	    undef @extlines;
1248	} elsif ($ext =~ /$caption_suffixes/) {
1249	    local(@extlines) = split ("\n", $_);
1250	    print " translating ".(0+@extlines). " lines "if ($VERBOSITY >1);
1251	    local($eline); $_ = '';
1252	    foreach $eline (@extlines) {
1253		# remove \index and \label commands, else invalid links may result
1254		$eline =~ s/\\(index|label)\s*($O\d+$C).*\2//gso;
1255                if ($eline =~ /\\(old)?contentsline/) {
1256		    do { local($_,$save_PREAMBLE) = ($eline,$PREAMBLE);
1257		    $PREAMBLE = 0;
1258                    &wrap_shorthand_environments;
1259                    $eline = &translate_environments($_);
1260		    $PREAMBLE = $save_PREAMBLE;
1261                    undef $_ };
1262                }
1263		$_ .= &translate_commands($eline);
1264	    }
1265	    undef @extlines;
1266	} else {
1267	    print " wrapping " if ($VERBOSITY >1);
1268	    &wrap_shorthand_environments;
1269	    $_ = &translate_commands(&translate_environments($_));
1270	    print " translating " if ($VERBOSITY >1);
1271	}
1272	print "\n processed size: ".length($_)."\n" if($VERBOSITY>1);
1273	$dum = &do_cmd_makeatother();
1274    } else {
1275	print "\n*** Could not find file: $file ***\n" if ($DEBUG)
1276    };
1277    $found;
1278}
1279
1280sub deal_with_texinputs {
1281# The dot precedes all, this let's local files override always.
1282# The dirs we want are given as parameter list.
1283    if(!$TEXINPUTS) { $TEXINPUTS = '.' }
1284    elsif ($TEXINPUTS =~ /^$envkey/) {
1285	$TEXINPUTS = '.'.$TEXINPUTS
1286    };
1287    if ($ROOTED) {$TEXINPUTS .= "$envkey$FIXEDDIR"}
1288    $TEXINPUTS = &absolutize_path($TEXINPUTS);
1289    $ENV{'TEXINPUTS'} = join($envkey,".",@_,$TEXINPUTS,$ENV{'TEXINPUTS'});
1290}
1291
1292# provided by Fred Drake
1293sub absolutize_path {
1294    my ($path) = @_;
1295    my $npath = '';
1296    foreach $dir (split /$envkey/o, $path) {
1297        $npath .= L2hos->Make_directory_absolute($dir) . $envkey;
1298    }
1299    $npath =~ s/$envkey$//;
1300    $npath;
1301}
1302
1303sub add_document_info_page {
1304    # Uses $outermost_level
1305    # Nasty race conditions if the next two are done in parallel
1306    local($X) = ++$global{'max_id'};
1307    local($Y) = ++$global{'max_id'};
1308    ###MEH -- changed for math support: no underscores in commandnames
1309    $_ = join('', $_
1310	      , (($MAX_SPLIT_DEPTH <= $section_commands{$outermost_level})?
1311		 "\n<HR>\n" : '')
1312	      , "\\$outermost_level", "*"
1313	      , "$O$X$C$O$Y$C\\infopagename$O$Y$C$O$X$C\n",
1314	      , " \\textohtmlinfopage");
1315}
1316
1317
1318# For each style file name in TMP_styles (generated by texexpand) look for a
1319# perl file in $LATEX2HTMLDIR/styles and load it.
1320sub load_style_file_translations {
1321    local($_, $style, $options, $dir);
1322    print "\n";
1323    if ($TEXDEFS) {
1324	foreach $dir (split(/$envkey/,$LATEX2HTMLSTYLES)) {
1325	    if (-f ($_ = "$dir${dd}texdefs.perl")) {
1326		print "\nLoading $_...";
1327		require ($_);
1328		$styles_loaded{'texdefs'} = 1;
1329		last;
1330	    }
1331	}
1332    }
1333
1334    # packages automatically implemented
1335    local($auto_styles) = $AUTO_STYLES;
1336    $auto_styles .= 'array|' if ($HTML_VERSION > 3.1);
1337    $auto_styles .= 'tabularx|' if ($HTML_VERSION > 3.1);
1338    $auto_styles .= 'theorem|';
1339
1340    # these are not packages, but can appear as if class-options
1341    $auto_styles .= 'psamsfonts|';
1342    $auto_styles .= 'noamsfonts|';
1343
1344    $auto_styles =~ s/\|$//;
1345
1346    if(open(STYLES, "<$TMP_${dd}styles")) {
1347        while(<STYLES>) {
1348            if(s/^\s*(\S+)\s*(.*)$/$style = $1; $options = $2;/eo) {
1349                &do_require_package($style);
1350	        $_ = $DONT_INCLUDE;
1351	        s/:/|/g;
1352	        &write_warnings("No implementation found for style \`$style\'\n")
1353		    unless ($styles_loaded{$style} || $style =~ /^($_)$/
1354			|| $style =~ /$auto_styles/);
1355
1356                # MRO: Process options for packages
1357                &do_package_options($style,$options) if($options);
1358            }
1359        }
1360        close(STYLES);
1361    } else {
1362        print "\nError: Cannot read '$TMP_${dd}styles': $!\n";
1363    }
1364}
1365
1366################## Weird Special case ##################
1367
1368# The new texexpand can be told to leave in \input and \include
1369# commands which contain code that the translator should simply pass
1370# to latex, such as the psfig stuff.  These should still be seen by
1371# TeX, so we add them to the preamble ...
1372
1373sub do_include_lines {
1374    while (s/$include_line_rx//o) {
1375	local($include_line) = &revert_to_raw_tex($&);
1376	&add_to_preamble ('include', $include_line);
1377    }
1378}
1379
1380########################## Preprocessing ############################
1381
1382# JCL(jcl-verb)
1383# The \verb declaration and the verbatim environment contain simulated
1384# typed text and should not be processed. Characters such as $,\,{,and }
1385# loose their special meanings and should not be considered when marking
1386# brackets etc. To achieve this \verb declarations and the contents of
1387# verbatim environments are replaced by markers. At the end the original
1388# text is put back into the document.
1389# The markers for verb and verbatim are different so that these commands
1390# can be restored to what the raw input was just in case they need to
1391# be passed to latex.
1392
1393sub pre_process {
1394    # Modifies $_;
1395    #JKR: We need support for some special environments.
1396    # This has to be here, because  they might contain
1397    # structuring commands like \section etc.
1398    local(%comments);
1399    &pre_pre_process if (defined &pre_pre_process);
1400    s/\\\\/\\\\ /go;		# Makes it unnecessary to look for escaped cmds
1401    &replace_html_special_chars;
1402    # Remove fake environment which should be invisible to LaTeX2HTML.
1403    s/\001//m;
1404    s/[%]end\s*{latexonly}/\001/gom;
1405    s/[%]begin\s*{latexonly}([^\001]*)\001/%/gos;
1406    s/\001//m;
1407
1408    &preprocess_alltt if defined(&preprocess_alltt);
1409
1410    $KEEP_FILE_MARKERS = 1;
1411    if ($KEEP_FILE_MARKERS) {
1412#	if (s/%%% TEXEXPAND: \w+ FILE( MARKER)? (\S*).*/
1413#	    '<tex2html_'.($1?'':'end').'file>'.qq|#$2#|."\n"/em) {
1414#	    $_ = "<tex2html_file>#$2#\n". $_ };
1415	#RRM: ignore \n at end of included file, else \par may result
1416	if (s/(\n{1,2})?%%% TEXEXPAND: \w+ FILE( MARKER)? (\S*).*\n?/
1417	    ($2?$1:"\n").'<tex2html_'.($2?'':'end').'file>'.qq|#$3#|."\n"/em) {
1418	    $_ = "<tex2html_file>#$3#\n". $_ };
1419    } else {
1420	s/%%% TEXEXPAND[^\n]*\n//gm;
1421    }
1422
1423    # Move all LaTeX comments into a local list
1424    s/([ \t]*(^|\G|[^\\]))(%.*(\n[ \t]*|$))/print "%";
1425	$comments{++$global{'verbatim_counter'}} = "$3";
1426	&write_mydb("verbatim", $global{'verbatim_counter'}, $3);
1427	"$1$comment_mark".$global{'verbatim_counter'}."\n"/mge;
1428    # Remove the htmlonly-environment
1429    s/\\begin\s*{htmlonly}\s*\n?//gom;
1430    s/\\end\s*{htmlonly}\s*\n?//gom;
1431    # Remove enviroments which should be invisible to LaTeX2HTML.
1432    s/\n[^%\n]*\\end\s*{latexonly}\s*\n?/\001/gom;
1433    s/((^|\n)[^%\n]*)\\begin\s*{latexonly}([^\001]*)\001/$1/gom;
1434    s/\\end\s*{comment}\s*\n?/\001/gom;
1435    s/\\begin\s*{comment}([^\001]*)\001//gom;
1436
1437    # this used to be earlier, but that can create problems with comments
1438    &wrap_other_environments if (%other_environments);
1439
1440#    s/\\\\/\\\\ /go;		# Makes it unnecessary to look for escaped cmds
1441    local($next, $esc_del);
1442    &normalize_language_changes;
1443    # Patches by #JKR, #EI#, #JCL(jcl-verb)
1444
1445    #protect \verb|\begin/end....|  parts, for LaTeX documentation
1446    s/(\\verb\*?(.))\\(begin|end)/$1\003$3/g;
1447
1448    local(@processedV);
1449    local($opt, $style_info,$before, $contents, $after, $env);
1450    while (($UNFINISHED_COMMENT)||
1451  (/\\begin\s*($opt_arg_rx)?\s*\{($verbatim_env_rx|$keepcomments_rx)\}/o)) {
1452	($opt, $style_info) = ($1,$2);
1453	$before=$contents=$after=$env='';
1454	if ($UNFINISHED_COMMENT) {
1455	    $UNFINISHED_COMMENT =~ s/([^:]*)::(\d+)/$env=$1;$after=$_;
1456	        $before = join("",$unfinished_mark,$env,$2,"#");''/e;
1457	    print "\nfound the lost \\end{$env}\n";
1458	}
1459	#RRM: can we avoid copying long strings here ?
1460	#     maybe this loop can be an  s/.../../s  with (.*?)
1461	#
1462	($before, $after, $env) = ($`, $', $3) unless ($env);
1463	if (!($before =~
1464     /\\begin(\s*\[[^\]]*\]\s*)?\{($verbatim_env_rx|$keepcomments_rx)\}/)) {
1465	    push(@processedV,$before);
1466	    print "'";$before = '';
1467	}
1468 	if ($after =~ /\s*\\end\{$env[*]?}/) { # Must NOT use the s///o option!!!
1469	    ($contents, $after) = ($`, $');
1470 	    $contents =~ s/^\n+/\n/s;
1471# 	    $contents =~ s/\n+$//s;
1472
1473	    # re-insert comments
1474	    $contents =~ s/$comment_mark(\d+)\n?/$comments{$1}/g;
1475#	    $contents =~ s/$comment_mark(\d+)/$verbatim{$1}/g;
1476
1477	    # revert '\\ ' -> '\\' only once
1478	    if ($env =~ /rawhtml|$keepcomments_rx/i) {
1479		$contents = &revert_to_raw_tex($contents);
1480	    } else {
1481		$contents =~ s/([^\\](?:\\\\)*\\)([$html_escape_chars])/$1.&special($2)/geos;
1482		$contents =~ s/\\\\ /\\\\/go;
1483	    }
1484
1485	    if ($env =~/$keepcomments_rx/) {
1486		$verbatim{++$global{'verbatim_counter'}} = "$contents";
1487	    } else {
1488		&write_mydb("verbatim", ++$global{'verbatim_counter'}, $contents);
1489	    }
1490#	    $verbatim{$global{'verbatim_counter'}} = "$contents" if ($env =~/$keepcomments_rx/);
1491#	    $verbatim{$global{'verbatim_counter'}} = "$contents";
1492
1493	    if ($env =~ /rawhtml|$keepcomments_rx/i) {
1494		if ($before) {
1495		    $after = join("",$verbatim_mark,$env
1496			      ,$global{'verbatim_counter'},"#",$after);
1497		} else {
1498		    push (@processedV, join("",$verbatim_mark,$env
1499			   ,$global{'verbatim_counter'},"#"));
1500		}
1501	    } elsif ($env =~ /tex2html_code/) {
1502		if ($before) {
1503		    $after = join("","\\begin", $opt, "\{verbatim_code\}"
1504			  , $verbatim_mark,$env
1505			  , $global{'verbatim_counter'},"#"
1506			  , "\\end\{verbatim_code\}",$after);
1507		} else {
1508		    push (@processedV
1509			  , join("","\\begin", $opt, "\{verbatim_code\}"
1510				 , $verbatim_mark,$env
1511				 , $global{'verbatim_counter'},"#"
1512				 , "\\end\{verbatim_code\}"));
1513		}
1514	    } else {
1515		if ($before) {
1516		    $after = join("","\\begin", $opt, "\{tex2html_preform\}"
1517			  , $verbatim_mark,$env
1518			  , $global{'verbatim_counter'},"#"
1519			  , "\\end\{tex2html_preform\}",$after);
1520		} else {
1521		    push (@processedV
1522			  , join("","\\begin", $opt, "\{tex2html_preform\}"
1523				 , $verbatim_mark,$env
1524				 , $global{'verbatim_counter'},"#"
1525				 , "\\end\{tex2html_preform\}" ));
1526		}
1527	    }
1528	} else {
1529	    print "Cannot find \\end{$env}\n";
1530	    $after =~ s/$comment_mark(\d+)\n?/$comments{$1}/g;
1531#	    $after =~ s/$comment_mark(\d+)/$verbatim{$1}/g;
1532	    if ($env =~ /rawhtml|$keepcomments_rx/i) {
1533                $after = &revert_to_raw_tex($contents);
1534	    } else {
1535		$after =~ s/([^\\](?:\\\\)*\\)([$html_escape_chars])/$1.&special($2)/geos;
1536                $after =~ s/\\\\ /\\\\/go;
1537	    }
1538	    if ($env =~/$keepcomments_rx/) {
1539                $verbatim{++$global{'verbatim_counter'}} = "$after";
1540	    } else {
1541                &write_mydb("verbatim", ++$global{'verbatim_counter'}, $after );
1542	    }
1543	    $after = join("",$unfinished_mark,$env
1544			  ,$global{'verbatim_counter'},"#");
1545	}
1546	$_ = join("",$before,$after);
1547    }
1548    print STDOUT "\nsensitive environments found: ".(int(0+@processedV/2))." "
1549	if((@processedV)&&($VERBOSITY > 1));
1550    $_ = join('',@processedV, $_); undef @processedV;
1551
1552    #restore \verb|\begin/end....|  parts, for LaTeX documentation
1553#    $_ =~ s/(\\verb\W*?)\003(begin|end)/$1\\$2/g;
1554    $_ =~ s/(\\verb(;SPM\w+;|\W*?))\003(begin|end)/$1\\$3/g;
1555
1556    # Now do the \verb declarations
1557    # Patches by: #JKR, #EI#, #JCL(jcl-verb)
1558    # Tag \verb command and legal opening delimiter with unique number.
1559    # Replace tagged ones and its contents with $verb_mark & id number if the
1560    # closing delimiter can be found. After no more \verb's are to tag, revert
1561    # tagged one's to the original pattern.
1562    local($del,$contents,$verb_rerun);
1563    local($id) = $global{'verb_counter'};
1564    # must tag only one alternation per loop
1565    ##RRM: can this be speeded up using a list ??
1566    my $vbmark = $verb_mark;
1567    while (s/\\verb(\t*\*\t*)(\S)/"<verb$1".++$id.">$2"/e ||
1568	    s/\\verb()(\;SPM\w+\;|[^a-zA-Z*\s])/"<verb$1".++$id.">$2"/e ||
1569	    s/\\verb(\t\t*)([^*\s])/"<verb$1".++$id.">$2"/e) {
1570
1571	$del = $2;
1572	#RRM: retain knowledge of whether \verb* or \verb
1573	$vb_mark = ($1 =~/^\s*\*/? $verbstar_mark : $verb_mark);
1574	$esc_del = &escape_rx_chars($del);
1575	$esc_del = '' if (length($del) > 2);
1576
1577	# try to find closing delimiter and substitute the complete
1578	# statement with $verb_mark or $verbstar_mark
1579#	s/(<verb[^\d>]*$id>[\Q$del\E])([^$esc_del\n]*)([\Q$del\E]|$comment_mark(\d+)\n?)/
1580	s/(<verb[^\d>]*$id>\Q$del\E)([^$esc_del\n]*?)(\Q$del\E|$comment_mark(\d+)\n?)/
1581	    $contents=$2;
1582	    if ($4) { $verb_rerun = 1;
1583		join('', "\\verb$del", $contents, $comments{$4})
1584	    } else {
1585		$contents =~ s|\\\\ |\\\\|g;
1586		$contents =~ s|\n| |g;
1587		$verb{$id}=$contents;
1588		$verb_delim{$id}=$del;
1589		join('',$vb_mark,$id,$verb_mark)
1590	    }
1591	/e;
1592    }
1593    $global{'verb_counter'} = $id;
1594    # revert changes to fake verb statements
1595    s/<verb([^\d>]*)\d+>/\\verb$1/g;
1596
1597    #JKR: the comments include the linebreak and the following whitespace
1598#   s/([^\\]|^)(%.*\n[ \t]*)+/$1/gom; # Remove Comments but not % which may be meaningful
1599    s/((^|\n)$comment_mark(\d+))+//gom; # Remove comment markers on new lines, but *not* the trailing \n
1600    s/(\\\w+|(\W?))($comment_mark\d*\n?)/($2)? $2.$3:($1? $1.' ':'')/egm; # Remove comment markers, not after braces
1601#    s/(\W?)($comment_mark\d*\n?)/($1)? $1.$2:''/egm; # Remove comment markers, not after braces
1602    # Remove comment markers, but *not* the trailing \n
1603#  HWS:  Correctly remove multiple %%'s.
1604#
1605    s/\\%/\002/gm;
1606#    s/(%.*\n[ \t]*)//gm;
1607    s/(%[^\n]*\n)[ \t]*/$comment_mark\n/gm;
1608
1609    s/\002/\\%/gm;
1610
1611    local($tmp1,$tmp2);
1612    s/^$unfinished_mark$keepcomments_rx(\d+)#\n?$verbatim_mark$keepcomments_rx(\d+)#/
1613	$verbatim{$4}."\n\\end{$1}"/egm; # Raw TeX
1614    s/$verbatim_mark$keepcomments_rx(\d+)#/
1615	$tmp1 = $1;
1616	$tmp2 = &protect_after_comments($verbatim{$2});
1617	$tmp2 =~ s!\n$!!s;
1618	join ('', "\\begin{$tmp1}"
1619		, $tmp2
1620		, "\n\\end{$tmp1}"
1621		)/egm; # Raw TeX
1622    s/$unfinished_mark$keepcomments_rx(\d+)#/$UNFINISHED_COMMENT="$1::$2";
1623	"\\begin{$1}\n".$verbatim{$2}/egm; # Raw TeX
1624
1625    $KEEP_FILE_MARKERS = 1;
1626    if ($KEEP_FILE_MARKERS) {
1627	s/%%% TEXEXPAND: \w+ FILE( MARKER) (\S*).*\n/
1628	    '<tex2html_'.($1?'':'end').'file>'.qq|#.$2#\n|/gem;
1629    } else {
1630	s/%%% TEXEXPAND[^\n]*\n//gm;
1631    }
1632
1633    &mark_string($_);
1634
1635
1636    # attempt to remove the \html \latex and \latexhtml commands
1637    s/\\latex\s*($O\d+$C)(.*)\1//gm;
1638    s/\\latexhtml\s*($O\d+$C)(.*)\1\s*($O\d+$C)(.*)\3/$4/sg;
1639    s/\\html\s*($O\d+$C)(.*)\1/$2/sg;
1640    s/\\html\s*($O\d+$C)//gm;
1641
1642#    &make_unique($_);
1643}
1644
1645# RRM:  When comments are retained, then ensure that they are benign
1646# by removing \s and escaping braces,
1647# so that environments/bracing cannot become unbalanced.
1648sub protect_after_comments {
1649    my ($verb_text) = @_;
1650#    $verb_text =~ s/\%(.*)/'%'.&protect_helper($1)/eg;
1651    $verb_text =~ s/(^|[^\\])(\\\\)*\%(.*)/$1.$2.'%'.&protect_helper($3)/emg;
1652    $verb_text;
1653}
1654
1655sub protect_helper {
1656    my ($text) = @_;
1657    $text =~ s/\\/ /g;
1658    $text =~ s/(\{|\})/\\$1/g;
1659    $text;
1660}
1661
1662sub make_comment {
1663    local($type,$_) = @_;
1664    $_ =~ s/\\(index|label)\s*(($O|$OP)\d+($C|$CP)).*\2//sg;
1665    $_ = &revert_to_raw_tex($_);  s/^\n+//m;
1666    $_ =~ s/\\(index|label)\s*\{.*\}//sg;
1667    s/\-\-/- -/g; s/\-\-/- -/g; # cannot have -- inside a comment
1668    $_ = join('', '<!-- ', $type , "\n ", $_ , "\n -->" );
1669    $verbatim{++$global{'verbatim_counter'}} = $_;
1670    &write_mydb('verbatim', $global{'verbatim_counter'}, $_ );
1671    join('', $verbatim_mark, 'verbatim' , $global{'verbatim_counter'},'#')
1672}
1673
1674sub wrap_other_environments {
1675    local($key, $env, $start, $end, $opt_env, $opt_start);
1676    foreach $key (keys %other_environments) {
1677	# skip bogus entries
1678	next unless ($env = $other_environments{$key});
1679	$key =~ s/:/($start,$end)=($`,$');':'/e;
1680
1681	if (($end =~ /^\#$/m) && ($start =~ /^\#/m)) {
1682	    # catch Indica pre-processor language switches
1683	    $opt_start = $';
1684	    if ($env =~ s/\[(\w*)\]//o) {
1685		$opt_env = join('','[', ($1 ? $1 : $opt_start ), ']');
1686	    }
1687	    local($next);
1688	    while ($_ =~ /$start\b/) {
1689		push(@pre_wrapped, $`, "\\begin\{pre_$env\}", $opt_env );
1690		$_=$';
1691		if (/(\n*)$end/) {
1692		    push(@pre_wrapped, $`.$1,"\\end\{pre_$env\}$1");
1693		    $_ = $';
1694		    if (!(s/^N(IL)?//o)) {$_ = '#'.$_ }
1695		} else {
1696		    print "\n *** unclosed $start...$end chunk ***\n";
1697		    last;
1698		}
1699	    }
1700	    $_ = join('', @pre_wrapped, $_);
1701	    undef @pre_wrapped;
1702
1703	} elsif (($end=~/^\n$/) && ($start =~ /^\#/)) {
1704	    # catch ITRANS pre-processor language info;  $env = 'nowrap';
1705	    local($ilang) = $start; $ilang =~ s/^\#//m;
1706	    s/$start\s*\=([^<\n%]*)\s*($comment_mark\d*|\n|%)/\\begin\{tex2html_$env\}\\ITRANSinfo\{$ilang\}\{$1\}\n\\end\{tex2html_$env\}$2/g;
1707
1708	} elsif (!$end &&($start =~ /^\#/m)) {
1709	    # catch Indica pre-processor input-mode switches
1710	    s/$start(.*)\n/\\begin\{tex2html_$env\}$&\\end\{tex2html_$env\}\n/g;
1711
1712	} elsif (($start eq $end)&&(length($start) == 1)) {
1713	    $start =~ s/(\W)/\\$1/; $end = $start;
1714	    s/([^$end])$start([^$end]+)$end/$1\\begin\{pre_$env\}$2\\end\{pre_$env\}/mg;
1715	} elsif ($start eq $end) {
1716	    if (!($start =~ /\#\#/)) {
1717		$start =~ s/(\W)/\\$1/g; $end = $start; }
1718	    local (@pre_wrapped);
1719	    local($opt); $opt = '[indian]' if ($start =~ /^\#\#$/m);
1720	    while ($_ =~ /$start/s) {
1721		push(@pre_wrapped, $` , "\\begin\{pre_$env\}$opt");
1722		$_=$';
1723		if (/$end/s) {
1724		    push(@pre_wrapped, $`, "\\end\{pre_$env\}");
1725		    $_ = $';
1726		} else {
1727		    print "\n *** unclosed $start...$end chunk ***\n";
1728		    last;
1729		}
1730	    }
1731	    $_ = join('', @pre_wrapped, $_);
1732	    undef @pre_wrapped;
1733	} elsif ($start && ($env =~ /itrans/)) {
1734	    # ITRANS is of this form
1735	    local($indic); if($start =~ /\#(\w+)$/m) {$indic = $1}
1736	    #include the language-name as an optional parameter
1737	    s/$start\b/\\begin\{pre_$env\}\[$indic\]/sg;
1738	    s/$end\b/\\end\{pre_$env\}/sg;
1739	} elsif (($start)&&($end)) {
1740	    s/$start\b/\\begin\{pre_$env\}/sg;
1741	    s/$end\b/\\end\{pre_$env\}/sg;
1742	}
1743    }
1744    $_;
1745}
1746
1747#################### Marking Matching Brackets ######################
1748
1749# Reads the entire input file and performs pre_processing operations
1750# on it before returning it as a single string. The pre_processing is
1751# done on separate chunks of the input file by separate Unix processes
1752# as determined by LaTeX \input commands, in order to reduce the memory
1753# requirements of LaTeX2HTML.
1754sub slurp_input_and_partition_and_pre_process {
1755    local($file) = @_;
1756    local(%string, @files, $pos);
1757    local ($count) =  1;
1758
1759    unless(open(SINPUT,"<$file")) {
1760        die "\nError: Cannot read '$file': $!\n";
1761    }
1762    local(@file_string);
1763    print STDOUT "$file" if ($VERBOSITY >1);
1764    while (<SINPUT>) {
1765	if (/TEXEXPAND: INCLUDED FILE MARKER (\S*)/) {
1766	    # Forking seems to screw up the rest of the input stream
1767	    # We save the current position ...
1768	    $pos = tell SINPUT;
1769	    print STDOUT " fork at offset $pos " if ($VERBOSITY >1);
1770            $string{'STRING'} = join('',@file_string); @file_string = ();
1771	    &write_string_out($count);
1772	    delete $string{'STRING'};
1773	    # ... so that we can return to it
1774	    seek(SINPUT, $pos, 0);
1775	    print STDOUT "\nDoing $1 ";
1776	    ++$count}
1777	else {
1778#	    $string{'STRING'} .= $_
1779	    push(@file_string,$_);
1780	}
1781    }
1782    $string{'STRING'} = join('',@file_string); @file_string = ();
1783    &write_string_out($count);
1784    delete $string{'STRING'};
1785    close SINPUT;
1786    @files = ();
1787    if(opendir(DIR, $TMP_)) {
1788        @files = sort grep(/^\Q$PARTITION_PREFIX\E\d+/, readdir(DIR));
1789        closedir(DIR);
1790    }
1791
1792    unless(@files) {
1793        die "\nFailed to read in document parts.\n".
1794	     "Look up section Globbing in the troubleshooting manual.\n";
1795    }
1796
1797    $count = 0;
1798    foreach $file (@files) {
1799	print STDOUT "\nappending file: $TMP_$dd$file " if ($VERBOSITY > 1);
1800        $_ .= (&catfile("$TMP_$dd$file") || '');
1801	print STDOUT "\ntotal length: ".length($_)." characters\n" if ($VERBOSITY > 1);
1802    }
1803    die "\nFailed to read in document parts (out of memory?).\n"
1804	unless length($_);
1805    print STDOUT "\ntotal length: ".length($_)." characters\n" if ($VERBOSITY > 1);
1806}
1807
1808sub write_string_out {
1809    local($count) = @_;
1810    if ($count < 10) {$count = '00'.$count}
1811    elsif ($count < 100) {$count = '0'.$count}
1812    local($pid);
1813    # All open unflushed streams are inherited by the child. If this is
1814    # not set then the parent will *not* wait
1815    $| = 1;
1816    # fork returns 0 to the child and PID to the parent
1817    &write_mydb_simple("prelatex", $prelatex);
1818    &close_dbm_database;
1819    unless ($CAN_FORK) {
1820	&do_write_string_out;
1821    } else {
1822	unless ($pid = fork) {
1823	    &do_write_string_out;
1824	    exit 0;
1825	};
1826	waitpid($pid,0);
1827    }
1828    &open_dbm_database;
1829}
1830
1831sub do_write_string_out {
1832    local($_);
1833    close (SINPUT) if($CAN_FORK);
1834    &open_dbm_database;
1835    $_ = delete $string{'STRING'};
1836    # locate blank-lines, for paragraphs.
1837    # Replace verbatim environments etc.
1838    &pre_process;
1839    # locate the blank lines for \par s
1840    &substitute_pars;
1841    # Handle newcommand, newenvironment, newcounter ...
1842    &substitute_meta_cmds;
1843    &wrap_shorthand_environments;
1844    print STDOUT "\n *** End-of-partition ***" if ($VERBOSITY > 1);
1845    if(open(OUT, ">$TMP_$dd$PARTITION_PREFIX$count")) {
1846        print OUT $_;
1847        close(OUT);
1848    } else {
1849        print "\nError: Cannot write '$TMP_$dd$PARTITION_PREFIX$count': $!\n";
1850    }
1851    print STDOUT $_ if ($VERBOSITY > 9);
1852    $preamble = join("\n",$preamble,@preamble); # undef @preamble;
1853    &write_mydb_simple("preamble", $preamble);
1854    # this was done earlier; it should not be repeated
1855    #&write_mydb_simple("prelatex", $prelatex);
1856    &write_mydb_simple("aux_preamble", $aux_preamble);
1857    &close_dbm_database;
1858}
1859
1860# Reads the entire input file into a
1861# single string.
1862sub slurp_input  {
1863    local($file) = @_;
1864    local(%string);
1865    if(open(INPUT,"<$file")) {
1866        local(@file_string);
1867        while (<INPUT>) {
1868	    push(@file_string, $_ );
1869        }
1870        $string{'STRING'} = join('',@file_string);
1871        close INPUT;
1872        undef @file_string;
1873    } else {
1874        print "\nError: Cannot read '$file': $!\n";
1875    }
1876    $_ = delete $string{'STRING'}; # Blow it away and return the result
1877}
1878
1879# MRO: make them more efficient
1880sub special {
1881    $html_specials{$_[0]} || $_[0];
1882}
1883
1884sub special_inv {
1885    $html_specials_inv{$_[0]} || $_[0];
1886}
1887
1888sub special_html {
1889    $html_special_entities{$_[0]} || $_[0];
1890}
1891
1892sub special_html_inv {
1893    $html_spec_entities_inv{$_[0]} || $_[0];
1894}
1895
1896# Mark each matching opening and closing bracket with a unique id.
1897sub mark_string {
1898    # local (*_) = @_; # Modifies $_ in the caller;
1899    # -> MRO: changed to $_[0] (same effect)
1900    # MRO: removed deprecated $*, replaced by option /m
1901    $_[0] =~ s/(^|[^\\])\\\{/$1tex2html_escaped_opening_bracket/gom;
1902    $_[0] =~ s/(^|[^\\])\\\{/$1tex2html_escaped_opening_bracket/gom; # repeat this
1903    $_[0] =~ s/(^|[^\\])\\}/$1tex2html_escaped_closing_bracket/gom;
1904    $_[0] =~ s/(^|[^\\])\\}/$1tex2html_escaped_closing_bracket/gom; # repeat this
1905    my $id = $global{'max_id'};
1906    my $prev_id = $id;
1907    # mark all balanced braces
1908    # MRO: This should in fact mark all of them as the hierarchy is
1909    # processed inside-out.
1910    1 while($_[0] =~ s/\{([^{}]*)}/join("",$O,++$id,$C,$1,$O,$id,$C)/geo);
1911    # What follows seems esoteric...
1912    my @processedB = ();
1913    # Take one opening brace at a time
1914    while ($_[0] =~ /\{/) {
1915	my ($before,$after) = ($`,$');
1916        my $change = 0;
1917	while (@UNMATCHED_OPENING && $before =~ /\}/) {
1918            my $this = pop(@UNMATCHED_OPENING);
1919            print "\n *** matching brace \#$this found ***\n";
1920            $before =~ s/\}/join("",$O,$this,$C)/eo;
1921            $change = 1;
1922        }
1923        $_[0] = join('',$before,"\{",$after) if($change);
1924        # MRO: mark one opening brace
1925	if($_[0] =~ s/^([^{]*)\{/push(@processedB,$1);join('',$O,++$id,$C)/eos) {
1926	    $before=''; $after=$';
1927        }
1928        if ($after =~ /\}/) {
1929	    $after =~ s/\}/join("",$O,$id,$C)/eo;
1930	    $_[0] = join('',$before,$O,$id,$C,$after);
1931	} else {
1932	    print "\n *** opening brace \#$id  is unmatched ***\n";
1933	    $after =~ /^(.+\n)(.+\n)?/;
1934	    print " preceding: $after \n";
1935	    push (@UNMATCHED_OPENING,$id);
1936	}
1937    }
1938    $_[0] = join('',@processedB,$_[0]); undef(@processedB);
1939    print STDOUT "\nInfo: bracketings found: ", $id - $prev_id,"\n"
1940        if ($VERBOSITY > 1);
1941    # process remaining closing braces
1942    while (@UNMATCHED_OPENING && $_[0] =~ /\}/) {
1943        my $this = pop(@UNMATCHED_OPENING);
1944        print "\n *** matching brace \#$this found ***\n";
1945	$_[0] =~ s/\}/join("",$O,$this,$C)/eo;
1946    }
1947
1948    while ($_[0] =~ /\}/) {
1949        print "\n *** there was an unmatched closing \} ";
1950        my ($beforeline,$prevline,$afterline) = ($`, $`.$& , $');
1951        $prevline =~ /\n([^\n]+)\}$/m;
1952        if ($1) {
1953	    print "at the end of:\n" . $1 . "\}\n\n";
1954        } else {
1955	    $afterline =~ /^([^\n]+)\n/m;
1956	    if ($1) {
1957	        print "at the start of:\n\}" . $1 ."\n\n";
1958	    } else {
1959	        $prevline =~ /\n([^\n]+)\n\}$/m;
1960	        print "on a line by itself after:\n" . $1 . "\n\}\n\n";
1961	    }
1962        }
1963        $_[0] =  $beforeline . $afterline;
1964    }
1965    $global{'max_id'} = $id;
1966
1967    # restore escaped braces
1968    $_[0] =~ s/tex2html_escaped_opening_bracket/\\{/go;
1969    $_[0] =~ s/tex2html_escaped_closing_bracket/\\}/go;
1970}
1971
1972sub replace_html_special_chars {
1973    # Replaces html special characters with markers unless preceded by "\"
1974    s/([^\\])(<|>|&|\"|``|'')/&special($1).&special($2)/geom;
1975    # MUST DO IT AGAIN JUST IN CASE THERE ARE CONSECUTIVE HTML SPECIALS
1976    s/([^\\])(<|>|&|\"|``|'')/&special($1).&special($2)/geom;
1977    s/^(<|>|&|\"|``|'')/&special($1)/geom;
1978}
1979
1980#  used in \verbatiminput only:   $html_escape_chars = '<>&';
1981sub replace_all_html_special_chars { s/([$html_escape_chars])/&special($1)/geom; }
1982
1983# The bibliography and the index should be treated as separate sections
1984# in their own HTML files. The \bibliography{} command acts as a sectioning command
1985# that has the desired effect. But when the bibliography is constructed
1986# manually using the thebibliography environment, or when using the
1987# theindex environment it is not possible to use the normal sectioning
1988# mechanism. This subroutine inserts a \bibliography{} or a dummy
1989# \textohtmlindex command just before the appropriate environments
1990# to force sectioning.
1991sub add_bbl_and_idx_dummy_commands {
1992    local($id) = $global{'max_id'};
1993
1994    s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
1995    ## if ($bbl_cnt == 1) {
1996	s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
1997    #}
1998    $global{'max_id'} = $id;
1999    s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
2000    s/[\\]printindex/\\textohtmlindex /o;
2001    &lib_add_bbl_and_idx_dummy_commands() if defined(&lib_add_bbl_and_idx_dummy_commands);
2002}
2003
2004
2005# Uses and modifies $default_language
2006# This would be straight-forward except when there are
2007#  \MakeUppercase, \MakeLowercase  or \uppercase , \lowercase commands
2008# present in the source. The cases have to be adjusted before the
2009# ISO-character code is set; e.g. with "z --> "Z  in  german.perl
2010#
2011sub convert_iso_latin_chars {
2012    local($_) = @_;
2013    local($next_language, $pattern);
2014    local($xafter, $before, $after, $funct, $level, $delim);
2015    local(@case_processed);
2016    while (/$case_change_rx/) {
2017	$xafter = $2;
2018#	$before .= $`;
2019	push(@case_processed, $`);
2020	$funct = $3;
2021	$after = '';
2022	$_ = $';
2023	if ($xafter =~ /noexpand/) { $before .= "\\$funct"; next; }
2024
2025	s/^[\s%]*(.)/$delim=$1;''/eo;
2026	if ($delim =~ /{/ ) {
2027            # brackets not yet numbered...
2028#	    $before .= $funct . $delim;
2029	    push(@case_processed, $funct . $delim);
2030	    $level = 1;
2031	    $after = $delim;
2032	    while (($level)&&($_)&&(/[\{\}]/)) {
2033		$after .= $` . $&;
2034		$_ = $';
2035		if ( "$&" eq "\{" ) {$level++}
2036		elsif ( "$&" eq "\}" ) { $level-- }
2037		else { print $_ }
2038		print "$level";
2039	    }
2040#	    $before .= $after;
2041	    push(@case_processed, $after);
2042	} elsif ($delim eq "<") {
2043            # brackets numbered, but maybe not processed...
2044	    s/((<|#)(\d+)(>|#)>).*\1//;
2045	    $after .= $delim . $&;
2046	    $_ = $';
2047	    print STDOUT "\n<$2$funct$4>" if ($VERBOSITY > 2);
2048	    $funct =~ s/^\\//o;
2049	    local($cmd) = "do_cmd_$funct";
2050	    $after = &$cmd($after);
2051#	    $before .= $after;
2052	    push(@case_processed, $after);
2053	} elsif (($xafter)&&($delim eq "\\")) {
2054	    # preceded by \expandafter ...
2055	    # ...so expand the following macro first
2056	    $funct =~ s/^\\//o;
2057	    local($case_change) = $funct;
2058	    s/^(\w+|\W)/$funct=$1;''/eo;
2059	    local($cmd) = $funct;
2060	    local($thiscmd) = "do_cmd_$funct";
2061	    if (defined &$thiscmd) { $_ = &$thiscmd($_) }
2062	    elsif ($new_command{$funct}) {
2063		local($argn, $body, $opt) = split(/:!:/, $new_command{$funct});
2064		do { ### local($_) = $body;
2065		     &make_unique($body);
2066		} if ($body =~ /$O/);
2067		if ($argn) {
2068		    do {
2069			local($before) = '';
2070			local($after) = "\\$funct ".$_;
2071			$after = &substitute_newcmd;   # may change $after
2072			$after =~ s/\\\@#\@\@/\\/o ;
2073		    }
2074		} else { $_ = $body . $_; }
2075	    } else { print "\nUNKNOWN COMMAND: $cmd "; }
2076
2077	    $cmd = $case_change;
2078	    $case_change = "do_cmd_$cmd";
2079	    if (defined &$case_change) { $_ = &$case_change($_) }
2080	} else {
2081            # this should not happen, but just in case...
2082	    $funct =~ s/^\\//o;
2083	    local($cmd) = "do_cmd_$funct";
2084	    print STDOUT "\n\n<$delim$funct>" if ($VERBOSITY > 2);
2085	    $_ = join('', $delim , $_ );
2086	    if (defined &$cmd) { $_ = &$cmd($_) }
2087	}
2088    }
2089#   $_ = join('', $before, $_) if ($before);
2090    $_ = join('', @case_processed, $_) if (@case_processed);
2091
2092    # ...now do the conversions
2093    ($before, $after, $funct) = ('','','');
2094    @case_processed = ();
2095    if (/$language_rx/o) {
2096	($next_language, $pattern, $before, $after) = (($2||$1), $&, $`, $');
2097	$before = &convert_iso_latin_chars($before) if ($before);
2098#	push(@case_processed, $pattern, $before);
2099	local($br_id) = ++$global{'max_id'};
2100	$pattern = join('' , '\selectlanguage', $O.$br_id.$C
2101	    , (($pattern =~ /original/) ? $TITLES_LANGUAGE : $next_language )
2102	    , $O.$br_id.$C );
2103	push(@case_processed, $before, $pattern);
2104	push(@language_stack, $default_language);
2105	$default_language = $next_language;
2106	$_ = &convert_iso_latin_chars($after);
2107	$default_language = pop @language_stack;
2108    } else {
2109	$funct = $language_translations{$default_language};
2110	(defined(&$funct) ? $_ = &$funct($_) :
2111	 do {   &write_warnings(
2112		"\nCould not find translation function for $default_language.\n\n")
2113	    }
2114	);
2115	if ($USE_UTF ||(!$NO_UTF &&(%unicode_table)&&length(%unicode_table)>2)) {
2116	    &convert_to_unicode($_)};
2117    }
2118    $_ = join('', @case_processed, $_); undef(@case_processed);
2119    $_;
2120}
2121
2122# May need to add something here later
2123sub english_translation { $_[0] }
2124
2125# This replaces \setlanguage{\language} with \languageTeX
2126# This makes the identification of language chunks easier.
2127sub normalize_language_changes {
2128    s/$setlanguage_rx/\\$2TeX/gs;
2129}
2130
2131sub get_current_language {
2132    return () if ($default_language eq $TITLES_LANGUAGE);
2133    local($lang,$lstyle) = ' LANG="';
2134    $lang_code = $iso_languages{$default_language};
2135    if (%styled_languages) {
2136	$lstyle = $styled_languages{$default_language};
2137	$lstyle = '" CLASS="'.$lstyle  if $lstyle;
2138    }
2139    ($lang_code ? $lang.$lang_code.$lstyle.'"' : '');
2140}
2141
2142%styled_languages = ();
2143
2144sub do_cmd_htmllanguagestyle {
2145    local($_) = @_;
2146    local($class) = &get_next_optional_argument;
2147    local($lang) = &missing_braces unless (
2148	(s/$next_pair_pr_rx/$lang=$2;''/e)
2149	||(s/$next_pair_rx/$lang=$2;''/e));
2150    return ($_) unless $lang;
2151    local($class) = $iso_languages{$lang} unless $class;
2152    if ($USING_STYLES && $class) {
2153	print "\nStyling language: $lang = \"$class\" ";
2154    	$styled_languages{"$lang"} = $class;
2155    }
2156    $_;
2157}
2158
2159# General translation mechanism:
2160#
2161#
2162# The main program latex2html calls texexpand with the document name
2163# in order to expand some of its \input and \include statements, here
2164# also called 'merging', and to write a list of sensitized style, class,
2165# input, or include file names.
2166# When texexpand has finished, all is contained in one file, TMP_foo.
2167# (assumed foo.tex is the name of the document to translate).
2168#
2169# In this version, texexpand cares for following environments
2170# that may span include files / section boundaries:
2171# (For a more technical description, see texexpand.)
2172#  a) \begin{comment}
2173#  b) %begin{comment}
2174#  c) \begin{any}  introduced with \excludecomment
2175#  d) %begin{any}
2176#  e) \begin{verbatim}
2177#  f) \begin{latexonly}
2178#  g) %begin{latexonly}
2179#
2180# a)-d) cause texexpand to drop its contents, it will not show up in the
2181# output file. You can use this to 'comment out' a bunch of files, say.
2182#
2183# e)-g) prevent texexpand from expanding input files, but the environment
2184# content goes fully into the output file.
2185#
2186# Together with each merging of \input etc. there are so-called %%%texexpand
2187# markers accompanying the boundary.
2188#
2189# When latex2html reads in the output file, it uses these markers to write
2190# each part to a separate file, and process them further.
2191#
2192#
2193# If you have, for example:
2194#
2195# a) preample
2196# b) \begin{document}
2197# c) text
2198# d) \input{chapter}
2199# e) more text
2200# f) \end{document}
2201#
2202# you end up in two parts, part 1 is a)-c), part 2 is the rest.
2203# Regardless of environments spanning input files or sections.
2204#
2205#
2206# What now starts is meta command substitution:
2207# Therefore, latex2html forks a child process on the first part and waits
2208# until it finished, then forks another on the next part and so forth
2209# (see also &slurp_input_and_partition_and_preprocess).
2210#
2211# Here's what each child is doing:
2212# Each child process reads the new commands translated so far by the previous
2213# child from the TMP_global DBM database.
2214# After &pre_processing, it substitutes the meta commands (\newcommand, \def,
2215# and the like) it finds, and adds the freshly retrieved new commands to the
2216# list so far.
2217# This is done *only on its part* of the document; this saves upwards of memory.
2218# Finally, it writes its list of new commands (synopsis and bodies) to the
2219# DBM database, and exits.
2220# After the last child finished, latex2html reads in all parts and
2221# concatenates them.
2222#
2223#
2224# So, at this point in time (start of &translate), it again has the complete
2225# document, but now preprocessed and with new commands substituted.
2226# This has several disadvantages: an amount of commands is substituted (in
2227# TeX lingo, expanded) earlier than the rest.
2228# This causes trouble if commands really must get expanded at the point
2229# in time they show up.
2230#
2231#
2232# Then, still in &translate, latex2html uses the list of section commands to
2233# split the complete document into chunks.
2234# The chunks are not written to files yet. They are retained in the @sections
2235# list, but each chunk is handled separately.
2236# latex2html puts the current chunk to $_ and processes it with
2237# &translate_environments etc., then fetches the next chunk, and so on.
2238# This prevents environments that span section boundaries from getting
2239# translated, because \begin and \end cannot find one another, to say it this
2240# way.
2241#
2242#
2243# After the chunk is translated to HTML, it is written to a file.
2244# When all chunks are done, latex2html rereads each file to get cross
2245# references right, replace image markers with the image file names, and
2246# writes index and bibliography.
2247#
2248#
2249sub translate {
2250    &normalize_sections;	# Deal with the *-form of sectioning commands
2251
2252    # Split the input into sections, keeping the preamble together
2253    # Due to the regular expression, each split will create 5 more entries.
2254    # Entry 1 and 2: non-letter/letter sectioning command,
2255    # entry 4: the delimiter (may be empty)
2256    # entry 5: the text.
2257    local($pre_section, @sections);
2258    if (/\\(startdocument|begin\s*($O\d+$C)\s*document\s*\2)/) {
2259	$pre_section = $`.$&; $_ = $';
2260    }
2261    @sections = split(/$sections_rx/, $_);
2262    $sections[0] = $pre_section.$sections[0] if ($pre_section);
2263    undef $pre_section;
2264    local($sections) = int(scalar(@sections) / 5);
2265
2266    # Initialises $curr_sec_id to a list of 0's equal to
2267    # the number of sectioning commands.
2268    local(@curr_sec_id) = split(' ', &make_first_key);
2269    local(@segment_sec_id) = @curr_sec_id;
2270    local($i, $j, $current_depth) = (0,0,0);
2271    local($curr_sec) = $SHORT_FILENAME||$FILE;
2272    local($top_sec) = ($SEGMENT ? '' : 'top of ');
2273#    local(%section_info, %toc_section_info, $CURRENT_FILE, %cite_info, %ref_files);
2274    local($CURRENT_FILE);
2275    # These filenames may be set when translating the corresponding commands.
2276    local($tocfile, $loffile, $lotfile, $footfile, $citefile, $idxfile,
2277	  $figure_captions, $table_captions, $footnotes, $citations, %font_size, %index,
2278	  %done, $t_title, $t_author, $t_date, $t_address, $t_affil, $changed);
2279    local(@authors,@affils,@addresses,@emails,@authorURLs);
2280    local(%index_labels, %index_segment, $preindex, %footnotes, %citefiles);
2281    local($segment_table_captions, $segment_figure_captions);
2282    local($dir,$nosave) = ('','');
2283    local($del,$close_all,$open_all,$toc_sec_title,$multiple_toc);
2284    local($open_tags_R) = [];
2285    local(@save_open_tags)= ();
2286    local(@language_stack) = ();
2287    push (@language_stack, $default_language);
2288
2289#    $LATEX_FONT_SIZE = '10pt' unless ($LATEX_FONT_SIZE);
2290    &process_aux_file
2291	if $SHOW_SECTION_NUMBERS || /\\(caption|(html|hyper)?((eq)?ref|cite))/;
2292
2293    require ("${PREFIX}internals.pl") if (-f "${PREFIX}internals.pl");
2294#JCL(jcl-del)
2295    &make_single_cmd_rx;
2296#
2297    $tocfile = $EXTERNAL_CONTENTS;
2298    $idxfile = $EXTERNAL_INDEX;
2299    $citefile = $EXTERNAL_BIBLIO; $citefile =~ s/#.*$//;
2300    $citefiles{1} = $citefile if ($citefile);
2301    print "\nTranslating ...";
2302
2303    while ($i <= @sections) {
2304        undef $_;
2305	$_ = $sections[$i];
2306	s/^[\s]*//;		# Remove initial blank lines
2307
2308	# The section command was removed when splitting ...
2309	s/^/\\$curr_sec$del/  if ($i > 0); # ... so put it back
2310	if ($current_depth < $MAX_SPLIT_DEPTH)  {
2311	    if (($footnotes)&&($NO_FOOTNODE)&&( $current_depth < $MAX_SPLIT_DEPTH)) {
2312		local($thesenotes) = &make_footnotes ;
2313		print OUTPUT $thesenotes;
2314	    }
2315	    $CURRENT_FILE = &make_name($curr_sec, join('_',@curr_sec_id));
2316
2317	    open(OUTPUT, ">$CURRENT_FILE")
2318		|| die "Cannot write '$CURRENT_FILE': $!\n";
2319	    if ($XBIT_HACK) { # use Apache's XBit hack
2320		chmod 0744, $CURRENT_FILE;
2321		&check_htaccess;
2322	    } else {
2323		chmod 0644, $CURRENT_FILE;
2324	    }
2325
2326	    if ($MULTIPLE_FILES && $ROOTED) {
2327	        if ($DESTDIR =~ /^\Q$FIXEDDIR\E[$dd$dd]?([^$dd$dd]+)/)
2328	            { $CURRENT_FILE = "$1$dd$CURRENT_FILE" };
2329	    }
2330	}
2331	&remove_document_env;
2332#        &wrap_shorthand_environments;    #RRM  Is this needed ?
2333	print STDOUT "\n" if ($VERBOSITY);
2334	print STDOUT "\n" if ($VERBOSITY > 2);
2335	print $i/5,"/$sections";
2336	print ":$top_sec$curr_sec:" if ($VERBOSITY);
2337
2338	# Must do this early ... It also sets $TITLE
2339	&process_command($sections_rx, $_) if (/^$sections_rx/);
2340	# reset tags saved from the previous section
2341	$open_tags_R = [ @save_open_tags ];
2342	@save_open_tags = ();
2343
2344	local($curr_sec_tex);
2345	if ((! $TITLE) || ($TITLE eq $default_title)) {
2346	    eval '$TITLE = '.$default_title;
2347	    $TITLE = $default_title if $@;
2348	    $curr_sec_tex = ($top_sec ? '' :
2349		  join('', '"', &revert_to_raw_tex($curr_sec), '"'));
2350	    print STDOUT "$curr_sec_tex for $CURRENT_FILE\n" if ($VERBOSITY);
2351	} else {
2352	    local($tmp) = &purify($TITLE,1);
2353	    $tmp = &revert_to_raw_tex($tmp);
2354	    print STDOUT "\"$tmp\" for $CURRENT_FILE\n" if ($VERBOSITY);
2355	}
2356
2357	if (/\\(latextohtmlditchpreceding|startdocument)/m) {
2358 	    local($after) = $';
2359 	    local($before) = $`.$&;
2360	    $SEGMENT = 1 if ($1 =~ /startdocument/);
2361	    print STDOUT "\n *** translating preamble ***\n" if ($VERBOSITY);
2362	    $_ = &translate_preamble($before);
2363	    s/\n\n//g; s/<BR>//g;	# remove redundant blank lines and breaks
2364#
2365#	    &process_aux_file  if $AUX_FILE_NEEDED;
2366#
2367	    print STDOUT "\n *** preamble done ***\n" if ($VERBOSITY);
2368	    $PREAMBLE = 0;
2369 	    $NESTING_LEVEL=0;
2370	    &do_AtBeginDocument;
2371	    $after =~ s/^\s*//m;
2372	    print STDOUT (($VERBOSITY >2)? "\n*** Translating environments ***" : ";");
2373	    $after = &translate_environments($after);
2374	    print STDOUT (($VERBOSITY >2)? "\n*** Translating commands ***" : ";");
2375	    $_ .= &translate_commands($after);
2376#            $_ = &translate_commands($after);
2377 	} else {
2378	    &do_AtBeginDocument;
2379	    $PREAMBLE = 0;
2380 	    $NESTING_LEVEL=0;
2381	    print STDOUT (($VERBOSITY >2)? "\n*** Translating environments ***" : ";");
2382 	    $_ = &translate_environments($_);
2383	    print STDOUT (($VERBOSITY >2)? "\n*** Translating commands ***" : ";");
2384 	    $_ = &translate_commands($_);
2385 	}
2386
2387	# close any tags that remain open
2388	if (@$open_tags_R) {
2389	    ($close_all,$open_all) = &preserve_open_tags();
2390	    $_ .= $close_all;
2391	    @save_open_tags = @$open_tags_R; $open_tags_R = [];
2392	} else { ($close_all,$open_all) = ('','') }
2393
2394	print STDOUT (($VERBOSITY >2)? "\n*** Translations done ***" : "\n");
2395#	if (($footnotes)&&($NO_FOOTNODE)&&( $current_depth < $MAX_SPLIT_DEPTH)) {
2396#	    $_ .= &make_footnotes
2397#	}
2398	print OUTPUT $_;
2399
2400	# Associate each id with the depth, the filename and the title
2401	###MEH -- starred sections don't show up in TOC ...
2402	# RRM:  ...unless $TOC_STARS is set
2403#	$toc_sec_title = &simplify($toc_sec_title);
2404	$toc_sec_title = &purify($toc_sec_title);# if $SEGMENT;
2405	$toc_sec_title = &purify($TITLE) unless ($toc_sec_title);
2406
2407	if ($TOC_STARS) {
2408	    $toc_section_info{join(' ',@curr_sec_id)} =
2409		"$current_depth$delim$CURRENT_FILE$delim$toc_sec_title"
2410#		    if ($current_depth <= $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
2411		    if ($current_depth <= $TOC_DEPTH);
2412	} else {
2413	    $toc_section_info{join(' ',@curr_sec_id)} =
2414		"$current_depth$delim$CURRENT_FILE$delim$toc_sec_title"
2415		. ($curr_sec =~ /star$/ ? "$delim<tex2html_star_mark>" : "")
2416#		    if ($current_depth <= $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
2417		    if ($current_depth <= $TOC_DEPTH);
2418	}
2419
2420	# include $BODYTEXT in the section_info, when starting a new page
2421	$section_info{join(' ',@curr_sec_id)} =
2422	    "$current_depth$delim$CURRENT_FILE$delim$TITLE$delim"
2423		. (($current_depth < $MAX_SPLIT_DEPTH)? $BODYTEXT: "");
2424
2425	# Get type of section (see also the split above)
2426	$curr_sec = $sections[$i+1].$sections[$i+2];
2427	$del = $sections[$i+4];
2428
2429	# Get the depth of the current section;
2430#	$curr_sec = $outermost_level unless $curr_sec;
2431	$current_depth = $section_commands{$curr_sec};
2432	if ($after_segment) {
2433	    $current_depth = $after_segment;
2434            $curr_sec_id[$after_segment] += $after_seg_num;
2435            ($after_segment,$after_seg_num) = ('','');
2436	    for($j=1+$current_depth; $j <= $#curr_sec_id; $j++) {
2437		$curr_sec_id[$j] = 0;
2438	    }
2439	}
2440	if ($SEGMENT||$SEGMENTED) {
2441	    for($j=1; $j <= $#curr_sec_id; $j++) {
2442		$curr_sec_id[$j] += $segment_sec_id[$j];
2443		$segment_sec_id[$j] = 0;
2444	    }
2445	};
2446
2447
2448	# this may alter the section-keys
2449	$multiple_toc = 1 if ($MULTIPLE_FILES && $ROOTED && (/$toc_mark/));
2450
2451
2452	#RRM : Should this be done here, or in \stepcounter ?
2453	@curr_sec_id = &new_level($current_depth, @curr_sec_id);
2454
2455	$toc_sec_title = $TITLE = $top_sec = '';
2456	$i+=5; #skip to next text section
2457    }
2458    $open_tags_R = [];
2459    $open_all = '';
2460
2461    $_ = undef;
2462    $_ = &make_footnotes if ($footnotes);
2463    $CURRENT_FILE = '';
2464    print OUTPUT;
2465    close OUTPUT;
2466
2467
2468#    # this may alter the section-keys
2469#    &adjust_root_keys if $multiple_toc;
2470
2471    if ($PREPROCESS_IMAGES) { &preprocess_images }
2472    else { &make_image_file }
2473    print STDOUT "\n *** making images ***" if ($VERBOSITY > 1);
2474    &make_images;
2475
2476    # Link sections, add head/body/address do cross-refs etc
2477    print STDOUT "\n *** post-process ***" if ($VERBOSITY > 1);
2478    &post_process;
2479
2480    if (defined &document_post_post_process) {
2481    	#BRM: extra document-wide post-processing
2482	print STDOUT "\n *** post-processing Document ***" if ($VERBOSITY > 1);
2483	&document_post_post_process();
2484    }
2485
2486    print STDOUT "\n *** post-processed ***" if ($VERBOSITY > 1);
2487    &copy_icons if $LOCAL_ICONS;
2488    if ($SEGMENT || $DEBUG || $SEGMENTED) {
2489	&save_captions_in_file("figure",  $figure_captions) if $figure_captions;
2490	&save_captions_in_file("table",  $table_captions) if $table_captions;
2491#	&save_array_in_file ("captions", "figure_captions", 0, %figure_captions) if %figure_captions;
2492#	&save_array_in_file ("captions", "table_captions", 0, %table_captions) if %table_captions;
2493	&save_array_in_file ("index", "index", 0, %index);
2494	&save_array_in_file ("sections", "section_info", 0, %section_info);
2495	&save_array_in_file ("contents", "toc_section_info", 0,%toc_section_info);
2496	&save_array_in_file ("index", "sub_index", 1, %sub_index) if %sub_index;
2497	&save_array_in_file ("index", "index_labels", 1, %index_labels) if %index_labels;
2498	&save_array_in_file ("index", "index_segment", 1, %index_segment) if %index_segment;
2499	&save_array_in_file ("index", "printable_key", 1, %printable_key)
2500	    if (%printable_key || %index_segment);
2501    }
2502    elsif ($MULTIPLE_FILES && $ROOTED) {
2503	&save_array_in_file ("sections", "section_info", 0, %section_info);
2504	&save_array_in_file ("contents", "toc_section_info", 0, %toc_section_info);
2505    }
2506    &save_array_in_file ("internals", "ref_files", 0, %ref_files) if $changed;
2507    &save_array_in_file ("labels", "external_labels", 0, %ref_files);
2508    &save_array_in_file ("labels", "external_latex_labels", 1, %latex_labels);
2509    &save_array_in_file ("images", "cached_env_img", 0, %cached_env_img);
2510}
2511
2512# RRM:
2513sub translate_preamble {
2514    local($_) = @_;
2515    $PREAMBLE = 1;
2516    $NESTING_LEVEL=0;   #counter for TeX group nesting level
2517    # remove some artificially inserted constructions
2518    s/\n${tex2html_deferred_rx}\\par\s*${tex2html_deferred_rx2}\n/\n/gm;
2519    s/\\newedcommand(<<\d+>>)([A-Za-z]+|[^A-Za-z])\1(\[\d+\])?(\[[^]]*\])?(<<\d+>>)[\w\W\n]*\5($comment_mark\d*)?//gm;
2520    s/\n{2,}/\n/ogm;
2521
2522    if (/\\htmlhead/) {
2523        print STDOUT "\nPREAMBLE: discarding...\n$`" if ($VERBOSITY > 4);
2524        local($after) = $&.$';
2525	# translate segment preamble preceding  \htmlhead
2526	&translate_commands(&translate_environments($`));
2527	# translate \htmlhead  and rest of preamble
2528	$_=&translate_commands(&translate_environments($after));
2529        print STDOUT "\nPREAMBLE: retaining...\n$_" if ($VERBOSITY > 4);
2530    } else {
2531	# translate only preamble here (metacommands etc.)
2532	# there should be no textual results, if so, discard them
2533	&translate_commands(&translate_environments($_));
2534        print STDOUT "\nPREAMBLE: discarding...\n$_" if ($VERBOSITY > 4);
2535	$_="";
2536    };
2537    $_ = &do_AtBeginDocument($_);
2538    if (! $SEGMENT) { $_ = ''} # segmented documents have a heading already
2539    $_;
2540}
2541
2542############################ Processing Environments ##########################
2543
2544sub wrap_shorthand_environments {
2545    # This wraps a dummy environment around environments that do not use
2546    # the begin-end convention. The wrapper will force them to be
2547    # evaluated by Latex rather than them being translated.
2548    # Wrap a dummy environment around matching TMPs.
2549    # s/^\$\$|([^\\])\$\$/{$1.&next_wrapper('tex2html_double_dollar')}/ge;
2550    # Wrap a dummy environment around matching $s.
2551    # s/^\$|([^\\])\$/{$1.&next_wrapper('$')}/ge;
2552    # s/tex2html_double_dollar/\$\$/go;
2553    # Do \(s and \[s
2554    #
2555    local($wrapper) = "tex2html_wrap_inline";	# \ensuremath wrapper
2556    print STDOUT "\n *** wrapping environments ***\n" if ($VERBOSITY > 3);
2557
2558    # MRO: replaced $* with /m
2559    print STDOUT "\\(" if ($VERBOSITY > 3);
2560    s/(^\\[(])|([^\\])(\\[(])/{$2.&make_any_wrapper(1,'',$wrapper).$1.$3}/geom;
2561    print STDOUT "\\)" if ($VERBOSITY > 3);
2562    s/(^\\[)]|[^\\]\\[)])/{$1.&make_any_wrapper(0,'',$wrapper)}/geom;
2563
2564    print STDOUT "\\[" if ($VERBOSITY > 3);
2565    s/(^\\[[])|([^\\])(\\[[])/{$2.&make_any_wrapper(1,1,"displaymath")}/geom;
2566    print STDOUT "\\]" if ($VERBOSITY > 3);
2567    s/(^\\[\]])|([^\\])(\\[\]])/{$2.&make_any_wrapper(0,1,"displaymath")}/geom;
2568
2569    print STDOUT "\$" if ($VERBOSITY > 3);
2570    s/$enspair/print "\$";
2571       {&make_any_wrapper(1,'',$wrapper).$&.&make_any_wrapper(0,'',$wrapper)}/geom;
2572
2573    $double_dol_rx = '(^|[^\\\\])\\$\\$';
2574    $single_dol_rx = '(^|[^\\\\])\\$';
2575    print STDOUT "\$" if ($VERBOSITY > 3);
2576
2577    local($dollars_remain) = 0;
2578    $_ = &wrap_math_environment;
2579    $_ = &wrap_raw_arg_cmds;
2580}
2581
2582sub wrap_math_environment {
2583
2584    # This wraps math-type environments
2585    # The trick here is that the opening brace is the same as the close,
2586    # but they *can* still nest, in cases like this:
2587    #
2588    # $ outer stuff ... \hbox{ ... $ inner stuff $ ... } ... $
2589    #
2590    # Note that the inner pair of $'s is nested within a group.  So, to
2591    # handle these cases correctly, we need to make sure that the outer
2592    # brace-level is the same as the inner. --- rst
2593    #tex2html_wrap
2594    # And yet another problem:  there is a scungy local idiom to do
2595    # this:  $\_$ for a boldfaced underscore.  xmosaic can't display the
2596    # resulting itty-bitty bitmap, for some reason; even if it could, it
2597    # would probably come out as an overbar because of the floating-
2598    # baseline problem.  So, we have to special case this.  --- rst again.
2599
2600    local ($processed_text, @processed_text, $before, $end_rx, $delim, $ifclosed);
2601    local ($underscore_match_rx) = "^\\s*\\\\\\_\\s*\\\$";
2602    local ($wrapper);
2603    print STDOUT "\nwrap math:" if ($VERBOSITY > 3);
2604
2605    #find braced dollars, in tabular-specs
2606    while (/((($O|$OP)\d+($C|$CP))\s*)\$(\s*\2)/) {
2607        push (@processed_text, $`, $1.$dol_mark.$5);
2608        $_ = $';
2609    }
2610    $_ = join('',@processed_text, $_) if (@processed_text);
2611    undef @processed_text;
2612
2613    $dollars_remain = 0;
2614    while (/$single_dol_rx/) {
2615	$processed_text .= $`.$1;
2616	$_ = $';
2617	$wrapper = "tex2html_wrap_inline";
2618	$end_rx = $single_dol_rx; # Default, unless we begin with $$.
2619	$delim = "\$";
2620
2621        if (/^\$/ && (! $`)) {
2622	    s/^\$//;
2623	    $end_rx = $double_dol_rx;
2624	    $delim = "";	# Cannot say "\$\$" inside displaymath
2625	    $wrapper = "displaymath";
2626
2627        } elsif (/$underscore_match_rx/ && (! $`)) {
2628
2629            # Special case for $\_$ ...
2630
2631            s/$underscore_match_rx//;
2632            $processed_text .= '\\_';
2633            next;
2634        }
2635
2636        # Have an opening $ or $$.  Find matching close, at same bracket level
2637#	$processed_text .= &make_any_wrapper(1,'',$wrapper).$delim;
2638
2639	print STDOUT "\$" if ($VERBOSITY > 3);
2640	$ifclosed = 0;
2641	local($thismath);
2642        while (/$end_rx/) {
2643	    # Forget the $$ if we are going to replace it with "displaymath"
2644            $before = $` . (($wrapper eq "displaymath")? "$1" : $&);
2645	    last if ($before =~ /\\(sub)*(item|section|chapter|part|paragraph)(star)?\b/);
2646	    $thismath .= $before;
2647            $_ = $';
2648	    s/^( [^\n])/\\space$1/s;  #make sure a trailing space doesn't get lost.
2649
2650            # Found dollar sign inside open subgroup ... now see if it's
2651            # at the same brace-level ...
2652
2653            local ($losing, $br_rx) = (0, '');
2654	    print STDOUT "\$" if ($VERBOSITY > 3);
2655            while ($before =~ /$begin_cmd_rx/) {
2656                $br_rx = &make_end_cmd_rx($1);  $before = $';
2657
2658                if ($before =~ /$br_rx/) { $before = $'; }
2659                else { $losing = 1; last; }
2660            }
2661            do { $ifclosed = 1; last } unless $losing;
2662
2663            # It wasn't ... find the matching close brace farther on; then
2664            # keep going.
2665
2666            /$br_rx/;
2667
2668            $thismath .= $`.$&;
2669
2670	    #RRM: may now contain unprocessed $s e.g. $\mbox{...$...$...}$
2671	    # the &do_cmd_mbox uses this specially to force an image
2672	    # ...but there may be other situations; e.g. \hbox
2673	    # so set a flag:
2674	    $dollars_remain = 1;
2675
2676            $_ = $';
2677        }
2678
2679        # Got to the end.  Whew!
2680	if ($ifclosed) {
2681	    # also process any nested math
2682	    while (($dollars_remain)&&($delim eq "\$")) {
2683		local($saved) = $_;
2684                $thismath =~ s/\$$//;
2685                $_ = $thismath;
2686		$thismath =  &wrap_math_environment;
2687		$thismath .= "\$";
2688		$_ = $saved;
2689	    }
2690	    $processed_text .= &make_any_wrapper(1,'',$wrapper) . $delim
2691		. $thismath . &make_any_wrapper(0,'',$wrapper);
2692	} else {
2693	    print STDERR "\n\n *** Error: unclosed math or extra `\$', before:\n$thismath\n\n";
2694#	    # remove a $ to try to recover as much as possible.
2695#	    $thismath =~ s/([^\\]\\\\|[^\\])\$/$1\%\%/;
2696#	    $_ = $thismath . $_; $thismath = "";
2697	print "\n$thismath\n\n\n$_\n\n\n"; die;
2698
2699	}
2700    }
2701    $processed_text . $_;
2702}
2703
2704sub translate_environments {
2705    local ($_) = @_;
2706    local($tmp, $capenv);
2707#   print "\nTranslating environments ...";
2708    local($after, @processedE);
2709    local ($contents, $before, $br_id, $env, $pattern);
2710    for (;;) {
2711#	last unless (/$begin_env_rx/o);
2712	last unless (/$begin_env_rx|$begin_cmd_rx|\\(selectlanguage)/o);
2713#	local ($contents, $before, $br_id, $env, $pattern);
2714	local($this_env, $opt_arg, $style_info);
2715	$contents = '';
2716	# $1,$2 : optional argument/text --- stylesheet info
2717	# $3 : br_id (at the beginning of an environment name)
2718	# $4 : environment name
2719	# $5 : br_id of open-brace, when $3 == $4 == '';
2720	# $6 : \selectlanguage{...}
2721	if ($7) {
2722	    push(@processedE,$`);
2723	    $_ = $';
2724	    if (defined &do_cmd_selectlanguage) {
2725		$_ = &do_cmd_selectlanguage($_);
2726	    } else {
2727		local($cmd) = $7;
2728		$pattern = &missing_braces unless (
2729		    s/$next_pair_rx/$pattern = $2;''/e);
2730		local($trans) = $pattern.'_translation';
2731		if (defined &$trans) {
2732		    &set_default_language($pattern,$_);
2733		}
2734		undef $cmd; undef $trans;
2735	    }
2736	    next;
2737	} elsif ($4) {
2738	    ($before, $opt_arg, $style_info, $br_id
2739	         , $env, $after, $pattern) = ($`, $2, $3, $4, $5, $', $&);
2740	    if (($before)&& (!($before =~ /$begin_env_rx|$begin_cmd_rx/))) {
2741		push(@processedE,$before);
2742		$_ = $pattern . $after; $before = '';
2743	    }
2744	} else {
2745	    ($before, $br_id, $env, $after, $pattern) = ($`, $6, 'group', $', $&);
2746	    if (($before)&& (!($before =~ /$begin_env_rx|$begin_cmd_rx/))) {
2747		push(@processedE,$before);
2748		$_ = $pattern . $after; $before = '';
2749	    }
2750	    local($end_cmd_rx) = &make_end_cmd_rx($br_id);
2751	    if ($after =~ /$end_cmd_rx/) {
2752		# ... find the the matching closing one
2753		$NESTING_LEVEL++;
2754		($contents, $after) = ($`, $');
2755		$contents = &process_group_env($contents);
2756		print STDOUT "\nOUT: {$br_id} ".length($contents) if ($VERBOSITY > 3);
2757		print STDOUT "\n:$contents\n" if ($VERBOSITY > 7);
2758		# THIS MARKS THE OPEN-CLOSE DELIMITERS AS PROCESSED
2759		$_ = join("", $before,"$OP$br_id$CP", $contents,"$OP$br_id$CP", $after);
2760		$NESTING_LEVEL--;
2761	    } else {
2762		$pattern = &escape_rx_chars($pattern);
2763		s/$pattern//;
2764		print "\nCannot find matching bracket for $br_id";
2765		$_ = join("", $before,"$OP$br_id$CP", $after);
2766	    }
2767	    next;
2768	}
2769	$contents = undef;
2770	local($defenv) = $env =~ /deferred/;
2771#	local($color_env);
2772	local($color_env)
2773	    unless ($env =~ /tabular|longtable|in(line|display)|math/);
2774	local($closures,$reopens);
2775	local(@save_open_tags) = @$open_tags_R unless ($defenv);
2776	local($open_tags_R) = [ @save_open_tags ] unless ($defenv);
2777	local(@saved_tags) if ($env =~ /tabular|longtable/);
2778	if ($env =~ /tabular|longtable|makeimage|in(line|display)/) {
2779	    @save_open_tags = @$open_tags_R;
2780	    $open_tags_R = [ @save_open_tags ];
2781	    # check for color
2782	    local($color_test) = join(',',@$open_tags_R);
2783	    if ($color_test =~ /(color\{[^}]*})/g ) {
2784		$color_env = $1;
2785	    } # else { $color_env = '' }
2786
2787	    if ($env =~ /tabular|longtable|makeimage/) {
2788		# close to the surrounding block-type tag
2789		($closures,$reopens,@saved_tags) = &preserve_open_block_tags();
2790		@save_open_tags = @$open_tags_R;
2791		$open_tags_R = [ @save_open_tags ];
2792		if ($color_env) {
2793		    $color_test = join(',',@saved_tags);
2794		    if ($color_test =~ /(color\{[^}]*})/g ) {
2795		        $color_env = $1;
2796		    }
2797		}
2798	    } elsif ($env =~ /in(line|display)/) {
2799		$closures = &close_all_tags() if ((&defined_env($env))
2800		    &&!($defenv)&&!($env=~/inline/)&&(!$declarations{$env}));
2801		if ($color_env) {
2802		    $color_test = $declarations{$color_env};
2803		    $color_test =~ s/<\/.*$//;
2804		    $closures .= "\n$color_test";
2805		    push (@$open_tags_R , $color_env);
2806		}
2807	    }
2808	} elsif ($env =~ /alltt|tex2html_wrap/) {
2809	    # alltt is constructed as paragraphs, not with <PRE>
2810	    #  tex2html_wrap  creates an image, which is at text-level
2811	} else {
2812	    $closures = &close_all_tags() if ((&defined_env($env))
2813		&&!($defenv)&&(!$declarations{$env}) );
2814	}
2815	# Sets $contents and modifies $after
2816	if (&find_end_env($env,$contents,$after)) {
2817	    print STDOUT "\nIN-A {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2818	    &process_command($counters_rx, $before)
2819		if ($before =~ /$counters_rx/);
2820	    # This may modify $before and $after
2821	    # Modifies $contents
2822#RRM: the do_env_... subroutines handle when to translate sub-environments
2823#	    $contents = &translate_environments($contents) if
2824##		((!$defenv) && (&defined_env($env)) && (! $raw_arg_cmds{$env})
2825##		&& (!$declarations{$env})
2826#		((&defined_env($env)) && (! $raw_arg_cmds{$env})
2827#		&& (!($env =~ /latexonly|enumerate|figure|table|makeimage|wrap_inline/))
2828#		&& ((! $NO_SIMPLE_MATH)||(!($env =~ /wrap/)))
2829#		&& (!($env =~ /(math|wrap|equation|eqnarray|makeimage|minipage|tabular)/) )
2830#		);
2831	    if ($opt_arg) {
2832		&process_environment(1, $env, $br_id, $style_info); # alters $contents
2833	    } else {
2834		&process_environment(0, $env, $br_id, '');
2835	    }
2836	    undef $_;
2837	    print STDOUT "\nOUT-A {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2838	    #JCL(jcl-env) - insert the $O$br_id$C stuff to handle environment grouping
2839	    if (!($contents eq '')) {
2840		$after =~ s/^\n//o if ($defenv);
2841		$this_env = join("", $before, $closures
2842			  , $contents
2843			  , ($defenv ? '': &balance_tags())
2844			  , $reopens ); $_ = $after;
2845	    } else {
2846		$this_env = join("", $before , $closures
2847			  , ($defenv ? '': &balance_tags())
2848			  , $reopens ); $_ = $after;
2849	    };
2850	### Evan Welsh <welsh@epcc.ed.ac.uk> added the next 24 lines ##
2851	} elsif (&defined_env($env)) {
2852	    print STDOUT "\nIN-B {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2853	    # If I specify a function for the environment then it
2854	    # calls it with the contents truncated at the next section.
2855	    # It assumes I know what I'm doing and doesn't give a
2856	    # deferred warning.
2857	    $contents = $after;
2858	    if ($opt_arg) {
2859		$contents = &process_environment(1, $env, $br_id, $style_info);
2860	    } else {
2861		$contents = &process_environment(0, $env, $br_id, '');
2862	    }
2863	    print STDOUT "\nOUT-B {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2864	    $this_env = join("", $before, $closures ,$contents, $reopens);
2865
2866	    # there should not be anything left over
2867#	    $_ = $after;
2868	    $_ = '';
2869	} elsif ($ignore{$env}) {
2870	    print STDOUT "\nIGNORED {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2871	    # If I specify that the environment should be ignored then
2872	    # it is but I get a deferred warning.
2873	    $this_env = join("", $before , $closures , &balance_tags()
2874		      , $contents, $reopens );
2875	    $_ = $after;
2876	    &write_warnings("\n\\end{$env} not found (ignored).\n");
2877	} elsif ($raw_arg_cmds{$env}) {
2878	    print "\nIN-C {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2879	    # If I specify that the environment should be passed to tex
2880	    # then it is with the environment truncated at the next
2881	    # section and I get a deferred warning.
2882
2883	    $contents = $after;
2884	    if ($opt_arg) {
2885		$contents = &process_environment(1, $env, $br_id, $style_info);
2886	    } else {
2887		$contents = &process_environment(0, $env, $br_id, '');
2888	    }
2889	    print STDOUT "\nOUT-C {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
2890	    $this_env = join("", $before, $closures
2891			     , $contents, &balance_tags(), $reopens );
2892	    $_='';
2893	    &write_warnings(
2894	        "\n\\end{$env $br_id} not found (truncated at next section boundary).\n");
2895	} else {
2896	    $pattern = &escape_rx_chars($pattern);
2897	    s/$pattern/$closures/;
2898	    print "\nCannot find \\end{$env $br_id}\n";
2899	    $_ .= join('', &balance_tags(), $reopens) unless ($defenv);
2900	}
2901	if ($this_env =~ /$begin_env_rx|$begin_cmd_rx/) {
2902	    $_ = $this_env . $_;
2903	} else { push (@processedE, $this_env) }
2904    }
2905    $_ = join('',@processedE) . $_;
2906    $tmp = $_; undef $_;
2907    &process_command($counters_rx, $tmp) if ($tmp =~ /$counters_rx/);
2908    $_ = $tmp; undef $tmp;
2909    $_
2910}
2911
2912sub find_end_env {
2913    # MRO: find_end_env($env,$contents,$rest)
2914    #local ($env, *ref_contents, *rest) = @_;
2915    my $env = $_[0];
2916    my $be_rx = &make_begin_end_env_rx($env);
2917    my $count = 1;
2918
2919    while ($_[2] =~ /($be_rx)(\n?)/s) { # $rest
2920	$_[1] .= $`; # $contents
2921
2922	if ($2 eq "begin") { ++$count }
2923	else { --$count };
2924
2925	#include any final \n at an {end} only
2926	$_[2] = (($2 eq 'end')? $5 : '') . $'; # $rest
2927	last if $count == 0;
2928
2929	$_[1] .= $1; # $contents
2930    }
2931
2932    if ($count != 0) {
2933	$_[2] = join('', $_[1], $_[2]); # $rest = join('', $contents, $rest);
2934	$_[1] = ''; # $contents
2935	return(0)
2936    } else { return(1) }
2937}
2938
2939
2940sub process_group_env {
2941    local($contents) = @_;
2942    local(@save_open_tags) = @$open_tags_R;
2943    local($open_tags_R) = [ @save_open_tags ];
2944    print STDOUT "\nIN::{group $br_id}" if ($VERBOSITY > 4);
2945    print STDOUT "\n:$contents\n" if ($VERBOSITY > 6);
2946
2947    # need to catch explicit local font-changes
2948    local(%font_size) = %font_size if (/\\font\b/);
2949
2950    # record class/id info for a style-sheet entry
2951    local($env_id, $tmp, $etmp);
2952    if (($USING_STYLES) && !$PREAMBLE ) { $env_id = $br_id; }
2953#	$env_id = "grp$br_id";
2954#	$styleID{$env_id} = " ";
2955#        $env_id = " ID=\"$env_id\"";
2956#    }
2957
2958    undef $_;
2959    $contents =~ s/^\s*$par_rx\s*//s; # don't start with a \par
2960    if ($contents =~ /^\s*\\($image_switch_rx)\b\s*/s) {
2961	# catch TeX-like environments: {\fontcmd ... }
2962	local($image_style) = $1;
2963	if ($USING_STYLES) {
2964	    $env_style{$image_style} = " " unless ($env_style{$image_style});
2965	}
2966	local($switch_cmd) = "do_cmd_${image_style}";
2967	if (defined &$switch_cmd ) {
2968	    eval "\$contents = \&${switch_cmd}(\$')";
2969	    print "\n*** &$switch_cmd didn't work: $@\n$contents\n\n" if ($@);
2970	} elsif ($contents =~ /$par_rx/) {
2971	    # split into separate image for each paragraph
2972	    local($par_style,$this_par_img) = '';
2973	    local(@par_pieces) = split($par_rx, $contents);
2974	    local($this_par,$par_style,$par_comment);
2975	    $contents = '';
2976	    while (@par_pieces) {
2977		$this_par = shift @par_pieces;
2978		if ($this_par =~ /^\s*\\($image_switch_rx)\b/s) {
2979		    $image_style = $1;
2980		    $par_style = 'P.'.$1;
2981		    $env_style{$par_style} = " " unless ($env_style{$par_style});
2982		}
2983#	no comment: source is usually too highly encoded to be meaningful
2984#	$par_comment = &make_comment($image_style,$this_par);
2985		$this_par_img = &process_in_latex("\{".$this_par."\}");
2986		$contents .=  join(''  #,"\n", $par_comment
2987			, "\n<P"
2988			, (($USING_STYLES && $image_style)? " CLASS=\"$image_style\"" :'')
2989			,">", $this_par_img
2990			, "</P>\n");
2991		if (@par_pieces) {
2992		    # discard the pieces from matching  $par_rx
2993		    $dum = shift @par_pieces;
2994		    $dum = shift @par_pieces;
2995		    $dum = shift @par_pieces;
2996		    $dum = shift @par_pieces;
2997		    $dum = shift @par_pieces;
2998		    $dum = shift @par_pieces;
2999#		    $contents .= "\n</P>\n<P>";
3000		}
3001	    }
3002	} else {
3003	    $contents = &process_undefined_environment("tex2html_accent_inline"
3004		, ++$global{'max_id'},"\{".$contents."\}");
3005        }
3006    } elsif ($contents =~ /^\s*\\(html)?url\b($O\d+$C)[^<]*\2\s*/) {
3007	# do nothing
3008	$contents = &translate_environments($contents);
3009	$contents = &translate_commands($contents);
3010    } elsif (($env_switch_rx)&&($contents =~ s/^(\s*)\\($env_switch_rx)\b//s)) {
3011	# write directly into images.tex, protected by \begingroup...\endgroup
3012	local($prespace, $cmd, $tmp) = ($1,$2,"do_cmd_$2");
3013	$latex_body .= "\n\\begingroup ";
3014	if (defined &$tmp) {
3015	    eval("\$contents = &do_cmd_$cmd(\$contents)");
3016	}
3017	$contents = &translate_environments($contents);
3018	$contents = &translate_commands($contents);
3019	undef $tmp; undef $cmd;
3020	$contents .= "\n\\endgroup ";
3021    } elsif ($contents =~ /^\s*\\([a-zA-Z]+)\b/s) {
3022	local($after_cmd) = $';
3023	local($cmd) = $1; $tmp = "do_cmd_$cmd"; $etmp = "do_env_$cmd";
3024	if (($cmd =~/^(rm(family)?|normalsize)$/)
3025		||($declarations{$cmd}&&(defined &$tmp))) {
3026	    do{
3027		local(@save_open_tags) = @$open_tags_R;
3028		eval "\$contents = \&$tmp(\$after_cmd);";
3029		print "\n*** eval &$tmp failed: $@\n$contents\n\n" if ($@);
3030		$contents .= &balance_tags();
3031	    };
3032	} elsif ($declarations{$cmd}&&(defined &$etmp)) {
3033	    eval "\$contents = \&$etmp(\$after_cmd);";
3034	} else {
3035	    $contents = &translate_environments($contents);
3036	    $contents = &translate_commands($contents)
3037		if ($contents =~ /$match_br_rx/o);
3038	    # Modifies $contents
3039	    &process_command($single_cmd_rx,$contents) if ($contents =~ /\\/o);
3040	}
3041	undef $cmd; undef $tmp; undef $etmp;
3042    } else {
3043	$contents = &translate_environments($contents);
3044	$contents = &translate_commands($contents)
3045	    if ($contents =~ /$match_br_rx/o);
3046        # Modifies $contents
3047	&process_command($single_cmd_rx,$contents)
3048	    if ($contents =~ /\\/o);
3049    }
3050    $contents . &balance_tags();
3051}
3052
3053# MODIFIES $contents
3054sub process_environment {
3055    local($opt, $env, $id, $styles) = @_;
3056
3057    local($envS) = $env; $envS =~ s/\*\s*$/star/;
3058    local($env_sub,$border,$attribs,$env_id) = ("do_env_$envS",'','','');
3059    local($original) = $contents;
3060
3061    if ($env =~ /tex2html_deferred/ ) {
3062	$contents = &do_env_tex2html_deferred($contents);
3063	return ($contents);
3064    }
3065    $env_id = &read_style_info($opt, $env, $id, $styles)
3066	if (($USING_STYLES)&&($opt));
3067
3068    if (&defined_env($env)) {
3069	print STDOUT ",";
3070	print STDOUT "{$env $id}" if ($VERBOSITY > 1);
3071#	$env_sub =~ s/\*$/star/;
3072	$contents = &$env_sub($contents);
3073
3074    } elsif ($env =~ /tex2html_nowrap/) {
3075	#pass it on directly for LaTeX, via images.tex
3076	$contents = &process_undefined_environment($env, $id, $contents);
3077	return ($contents);
3078
3079#    elsif (&special_env) {	# &special_env modifies $contents
3080    } else {
3081	local($no_special_chars) = 0;
3082	local($failed) = 0;
3083	local($has_special_chars) = 0;
3084	&special_env; #  modifies $contents
3085	print STDOUT "\n<MATH $env$id $contents>" if ($VERBOSITY > 3);
3086	if ($failed || $has_special_chars) {
3087	    $contents = $original;
3088	    $failed = 1;
3089	    print STDOUT " !failed!\n" if ($VERBOSITY > 3);
3090        }
3091    }
3092    if (($contents) && ($contents eq $original)) {
3093        if ($ignore{$env}) {  return(''); }
3094        # Generate picture
3095	if ($contents =~ s/$htmlborder_rx//o) {
3096	    $attribs = $2; $border = (($4)? "$4" : 1)
3097	} elsif ($contents =~ s/$htmlborder_pr_rx//o) {
3098	    $attribs = $2; $border = (($4)? "$4" : 1)
3099	}
3100	$contents = &process_undefined_environment($env, $id, $contents);
3101	$env_sub = "post_latex_$env_sub"; # i.e. post_latex_do_env_ENV
3102        if ( defined &$env_sub) {
3103	    $contents = &$env_sub($contents);
3104	} elsif (($border||($attributes))&&($HTML_VERSION > 2.1)) {
3105	    $contents = &make_table($border,$attribs,'','','',$contents);
3106	} else {
3107	    $contents = join('',"<BR>\n",$contents,"\n<BR>")
3108	        unless (!($contents)||($inner_math)||($env =~
3109	              /^(tex2html_wrap|tex2html_nowrap|\w*math|eq\w*n)/o ));
3110	}
3111    }
3112    $contents;
3113}
3114
3115
3116#RRM: This reads the style information contained in the optional argument
3117#   to the \begin command. It is stored to be recovered later as an entry
3118#   within the automatically-generated style-sheet, if $USING_STYLES is set.
3119# Syntax for this info is:
3120#   <style names> ; <extra style-info>
3121
3122sub read_style_info {
3123    local($opt, $envS, $id, $styles) = @_;
3124    return() unless (($opt)&&($USING_STYLES));
3125    # allow macro-expansion within the style-info
3126    $opt = &translate_commands($opt) if ($opt =~ /\\/);
3127
3128    # record class/id info for a style-sheet entry
3129    local($style_names, $style_extra, $env_id)=(''," ",'');
3130    if ($opt) {
3131	# if there is a `;'  then <names> ; <extra>
3132	if ($styles =~ /^\s*([^\|]*)\|\s*(.*)$/) {
3133	    $style_names = $1; $style_extra = $2;
3134	    if ($style_names =~ /[=:;]/) {
3135		# cannot be <names>, so is <extra>
3136		$style_extra = $style_names.$style_extra;
3137		$style_names = '';
3138	    }
3139	} elsif ($styles =~ /[\=\:]/) {
3140	    # cannot be <names>, so is <extras>
3141	    $style_extra = $styles;
3142	} else { $style_names = $styles }
3143	$style_extra =~ s/\s*[=:]\s*/ : /go;
3144	$style_extra =~ s/([\w,\-]+)\s+([\w,\-]+)/$1 ; $2/go;
3145	$style_extra =~ s/\s*,\s*/ /go;
3146
3147	if ($style_names) {
3148	    local($sname);
3149	    local(@names) = split ( /\s+/ , $style_names );
3150	    # ensure a style-sheet entry for each new name
3151	    foreach $sname (@names) {
3152		$env_style{$sname} = " "
3153		    unless (($env_style{$sname})||($sname =~ /^\s*$/));
3154	    }
3155	}
3156    }
3157    # remove uninformative part of internally-defined env names
3158    $envS =~ s/tex2html_(\w+_)?(\w+)/$2/; $envS =~ s/preform/pre/;
3159    $env_id = $envS.$id;
3160    $styleID{$env_id} = $style_extra unless ($PREAMBLE);
3161
3162    if ($style_names) { $envS = "$style_names" }
3163    elsif (($envS =~ /^pre$/)&&
3164	(/^\\begin.*preform($O|$OP)\d+($C|$CP)$verbatim_mark(\w*[vV]erbatim)(\*?)/))
3165	    { $envS = $3.($4 ? 'star' : '') };
3166    $env_style{$envS} = " " unless (($style_names)||($env_style{$envS}));
3167    $env_id = " ID=\"$env_id\"".(($envS) ? " CLASS=\"$envS\"" : '');
3168    return($env_id);
3169}
3170
3171# RRM: This provides the mechanism to save style information in %env_style
3172#      using LaTeX macros  \htmlsetstyle  and  \htmladdtostyle
3173#
3174sub process_htmlstyles {
3175    local($mode, $_) = @_;
3176    local($pre_tags) = &get_next_optional_argument;
3177    local($class) = &missing_braces unless (
3178        (s/$next_pair_pr_rx/$class = $2;''/e)
3179        ||(s/$next_pair_rx/$class = $2;''/e));
3180    local($sinfo) = &missing_braces unless (
3181        (s/$next_pair_pr_rx/$sinfo = $2;''/e)
3182        ||(s/$next_pair_rx/$sinfo = $2;''/e));
3183    return ($_) unless ($class||$pre_tags);
3184
3185    $class = $pre_tags.($class ?'.':'').$class;
3186    $sinfo =~ s/\s*[:=]\s*/ : /g;
3187    $sinfo =~ s/\s*,\s*/ /g;
3188    if ($mode =~ /add/) {
3189    	$sinfo = '; '.$sinfo if ($env_style{$class});
3190	$env_style{$class} .= $sinfo;
3191    } else { $env_style{$class} = $sinfo }
3192    $_;
3193}
3194sub do_cmd_htmlsetstyle   { &process_htmlstyles('set',@_) }
3195sub do_cmd_htmladdtostyle { &process_htmlstyles('add',@_) }
3196
3197
3198# The $<$, $>$, $|$ and $=>$, etc strings are replaced with their textual
3199# equivalents instead of passing them on to latex for processing in math-mode.
3200# This will not be necessary when the mechanism for passing environments
3201# to Latex is improved.
3202# RETURNS SUCCESS OR FAILURE
3203sub special_env {
3204    # Modifies $contents in its caller
3205    local($next)='';
3206    local ($allow) = $HTML_VERSION ge '3.0' ?
3207	 "[^#\$%&~\\\\{}]|\\limits" : "[^^#\$%&~_\\\\{}]";
3208    #JKR: Use italics instead of bold #HWS: Generalize to include more symbols.
3209#    $contents =~ s/^\$(\s*($html_specials_inv_rx|$allow)*\s*)\$(.)?/
3210#	$next=$3;&simple_math_env($1).(($next =~ m|\w|)? " ":'').$next/ige;
3211    $contents =~ s/^\$(\s*($html_specials_inv_rx|$allow)*\s*)\$$/
3212	&simple_math_env($1)." "/ige;
3213    if ($contents =~ /\&\w*;/) { $has_math_chars=1 }
3214    if ($contents =~ /;SPM([a-zA-Z]+);/) { $has_special_chars=1 };
3215}
3216
3217# Translate simple math environments into italic.
3218# Only letters should become italic; symbols should stay non-italic.
3219sub simple_math_env {
3220    local($mathcontents) = @_;
3221    if ($mathcontents eq '') { return("$mathcontents"); }
3222    elsif ($NO_SIMPLE_MATH) {  # always make an image
3223	$failed = 1; return($mathcontents);
3224    } elsif ($mathcontents =~ /\\/) { # any macro kills "simple-math"
3225	local($save_math) = $mathcontents;
3226	local(@text_only) = ();
3227	while ((!$failed)&&($mathcontents =~
3228		/\\((boldsymbol|bm)|(math|text)(bf|rm|it|tt)|times|[{}@#^_])(\b|[^A-Za-z]|$)/)) {
3229	    # ...except when only simple styles
3230	    push (@text_only, $`, ("$2$4" ? "\\simplemath".($4 ? $4 :"bf") :"\\$1") );
3231	    $mathcontents = $5.$';
3232	    $failed = 1 if ($` =~ /\\/);
3233	}
3234	$failed = 1 if ($mathcontents =~ /\\/);
3235	return($save_math) if $failed;
3236	$mathcontents = join('',@text_only,$mathcontents);
3237    }
3238    # Is there a problem here, with nested super/subscripts ?
3239    # Yes, so do each pattern-match for bracketings within a while-loop
3240    while ($mathcontents =~ s/\^$any_next_pair_rx/<SUP>$2<\/SUP>/go){};
3241    while ($mathcontents =~ s/\^$any_next_pair_pr_rx/<SUP>$2<\/SUP>/go){};
3242    while ($mathcontents =~ s/_$any_next_pair_rx/<SUB>$2<\/SUB>/g){};
3243    while ($mathcontents =~ s/_$any_next_pair_pr_rx/<SUB>$2<\/SUB>/g){};
3244
3245    $mathcontents =~ s/\^(\\[a-zA-Z]+|.)/<SUP>$1<\/SUP>/g;
3246    $mathcontents =~ s/_(\\[a-zA-Z]+|.)/<SUB>$1<\/SUB>/g;
3247    $mathcontents =~ s/(^|\s|[,;:'\?\.\[\]\(\)\+\-\=\!>]|[^\\<]\/|\d)(<(I|TT|B)>)?([a-zA-Z]([a-zA-Z ]*[a-zA-Z])?)(<\/\3>)?/
3248	$1.(($2)? $2 :'<I>').$4.(($6)? $6 : '<\/I>')/eig;
3249
3250    $mathcontents =~ s/\\times($|\b|[^A-Za-z])/ x $1/g;
3251    $mathcontents =~ s/\\times($|\b|[^A-Za-z])/ x $1/g;
3252    $mathcontents =~ s/\\\\/<BR>\n/g;
3253    $mathcontents =~ s/\\\\/<BR>\n/g;
3254    $mathcontents =~ s/\\([,;])/ /g;
3255    $mathcontents =~ s/\\(\W)/$1/g;
3256    $mathcontents =~ s/ {2,}/ /g;
3257
3258    # any simple style changes remove enclosed <I> tags
3259    $mathcontents = &translate_commands($mathcontents)
3260	if ($mathcontents =~ /\\/);
3261
3262    $mathcontents =~ s/<I><\/(SUB|SUP)>/<\/$1><I>/g;
3263    $mathcontents =~ s/<(SUB|SUP)><\/I>/<\/I><$1>/g;
3264    $mathcontents =~ s/;<I>SPM([a-zA-Z]+)<\/I>;/;SPM$1;/go;
3265    $mathcontents =~ s/<(\/?)<I>(SUB|SUP|I|B|TT)<\/I>>/<$1$2>/g;
3266    $mathcontents =~ s/<\/(B|I|TT)><\1>//g;
3267    $mathcontents;
3268}
3269
3270sub do_cmd_simplemathrm {
3271    local ($_) = @_;
3272    local($text);
3273    $text = &missing_braces unless (
3274        (s/$next_pair_pr_rx/$text = $2;''/e)
3275        ||(s/$next_pair_rx/$text = $2;''/e));
3276    $text =~ s/<\/?I>//g;
3277    join('', $text, $_)
3278}
3279sub do_cmd_simplemathbf {
3280    local ($_) = @_;
3281    local($text);
3282    $text = &missing_braces unless (
3283        (s/$next_pair_pr_rx/$text = $2;''/e)
3284        ||(s/$next_pair_rx/$text = $2;''/e));
3285    $text =~ s/<\/?I>//g;
3286    join('','<B>', $text, '</B>', $_)
3287}
3288sub do_cmd_simplemathtt {
3289    local ($_) = @_;
3290    local($text);
3291    $text = &missing_braces unless (
3292        (s/$next_pair_pr_rx/$text = $2;''/e)
3293        ||(s/$next_pair_rx/$text = $2;''/e));
3294    $text =~ s/<\/?I>//g;
3295    join('','<TT>', $text, '</TT>', $_)
3296}
3297
3298sub process_math_in_latex {
3299    local($mode,$style,$level,$math) = @_;
3300    local(@anchors);
3301    if ($level) {
3302	$style = (($level > 1) ? "script" : "") . "script";
3303    } elsif (! $style) {
3304	$style = (($mode =~/display|equation/)? "display" : "")
3305    }
3306    $style = "\\${style}style" if ($style);
3307
3308    #  &process_undefined_environment  changes $_ , so save it.
3309    local($after) = $_;
3310
3311    # the 'unless' catches nested AMS-aligned environments
3312    $mode = "tex2html_wrap_" .
3313	(($mode =~/display|equation|eqnarray/) ? 'indisplay' : 'inline')
3314	    unless ($mode =~ /^equationstar/ && $outer_math =~ /^equationstar/);
3315
3316    $global{'max_id'}++;
3317    $math =~ s/\\(\n|$)/\\ $1/g;	# catch \ at end of line or string
3318    $math =~ s/^\s*((\\!|;SPMnegsp;)\s*)*//g;		# remove neg-space at start of string
3319    if ($mode =~ /tex2html_wrap_/ ) {
3320	$math = &process_undefined_environment( $mode
3321	    , $global{'max_id'}, join('', "\$$style ", $math, "\$"));
3322    } else {
3323	# some AMS environments must be within {equation} not {displaymath}
3324	$math =~ s/displaymath/equation*/
3325		if ($math =~ /\\begin\{(x+|fl)*align/);
3326	$math = &process_undefined_environment($mode, $global{'max_id'}, $math);
3327    }
3328    $math .= "\n" if ($math =~ /$comment_mark\s*\d+$/s);
3329    $_ = $after;
3330    # the delimiter \001 inhibits an unwanted \n at image-replacement
3331    $math . ($math =~ /$image_mark/? "\001" : '');
3332}
3333
3334#RRM: Explicit font switches need images. Use the image_switch mechanism.
3335sub do_cmd_font {
3336    local($_) = @_;
3337    local($fontinfo,$fontname,$size) = ('','','10pt');
3338    s/\s*\\(\w+)\s*=?\s*(.*)(\n|$)/$fontname=$1;$fontinfo=$2;''/eo;
3339    $image_switch_rx .= "|$fontname";
3340
3341    if ($fontinfo =~ /([.\d]+\s*(true)?(pt|mm|cm))/ ) { $size = $1 }
3342    elsif ( $fontinfo =~ /[a-zA-Z]+(\d+)\b/ ) { $size = $1.'pt' }
3343    if  ( $fontinfo =~ /(scaled|at)\s*\\?(.+)/) { $size .= " scaled $1" }
3344    $font_size{$fontname} = $size;
3345    $_;
3346}
3347sub wrap_cmd_font {
3348    local($cmd, $_) = @_;
3349    local ($args, $dummy, $pat) = "";
3350    if (/\n/) { $args .= $`.$& ; $_ = $' } else {$args = $_; $_ = ''};
3351    (&make_deferred_wrapper(1).$cmd.$padding.$args.&make_deferred_wrapper(0),$_)
3352}
3353
3354sub do_cmd_newfont {
3355    local($_) = @_;
3356    local($fontinfo,$fontname,$size) = ('','','10pt');
3357    $fontname = &missing_braces unless (
3358	(s/$next_pair_pr_rx/$fontname=$2;''/eo)
3359	||(s/$next_pair_rx/$fontname=$2;''/eo));
3360    $fontname=~ s/^\s*\\|\s*$//g;
3361    $image_switch_rx .= "|$fontname";
3362
3363    $fontinfo = &missing_braces unless (
3364	(s/$next_pair_pr_rx/$fontinfo=$2;''/eo)
3365	||(s/$next_pair_rx/$fontinfo=$2;''/eo));
3366    if ($fontinfo =~ /([.\d]+\s*(true)?(pt|mm|cm))/ ) { $size = $1 }
3367    elsif ( $fontinfo =~ /[a-zA-Z]+(\d+)\b/ ) { $size = $1.'pt' }
3368    if  ( $fontinfo =~ /(scaled|at)\s*\\?(.+)/) { $size .= " scaled $1" }
3369    $font_size{$fontname} = $size;
3370    $_;
3371}
3372
3373sub defined_env {
3374    local($env) = @_;
3375    $env =~ s/\*$/star/;
3376    local($env_sub) = ("do_env_$env");
3377    # The test using declarations should not be necessary but 'defined'
3378    # doesn't seem to recognise subroutines generated dynamically using 'eval'.
3379    # Remember that each entry in $declarations generates a dynamic prodedure ...
3380    ((defined &$env_sub) || ($declarations{$env}));
3381}
3382
3383# RRM: utility to add style information to stored image-parameters
3384#      currently only (math) scaling info is included;
3385#      current color, etc.  could also be added here.
3386sub addto_encoding {
3387    local($env, $contents) = @_;
3388#    $contents =~ s/(\\(begin|end)\s*)?<<\d*>>|\n//g;	# RRM: remove env delimiters
3389    $contents =~ s/(\\(begin|end)\s*(<<\d*>>))|\n//g;	# RRM: remove env delimiters
3390    # append scaling information for environments using it
3391    if (($MATH_SCALE_FACTOR)
3392	&&(($contents =~ /makeimage|inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/)
3393	   ||($env =~ /makeimage|inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/))
3394	) { $contents .= ";MSF=$MATH_SCALE_FACTOR" }
3395
3396    if ($LATEX_FONT_SIZE =~ /([\d\.]+)pt/) {
3397	local($fsize) = $1;
3398	$contents .= ";LFS=$fsize" unless ($fsize ==10);
3399    }
3400
3401    if (($EXTRA_IMAGE_SCALE)
3402	&&(($contents =~ /makeimage|inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/)
3403	   ||($env =~ /makeimage|inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/))
3404	) { $contents .= ";EIS=$EXTRA_IMAGE_SCALE" }
3405
3406    if (($DISP_SCALE_FACTOR)
3407	&&(($contents =~ /indisplay|displaymath|eqnarray|equation/)
3408	   ||($env =~ /indisplay|displaymath|eqnarray|equation/))
3409	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
3410	) { $contents .= ";DSF=$DISP_SCALE_FACTOR" }
3411
3412    if (($EQN_TAGS)
3413	&&(($env =~ /eqnarray($|[^_\*])|equation/)
3414	   ||($contents =~ /eqnarray($|[^_\*])|equation/))
3415	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
3416	) { $contents .= ";TAGS=$EQN_TAGS" }
3417
3418    if (($FIGURE_SCALE_FACTOR)
3419	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
3420	&&(($contents =~ /figure/)||($env =~ /figure/))
3421	) { $contents .= ";FSF=$FIGURE_SCALE_FACTOR"}
3422
3423    if (($ANTI_ALIAS)
3424	&&(($contents =~ /figure/)||($env =~ /figure/))
3425	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
3426	) { $contents .= ";AAF" }
3427    elsif ($ANTI_ALIAS_TEXT) { $contents .= ";AAT" }
3428    if (!$TRANSPARENT_FIGURES) { $contents .= ";NTR" }
3429
3430    $contents;
3431}
3432
3433sub process_undefined_environment {
3434    local($env, $id, $contents) = @_;
3435    if ($env =~ s/\*{2,}/*/) { print "\n*** $_[0] has too many \*s ***"};
3436
3437    local($name,$cached,$raw_contents,$uucontents) = ("$env$id");
3438    $name =~ s/\*/star/;
3439    local($oldimg,$size,$fullcontents,$imgID);
3440    return if ($AUX_FILE);
3441
3442    # catch \footnotemark within an image, especially if in math
3443    local(@foot_anchors,$foot_anchor);
3444    local($im_footnote,$im_mpfootnote) = ($global{'footnote'},$global{'mpfootnote'});
3445    @foot_anchors = &process_image_footnote($contents)
3446	if ($contents =~ /\\footnote(mark)?\b/s);
3447    if ((@foot_anchors)&&($eqno)) {
3448	# append the markers to the equation-numbers
3449	$eqno .= join(' ', ' ', @foot_anchors);
3450	@foot_anchors = ();
3451    }
3452
3453    print STDOUT "\nUNDEF-IN {$env $id}:\n$contents\n" if ($VERBOSITY > 4);
3454    #RRM - LaTeX commands wrapped with this environment go directly into images.tex.
3455    if ($env =~ /tex2html_nowrap|^lrbox$/){ # leave off the wrapper, do not cache
3456	# totally ignore if in preamble...
3457	# ...since it will be put into  images.tex  anyway!!
3458	if (!($PREAMBLE)) {
3459	    $contents =~ s/^\n+|\n+$/\n/g;
3460	    local($lcontents) = join('', "\\begin{$env}", $contents , "\\end{$env}" );
3461	    $lcontents =~ s/\\(index|label)\s*(($O|$OP)\d+($C|$CP)).*\2//sg;
3462	    print STDOUT "pre-LATEX {$env}:\n$lcontents\n" if ($VERBOSITY > 3);
3463	    $raw_contents = &revert_to_raw_tex($lcontents);
3464	    print STDOUT "LATEX {$env}:\n$raw_contents\n" if ($VERBOSITY > 3);
3465	    $latex_body .= "\n$raw_contents"."%\n\n" ;
3466	}
3467	return("") if ($env =~ /^lrbox/);
3468	# ignore enclosed environments; e.g. in  \settolength  commands
3469#	$contents = &translate_environments($contents); # ignore environments
3470#	$contents = &translate_commands($contents);
3471	# ...but apply any Perl settings that may be defined
3472	$contents = &process_command($single_cmd_rx,$contents);
3473	print STDOUT "\nOUT {$env $id}:\n$contents\n" if ($VERBOSITY > 4);
3474	return("");
3475    }
3476    # catch pre-processor environments
3477    if ($PREPROCESS_IMAGES) {
3478	local($pre_env,$which, $done, $indic);
3479	while ($contents =~ /$pre_processor_env_rx/) {
3480	    $done .= $`; $pre_env = $5; $which =$1; $contents = $';
3481	    if (($which =~ /begin/)&&($pre_env =~ /indica/)) {
3482		if ($contents =~ s/^\[(\w+)]//o) { $done .= '#'.$1 }
3483	    } elsif (($which =~ /end/)&&($pre_env =~ /indica/)) {
3484		$done .= '#NIL';
3485	    } elsif (($which =~ /begin/)&&($pre_env =~ /itrans/)) {
3486		if ($contents =~ s/^\[(\w+)]/$indic=$1;''/e)
3487	            { $done .= "\#$indic" }
3488	    } elsif (($which =~ /end/)&&($pre_env =~ /itrans/)) {
3489		$done .= "\#end$indic";
3490	    } elsif ($which =~ /begin/) {
3491		$done .= (($which =~ /end/)? $end_preprocessor{$pre_env}
3492		          : $begin_preprocessor{$pre_env} )
3493	    }
3494	}
3495	$contents = $done . $contents;
3496    }
3497    $fullcontents =  $contents; # save for later \label search.
3498    # MRO: replaced $* with /m
3499    $contents =~ s/\n?$labels_rx(\%([^\n]+$|$EOL))?/\n/gm;
3500
3501    local($tmp) = $contents;
3502    $tmp =~ s/^((\\par|\%)?\s*\n)+$//g;
3503    return( &do_labels($fullcontents, "\&nbsp;") ) unless $tmp;
3504
3505    # just a comment as the contents of a cell in a math-display
3506    if ($tmp =~ /\$\\(display|text|(script)+)style\s*$comment_mark\d+\s*\$$/)
3507	{ return ( &do_labels($fullcontents, "\&nbsp;") ) };
3508
3509    $contents = "\n% latex2html id marker $id\n$contents" if
3510	(!$PREAMBLE &&($contents =~ /$order_sensitive_rx/)
3511		&&(!($env =~ /makeimage/)));
3512
3513    $env =~ s/displaymath/equation*/
3514	if ($contents =~ /\\begin\{(x+|fl)*align/);
3515    #RRM: include the inline-color, when applicable
3516    $contents = join(''
3517	    , (($inner_math =~ /in(display|line)/) ? '$' : '')
3518	    , "\\begin{$env}"
3519	    , ($color_env ? "\\bgroup\\$color_env" : '')
3520	    , $contents , ($color_env ? "\\egroup" : '')
3521	    , "\\end{$env}"
3522	    , (($inner_math =~ /in(display|line)/) ? '$' : '')
3523	) if ($contents);
3524
3525    # append to the name of special environments found within math
3526    if ($inner_math) {
3527	local($ext) = $inner_math;
3528	if ($inner_math =~ /(display|line)/){ $ext = 'in'.$1;};
3529	$name =~ s/(\d+)$/_$ext$1/;
3530    }
3531
3532    if (!($latex_body{$name} = $contents)) {
3533	print "\n *** code for $name is too long ***\n"}
3534    if ($contents =~ /$htmlimage_rx/) {
3535	$uucontents = &special_encoding($env,$2,$contents);
3536    } elsif ($contents =~ /$htmlimage_pr_rx/) {
3537	$uucontents = &special_encoding($env,$2,$contents);
3538    } else {
3539	$uucontents = &encode(&addto_encoding($env,$contents));
3540    }
3541    $cached = $cached_env_img{$uucontents};
3542    print STDOUT "\nCACHED: $uucontents:\n$cached\n" if ($VERBOSITY > 4);
3543    if ($NOLATEX) {
3544	$id_map{$name} = "[$name]";
3545    } elsif (defined ($_ = $cached)) { # Is it in our cache?
3546	# Have we already used it?
3547	if (($oldimg) = /SRC="$PREFIX$img_rx\.$IMAGE_TYPE"/o) {
3548	    # No, check its size
3549	    local($eis) = 1;
3550	    # Does it have extra scaling ?
3551	    if ($uucontents =~ /EIS=(.*);/) { $eis = $1 }
3552	    ($size, $imgID) = &get_image_size("$PREFIX$oldimg.old", $eis);
3553	    # Does it have extra scaling ?
3554#	    if ($uucontents =~ /EIS=(.*);/) {
3555#		local($eis) = $1; local($w,$h);
3556#		# quotes will not be there with HTML 2.0
3557#		$size =~ s/(WIDTH=\")(\d*)(\".*HEIGHT=\")(\d*)\"/
3558#		    $w = int($2\/$eis + .5); $h=int($4\/$eis + .5);
3559#		    "$1$w$3$h\""/e ; # insert the re-scaled size
3560#	    }
3561	    # quotes will not be there with HTML 2.0
3562	    $size =~ s/\"//g if ($HTML_VERSION < 2.2);
3563	    if ($size && /\s$size\s/) {
3564		# Size is OK; recycle it!
3565		++$global_page_num;
3566		$_ = $cached ;    # ...perhaps restoring the desired size.
3567		s/(${PREFIX}T?img)\d+\.($IMAGE_TYPE|html)/
3568			&rename_html($&,"$1$global_page_num.$2")/geo;
3569	    } else {
3570		if ($env =~ /equation/) { &extract_eqno($name,$cached) }
3571		$_ = "";				# The old Image has wrong size!
3572		undef($cached);			#  (or it doesn't exist)
3573	    }
3574	}
3575	s/(IMG\n)/$1$imgID/ if $imgID;
3576
3577	s/$PREFIX$img_rx\.new/$PREFIX$1.$IMAGE_TYPE/go; # Point to the actual image file(s)
3578	$id_map{$name} = $_;
3579	s/$PREFIX$img_rx\.$IMAGE_TYPE/$PREFIX$1.new/go;	# But remember them as used.
3580	$cached_env_img{$uucontents} = $_;
3581    }
3582
3583    if (! defined($cached)) {				# Must generate it anew.
3584	&clear_images_dbm_database
3585	    unless ($new_page_num ||($NO_SUBDIR && $FIXEDDIR));
3586	$new_id_map{$name} = $id_map{$name} = ++$global_page_num . "#" .
3587	    ++$new_page_num;
3588	$orig_name_map{$id_map{$name}} = $name;
3589	$cached_env_img{$uucontents} = $id_map{$name} if ($REUSE == 2);
3590
3591	#RRM: this (old) code frequently crashes NDBM, so do it in 2 steps
3592#	$img_params{$name} = join('#', &extract_parameters($contents));
3593	local(@params) = &extract_parameters($contents);
3594	$img_params{$name} = join('#',@params); undef $params;
3595	print "\nIMAGE_PARAMS $name: ".$img_params{$name} if ($VERBOSITY > 3);
3596
3597	$contents =~ s/\\(index|label)\s*(($O|$OP)\d+($C|$CP)).*\2//sg;
3598	print STDOUT "\nLATEX {$env}:\n$contents" if ($VERBOSITY > 3);
3599	$raw_contents = &revert_to_raw_tex($contents) unless ($contents =~ /^\s*$/) ;
3600	$raw_contents =~ s/\\pagebreak|\\newpage|\\clearpage/\\\\/go;
3601	print STDOUT "\nLATEX {$env}:\n$raw_contents\n" if ($VERBOSITY > 3);
3602	local($box_type) = '';
3603	if ($raw_contents =~ /\\special\s*\{/) {
3604	    $tex_specials{$name} = "1";
3605	    &write_warnings("\nenvironment $name contains \\special commands");
3606	    print STDOUT "\n *** environment $name contains \\special commands ***\n"
3607		if ($VERBOSITY);
3608	} elsif (($env =~ /$inline_env_rx/)||($inner_math =~ /in(line|display)/)) {
3609	    # crop to the marks only... or shave a bit off the bottom
3610	    if (($env =~ /tex2html_[^w]/)||$inner_math) {
3611		# e.g. accents, indic  but not wrap
3612		$crop{$name} = "bl";
3613		$box_type = "i";
3614	    } else {
3615	    # ...or shave a bit off the bottom as well
3616		$crop{$name} = "bls";
3617		$box_type = "h";
3618	    }
3619	} elsif (($env =~ /(eqnarray|equation)(\*|star)/)||($inner_math)) {
3620	    # crop to minimum size...
3621	    $crop{$name} = "blrl";
3622	    $box_type = "v";
3623	} elsif ($env =~ /(picture|tex2html_wrap)(\*|star)?/) {
3624	    # crop hbox to minimum size...
3625	    $crop{$name} = "";
3626	    $box_type = "p";
3627	} elsif ($env =~ /$display_env_rx/) {
3628	    # crop vbox to minimum size...
3629	    $crop{$name} = "blrl" ;
3630	    if ($env =~ /(equation|eqnarray)((s)?$|\d)/) {
3631		# ... unless equation numbers are included ...
3632		if ($3) { #  AMS {subequations}
3633		    $global{'eqn_number'}=$prev_eqn_number if $prev_eqn_number;
3634		    --$global{'eqn_number'};
3635		}
3636		$raw_contents = join('' ,
3637		    (($eqno{$name}||$global{'eqn_number'})?
3638		      &set_equation_counter($eqno{$name}) : '')
3639		    , $raw_contents);
3640		$crop{$name} = "bl" ;
3641	    } elsif ($HTML_VERSION < 2.2) {
3642		# ... HTML 2.0 cannot align images, so keep the full typeset width
3643		$crop{$name} = "bl" ;
3644	    }
3645	    $box_type = "v";
3646	}
3647
3648	#RRM: include the TeX-code for the appropriate type of box.
3649	eval "\$raw_contents = \&make_$box_type"."box($name, \$raw_contents);";
3650
3651	# JCL(jcl-pag) - remember html text if debug is set.
3652	local($_);
3653	if ($DEBUG) {
3654	    $_ = $contents;
3655	    s/\n/ /g;
3656	    $_ = &revert_to_raw_tex($_);
3657	    # incomplete or long commented code can break pre-processors
3658	    if ($PREPROCESS_IMAGES) {
3659		$_ = ((/^(\\\w+)?\{[^\\\}\<]*\}?/)? $& : '').'...' ;
3660		$_ = '{ ... }' if ( length($_) > 100);
3661	    } elsif ( length($_) > 200) {
3662		    $_ = join('',substr($_,0,200),"...\}");
3663	    }
3664	    s/\\(begin|end)/$1/g; s/[\000-\020]//g;
3665	    $_ = join('',"% contents=",$_,"\n");
3666	}
3667	$raw_contents = '\setcounter{equation}{'.$prev_eqn_number."}\n".$raw_contents
3668	    if ($env =~ /subequations/);
3669
3670# JCL(jcl-pag) - build the page entries for images.tex:  Each page is embraced to
3671# let most statements have only local effect. Each page must compile into a
3672# single dvi page to get proper image translation. Hence the invisible glue to
3673# get *at least* one page (raw_contents alone might not wield glue), and
3674# sufficing page length to get *exactly* one page.
3675#
3676	$latex_body .= "{\\newpage\\clearpage\n$_" .
3677#	    "$raw_contents\\hfill\\vglue1pt\\vfill}\n\n";
3678#	    "$raw_contents\\hfill\\vss}\n\n" if ($raw_contents);
3679#	    "$raw_contents\\hfill\\lthtmlcheckvsize\\clearpage}\n\n" if ($raw_contents);
3680	    "$raw_contents\\lthtmlcheckvsize\\clearpage}\n\n" if ($raw_contents);
3681    }
3682    print STDOUT "\nIMAGE_CODE:{$env $id}:\n$raw_contents\n" if ($VERBOSITY > 4);
3683
3684    # Anchor the labels and put a marker in the text;
3685    local($img) = &do_labels($fullcontents,"$image_mark#$name#");
3686    print STDOUT "\nUNDEF_OUT {$env $id}:\n$img\n" if ($VERBOSITY > 4);
3687    return($img) unless (@foot_anchors);
3688
3689    # use the image as source to the 1st footnote, unless it is already an anchor.
3690    if ($img =~ /<\/?A>/) {
3691	join(' ', $img, @foot_anchors);
3692    } elsif ($#foot_anchors ==0) {
3693	$foot_anchor = shift @foot_anchors;
3694	$foot_anchor =~ s/<SUP>.*<\/SUP>/$img/;
3695#	join(' ', $foot_anchor, @foot_anchors);
3696	$foot_anchor;
3697    } else {
3698	join(' ', $img, @foot_anchors);
3699    }
3700}
3701
3702sub special_encoding { # locally sets $EXTRA_IMAGE_SCALE
3703    local($env,$_,$contents) = @_;
3704    local($exscale) = /extrascale=([\.\d]*)/;
3705    local($EXTRA_IMAGE_SCALE) = $exscale if ($exscale);
3706    &encode(&addto_encoding($env,$contents));
3707}
3708
3709
3710sub extract_eqno{
3711    local($name,$contents) = @_;
3712    if ($contents =~ /<P ALIGN="\w+">\(([^<>])\)<\/P>$/) {
3713	if (($eqno{$name})&&!($eqno{$name} eq $1)) {
3714	    &write_warnings("\nequation number for $name may be wrong.")};
3715	$eqno{$name}="$1";
3716    }
3717}
3718sub set_equation_counter{
3719    if ( $global{'eqn_number'}) {
3720	"\\setcounter{equation}{". $global{'eqn_number'} ."}\n"
3721    } else { "\\setcounter{equation}{0}\n" }
3722}
3723
3724# RRM: 3 different types of boxing, for image environments.
3725
3726#	general environments --- crops to width & height
3727sub make_box {
3728    local($id,$contents) = @_;
3729    "\\lthtmlfigureA{". $id ."}%\n". $contents ."%\n\\lthtmlfigureZ\n";
3730}
3731
3732#	inline math --- horizontal mode, captures height/depth + \mathsurround
3733sub make_hbox {
3734    local($id,$contents) = @_;
3735    if ($id =~ /indisplay/) {
3736	"\\lthtmlinlinemathA{". $id ."}%\n". $contents ."%\n\\lthtmlindisplaymathZ\n";
3737    } else {
3738	"\\lthtmlinlinemathA{". $id ."}%\n". $contents ."%\n\\lthtmlinlinemathZ\n";
3739    }
3740}
3741
3742#	inline text-image (e.g. accents) --- horizontal mode, captures height/depth
3743sub make_ibox {
3744    local($id,$contents) = @_;
3745    "\\lthtmlinlineA{". $id ."}%\n". $contents ."%\n\\lthtmlinlineZ\n";
3746}
3747
3748#	centered images (e.g. picture environments) --- horizontal mode
3749sub make_pbox {
3750    local($id,$contents) = @_;
3751    "\\lthtmlpictureA{". $id ."}%\n". $contents ."%\n\\lthtmlpictureZ\n";
3752}
3753
3754#	displayed math --- vertical mode, captures height/depth + page-width
3755sub make_vbox {
3756    local($id,$contents) = @_;
3757    if (($HTML_VERSION >=3.2)&&($id =~/(equation|eqnarray)($|\d)/) &&! $failed ) {
3758	if ($contents =~ s/^\\setcounter\{equation\}\{\d+\}/$&%\n\\lthtmldisplayB\{$id\}%/)
3759	    { $contents ."%\n\\lthtmldisplayZ\n" }
3760	else { "\\lthtmldisplayB{$id}%\n". $contents ."%\n\\lthtmldisplayZ\n" }
3761    } else { "\\lthtmldisplayA{$id}%\n". $contents ."%\n\\lthtmldisplayZ\n"}
3762}
3763
3764sub preprocess_images {
3765    do {
3766	print "\nWriting image.pre file ...\n";
3767	open(ENV,">.$dd${PREFIX}images.pre")
3768            || die "\nCannot write '${PREFIX}images.pre': $!\n";
3769	print ENV &make_latex($latex_body);
3770	print ENV "\n";
3771	close ENV;
3772	&copy_file($FILE, "bbl");
3773	&copy_file($FILE, "aux");
3774	local($num_cmds, $cnt, $this, @cmds);
3775	@cmds = (split ('\n', $preprocessor_cmds));
3776	$this_cmd = $num_cmds = 1+$#cmds;
3777	$cnt = $num_cmds; $preprocessor_cmds = '';
3778	while (@cmds) {
3779	    $this_cmd = shift @cmds; last unless ($this_cmd);
3780	    $this_cmd =~ s/.pre /.tex$cnt / if(($cnt)&&($cnt < $num_cmds));
3781	    $cnt--; $this_cmd .= $cnt if ($cnt);
3782	    $preprocessor_cmds .= $this_cmd."\n";
3783	    L2hos->syswait($this_cmd);
3784	}
3785	# save pre-processor commands in a file:  preproc
3786	open(CMDS,">.$dd${PREFIX}preproc")
3787            || die "\nCannot write '${PREFIX}preproc': $!\n";
3788	print CMDS $preprocessor_cmds ;
3789	close CMDS;
3790
3791    } if ((%latex_body) && ($latex_body =~ /newpage/));
3792}
3793sub make_image_file {
3794    do {
3795	print "\nWriting image file ...\n";
3796	open(ENV,">.$dd${PREFIX}images.tex")
3797            || die "\nCannot write '${PREFIX}images.tex': $!\n";
3798	print ENV &make_latex($latex_body);
3799	print ENV "\n";
3800	close ENV;
3801	&copy_file($FILE, "bbl");
3802	&copy_file($FILE, "aux");
3803    } if ((%latex_body) && ($latex_body =~ /newpage/));
3804}
3805
3806sub make_latex_images{
3807    &close_dbm_database if $DJGPP;
3808    local($dd) = $dd; $dd = '/' if ($dd eq "\\");
3809    local($latex_call) = "$LATEX .$dd${PREFIX}images.tex";
3810    print "$latex_call\n" if (($DEBUG)||($VERBOSITY > 1));
3811    L2hos->syswait($latex_call);
3812    &open_dbm_database if $DJGPP;
3813}
3814
3815sub make_off_line_images {
3816    local($name, $page_num);
3817    if (!$NOLATEX && -f ".${dd}${PREFIX}images.tex") {
3818	&make_tmp_dir;	# sets  $TMPDIR  and  $DESTDIR
3819	$IMAGE_PREFIX =~ s/^_//o if ($TMPDIR);
3820
3821	&make_latex_images();
3822
3823	print "\nGenerating postscript images using dvips ...\n";
3824	&process_log_file(".$dd${PREFIX}images.log"); # Get eqn size info
3825	unless ($LaTeXERROR) {
3826	    local($dvips_call) =
3827		"$DVIPS -S1 -i $DVIPSOPT -o$TMPDIR$dd${IMAGE_PREFIX} .${dd}${PREFIX}images.dvi";
3828	    print "$dvips_call\n" if (($DEBUG)||($VERBOSITY > 1));
3829
3830	    &close_dbm_database if $DJGPP;
3831	    L2hos->syswait($dvips_call) && print "Error: $!\n";
3832	    undef $dvips_call;
3833	    &open_dbm_database if $DJGPP;
3834
3835	    # add suffix .ps to the file-names for each image
3836	    if(opendir(DIR, $TMPDIR || '.')) {
3837                #  use list-context instead; thanks De-Wei Yin <yin@asc.on.ca>
3838	        my (@ALL_IMAGE_FILES) = grep /^$IMAGE_PREFIX\d+$/o, readdir(DIR);
3839	        foreach (@ALL_IMAGE_FILES) {
3840		        L2hos->Rename("$TMPDIR$dd$_", "$TMPDIR$dd$_.ps");
3841	        }
3842	        closedir(DIR);
3843            } else {
3844                print "\nError: Cannot read dir '$TMPDIR': $!\n";
3845            }
3846	}
3847    }
3848    if ($LaTeXERROR) {
3849        print "\n\n*** LaTeXERROR\n"; return();
3850    }
3851
3852    while ( ($name, $page_num) = each %new_id_map) {
3853	# Extract the page, convert and save it
3854	&extract_image($page_num,$orig_name_map{$page_num});
3855    }
3856}
3857
3858# Generate images for unknown environments, equations etc, and replace
3859# the markers in the main text with them.
3860# - $cached_env_img maps encoded contents to image URL's
3861# - $id_map maps $env$id to page numbers in the generated latex file and after
3862# the images are generated, maps page numbers to image URL's
3863# - $page_map maps page_numbers to image URL's (temporary map);
3864# Uses global variables $id_map and $cached_env_img,
3865# $new_page_num and $latex_body
3866
3867
3868sub make_images {
3869    local($name, $contents, $raw_contents, $uucontents, $page_num,
3870	  $uucontents, %page_map, $img);
3871    # It is necessary to run LaTeX this early because we need the log file
3872    # which contains information used to determine equation alignment
3873    if ( $latex_body =~ /newpage/) {
3874	print "\n";
3875	if ($LATEX_DUMP) {
3876	    # dump a pre-compiled format
3877	    if (!(-f "${PREFIX}images.fmt")) {
3878	        print "$INILATEX ./${PREFIX}images.tex\n"
3879		    if (($DEBUG)||($VERBOSITY > 1));
3880	        print "dumping ${PREFIX}images.fmt\n"
3881		    unless ( L2hos->syswait("$INILATEX ./${PREFIX}images.tex"));
3882	    }
3883	    local ($img_fmt) = (-f "${PREFIX}images.fmt");
3884	    if ($img_fmt) {
3885                # use the pre-compiled format
3886	        print "$TEX \"&./${PREFIX}images\" ./${PREFIX}images.tex\n"
3887		    if (($DEBUG)||($VERBOSITY > 1));
3888	        L2hos->syswait("$TEX \"&./${PREFIX}images\" ./${PREFIX}images.tex");
3889	    } elsif (-f "${PREFIX}images.dvi") {
3890	        print "${PREFIX}images.fmt failed, proceeding anyway\n";
3891	    } else {
3892	        print "${PREFIX}images.fmt failed, trying without it\n";
3893		print "$LATEX ./${PREFIX}images.tex\n"
3894		    if (($DEBUG)||($VERBOSITY > 1));
3895		L2hos->syswait("$LATEX ./${PREFIX}images.tex");
3896	    }
3897	} else { &make_latex_images() }
3898#	    local($latex_call) = "$LATEX .$dd${PREFIX}images.tex";
3899#	    print "$latex_call\n" if (($DEBUG)||($VERBOSITY > 1));
3900#	    L2hos->syswait("$latex_call");
3901##	    print "$LATEX ./${PREFIX}images.tex\n" if (($DEBUG)||($VERBOSITY > 1));
3902##	    L2hos->syswait("$LATEX ./${PREFIX}images.tex");
3903##        }
3904	$LaTeXERROR = 0;
3905	&process_log_file("./${PREFIX}images.log"); # Get image size info
3906    }
3907    if ($NO_IMAGES) {
3908        my $img = "image.$IMAGE_TYPE";
3909	my $img_path = "$LATEX2HTMLDIR${dd}icons$dd$img";
3910	L2hos->Copy($img_path, ".$dd$img")
3911            if(-e $img_path && !-e $img);
3912    }
3913    elsif ((!$NOLATEX) && ($latex_body =~ /newpage/) && !($LaTeXERROR)) {
3914   	print "\nGenerating postscript images using dvips ...\n";
3915        &make_tmp_dir;  # sets  $TMPDIR  and  $DESTDIR
3916	$IMAGE_PREFIX =~ s/^_//o if ($TMPDIR);
3917
3918	local($dvips_call) =
3919		"$DVIPS -S1 -i $DVIPSOPT -o$TMPDIR$dd$IMAGE_PREFIX .${dd}${PREFIX}images.dvi\n";
3920	print $dvips_call if (($DEBUG)||($VERBOSITY > 1));
3921
3922	if ((($PREFIX=~/\./)||($TMPDIR=~/\./)) && not($DVIPS_SAFE)) {
3923	    print " *** There is a '.' in $TMPDIR or $PREFIX filename;\n"
3924	    	. "  dvips  will fail, so image-generation is aborted ***\n";
3925	} else {
3926	    &close_dbm_database if $DJGPP;
3927	    L2hos->syswait($dvips_call) && print "Error: $!\n";
3928	    &open_dbm_database if $DJGPP;
3929	}
3930
3931	# append .ps suffix to the filenames
3932	if(opendir(DIR, $TMPDIR || '.')) {
3933            # use list-context instead; thanks De-Wei Yin <yin@asc.on.ca>
3934	    my @ALL_IMAGE_FILES = grep /^$IMAGE_PREFIX\d+$/o, readdir(DIR);
3935	    foreach (@ALL_IMAGE_FILES) {
3936	        L2hos->Rename("$TMPDIR$dd$_", "$TMPDIR$dd$_.ps");
3937	    }
3938	    closedir(DIR);
3939        } else {
3940            print "\nError: Cannot read dir '$TMPDIR': $!\n";
3941        }
3942    }
3943    do {print "\n\n*** LaTeXERROR"; return()} if ($LaTeXERROR);
3944    return() if ($LaTeXERROR); # empty .dvi file
3945    L2hos->Unlink(".$dd${PREFIX}images.dvi") unless $DEBUG;
3946
3947    print "\n *** updating image cache\n" if ($VERBOSITY > 1);
3948    while ( ($uucontents, $_) = each %cached_env_img) {
3949	delete $cached_env_img{$uucontents}
3950	    if ((/$PREFIX$img_rx\.$IMAGE_TYPE/o)&&!($DESTDIR&&$NO_SUBDIR));
3951	$cached_env_img{$uucontents} = $_
3952	    if (s/$PREFIX$img_rx\.new/$PREFIX$1.$IMAGE_TYPE/go);
3953    }
3954    print "\n *** removing unnecessary images ***\n" if ($VERBOSITY > 1);
3955    while ( ($name, $page_num) = each %id_map) {
3956	$contents = $latex_body{$name};
3957	if ($page_num =~ /^\d+\#\d+$/) { # If it is a page number
3958	    do {		# Extract the page, convert and save it
3959		$img = &extract_image($page_num,$orig_name_map{$page_num});
3960		if ($contents =~ /$htmlimage_rx/) {
3961		    $uucontents = &special_encoding($env,$2,$contents);
3962		} elsif ($contents =~ /$htmlimage_pr_rx/) {
3963		    $uucontents = &special_encoding($env,$2,$contents);
3964		} else {
3965		    $uucontents = &encode(&addto_encoding($contents,$contents));
3966		}
3967		if (($HTML_VERSION >=3.2)||!($contents=~/$order_sensitive_rx/)){
3968		    $cached_env_img{$uucontents} = $img;
3969		} else {
3970                    # Blow it away so it is not saved for next time
3971		    delete $cached_env_img{$uucontents};
3972		    print "\nimage $name not recycled, contents may change (e.g. numbering)";
3973		}
3974		$page_map{$page_num} = $img;
3975	    } unless ($img = $page_map{$page_num}); # unless we've just done it
3976	    $id_map{$name} = $img;
3977	} else {
3978	    $img = $page_num;	# it is already available from previous runs
3979	}
3980	print STDOUT " *** image done ***\n" if ($VERBOSITY > 2);
3981    }
3982    &write_warnings(
3983		    "\nOne of the images is more than one page long.\n".
3984		    "This may cause the rest of the images to get out of sync.\n\n")
3985	if (-f sprintf("%s%.3d%s", $IMAGE_PREFIX, ++$new_page_num, ".ps"));
3986    print "\n *** no more images ***\n"  if ($VERBOSITY > 1);
3987    # MRO: The following cleanup seems to be incorrect: The DBM is
3988    # still open at this stage, this causes a lot of unlink errors
3989    #
3990    #do { &cleanup; print "\n *** clean ***\n"  if ($VERBOSITY > 1);}
3991    #	unless $DJGPP;
3992}
3993
3994# MRO: This copies the navigation icons from the distribution directory
3995# or an alternative specified in $ALTERNATIVE_ICONS
3996# to the document directory.
3997
3998sub copy_icons {
3999    local($icon,$_);
4000    print "\nCopying navigation icons ...";
4001    foreach (keys %used_icons) {
4002	# each entry ends in gif or png
4003	if ($ALTERNATIVE_ICONS) {
4004	    L2hos->Copy("$ALTERNATIVE_ICONS$dd$_", ".$dd$_")
4005		if (-e "$ALTERNATIVE_ICONS$dd$_" && !-e $_);
4006	} elsif (/(gif|png)$/) {
4007	    L2hos->Copy("$LATEX2HTMLDIR${dd}icons$dd$_", ".$dd$_")
4008		if (-e "$LATEX2HTMLDIR${dd}icons$dd$_" && !-e $_);
4009	}
4010    }
4011}
4012
4013sub process_log_file {
4014    local($logfile) = @_;
4015    local($name,$before,$lengthsfound);
4016    local($TeXpt)= 72/72.27;
4017    local($image_counter);
4018    open(LOG, "<$logfile") || die "\nCannot read logfile '$logfile': $!\n";
4019    while (<LOG>) {
4020        if (/Overfull/) { $before .= $_ }
4021        elsif (/latex2htmlLength ([a-zA-Z]+)=(\-?[\d\.]+)pt/) {
4022	    ${$1} = 0.0+$2; $lengthsfound = 1;
4023	} elsif (/latex2htmlSize|l2hSize/) {
4024	    /:([^:]*):/;
4025	    $name = $1; $name =~ s/\*//g;
4026	    ++$image_counter;
4027	    s/:([0-9.]*)pt/$height{$name} = $1*$TeXpt;''/e;
4028	    s/::([0-9.]*)pt/$depth{$name} = $1*$TeXpt;''/e;
4029	    s/::([0-9.]*)pt/$width{$name} = $1*$TeXpt;''/e;
4030	    s/\((.*)\)/$eqno{$name} = 1+$1;''/e;
4031	    if ($before) {
4032		local($tmp);
4033		if ($before =~ /hbox\s*\((\d+\.?\d*)pt/) {
4034		    $width{$name} = $width{$name}+$1*$TeXpt;
4035		}
4036		if ($before =~ /vbox\s*\((\d+\.?\d*)pt/) {
4037		    $height{$name} = $height{$name}+$1*$TeXpt;
4038		}
4039	        $before = '';
4040	    }
4041	}
4042    $LaTeXERROR = 1 if (/^No pages of output./);
4043    }
4044
4045    if ($LaTeXERROR) {
4046	print STDERR "\n\n *** LaTeX produced no output ***\n"
4047	    . " *** no new images can be created\n"
4048	    . " *** Examine the  images.log  file.\n\n";
4049	return;
4050    }
4051    print STDOUT "\n *** processing $image_counter images ***\n";
4052    print STDOUT "\n *** LATEX LOG OK. ***\n" if ($VERBOSITY > 1);
4053
4054    if ($lengthsfound) {
4055	$ODD_HMARGIN  = $hoffset + $oddsidemargin;
4056	$EVEN_HMARGIN = $hoffset + $evensidemargin;
4057	$VMARGIN = $voffset + $topmargin + $headheight + $headsep;
4058        if ($dvi_mag >0 && $dvi_mag != 1000) {
4059	    $ODD_HMARGIN = int($dvi_mag /1000 * $ODD_HMARGIN);
4060	    $EVEN_HMARGIN = int($dvi_mag /1000 * $EVEN_HMARGIN);
4061	    $VMARGIN = int($dvi_mag /1000 * $VMARGIN);
4062	}
4063    } else {
4064	$ODD_HMARGIN = 0;
4065	$EVEN_HMARGIN = 0;
4066	$VMARGIN = 0;
4067    }
4068    $ODD_HMARGIN  = int($ODD_HMARGIN*$TeXpt  + 72.5);
4069    $EVEN_HMARGIN = int($EVEN_HMARGIN*$TeXpt + 72.5);
4070    $VMARGIN = int($VMARGIN*$TeXpt + 72.5);
4071    close(LOG);
4072}
4073
4074sub extract_image { # clean
4075    my ($page_num,$name) = @_;
4076
4077    # The followin come out of %img_params
4078    my ($scale, $external, $thumbnail, $map, $psimage, $align, $usemap,
4079	  $flip, $aalias, $trans, $exscale, $alt, $exstr);
4080
4081    my ($lwidth, $val) = (0, '');
4082    my ($custom_size,$color_depth,$height,$width,$croparg);
4083
4084    print STDOUT "\nextracting $name as $page_num\n" if ($VERBOSITY > 1);
4085    # $global_num identifies this image in the original source file
4086    # $new_num identifies this image in images.tex
4087    my ($global_num, $new_num) = split(/#/, $page_num);
4088    $name =~ s/\*/star/;
4089    my ($env,$basename,$img) = ($name,"img$global_num",'');
4090    $env =~ s/\d+$//;
4091    $psname = sprintf("%s%.3d", "$TMPDIR$dd$IMAGE_PREFIX", $new_num);
4092    if ( $EXTERNAL_IMAGES && $PS_IMAGES ) {
4093	$img =  "$basename.ps";
4094	L2hos->Copy("$psname.ps", "${PREFIX}$img");
4095    } else {
4096	$img = "$basename.$IMAGE_TYPE";
4097	($scale, $external, $thumbnail, $map, $psimage, $align, $usemap,
4098	    $flip, $aalias, $trans, $exscale, $alt, $exstr) =
4099            split('#', $img_params{$name});
4100	$lwidth = ($align =~ s/nojustify/middle/) ? 0 : $LINE_WIDTH;
4101	$alt = "ALT=\"$name\"" unless $alt;
4102	$exscale = $EXTRA_IMAGE_SCALE unless($exscale);
4103	if ($NO_IMAGES) {
4104	    L2hos->Symlink("image.$IMAGE_TYPE", "${PREFIX}$img");
4105	    if ($thumbnail) {
4106		L2hos->Symlink("image.$IMAGE_TYPE", "${PREFIX}T$img");
4107		$thumbnail = "${PREFIX}T$img";
4108	    }
4109	} else {
4110	    # RRM: deal with size data
4111 	    if ($width{$name} < 0) {
4112		if ($exscale && $PK_GENERATION) {
4113	    	    $height = int(
4114			$exscale*$height{$name}+
4115			$exscale*$depth{$name} +.5);
4116		    $width = int($exscale*$width{$name}-.5);
4117		} else {
4118	    	    $height = int($height{$name}+$depth{$name}+.5);
4119		    $width = int($width{$name}-.5);
4120		}
4121		$custom_size = "${width}x$height";
4122	    } elsif ($width{$name}) {
4123		if ($exscale && $PK_GENERATION) {
4124		    $height = int( $height{$name} * $exscale +
4125			$depth{$name} * $exscale +.5);
4126		    $width = int($width{$name} * $exscale +.5);
4127		} else {
4128		    $height = int($height{$name}+$depth{$name}+.5);
4129		    $width = int($width{$name}+.5);
4130		}
4131		$custom_size = "${width}x$height";
4132            } else {
4133		$custom_size = '';
4134	    }
4135            # MRO: add first overall crop
4136	    $croparg = '-crop a' . ($crop{$name} || '') . ' ';
4137	    $page_num  =~ s/^\d+#//o;
4138	    $custom_size .= " -margins "
4139		. (($page_num % 2) ? $ODD_HMARGIN:$EVEN_HMARGIN)
4140		. ",$VMARGIN" if ($custom_size);
4141
4142	    #RRM: \special commands may place ink outside the expected bounds:
4143	    $custom_size = '' if ($tex_specials{$name});
4144
4145	    # MRO: Patches for image conversion with pstoimg
4146	    # RRM: ...with modifications and fixes
4147	    L2hos->Unlink("${PREFIX}$img");
4148	    &close_dbm_database if $DJGPP;
4149            print "Converting image #$new_num\n";
4150
4151	    if ( ($name =~ /figure/) || $psimage || $scale || $thumbnail) {
4152		$scale = $FIGURE_SCALE_FACTOR unless ($scale);
4153		print "\nFIGURE: $name scaled $scale  $aalias\n" if ($VERBOSITY > 2);
4154		(L2hos->syswait( "$PSTOIMG -type $IMAGE_TYPE "
4155		. ($DEBUG ? '-debug ' : '-quiet ' )
4156		. ($TMPDIR ? "-tmp $TMPDIR " : '' )
4157		. (($DISCARD_PS && !$thumbnail && !$psimage)? "-discard " :'')
4158		. (($INTERLACE) ? "-interlace " : '' )
4159		. (((($ANTI_ALIAS)||($aalias))&&($aalias !~ /no|text/))? "-antialias ":'')
4160		. (($ANTI_ALIAS_TEXT||(($aalias =~/text/)&&($aalias !~/no/)))?
4161			"-aaliastext ":'')
4162		. (($custom_size) ? "-geometry $custom_size ": '' )
4163		. $croparg
4164		. ($color_depth || '')
4165		. (($flip) ? "-flip $flip " : '' )
4166		. (($scale > 0) ? "-scale $scale " : '' )
4167		. (((($TRANSPARENT_FIGURES && ($env =~ /figure/o))||($trans))
4168		     &&(!($trans =~ /no/))) ? "-transparent " : '')
4169		. (($WHITE_BACKGROUND) ? "-white " : '' )
4170		. "-out ${PREFIX}$img $psname.ps"
4171		) ) # ||!(print "\nWriting image: ${PREFIX}$img"))
4172		    && print "\nError while converting image: $!\n";
4173
4174		if ($thumbnail) { # $thumbnail contains the reduction factor
4175		    L2hos->Unlink("${PREFIX}T$img");
4176		    print "\nIMAGE thumbnail: $name" if ($VERBOSITY > 2);
4177		    (L2hos->syswait( "$PSTOIMG -type $IMAGE_TYPE "
4178		    . ($DEBUG ? '-debug ' : '-quiet ' )
4179		    . ($TMPDIR ? "-tmp $TMPDIR " : '' )
4180		    . (($DISCARD_PS && !$psimage) ? "-discard " : '' )
4181		    . (($INTERLACE) ? "-interlace " : '' )
4182		    . ((($ANTI_ALIAS||($aalias))&&(!($aalias =~/no/)))? "-antialias " :'')
4183		    . (($ANTI_ALIAS_TEXT||(($aalias =~/text/)&&($aalias !~/no/)))?
4184			"-aaliastext ":'')
4185		    . (($custom_size) ? "-geometry $custom_size " : '' )
4186		    . ($color_depth || '')
4187		    . (($flip) ? "-flip $flip " : '' )
4188		    . (($thumbnail > 0) ? "-scale $thumbnail " : '' )
4189		    . ((($trans)&&(!($trans =~ /no/))) ? "-transparent " : '')
4190		    . (($WHITE_BACKGROUND) ? "-white " : '' )
4191		    . "-out ${PREFIX}T$img $psname.ps"
4192		    ) ) # ||!(print "\nWriting image: ${PREFIX}T$img"))
4193			&& print "\nError while converting thumbnail: $!\n";
4194		    $thumbnail = "${PREFIX}T$img";
4195		}
4196	    } elsif (($exscale &&(!$PK_GENERATION))&&($width{$name})) {
4197		my $under = '';
4198		my $mathscale = ($MATH_SCALE_FACTOR > 0) ? $MATH_SCALE_FACTOR : 1;
4199		if (($DISP_SCALE_FACTOR > 0) &&
4200		    ( $name =~ /equation|eqnarray|display/))
4201		        { $mathscale *= $DISP_SCALE_FACTOR; };
4202		if ($scale) {
4203		    $scale *= $exscale if ($name =~ /makeimage|tab/);
4204		} else {
4205		    $scale = $mathscale*$exscale;
4206		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
4207		}
4208		print "\nIMAGE: $name  scaled by $scale \n" if ($VERBOSITY > 2);
4209		(L2hos->syswait( "$PSTOIMG -type $IMAGE_TYPE "
4210		. ($DEBUG ? '-debug ' : '-quiet ' )
4211		. ($TMPDIR ? "-tmp $TMPDIR " : '' )
4212		. (($DISCARD_PS)? "-discard " : '' )
4213		. (($INTERLACE)? "-interlace " : '' )
4214		. ((($ANTI_ALIAS_TEXT||($aalias))&&($aalias !=~/no/))?
4215		    "-antialias -depth 1 " :'')
4216		. (($custom_size)? "-geometry $custom_size " : '' )
4217                . $croparg
4218		. (($scale != 1)? "-scale $scale " : '' )
4219		. ((($exscale)&&($exscale != 1)&&
4220		    !($ANTI_ALIAS_TEXT &&($LATEX_COLOR)))?
4221			"-shoreup $exscale$under " :'')
4222		. ((($TRANSPARENT_FIGURES ||($trans))
4223		     &&(!($trans =~ /no/)))? "-transparent " : '')
4224		. (($WHITE_BACKGROUND && !$TRANSPARENT_FIGURES) ? "-white " : '' )
4225		. "-out ${PREFIX}$img $psname.ps"
4226		) ) # ||!(print "\nWriting image: ${PREFIX}$img"))
4227		    && print "\nError while converting image: $!\n";
4228	    } else {
4229		print "\nIMAGE: $name\n" if ($VERBOSITY > 2);
4230		my $under = '';
4231		my $mathscale = ($MATH_SCALE_FACTOR > 0) ? $MATH_SCALE_FACTOR : 1;
4232		if (($DISP_SCALE_FACTOR > 0) &&
4233		    ( $name =~ /equation|eqnarray|display/))
4234		        { $mathscale *= $DISP_SCALE_FACTOR; };
4235		if (($scale)&&($exscale)) {
4236		    $scale *= $exscale if ($name =~ /makeimage|tab/);
4237		} elsif ($scale) {
4238		} elsif (($mathscale)&&($exscale)) {
4239		    $scale = $mathscale*$exscale;
4240		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
4241		} elsif ($mathscale) { $scale = $mathscale; }
4242
4243		(L2hos->syswait("$PSTOIMG -type $IMAGE_TYPE "
4244		. ($DEBUG ? '-debug ' : '-quiet ' )
4245		. ($TMPDIR ? "-tmp $TMPDIR " : '' )
4246		. (($DISCARD_PS) ? "-discard " : '' )
4247		. (($INTERLACE) ? "-interlace " : '' )
4248		. ((($ANTI_ALIAS_TEXT||($aalias))&&(!($aalias =~ /no/)))?
4249		    "-antialias -depth 1 " :'')
4250		. ((($exscale)&&($exscale != 1)&&
4251		    !($ANTI_ALIAS_TEXT &&($LATEX_COLOR)))?
4252			"-shoreup $exscale " :'')
4253		. (($scale ne 1) ? "-scale $scale " : '' )
4254		. (($custom_size) ? "-geometry $custom_size " : '' )
4255                . $croparg
4256#		.  (($name =~ /(equation|eqnarray)/) ? "-rightjustify $lwidth " : '')
4257#		.  (($name =~ /displaymath/) ? "-center $lwidth " : '')
4258		. (($name =~ /inline|indisplay/ && (!($custom_size))&&$depth{$name}!= 0) ?
4259		    do {$val=($height{$name}-$depth{$name})/($height{$name}+$depth{$name});
4260			"-topjustify x$val "} : '')
4261		. ((($TRANSPARENT_FIGURES||($trans))
4262		    &&(!($trans =~ /no/))) ? "-transparent " : '')
4263		. (($WHITE_BACKGROUND && !$TRANSPARENT_FIGURES) ? "-white " : '' )
4264		. "-out ${PREFIX}$img $psname.ps")
4265		) #|| !(print "\nWriting image: ${PREFIX}$img"))
4266		    && print "\nError while converting image\n";
4267	    }
4268	    if (! -r "${PREFIX}$img") {
4269		&write_warnings("\nFailed to convert image $psname.ps")
4270	    } else { } #L2hos->Unlink("$psname.ps") unless $DEBUG }
4271	    &open_dbm_database if $DJGPP;
4272	}
4273    }
4274    print "\nextracted $name as $page_num\n" if ($VERBOSITY > 1);
4275    &embed_image("${PREFIX}$img", $name, $external, $alt, $thumbnail, $map,
4276        $align, $usemap, $exscale, $exstr);
4277}
4278
4279sub extract_parameters {
4280    local($contents) = @_;
4281    local($_, $scale, $external, $thumbnail, $map, $psimage, $align,
4282	  $usemap, $flip, $aalias, $trans, $pagecolor, $alt, $exscale,
4283	  $cdepth, $htmlparams);
4284
4285    #remove the \htmlimage commands and arguments before...
4286    $contents =~ s/$htmlimage_rx/$_ = $2;''/ego;
4287    $contents =~ s/$htmlimage_pr_rx/$_ .= $2;''/ego;
4288
4289    # code adapted from original idea by Stephen Gildea:
4290    # If the document specifies the ALT tag explicitly
4291    # with \htmlimage{alt=some text} then use it.
4292    s!alt=([^,]+)!$alt = $1;
4293        $alt =~ s/^\s+|\s+$//g; $alt =~ s/"//g;
4294        $alt="ALT=\"$alt\"";
4295    ''!ie;
4296
4297  if (!$alt) {
4298    #...catching all the code for the ALT text.
4299    local($keep_gt)=1;
4300    $alt = &flatten_math($contents); undef $keep_gt;
4301    #RRM: too long strings upset the DBM. Truncate to <= 165 chars.
4302    if ( length($alt) > 163 ) {
4303	local($start,$end);
4304	$start = substr($alt,0,80);
4305	$end = substr($alt,length($alt)-80,80);
4306	$alt = join('',$start,"...\n ...",$end);
4307    }
4308    s/ALT\s*=\"([\w\W]*)\"/$alt=$1;''/ie;
4309    if ($alt) {
4310	if ($alt =~ /\#/) {
4311	    $alt =~ s/^(\\vbox\{)?\#[A-Za-z]*\s*//;
4312	    $alt =~ s/\n?\#[A-Za-z]*\s*\}?$//s;
4313	    if ($alt =~ /\#/) { $alt = $` . " ... " };
4314	}
4315	$alt =~ s/\`\`/\\lq\\lq /g; $alt =~ s/\`/\\lq /g;
4316	$alt =~ s/(^\s*|\s*$)//mg;
4317	$alt = "ALT=\"$alt\"" if ($alt);
4318    } else { $alt = 'ALT="image"' }
4319  }
4320
4321    $psimage++ if ($contents =~ /\.ps/);
4322#    $contents =~ s/\s//g;	# Remove spaces   Why ?
4323    s/extrascale=([\.\d]*)/$exscale=$1;''/ie;
4324    s/\bscale=([\.\d]*)/$scale=$1;''/ie;
4325    s/(^|,\s*)external/$external=1;''/ie;
4326    s/(^|,\s*)((no)?_?anti)alias(_?(text))?/$aalias = $2.$4;''/ie;
4327    s/(^|,\s*)((no)?_?trans)parent/$trans = $2;''/ie;
4328    s/thumbnail=([\.\d]*)/$thumbnail=$1;''/ie;
4329    s/usemap=([^\s,]+)/$usemap=$1;''/ie;
4330    s/map=([^\s,]+)/;$map=$1;''/ie;
4331    s/align=([^\s,]+)/$align=$1;''/ie;
4332    s/flip=([^\s,]+)/$flip=$1;''/ie;
4333    s/color_?(depth)?=([^\s,]+)/$cdepth=$2;''/ie;
4334    ($scale,$external,$thumbnail,$map,$psimage,$align
4335     ,$usemap,$flip,$aalias,$trans,$exscale,$alt,$_);
4336}
4337
4338
4339# RRM: Put the raw \TeX code into the ALT tag
4340# replacing artificial environments and awkward characters
4341sub flatten_math {
4342    local ($_) = @_;
4343    $_ = &revert_to_raw_tex($_);
4344    s/[ \t]+/ /g;
4345    # MRO: replaced $* with /m
4346    s/$tex2html_wrap_rx//gm;
4347    s/(\\begin\s*\{[^\}]*\})(\s*(\[[^]]*\]))?[ \t]*/$1$3/gm;
4348    s/(\\end\{[^\}]*\})\n?/$1/gm;
4349    s/>(\w)?/($1)?"\\gt $1":"\\gt"/eg unless ($keep_gt); # replace > by \gt
4350    s/\\\|(\w)?/($1)?"\\Vert $1":"\\Vert"/eg; 	# replace \| by \Vert
4351    s/\|(\w)?/($1)?"\\vert $1":"\\vert"/eg; 	# replace | by \vert
4352    s/\\\\/\\\\ /g; 	# insert space after \\
4353    s/\\"/\\uml /g;	# screen umlaut accents...
4354    s/"/\'\'/g;		# replace " by ''
4355    s/\\\#/\\char93 /g;	# replace \# by \char93 else caching fails
4356#    s/"(\w)?/($1)?"\\rq\\rq $1":"\'\'"/eg;	# replace " by \rq\rq
4357#    s/\&\\uml /\\\"/g;	# ...reinstate umlauts
4358    $_;
4359}
4360
4361sub scaled_image_size {
4362    local($exscale,$_) = @_;
4363    local($width,$height) = ('','');
4364    /WIDTH=\"?(\d*)\"?\s*HEIGHT=\"?(\d*)\"?$/o;
4365    $width=int($1/$exscale + .5);
4366    $height=int($2/$exscale + .5);
4367    "WIDTH=\"$width\" HEIGHT=\"$height\""
4368}
4369
4370sub process_in_latex {
4371    # This is just a wrapper for process_undefined_environment.
4372    # @[0] = contents
4373    $global{'max_id'}++;
4374    &process_undefined_environment('tex2html_wrap',$global{'max_id'},$_[0]);
4375}
4376
4377# MRO: cp deprecated, replaced by L2hos->Copy
4378
4379# Marcus Hennecke  6/3/96
4380# MRO: test for existance
4381sub copy_file {
4382    local($file, $ext) = @_;
4383    $file =  &fulltexpath("$FILE.$ext");
4384    if(-r $file) {
4385        print "\nNote: Copying '$file' for image generation\n"
4386            if($VERBOSITY > 2);
4387        L2hos->Copy($file, ".$dd${PREFIX}images.$ext");
4388    }
4389}
4390
4391sub rename_image_files {
4392    local($_, $old_name, $prefix);
4393    if ($PREFIX) {
4394	foreach (<${PREFIX}*img*.$IMAGE_TYPE>) {
4395	    $old_name = $_;
4396	    s/\.$IMAGE_TYPE$/\.old/o;
4397	    L2hos->Rename($old_name, $_);
4398	    }
4399	}
4400    else {
4401	foreach (<img*.$IMAGE_TYPE>) {
4402	    $old_name = $_;
4403	    s/\.$IMAGE_TYPE$/\.old/o;
4404	    L2hos->Rename($old_name, $_);
4405	}
4406	foreach (<Timg*.$IMAGE_TYPE>) {
4407	    $old_name = $_;
4408	    s/\.$IMAGE_TYPE$/\.old/o;
4409	    L2hos->Rename($old_name, $_);
4410	}
4411    }
4412}
4413
4414
4415############################ Processing Commands ##########################
4416
4417sub ignore_translate_commands {
4418    local ($_) = @_;
4419#   print "\nTranslating commands ...";
4420
4421    local(@processedC);
4422    &replace_strange_accents;
4423    local($before, $contents, $br_id, $after, $pattern, $end_cmd_rx);
4424    s/$begin_cmd_rx/&replace_macro_expansion($`, $1, $&, $')/eg;
4425}
4426
4427sub replace_macro_expansion {
4428    push(@processedC,$_[1]);
4429    $end_cmd_rx = &make_end_cmd_rx($_[2]);
4430    $pattern = $_[3];
4431    $_ = join('',$_[3],$_[4]);
4432    $after = $_[4];
4433    if (($before)&&(!($before =~ /$begin_cmd_rx/))) {
4434	push(@processedC,$before);
4435	    $_ = join('',$pattern,$after); $before = '';
4436	}
4437	local($end_cmd_rx) = &make_end_cmd_rx($br_id);
4438
4439}
4440
4441sub translate_aux_commands {
4442    s/^(.*)$/&translate_commands($1)/s;
4443}
4444
4445sub translate_commands {
4446    local ($_) = @_;
4447#   print "\nTranslating commands ...";
4448
4449    local(@processedC);
4450    &replace_strange_accents;
4451    for (;;) {			# For each opening bracket ...
4452	last unless ($_ =~ /$begin_cmd_rx/);
4453	local($before, $contents, $br_id, $after, $pattern);
4454	($before, $br_id, $after, $pattern) = ($`, $1, $', $&);
4455	if (($before)&&(!($before =~ /$begin_cmd_rx/))) {
4456	    push(@processedC,$before);
4457	    $_ = join('',$pattern,$after); $before = '';
4458	}
4459	local($end_cmd_rx) = &make_end_cmd_rx($br_id);
4460	if ($after =~ /$end_cmd_rx/) { # ... find the the matching closing one
4461	    $NESTING_LEVEL++;
4462	    ($contents, $after) = ($`, $');
4463	    do {
4464		local(@save_open_tags) = @$open_tags_R;
4465		local($open_tags_R) = [ @save_open_tags ];
4466		print STDOUT "\nIN::{$br_id}" if ($VERBOSITY > 4);
4467		print STDOUT "\n:$contents\n" if ($VERBOSITY > 7);
4468		undef $_;
4469		$contents = &translate_commands($contents)
4470		    if ($contents =~ /$match_br_rx/o);
4471                # Modifies $contents
4472		&process_command($single_cmd_rx,$contents)
4473		    if ($contents =~ /\\/o);
4474
4475		$contents .= &balance_tags();
4476	    };
4477
4478	    print STDOUT "\nOUT: {$br_id}" if ($VERBOSITY > 4);
4479	    print STDOUT "\n:$contents\n" if ($VERBOSITY > 7);
4480	    # THIS MARKS THE OPEN-CLOSE DELIMITERS AS PROCESSED
4481	    $_ = join("", $before,"$OP$br_id$CP", $contents,"$OP$br_id$CP", $after);
4482	    $NESTING_LEVEL--;
4483	}
4484	else {
4485	    $pattern = &escape_rx_chars($pattern);
4486	    s/$pattern//;
4487	    print "\nCannot find matching bracket for $br_id" unless $AUX_FILE;
4488	}
4489	last unless ($_ =~ /$begin_cmd_rx/o);
4490    }
4491    $_ = join('',@processedC) . $_;
4492    # Now do any top level commands that are not inside any brackets
4493    # MODIFIES $_
4494    print $_ if ($VERBOSITY > 8);
4495    &process_command($single_cmd_rx,$_);
4496}
4497
4498#RRM: based on earlier work of Marcus Hennecke
4499# makes sure the $open_tags_R at the end of an environment
4500# is the same as @save_open_tags from the start,
4501# ensuring that the HTML page indeed has balanced tags
4502sub balance_tags {
4503    local($tag_cmd, $tags, $save_tags, $open_tags, @reopen_tags);
4504    $save_tags = join(',',@save_open_tags) if (@save_open_tags);
4505    $open_tags = join(',',@$open_tags_R) if (@$open_tags_R);
4506    if ($open_tags eq $save_tags) { return(); }
4507    if ($save_tags =~ s/^$open_tags//) {
4508	@reopen_tags = split (',',$');
4509    } else {
4510	@reopen_tags = @save_open_tags;
4511	while (@$open_tags_R) {
4512	    $tag_cmd = pop (@$open_tags_R);
4513	    print STDOUT "\n</$tag_cmd>" if $VERBOSITY > 2;
4514	    $declarations{$tag_cmd} =~ m|</.*$|;
4515	    $tags .= $& unless ($` =~ /^<>$/);
4516	    $open_tags = join(',',@$open_tags_R) if (@$open_tags_R);
4517	    last if ( $save_tags =~ s/^$open_tags/
4518		     @reopen_tags = split (',',$');''/e);
4519	}
4520    }
4521    while (@reopen_tags) {
4522	$tag_cmd = shift @reopen_tags;
4523	if ($tag_cmd) {
4524	    push (@$open_tags_R, $tag_cmd) if ($tag_cmd);
4525	    print STDOUT "\n<$tag_cmd>" if $VERBOSITY > 2;
4526	    $declarations{$tag_cmd} =~ m|</.*$|;
4527	    $tags .= $` unless ($` =~ /^<>$/);
4528	}
4529    }
4530    $tags;
4531}
4532
4533sub close_all_tags {
4534    return() if (!@$open_tags_R);
4535    local($tags,$tag_cmd);
4536    while (@$open_tags_R) {
4537	$tag_cmd = pop (@$open_tags_R);
4538	print STDOUT "\n</$tag_cmd>" if $VERBOSITY > 2;
4539	$declarations{$tag_cmd} =~ m|</.*$|;
4540	$tags .= $& unless ($` =~ /^<>$/);
4541    }
4542    $tags;
4543}
4544
4545sub preserve_open_tags {
4546    local(@save_open_tags) = @$open_tags_R;
4547    local($open_tags_R) = [ @save_open_tags ];
4548    # provides the markup to close and reopen the current tags
4549    (&close_all_tags(), &balance_tags());
4550}
4551
4552sub preserve_open_block_tags {
4553    local($tag_cmd,$tags_open,$tags_close,$pre,$post,@tags);
4554    while (@$open_tags_R) {
4555	$tag_cmd = pop (@$open_tags_R);
4556	print STDOUT "\n</$tag_cmd>" if $VERBOSITY > 2;
4557	$declarations{$tag_cmd} =~ m|</.*$|;
4558	($pre,$post) = ($`,$&);
4559	if ($post =~ /$block_close_rx/) {
4560	    # put it back and exit
4561	    push(@$open_tags_R,$tag_cmd);
4562	    last;
4563	} else {
4564	    # leave it closed, collecting tags for it
4565	    $tags_close .= $post;
4566	    $tags_open = $pre . $tags_open;
4567	    unshift(@tags,$tag_cmd);
4568	}
4569    }
4570    ($tags_close , $tags_open, @tags);
4571}
4572
4573sub minimize_open_tags {
4574    local($this_tag, $close_only) = @_;
4575    local($pre,$post,$decl);
4576    $decl = $declarations{$this_tag};
4577    if ($decl) {
4578    # if it is a declaration, get the corresponding tags...
4579	$decl =~ m|</.*$|;
4580	($pre,$post) = ($`,$&) unless ($` =~ /^<>$/);
4581	if (!@$open_tags_R) { # when nothing else is open...
4582            # pushing the style, when appropriate
4583	    push (@$open_tags_R, $this_tag)
4584		unless ($close_only ||($post =~ /$block_close_rx/));
4585	    print STDOUT "\n<$this_tag>" if $VERBOSITY > 2;
4586            # and return the tags
4587	    return($pre,$post) unless ($USING_STYLES);
4588	    local($env_id) = '' if ($env_id =~/^\w+$/);
4589	    $pre =~ s/>$/ $env_id>/ if ($env_id);
4590	    return($pre,$post);
4591	}
4592    } else { # ...else record the argument as $pre
4593	$pre = $this_tag unless $close_only;
4594    }
4595    local($env_id) = '' if ($env_id =~/^\w+$/);
4596    $pre =~ s/>$/ ID="$env_id">/ if ($USING_STYLES &&($env_id));
4597
4598    # return the tags, if nothing is already open
4599    if (!@$open_tags_R) {
4600	return($pre,$post);
4601    }
4602#    elsif ($close_only) { push (@$open_tags_R, $this_tag) }
4603
4604    local($tags,$tag_cmd,$tag_open);
4605    local($closures,$reopens,@tags);
4606    local($tag_close,$tag_open);
4607    local($size_cmd,$size_open);
4608    local($font_cmd,$font_open);
4609    local($fontwt_cmd,$fontwt_open);
4610    local($color_cmd,$color_open);
4611     if ($decl) {
4612	if ($this_tag =~ /$sizechange_rx/) {
4613	    $size_cmd = $this_tag;
4614	} else {
4615	    if ($this_tag =~ /$fontchange_rx/) {
4616	        $font_cmd = $this_tag }
4617	    if ($this_tag =~ /$fontweight_rx/) {
4618		$fontwt_cmd = $this_tag }
4619	}
4620    }
4621    while (@$open_tags_R) {
4622	($tag_close,$tag_open) = ('','');
4623	$tag_cmd = pop (@$open_tags_R);
4624	print STDOUT "\n</$tag_cmd>" if $VERBOSITY > 2;
4625	$declarations{$tag_cmd} =~ m|</.*$|;
4626	($tag_close,$tag_open) = ($&,$`) unless ($` =~ /<>/);
4627	$closures .= $tag_close;
4628
4629	if ((!$size_cmd)&&($tag_cmd =~ /$sizechange_rx/)) {
4630	    $size_cmd = $tag_cmd;
4631	    $size_open = $tag_open;
4632	}
4633	elsif ((!$font_cmd)&&($tag_cmd =~ /$fontchange_rx/)) {
4634	    $font_cmd = $tag_cmd;
4635	    $font_open = $tag_open;
4636	}
4637	elsif ((!$fontwt_cmd)&&($tag_cmd =~ /$fontweight_rx/)) {
4638	    $fontwt_cmd = $tag_cmd;
4639	    $fontwt_open = $tag_open;
4640	}
4641	elsif ((!$color_cmd)&&($tag_cmd =~ /$colorchange_rx/)) {
4642	    $color_cmd = $tag_cmd;
4643	    $color_open = $tag_open;
4644	}
4645	elsif ($tag_cmd =~
4646	     /$sizechange_rx|$fontchange_rx|$fontweight_rx|$colorchange_rx/) {
4647	} else {
4648	    unshift (@tags, $tag_cmd);
4649	    print STDOUT "\n<<$tag_cmd>" if $VERBOSITY > 2;
4650	    $reopens = $tag_open . $reopens;
4651	}
4652    }
4653    if ($USING_STYLES) {
4654	local($TAG) = "DIV";
4655	if ($pre =~ /^<(DIV|SPAN|PRE)/) { $TAG = $1 };
4656	if (($pre =~ /^<$TAG/)&&($env_id =~ /^\s+(CLASS|ID)/)) {
4657	    $pre =~ s/<$TAG/<$TAG$env_id/;
4658	} elsif ($pre =~ /<P>/) {
4659	    $TAG = 'P';
4660	} else {
4661	}
4662#	$post .= "</$TAG>";
4663    }
4664    push (@$open_tags_R, @tags);
4665    $tags .= $pre if ($pre && $post =~ /$block_close_rx/);
4666    if ($font_cmd && !($font_cmd eq $this_tag)) {
4667	push (@$open_tags_R,$font_cmd);
4668	print STDOUT "\n<$font_cmd>" if $VERBOSITY > 2;
4669	$tags .= $font_open;
4670    }
4671    if ($fontwt_cmd && !($fontwt_cmd eq $this_tag)) {
4672	push (@$open_tags_R,$fontwt_cmd);
4673	print STDOUT "\n<$fontwt_cmd>" if $VERBOSITY > 2;
4674	$tags .= $fontwt_open;
4675    }
4676    if ($size_cmd && !($size_cmd eq $this_tag)) {
4677	push (@$open_tags_R,$size_cmd);
4678	print STDOUT "\n<$size_cmd>" if $VERBOSITY > 2;
4679	$tags .= $size_open;
4680    }
4681    if ($color_cmd && !($color_cmd eq $this_tag)) {
4682	push (@$open_tags_R,$color_cmd);
4683	print STDOUT "\n<$color_cmd>" if $VERBOSITY > 2;
4684	$tags .= $color_open;
4685    }
4686    $tags .= $pre unless ($pre && $post =~ /$block_close_rx/);
4687    push (@$open_tags_R, $this_tag)
4688	if ($decl &&!($post =~ /$block_close_rx|$all_close_rx/));
4689    print STDOUT "\n<$this_tag>" if $VERBOSITY > 2;
4690    ($closures.$reopens.$tags , $post );
4691}
4692
4693
4694sub declared_env {
4695    local($decl, $_, $deferred) = @_;
4696    local($after_cell,$pre,$post);
4697    local($decls) = $declarations{$decl};
4698    $decls =~ m|</.*$|;
4699    ($pre,$post) = ($`,$&);
4700    if ($USING_STYLES) {
4701	$env_style{$decl} = " " unless ($env_style{$decl});
4702	$pre =~ s/>$/$env_id>/ if ($env_id);
4703    }
4704    local($closing_tag) = 1 if ($pre =~ /^<>$/);
4705    $pre = $post = '' if $closing_tag;
4706    local($closures,$reopens);
4707
4708    local(@save_open_tags) = @$open_tags_R
4709	unless ($closing_tag || $deferred);
4710    local($open_tags_R) = [ @save_open_tags ]
4711	unless ($closing_tag || $deferred );
4712
4713    if ($post =~ /$block_close_rx/) {
4714	local($last_tag) = pop (@$open_tags_R);
4715	local($ldecl) = $declarations{$last_tag};
4716	if ($ldecl =~ m|</.*$|) { $ldecl = $& }
4717	if (($last_tag)&&!($ldecl =~ /$block_close_rx/)) {
4718	    # need to close tags, for re-opening inside
4719	    push (@$open_tags_R, $last_tag);
4720	    ($closures,$reopens) = &preserve_open_tags();
4721	    $pre = join('', $closures, "\n", $pre, $reopens);
4722	    $post = join('', $closures, $post, $reopens);
4723	} elsif ($last_tag) {
4724	    $pre = "\n".$pre;
4725	    push (@$open_tags_R, $last_tag);
4726	    undef $ldecl;
4727	} else {
4728	}
4729
4730	if ($deferred) {
4731	    if (defined $ldecl) {
4732		print STDOUT "\n<<$decl>" if $VERBOSITY > 2;
4733		unshift(@$open_tags_R, $decl);
4734	    } else {
4735		print STDOUT "\n<$decl>" if $VERBOSITY > 2;
4736		push(@$open_tags_R, $decl);
4737	    }
4738	    return ( $pre . $_ );
4739	} else {
4740	    if (defined $ldecl) {
4741		print STDOUT "\n<<$decl>" if $VERBOSITY > 2;
4742		unshift(@$open_tags_R, $decl);
4743	    } else {
4744		print STDOUT "\n<$decl>" if $VERBOSITY > 2;
4745		push(@$open_tags_R, $decl);
4746	    }
4747	}
4748    } elsif ($post =~/$all_close_rx/) {
4749	($closures,$reopens) = &preserve_open_tags();
4750	($pre,$post) = &minimize_open_tags($decl,1);
4751	$pre = join('', $closures, $pre);
4752    } elsif ($closing_tag) {
4753	$prev_open = $pre;
4754	($pre,$post) = &minimize_open_tags($decl,1);
4755	$pre =~ s/<\/?>//g; $post =~ s/<\/?>//;
4756    } else {
4757	($pre,$post) = &minimize_open_tags($decl);
4758    }
4759    $_ =~ s/^\s+//s; #RRM:28/4/99 remove spaces at the beginning
4760    $_ = &translate_environments($_);
4761    $_ = &translate_commands($_) if (/\\/);
4762    if ($post =~ /$block_close_rx/) {
4763	s/^\n?/\n/o;
4764	if (defined $ldecl) {
4765	    $post = &close_all_tags();
4766	} else {
4767	    $post = "\n";
4768	}
4769    } elsif ($post =~/$all_close_rx/) {
4770    } else { $post = '' };
4771
4772    join('', $pre, $_, $post
4773	   , ($closing_tag ? '' : &balance_tags()) );
4774}
4775
4776sub do_cmd_centering{&declared_env('center',$_[0],$tex2html_deferred)}
4777sub do_cmd_raggedright{&declared_env('flushleft',$_[0],$tex2html_deferred)}
4778sub do_cmd_raggedleft{&declared_env('flushright',$_[0],$tex2html_deferred)}
4779
4780sub do_env_verse { &declared_env('verse',@_) }
4781sub do_env_quote { &declared_env('quote', @_) }
4782sub do_env_quotation { &declared_env('quote', @_) }
4783sub do_env_tex2html_preform { &declared_env('preform', @_) }
4784sub do_env_tex2html_ord { &declared_env('ord', @_) }
4785sub do_env_tex2html_unord { &declared_env('unord', @_) }
4786sub do_env_tex2html_desc { &declared_env('desc', @_) }
4787
4788
4789# Modifies $contents
4790sub process_command {
4791    # MRO: modified to use $_[1]
4792    # local ($cmd_rx, *ref_contents) = @_;
4793    local ($cmd_rx) = @_;
4794    local($ref_before, $cmd , $pc_after);
4795    local($cmd_sub, $cmd_msub, $cmd_trans, $mathentity);
4796    local (@open_font_tags,@open_size_tags);
4797    $_[1] = &convert_iso_latin_chars($_[1])
4798	unless (($cmd =~ /(Make)?([Uu]pp|[Ll]ow)ercase/)||
4799	    ((!$cmd)&&($_[1] =~ /^\\(Make)?([Uu]pp|[Ll]ow)ercase/s)));
4800
4801    local(@ref_processed);
4802    for (;;) {			# Do NOT use the o option
4803	last unless ($_[1] =~ /$cmd_rx/ );
4804	print ".";
4805	#JCL(jcl-del) - use new regexp form which handles white space
4806	($ref_before, $cmd, $pc_after) = ($`, $1.$2, $4.$');
4807	push(@ref_processed,$ref_before);
4808#print "\nAFTER:$1.$2:".$4."\n" if ($cmd_rx eq $single_cmd_rx);
4809	print STDOUT "$cmd" if ($VERBOSITY > 2);
4810	print STDOUT "\nIN: $_[1]\n" if ($VERBOSITY > 6);
4811	#
4812	if ( $cmd = &normalize($cmd,$pc_after) ) {
4813	    ($cmd_sub, $cmd_msub, $cmd_trans, $mathentity) =
4814		("do_cmd_$cmd", "do_math_cmd_$cmd"
4815		, $declarations{$cmd}, $mathentities{$cmd});
4816	    if ($new_command{$cmd}||$renew_command{$cmd}) {
4817                # e.g. some \the$counter
4818		local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
4819		&make_unique($body) if ($body =~ /$O/);
4820		if ($argn) {
4821		    do {
4822			local($before) = '';
4823			local($_) = "\\$cmd ".$pc_after;
4824			# &substitute_newcmd  may need what comes after the $cmd
4825			# from the value of $after
4826			#RRM: maybe best to pass it as a parameter ?
4827			my $keep_after = $after;
4828			$after = $pc_after;
4829			$pc_after = &substitute_newcmd;   # may change $after
4830			$pc_after =~ s/\\\@#\@\@/\\/o ;
4831			$pc_after .= $after;
4832			$after = $keep_after;
4833		    }
4834		} else {
4835		    $pc_after = $body . $pc_after;
4836		}
4837	    } elsif (defined &$cmd_sub) {
4838		# $ref_before may also be modified ...
4839		if ($cmd =~ /$sizechange_rx/o) {
4840		    $pc_after = &$cmd_sub($pc_after, $open_tags_R);
4841		} else {
4842		    $pc_after = &$cmd_sub($pc_after, $open_tags_R);
4843		};
4844	    } elsif ((defined &$cmd_msub)&&!$NO_SIMPLE_MATH) {
4845#print "\nMCMD:$cmd_msub :  ";
4846		# $ref_before may also be modified ...
4847		$pc_after = &$cmd_msub($pc_after, $open_tags_R);
4848		if ( !$math_mode ) {
4849		    $pc_after = "<MATH>" . $pc_after . "</MATH>";
4850		    ++$commands_outside_math{$cmd};
4851		};
4852	    } elsif ($cmd_trans) { # One to one transform
4853#print "\nCMD-DECL: $inside_tabular : $cmd_trans". join(',',@$open_tags_R);
4854		if ($inside_tabular) {
4855		    push (@ref_processed , "\\$cmd ")
4856		} else {
4857		    $cmd_trans =~ m|</.*$|;
4858		    $pc_after = $` . $pc_after unless ($` =~ /^<>/);
4859		    push(@$open_tags_R, $cmd)
4860			if ($cmd =~ /$fontchange_rx|$fontweight_rx|$sizechange_rx/o);
4861		}
4862	    } elsif ($mathentity) {
4863#print "\nM-ENT:$mathentity :  ";
4864		if ( $math_mode ) {
4865		    $pc_after = "&$mathentity#$cmd;" . $pc_after;
4866		} elsif ($NO_SIMPLE_MATH) {
4867		    $pc_after = "&$mathentity#$cmd;" . $pc_after;
4868#		    ++$commands_outside_math{$cmd};
4869		} else {
4870		    $pc_after = "<MATH>&$mathentity#$cmd;</MATH>" . $pc_after;
4871		    ++$commands_outside_math{$cmd};
4872		}
4873	    } elsif ($ignore{$cmd}) { # Ignored command
4874		print "\nignoring \\$cmd" if $VERBOSITY > 5;
4875		$pc_after = join('', " ", $pc_after) if ($cmd eq " "); # catches `\ '
4876		$pc_after = join(''," ", $pc_after)
4877		    if (($cmd eq ',')&&($pc_after =~ /^\-/s)&&($ref_before =~/\-$/s));
4878	    } elsif ($cmd =~ /^the(.+)$/){
4879		$counter = $1;
4880		local($tmp)="do_cmd_$cmd";
4881		if (defined &$tmp) { # Counter
4882		    $pc_after = &do_cmd_thecounter($pc_after);
4883		} else {
4884		    if (defined $failed) {
4885			$failed = 1;
4886#			$ref_before .= "$cmd";
4887			push(@ref_processed,$cmd);  # $ref_before .= "$cmd";
4888		    } else {  &declare_unknown_cmd($cmd) }
4889#		    $ref_before .= "$cmd" if ($failed);
4890		}
4891	    } elsif ($cmd eq "\n") { push(@ref_processed," ");  # $ref_before .= " ";
4892	    } else {
4893		# Do not add if reading an auxiliary file
4894		if (defined $failed) {
4895		    $failed = 1;
4896		} else { &declare_unknown_cmd($cmd) }
4897	    }
4898	} else {
4899	    # &normalize should have already handled it adequately
4900	    # '\ ' (space) gets thru to here. Perhaps some others too ?
4901#	    print "\n ?? This should not happen: \\$cmd ??\n";
4902	}
4903#	$_[1] = join('', $ref_before, $pc_after);
4904	$_[1] = $pc_after;
4905	print STDOUT "\n-> $ref_before\n" if ($VERBOSITY > 6);
4906    }
4907    $_[1] = join('',@ref_processed).$_[1];
4908}
4909
4910sub declare_unknown_cmd {
4911    local($this_cmd) = @_;
4912    local($tmp) = "wrap_cmd_$cmd";
4913    do { ++$unknown_commands{$cmd};
4914	print STDOUT "\n*** Unknown command[1]: \\$cmd *** \n"
4915	    if ($VERBOSITY > 2);
4916    } unless ($AUX_FILE||(defined &$tmp)||($image_switch_rx=~/\b\Q$cmd\E\b/));
4917}
4918
4919
4920# This makes images from the code for math-entities,
4921# when $NO_SIMPLE_MATH is set and the  math  extension is loaded.
4922#
4923sub replace_math_constructions {
4924    local($math_mode) = @_;
4925    &make_math_box_images($math_mode) if (/<BOX>/);
4926    &make_math_entity_images($math_mode) if (/\&\w+#\w+;/);
4927}
4928
4929sub make_math_box_images {
4930    local($math_mode) = @_;
4931    local($pre,$this,$post,$tmp) = ('','','');
4932    local($slevel,$blevel) = 0;
4933
4934    while (/<BOX>/) {
4935	$pre .= $`; $tmp = $`; $this = ''; $post = $';
4936	# compute the super/sub-scripting level for each entity
4937	$tmp =~ s/<(\/?)SU[BP]>/
4938	    if ($1) { $slevel--} else { $slevel++};''/eog;
4939
4940	$tmp = $post;
4941	if ($tmp =~ /<(\/?)BOX>/o ) {
4942	    if ($1) { $this = $`; $post = $' }
4943	    else { $failed = 1 } # nested box, too complicated !
4944	} else {
4945	    &write_warnings("\nLost end of a <BOX> ?");
4946	    $failed = 1;
4947	}
4948	last if ($failed);
4949
4950	($this,$_) = &process_box_in_latex(
4951		    $math_mode, $slevel, $this, $post);
4952	$_ =~ s/^\s*//; # remove any leading spaces
4953	$pre .= $this ."\001";
4954    }
4955    return  if ($failed);
4956    $_ = $pre . $_;
4957}
4958
4959sub make_math_entity_images {
4960    local($math_mode) = @_;
4961    local($pre,$this,$post,$tmp) = ('','','');
4962    local($slevel) = 0;
4963    # compute the super/sub-scripting level for each entity
4964    while (/\&\w+#(\w+);/) {
4965	$pre .= $`; $tmp = $`; $this = $1; $post = $';
4966	$tmp =~ s/<(\/?)SU[BP]>/
4967	    if ($1) { $slevel--} else { $slevel++};''/eog;
4968	($this,$_) = &process_entity_in_latex(
4969		$math_mode, $slevel, $this, $post);
4970	$_ =~ s/^\s*//; # remove any leading spaces
4971	$pre .= $this ."\001";
4972    }
4973    $_ = $pre . $_;
4974}
4975
4976
4977#RRM:  Revert a math-entity to create image using LaTeX, together with
4978# any super/sub-scripts (possibly nested or with \limits ).
4979# Must also get the correct  \display/text/(script)script  style.
4980#
4981sub process_entity_in_latex {
4982    local($mode,$level,$entity,$after) = @_;
4983    local($math_style,$supsub,$rest) = ('','','');
4984    $level++ if ($mode =~/box/); # for top/bottom of inline fractions, etc.
4985
4986    if ($level) {
4987	$math_style = "\\". (($level > 1) ? "script" : "")."scriptstyle"
4988    } else {
4989	$math_style = "\\displaystyle" unless ($mode =~ /inline/);
4990    }
4991    while ($after =~ s/^\s*((\\limits|\&limits;)?\s*<SU(P|B)>)\s*/$supsub .= $1;''/eo) {
4992	local($slevel) = 1;
4993	local($aftersupb) = '';
4994	while ($slevel) {
4995	    $after =~ s/(<(\/)SU(B|P)>)/($2)? $slevel-- : $slevel++;''/oe;
4996	    $supsub .= $`.$&;
4997	    $aftersupb = $';
4998	}
4999	$after = $aftersupb;
5000    }
5001
5002    local($latex_code) = "\$$math_style\\$entity$supsub\$";
5003
5004    $global{'max_id'}++;
5005    ( &process_undefined_environment('tex2html_wrap_inline'
5006	     ,$global{'max_id'}, $latex_code ) , $after);
5007}
5008
5009sub process_box_in_latex {
5010    local($mode,$level,$inside,$after) = @_;
5011    local($math_style,$which,$pre,$post,$tmp) = ('','',"\{","\}");
5012
5013    if ($level) {
5014	$math_style = "\\". (($level > 1) ? "script" : "")."scriptstyle"
5015    } else {
5016	$math_style = "\\displaystyle" unless ($mode =~ /inline/);
5017    }
5018
5019    if ($inside =~ /<((LEFT)|(RIGHT))>/ ) {
5020	$pre = "\\left"; $post = "\\right";
5021	if ($2) {
5022	    $tmp = $`; $inside = $';
5023	    $pre .= (($tmp) ? $tmp : ".") . "\{";
5024	    if ( $inside =~ /<RIGHT>/ ) {
5025		$tmp = $';
5026		$inside = $`;
5027		$post = "\}". (($tmp) ? $tmp : ".");
5028	    }
5029	} else {
5030	    $pre .= ".\{"; $tmp = $'; $inside = $`;
5031	    $post = "\}". (($tmp) ? $tmp : ".");
5032	}
5033    }
5034    if ( $inside =~ /<((OVER)|(ATOP)|(CHOOSE))>/ ) {
5035	$pre .= $`;
5036	$post = $' . $post ;
5037	if ($2) { $which = "over " }
5038	elsif ($3) { $which = "atop " }
5039	elsif ($4) { $which = "atopwithdelims\(\)" }
5040    }
5041
5042    local($latex_code) = join('', "\$" , $math_style , " ", $pre
5043	  , (($which)? "\\$which" : "") , $post , "\$" );
5044
5045    if ($after =~ s/<SUP ALIGN=\"CENTER\">([^<]*)<\/SUP>/
5046	$tmp =$1;''/eo ) {
5047	$latex_code = join('', "\\stackrel" , $latex_code
5048			   , "\{" , $tmp , "\}" );
5049    }
5050
5051    $global{'max_id'}++;
5052    ( &process_undefined_environment('tex2html_wrap_inline'
5053	     ,$global{'max_id'}, $latex_code ) , $after);
5054}
5055
5056####################### Processing Meta Commands ############################
5057# This is a specialised version of process_command above.
5058# The special commands (newcommand, newenvironment etc.)
5059# must be processed before translating their arguments,
5060# and before we cut up the document into sections
5061# (there might be sectioning commands in the new definitions etc.).
5062# \newtheorem commands are treated during normal processing by
5063# generating code for the environments they define.
5064
5065sub substitute_meta_cmds {
5066    local ($next_def);
5067    local ($cmd, $arg, $argn, $opt, $body, $before, $xafter);
5068    local ($new_cmd_no_delim_rx, $new_cmd_rx, $new_env_rx, $new_cmd_or_env_rx);
5069    local ($new_end_env_rx);
5070    &tokenize($meta_cmd_rx);	#JCL(jcl-del) - put delimiter after meta command
5071    print "\nProcessing macros ..." if (%new_command || %new_environment);
5072    # First complete any replacement left-over from the previous part.
5073    if ($UNFINISHED_ENV) {
5074	s/$UNFINISHED_ENV/$REPLACE_END_ENV/;
5075	$UNFINISHED_ENV = '';
5076	$REPLACE_END_ENV = '';
5077    }
5078
5079    local(@processed);
5080    local($processed, $before, $after)=('', '', $_);
5081    while ($after =~ /$meta_cmd_rx$EOL/o) {	# ... and uses the delimiter
5082	($cmd, $after) = ($1.$2, $');
5083	$before .= $`;
5084#	$next_def = '';
5085	if (!($before =~ /$meta_cmd_rx$EOL/)) {
5086	    push(@processed, $before); $before = '';
5087	}
5088
5089	print ",";
5090#	$next_def = "\\$cmd" unless (($cmd =~ /renewcommand/));
5091	local($cmd_sub) = "get_body_$cmd";
5092	if (defined &$cmd_sub) {
5093#	    $processed = &$cmd_sub(*after);
5094	    $processed = &$cmd_sub(\$after);
5095#	    if ($processed) { $after = $before . $processed; }
5096#	    $next_def = ''
5097#		if (($PREAMBLE > 1)&&($cmd =~ /(re)?newcommand/));
5098#	    &add_to_preamble($cmd, $next_def)
5099#		unless ($next_def =~ /^\s*$/);
5100### new style of handling meta-commands
5101	    if ($processed) { push(@processed, "\\".$processed) }
5102	}
5103	elsif ($before) {
5104	    # this shouldn't happen !!
5105	    print STDERR "\nCannot handle \\$cmd , since there is no $cmd_sub ";
5106	    $after = $before . $cmd . $after;
5107	    $before = '';
5108	} else {
5109	    push(@processed, "\\$cmd ") if $cmd;
5110	}
5111    }
5112    print "\nmeta-commands: ". (0+@processed) ." found "
5113	if ((@processed)&&($VERBOSITY > 1));
5114    $_ = join('',@processed, $after); undef @processed;
5115    if ($PREAMBLE) {
5116	# MRO: replaced $* with /m
5117        s/((\n$comment_mark\d*)+\n)//gm;
5118        s/(\\par\b\s*\n?)+/\\par\n/gm;
5119        s/(\\par\b\n?)+/\\par\n/gm;
5120    }
5121
5122    # hard-code the new-command replacements for these
5123    $new_command{'begingroup'} = "0:!:\\begin<<0>>tex2html_begingroup<<0>>:!:}";
5124    $new_command{'endgroup'} = "0:!:\\end<<0>>tex2html_begingroup<<0>>:!:}";
5125    $new_command{'bgroup'} = "0:!:\\begin<<0>>tex2html_bgroup<<0>>:!:}";
5126    $new_command{'egroup'} = "0:!:\\end<<0>>tex2html_bgroup<<0>>:!:}";
5127
5128    # All the definitions have now moved to the $preamble and their bodies
5129    # are stored in %new_command and %new_environment
5130    #
5131    # Now substitute the new commands and environments:
5132    # (must do them all together because of cross definitions)
5133    $new_cmd_rx = &make_new_cmd_rx(keys %new_command);
5134    $new_cmd_no_delim_rx = &make_new_cmd_no_delim_rx(keys %new_command);
5135    $new_env_rx = &make_new_env_rx;
5136    $new_end_env_rx = &make_new_end_env_rx;
5137#    $new_cnt_rx = &make_new_cnt_rx(keys %new_counter);
5138    $new_cmd_or_env_rx = join("|", $new_cmd_no_delim_rx." ", $new_env_rx);
5139#    $new_cmd_or_env_rx = join("|", $new_cmd_no_delim_rx." ", $new_env_rx, " ".$new_cnt_rx);
5140    $new_cmd_or_env_rx =~ s/^ \||\|$//;
5141
5142    print STDOUT "\nnew commands:\n" if ($VERBOSITY > 2);
5143    while (($cmd, $body) = each %new_command) {
5144	unless ($expanded{"CMD$cmd"}++) {
5145	    print STDOUT ".$cmd " if ($VERBOSITY > 2);
5146	    $new_command{$cmd} = &expand_body;
5147	    print STDOUT " ".$new_command{$cmd}."\n" if ($VERBOSITY > 4);
5148	    &write_mydb("new_command", $cmd, $new_command{$cmd});
5149	}
5150    }
5151
5152    print STDOUT "\nnew environments:\n" if ($VERBOSITY > 2);
5153    while (($cmd, $body) = each %new_environment) {
5154	unless ($expanded{"ENV$cmd"}++) {
5155	    print STDOUT ".$cmd" if ($VERBOSITY > 2);
5156	    $new_environment{$cmd} = &expand_body;
5157	    &write_mydb("new_environment", $cmd, $new_environment{$cmd});
5158	}
5159    }
5160
5161    print STDOUT "\nnew counters and dependencies:\n" if ($VERBOSITY > 2);
5162    &clear_mydb("dependent") if ($DEBUG);     #avoids appending to a previous version
5163    while (($cmd, $body) = each %dependent) {
5164	print STDOUT ".($cmd,$body)" if ($VERBOSITY > 2);
5165        &write_mydb("dependent", $cmd, $dependent{$cmd});
5166    }
5167    &clear_mydb("img_style") if ($DEBUG);     #avoids appending to a previous version
5168    while (($cmd, $body) = each %img_style) {
5169        &write_mydb("img_style", $cmd, $img_style{$cmd});
5170    }
5171
5172    &clear_mydb("depends_on") if ($DEBUG);     #avoids appending to a previous version
5173    while (($cmd, $body) = each %depends_on) {
5174	print STDOUT ".($cmd,$body)" if ($VERBOSITY > 2);
5175        &write_mydb("depends_on", $cmd, $depends_on{$cmd});
5176    }
5177
5178
5179    &clear_mydb("styleID") if ($DEBUG);     #avoids appending to a previous version
5180    while (($cmd, $body) = each %styleID) {
5181        &write_mydb("styleID", $cmd, $styleID{$cmd});
5182    }
5183
5184    &clear_mydb("env_style") if ($DEBUG);     #avoids appending to a previous version
5185    while (($cmd, $body) = each %env_style) {
5186        &write_mydb("env_style", $cmd, $env_style{$cmd});
5187    }
5188    &clear_mydb("txt_style") if ($DEBUG);     #avoids appending to a previous version
5189    while (($cmd, $body) = each %txt_style) {
5190        &write_mydb("txt_style", $cmd, $txt_style{$cmd});
5191    }
5192
5193    print STDOUT "\ntheorem counters:\n" if ($VERBOSITY > 2);
5194    &clear_mydb("new_theorem") if ($DEBUG);     #avoids appending to a previous version
5195    while (($cmd, $body) = each %new_theorem) {
5196	print STDOUT ".($cmd,$body)" if ($VERBOSITY > 2);
5197        &write_mydb("new_theorem", $cmd, $new_theorem{$cmd});
5198    }
5199
5200
5201    print "+";
5202    if (length($new_env_rx)) {
5203	local(@pieces);
5204        print STDOUT "\nsubstituting new environments: $new_env_rx\n" if ($VERBOSITY > 3);
5205#	while (/\n?$new_env_rx/ && (($before, $cmd, $after) = ($`, $2, $'))) {
5206	while (/$new_env_rx/ && (($before, $cmd, $after) = ($`, $2, $'))) {
5207	    print STDOUT ",";
5208	    print STDOUT "{$cmd}" if ($VERBOSITY > 1);
5209	    if (!($before =~ /$new_env_rx/)) {
5210		push (@pieces, $before); $before = ''; print "{}";
5211	    }
5212	    $_ = join('',$before, &substitute_newenv);
5213	}
5214	print "\n ".(0+@pieces). " new environments replaced\n" if (@pieces);
5215	$_ = join('', @pieces, $_); undef @pieces;
5216    }
5217
5218
5219    print "+";
5220    if (length($new_cmd_rx)) {
5221	print STDOUT "\ntokenizing: $new_cmd_rx\n" if ($VERBOSITY > 2);
5222	&tokenize($new_cmd_rx); # Put delimiter after the new commands
5223
5224	# and use the delimiter.
5225	print STDOUT "\nsubstituting new commands: $new_cmd_rx\n" if ($VERBOSITY > 2);
5226	print STDOUT "\ninitial size: ".length($after) if ($VERBOSITY > 1);
5227	# store processed pieces in an array
5228	local($this_cmd, @pieces);
5229	# speed-up processing of long files by splitting into smaller segments
5230	# but don't split within the preamble, else \newenvironment may break
5231	local($pre_segment,@segments); &make_sections_rx;
5232	local($within_preamble,$this_section) = 1 if ($PREAMBLE>1);
5233	while (/$sections_rx/) {
5234	    $pre_segment .= $`; $_ = $'; $this_section = $&;
5235	    do {
5236		push(@segments,$pre_segment);
5237		$pre_segment = '';
5238	    } unless ($within_preamble);
5239	    $within_preamble = 0 if ($within_preamble && ($pre_segment =~
5240		    /\\(startdocument|begin\s*($O\d+${C})\s*document\s*\2)/));
5241	    $pre_segment .= $this_section;
5242	}
5243	push(@segments,$pre_segment.$_);
5244	local($replacements,$seg) ; $before = ''; # count the segments
5245	local($within_preamble) = 1 if ($PREAMBLE>1);
5246	foreach $after (@segments) {
5247	  while ($after =~ /(\\(expandafter|noexpand)\b\s*)?$new_cmd_no_delim_rx\b\s?/) {
5248	    ($before, $xafter, $cmd, $after) = ($`, $2, $3, $');
5249	    $within_preamble = 0
5250		if ($before =~ /\\(startdocument|begin\s*($O\d+${C})\s*document\s*\2)/);
5251	    push(@pieces, $before);
5252	    print "."; ++$replacements;
5253	    print STDOUT "$cmd" if ($VERBOSITY > 2);
5254	    if ($xafter =~ /no/) { $this_cmd = "\\\@#\@\@".$cmd  }
5255	    elsif (($xafter =~ /after/)&&($after =~ /^\s*\\/)) {
5256		local($delayed) = $cmd;
5257		local($nextcmd);
5258		$after =~ s/^\s*\\([a-zA-Z]+|.)/$nextcmd = $1;''/eo;
5259		($cmd,$nextcmd) = ($nextcmd, "do_cmd_$nextcmd");
5260		if (defined &$nextcmd) { $after = &$nextcmd($after); }
5261		elsif ($new_command{$cmd}) {
5262		    local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
5263		    &make_unique($body) if ($body =~ /$O/);
5264		    if ($argn) {
5265			do {
5266			    local($before) = '';
5267			    $after = join('',&substitute_newcmd, $after);
5268			    $after =~ s/\\\@#\@\@/\\/o ;
5269			};
5270		    } else { $after = $body . $after; }
5271		} else { print "\nUNKNOWN COMMAND: $cmd "; }
5272		$cmd = $delayed;
5273		if ($new_command{$cmd}) {
5274		    if ($renew_command{$cmd}) {
5275#			# must wrap it in a deferred environment
5276#			$this_cmd = join('', &make_deferred_wrapper(1)
5277#				,"\\$cmd".(($cmd =~ /\w$/)? " ":'')
5278#				, &make_deferred_wrapper(0));
5279#			push(@pieces, $this_cmd); $this_cmd = '';
5280			push(@pieces, "\\$cmd".(($cmd =~ /\w$/)? " ":''));
5281			$this_cmd = '';
5282		    } elsif ($provide_command{$cmd}&&$within_preamble) {
5283			# leave it alone
5284			push(@pieces, "\\$cmd".(($cmd =~ /\w$/)? " ":''));
5285			$this_cmd = '';
5286		    } else {
5287			# do the substitution
5288			$this_cmd = &substitute_newcmd;
5289		    }
5290		}
5291	    } elsif ($renew_command{$cmd}) {
5292		# leave it alone
5293		push(@pieces, "\\$cmd".(($cmd =~ /\w$/)? " ":''));
5294		$this_cmd = '';
5295	    } elsif (($provide_command{$cmd})&&($within_preamble)) {
5296		# leave it alone
5297		push(@pieces, "\\$cmd".(($cmd =~ /\w$/)? " ":''));
5298		$this_cmd = '';
5299	    } else {
5300		# do the substitution
5301		$this_cmd = &substitute_newcmd if ($new_command{$cmd});
5302	    }
5303	    if ($this_cmd =~ /(\\(expandafter|noexpand)\s*)?$new_cmd_no_delim_rx\b\s?/)
5304	        { $after = $this_cmd . $after }
5305	    elsif ($this_cmd) { push(@pieces, $this_cmd) }
5306	  }
5307	  push(@pieces, $after);
5308	}
5309	print " $replacements new-command replacements\n"
5310	    if (($VERBOSITY>1) && $replacements);
5311	# recombine the processed pieces
5312	$_ = join('', @pieces); undef @pieces;
5313        print STDOUT ", resulting size: ".length($_)." " if ($VERBOSITY > 1);
5314	$_ =~ s/\\\@#\@\@/\\/go;
5315    }
5316
5317    print STDOUT "\n *** substituting metacommands done ***\n" if ($VERBOSITY > 3);
5318}
5319
5320sub insert_command_expansion {
5321    ($xafter, $cmd) = @_;
5322#   push(@pieces, $_[1]);
5323    print ".$cmd";
5324    print STDOUT "$_[3]" if ($VERBOSITY > 2);
5325#   $xafter = $_[2];
5326#   $cmd = $_[3];
5327    if ($xafter =~ /no/) { $this_cmd = "\\\@#\@\@".$cmd }
5328    elsif (($xafter =~ /after/)&&($after =~ /^\s*\\/)) {
5329	local($delayed,$nextcmd) = ($_[3],'');
5330
5331	$after =~ s/^\s*\\([a-zA-Z]+|.)/$nextcmd = $1;''/eo;
5332	($cmd,$nextcmd) = ($nextcmd, "do_cmd_$nextcmd");
5333	if (defined &$nextcmd) { $after = &$nextcmd($after); }
5334	elsif ($new_command{$cmd}) {
5335	    local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
5336	    &make_unique($body) if ($body =~ /$O/);
5337	    if ($argn) {
5338		do {
5339		    local($before) = '';
5340		    $after = join('',&substitute_newcmd, $after);
5341		    $after =~ s/\\\@#\@\@/\\/o ;
5342		};
5343	    } else { $after = $body . $after; }
5344	} else { print "\nUNKNOWN COMMAND: $cmd "; }
5345	$cmd = $delayed;
5346	$this_cmd = &substitute_newcmd if ($new_command{$cmd});
5347    } else {
5348	$this_cmd = &substitute_newcmd if ($new_command{$cmd});
5349    }
5350#   if ($this_cmd =~ /(\\(expandafter|noexpand)\s*)?$new_cmd_no_delim_rx\s?/){
5351#	$after = $this_cmd . $after
5352#   } else { push(@pieces, $this_cmd); }
5353    $this_cmd;
5354}
5355
5356
5357sub expand_body {
5358    return unless length($new_cmd_or_env_rx);
5359    local($_) = $body;
5360    local($cmd,$saveafter,$avoid_looping);
5361    # Uses $before, $body, $arg, etc. of the caller, but not $cmd.
5362    # Uses $new_cmd_rx (resp. $new_cmd_no_delim_rx) and $new_env_rx
5363    # set in the caller, of which one might be empty.
5364
5365    # Puts delimiter after the new commands ...
5366    &tokenize($new_cmd_rx) if length($new_cmd_rx);
5367
5368    while (/$new_cmd_or_env_rx/) {
5369	# $new_cmd_rx binds $1, and $new_env_rx binds $3.
5370	($before,$cmd,$after,$saveafter) = ($`,$1.$3,$',$');
5371	if (length($new_command{$cmd})) { # We have a command
5372	    # this tokenizes again
5373	    local($replace) = &substitute_newcmd; # sets $_, changes $after
5374	    if (!($replace)) {
5375		# protect name of unexpanded macro
5376		$_ = join('', $before ,"\\@#@@", $cmd, $saveafter );
5377	    } else {
5378		$_ = join('', $before , $replace, $after );
5379	    }
5380	} elsif (length($new_environment{$cmd})) {
5381	    $_ = join('',$before, &substitute_newenv);
5382	}
5383	last if $avoid_looping;
5384    }
5385    # remove protection from unreplaced macro names
5386    s/\\\@#\@\@/\\/go;
5387
5388    # remove trivial comments
5389    s/(\\\w+)$comment_mark\d*\n[ \t]*/$1 /go;
5390    s/$comment_mark\d*\n[ \t]*//go;
5391#    s/($O\d+$C)?($comment_mark\n)[ \t]*/($1 ? $1.$2 : '')/eg;
5392
5393    $_;
5394}
5395
5396
5397sub substitute_newcmd {
5398    # Modifies $after in the caller
5399    # Get the body from the new_command array
5400    local($tmp,$cnt,$saved, $arg, $isword) = ('',0,$cmd);
5401    local($argn, $_, $opt) = split(/:!:/, $new_command{$cmd});
5402    $avoid_looping = 1 if ($new_command{$cmd} =~ /\\$cmd\b/);
5403
5404    &tokenize($new_cmd_rx); # must do it again for newly inserted cmd bodies
5405    print STDOUT "\nNEW:$cmd:$_" if ($VERBOSITY > 5);
5406    foreach $i (1..$argn) {
5407	$arg = $isword = '';
5408	if ($i == 1 && $opt ne '}') {
5409	    $arg = ($after =~ s/$optional_arg_rx//o) ? $1 : $opt;
5410	}
5411	else {
5412	    # Get the next argument, if not in braces, get next character
5413	    #RRM: allow also for processed braces, in case substitution
5414	    #     was delayed; e.g. by \renewcommand
5415	    if (!(($after =~ s/$next_pair_rx/$arg = $2;''/e)
5416		  ||($after =~ s/$next_pair_pr_rx/$arg = $2;''/e))) {
5417		$after =~ s/^\s*(\\[a-zA-Z]+|.)/$arg = $1;''/e;
5418	    }
5419	    if ($arg eq '#') {
5420		&write_warnings("\nSubstitution of arg to $cmd delayed.");
5421		$_ = "\\\@#\@\@$saved";
5422		return ();
5423	    };
5424	}
5425	$arg =~ s/(^|\G|[^\\])\\\#/$1$hash_mark/gs;
5426	$arg =~ s/\#/$param_mark/gs;
5427
5428	#RRM: Substitute the arguments in the body one at a time
5429	#     else multiple instances would fail in  &make_unique
5430
5431	# First protect ## parameters in TeX-like substitutions
5432	# suggested by Dirk Pleiter (Berlin)
5433	s/((^|[^\\])(\\\\)*)\#\#$i/$1$protected_hash/gs;
5434	$tmp = $_;
5435	$cnt = $tmp =~ s/\#$i//g ;
5436	$isword = 1 if ($arg =~ /^\w/);
5437	if ($cnt > 1 ) {
5438	    $tmp = $_;
5439	    while ($cnt > 1) {
5440		if ( s/(\\\w+)?\#$i/(($1&&$isword)? $1.' ': '').$arg/e) {
5441		    &make_unique($_) if ($arg =~ /$O/ );
5442		    &make_unique_p($_) if ($arg =~ /$OP/ );
5443		}
5444		$cnt--;
5445	    }
5446	    $tmp = $_;
5447	}
5448#	s/(\\\w+)?\#$i/(($1&&$isword)? $1.' ': '').$arg/e ;
5449	s/(\\\w+)?\#$i/$1.(($1&&$isword)? ' ': '').$arg/e ;
5450	print "\n *** substitution: $arg \nfor \#$i in \\$cmd did not take ***\n"
5451	   if (/\#$i/);
5452	&write_warnings("incomplete substitution in a \\$cmd command:\n$_") if (/\#$i/);
5453	s/$protected_hash/\#$i/g;
5454    }
5455    s/$param_mark/\#/g;
5456    s/$hash_mark/\\\#/g;
5457    s/(\\\w+)$/$1 /s;
5458
5459    # Make the body unique (give unique id's to the brackets),
5460    # translate, and return it
5461    &make_unique($_);
5462    if ($avoid_looping) {
5463	s/\\$cmd\b/\\csname $cmd\\endcsname/g;
5464	print STDERR "\n *** possible looping with new-command \\$cmd ***\n";
5465	&write_warnings("\npossible looping with new-command \\$cmd ");
5466    }
5467    print STDOUT "\nOUT:$cmd:$_" if ($VERBOSITY > 5);
5468
5469# Insert a space to prevent letters from clashing together with a
5470# letter command. Consider this:
5471# New command substitution is restricted to commands introduced by
5472# \newcommand etc. (so-called meta commands), but it is not done
5473# for already defined commands, eg. \large.
5474# But new command, as well as new environment, substitution is done
5475# prior to any other substitution.
5476# So \newcommand{\this}{...} {\large\this b} will get expanded the
5477# following way:
5478# 1. \newcommand{\this}{...}
5479#    is handled by &substitute_meta_cmds, it gets the definition
5480#    of \this and stores it within a table, %new_command.
5481#    After all new commands are recognized, &expand_body is called
5482#    to expand one command body from each other. That's O(n*n)!
5483# 2. A regular expression $new_cmd_rx is built containing a pattern
5484#    that matches all occurrences of a properly delimited \this
5485#    macro. When matching, ensuing white space gets lost.
5486#    (But only for letter commands, see also &make_new_cmd_rx.)
5487#    Another regular expression called $new_cmd_no_delim_rx is built
5488#    which matches exact the \this, and would also match the prefix
5489#    of \thisx.
5490# 3. The *whole* text is tokenized using $new_cmd_rx, with separates
5491#    \this from the ensuing text by one white space.
5492# 4. Then $new_cmd_no_delim_rx together with the delimiting space
5493#    is used to substitute \this with its body.
5494# 5. The following situations may occur:
5495#  a) ... is some text (no macros) => {\large<text>yyy}
5496#     Then we must prevent that the text clashes into \large.
5497#     This is only dangerous when <text> begins with a letter.
5498#  b) ... contains another, not expanded new command.
5499#     This happens during &expand_body.
5500#     In this case, make sure to &tokenize the body before giving
5501#     the result to the caller. Also take care that leading letters
5502#     of the body cannot clash into \large.
5503#  e) ... contains a macro not known as new command:
5504#     Make sure that the macro cannot clash with the ensuing yyy.
5505#  f) ... is empty:
5506#     Make sure that \large cannot clash with yyy.
5507# 6. We prevent clashing by inserting a delimiting blank.
5508#    Out of the scetched situation, there are three conditions to
5509#    take care of:
5510#  a) empty body, left a letter command, right a letter => blank
5511#  b) body starts with letter, left a letter command    => blank
5512#  c) body ends with letter command, right a letter     => blank
5513#  d) else => no blank, clash all together, it will work.
5514# 7. With this rules, the expansion should work quite well,
5515#    concerning letter/non-letter commands and white space
5516#    handling.
5517# 8. Deficiencies:
5518# 8.1 Consider \this<CR>that. It's handled this way:
5519#  a) The \this swallows the <CR> in LaTeX, but what LaTeX2HTML does
5520#     is to &tokenize the expression into \this <CR>that.
5521#  b) If ... is some text, it results in <text><CR>that.
5522#  c) If ... is a macro (or command, or control sequence, these
5523#     terms are often mixed up, but effectively mean the same),
5524#     then if the macro later takes at least one argument, the <CR>
5525#     might get swallowed, this depends on the grace of $next_pair_rx
5526#     resp. $next_pair_pr_rx.
5527#     If the macro takes no arguments, the <CR> remains in the text.
5528#  d) If ... ends in another new command, the problem repeats.
5529# 8.2 The new commands are substituted in a very insensitive way.
5530#     If \this occurs within an environment which sees \this
5531#     totally different, there's no chance to substitute \this in
5532#     a different way.
5533# 8.3 In relation to 8.2 a similar problem arises when the meta
5534#     command, or several meta commands redefining \this, occur
5535#     amongst several \this macros.
5536# 8.4 In raw TeX like environments it is not possible to revert the
5537#     expansion of \this, but \this probably *must* occur in its
5538#     raw form.
5539
5540# Handle the cases as depicted in the description of new command
5541# substitution.
5542    local($befdel,$aftdel);
5543    $befdel = ' '
5544	if ($before=~/(^|[^\\])\\[a-zA-Z]+$/ && /^$/ && $after=~/^[a-zA-Z]/) ||
5545	    ($before=~/(^|[^\\])\\[a-zA-Z]+$/ && /^[a-zA-Z]/);
5546    $aftdel = ' '
5547	if /(^|[^\\])\\[a-zA-Z]+$/s && $after=~/^[a-zA-Z]/;
5548    join('', $befdel, $_, $aftdel);
5549}
5550
5551#RRM:  use this to test whether a specific command is substituting correctly
5552sub trace_cmd {
5553    local($this) = @_;
5554    if ($cmd eq $this) { print "\n$1=>$id:$2::"}
5555}
5556
5557# Make the text unique (give unique id's to the brackets).
5558# The text shouldn't contain processed brackets.
5559sub make_unique {
5560    # MRO: Change to references $_[0]
5561    # local(*_) = @_;
5562    my $id = $global{'max_id'};
5563    # MRO: replaced $* by /m
5564    # this looks quite funny but is optimized
5565    1 while($_[0] =~ s/$O(\d+)$C([\w\W]*)$O\1$C/$id++;"\000$id $2\000$id "/geom);
5566    $_[0] =~ s/\000(\d+) /$O$1$C/gom;
5567    $global{'max_id'} = $id;
5568}
5569
5570#RRM: this shouldn't be needed, but just in case...
5571sub make_unique_p {
5572    # MRO: Change to references $_[0]
5573    my $id = $global{'max_id'};
5574    # MRO: replaced $* by /m
5575    # this looks quite funny but is optimized
5576    1 while($_[0] =~ s/$OP(\d+)$CP([\w\W]*)$OP\1$CP/$id++;"\000$id $2\000$id "/geom);
5577    $_[0] =~ s/\000(\d+) /$OP$1$CP/gom;
5578    $global{'max_id'} = $id;
5579}
5580
5581
5582sub substitute_newenv {
5583    # Modifies $cmd and $after in the caller
5584    # Get the body from the new_environment array
5585    local($argn, $begdef, $enddef, $opt) = split(/:!:/, $new_environment{$cmd});
5586    local($arg,$new_def_rx,$tmp,$cnt);
5587
5588    # Note that latex allows argument substitution only in the
5589    # \begin part of the new definition
5590    foreach $i (1..$argn) {	# Process the arguments
5591	if (($i == 1) && ($opt ne '}')) {
5592	    $after =~ s/$optional_arg_rx/$arg = $1;''/eo;
5593	    $arg = $opt unless $arg;
5594	}
5595	else {
5596	    $after =~ s/$next_pair_rx/$arg = $2;''/eo;
5597	}
5598	$arg =~ s/(^|[^\\])\\\#/$1$hash_mark/g;
5599	$arg =~ s/\#/$param_mark/g;
5600
5601        #RRM: multiple instances can fail later in  &make_unique
5602#       s/\#$i/$arg/g;          # Substitute the arguments in the body
5603        #RRM: ...so do one at a time and  &make_unique_p
5604        $tmp = $begdef;
5605        $cnt = $tmp =~ s/\#$i//g ;
5606        if ($cnt > 1) {
5607            $tmp = $begdef;
5608            while ($cnt > 1) {
5609		if ( $begdef =~ s/\#$i/$arg/) {
5610		    &make_unique($begdef) if ($arg =~ /$O/ );
5611		    &make_unique_p($begdef) if ($arg =~ /$OP/ );
5612		}
5613                $cnt--;
5614            }
5615            $tmp = $_;
5616        }
5617        $begdef =~ s/\#$i/$arg/ ;
5618        print "\n *** substitution: $arg \nfor \#$i in {$cmd} did not take ***\n"
5619           if ($begdef =~ /\#$i/);
5620	&write_warnings("incomplete substitution in a {$cmd} environment:\n$begdef")
5621	    if ($begdef =~ /\#$i/);
5622    }
5623    $begdef =~ s/$param_mark/\#/g;
5624    $begdef =~ s/$hash_mark/\\\#/g;
5625    $begdef =~ s/(\\\w+)$/$1 /s;
5626
5627    # Make the body unique (Give unique id's to the brackets),
5628    # translate, and return it
5629#RRM: when are these needed ?
5630#    $_ = &revert_to_raw_tex($_);
5631#    &pre_process;
5632
5633    &make_unique($begdef);		# Make bracket IDs unique
5634    print STDOUT "\nBEGIN:$cmd:$begdef" if ($VERBOSITY > 4);
5635
5636    # Now substitute the \end part:
5637#RRM: when are these needed ?
5638#    $_ = &revert_to_raw_tex($enddef);
5639#    &pre_process;
5640
5641    &make_unique($enddef);		# Make bracket IDs unique
5642    print STDOUT "\nEND:$cmd:$enddef" if (($enddef)&&($VERBOSITY > 4));
5643    $enddef =~ s/(\\\w+)$/$1 /s;
5644
5645    local($new_end_def_rx) = &make_end_env_rx($cmd);
5646    if (($enddef)&&!($after =~ s/\n?$new_end_def_rx/$enddef/ )) {
5647        $UNFINISHED_ENV = $new_end_def_rx;
5648        $REPLACE_END_ENV = $enddef;
5649    };
5650    join('',$begdef,$after);
5651}
5652
5653sub substitute_pars {
5654    s/((\%|$comment_mark\d*)|.)(\r*\n[ \t]*){2,}[ \t]*/$1\n\\par \n/og;
5655#    s/((\%|$comment_mark\d*)|\d|.)[\r\n\015]{2,}/print "\nPAR:".$`.$&;"$1\n\\par \n"/egs;
5656}
5657
5658sub do_cmd_end { #RRM:  catches the end of any unclosed environments
5659    local($_) = @_;
5660    &missing_braces unless (
5661	(s/$next_pair_pr_rx//o)||(s/$next_pair_rx//o));
5662    s/^\n//;
5663    $_;
5664}
5665
5666# Removes the definition from the input string,
5667# adds to the preamble unless it is part of the preamble already
5668# and stores the body in %new_command;
5669sub get_body_newcommand {
5670    local($newed, $n_after) = &process_body_newcommand(0,@_);
5671    (($PREAMBLE)? "newed".$newed : '');
5672}
5673
5674sub process_body_newcommand {
5675#    local($renewed,*_) = @_;
5676    local($renewed,$after_R) = @_;
5677    local($_) = $$after_R;
5678    local($no_change) = $_;
5679    local($argn,$newcmd,$cmd_br,$body,$body_br,$tmp,$tmp1,$opt,$pat);
5680    local($new_cmd) = 'command';
5681    if ($renewed =~ /provide/||$renewed == 2) {
5682	# $newcmd = &missing_braces unless (
5683	($newcmd,$pat) = &get_next(1) unless (
5684	        (s/$next_pair_pr_rx/$pat=$&;$newcmd=$2;''/e)
5685	        ||(s/$next_pair_rx/$pat=$&;$newcmd=$2;''/e));
5686	if (!$pat) {
5687	    local($br_id) = ++$global{'max_id'};
5688	    $pat = "$O$br_id$C".$newcmd."$O$br_id$C";
5689	}
5690    } else {
5691	($newcmd,$pat) = &get_next(1); # Get command name
5692    }
5693    $pat =~ s/\\//; $new_cmd .= $pat;
5694    $newcmd =~ s/^\s*\\//;
5695    ($argn,$pat) = &get_next(0);	# Get optional no. of args
5696    $argn = 0 unless $argn; $new_cmd .= $pat if $argn;
5697    local($cmd) = $newcmd;
5698
5699    # Get the body of the code and store it with the name and number of args
5700    # UNLESS THE COMMAND IS ALREADY DEFINED
5701    # ...in which case $ALLOW_REDEFINE must also have been set.  # RRM
5702    # (This is the mechanism with which raw html can be ignored in a Latex document
5703    # but be recognised as such by the translator).
5704    $opt = '}';			# Flag for no optional arg
5705    local($bodyA) = '';
5706    if (/^\[/) {
5707	($opt,$pat) = &get_next(0);
5708	$new_cmd .= $pat;
5709	$bodyA .= "\n".'($dummy, $pat) = &get_next_optional_argument;' .
5710                    "\n". '$args .= $pat;';
5711    }
5712    local($nargs) = $argn;
5713    while ($nargs > 0) { $nargs--;
5714	$bodyA .=
5715	    "\n".'$args .= $`.$& if ((s/$next_pair_pr_rx//o)||(s/$next_pair_rx//o));';
5716    }
5717    if ($renewed =~ /provide/||$renewed == 2 ) {
5718        $body = &missing_braces unless (
5719	        (s/$next_pair_pr_rx/$pat=$&;$body=$2;''/e)
5720	        ||(s/$next_pair_rx/$pat=$&;$body=$2;''/e));
5721	$new_cmd .= $pat;
5722    } else {
5723	($body,$pat) = &get_next(4);  #get the body
5724	$new_cmd .= $pat;
5725    }
5726
5727    local($thisone);
5728#    $thisone = 1 if ($cmd =~ /div|vec/);  # for debugging
5729
5730    $tmp = "do_cmd_$cmd";
5731    local($wtmp) = "wrap_cmd_$cmd";
5732    if ((defined &$tmp)||(defined &$wtmp)){
5733	# command already exists, so \providecommand  does nothing
5734	# but may still be needed in  images.tex
5735	$$after_R = $_;
5736	return ($new_cmd) if ($renewed =~ /provide/);
5737
5738	print "\n*** redefining \\$cmd ***\n";
5739	&write_warnings("\nredefining command \\$cmd ");
5740	if (!$ALLOW_REDEFINE) {
5741	    print "*** overriding previous meaning ***\n";
5742	    &write_warnings("\nprevious meaning of \\$cmd will be lost");
5743	}
5744#	local($code) = "undef \&$tmp"; eval ($code);
5745#	if ($@) {print "\n*** undef \&$cmd failed \n"}
5746	if ((!$PREAMBLE)||($renewed>1)) {
5747	    $new_command{$cmd} = join(':!:',$argn,$body,$opt);
5748#	    local($code) = "sub $tmp\{\&replace_new_command(\"$cmd\");\}";
5749#	    eval $code;
5750#	    print STDERR "\n*** sub do_cmd_$cmd failed:\nPERL: $@\n" if ($@);
5751#	    &replace_new_command($cmd);
5752	}
5753
5754	$renew_command{$cmd} = 1;
5755	&write_mydb("renew_command", $cmd, $renew_command{$cmd});
5756        local($padding) = " ";
5757        $padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
5758        # Generate a new subroutine
5759        local($codeA) = "sub wrap_cmd_$cmd {" . "\n"
5760            .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'
5761            . $bodyA
5762	    . (($thisone)? "\nprint \"\\nwrap $cmd:\".\$args.\"\\n\";" : '')
5763            . "\n".'(&make_deferred_wrapper(1).$cmd.'
5764            . "\"$padding\"".'.$args.&make_deferred_wrapper(0),$_)}'
5765            . "\n";
5766        print "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
5767        eval $codeA;
5768        print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
5769	$raw_arg_cmds{$cmd} = 1;
5770
5771    } elsif (($ALLOW_REDEFINE)&&($PREAMBLE < 2)) {
5772	print "\n*** redefining \\$cmd ***\n";
5773	&write_warnings("\ncommand \\$cmd had no previous definition")
5774	    if (!($new_command{$cmd}));
5775    }
5776    if ($renewed && ($PREAMBLE > 1) &&($new_command{$cmd})) {
5777	$raw_arg_cmds{$cmd} = 1 ;
5778	$renew_command{$cmd} = 1;
5779        local($padding) = " ";
5780        $padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
5781        # Generate a new subroutine
5782        local($codeA) = "sub wrap_cmd_$cmd {" . "\n"
5783            .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'
5784            . $bodyA
5785	    . (($thisone)? "\nprint \"\\nwrap $cmd:\".\$args.\"\\n\";" : '')
5786            . "\n".'(&make_deferred_wrapper(1).$cmd.'
5787	    . "\"$padding\"".'.$args.&make_deferred_wrapper(0),$_)}'
5788            . "\n";
5789        print "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
5790        eval $codeA;
5791        print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
5792
5793	&write_mydb("renew_command", $cmd, $renew_command{$cmd});
5794    } elsif ($renewed) {
5795        $new_command{$cmd} = join(':!:',$argn,$body,$opt);
5796    } else {
5797	$new_command{$cmd} = join(':!:',$argn,$body,$opt)
5798	    unless (($PREAMBLE > 1)&&($renew_command{$cmd}));
5799    }
5800
5801    local($this_cmd);
5802    $this_cmd = join(''
5803	, "command{\\$cmd}"
5804	, ($argn ? "[$argn]" :'')
5805	, (($opt =~ /^}$/) ? '' : "[$opt]" )
5806	, "{", $body , "}" );
5807    $this_cmd = &revert_to_raw_tex($this_cmd);
5808    if ($renewed) {
5809	if ($renewed=~/provide/){
5810	    $provide_command{$cmd} = 1;
5811	    &write_mydb("provide_command", $cmd, $provide_command{$cmd});
5812#	} else {
5813#	    print "\n ** marking $cmd as renewed **";
5814#	    $renew_command{$cmd} = 1;
5815	};
5816	if ((!$PREAMBLE)&&($renewed>1)) {
5817#	    local($this_cmd) = join(''
5818#		, "\n\\renewcommand{\\$cmd}"
5819#		, ($argn ? "[$argn]" :'')
5820#		, (($opt =~ /^}$/) ? '' : "[$opt]" )
5821#		, "{", $body , "}\n" );
5822#	    $latex_body .= &revert_to_raw_tex($this_cmd);
5823	    $latex_body .= "\n\\renew". $this_cmd."\n";
5824	} else {
5825##	    &add_to_preamble('command',"\\" . $this_cmd);
5826	}
5827    } else {
5828	&add_to_preamble('command',"\\new" . $this_cmd)
5829	    unless ($PREAMBLE);
5830    }
5831    undef $body;
5832    if ($renewed == 2) {
5833	# there is no output to return
5834	$$after_R = $_;
5835	return();
5836    }
5837
5838    if (!$PREAMBLE) {
5839	$$after_R = $_;
5840	return ($new_cmd) if ($renewed);
5841#	    $cmd_br =~ s/\\//;
5842#	( join ('', &make_deferred_wrapper(1)
5843#	    , "\\". ($renewed ? (($renewed =~ /provide/)? 'provid' : 'renew')
5844#		: 'new')."edcommand"
5845#	    , $cmd_br , ($argn ? "[$argn]" :'')
5846#	    , ( ($opt =~ /^\}$/ ) ? '' : "[$opt]" ) , $body_br
5847#	    , &make_deferred_wrapper(0)) , $_ );
5848	$new_cmd = join('', "command{\\$cmd}"
5849			 , ($argn ? "[$argn]" :'')
5850			 , (($opt =~ /^\}$/) ? '' : "[$opt]" )
5851			 , "{", $body , "}" );
5852	$new_cmd = &revert_to_raw_tex($new_cmd);
5853	&add_to_preamble('command', "\\provide".$new_cmd );
5854	$$after_R = $_;
5855	return();
5856    }
5857    $new_cmd =~ s/\\$cmd([\d\W]|$)/$cmd$1/s;
5858    $$after_R = $_;
5859    $new_cmd;
5860}
5861
5862sub replace_new_command {
5863    local($cmd) = @_;
5864    local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
5865    do { ### local($_) = $body;
5866	 &make_unique($body);
5867	 } if ($body =~ /$O/);
5868    $body =~ s/(^|[^\\])\~/$1\\nobreakspace /g;
5869    if ($argn) {
5870	do {
5871	    local($before) = '';
5872	    local($after) = "\\$cmd ".$_;
5873	    $after = &substitute_newcmd;   # may change $after
5874	    $after =~ s/\\\@#\@\@/\\/o ;
5875	};
5876    } elsif ($body =~ /\\/) {
5877	$body = &translate_commands($body);  # ???
5878	$_ = $body . $_;
5879    } else { $_ = $body . $_; }
5880    $_;
5881}
5882
5883sub get_body_let {
5884#    local(*_) = @_;
5885    local($_) = @_;
5886    local($cmd,$body,$replace,$tmp,$pat);
5887    ($cmd,$body) = &get_next_tex_cmd;
5888    s/^\s*=?\s*/$body .= $&;''/e;
5889    ($replace,$pat) = &get_next_tex_cmd;
5890#    return() if ($replace eq $cmd);
5891    $body .= $pat;
5892    $body = &revert_to_raw_tex($body);
5893    &add_to_preamble('', "\\let ".$body );
5894    $_[0] = $_;
5895    if (($replace eq $cmd)||($cmd="\\")||($cmd =~/(style|size)$/)) {
5896	"let ".$body
5897    } else {
5898	$new_command{$cmd} = join(':!:','',"\\$replace ",'}');
5899	'';
5900    }
5901}
5902
5903
5904#  do not remove the \renewcommand code, since it may be needed
5905#  within images. Instead replace it with \renewedcommand;
5906#  This will be reverted in &revert_to_raw_tex
5907sub get_body_renewcommand {
5908    local($ALLOW_REDEFINE) = 1;
5909    local($renew, $n_after) = &process_body_newcommand(1,@_);
5910    ($renew ? 'renewed' . $renew : '');
5911}
5912
5913sub do_cmd_renewedcommand {
5914    local($_) = @_;
5915    local($ALLOW_REDEFINE) = 1;
5916    &process_body_newcommand(2,\$_);
5917    $_ ;
5918}
5919
5920sub get_body_providecommand {
5921    local($provide, $n_after) = &process_body_newcommand('provide',@_);
5922    (($PREAMBLE && $provide) ? 'provided'.$provide : '');
5923}
5924
5925sub do_cmd_providedcommand{ &do_cmd_renewedcommand(@_) }
5926
5927sub get_body_DeclareRobustCommand {
5928    local($provide, $n_after) = &process_body_newcommand('provide',@_);
5929    (($PREAMBLE && $provide) ? 'provided'.$provide : '');
5930}
5931
5932sub get_body_DeclareMathOperator {
5933    local($after_R) = @_;
5934    local($_) = $$after_R;
5935    my $star;
5936    s/^\\DeclareMathOperator\s*(\*|star)/$star = $1;''/s;
5937    my ($mcmd,$patA) = &get_next(1);
5938    my ($mop,$patB) = &get_next(1);
5939    if ($star) {
5940	$patA .= "${O}0$C\\mathop${O}1$C\\mathrm${patB}${O}1$C${O}0$C".$_;
5941    } else {
5942	$patA .= "${O}0$C${O}1$C\\mathrm${patB}${O}1$C${O}0$C".$_;
5943    }
5944    local($provide, $n_after) = &process_body_newcommand('provide',\$patA);
5945    $$after_R = $patA;
5946    (($PREAMBLE && $provide) ? 'provided'.$provide : '');
5947}
5948
5949sub get_body_DeclareMathOperatorstar {
5950    local($after_R) = @_;
5951    local($_) = $$after_R;
5952    my $star;
5953    s/^\\DeclareMathOperator\s*(\*|star)/$star = $1;''/s;
5954    my ($mcmd,$patA) = &get_next(1);
5955    my ($mop,$patB) = &get_next(1);
5956    $patA .= "${O}0$C\\mathop${O}1$C\\mathrm${patB}${O}1$C${O}0$C".$_;
5957    local($provide, $n_after) = &process_body_newcommand('provide',\$patA);
5958    $$after_R = $patA;
5959    (($PREAMBLE && $provide) ? 'provided'.$provide : '');
5960}
5961
5962
5963# Removes the definition from the input string, adds to the preamble
5964# and stores the body in %new_environment;
5965sub get_body_newenvironment {
5966    local($newed,$after) = &process_body_newenvironment(0,@_);
5967    ( $PREAMBLE ? "newed".$newed : '');
5968}
5969
5970sub process_body_newenvironment {
5971#    local($renew,*_) = @_;
5972    local($renew,$after_R) = @_;
5973    local($_) = $$after_R;
5974    local($no_change) = $_;
5975    local($argn,$env,$begin,$end,$tmp,$opt,$pat);
5976    local($new_env) = 'environment';
5977    if ($renew == 2) {
5978        $env = &missing_braces unless (
5979	        (s/$next_pair_pr_rx/$pat=$&;$env=$2;''/e)
5980	        ||(s/$next_pair_rx/$pat=$&;$env=$2;''/e));
5981	$new_env .= $pat;
5982    } else {
5983	($env,$pat) = &get_next(1);	# Get the environment name
5984	$env =~ s/^\s*\\//; $new_env .= $pat;
5985    }
5986    ($argn,$pat) = &get_next(0);	# Get optional no. of args
5987    $argn = 0 unless $argn; $new_env .= $pat if $argn;
5988
5989    # Get the body of the code and store it with the name and number of args
5990    # UNLESS THE COMMAND IS ALREADY DEFINED (see get_body_newcommand)
5991    # ...in which case $ALLOW_REDEFINE must also have been set.  # RRM
5992    $opt = '}';			# Flag for no optional arg
5993    if (/^\[/) {
5994	($opt,$pat) = &get_next(0);
5995	$new_env .= $pat;
5996    }
5997    $tmp = "do_env_$env";
5998
5999    if ($renewed == 2 ) {
6000        $begin = &missing_braces unless (
6001	        (s/$next_pair_pr_rx/$pat=$&;$begin=$2;''/e)
6002	        ||(s/$next_pair_rx/$pat=$&;$begin=$2;''/e));
6003	$new_env .= $pat;
6004	$end = &missing_braces unless (
6005	        (s/$next_pair_pr_rx/$pat=$&;$end=$2;''/e)
6006	        ||(s/$next_pair_rx/$pat=$&;$end=$2;''/e));
6007	$new_env .= $pat;
6008    } else {
6009	($begin,$pat) = &get_next(1); $new_env .= $pat;
6010	($end,$pat) = &get_next(1); $new_env .= $pat;
6011    }
6012    if ((defined &$tmp)&&($ALLOW_REDEFINE)) {
6013	print STDOUT "\n*** redefining environment {$env} ***\n";
6014	&write_warnings("\nredefined environment {$env} ");
6015    }
6016    $new_environment{$env} = join(':!:', $argn, $begin, $end, $opt)
6017	unless ((defined &$tmp)&&(! $ALLOW_REDEFINE));
6018
6019    if (!$PREAMBLE) {
6020	$new_env = join ('',
6021	    , "environment{$env}"
6022	    , ($argn ? "[$argn]" : '')
6023	    , (($opt ne '}')? "[$opt]" : '')
6024	    , "{$begin}{$end}"
6025	    );
6026	&revert_to_raw_tex($new_env);
6027	if ($renew == 2) {
6028	    $latex_body .= "\n\\".($renew ? 're':'').'new'.$new_env."\n";
6029	} else {
6030	    &add_to_preamble ('environment'
6031		, "\\".($renew ? 're':'').'new'.$new_env );
6032	}
6033	$$after_R = $_;
6034	return();
6035    }
6036    if ($new_env =~ /$sections_rx/) {
6037    	$new_env = join('', $`,'\csname ',$2,'\endcsname',$3,$');
6038    }
6039    $new_env =~ s/$par_rx/\\par /g;
6040    $$after_R = $_;
6041    $new_env;
6042}
6043
6044sub get_body_renewenvironment {
6045    local($ALLOW_REDEFINE) = 1;
6046    local($renewed, $after) = &process_body_newenvironment(1,@_);
6047    'renewed'.$renewed;
6048}
6049
6050sub do_cmd_renewedenvironment {
6051    local($ALLOW_REDEFINE) = 1;
6052    local($_) = @_;
6053    &process_body_newenvironment(2,\$_);
6054    $_;
6055}
6056
6057# Instead of substituting as with newcommand and newenvironment,
6058# or generating code to handle each new theorem environment,
6059# it now does nothing. This forces theorem environments to be passed
6060# to latex. Although it would be possible to handle theorem
6061# formatting in HTML as it was done previously it is impossible
6062# to keep the theorem counters in step with other counters (e.g. equations)
6063# to which only latex has access to. Sad...
6064sub get_body_newtheorem {
6065#    local(*_) = @_;
6066    local($after_R) = @_;
6067    local($_) = $$after_R;
6068    my ($orig, $body) = ($_, '');
6069    my ($title, $env, $ctr, $within, $cmd, $tmp, $begin, $end, $pat);
6070    my ($new_thm) = 'theorem';
6071    # Just chop off the arguments and append to $next_def
6072    ($env,$pat) = &get_next(1); $new_thm .= $pat;
6073    ($ctr,$pat) = &get_next(0); $new_thm .= $pat;
6074    ($title,$pat) = &get_next(1); $new_thm .= $pat;
6075    ($within,$pat) = &get_next(0); $new_thm .= $pat;
6076
6077    #check the style parameters
6078    my ($hfont,$bfont,$thm_style);
6079    my ($before_thm) = join('',@processed);
6080    my ($which,$cmds);
6081    while ($before_thm =~ /$theorem_cmd_rx/) {
6082	$which = $1;
6083	$before_thm = $';
6084	$before_thm =~ s/$next_pair_rx/$cmds = $2;''/e;
6085	$cmds =~ s/\\/\|/g;  # escape any backslash
6086	if ($which =~ /style/) { $thm_style = $cmds }
6087	elsif ($which =~ /header/) { $hfont = $cmds }
6088	elsif ($which =~ /body/)   { $bfont = $cmds }
6089    }
6090    $hfont = '['.$hfont.']';
6091    $bfont = '['.$bfont.']';
6092    $thm_style = '['.$thm_style.']';
6093    undef $before_thm;
6094
6095    if (!($ctr)) {
6096	# define the new counter
6097	$ctr = $env;
6098	do {
6099###	    local($_) = "\\arabic<<1>>$ctr<<1>>";
6100###	    $_ = join('',"\\the$within", "." , $_) if ($within);
6101	    $body = "\\arabic<<1>>$ctr<<1>>";
6102	    $body = join('',"\\the$within", "." , $body) if ($within);
6103	    &make_unique($body);
6104	    $cmd = "the$ctr";
6105	    $tmp = "do_cmd_$cmd";
6106	    do {
6107                $new_command{$cmd} = join(':!:',0,$body,'}')
6108	    } unless (defined &$tmp);
6109	    &write_mydb("new_command", $cmd, $new_command{$cmd});
6110	    eval "sub do_cmd_$cmd {\n"
6111		. 'local($_,$ot) = @_;'."\n"
6112		. 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'."\n"
6113		. '&translate_commands(' . "\"$body\"" . ");\n}\n";
6114	    print STDERR "\n*** sub $tmp failed:\n$@\n" if ($@);
6115	    $raw_arg_cmds{$cmd} = 1;
6116	    undef $body;
6117	};
6118	&do_body_newcounter($ctr);
6119    } else {
6120	do {
6121###	    local($_) = "\\arabic<<1>>$ctr<<1>>";
6122	    $body = "\\arabic<<1>>$ctr<<1>>";
6123	    &make_unique($body);
6124	    $cmd = "the$env";
6125	    $tmp = "do_cmd_$cmd";
6126	    do {
6127                $new_command{$cmd} = join(':!:',0,$body,'}')
6128	    } unless (defined &$tmp);
6129	    &write_mydb("new_command", $cmd, $new_command{$cmd});
6130	    eval "sub do_cmd_$cmd {\n"
6131		. 'local($_,$ot) = @_;'
6132		. 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'
6133		. '&translate_commands(' . "\"$body\"" . ");\n}\n";
6134	    print STDERR "\n*** sub $tmp failed:\n$@\n" if ($@);
6135	    $raw_arg_cmds{$cmd} = 1;
6136	    undef $body;
6137	};
6138    }
6139
6140    # record the counter dependency
6141    &addto_dependents($within,$ctr) if ($within);
6142
6143    # save the text-label in the %new_theorem hash
6144    $new_theorem{$env} = $title;
6145
6146    # define a new environment
6147    my ($id) = ++$global{'max_id'};
6148    $begin = "\\begin<<$id>>theorem_type<<$id>>"
6149	. "[$env][$ctr][$within]$thm_style$hfont$bfont\n";
6150    $id = ++$global{'max_id'};
6151    $end = "\\end<<$id>>theorem_type<<$id>>\n";
6152    $tmp = "do_env_$env";
6153    if ((defined &$tmp)&&($ALLOW_REDEFINE)) {
6154	print STDOUT "\n*** redefining theorem environment {$env} ***\n";
6155    }
6156    $new_environment{$env} = join(':!:', '', $begin, $end, '')
6157	unless ((defined &$tmp)&&(! $ALLOW_REDEFINE));
6158
6159    if (!$PREAMBLE) {
6160	my ($new_cmd) = join(''
6161	    , 'theorem{}' );
6162	&add_to_preamble('theorem', "\\new".$new_cmd );
6163	$$after_R = $_;
6164	return();
6165    }
6166    $$after_R = $_;
6167    'newed'.$new_thm;
6168}
6169
6170sub do_cmd_theoremstyle {
6171    local($_) = @_;
6172    local($thm_type);
6173    $thm_type = &missing_braces unless (
6174	(s/$next_pair_pr_rx/$thm_type=$2;''/e)
6175	||(s/$next_pair_rx/$thm_type=$2;''/e));
6176#   $THM_STYLE = $thm_type;
6177    $_;
6178}
6179sub do_cmd_theoremheaderfont {
6180    local($_) = @_;
6181    local($thm_type);
6182    $thm_type = &missing_braces unless (
6183	(s/$next_pair_pr_rx/$thm_type=$2;''/e)
6184	||(s/$next_pair_rx/$thm_type=$2;''/e));
6185#   $THM_HFONT = $thm_type;
6186    $_;
6187}
6188sub do_cmd_theorembodyfont {
6189    local($_) = @_;
6190    local($thm_type);
6191    $thm_type = &missing_braces unless (
6192	(s/$next_pair_pr_rx/$thm_type=$2;''/e)
6193	||(s/$next_pair_rx/$thm_type=$2;''/e));
6194#   $THM_BFONT = $thm_type;
6195    $_;
6196}
6197
6198sub do_env_theorem_type {
6199    local($_) = @_;
6200    local($dum,$env,$ctr,$within, $label, $name, $title, $text, $index);
6201    ($env, $dum) = &get_next_optional_argument;
6202    ($ctr, $dum) = &get_next_optional_argument;
6203    ($within, $dum) = &get_next_optional_argument;
6204
6205    local($thm_num, $thm_style);
6206    # defaults for plain theorem-style
6207    my ($hfont,$bfont) = ('','');
6208
6209    ($thm_style, $dum) = &get_next_optional_argument;
6210    ($hfont, $dum) = &get_next_optional_argument;
6211    $hfont =~ s/\|/\\/og;
6212    ($bfont, $dum) = &get_next_optional_argument;
6213    $bfont =~ s/\|/\\/og;
6214
6215    # the pre-defined alternative theorem-styles
6216    if ($thm_style =~ /definition/) {
6217	$bfont = '\normalfont' unless $bfont;
6218    } elsif ($thm_style =~ /remark/) {
6219	$hfont = '\itshape' unless $hfont;
6220	$bfont = '\normalfont' unless $bfont;
6221    }
6222
6223    # defaults for plain theorem-style
6224    $hfont = '\bfseries' unless $hfont;
6225    $bfont = '\itshape' unless $bfont;
6226
6227    ($name, $dum) = &get_next_optional_argument;
6228    $name = &translate_environments("${O}0$C".$name."${O}0$C") if $name;
6229    $name = &translate_commands($name) if ($name =~ /\\/);
6230
6231    $index = $section_commands{$ctr};
6232    if ($index) {
6233	# environment actually starts a new (sub-)section
6234	$curr_sec_id[$index]++;
6235	local($this) = &translate_commands("\\the$ctr");
6236	local($hash) = &sanitize($name." $this");
6237	local($section_tag) = join('', @curr_sec_id);
6238	$encoded_section_number{$hash} = join($;, $section_tag);
6239	&reset_dependents($ctr) if ($dependent{$ctr});
6240	$thm_num = &translate_commands("\\the$ctr");
6241	$thm_num =~ s/(\w)\.(\.\w)/$1$2/g;
6242
6243	# construct the sectioning title from the counter values
6244	$title = join( '', $new_theorem{$env}, " "
6245	    , &translate_commands("\\the$ctr") );
6246	$toc_section_info{join(' ',@curr_sec_id)} = \
6247	    "$current_depth$delim$CURRENT_FILE$delim$title"
6248		if ($current_depth <= $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
6249	$section_info{join(' ',@curr_sec_id)} = \
6250	    "$current_depth$delim$CURRENT_FILE$delim$title$delim";
6251	$title = join('',"<A NAME=\"SECTION$section_tag\"><B>"
6252		      , $title , "</B></A>" );
6253    } else {
6254	if ($ctr) {
6255	    print STDOUT "\nSTP:$ctr:+1" if ($VERBOSITY > 3);
6256	    $global{$ctr}++;
6257	    print STDOUT "=".$global{$ctr}." " if ($VERBOSITY > 3);
6258	    &reset_dependents($ctr) if ($dependent{$ctr});
6259	    $thm_num = "\\the$ctr ";
6260	} else { $thm_num = ''; }
6261
6262	# construct the full title from the counter values
6263	$title = $new_theorem{$env};
6264	if (($thm_style =~ /margin/)&&($HTML_VERSION > 2.1)) {
6265	    # don't use the number yet
6266	} elsif ($thm_style =~ /change/) {
6267	    $title = join(' ', $thm_num, "\\space", $title)
6268	} else {
6269	    $title = join(' ', $title, "\\space", $thm_num);
6270	}
6271
6272	if ($hfont) {
6273	    $title = join('',$O,++$global{'max_id'},$C,$hfont," "
6274		      , $title, $O,++$global{'max_id'},$C);
6275	    $title = &translate_environments($title);
6276	    $title = &translate_commands($title);
6277	} else {
6278	    $title = join('',"<B>",&translate_commands($title),"</B>");
6279	}
6280	$title =~ s/(\w)\.(\.\w)/$1$2/g;
6281    }
6282    # extract any name or label that may occur at the start
6283    s/^\s*(\\label(($O|$OP)\d+($C|$CP))([^<]*)\2)?\s*(\(([^\)]*)\))?/
6284	$label=$1; $text=$5; $name=$7 if ($7); ''/eo;
6285    if ($label) {
6286	$label = &anchor_label($text,$CURRENT_FILE,'');
6287	$label =~ s/$anchor_mark/$title/;
6288	$title = $label;
6289    }
6290    if ($name) {
6291	$name =~ s/^\s*|\s*$//g;
6292	$name = join('', " (", $name, ") ") if $name;
6293    }
6294    local($attribs, $border);
6295    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
6296    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
6297
6298    $_ = join('', $O,++$global{'max_id'},$C, $bfont
6299	    , " ", $_ ,$O,++$global{'max_id'},$C) if ($bfont);
6300
6301    my($cmd) = 'do_thm_'.$env;
6302    if (defined &$cmd) {
6303	$_ = &$cmd($ctr, $title, $_);
6304    } else {
6305	$_ = &translate_environments($_);
6306	$_ = &translate_commands($_);
6307    }
6308
6309    if ($thm_style =~ /margin/) {
6310	local($valign);
6311	$valign = ($NETSCAPE_HTML ? ' VALIGN="BASELINE"':'');
6312	if ($hfont) {
6313	    $thm_num = join('',$O,++$global{'max_id'},$C,$hfont," "
6314		      , $thm_num, $O,++$global{'max_id'},$C);
6315	    $thm_num = &translate_environments($thm_num);
6316	    $thm_num = &translate_commands($thm_num);
6317	} else {
6318	    $thm_num = join('',"<B>",&translate_commands($thm_num),"</B>");
6319	}
6320	$thm_num =~ s/(\w)\.(\.\w)/$1$2/g;
6321
6322	# code copied from  &make_table
6323	local($Tattribs);
6324	if ($attribs) {
6325	    if (!($attribs =~ /=/)) {
6326		$Tattribs = &parse_valuesonly($attribs,"TABLE");
6327	    } else {
6328		$Tattribs = &parse_keyvalues($attribs,"TABLE");
6329	    }
6330	    $Tattribs = ' '.$Tattribs if ($Tattribs);
6331	}
6332	$_ = join ('', "\n<P><DIV$env_id><TABLE"
6333		, (($border) ? " BORDER=\"$border\"" : '')
6334		, $Tattribs , ">\n<TR VALIGN=\"TOP\">"
6335		, "<TD$valign>", &translate_commands($thm_num)
6336		, "</TD>\n<TD>", $title, $name
6337		, (($thm_style =~ /break/)? "\n<BR>":" \&nbsp; \n")
6338		, $_ , "\n</TD></TR></TABLE></DIV>");
6339    } else {
6340	$_ = join('', "<P><DIV$env_id>"
6341		, $title, $name
6342		, (($thm_style =~ /break/)? "\n<BR>":" \&nbsp; \n")
6343		, $_
6344		,"</DIV><P></P>\n");
6345	if (($border||($attribs))&&($HTML_VERSION > 2.1 )) {
6346	    &make_table( $border, $attribs, '', '', '', $_ )
6347	} else { $_ }
6348    }
6349}
6350
6351# Modifies $_ in the caller and as a side-effect it modifies $next_def
6352# which is local to substitute_meta_cmds
6353sub get_next {
6354    local($what) = @_;
6355    local($next, $pat, $tmp);
6356    if ($what == 1) {
6357	($next, $tmp, $pat) = &get_next_argument;
6358    }
6359    elsif ($what == 2) {
6360	($next, $pat) = &get_next_tex_cmd;
6361    }
6362    elsif ($what == 3) {
6363	($next, $pat) = &get_next_def_arg;
6364    }
6365    elsif ($what == 4) {
6366	($next, $tmp, $pat) = &get_next_argument;
6367    }
6368    else {
6369	($next, $pat) =  &get_next_optional_argument;
6370    }
6371    do {
6372	$next_def .= &revert_to_raw_tex($pat) if $pat;
6373    } unless ($renewed); # don't add \renewcommand to preamble
6374#    $next =~ s/(^\s*)|(\s*$)//g unless ($what == 4); #don't lose white space on body
6375    $next =~ s/(^\s*)|(\s*$)//g unless ($what =~ /[14]/); #retain white space in body
6376    ($next, $pat);
6377}
6378
6379# The following get_next_<something> ARE ALL DESTRUCTIVE.
6380sub get_next_argument {
6381    local($next, $br_id, $pat);
6382    if (!(s/$next_pair_rx/$br_id=$1;$next=$2;$pat=$&;''/seo)) {
6383	print " *** Could not find argument for command \\$cmd ***\n";
6384	print "$_\n";
6385    };
6386    ($next, $br_id, $pat);
6387}
6388
6389sub get_next_pair_or_char_pr {
6390    local($next, $br_id, $pat, $epat);
6391    if ( /^\{([^\}]*)\}/o && (! $`)) {
6392	($next, $pat) = ($1, $&);
6393    } elsif ( (/^\s*([^\s\\<])/o && (! $`))) {
6394	($next, $pat) = ($1, $&);
6395    } elsif ( /$next_pair_pr_rx/o && (! $`)) {
6396	($next, $br_id, $pat) = ($2, $1, $&);
6397    };
6398    $epat = &escape_rx_chars($pat);
6399    s/$epat// if $pat;
6400    ($next, $br_id, $pat);
6401}
6402
6403sub get_next_optional_argument {
6404    local($next, $pat);
6405    s/$optional_arg_rx/$next=$1;$pat=$&;''/eo
6406	if (/\s*[[]/ && (! $`)); # if the first character is a [
6407    #remove trailing spaces and/or comments
6408    s/^($comment_mark(\d+\n?)?|$EOL)//gos;
6409
6410    # if  nested inside {}s  we need to get more tokens
6411    if ($pat) {
6412	# check for \item, indicating something has gone wrong
6413	if ($pat =~ /\\item\b/ ) {
6414	    print "\n*** optional argument badly formed:\n" . $pat . "\n\n";
6415	    $_ = $pat . $_;
6416	    return('','');
6417	}
6418	# check for being nested inside {}s
6419	local($found) = $pat;
6420	while ($found =~ s/$O(\d+)$C[\s\S]*$O\1$C//g) {
6421	    if ($found =~ /$O(\d+)$C/) {
6422		local($br_id) = $1;
6423		if (s/$O$br_id$C//) {
6424		    $found .= $`.$&;
6425		    $pat .= "]".$`.$&;
6426		    $next .= "]".$`.$&;
6427		    $_ = $';
6428		    s/^([^]]*)\]/$next.=$1;$pat.=$&;''/e;
6429		    $found .= $&;
6430		} else { last } # give up if no closing brace
6431	    }
6432	}
6433    } else {
6434	s/^\s*\[\]/$pat=$&;''/e; # This is not picked by $optional_arg_rx
6435    }
6436    ($next, $pat);
6437}
6438
6439#JCL(jcl-del) - use new form of $single_cmd_rx.
6440sub get_next_tex_cmd {
6441    local($next, $pat);
6442    s/^\s*\=?\s*$single_cmd_rx/$4/;
6443    ($next, $pat) = ($1.$2,"\\".$1.$2);
6444}
6445
6446sub get_next_def_arg {
6447    local($next, $pat);
6448
6449    # Sets is_simple_def for caller.  Start by turning it off, then
6450    # turn it on if we find one of the "simple" patterns.
6451
6452    # This has got to be hit-or-miss to an extent, given the
6453    # thoroughly incestuous relationship between the TeX macroprocessor
6454    # ('mouth') and typesetting back-end ('stomach').  Anything which
6455    # even does catcode hacking is going to lose BAD.
6456
6457    s/^\s*//so;			# Remove whitespace
6458
6459    $is_simple_def = 0;
6460
6461    # no arguments
6462
6463    if (/^$O/ && (! $`)) { $next=0; $pat=''; $is_simple_def=1; }
6464
6465    # 'simple' arguments
6466
6467    if (! $is_simple_def && /$tex_def_arg_rx/o && (! $`)) {
6468	s/$tex_def_arg_rx/$next=$1; $pat=$&; $is_simple_def=1; $2/seo; }
6469
6470    # MESSY arguments
6471
6472    if (! $is_simple_def) {
6473 	print "Arguments to $cmd are too complex ...\n";
6474	print "It will not be processed unless used in another environment\n";
6475	print "which is passed to LaTeX whole for processing.\n";
6476
6477	s/^[^<]*(<[^<]+)*<</$next=''; $pat=$&; $O/seo;
6478    }
6479
6480    $pat =~ s/$O$//so;
6481
6482    ($next, $pat);
6483}
6484
6485#### Key-value parsing added by RRM
6486#
6487#   This cleans-up the key-value pairs for a given tag,
6488#   by removing unnecessary spaces and commas, inserting quotes
6489#   around the value and puts a preceding space.
6490#   The key becomes upper-case, while the value becomes lower-case.
6491#   If specific `tags' are provided, then checking is done to verify
6492#   that the keys and values are valid for these tags, eliminating
6493#   any that are not; unmatched keys or values are handled as well.
6494#   If no tags are provided, then just a list of pairs is returned.
6495#
6496sub parse_keyvalues {
6497    local($_,@tags) = @_;
6498    local($key,$KEY,$attribs,$atts,%attributes)=('','','','');
6499
6500    # beware active " in german
6501    local($is_german);
6502    if (s/\&#34;/'/g) {
6503	$is_german=1;
6504	s/(^|[\s,=])(\&\#\d\d\d;)/$1'$2/g
6505    }
6506    local($saved) = &revert_to_raw_tex(&translate_commands($_));
6507    print "\nATTRIBS: $saved\n" if ($VERBOSITY > 6);
6508
6509    $saved =~ s/$percent_mark/%/g;
6510    $saved =~ s/((^|[\s,=])')\\\W\{(\w)}/$1$3/g
6511	if $is_german;  #unwanted accents, from active "
6512    if (@tags) {
6513	foreach $tag (@tags) {
6514	    $_ = $saved;
6515	    local($name)= $tag."_attribs";
6516	    $taglist = $$name;
6517	    $name .= "_rx_list";
6518	    $taglist .= $$name;
6519	    $taglist =~ s/,,/,/;
6520#	    s/(^|,)\s*([a-zA-Z]+)\s*\=\s*"?([\#\%\w\d]+)"?\s*/$attributes{$2}="$3";''/eg;
6521#	    s/(^|,)\s*([a-zA-Z]+)\s*\=\s*(\"([^"]*)\"|\'([^\']*)\'|([#%\w\d]*))\s*/
6522#	    s/(^|,)\s*([a-zA-Z]+)\s*\=\s*(\"([^"]*)\"|\'([^\']*)\'|([#%&@;:+-\/\w\d]*))\s*/
6523	    s/(^|,)\s*([a-zA-Z]+)\s*\=\s*(\"([^"]*)\"|\'([^\']*)\'|([^<>,=\s]*))\s*/
6524		$attributes{$2}=($4?$4:($5?$5:$6));' '/eg;
6525	    foreach $key (keys %attributes){
6526		$KEY = $key;
6527		$KEY =~ tr/a-z/A-Z/;
6528		if ($taglist =~ /,$KEY,/i) {
6529		    local($keyname) = $tag."__".$KEY;
6530		    local($keyvalues) = '';
6531		    if ($$keyname) {
6532			$keyvalues = $$keyname;
6533			$atts = $attributes{$key};
6534			if ($keyvalues =~ /\,$atts\,/i ) {
6535#			    $atts =~ tr/A-Z/a-z/;
6536			    $attribs .= " $KEY=\"$atts\"";
6537			    print "\n$KEY=$atts " if ($VERBOSITY > 3);
6538			} else { &invalid_tag($tag,$KEY,$atts); }
6539		    } else {	# test for a regular expression
6540		        $keyname = $keyname."_rx";
6541			if ($$keyname) {
6542			    $keyvalues = $$keyname;
6543			    $atts = $attributes{$key};
6544			    if ($atts =~ /$keyvalues/) {
6545#				$atts =~ tr/A-Z/a-z/;
6546				$attribs .= " $KEY=\"$atts\"";
6547				print "\n$KEY=$atts " if ($VERBOSITY > 3);
6548			    } else { &invalid_tag($tag,$KEY,$atts) }
6549			} else {
6550			    $atts = $attributes{$key};
6551#			    $atts =~ tr/A-Z/a-z/;
6552			    $attribs .= " $KEY=\"$atts\"";
6553			    print "\n$KEY=$atts " if ($VERBOSITY > 3);
6554			}
6555		    }
6556		} else {
6557		    print "\n$key not in $taglist for $tag" if ($VERBOSITY > 3);
6558		}
6559	    }
6560	}
6561        s/(^|\s,)\'([^\s,]*)\'(\s|$)/$1$2 /g if $is_german;
6562	$attribs .= &parse_valuesonly($_,@tags);
6563    } else {
6564	# with no tags provided, just list the key-value pairs
6565	$_ = $saved;
6566	s/\s*(\w+)\s*=\s*\"?(\w+)\"?\s*,?/$attributes{$1}=$2;''/eg;
6567	foreach $key (keys %attributes){
6568	    $KEY = $key;
6569	    $KEY =~ tr/a-z/A-Z/;
6570	    $atts = $attributes{$key};
6571	    $atts =~ tr/A-Z/a-z/;
6572	    $attribs .= " $KEY=\"$atts\"";
6573	}
6574    }
6575    $attribs;
6576}
6577
6578sub invalid_tag {
6579    local($tag,$key,$value) = @_;
6580    &write_warnings("$key=$value is an invalid value in the <$tag> tag\n");
6581}
6582
6583# RRM
6584#   This creates key-value pairs from values only,
6585#   by checking whether the data matches any key to the provided tags.
6586#   Only the first match found is retained.
6587#   Attributes with no values are also recognised here.
6588#
6589sub parse_valuesonly {
6590    local($values,@tags) = @_;
6591    local($i,$tag,$key,$KEY,$attribs,$atts)=(0,'','','','','');
6592    local($saved) = &revert_to_raw_tex(&translate_commands($values));
6593    $saved =~ s/$percent_mark/%/g;
6594    foreach $tag (@tags) {
6595	local($name)= $tag."_attribs";
6596	$taglist = $$name;
6597	$values = $saved;
6598        $values =~ s/\s*\"?([^,\s\"]+)\"?\s*,?/$i++;$attributes{$i}=$1;''/eg;
6599        local($j) = 0;
6600	while ($j < $i) {
6601	    $j++;
6602	    $key = $attributes{$j};
6603	    if ($taglist =~ /,$key,/i) {
6604		$KEY = $key;
6605		$KEY =~ tr/a-z/A-Z/;
6606		$attribs .= " $KEY";
6607		print " $KEY" if ($VERBOSITY > 3);
6608	    } else {
6609		$atts = $attributes{$j};
6610		$key = &find_attribute($key,$tag);
6611	        if ($key) {
6612		    $KEY = $key;
6613		    $KEY =~ tr/a-z/A-Z/;
6614		    $atts =~ tr/A-Z/a-z/;
6615	            $attribs .= " $KEY=\"$atts\"";
6616		    print " $KEY = $atts" if ($VERBOSITY > 3);
6617		} else { }
6618	    }
6619	}
6620    }
6621    $attribs;
6622}
6623
6624# RRM
6625#   Extracts key-value pairs using a supplied (comma-separated) list.
6626#   When no list is given, it checks for a pre-defined list for the tag.
6627#
6628sub extract_attributes {
6629    local($tag,$taglist,$_) = @_;
6630    local($key,$attribs,$unused,%attributes);
6631    if (! ($taglist)) {
6632	local($name) = "$tag"."_attribs";
6633	if ($$name) { $taglist = $$name }
6634    }
6635    s/\s*(\w+)\s*=\s*\"?(\w+)\"?\s*,?/$attributes{$1}=$2;''/eg;
6636    foreach $key (keys %attributes){
6637	if ($taglist =~ /\,$key\,/) {
6638	    $attribs .= " $key=\"$attributes{$key}\"";
6639	    &write_warnings("valid attribute $key for $tag\n");
6640	} else {
6641	    &write_warnings("unknown attribute $key for $tag\n");
6642	    $unused .= " $key=\"$attributes{$key}\"";
6643	}
6644    }
6645    ($attribs,$unused);
6646}
6647
6648# RRM
6649#   Finds the attribute of a given tag, for which a given value is valid.
6650#   Requires variables: <tag>_<key> to be a comma-separated list of keys.
6651#   So far it cannot recognise data-types, only names.
6652#
6653sub find_attribute {
6654    local($key,$attrib,$tag) = ('',@_);
6655    local($name) = $tag."_attribs";
6656    local($attrib_list)=$$name;
6657    if ($attrib_list) {
6658	$attrib_list =~ s/^\,//o;
6659	$attrib_list =~ s/\,$//o;
6660	local(@keys) = split(',',$attrib_list);
6661	local($attrib_vals) = '';
6662	foreach $key (@keys) {
6663	    $name = $tag."__".$key;
6664	    $attrib_vals = $$name;
6665	    return ($key) if ($attrib_vals =~ /\,$attrib\,/i );
6666	}
6667    }
6668    $name = $tag."_attribs_rx_list";
6669    $attrib_list=$$name;
6670    if (!($attrib_list)) { return(); }
6671    $attrib_list =~ s/^\,//o;
6672    $attrib_list =~ s/\,$//o;
6673    @keys = split(',',$attrib_list);
6674    foreach $key (@keys) {
6675	next if ($attribs =~ / $key=/);
6676	$name = $tag."__".$key."_rx";
6677	$attrib_vals = $$name;
6678	if ( $attrib =~ /^$attrib_vals$/ ) {
6679	    return ($key);
6680	}
6681    }
6682    0;
6683}
6684
6685# in case \HTML is defined differently in packages
6686sub do_cmd_HTML { &do_cmd_HTMLcode(@_) }
6687
6688sub do_cmd_HTMLcode {
6689    local($_) = @_;
6690    local($tag,$attribs,$dum);
6691    local($attribs, $dum) = &get_next_optional_argument;
6692    $tag = &missing_braces unless (
6693	(s/$next_pair_pr_rx/$tag = $2;''/eo)
6694	||(s/$next_pair_rx/$tag = $2;''/eo));
6695    $tag = &translate_commands($tag) if ($tag =~ /\\/);
6696    if (! $tag) {
6697	print "*** no tag given with \\HTML command, ignoring it";
6698	return($_);
6699    }
6700    local($afterHTML) = $_;
6701    local($value,$TAGattribs,$etag);
6702    if (defined $unclosed_tags_list{$tag}) {
6703    } elsif (defined $closed_tags_list{$tag}) {
6704	$value = &missing_braces unless (
6705	    (s/$next_pair_pr_rx/$value = $2;''/eo)
6706	    ||(s/$next_pair_rx/$value = $2;''/eo));
6707	$etag = "</$tag>";
6708	$afterHTML = $_;
6709    } else {
6710	print "\n*** <$tag> is not a valid tag for HTML $HTML_VERSION";
6711	print "\n rejecting: \\HTML".(($attribs)? "[$attribs]" : '')."{$tag}";
6712	return $_ ;
6713    }
6714    if ($dum) {
6715	$attribs = &translate_commands($attribs) if ($attribs=~/\\/);
6716        if ($attribs) {
6717            if (!($attribs =~ /=/)) {
6718                $TAGattribs = &parse_valuesonly($attribs,$tag);
6719            } else {
6720                $TAGattribs = &parse_keyvalues($attribs,$tag);
6721            }
6722        }
6723    } else { }  # default if no [...]
6724    local($needed) = join(','
6725	    , $closed_tags_list{$tag},$unclosed_tags_list{$tag});
6726    $needed =~ s/,,/,/g; $needed =~ s/^,|,$//g;
6727    if ($TAGattribs) {
6728	if ($needed) {
6729	    $needed =~ s/,,/,/g;
6730	    local($this, @needed);
6731	    (@needed) = split(',',$needed);
6732	    foreach $this (@needed) {
6733		next unless ($this);
6734		next if ($TAGattribs =~ /\b$this\b/);
6735		print "\n*** attribute $this required for <$tag> ***";
6736		print "\n rejecting: \\HTML".(($attribs)? "[$attribs]" : '')."{$tag}";
6737		return($value.$afterHTML);
6738	    }
6739	}
6740	$value = &translate_environments($value);
6741	$value = &translate_commands($value) if ($value =~ /\\/);
6742	$_ = join('', "<$tag", $TAGattribs, ">", $value, $etag);
6743   } elsif ($needed) {
6744	print STDOUT "\n*** attributes $needed are required for <$tag> ***";
6745	return($value.$after);
6746    } elsif ($value) {
6747	$value = &translate_environments($value);
6748	$value = &translate_commands($value) if ($value =~ /\\/);
6749	$_ = join('', "<$tag>", $value, $etag);
6750    } else {
6751	$_ = join('', "<$tag>", $etag);
6752    }
6753    $_.$afterHTML;
6754}
6755
6756sub do_cmd_HTMLget {
6757    local($_) = @_;
6758    local($which,$value,$hash,$dummy);
6759    local($hash, $dummy) = &get_next_optional_argument;
6760    $which = &missing_braces unless (
6761	(s/$next_pair_pr_rx/$which = $2;''/eo)
6762	||(s/$next_pair_rx/$which = $2;''/eo));
6763    if ($hash) {
6764	local($tmp) = "\%$hash";
6765	if (eval "defined \%{$hash}") { $! = '';
6766	    $value = ${$hash}{'$which'};
6767	} else { print "\nhash: \%$hash not defined" }
6768    } elsif ($which) {
6769	$value = ${$which};
6770    }
6771    $value.$_;
6772}
6773
6774sub do_cmd_HTMLset {
6775    local($_) = @_;
6776    local($which,$value,$hash,$dummy);
6777    local($hash, $dummy) = &get_next_optional_argument;
6778    $which = &missing_braces unless (
6779	(s/$next_pair_pr_rx/$which = $2;''/eo)
6780	||(s/$next_pair_rx/$which = $2;''/eo));
6781    $value = &missing_braces unless (
6782	(s/$next_pair_pr_rx/$value = $2;''/eo)
6783	||(s/$next_pair_rx/$value = $2;''/eo));
6784    if ($hash) {
6785	local($tmp) = "\%$hash";
6786	if (eval "defined \%{$hash}") { $! = '';
6787#	    eval "\$$hash{'$which'} = \"$value\";";
6788	    ${$hash}{'$which'} = $value;
6789	    print "\nHTMLset failed: $! " if ($!);
6790	} else { print "\nhash: \%$hash not defined" }
6791    } elsif ($which) { $! = '';
6792	eval "\${$which} = \"$value\";";
6793	print "\nHTMLset failed: $! " if ($!);
6794    }
6795    $_;
6796}
6797
6798sub do_cmd_HTMLsetenv { &do_cmd_HTMLset(@_) }
6799
6800####
6801
6802
6803# Appends $next_def to the preamble if it is not already there.
6804sub add_to_preamble {
6805    local($type, $next_def) = @_;
6806    local($name);
6807    if ($type =~ /def|include|special|graphicspath/) {
6808        local($pat) = &escape_rx_chars ($next_def);
6809#	$preamble .= $next_def . "\n" unless ($preamble =~ /$pat/);
6810	push(@preamble, $pat);
6811    }
6812    elsif ($type =~ /command|environment|theorem|counter/) {
6813	push(@preamble, $next_def );
6814    }
6815    else {
6816	($name) = $next_def =~ /$marker\s*({[^}]+})/; # matches type{name}
6817	$name = &escape_rx_chars($name);
6818#	$preamble .= $next_def . "\n" unless ($preamble =~ /$marker\s*$name/);
6819	push(@preamble, $name );
6820    }
6821}
6822
6823sub make_latex{
6824# This is the environment in which to process constructs that cannot be
6825# translated to HTML.
6826# The environment tex2html_wrap will be wrapped around any shorthand
6827# environments (e.g. $, \(, \[).
6828# The tex2html_wrap environment will be treated as an unrecognised
6829# evironment by the translator and its contents (i.e. the 'shorthand'
6830# environment) will be passed to latex for processing as usual.
6831    local($contents) = @_;
6832    local($preamble) = $preamble;
6833    local($aux_preamble) = $aux_preamble;
6834    while ($preamble =~ s/^(\@.*\n)/$prelatex .= $1;''/e) {}
6835    print "\nPRE-LATEX: $prelatex" if (($prelatex)&&($VERBOSITY > 1));
6836
6837    %newed_commands =
6838	 ( 'newedcommand' , 'newcommand'
6839	 , 'renewedcommand' , 'renewcommand'
6840	 , 'providedcommand' , 'providecommand'
6841	 , 'newedenvironment' , 'newenvironment'
6842	 , 'newedboolean' , 'newboolean'
6843	 , 'newedcounter' , 'newcounter'
6844	 , 'newedtheorem' , 'newtheorem'
6845	 , 'newedfont' , 'newfont' , 'newedif', 'newif'
6846	 );
6847
6848
6849    # Make the @ character a normal letter ...
6850    $preamble =~ s/\\par([^A-Za-z]|$)/\n$1/g;
6851    $preamble =~ s/(\\document(class|style)(\[[^\]]+\])?\{\w+\})/$1\n/;
6852    $preamble =~ s/(\\document(class|style)(\[[^\]]+\])?\{\w+\})/$1\n\\RequirePackage{ifthen}\n/
6853			 unless ($preamble =~/\{ifthen\}/);
6854#    $preamble =~ s/(\\document(class|style)(\[[^\]]+\])?\{\w+\})/$1\n\\makeatletter/;
6855    # ... and make it special again after the preamble
6856    # remove the  \begin/\end  for  tex2html_nowrap and tex2html_deferred environments
6857    $preamble =~s/\\(begin|end)\s*\{(tex2html_(nowrap|deferred|nomath|preform)[_a-z]*|imagesonly)\}//g;
6858    $preamble =~s/\n?\s?<tex2html_(end)?file>\#[^#]*\#//mg;
6859
6860    $preamble = "\\documentclass\{article\}%\n\\usepackage{html}\n\\usepackage[dvips]{color}\n"
6861	unless ($preamble);
6862    if (($LATEX_DUMP)&&(!($preamble =~ /\\usepackage\{ldump\}/))) {
6863	# MRO: replaced $* with /m
6864	$preamble =~ s/(\\document(class|style)[^\n]*\n)/$1\\usepackage\{ldump\}\n/m;
6865    }
6866    if ($preamble =~ /pstricks/) {
6867	if ($LOAD_LATEX_COLOR) {
6868	    $LOAD_LATEX_COLOR =~ s/\{color\}/\{pstcol\}/ ;
6869	} else {
6870	    $LOAD_LATEX_COLOR = "\n\\usepackage[dvips]{pstcol}\n";
6871	}
6872    } else {
6873	$LOAD_LATEX_COLOR = "\n\\usepackage[dvips]{color}";
6874    }
6875    $LATEX_COLOR = "\\pagecolor[gray]{.85}\\nobreak " unless $LATEX_COLOR;
6876    if ($preamble =~ /(^|\s*[^%])\s*\\documentstyle/) {
6877	# \usepackage is invalid in LaTeX 2.09 and LaTeX-2e compatibility mode
6878	$LATEX_COLOR = ''; $LOAD_LATEX_COLOR = '';
6879	# ... so is \providecommand
6880	$preamble =~ s/\\documentstyle[^{]*{[^}]*}\n?/
6881		$&."\n\\let\\providecommand\\newcommand\n"/eo;
6882    }
6883
6884    $preamble .= $LOAD_LATEX_COLOR."\n" unless ($preamble =~ /[,\{]color[,\}]/);
6885    $preamble .= "\n\n".$LATEX_COLOR."\n" unless ($preamble =~ /\\pagecolor/);
6886    do {
6887	if ($ISOLATIN_CHARS) { $INPUTENC = $INPUTENC || 'latin1' };
6888	$preamble .= "\n\\usepackage[".$INPUTENC."]\{inputenc\}\n";
6889	} unless ($preamble =~ /\\inputenc/);
6890
6891    $aux_preamble = '' unless (($aux_preamble)&&($contents =~ /\\(hyper)?(ref|cite)/));
6892
6893    $preamble =~ s/\\((provide|(re)?new)ed(command|counter|if|theorem|environment|font))\b/
6894			 "%\n\\".$newed_commands{$1}/eg;
6895    $preamble =~ s/(\\(re)?newcommand)\s*(\{(\\?)(\}|[^\}]+)\})/
6896		$1.(($4)? $3 : "{\\".$5.'}' )/eg;
6897
6898    $preamble =~s/$verbatim_mark(imagesonly)(\d+)#/$verbatim{$2}/eg; # for images.tex only
6899
6900#    local($key);
6901#    foreach $key (keys %newed_commands) {
6902#	$preamble .= "\n\\let\\$key\\".$newed_commands{$key}
6903#    }
6904    $preamble .= "\n";
6905
6906    local($paperwidth) = '';
6907    if ($PAPERSIZE) { $paperwidth = &adjust_textwidth($PAPERSIZE); }
6908    else { $paperwidth = &adjust_textwidth("a5"); }
6909    local($kern) = ($EXTRA_IMAGE_SCALE ? $EXTRA_IMAGE_SCALE/2 : ".5" );
6910    $kern = $kern * $MATH_SCALE_FACTOR;
6911    $prelatex . ($DEBUG ? "\\nonstopmode" : "\\batchmode") .
6912    "\n$preamble\n\n\\makeatletter\n$aux_preamble\n" .
6913    "\\makeatletter\n\\count\@=\\the\\catcode`\\_ \\catcode`\\_=8 \n" .
6914    "\\newenvironment{tex2html_wrap}{}{}%\n" .
6915    "\\catcode`\\<=12\\catcode`\\_=\\count\@\n" .
6916    "\\newcommand{\\providedcommand}[1]{\\expandafter\\providecommand\\csname #1\\endcsname}%\n" .
6917    "\\newcommand{\\renewedcommand}[1]{\\expandafter\\providecommand\\csname #1\\endcsname{}%\n" .
6918    "  \\expandafter\\renewcommand\\csname #1\\endcsname}%\n" .
6919    "\\newcommand{\\newedenvironment}[1]{\\newenvironment{#1}{}{}\\renewenvironment{#1}}%\n" .
6920    "\\let\\newedcommand\\renewedcommand\n" .
6921    "\\let\\renewedenvironment\\newedenvironment\n" .
6922    "\\makeatother\n" .
6923    "\\let\\mathon=\$\n\\let\\mathoff=\$\n" .
6924    "\\ifx\\AtBeginDocument\\undefined \\newcommand{\\AtBeginDocument}[1]{}\\fi\n" .
6925    "\\newbox\\sizebox\n" . "$paperwidth" .
6926    "\\newwrite\\lthtmlwrite\n" . "\\makeatletter\n" .
6927    "\\let\\realnormalsize=\\normalsize\n\\global\\topskip=2sp\n\\def\\preveqno{}" .
6928    "\\let\\real\@float=\\\@float \\let\\realend\@float=\\end\@float\n" .
6929    "\\def\\\@float{\\let\\\@savefreelist\\\@freelist\\real\@float}\n" .
6930#    "\\def\\\@float{\\\@dbflt}\n" .
6931    "\\def\\liih\@math{\\ifmmode\$\\else\\bad\@math\\fi}\n" .
6932    "\\def\\end\@float{\\realend\@float\\global\\let\\\@freelist\\\@savefreelist}\n" .
6933    "\\let\\real\@dbflt=\\\@dbflt \\let\\end\@dblfloat=\\end\@float\n" .
6934    "\\let\\\@largefloatcheck=\\relax\n" .
6935    "\\let\\if\@boxedmulticols=\\iftrue\n" .
6936    "\\def\\\@dbflt{\\let\\\@savefreelist\\\@freelist\\real\@dbflt}\n" .
6937    "\\def\\adjustnormalsize{\\def\\normalsize{\\mathsurround=0pt \\realnormalsize\n" .
6938    " \\parindent=0pt\\abovedisplayskip=0pt\\belowdisplayskip=0pt}%\n" .
6939    " \\def\\phantompar{\\csname par\\endcsname}\\normalsize}%\n" .
6940    "\\def\\lthtmltypeout#1{{\\let\\protect\\string \\immediate\\write\\lthtmlwrite{#1}}}%\n" .
6941    "\\newcommand\\lthtmlhboxmathA{\\adjustnormalsize\\setbox\\sizebox=\\hbox\\bgroup\\kern.05em }%\n" .
6942    "\\newcommand\\lthtmlhboxmathB{\\adjustnormalsize\\setbox\\sizebox=\\hbox to\\hsize\\bgroup\\hfill }%\n" .
6943    "\\newcommand\\lthtmlvboxmathA{\\adjustnormalsize\\setbox\\sizebox=\\vbox\\bgroup %\n".
6944    " \\let\\ifinner=\\iffalse \\let\\)\\liih\@math }%\n" .
6945    "\\newcommand\\lthtmlboxmathZ{\\\@next\\next\\\@currlist{}{\\def\\next{\\voidb\@x}}%\n" .
6946#    " \\expandafter\\box\\next\\edef\\next{\\egroup\\def\\noexpand\\thiseqn{\\theequation}}\\next}%\n" .
6947    " \\expandafter\\box\\next\\egroup}%\n" .
6948    "\\newcommand\\lthtmlmathtype[1]{\\gdef\\lthtmlmathenv{#1}}%\n" .
6949    "\\newcommand\\lthtmllogmath{\\dimen0\\ht\\sizebox \\advance\\dimen0\\dp\\sizebox\n" .
6950    "  \\ifdim\\dimen0>.95\\vsize\n" .  "   \\lthtmltypeout{%\n" .
6951    "*** image for \\lthtmlmathenv\\space is too tall at \\the\\dimen0, reducing to .95 vsize ***}%\n" .
6952    "   \\ht\\sizebox.95\\vsize \\dp\\sizebox\\z\@ \\fi\n" .  "  \\lthtmltypeout{l2hSize %\n" .
6953    ":\\lthtmlmathenv:\\the\\ht\\sizebox::\\the\\dp\\sizebox::\\the\\wd\\sizebox.\\preveqno}}%\n" .
6954    "\\newcommand\\lthtmlfigureA[1]{\\let\\\@savefreelist\\\@freelist
6955       \\lthtmlmathtype{#1}\\lthtmlvboxmathA}%\n" .
6956    "\\newcommand\\lthtmlpictureA{\\bgroup\\catcode`\\_=8 \\lthtmlpictureB}%\n" .
6957    "\\newcommand\\lthtmlpictureB[1]{\\lthtmlmathtype{#1}\\egroup
6958       \\let\\\@savefreelist\\\@freelist \\lthtmlhboxmathB}%\n" .
6959    "\\newcommand\\lthtmlpictureZ[1]{\\hfill\\lthtmlfigureZ}%\n" .
6960    "\\newcommand\\lthtmlfigureZ{\\lthtmlboxmathZ\\lthtmllogmath\\copy\\sizebox
6961       \\global\\let\\\@freelist\\\@savefreelist}%\n" .
6962    "\\newcommand\\lthtmldisplayA{\\bgroup\\catcode`\\_=8 \\lthtmldisplayAi}%\n" .
6963    "\\newcommand\\lthtmldisplayAi[1]{\\lthtmlmathtype{#1}\\egroup\\lthtmlvboxmathA}%\n" .
6964    "\\newcommand\\lthtmldisplayB[1]{\\edef\\preveqno{(\\theequation)}%\n" .
6965    "  \\lthtmldisplayA{#1}\\let\\\@eqnnum\\relax}%\n" .
6966    "\\newcommand\\lthtmldisplayZ{\\lthtmlboxmathZ\\lthtmllogmath\\lthtmlsetmath}%\n" .
6967    "\\newcommand\\lthtmlinlinemathA{\\bgroup\\catcode`\\_=8 \\lthtmlinlinemathB}\n" .
6968    "\\newcommand\\lthtmlinlinemathB[1]{\\lthtmlmathtype{#1}\\egroup\\lthtmlhboxmathA\n" .
6969    "  \\vrule height1.5ex width0pt }%\n" .
6970    "\\newcommand\\lthtmlinlineA{\\bgroup\\catcode`\\_=8 \\lthtmlinlineB}%\n" .
6971    "\\newcommand\\lthtmlinlineB[1]{\\lthtmlmathtype{#1}\\egroup\\lthtmlhboxmathA}%\n" .
6972    "\\newcommand\\lthtmlinlineZ{\\egroup\\expandafter\\ifdim\\dp\\sizebox>0pt %\n" .
6973    "  \\expandafter\\centerinlinemath\\fi\\lthtmllogmath\\lthtmlsetinline}\n" .
6974    "\\newcommand\\lthtmlinlinemathZ{\\egroup\\expandafter\\ifdim\\dp\\sizebox>0pt %\n" .
6975    "  \\expandafter\\centerinlinemath\\fi\\lthtmllogmath\\lthtmlsetmath}\n" .
6976    "\\newcommand\\lthtmlindisplaymathZ{\\egroup %\n" .
6977    "  \\centerinlinemath\\lthtmllogmath\\lthtmlsetmath}\n" .
6978    "\\def\\lthtmlsetinline{\\hbox{\\vrule width.1em \\vtop{\\vbox{%\n" .
6979    "  \\kern.1em\\copy\\sizebox}\\ifdim\\dp\\sizebox>0pt\\kern.1em\\else\\kern.3pt\\fi\n" .
6980    "  \\ifdim\\hsize>\\wd\\sizebox \\hrule depth1pt\\fi}}}\n" .
6981    "\\def\\lthtmlsetmath{\\hbox{\\vrule width.1em\\kern-.05em\\vtop{\\vbox{%\n" .
6982    "  \\kern.1em\\kern$kern pt\\hbox{\\hglue.17em\\copy\\sizebox\\hglue$kern pt}}\\kern.3pt%\n" .
6983    "  \\ifdim\\dp\\sizebox>0pt\\kern.1em\\fi \\kern$kern pt%\n" .
6984    "  \\ifdim\\hsize>\\wd\\sizebox \\hrule depth1pt\\fi}}}\n" .
6985    "\\def\\centerinlinemath{%\n" .
6986    "  \\dimen1=\\ifdim\\ht\\sizebox<\\dp\\sizebox \\dp\\sizebox\\else\\ht\\sizebox\\fi\n" .
6987    "  \\advance\\dimen1by.5pt \\vrule width0pt height\\dimen1 depth\\dimen1 \n".
6988    " \\dp\\sizebox=\\dimen1\\ht\\sizebox=\\dimen1\\relax}\n\n" .
6989    "\\def\\lthtmlcheckvsize{\\ifdim\\ht\\sizebox<\\vsize \n" .
6990    "  \\ifdim\\wd\\sizebox<\\hsize\\expandafter\\hfill\\fi \\expandafter\\vfill\n" .
6991    "  \\else\\expandafter\\vss\\fi}%\n" .
6992    "\\providecommand{\\selectlanguage}[1]{}%\n" .
6993#    "\\def\\\@enddocumenthook{\\ifnum\\count0>1 \\ifvoid\\\@cclv\\penalty-\\\@MM\\fi\\fi}\n" .
6994    "\\makeatletter \\tracingstats = 1 \n"
6995    . ($itrans_loaded ? $itrans_tex_mod : '')
6996    . $LaTeXmacros . "\n"  # macros defined in extension files
6997#    "\\usepackage{lthimages}\n" .
6998    . (($LATEX_DUMP)? "\\latexdump\n" : '')
6999    . "\n\\begin{document}\n" .
7000    "\\pagestyle{empty}\\thispagestyle{empty}\\lthtmltypeout{}%\n" .
7001    "\\lthtmltypeout{latex2htmlLength hsize=\\the\\hsize}\\lthtmltypeout{}%\n" .
7002    "\\lthtmltypeout{latex2htmlLength vsize=\\the\\vsize}\\lthtmltypeout{}%\n" .
7003    "\\lthtmltypeout{latex2htmlLength hoffset=\\the\\hoffset}\\lthtmltypeout{}%\n" .
7004    "\\lthtmltypeout{latex2htmlLength voffset=\\the\\voffset}\\lthtmltypeout{}%\n" .
7005    "\\lthtmltypeout{latex2htmlLength topmargin=\\the\\topmargin}\\lthtmltypeout{}%\n" .
7006    "\\lthtmltypeout{latex2htmlLength topskip=\\the\\topskip}\\lthtmltypeout{}%\n" .
7007    "\\lthtmltypeout{latex2htmlLength headheight=\\the\\headheight}\\lthtmltypeout{}%\n" .
7008    "\\lthtmltypeout{latex2htmlLength headsep=\\the\\headsep}\\lthtmltypeout{}%\n" .
7009    "\\lthtmltypeout{latex2htmlLength parskip=\\the\\parskip}\\lthtmltypeout{}%\n" .
7010    "\\lthtmltypeout{latex2htmlLength oddsidemargin=\\the\\oddsidemargin}\\lthtmltypeout{}%\n" .
7011    "\\makeatletter\n" .
7012    "\\if\@twoside\\lthtmltypeout{latex2htmlLength evensidemargin=\\the\\evensidemargin}%\n" .
7013    "\\else\\lthtmltypeout{latex2htmlLength evensidemargin=\\the\\oddsidemargin}\\fi%\n" .
7014    "\\lthtmltypeout{}%\n" .
7015    "\\makeatother\n\\setcounter{page}{1}\n\\onecolumn\n\n% !!! IMAGES START HERE !!!\n\n"
7016    . "$contents\n"
7017#    "\\clearpage\n" .
7018    . "\\end{document}";
7019}
7020
7021sub adjust_textwidth {
7022    local($_) = @_;
7023    local($width,$length) = ('','');
7024    if (/a4/) {$width = 595; $length= 842; }
7025    elsif (/letter/) {$width = 612; $length= 792; }
7026    elsif (/legal/) {$width = 612; $length= 1008; }
7027    elsif (/note/) {$width = 540; $length= 720; }
7028    elsif (/b5/) {$width = 501; $length= 709; }
7029    elsif (/a5/) {$width = 421; $length= 595; }
7030    elsif (/a6/) {$width = 297; $length= 421; }
7031    elsif (/a7/) {$width = 210; $length= 297; }
7032    elsif (/a8/) {$width = 148; $length= 210; }
7033    elsif (/a9/) {$width = 105; $length= 148; }
7034    elsif (/a10/) {$width = 74; $length= 105; }
7035    elsif (/b4/) {$width = 709; $length= 1002; }
7036    elsif (/a3/) {$width = 842; $length= 1190; }
7037    elsif (/b3/) {$width = 1002; $length= 1418; }
7038    elsif (/a2/) {$width = 1190; $length= 1684; }
7039    elsif (/b2/) {$width = 1418; $length= 2004; }
7040    elsif (/a1/) {$width = 1684; $length= 2380; }
7041    elsif (/b1/) {$width = 2004; $length= 2836; }
7042    elsif (/a0/) {$width = 2380; $length= 3368; }
7043    elsif (/b0/) {$width = 2836; $length= 4013; }
7044    else {
7045	&write_warnings("\nPAPERSIZE: $_ unknown, using LaTeX's size.");
7046	return();
7047     }
7048    if ($width > 500) { $width = $width - 144; $length = $length - 288; }
7049    elsif ($width > 250) { $width = $width - 72; $length = $length - 144; }
7050    elsif ($width > 125) { $width = $width - 36; $length = $length - 72; }
7051#    "\\setlength{\\oddsidemargin}{0pt}\n" .
7052#    "\\setlength{\\evensidemargin}{0pt}\n" .
7053#    "\\setlength{\\parskip}{0pt}\\setlength{\\topskip}{0pt}\n" .
7054    "\\setlength{\\hoffset}{0pt}\\setlength{\\voffset}{0pt}\n" .
7055    "\\addtolength{\\textheight}{\\footskip}\\setlength{\\footskip}{0pt}\n" .
7056    "\\addtolength{\\textheight}{\\topmargin}\\setlength{\\topmargin}{0pt}\n" .
7057    "\\addtolength{\\textheight}{\\headheight}\\setlength{\\headheight}{0pt}\n" .
7058    "\\addtolength{\\textheight}{\\headsep}\\setlength{\\headsep}{0pt}\n" .
7059    "\\setlength{\\textwidth}{${width}pt}\n"
7060    . (($length > 500) ? "\\setlength{\\textheight}{${length}pt}\n" : '')
7061}
7062
7063# Given the depth of the current sectioning declaration and the current
7064# section numbers it returns the new section numbers.
7065# It increments the $depth-ieth element of the @curr_sec_id list and
7066# 0's the elements after the $depth-ieth element.
7067sub new_level {
7068    local($depth, @curr_sec_id) = @_;
7069    $depth = $section_commands{$outermost_level} unless $depth;
7070    local($i) = 0;
7071    grep( do { if ($i == $depth) {$_++ ;}
7072	       elsif ($i > $depth) {$_ = 0 ;};
7073	       $i++;
7074	       0;
7075	   },
7076	 @curr_sec_id);
7077    @curr_sec_id;
7078}
7079
7080sub make_head_and_body {
7081    local($title,$body,$before_body) = @_;
7082    local($DTDcomment) = '';
7083    local($version,$isolanguage) = ($HTML_VERSION, 'EN');
7084    local(%isolanguages) = (  'english',  'EN'   , 'USenglish', 'EN-US'
7085			    , 'original', 'EN'   , 'german'   , 'DE'
7086			    , 'austrian', 'DE-AT', 'french'   , 'FR'
7087			    , 'spanish',  'ES'
7088			    , %isolanguages );
7089#    $isolanguage = $isolanguages{$default_language};  # DTD is in EN
7090    $isolanguage = 'EN' unless $isolanguage;
7091#JCL(jcl-tcl)
7092# clean title as necessary
7093# the first words ... is a kludge, but reasonable (or not?)
7094#RRM: why bother? --- as long as it is pure text.
7095    $title = &purify($title,1);
7096    eval("\$title = ". $default_title ) unless ($title);
7097#    $title = &get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
7098
7099    # allow user-modification of the <TITLE> tag; thanks Dan Young
7100    if (defined &custom_TITLE_hook) {
7101	$title = &custom_TITLE_hook($title, $toc_sec_title);
7102    }
7103
7104    if ($DOCTYPE =~ /\/\/[\w\.]+\s*$/) { # language spec included
7105	$DTDcomment = '<!DOCTYPE HTML PUBLIC "'. $DOCTYPE .'"';
7106    } else {
7107	$DTDcomment = '<!DOCTYPE HTML PUBLIC "'. $DOCTYPE .'//'
7108	    . ($ISO_LANGUAGE ? $ISO_LANGUAGE : $isolanguage) . '"'
7109    }
7110    $DTDcomment .= ($PUBLIC_REF ? "\n  \"".$PUBLIC_REF.'"' : '' ) . '>'."\n";
7111
7112    $STYLESHEET = $FILE.".css" unless defined($STYLESHEET);
7113
7114    my ($this_charset) = $charset;
7115    if ($USE_UTF) { $charset = $utf8_str; $NO_UTF = ''; }
7116    if (!$charset && $CHARSET) {
7117	$this_charset = $CHARSET;
7118	$this_charset =~ s/_/\-/go;
7119    }
7120    if ($NO_UTF && $charset =~/utf/) {
7121	$this_charset = $PREV_CHARSET||$CHARSET;
7122	$this_charset =~ s/_/\-/go;
7123    }
7124
7125    join("\n", (($DOCTYPE)? $DTDcomment : '' )
7126	,"<!--Converted with LaTeX2HTML $TEX2HTMLVERSION"
7127	, "original version by:  Nikos Drakos, CBLU, University of Leeds"
7128	, "* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan"
7129	, "* with significant contributions from:"
7130	, "  Jens Lippmann, Marek Rouchal, Martin Wilck and others"
7131	    . " -->\n<HTML>\n<HEAD>\n<TITLE>".$title."</TITLE>"
7132	, &meta_information($title)
7133	,  ($CHARSET && $HTML_VERSION ge "2.1" ?
7134	      "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=$this_charset\">"
7135	      : "" )
7136	, $LATEX2HTML_META
7137	, ($BASE ? "<BASE HREF=\"$BASE\">" : "" )
7138	, $STYLESHEET_CASCADE
7139	, ($STYLESHEET ? "<LINK REL=\"STYLESHEET\" HREF=\"$STYLESHEET\">" : '' )
7140	, $more_links_mark
7141	, "</HEAD>" , ($before_body? $before_body : '')
7142	, "<BODY $body>", '');
7143}
7144
7145
7146sub style_sheet {
7147    local($env,$id,$style);
7148    #AXR:  don't overwrite existing .css
7149    #MRO: This is supposed to be $FILE.css, no?
7150    #RRM: only by default, others can be specified as well, via $EXTERNAL_STYLESHEET
7151    #return if (-f $EXTERNAL_STYLESHEET);
7152    return if (-r "$FILE.css" && -s _ && !$REFRESH_STYLES );
7153
7154    unless(open(STYLESHEET, ">$FILE.css")) {
7155        print "\nError: Cannot write '$FILE.css': $!\n";
7156        return;
7157    }
7158    if ( -f $EXTERNAL_STYLESHEET ) {
7159        if(open(EXT_STYLES, "<$EXTERNAL_STYLESHEET")) {
7160            while (<EXT_STYLES>) { print STYLESHEET $_; }
7161            close(EXT_STYLES);
7162        } else {
7163            print "\nError: Cannot read '$EXTERNAL_STYLESHEET': $!\n";
7164        }
7165    } else {
7166	print STYLESHEET <<"EOF"
7167/* Century Schoolbook font is very similar to Computer Modern Math: cmmi */
7168.MATH    { font-family: \"Century Schoolbook\", serif; }
7169.MATH I  { font-family: \"Century Schoolbook\", serif; font-style: italic }
7170.BOLDMATH { font-family: \"Century Schoolbook\", serif; font-weight: bold }
7171
7172/* implement both fixed-size and relative sizes */
7173SMALL.XTINY		{ font-size : xx-small }
7174SMALL.TINY		{ font-size : x-small  }
7175SMALL.SCRIPTSIZE	{ font-size : smaller  }
7176SMALL.FOOTNOTESIZE	{ font-size : small    }
7177SMALL.SMALL		{  }
7178BIG.LARGE		{  }
7179BIG.XLARGE		{ font-size : large    }
7180BIG.XXLARGE		{ font-size : x-large  }
7181BIG.HUGE		{ font-size : larger   }
7182BIG.XHUGE		{ font-size : xx-large }
7183
7184/* heading styles */
7185H1		{  }
7186H2		{  }
7187H3		{  }
7188H4		{  }
7189H5		{  }
7190
7191/* mathematics styles */
7192DIV.displaymath		{ }	/* math displays */
7193TD.eqno			{ }	/* equation-number cells */
7194
7195
7196/* document-specific styles come next */
7197EOF
7198    }
7199    print "\n *** Adding document-specific styles *** ";
7200    while (($env,$style) = each %env_style) {
7201        if ($env =~ /\./) {
7202            $env =~ s/\.$//;
7203            print STYLESHEET "$env\t\t{ $style }\n";
7204        } elsif ($env =~ /inline|^(text|math)?((tt|rm|sf)(family)?|(up|it|sl|sc)(shape)?|(bf|md)(series)?|normal(font)?)$/) {
7205            print STYLESHEET "SPAN.$env\t\t{ $style }\n";
7206        } elsif ($env =~ /\./) {
7207            print STYLESHEET "$env\t\t{ $style }\n";
7208        } elsif ($env =~ /^(preform|\w*[Vv]erbatim(star)?)$/) {
7209            print STYLESHEET "PRE.$env\t\t{ $style }\n";
7210        } elsif ($env =~ /figure|table|tabular|equation|$array_env_rx/) {
7211            print STYLESHEET "TABLE.$env\t\t{ $style }\n";
7212        } else {
7213            print STYLESHEET "DIV.$env\t\t{ $style }\n";
7214        }
7215    }
7216    while (($env,$style) = each %txt_style) {
7217        print STYLESHEET "SPAN.$env\t\t{ $style }\n";
7218    }
7219    while (($env,$style) = each %img_style) {
7220        print STYLESHEET "IMG.$env\t\t{ $style }\n";
7221    }
7222
7223    my ($style);
7224    foreach $id (sort(keys  %styleID)) {
7225        $style =  $styleID{$id};
7226        $style =~ s/font-(color)/$1/;
7227        print STYLESHEET "\#$id\t\t{ $style }\n"
7228            if ($styleID{$id} ne '');
7229    }
7230    close(STYLESHEET);
7231}
7232
7233sub clear_styleID {
7234    return unless ($USING_STYLES);
7235    local($env_id,$id) = ("grp", @_);
7236    undef $styleID{$env_id} if ($id =~ /^\d+$/);
7237}
7238
7239sub make_address {
7240    local($addr) = &make_real_address(@_);
7241    $addr .= "\n</BODY>\n</HTML>\n";
7242    &lowercase_tags($addr) if $LOWER_CASE_TAGS;
7243    $addr;
7244}
7245
7246sub make_real_address {
7247    local($addr) = $ADDRESS;
7248    if ((defined &custom_address)&&($addr)) {
7249	&custom_address($addr)
7250    } elsif ($addr) {
7251	"<ADDRESS>\n$addr\n</ADDRESS>";
7252    } else { '' }
7253}
7254
7255sub purify_caption {
7256    local($_) = @_;
7257    local($text) = &recover_image_code($_);
7258    $text =~ s/\\protect|ALT\=|%EQNO:\d+//g;
7259    $text =~ s/[\\\#\'\"\`]//g;
7260    $text;
7261}
7262
7263sub recover_image_code {
7264    local($key) = @_;
7265    local($text) = $img_params{$key};
7266    if (!$text) {
7267	if ($text = $id_map{$key}) {
7268	    if ($orig_name_map{$text}) {
7269		$text = $img_params{$orig_name_map{$text}}
7270	    }
7271	} elsif ($cached_env_img{$key}) {
7272	    $text = $img_params{$cached_env_img{$key}};
7273	}
7274	if ($text =~ /\#*ALT="([^"]+)"(>|#)/s) { $text = $1 }
7275    }
7276    $text =~ s/\\protect|%EQNO:\d+//g;
7277    $text =~ s/&(gt|lt|amp|quot);/&special_html_inv($1)/eg;
7278    $text;
7279}
7280
7281sub encode_title {
7282    local($_) = @_;
7283    $_ = &encode($_);
7284    while (/(<[^<>]*>)/o) {s/$1//g}; # Remove HTML tags
7285    s/#[^#]*#//g;               # Remove #-delimited markers
7286    $_;
7287}
7288
7289# Encodes the contents of enviroments that are passed to latex. The code
7290# is then used as key to a hash table pointing to the URL of the resulting
7291# picture.
7292sub encode {
7293    local($_) = @_;
7294    # Remove invocation-specific stuff
7295    1 while(s/\\(begin|end)\s*(($O|$OP)\d+($C|$CP))?|{?tex2html_(wrap|nowrap|deferred|)(_\w+)?}?(\2)?//go);
7296    $_ = &revert_to_raw_tex($_);
7297    s/\\protect//g;		# remove redundant \protect macros
7298    #$_ = pack("u*", $_);	# uuencode
7299    s/\\\$/dollar/g;		# replace funnies, may cause problems in a hash key
7300    s/\//slash/g;		# replace funnies, may cause problems in a hash key
7301    s/\$|\/|\\//g;		# remove funnies, may cause problems in a hash key
7302    s/\s*|\n//g;		# Remove spaces  and newlines
7303    s/^(.{80}).*(.{80})$/$1$2/;		# truncate to avoid DBM problems
7304    $_;
7305}
7306
7307
7308##################### Hypertext Section Links ########################
7309sub post_process {
7310    # Put hyperlinks between sections, add HTML headers and addresses,
7311    # do cross references and citations.
7312    # Uses the %section_info array created in sub translate.
7313    # Binds the global variables
7314    # $PREVIOUS, $PREVIOUS_TITLE
7315    # $NEXT, $NEXT_TITLE
7316    # $UP, $UP_TITLE
7317    # $CONTENTS, $CONTENTS_TITLE
7318    # $INDEX, $INDEX_TITLE
7319    # $NEXT_GROUP, $NEXT_GROUP_TITLE
7320    # $PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE
7321    # Converting to and from lists and strings is very inefficient.
7322    # Maybe proper lists of lists should be used (or wait for Perl5?)
7323    # JKR:  Now using top_navigation and bot_navigation instead of navigation
7324    local($_, $key, $depth, $file, $title, $header, @link, @old_link,
7325	  $top_navigation, $bot_navigation, @keys,
7326	  @tmp_keys, $flag, $child_links, $body, $more_links);
7327
7328    @tmp_keys = @keys = sort numerically keys %section_info;
7329    print "\nDoing section links ...";
7330    while (@tmp_keys) {
7331	$key = shift @tmp_keys;
7332	next if ($MULTIPLE_FILES &&!($key =~ /^$THIS_FILE/));
7333	print ".";
7334	$more_links = "";
7335	($depth, $file, $title, $body) = split($delim,$section_info{$key});
7336	print STDOUT "\n$key $file $title $body" if ($VERBOSITY > 3);
7337	next if ($body =~ /external/);
7338	$PREVIOUS = $PREVIOUS_TITLE = $NEXT = $NEXT_TITLE = $UP = $UP_TITLE
7339	    = $CONTENTS = $CONTENTS_TITLE = $INDEX = $INDEX_TITLE
7340	    = $NEXT_GROUP = $NEXT_GROUP_TITLE
7341	    = $PREVIOUS_GROUP = $PREVIOUS_GROUP_TITLE
7342	    = $_ = $top_navigation = $bot_navigation = undef;
7343	&add_link_tag('previous',$file);
7344	@link =  split(' ',$key);
7345        ($PREVIOUS, $PREVIOUS_TITLE) =
7346	    &add_link($previous_page_visible_mark,$file,@old_link);
7347	@old_link = @link;
7348	unless ($done{$file}) {
7349	    ++$link[$depth];
7350#	    if ($MULTIPLE_FILES && !$depth && $multiple_toc ) {
7351#	    	local($save_depth) = $link[$depth];
7352#	    	$link[$depth] = 1;
7353#		($NEXT_GROUP, $NEXT_GROUP_TITLE) =
7354#		    &add_link($next_visible_mark, $file, @link);
7355#		&add_link_tag('next', $file, @link);
7356#		$link[$depth] = $save_depth;
7357#	    } else {
7358		($NEXT_GROUP, $NEXT_GROUP_TITLE) =
7359		    &add_link($next_visible_mark, $file, @link);
7360		&add_link_tag('next', $file, @link);
7361#	    }
7362
7363	    $link[$depth]--;$link[$depth]--;
7364	    if ($MULTIPLE_FILES && !$depth ) {
7365	    } else {
7366		($PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE) =
7367		    &add_link($previous_visible_mark, $file,@link);
7368		&add_link_tag('previous', $file,@link);
7369	    }
7370
7371	    $link[$depth] = 0;
7372	    ($UP, $UP_TITLE) =
7373		&add_link($up_visible_mark, $file, @link);
7374	    &add_link_tag('up', $file, @link);
7375
7376	    if ($CONTENTS_IN_NAVIGATION) {
7377		($CONTENTS, $CONTENTS_LINK) =
7378		    &add_special_link($contents_visible_mark, $tocfile, $file);
7379		&add_link_tag('contents', $file, $delim.$tocfile);
7380	    }
7381
7382	    if ($INDEX_IN_NAVIGATION) {
7383		($INDEX, $INDEX_LINK) =
7384		    &add_special_link($index_visible_mark, $idxfile, $file);
7385		&add_link_tag('index', $file, $delim.$idxfile,);
7386	    }
7387
7388	    @link = split(' ',$tmp_keys[0]);
7389	    # the required `next' link may be several sub-sections along
7390	    local($nextdepth,$nextfile,$nextkey,$nexttitle,$nextbody)=
7391	        ($depth,$file,$key,'','');
7392	    $nextkey = shift @tmp_keys;
7393	    ($nextdepth, $nextfile,$nexttitle,$nextbody) = split($delim,$section_info{$nextkey});
7394	    if (($nextdepth<$MAX_SPLIT_DEPTH)&&(!($nextbody=~/external/))) {
7395		($NEXT, $NEXT_TITLE) =
7396		    &add_link($next_page_visible_mark, $file, @link);
7397		&add_link_tag('next', $file, @link);
7398	    } else {
7399		($NEXT, $NEXT_TITLE) = ('','');
7400		$nextfile = $file;
7401	    }
7402	    if ((!$NEXT || $NEXT =~ /next_page_inactive_visible_mark/)&&(@tmp_keys)) {
7403		# the required `next' link may be several sub-sections along
7404		while ((@tmp_keys)&&(($MAX_SPLIT_DEPTH < $nextdepth+1)||($nextfile eq $file))) {
7405		    $nextkey = shift @tmp_keys;
7406		    ($nextdepth, $nextfile,$nexttitle,$nextbody) = split($delim,$section_info{$nextkey});
7407		    if ($nextbody =~ /external/) {
7408			$nextfile = $file;
7409			next;
7410		    };
7411		    print ",";
7412		    print STDOUT "\n $nextkey" if ($VERBOSITY > 3);
7413		}
7414		@link = split(' ',$nextkey);
7415		if (($nextkey)&&($nextdepth<$MAX_SPLIT_DEPTH)) {
7416		    ($NEXT, $NEXT_TITLE) =
7417			&add_link($next_page_visible_mark, $file, @link);
7418		    &add_link_tag('next', $file, @link);
7419		} else {
7420		    ($NEXT, $NEXT_TITLE) = ($NEXT_GROUP, $NEXT_GROUP_TITLE);
7421		    $NEXT =~ s/next_page_(inactive_)?visible_mark/next_page_$1visible_mark/;
7422		    ($PREVIOUS, $PREVIOUS_TITLE) = ($PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE);
7423		    $PREVIOUS =~ s/previous_(inactive_)?visible_mark/previous_page_$1visible_mark/;
7424		}
7425	    }
7426	    unshift (@tmp_keys,$nextkey) if ($nextkey);
7427#
7428	    $top_navigation = (defined(&top_navigation_panel) ?
7429			       &top_navigation_panel : &navigation_panel)
7430		unless $NO_NAVIGATION;
7431	    $bot_navigation = (defined(&bot_navigation_panel) ?
7432			       &bot_navigation_panel : &navigation_panel)
7433		unless $NO_NAVIGATION;
7434	    local($end_navigation) = "\n<!--End of Navigation Panel-->\n";
7435	    if ($USING_STYLES) {
7436		$top_navigation = "\n".'<DIV CLASS="navigation">' . $top_navigation
7437			if $top_navigation;
7438		$bot_navigation = "\n".'<DIV CLASS="navigation">' . $bot_navigation
7439			if $bot_navigation;
7440		$end_navigation = '</DIV>' . $end_navigation;
7441		$env_style{'navigation'} = " ";
7442	    }
7443
7444	    $header = &make_head_and_body($title, $body);
7445	    $header = join('', $header, $top_navigation, $end_navigation) if ($top_navigation);
7446
7447	    local($this_file) = $file;
7448	    if ($MULTIPLE_FILES && $ROOTED) {
7449		if ($this_file =~ /\Q$dd\E([^$dd$dd]+)$/) { $this_file = $1 }
7450	    }
7451	    &slurp_input($this_file);
7452	    open(OUTFILE, ">$this_file")
7453                || die "\nError: Cannot write file '$this_file': $!\n";
7454
7455	    if (($INDEX) && ($SHORT_INDEX) && ($SEGMENT eq 1)) {
7456		&make_index_segment($title,$file); }
7457
7458	    local($child_star,$child_links);
7459	    local($CURRENT_FILE) = $this_file; # ensure $CURRENT_FILE is set correctly
7460	    if (/$childlinks_on_mark\#(\d)\#/) { $child_star = $1 }
7461	    $child_links = &add_child_links('',$file, $depth, $child_star,$key, @keys)
7462		unless (/$childlinks_null_mark\#(\d)\#/);
7463	    if (($child_links)&&(!/$childlinks_mark/)&&($MAX_SPLIT_DEPTH > 1)) {
7464		if ($depth < $MAX_SPLIT_DEPTH -1) {
7465		    $_ = join('', $header, $_, &child_line(), $childlinks_mark, "\#0\#" );
7466		} else {
7467		    $_ = join('', $header, "\n$childlinks_mark\#0\#", &upper_child_line(), $_ );
7468		}
7469	    } else {
7470		$_ = join('', $header, $_ );
7471	    }
7472	    $flag = (($BOTTOM_NAVIGATION || &auto_navigation) && $bot_navigation);
7473	    $_ .= $bot_navigation . $end_navigation if ($flag &&($bot_navigation));
7474	    $_ .= &child_line() unless $flag;
7475	    print STDOUT "\n *** replace markers *** " if ($VERBOSITY > 1);
7476	    &replace_markers;
7477	    print STDOUT "\n *** post-post-process *** " if ($VERBOSITY > 1);
7478	    &post_post_process if (defined &post_post_process);
7479	    &adjust_encoding;
7480	    print OUTFILE $_;
7481	    print OUTFILE &make_address;
7482	    close OUTFILE;
7483	    $done{$file}++;
7484	}
7485    }
7486    &post_process_footnotes if ($footfile);
7487}
7488
7489sub adjust_encoding {
7490    &convert_to_utf8($_) if ($USE_UTF);
7491    &lowercase_tags($_) if $LOWER_CASE_TAGS;
7492}
7493
7494sub post_replace_markers {
7495    # MRO: replaced $* with /m
7496    # clean up starts and ends of  P, BR and DIV tags
7497    s/(<\/?(P|BR|DIV)>)\s*(\w)/$1\n$3/gom unless ($file eq $citefile);
7498    s/([^\s])(<(BR|DIV))/$1\n$2/gom unless ($file eq $citefile);
7499    local($keep,$after);
7500
7501    # anchor images when otherwise there is an invisible-anchor
7502#    s/(<A[^>]*>)\&\#160;<\/A>\s?(<(P|DIV)[^>]*>)\s*(<IMG[^>]*>)\s*(<\/(P|DIV)>)/
7503    s/(<A[^>]*>)($anchor_mark|$anchor_invisible_mark)<\/A>\s?(<(P|DIV)[^>]*>)\s*(<IMG[^>]*>)\s*(<\/(P|DIV)>)/
7504	do{ $keep="$3$1$5<\/A>";
7505	    $after = $6;
7506	    join('',$keep, &after_punct_break($after), $after);
7507	} /egom;
7508
7509    # absorb named anchor (e.g. from index-entry) into preceding or following anchor
7510#    s/(<A NAME=\"[^\"]+\")>\&#160;<\/A>\s*\b?<A( HREF=\"[^\"]+\">)/$1$2/gom;
7511#    s/(<A HREF=\"[^\"]+\")(>\s*\b?([^<]+|<([^>\/]+|\/[^>A]+)>\s*)*<\/A>)\s*\b?<A( NAME=\"[^\"]+\")>\&#160;<\/A>/$1$5$2/gom;
7512
7513    # clean up empty table cells
7514    s/(<TD[^>]*>)\s*(<\/TD>)/<TD>$2/gom;
7515
7516    # clean up list items (only desirable in the bibliography ?)
7517    # s/\n<P>(<DT[^>]*>)/\n<P><\/P>\n$1/gom;
7518
7519    # remove blank lines and comment-markers
7520#    s/\n\n/\n/g;  # no, cause this kills intended ones in verbatims
7521    s/$comment_mark(\d+\n?)?//gm;
7522    s/\&quot;/"/gm;  # replace  &quot;  entities
7523
7524    # italic \LaTeX looks bad
7525    s:<(I|EM)>(($Laname|$AmSname)?$TeXname)</\1>:$2:gm;
7526}
7527
7528sub lowercase_tags {
7529    # MRO: modified to use $_[0]
7530    # local(*stream) = @_;
7531    my ($tags,$attribs);
7532    $_[0] =~ s!<(/?\w+)( [^>]*)?>!
7533	$tags = $1; $attribs = $2;
7534	$attribs =~ s/ ([\w\d-]+)(=| |$)/' '.lc($1).$2/eg;
7535	join('', '<', lc($tags) , $attribs , '>')!eg;
7536}
7537
7538sub after_punct_break {
7539    # MRO: modified to use $_[0]
7540    # local(*stream) = @_;
7541#    $stream =~ s/^([ \t]*)([,;\.\)\!\"\'\?])[ \t]*(\n)?/(($2)? "$2" : "$1")."\n"/em;
7542#    $stream;
7543    $_[0] =~ s/^([ \t]*)([,;\.\)\!\"\'\?\>]|\&gt;)[ \t]*(\n)?//em;
7544    ($2 ? $2 : $1)."\n";
7545}
7546
7547sub make_index_segment {
7548    local($title,$file)= @_ ;
7549#JCL(jcl-tcl)
7550#    s/<[^>]*>//g;
7551#
7552    $index_segment{$PREFIX} = "$title";
7553    if (!($ref_files{"segment"."$PREFIX"} eq "$file")) {
7554	$ref_files{"segment"."$PREFIX"} = "$file";
7555	$changed = 1
7556    }
7557    $SEGMENT = 2;
7558}
7559
7560
7561sub add_link {
7562    # Returns a pair (iconic link, textual link)
7563    local($icon, $current_file, @link) = @_;
7564    local($dummy, $file, $title, $lbody) = split($delim,$section_info{join(' ',@link)});
7565    if ($lbody =~ /external/) { return ('','') };
7566
7567#    local($dummy, $file, $title) = split($delim,$toc_section_info{join(' ',@link)});
7568
7569    if ($MULTIPLE_FILES && $ROOTED && $file) {
7570        if (!($DESTDIR =~ /\Q$FIXEDDIR\E[$dd$dd]?$/)) { $file = "..$dd$file" }
7571    }
7572#    if ($title && ($file ne $current_file || $icon ne $up_visible_mark)) {
7573    if ($title && ($file ne $current_file)) {
7574	#RRM: allow user-customisation of the link-text; thanks Dan Young
7575	if (defined &custom_link_hook ) {
7576	    $title = &custom_link_hook($title,$toc_section_info{join(' ',@link)});
7577	} else {
7578            $title = &purify($title);
7579	    $title = &get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
7580	}
7581	return ("\n".&make_href($file, $icon), &make_href($file, "$title"))
7582    }
7583#    elsif ($icon eq $up_visible_mark && $file eq $current_file && $EXTERNAL_UP_LINK) {
7584    elsif ($icon eq $up_visible_mark && $EXTERNAL_UP_LINK) {
7585 	return ("\n".&make_href($EXTERNAL_UP_LINK, $icon),
7586		&make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
7587    }
7588    elsif (($icon eq $previous_visible_mark || $icon eq $previous_page_visible_mark)
7589    	&& $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
7590	return ("\n".&make_href($EXTERNAL_PREV_LINK, $icon),
7591		&make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
7592    }
7593    elsif (($icon eq $next_visible_mark ||  $icon eq $next_page_visible_mark)
7594    	&& $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
7595	return ("\n".&make_href($EXTERNAL_DOWN_LINK, $icon),
7596		&make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
7597    }
7598    (&inactive_img($icon), "");
7599}
7600
7601sub add_special_link { &add_real_special_link(@_) }
7602sub add_real_special_link {
7603    local($icon, $file, $current_file) = @_;
7604    local($text);
7605    if ($icon eq $contents_visible_mark) { $text = $toc_title }
7606    elsif ($icon eq $index_visible_mark) { $text = $idx_title }
7607    elsif ($icon eq $biblio_visible_mark) { $text = $bib_title }
7608    (($file && ($file ne $current_file)) ?
7609    	("\n" . &make_href($file, $icon),
7610    	    ($text ? " ". &make_href($file, $text) : undef))
7611    	: ( undef, undef ))
7612}
7613
7614#RRM: add <LINK ...> tag to the HTML head.
7615#     suggested by Marcus Hennecke
7616#
7617sub add_link_tag {
7618    local($rel, $currentfile, @link ) = @_;
7619#    local($dummy, $file, $title) = split($delim,$toc_section_info{join(' ',@link)});
7620    local($dummy, $file, $title) = split($delim,$section_info{join(' ',@link)});
7621    ($dummy, $file, $title) = split($delim,$toc_section_info{join(' ',@link)})
7622	unless ($title);
7623
7624    if ($MULTIPLE_FILES && $ROOTED && $file) {
7625        if (!($DESTDIR =~ /\Q$FIXEDDIR\E[$dd$dd]?$/)) { $file = "..$dd$file" }
7626    }
7627    if ($file && !($file eq $currentfile) && (!$NO_NAVIGATION)) {
7628	#RRM: allow user-customisation of the REL attribute
7629	if (defined &custom_REL_hook ) {
7630	    $rel = &custom_REL_hook($rel,$toc_section_info{join(' ',@link)});
7631	}
7632        $more_links .= "\n<LINK REL=\"$rel\" HREF=\"$file\">";
7633    }
7634}
7635
7636sub remove_markers {
7637# modifies $_
7638    s/$lof_mark//go;
7639    s/$lot_mark//go;
7640    &remove_bbl_marks;
7641    s/$toc_mark//go;
7642    s/$idx_mark//go;
7643    &remove_cross_ref_marks;
7644    &remove_external_ref_marks;
7645    &remove_cite_marks;
7646    &remove_file_marks;
7647# sensitive markers
7648    &remove_image_marks;
7649    &remove_icon_marks;
7650    &remove_verbatim_marks;
7651    &remove_verb_marks;
7652    &remove_child_marks;
7653# uncaught markers
7654    s/$percent_mark/%/go;
7655    s/$ampersand_mark/\&amp;/go;
7656    s/$comment_mark\s*(\d+\n?)?//sgo;
7657    s/$caption_mark//go;
7658    s/<tex2html[^>]*>//g;
7659    s/$OP\d+\$CP//g;
7660    $_;
7661}
7662
7663sub replace_markers {
7664    &find_quote_ligatures;
7665    &replace_general_markers;
7666    &text_cleanup;
7667    # Must NOT clean the ~'s out of the navigation icons (in panel or text),
7668    # and must not interfere with verbatim-like environments
7669    &replace_sensitive_markers;
7670    &replace_init_file_mark if (/$init_file_mark/);
7671    &replace_file_marks;
7672    &post_replace_markers;
7673}
7674
7675sub replace_general_markers {
7676    if (defined &replace_infopage_hook) {&replace_infopage_hook if (/$info_page_mark/);}
7677    else { &replace_infopage if (/$info_page_mark/); }
7678    if (defined &add_idx_hook) {&add_idx_hook if (/$idx_mark/);}
7679    else {&add_idx if (/$idx_mark/);}
7680
7681    if ($segment_figure_captions) {
7682#	s/$lof_mark/<UL>$segment_figure_captions<\/UL>/o
7683#   } else { s/$lof_mark/<UL>$figure_captions<\/UL>/o }
7684	s/$lof_mark/$segment_figure_captions/o
7685    } else { s/$lof_mark/$figure_captions/o }
7686    if ($segment_table_captions) {
7687#	s/$lot_mark/<UL>$segment_table_captions<\/UL>/o
7688#   } else { s/$lot_mark/<UL>$table_captions<\/UL>/o }
7689	s/$lot_mark/$segment_table_captions/o
7690    } else { s/$lot_mark/$table_captions/o }
7691    &replace_morelinks();
7692    if (defined &replace_citations_hook) {&replace_citations_hook if /$bbl_mark/;}
7693    else {&replace_bbl_marks if /$bbl_mark/;}
7694    if (defined &add_toc_hook) {&add_toc_hook if (/$toc_mark/);}
7695    else {&add_toc if (/$toc_mark/);}
7696    if (defined &add_childs_hook) {&add_childs_hook if (/$childlinks_on_mark/);}
7697    else {&add_childlinks if (/$childlinks_on_mark/);}
7698    &remove_child_marks;
7699
7700    if (defined &replace_cross_references_hook) {&replace_cross_references_hook;}
7701    else {&replace_cross_ref_marks if /$cross_ref_mark||$cross_ref_visible_mark/;}
7702    if (defined &replace_external_references_hook) {&replace_external_references_hook;}
7703    else {&replace_external_ref_marks if /$external_ref_mark/;}
7704    if (defined &replace_cite_references_hook) {&replace_cite_references_hook;}
7705    else { &replace_cite_marks if /$cite_mark/; }
7706    if (defined &replace_user_references) {
7707 	&replace_user_references if /$user_ref_mark/; }
7708}
7709
7710sub replace_sensitive_markers {
7711    if (defined &replace_images_hook) {&replace_images_hook;}
7712    else {&replace_image_marks if /$image_mark/;}
7713    if (defined &replace_icons_hook) {&replace_icons_hook;}
7714    else {&replace_icon_marks if /$icon_mark_rx/;}
7715    if (defined &replace_verbatim_hook) {&replace_verbatim_hook;}
7716    else {&replace_verbatim_marks if /$verbatim_mark/;}
7717    if (defined &replace_verb_hook) {&replace_verb_hook;}
7718    else {&replace_verb_marks if /$verb_mark|$verbstar_mark/;}
7719    s/;SPMdollar;/\$/g; s/;SPMtilde;/\~/g; s/;SPMpct;/\%/g;
7720    s/;SPM/\&/go;
7721    s/$percent_mark/%/go;
7722    s/$ampersand_mark/\&amp;/go;
7723    #JKR: Turn encoded ~ back to normal
7724    s/&#126;/~/go;
7725}
7726
7727sub find_quote_ligatures {
7728    my $ent;
7729
7730# guillemets, governed by $NO_FRENCH_QUOTES
7731    do {
7732	$ent = &iso_map('laquo', "", 1);
7733	if ($NO_UTF && !$USE_UTF && $ent=~/\&\#(\d+);/) {
7734	    $ent='' if ($1 > 255);
7735	}
7736	s/((\&|;SPM)lt;){2}/$ent/ogs if $ent;
7737	$ent = &iso_map('raquo', "", 1) if ($ent);
7738	s/((\&|;SPM)gt;){2}/$ent/ogs if $ent;
7739	# single guillemot chars cannot be easily implemented this way
7740	# finding an approp regexp is work for the future
7741    } unless ($NO_FRENCH_QUOTES);
7742
7743    $ent = &iso_map("gg", "", 1);
7744    s/;SPMgg;/($ent ? $ent : '&gt;&gt;')/eg unless ($USE_NAMED_ENTITIES);
7745    $ent = &iso_map("ll", "", 1);
7746    s/;SPMll;/($ent ? $ent : '&lt;&lt;')/eg unless ($USE_NAMED_ENTITIES);
7747
7748    my $ldquo, $rdquo;
7749# "curly" quotes, governed by  $USE_CURLY_QUOTES.
7750    do {
7751	$ldquo = &iso_map("ldquo", "", 1);
7752	if ($NO_UTF && !$USE_UTF && $ldquo =~ /\&\#(\d+);/) {
7753	    $ldquo = '' if ($1 > 255);
7754	}
7755	s/``/$ldquo/ogs if ($ldquo);
7756	$rdquo = &iso_map("rdquo", "", 1) if ($ldquo);
7757	s/''/$rdquo/ogs if ($rdquo);
7758
7759	# single curly quotes cannot be easily implemented this way
7760	# finding an approp regexp is work for the future
7761    } if ($USE_CURLY_QUOTES);
7762
7763# "german" quotes, governed by  $NO_GERMAN_QUOTES.
7764    do {
7765	$ent = &iso_map('bdquo', "", 1);
7766	if ($NO_UTF && !$USE_UTF && $ent =~ /\&\#(\d+);/) {
7767	    $ent = '' if ($1 > 255);
7768	}
7769	s/,,/$ent/eg if $ent;
7770
7771	# closing upper quotes are not properly displayed in browsers
7772	s/($ent[\w\s\&\#;']+)$ldquo/$1``/og
7773		if ($USE_CURLY_QUOTES && $ldquo && $ent);
7774    } unless ($NO_GERMAN_QUOTES);
7775}
7776
7777sub add_childlinks {
7778    local($before, $after, $star);
7779    while (/$childlinks_on_mark\#(\d)\#/) {
7780	$star = $1;
7781	$before = $`;
7782	$after = $';
7783	$before =~ s/\n\s*$//;
7784	$_ = join('', $before, "\n", $child_links, $after);
7785    }
7786}
7787
7788sub replace_infopage {
7789    local($INFO)=1 if !(defined $INFO);
7790    if ($INFO == 1) {
7791    	local($title);
7792	if ((defined &do_cmd_infopagename)||$new_command{'infopagename'}) {
7793	    local($br_id)=++$global{'max_id'};
7794	    $title = &translate_environments("$O$br_id$C\\infopagename$O$br_id$C");
7795	} else { $title = $info_title }
7796	    if ($MAX_SPLIT_DEPTH <= $section_commands{$outermost_level}) {
7797	        $_ =~ s/(<HR[^>]*>\s*)?$info_title_mark/
7798		    ($1? $1 : "\n<HR>")."\n<H2>$title<\/H2>"/eog;
7799	    } else {
7800	        $_ =~ s/$info_title_mark/"\n<H2>$title<\/H2>"/eog;
7801	    }
7802    }
7803    while (/$info_page_mark/o) {
7804	$_ = join('', $`, &do_cmd_textohtmlinfopage, $');
7805    }
7806}
7807
7808sub replace_init_file_mark {
7809    local($init_file, $init_contents, $info_line)=($INIT_FILE,'','');
7810    if (-f $init_file) {
7811    } elsif (-f "$orig_cwd$dd$init_file") {
7812	$init_file = $orig_cwd.$dd.$init_file;
7813    } else {
7814	s/$init_file_mark//g;
7815	return();
7816    }
7817    if(open(INIT, "<$init_file")) {
7818        foreach $info_line (<INIT>) {
7819	    $info_line =~ s/[<>"&]/'&'.$html_special_entities{$&}.';'/eg;
7820	    $init_contents .= $info_line;
7821	}
7822        close INIT;
7823    } else {
7824        print "\nError: Cannot read '$init_file': $!\n";
7825    }
7826    s/$init_file_mark/\n<BLOCKQUOTE><PRE>\n$init_contents\n<\/PRE><\/BLOCKQUOTE>\n/g;
7827}
7828
7829sub replace_morelinks {
7830    $_ =~ s/$more_links_mark/$more_links/e;
7831}
7832
7833# This code is extremely inefficient. At least the subtrees should be
7834# filtered according to $MAX_LINK_DEPTH before going into the
7835# inner loops.
7836# RRM: revamped parts, for $TOC_STARS, fixing some errors.
7837#
7838sub add_child_links { &add_real_child_links(@_) }
7839sub add_real_child_links {
7840    local($exclude, $base_file, $depth, $star, $current_key, @keys) = @_;
7841    local $min_depth = $section_commands{$outermost_level} - 1;
7842    return ('') if ((!$exclude)&&(!$LEAF_LINKS)&&($depth >= $MAX_SPLIT_DEPTH));
7843    if ((!$depth)&&($outermost_level)) { $depth = $min_depth }
7844
7845    local($_, $child_rx, @subtree, $next, %open, @roottree);
7846    local($first, $what, $pre, $change_key, $list_class);
7847    $childlinks_start = "<!--Table of Child-Links-->";
7848    $childlinks_end = "<!--End of Table of Child-Links-->\n";
7849    $child_rx = $current_key;
7850    $child_rx =~ s/( 0)*$//;	# Remove trailing 0's
7851    if ((!$exclude)&&($depth < $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH -1 )
7852#	    &&($depth >= $MAX_SPLIT_DEPTH-1)) {
7853	    &&($depth > $min_depth)) {
7854	if ((defined &do_cmd_childlinksname)||$new_command{'childlinksname'}) {
7855	    local($br_id)=++$global{'max_id'};
7856	    $what = &translate_environments("$O$br_id$C\\childlinksname$O$br_id$C");
7857	} else {
7858	    $what = "<strong>$child_name</strong>";
7859	}
7860	$list_class = ' CLASS="ChildLinks"' if ($USING_STYLES);
7861	$first = "$childlinks_start\n<A NAME=\"CHILD_LINKS\">$what<\/A>\n";
7862    } elsif ($exclude) {
7863	# remove any surrounding braces
7864	$exclude =~ s/^($O|$OP)\d+($C|$CP)|($O|$OP)\d+($C|$CP)$//g;
7865	# Table-of-Contents
7866	$list_class = ' CLASS="TofC"' if ($USING_STYLES);
7867	$childlinks_start = "\n<!--Table of Contents-->\n";
7868	$childlinks_end = "<!--End of Table of Contents-->";
7869	$first = "$childlinks_start";
7870    } else {
7871	$list_class = ' CLASS="ChildLinks"' if ($USING_STYLES);
7872	$first = "$childlinks_start\n"
7873	    . ($star ? '':"<A NAME=\"CHILD_LINKS\">$anchor_mark<\/A>\n");
7874    }
7875    my $startlist, $endlist;
7876    $startlist = "<UL$list_class>" unless $CHILD_NOLIST;
7877    $endlist = '</UL>' unless $CHILD_NOLIST;
7878    my $alt_item = '<BR>&nbsp;<BR>'."\n";
7879    my $outer_item = ($CHILD_NOLIST ? $alt_item : '<LI>');
7880    my $inner_item = '<LI>';
7881    my $inner_end = '</UL><BR>';
7882
7883    # collect the relevant keys...
7884    foreach $next (@keys) {
7885	if ($MULTIPLE_FILES && $exclude) {
7886	    # ...all but with this document as the root
7887	    if ($next =~ /^$THIS_FILE /) {
7888#		# make current document the root
7889#	    	$change_key = '0 '.$';
7890		push(@roottree,$next);
7891		print "\n$next : m-root" if ($VERBOSITY > 3);
7892	    } else {
7893		push(@subtree,$next);
7894		print "\n$next : m-sub" if ($VERBOSITY > 3);
7895	    }
7896	} elsif (($next =~ /^$child_rx /)&&($next ne $current_key)) {
7897	# ...which start as $current_key
7898	    push(@subtree,$next);
7899	    print "\n$next : sub $child_rx" if ($VERBOSITY > 3);
7900	} else {
7901	    print "\n$next : out $current_key" if ($VERBOSITY > 3);
7902	}
7903    }
7904    if (@subtree) { @subtree = sort numerically @subtree; }
7905    if (@roottree) {
7906    	@roottree = sort numerically @roottree;
7907    	@subtree = ( @roottree, @subtree );
7908    }
7909    # @subtree now contains the subtree rooted at the current node
7910
7911    local($countUL); #counter to ensure correct tag matching
7912    my $root_file, $href;
7913    if (@subtree) {
7914	local($next_depth, $file, $title, $sec_title, $star, $ldepth,$this_file, $prev_file);
7915	$ldepth = $depth;
7916	$prev_file = $base_file;
7917#	@subtree = sort numerically @subtree;
7918	foreach $next (@subtree) {
7919	    $title = '';
7920	    if ($exclude) {
7921		# making TOC
7922		($next_depth, $file, $sec_title) =
7923			split($delim,$section_info{$next});
7924		($next_depth, $file, $title, $star) =
7925			split($delim,$toc_section_info{$next});
7926		# use the %section_info  title, in case there are images
7927		$title = $sec_title if ($sec_title =~ /image_mark>\#/);
7928	    } else {
7929		# making mini-TOC i.e. the child-links tables
7930		$star = '';
7931		($next_depth, $file, $title) =
7932			split($delim,$section_info{$next});
7933	    }
7934	    $root_file = $file unless $root_file;
7935	    if ($root_file && $root_file =~ /_mn\./) { $root_file=$` };
7936	    # remove any surrounding braces
7937	    $title =~ s/^($O|$OP)\d+($C|$CP)|($O|$OP)\d+($C|$CP)$//g;
7938	    next if ($exclude && $title =~ /^$exclude$/);
7939	    if (!$title) {
7940		($next_depth, $file, $title, $star) =
7941			split($delim,$toc_section_info{$next});
7942	    }
7943	    $this_file = $file;
7944	    $title = "\n".$title if !($title =~/^\n/);
7945	    next if ( $exclude &&(				# doing Table-of-Contents
7946		( $TOC_DEPTH &&($next_depth > $TOC_DEPTH))	# and  too deep
7947		||($star && !$TOC_STARS ) ));			# or no starred sections
7948	    $file = "" if (!$MAX_SPLIT_DEPTH); # Martin Wilck
7949	    next if ($exclude && !$MULTIPLE_FILES &&($title =~ /^\s*$exclude\s*$/));
7950	    next if (!$exclude && $next_depth > $MAX_LINK_DEPTH + $depth);
7951	    print "\n$next :" if ($VERBOSITY > 3);
7952	    if ($this_file =~ /^(\Q$prev_file\E|\Q$base_file\E)$/) {
7953		$file .= join('', "#SECTION", split(' ', $next));
7954	    } else { $prev_file = $file }
7955
7956	    if (!$next_depth && $MULTIPLE_FILES) { ++$next_depth }
7957	    local($num_open) = (split('/',%open))[0];
7958	    if ((($next_depth > $ldepth)||$first)
7959		&& ((split('/',%open))[0] < $MAX_LINK_DEPTH + $depth )
7960		) {
7961		# start a new <UL> list
7962		if ($first) {
7963		    $_ = "$first\n$startlist\n"; $countUL++;
7964		    local $i = 1;
7965		    while ($i <= $ldepth) {
7966			$open{$i}=0; $i++
7967		    }
7968		    $first = '';	# include NAME tag first time only
7969		    while ($i < $next_depth) {
7970			$open{$i}=1; $i++;
7971			$_ .= ($countUL >1 ? $inner_item : $outer_item)."<UL>\n";
7972			$countUL++;
7973		    }
7974		} else {
7975		    $_ .= "<UL>\n"; $countUL++;
7976		}
7977		$ldepth = $next_depth;
7978		$open{$ldepth}++;
7979		# append item to this list
7980		print " yes " if ($VERBOSITY > 3);
7981		if (defined &add_frame_child_links) {
7982		    $href = &make_href($file,$title);
7983		    if ($href =~ s/($root_file)_mn/$1_ct/) {
7984			$href =~ s/(target=")main(")/$1contents$2/i;
7985		    };
7986		    $_ .= ($countUL >1 ? $inner_item : $outer_item)
7987			. $href . "\n";
7988		} else {
7989		    $_ .= ($countUL >1 ? $inner_item : $outer_item)
7990			. &make_href($file,$title) . "\n";
7991		}
7992	    }
7993	    elsif (($next_depth)&&($next_depth <= $ldepth)
7994		&&((split('/',%open))[0] <= $MAX_LINK_DEPTH + $depth )
7995		) {
7996		# append item to existing <UL> list
7997		while (($next_depth < $ldepth) && %open ) {
7998		# ...closing-off any nested <UL> lists
7999		    if ($open{$ldepth}) {
8000			if (!(defined $open{$next_depth}))  {
8001			    $open{$next_depth}++;
8002			} else {
8003			    $_ .= ($countUL==2 ? $inner_end : '</UL>')."\n";
8004			    $countUL--;
8005			}
8006			delete $open{$ldepth};
8007		    };
8008		    $ldepth--;
8009		}
8010		$ldepth = $next_depth;
8011		print " yes" if ($VERBOSITY > 3);
8012		if (defined &add_frame_child_links) {
8013		    $href = &make_href($file,$title);
8014		    if ($href =~ s/($root_file)_mn/$1_ct/) {
8015			$href =~ s/(target=")main(")/$1contents$2/i;
8016		    };
8017		    $_ .= ($countUL >1 ? $inner_item : $outer_item)
8018			. $href . "\n";
8019		} else {
8020		    $_ .= ($countUL >1 ? $inner_item : $outer_item)
8021			. &make_href($file,$title) . "\n";
8022		}
8023	    } else {
8024		# ignore items that are deeper than $MAX_LINK_DEPTH
8025		print " no" if ($VERBOSITY > 3);
8026	    }
8027	}
8028
8029	if (%open) {
8030	# close-off any remaining <UL> lists
8031	    $countUL-- if $CHILD_NOLIST;
8032	    local $cnt = (split('/',%open))[0];
8033	    local $i = $cnt;
8034		while ($i > $depth) {
8035		    if ($open{$i}) {
8036			$_ .= '</UL>' if $countUL;
8037			$countUL--;
8038			delete $open{$i};
8039		    }
8040		$i--;
8041	    }
8042	}
8043    }
8044    # just in case the count is wrong
8045    $countUL-- if ($CHILD_NOLIST && $countUL > 0);
8046    $countUL = '' if ($countUL < 0);
8047    while ($countUL) { $_ .= '</UL>'; $countUL-- }
8048    ($_ ? join('', $_, "\n$childlinks_end") : '');
8049}
8050
8051sub child_line {($CHILDLINE) ? "$CHILDLINE" : "<BR>\n<HR>";}
8052sub upper_child_line { "<HR>\n"; }
8053
8054sub adjust_root_keys {
8055    return() unless ($MULTIPLE_FILES && $ROOTED);
8056    local($next,$change_key,$current_rx);
8057    local(@keys) = (keys %toc_section_info);
8058
8059    local($current_key) = join(' ',@curr_sec_id);
8060    $current_key =~ /^(\d+ )/;
8061    $current_rx = $1;
8062    return() unless $current_rx;
8063
8064    # alter the keys which start as $current_key
8065    foreach $next (@keys) {
8066	if ($next =~ /^$current_rx/) {
8067	    # make current document the root
8068	    $change_key = '0 '.$';
8069	    $toc_section_info{$change_key} = $toc_section_info{$next};
8070	    $section_info{$change_key} = $section_info{$next};
8071#	    if (!($next eq $current_key)) {
8072#		$toc_section_info{$next} = $section_info{$next} = '';
8073#	    }
8074	}
8075    }
8076}
8077
8078sub top_page {
8079    local($file, @navigation_panel) = @_;
8080    # It is the top page if there is a link to itself
8081    join('', @navigation_panel) =~ /$file/;
8082}
8083
8084# Sets global variable $AUX_FILE
8085sub process_aux_file {
8086    local(@exts) = ('aux');
8087    push(@exts, 'lof') if (/\\listoffigures/s);
8088    push(@exts, 'lot') if (/\\listoftables/s);
8089    local($_, $status);		# To protect caller from &process_ext_file
8090    $AUX_FILE = 1;
8091    foreach $auxfile (@exts) {
8092	$status = &process_ext_file($auxfile);
8093	if ($auxfile eq "aux" && ! $status) {
8094	    print "\nCannot open $FILE.aux $!\n";
8095	    &write_warnings("\nThe $FILE.aux file was not found," .
8096			    " so sections will not be numbered \nand cross-references "
8097			    . "will be shown as icons.\n");
8098	}
8099    }
8100    $AUX_FILE = 0;
8101}
8102
8103sub do_cmd_htmlurl {
8104    local($_) = @_;
8105    local($url);
8106    $url = &missing_braces unless (
8107	(s/$next_pair_pr_rx/$br_id=$1;$url=$2;''/e)
8108	||(s/$next_pair_rx/$br_id=$1;$url=$2;''/e));
8109    $url =~ s/\\(html)?url\s*($O|$OP)([^<]*)\2/$3/;
8110    $url =~ s/\\?~/;SPMtilde;/og;
8111    join('','<TT>', &make_href($url,$url), '</TT>', $_);
8112}
8113sub do_cmd_url { &do_cmd_htmlurl(@_) }
8114
8115sub make_href { &make_real_href(@_) }
8116sub make_real_href {
8117    local($link, $text) = @_;
8118    $href_name++;
8119    my $htarget = '';
8120    $htarget = ' target="'.$target.'"'
8121	if (($target)&&($HTML_VERSION > 3.2));
8122    #HWS: Nested anchors not allowed.
8123    $text =~ s/<A .*><\/A>//go;
8124    #JKR: ~ is handled different - &#126; is turned to ~ later.
8125    #$link =~ s/&#126;/$percent_mark . "7E"/geo;
8126    if ($text eq $link) { $text =~ s/~/&#126;/g; }
8127    $link =~ s/~/&#126;/g;
8128    # catch \url or \htmlurl
8129    $link =~ s/\\(html)?url\s*(($O|$OP)\d+($C|$CP))([^<]*)\2/$5/;
8130    $link =~ s:(<TT>)?<A [^>]*>([^<]*)</A>(</TT>)?(([^<]*)|$):$2$4:;
8131    # this should not be here; else TOC, List of Figs, etc. fail:
8132    # $link =~ s/^\Q$CURRENT_FILE\E(\#)/$1/ unless ($SEGMENT||$SEGMENTED);
8133    $text = &simplify($text);
8134    "<A NAME=\"tex2html$href_name\"$htarget\n  HREF=\"$link\">$text</A>";
8135}
8136
8137sub make_href_noexpand { # clean
8138    my ($link, $name, $text) = @_;
8139    do {$name = "tex2html". $href_name++} unless $name;
8140    #HWS: Nested anchors not allowed.
8141    $text =~ s/<A .*><\/A>//go;
8142    #JKR: ~ is handled different - &#126; is turned to ~ later.
8143    #$link =~ s/&#126;/$percent_mark . "7E"/geo;
8144    if ($text eq $link) { $text =~ s/~/&#126;/g; }
8145    $link =~ s/~/&#126;/g;
8146    # catch \url or \htmlurl
8147    $link =~ s/\\(html)?url\s*(($O|$OP)\d+($C|$CP))([^<]*)\2/$5/;
8148    $link =~ s:(<TT>)?<A [^>]*>([^<]*)</A>(</TT>)?(([^<]*)|$):$2$4:;
8149    "<A NAME=\"$name\"\n HREF=\"$link\">$text</A>";
8150}
8151
8152sub make_named_href {
8153    local($name, $link, $text) = @_;
8154    $text =~ s/<A .*><\/A>//go;
8155    $text = &simplify($text);
8156    if ($text eq $link) { $text =~ s/~/&#126;/g; }
8157    $link =~ s/~/&#126;/g;
8158    # catch \url or \htmlurl
8159    $link =~ s/\\(html)?url\s*(($O|$OP)\d+($C|$CP))([^<]*)\2/$5/;
8160    $link =~ s:(<TT>)?<A [^>]*>([^<]*)</A>(</TT>)?(([^<]*)|$):$2$4:;
8161    if (!($name)) {"<A\n HREF=\"$link\">$text</A>";}
8162    elsif ($text =~ /^\w/) {"<A NAME=\"$name\"\n HREF=\"$link\">$text</A>";}
8163    else {"<A NAME=\"$name\"\n HREF=\"$link\">$text</A>";}
8164}
8165
8166sub make_section_heading {
8167    local($text, $level, $anchors) = @_;
8168    local($elevel) = $level; $elevel =~ s/^(\w+)\s.*$/$1/;
8169    local($section_tag) = join('', @curr_sec_id);
8170    local($align,$pre_anchors);
8171
8172    # separate any invisible anchors or alignment, if this has not already been done
8173    if (!($anchors)){ ($anchors,$text) = &extract_anchors($text) }
8174    else {
8175	$anchors =~ s/(ALIGN=\"\w*\")/$align = " $1";''/e;
8176	$align = '' if ($HTML_VERSION < 2.2);
8177	$anchors = &translate_commands($anchors) if ($anchors =~ /\\/);
8178    }
8179
8180    # strip off remains of bracketings
8181    $text =~ s/$OP\d+$CP//g;
8182    if (!($text)) {
8183	# anchor to a single `.' only
8184	$text = "<A NAME=\"SECTION$section_tag\">.</A>$anchors\n";
8185    } elsif ($anchors) {
8186#	# put anchors immediately after, except if title is too long
8187#	if ((length($text)<60 )&&(!($align)||($align =~/left/))) {
8188#	    $text = "<A NAME=\"SECTION$section_tag\">$text</A>\n" . $anchors;
8189	# ...put anchors preceding the title, on a separate when left-aligned
8190#	} else {
8191	    $text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A>$anchors"
8192		. (!($align)||($align =~ /left/i ) ? "<BR>" : "") . "\n". $text;
8193#	}
8194    } elsif (!($text =~ /<A[^\w]/io)) {
8195	# no embedded anchors, so anchor it all
8196	$text = "<A NAME=\"SECTION$section_tag\">\n" . $text . "</A>";
8197    } else {
8198	# there are embedded anchors; these cannot be nested
8199	local ($tmp) = $text;
8200	$tmp =~ s/<//o ;	# find 1st <
8201	if ($`) {		# anchor text before the first <
8202#	    $text = "<A NAME=\"SECTION$section_tag\">\n" . $` . "</A>\n<" . $';
8203	    $text = "<A NAME=\"SECTION$section_tag\">\n" . $` . "</A>";
8204	    $pre_anchors = "<" . $';
8205	    if ($pre_anchors =~ /^(<A NAME=\"[^\"]+>${anchor_invisible_mark}<\/A>\s*)+$/) {
8206		$pre_anchors .= "\n"
8207	    } else { $text .= $pre_anchors; $pre_anchors = '' }
8208	} else {
8209	    # $text starts with a tag
8210	    local($after,$tmp) = ($','');
8211	    if ( $after =~ /^A[^\w]/i ) {
8212		# it is an anchor already, so need a separate line
8213		$text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A><BR>\n$text";
8214	    } else {
8215		# Is it a tag enclosing the anchor ?
8216		$after =~ s/^(\w)*[\s|>]/$tmp = $1;''/eo;
8217		if ($after =~ /<A.*<\/$tmp>/) {
8218		    # it encloses an anchor, so use anchor_mark + break
8219		    $text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A><BR>\n$text";
8220		} else {
8221		    # take up to the anchor
8222		    $text =~ s/^(.*)<A([^\w])/"<A NAME=\"SECTION$section_tag\">$1<A$2"/oe;
8223		}
8224	    }
8225	}
8226    }
8227    "$pre_anchors\n<$level$align>$text\n<\/$elevel>";
8228}
8229
8230sub do_cmd_captionstar { &process_cmd_caption(1, @_) }
8231sub do_cmd_caption { &process_cmd_caption('', @_) }
8232sub process_cmd_caption {
8233    local($noLOTentry, $_) = @_;
8234    local($text,$opt,$br_id, $contents);
8235    local($opt) = &get_next_optional_argument;
8236    $text = &missing_braces unless (
8237	(s/$next_pair_pr_rx/$br_id=$1;$text=$2;''/e)
8238	||(s/$next_pair_rx/$br_id=$1;$text=$2;''/e));
8239
8240    # put it in $contents, so &extract_captions can find it
8241    local($contents) = join('','\caption', ($opt ? "[$opt]" : '')
8242	   , "$O$br_id$C" , $text , "$O$br_id$C");
8243
8244    # $cap_env is set by the surrounding figure/table
8245    &extract_captions($cap_env);
8246    $contents.$_;
8247}
8248
8249sub extract_captions {
8250    # Uses and modifies $contents and $cap_anchors, defined in translate_environments
8251    # and modifies $figure_captions, $table_captions, $before and $after
8252    # MRO: no effect! local($env,*cap_width) = @_;
8253    local($env) = @_;
8254    local(%captions, %optional_captions, $key, $caption, $optional_caption,
8255	  $item, $type, $list, $extra_list, $number, @tmp, $br_id, $_);
8256    # associate the br_id of the caption with the argument of the caption
8257    $contents =~ s/$caption_rx(\n)?/do {
8258	$key = $9; $caption = $10; $optional_caption = $3;
8259	$key = &filter_caption_key($key) if (defined &filter_caption_key);
8260	$optional_captions{$key} = $optional_caption||$caption;
8261	$captions{$key} = $10; ''}/ego;
8262#	$captions{$9} = $10; $caption_mark }/ego;
8263    $key = $caption = $optional_caption = '';
8264
8265    #catch any  \captionwidth  settings that may remain
8266    $contents =~ s/$caption_width_rx(\n)?/&translate_commands($&);''/eo;
8267
8268#    $after = join("","<P>",$after) if ($&);
8269#    $before .= "</P>" if ($&);
8270    #JKR: Replaced "Figure" and "Table" with variables (see latex2html.config too).
8271    if ($env eq 'figure') {
8272	if ((defined &do_cmd_figurename)||$new_command{'figurename'}){
8273	    $br_id = ++$global{'max_id'};
8274	    $type = &translate_environments("$O$br_id$C\\figurename$O$br_id$C")
8275		unless ($noLOFentry);
8276	} else { $type = $fig_name }
8277	$list = "\$figure_captions";
8278#	$extra_list = "\$segment_figure_captions" if ($figure_table_captions);
8279	$extra_list = "\$segment_figure_captions" if ($segment_figure_captions);
8280    }
8281    elsif ($env =~ /table/) {
8282	if ((defined &do_cmd_tablename)||$new_command{'tablename'}) {
8283	    $br_id = ++$global{'max_id'};
8284	    $type = &translate_environments("$O$br_id$C\\tablename$O$br_id$C")
8285		unless ($noLOTentry);
8286	} else { $type = $tab_name }
8287	$list = "\$table_captions";
8288	$extra_list = "\$segment_table_captions" if ($segment_table_captions);
8289    }
8290
8291#    $captions = "";
8292    $cap_anchors = "";
8293    local($this);
8294    foreach $key (sort {$a <=> $b;} keys %captions){ # Sort numerically
8295	$this = $captions{$key};
8296	$this =~ s/\\label\s*($O\d+$C)[^<]+\1//g; # remove \label commands
8297       	local($br_id) = ++$global{'max_id'};
8298	local($open_tags_R) = []; # locally, initially no style
8299	$caption = &translate_commands(
8300	     &translate_environments("$O$br_id$C$this$O$br_id$C"));
8301
8302	# same again for the optional caption
8303	$this = $optional_captions{$key};
8304	$this =~ s/\\label\s*($O\d+$C)[^<]+\1//g; # remove \label commands
8305	local($open_tags_R) = []; local($br_id) = ++$global{'max_id'};
8306	$this = &translate_environments("$O$br_id$C$this$O$br_id$C");
8307	$optional_caption = &translate_commands($this);
8308
8309	$cap_anchors .= "<A NAME=\"$key\">$anchor_mark</A>";
8310	$_ = $optional_caption || $caption;
8311
8312
8313	# split at embedded anchor or citation marker
8314	local($pre_anchor,$post_anchor) = ('','');
8315	if (/\s*(<A\W|\#[^#]*\#<tex2html_cite_[^>]*>)/){
8316	    $pre_anchor = "$`";
8317	    $post_anchor = "$&$'";
8318	    $pre_anchor = $anchor_invisible_mark
8319		unless (($pre_anchor)||($SHOW_SECTION_NUMBERS));
8320	} else {
8321	    $pre_anchor = "$_";
8322	}
8323
8324#JCL(jcl-tcl)
8325##	&text_cleanup;
8326##	$_ = &encode_title($_);
8327##	s/&nbsp;//g;            # HWS - LaTeX changes ~ in its .aux files
8328#	$_ = &sanitize($_);
8329##
8330#	$_ = &revert_to_raw_tex($_);
8331
8332	#replace image-markers by the image params
8333	s/$image_mark\#([^\#]+)\#/&purify_caption($1)/e;
8334
8335	local($checking_caption, $cap_key) = (1, $_);
8336	$cap_key = &simplify($cap_key);
8337	$cap_key = &sanitize($cap_key);
8338	@tmp = split(/$;/, eval ("\$encoded_$env" . "_number{\$cap_key}"));
8339	$number = shift(@tmp);
8340	$number = "" if ($number eq "-1");
8341
8342	if (!$number) {
8343	    $cap_key = &revert_to_raw_tex($cap_key);
8344	    @tmp = split(/$;/
8345	       , eval ("\$encoded_$env" . "_number{\$cap_key}"));
8346	    $number = shift(@tmp);
8347	    $number = "" if ($number eq "-1");
8348	}
8349
8350	#resolve any embedded cross-references first
8351	$checking_caption = '';
8352	$_ = &simplify($_);
8353	$_ = &sanitize($_);
8354
8355
8356#	@tmp = split(/$;/, eval ("\$encoded_$env" . "_number{\$_}"));
8357#	$number = shift(@tmp);
8358#	$number = "" if ($number eq "-1");
8359
8360	&write_warnings(qq|\nNo number for "$_"|) if (! $number);
8361	eval("\$encoded_$env" . "_number{\$_} = join(\$;, \@tmp)");
8362
8363	$item = join( '', ($SHOW_SECTION_NUMBERS ? $number."\. " : '')
8364	    , &make_href("$CURRENT_FILE#$key", $pre_anchor)
8365	    , $post_anchor);
8366	undef $_;
8367	undef @tmp;
8368
8369	$captions = join("", ($captions ? $captions."\n<BR>\n" : '')
8370		, "<STRONG>$type" , ($number ? " $number:" : ":")
8371		, "</STRONG>\n$caption" , (($captions) ? "\n" : "" ));
8372
8373	do {
8374	    eval "$extra_list .= \"\n<LI>\" .\$item" if ($extra_list);
8375	    eval "$list .= \"\n<LI>\" .\$item" }
8376		 unless ( $noLOTentry || $noLOFentry);
8377#	eval("print \"\nCAPTIONS:\".$extra_list.\n\"");
8378    }
8379}
8380
8381
8382# This processes \label commands found in environments that will
8383# be handed over to Latex. Sets the table %symbolic_labels
8384sub do_labels {
8385    local($context,$new_context) = @_;
8386    local($label);
8387    # MRO: replaced $* by /m
8388    $context =~ s/\s*$labels_rx/do {
8389	$label = &do_labels_helper($2);
8390	$new_context = &anchor_label($label,$CURRENT_FILE,$new_context);""}/geom;
8391    $new_context;
8392}
8393
8394sub extract_labels {
8395    local($_) = @_;
8396    local($label,$anchors);
8397    # MRO: replaced $* by /m
8398    while (s/[ \t]*$labels_rx//om) {
8399        $label = &do_labels_helper($2);
8400        $anchors .= &anchor_label($label,$CURRENT_FILE,'');
8401    }
8402    ($_, $anchors);
8403}
8404
8405# This should be done inside the substitution but it doesn't work ...
8406sub do_labels_helper {
8407    local($_) = @_;
8408    s/$label_rx/_/g;  # replace non-alphanumeric characters
8409    $symbolic_labels{$_} = $latex_labels{$_}; # May be empty;
8410    $_;
8411}
8412
8413sub convert_to_description_list {
8414    # MRO: modified to use $_[1]
8415    # local($which, *list) = @_;
8416    my $which = $_[0];
8417    $_[1] =~ s!(</A>\s*)<[OU]L([^>]*)>!$1<DD><DL$2>!ig;
8418    $_[1] =~ s!<(/?)[OU]L([^>]*)>!$1? "<$1DL$2>":"<DL$2>"!eig;
8419    $_[1] =~ s!(</?)LI>!$1D$which>!ig;
8420#    $_[1] =~ s/^\s*<DD>//;
8421}
8422
8423sub add_toc { &add_real_toc(@_) }
8424sub add_real_toc {
8425    local($temp1, $temp2);
8426    print "\nDoing table of contents ...";
8427    local(@keys) = keys %toc_section_info;
8428    @keys = sort numerically @keys;
8429    $temp1 = $MAX_LINK_DEPTH; $temp2 = $MAX_SPLIT_DEPTH;
8430    $MAX_SPLIT_DEPTH = $MAX_LINK_DEPTH = 1000;
8431    #JKR: Here was a "Contents" - replaced it with $toc_title
8432    local($base_key) = $keys[0];
8433    if ($MULTIPLE_FILES) {
8434    	$base_key = $THIS_FILE;
8435    }
8436    local($title);
8437    if ((defined &do_cmd_contentsname)||$new_command{'contentsname'}) {
8438	local($br_id)=++$global{'max_id'};
8439	$title = &translate_environments("$O$br_id$C\\contentsname$O$br_id$C");
8440    } else { $title = $toc_title }
8441    local($toc,$on_first_page) = ('','');
8442    $on_first_page = $CURRENT_FILE
8443	unless ($MAX_SPLIT_DEPTH && $MAX_SPLIT_DEPTH <1000);
8444    $toc = &add_child_links($title,$on_first_page,'',1,$keys[0],@keys);
8445    &convert_to_description_list('T',$toc) if ($use_description_list);
8446    s/$toc_mark/$toc/;
8447    $MAX_LINK_DEPTH = $temp1; $MAX_SPLIT_DEPTH = $temp2;
8448}
8449
8450# Assign ref value, but postpone naming the label
8451sub make_half_href {
8452    local($link) = $_[0];
8453    $href_name++;
8454    "<A NAME=\"tex2html$href_name\"\n HREF=\"$link\">";
8455}
8456
8457
8458# Redefined in makeidx.perl
8459sub add_idx {
8460    local($sidx_style, $eidx_style) =('<STRONG>','</STRONG>');
8461    if ($INDEX_STYLES) {
8462	if ($INDEX_STYLES =~/,/) {
8463	local(@styles) = split(/\s*,\s*/,$INDEX_STYLES);
8464	    $sidx_style = join('','<', join('><',@styles) ,'>');
8465	    $eidx_style = join('','</', join('></',reverse(@styles)) ,'>');
8466	} else {
8467	    $sidx_style = join('','<', $INDEX_STYLES,'>');
8468	    $eidx_style = join('','</', $INDEX_STYLES,'>');
8469	}
8470    }
8471    &add_real_idx(@_)
8472}
8473sub add_real_idx {
8474    print "\nDoing the index ...";
8475    local($key, $str, @keys, $index, $level, $count,
8476	  @previous, @current);
8477    @keys = keys %index;
8478    @keys = sort keysort  @keys;
8479    $level = 0;
8480    foreach $key (@keys) {
8481	@current = split(/!/, $key);
8482	$count = 0;
8483	while ($current[$count] eq $previous[$count]) {
8484	    $count++;
8485	}
8486	while ($count > $level) {
8487	    $index .= "\n<DL COMPACT>";
8488	    $level++;
8489	}
8490	while ($count < $level) {
8491	    $index .= "\n</DL>";
8492	    $level--;
8493	}
8494	foreach $term (@current[$count .. $#current-1]) {
8495	    # need to "step in" a little
8496#	    $index .= "<DT>" . $term . "\n<DL COMPACT>";
8497	    $index .= "\n<DT>$sidx_style" . $term . "$eidx_style\n<DD><DL COMPACT>";
8498	    $level++;
8499	}
8500	$str = $current[$#current];
8501	$str =~ s/\#\#\#\d+$//o; # Remove the unique id's
8502	$index .= $index{$key} .
8503	    # If it's the same string don't start a new line
8504	    (&index_key_eq(join('',@current), join('',@previous)) ?
8505	     ", $sidx_style" . $cross_ref_visible_mark . "$eidx_style</A>\n" :
8506	     "<DT>$sidx_style" . $str . "$eidx_style</A>\n");
8507	@previous = @current;
8508    }
8509    while ($count < $level) {
8510	$index .= "\n</DL>";
8511	$level--;
8512    }
8513    $index = '<DD>'.$index unless ($index =~ /^\s*<D(T|D)>/);
8514
8515    $index =~ s/(<A [^>]*>)(<D(T|D)>)/$2$1/g;
8516
8517#    s/$idx_mark/<DL COMPACT>$index<\/DL>/o;
8518    s/$idx_mark/$preindex\n<DL COMPACT>\n$index<\/DL>\n/o;
8519}
8520
8521sub keysort {
8522    local($x, $y) = ($a,$b);
8523    $x = &clean_key($x);
8524    $y = &clean_key($y);
8525#    "\L$x" cmp "\L$y";  # changed sort-rules, by M Ernst.
8526    # Put alphabetic characters after symbols; already downcased
8527    $x =~ s/^([a-z])/~~~$1/;
8528    $y =~ s/^([a-z])/~~~$1/;
8529    $x cmp $y;
8530}
8531
8532sub index_key_eq {
8533    local($a,$b) = @_;
8534    $a = &clean_key($a);
8535    $b = &clean_key($b);
8536    $a eq $b;
8537}
8538
8539sub clean_key {
8540    local ($_) = @_;
8541    tr/A-Z/a-z/;
8542    s/\s+/ /g;		# squeeze white space and newlines into space
8543    s/ (\W)/$1/g;	# make foo( ), foo () and foo(), or <TT>foo</TT>
8544    ;			# and <TT>foo </TT> to be equal
8545    s/$O\d+$C//go;	# Get rid of bracket id's
8546    s/$OP\d+$CP//go;	# Get rid of processed bracket id's
8547    s/\#\#\#\d+$//o;	# Remove the unique id
8548    $_;
8549}
8550
8551
8552sub make_footnotes {
8553    # Uses $footnotes defined in translate and set in do_cmd_footnote
8554    # Also uses $footfile
8555    local($_) = "\n<DL>$footnotes\n<\/DL>";
8556    $footnotes = ""; # else they get used
8557    local($title);
8558    if ((defined &do_cmd_footnotename)||$new_command{'footnotename'}) {
8559	local($br_id)=++$global{'max_id'};
8560	$title = &translate_environments("$O$br_id$C\\footnotename$O$br_id$C");
8561    } else {
8562	$foot_title = "Footnotes" unless $foot_title;
8563	$title = $foot_title;
8564    }
8565    print "\nDoing footnotes ...";
8566#JCL(jcl-tcl)
8567# If the footnotes go into a separate file: see &make_file.
8568    if ($footfile) {
8569	$toc_sec_title = $title;
8570	&make_file($footfile, $title, $FOOT_COLOR); # Modifies $_;
8571	$_ = "";
8572    } else {
8573	$footnotes = ""; # else they get re-used
8574	$_ = join ('', '<BR><HR><H4>', $title, '</H4>', $_ );
8575    }
8576    $_;
8577}
8578
8579sub post_process_footnotes {
8580    &slurp_input($footfile);
8581    open(OUT, ">$footfile") || die "Cannot write file '$footfile': $!\n";
8582    &replace_markers;
8583    &post_post_process if (defined &post_post_process);
8584    &adjust_encoding;
8585    print OUT $_;
8586    close OUT;
8587}
8588
8589sub make_file {
8590    # Uses and modifies $_ defined in the caller
8591    local($filename, $title, $layout) = @_;
8592    $layout = $BODYTEXT unless $layout;
8593    $_ = join('',&make_head_and_body($title,$layout), $_
8594	, (($filename =~ /^\Q$footfile\E$/) ? '' : &make_address )
8595	, (($filename =~ /^\Q$footfile\E$/) ? "\n</BODY>\n</HTML>\n" : '')
8596	);
8597    &replace_markers unless ($filename eq $footfile);
8598
8599    unless(open(FILE,">$filename")) {
8600        print "\nError: Cannot write '$filename': $!\n";
8601        return;
8602    }
8603    print FILE $_;
8604    close(FILE);
8605}
8606
8607sub add_to_body {
8608    local($attrib, $value) = @_;
8609    local($body) = $BODYTEXT;
8610    if ($body =~ s/\Q$attrib\E\s*=\s*"[^"]*"/$attrib="$value"/) {
8611    } else {
8612	$body .= " $attrib=\"$value\""; $body =~ s/\s{2,}/ /g;
8613    }
8614    $BODYTEXT = $body if $body;
8615}
8616
8617sub replace_verbatim_marks {
8618    # Modifies $_
8619    my($tmp);
8620    s/$math_verbatim_rx/&make_comment('MATH', $verbatim{$1})/eg;
8621    s/$mathend_verbatim_rx/&make_comment('MATHEND', '')/eg;
8622#    s/$verbatim_mark(verbatim\*?)(\d+)#/<PRE>\n$verbatim{$2}\n<\/PRE>/go;
8623##    s/$verbatim_mark(\w*[vV]erbatim\*?)(\d+)#/\n$verbatim{$2}\n/go;
8624    s!$verbatim_mark(\w*[vV]erbatim\*?|tex2html_code)(\d+)#\n?!$tmp=$verbatim{$2};
8625	$tmp.(($tmp =~/\n\s*$/s)? '':"\n")!eg;
8626#	"\n".$tmp.(($tmp =~/\n\s*$/s)? '':"\n")!eg;
8627#    s/$verbatim_mark(rawhtml)(\d+)#/$verbatim{$2}/eg; # Raw HTML
8628    s/$verbatim_mark(imagesonly)(\d+)#//eg; # imagesonly is *not* replaced
8629    # Raw HTML, but replacements may have protected characters
8630    s/$verbatim_mark(rawhtml)(\d+)#/&unprotect_raw_html($verbatim{$2})/eg;
8631    s/$verbatim_mark$keepcomments_rx(\d+)#/$verbatim{$2}/ego; # Raw TeX
8632    s/$unfinished_mark$keepcomments_rx(\d+)#/$verbatim{$2}/ego; # Raw TeX
8633}
8634
8635# TeX's special characters may have been escaped with a '\'; remove it.
8636sub unprotect_raw_html {
8637    local($raw) = @_;
8638    $raw =~ s/\\($latex_specials_rx|~|\^|@)/$1/g;
8639    $raw;
8640}
8641
8642# remove file-markers; special packages may redefine &replace_file_marks
8643sub remove_file_marks {
8644    s/<(DD|LI)>\n?($file_mark|$endfile_mark)\#.*\#\n<\/\1>(\n|(<))/$4/gm;
8645    s/($file_mark|$endfile_mark)\#.*\#(\n|(<))/$3/gm;
8646}
8647sub replace_file_marks { &remove_file_marks }
8648
8649sub remove_verbatim_marks {
8650    # Modifies $_
8651    s/($math_verbatim_rx|$mathend_verbatim_rx)//go;
8652#    s/$verbatim_mark(verbatim\*?)(\d+)#//go;
8653    s/$verbatim_mark(\w*[Vv]erbatim\w*\*?)(\d+)#//go;
8654    s/$verbatim_mark(rawhtml|imagesonly)(\d+)#//go;
8655    s/$verbatim_mark$keepcomments_rx(\d+)#//go;
8656    s/$unfinished_mark$keepcomments_rx(\d+)#//go;
8657}
8658
8659sub replace_verb_marks {
8660    # Modifies $_
8661    s/(?:$verb_mark|$verbstar_mark)(\d+)$verb_mark/
8662	$code = $verb{$1};
8663	$code = &replace_comments($code) if ($code =~ m:$comment_mark:);
8664	"<code>$code<\/code>"/ego;
8665}
8666
8667sub replace_comments{
8668    local($_) = @_;
8669    $_ =~ s/$comment_mark(\d+)\n?/$verbatim{$1}/go;
8670    $_ =~ s/$comment_mark\d*\n/%\n/go;
8671    $_;
8672}
8673
8674sub remove_verb_marks {
8675    # Modifies $_
8676    s/($verb_mark|$verbstar_mark)(\d+)$verb_mark//go;
8677}
8678
8679# This is used by revert_to_raw_tex
8680sub revert_verbatim_marks {
8681    # Modifies $_
8682#    s/$verbatim_mark(verbatim)(\d+)#/\\begin{verbatim}$verbatim{$2}\\end{verbatim}\n/go;
8683    s/$verbatim_mark(\w*[Vv]erbatim)(\d+)#/\\begin{$1}\n$verbatim{$2}\\end{$1}\n/go;
8684    s/$verbatim_mark(rawhtml)(\d+)#/\\begin{rawhtml}\n$verbatim{$2}\\end{rawhtml}\n/go;
8685    s/$verbatim_mark(imagesonly|tex2html_code)(\d+)#\n?/$verbatim{$2}/go;
8686    s/$verbatim_mark$image_env_rx(\d+)#/\\begin{$1}\n$verbatim{$2}\\end{$1}\n/go;
8687    s/($math_verbatim_rx|$mathend_verbatim_rx)//go;
8688}
8689
8690sub revert_verb_marks {
8691    # Modifies $_
8692    s/$verbstar_mark(\d+)$verb_mark/\\verb*$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
8693    s/$verb_mark(\d+)$verb_mark/\\verb$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
8694}
8695
8696sub replace_cross_ref_marks {
8697    # Modifies $_
8698    local($label,$id,$ref_label,$ref_mark,$after,$name);
8699    local($invis) = "<tex2html_anchor_invisible_mark></A>";
8700#    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/
8701    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark<\/A>(\s*<A( NAME=\"\d+)\">$invis)?/
8702	do {($label,$id) = ($1,$2); $name = $4;
8703	    $ref_label = $external_labels{$label} unless
8704		($ref_label = $ref_files{$label});
8705	    print "\nXLINK<: $label : $id :$name " if ($VERBOSITY > 3);
8706	    $ref_label = '' if ($ref_label eq $CURRENT_FILE);
8707	    $ref_mark = &get_ref_mark($label,$id);
8708	    &extend_ref if ($name); $name = '';
8709	    print "\nXLINK: $label : $ref_label : $ref_mark " if ($VERBOSITY > 3);
8710	    '"' . "$ref_label#$label" . "\">" . $ref_mark . "<\/A>"
8711	}/geo;
8712
8713    # This is for pagerefs which cannot have symbolic labels ???
8714#    s/$cross_ref_mark#(\w+)#\w+>/
8715    s/$cross_ref_mark#([^#]+)#[^>]+>/
8716	do {$label = $1;
8717	    $ref_label = $external_labels{$label} unless
8718		($ref_label = $ref_files{$label});
8719	    $ref_label = '' if ($ref_label eq $CURRENT_FILE);
8720	    print "\nXLINKP: $label : $ref_label" if ($VERBOSITY > 3);
8721	    '"' . "$ref_files{$label}#$label" . "\">"
8722	}/geo;
8723}
8724
8725#RRM: this simply absorbs the name from the invisible anchor following,
8726#     when the anchor itself is not already named.
8727sub extend_ref {
8728    if ($ref_label !=~ /NAME=/) { $label .= "\"\n".$name  }
8729}
8730
8731sub remove_cross_ref_marks {
8732    # Modifies $_
8733#    s/$cross_ref_mark#(\w+)#(\w+)>$cross_ref_mark/
8734    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/
8735	print "\nLOST XREF: $1 : $2" if ($VERBOSITY > 3);''/ego;
8736#    s/$cross_ref_mark#(\w+)#\w+>//go;
8737    s/$cross_ref_mark#([^#]+)#[^#>]+>//go;
8738}
8739
8740sub replace_external_ref_marks {
8741    # Modifies $_
8742    local($label, $link);
8743#    s/$external_ref_mark#(\w+)#(\w+)>$external_ref_mark/
8744    s/$external_ref_mark#([^#]+)#([^>]+)>$external_ref_mark/
8745	do {($label,$id) = ($1,$2);
8746	    $link = $external_labels{$label};
8747	    print "\nLINK: $label : $link" if ($VERBOSITY > 3);
8748	    '"'. "$link#$label" . "\">\n"
8749	       . &get_ref_mark("userdefined$label",$id)
8750	}
8751    /geo;
8752}
8753
8754sub remove_external_ref_marks {
8755    # Modifies $_
8756#    s/$external_ref_mark#(\w+)#(\w+)>$external_ref_mark/
8757    s/$external_ref_mark#([^#]+)#([^>]+)>$external_ref_mark/
8758	print "\nLOST LINK: $1 : $2" if ($VERBOSITY > 3);''/ego;
8759}
8760
8761sub get_ref_mark {
8762    local($label,$id) = @_;
8763    ( ( $SHOW_SECTION_NUMBERS && $symbolic_labels{"$label$id"}) ||
8764     $latex_labels{"userdefined$label$id"} ||
8765     $symbolic_labels{"$label$id"} ||
8766     $latex_labels{$label} ||
8767     $external_latex_labels{$label} ||
8768     $cross_ref_visible_mark );
8769}
8770
8771sub replace_bbl_marks {
8772    # Modifies $_
8773    s/$bbl_mark#([^#]+)#/$citations{$1}/go;
8774}
8775
8776sub remove_bbl_marks {
8777    # Modifies $_
8778    s/$bbl_mark#([^#]+)#//go;
8779}
8780
8781sub replace_image_marks {
8782    # Modifies $_
8783    s/$image_mark#([^#]+)#([\.,;:\)\]])?(\001)?([ \t]*\n?)(\001)?/
8784	"$id_map{$1}$2$4"/ego;
8785#	"$id_map{$1}$2".(($4)?"\n":'')/ego;
8786}
8787
8788sub remove_image_marks {
8789    # Modifies $_
8790    s/$image_mark#([^#]+)#//go;
8791}
8792
8793sub replace_icon_marks {
8794    # Modifies $_
8795    if ($HTML_VERSION < 2.2 ) {
8796	local($icon);
8797	s/$icon_mark_rx/$icon = &img_tag($1);
8798	    $icon =~ s| BORDER="?\d+"?||;$icon/ego;
8799    } else {
8800	s/$icon_mark_rx/&img_tag($1)/ego;
8801    }
8802}
8803
8804sub remove_icon_marks {
8805    # Modifies $_
8806    s/$icon_mark_rx//go;
8807}
8808
8809sub replace_cite_marks {
8810    local($key,$label,$text,$file);
8811    # Modifies $_
8812    # Uses $citefile set by the thebibliography environment
8813    local($citefile) = $citefile;
8814    $citefile =~ s/\#.*$//;
8815
8816    s/#([^#]+)#$cite_mark#([^#]+)#((($OP\d+$CP)|[^#])*)#$cite_mark#/
8817	$text = $3; $label= $1; $file='';
8818	$text = $cite_info{$1} unless $text;
8819	if ($checking_caption){
8820	    "$label"
8821	} elsif ($citefiles{$2}){
8822	    $file = $citefiles{$2}; $file =~ s:\#.*$::;
8823	    &make_named_href('', "$file#$label","$text");
8824	} elsif ($PREAMBLE) {
8825	    $text || "\#!$1!\#" ;
8826	} elsif ($simplifying) {
8827	    $text
8828	} else {
8829	     &write_warnings("\nno reference for citation: $1");
8830	     "\#!$1!\#"
8831	}/sge ;
8832    #
8833    #RRM: Associate the cite_key with  $citefile , for use by other segments.
8834    if ($citefile) {
8835	local($cite_key, $cite_ref);
8836	while (($cite_key, $cite_ref) = each %cite_info) {
8837	    if ($ref_files{'cite_'."$cite_key"} ne $citefile) {
8838		$ref_files{'cite_'."$cite_key"} = $citefile;
8839		$changed = 1; }
8840	}
8841    }
8842}
8843
8844sub remove_cite_marks {
8845    # Modifies $_
8846    s/#([^#]+)#$cite_mark#([^#]+)#([^#]*)#$cite_mark#//go;
8847}
8848
8849sub remove_anchors {
8850# modifies $_
8851    s/<A[^>]*>//g;
8852    s/<\/A>//g;
8853}
8854
8855
8856# We need two matching keys to determine section/figure/etc. numbers.
8857# The "keys" are the name of the section/figure/etc. and its
8858# equivalent in the .aux file (also carrying the number we desire).
8859# But both keys might have been translated slightly different,
8860# depending on the usage of math, labels, special characters such
8861# as umlauts, or simply spacing!
8862#
8863# This routine tries to squeeze the HTML translated keys such
8864# that they match (hopefully very often). -- JCL
8865#
8866sub sanitize {
8867    local($_,$mode) = @_;
8868    &remove_markers;
8869    &remove_anchors;
8870    &text_cleanup;
8871    s/(\&|;SPM)nbsp;//g;            # HWS - LaTeX changes ~ in its .aux files
8872    #strip unwanted HTML constructs
8873    s/<\/?(P|BR|H)[^>]*>//g;
8874    s/\s+//g; #collapse white space
8875    $_;
8876}
8877
8878# This one removes any HTML markup, so that pure
8879# plain text remains. (perhaps with <SUP>/<SUB> tags)
8880# As the result will be part of the HTML file, it will be
8881# &text_cleanup'd later together with its context.
8882#
8883sub purify {
8884    local($_,$strict) = @_;
8885    &remove_markers;
8886    #strip unwanted HTML constructs
8887#    s/<[^>]*>/ /g;
8888    s/<(\/?SU[BP])>/>$1>/g unless ($strict);  # keep sup/subscripts ...
8889    s/<[^>]*>//g;                             # remove all other tags
8890    s/>(\/?SU[BP])>/<$1>/g unless ($strict);  # ...reinsert them
8891    s/^\s+|\001//g; s/\s\s+/ /g;              #collapse white space
8892    $_;
8893}
8894
8895# This one is not as strict as &sanitize.
8896# It is chosen to strip section names etc. a bit from
8897# constructs so that it better fits a table of contents,
8898# label files, etc.
8899# As the result will be part of the HTML file, it will be
8900# &text_cleanup'd later together with its context.
8901#
8902sub simplify {
8903    local($_) = @_;
8904    local($simplifying) = 1;
8905    s/$tex2html_envs_rx//g;
8906    if (/\\/) {
8907	local($USING_STYLES) = 0;
8908	$_ = &translate_commands($_);
8909	undef $USING_STYLES;
8910    }
8911    &replace_external_ref_marks if /$external_ref_mark/;
8912    &replace_cross_ref_marks if /$cross_ref_mark||$cross_ref_visible_mark/;
8913    &replace_cite_marks if /$cite_mark/;
8914    # strip unwanted HTML constructs
8915#    s/<\/?H[^>]*>/ /g;
8916    s/<\/?(H)[^>]*>//g;
8917    s/<\#\d+\#>//g;
8918    s/^\s+//;
8919    $_;
8920}
8921
8922#RRM: This extracts $anchor_mark portions from a given chunk of text,
8923#     so they can be positioned separately by the calling subroutine.
8924# added for v97.2:
8925#  search within the immediately following text also; so that
8926#  \index and \label after section-headings work as expected.
8927#
8928sub extract_anchors {
8929    local($search_text, $start_only) = @_;
8930    local($anchors) = '';
8931    local($untranslated_anchors) = '';
8932
8933    do {
8934	while ($search_text =~ s/<A[^>]*>($anchor_mark|$anchor_invisible_mark)<\/A>//) {
8935	    $anchors .= $&;
8936	}
8937    } unless ($start_only);
8938
8939    $search_text =~ s/\s*(\\protect)?\\(label|index|markright|markboth\s*(($O|$OP)\d+($C|$CP))[^<]*\3)\s*(($O|$OP)\d+($C|$CP))[^<]*\6/
8940	$anchors .= $&;''/eg unless ($start_only);
8941
8942    while ( s/^\s*<A[^>]*>($anchor_mark|$anchor_invisible_mark)<\/A>//m) {
8943	$untranslated_anchors .= $&;
8944    }
8945    while ( s/^\s*(\\protect)?\\(label|index|markright|markboth\s*(($O|$OP)\d+($C|$CP))[^<]*\3)\s*(($O|$OP)\d+($C|$CP))[^<]*\6//) {
8946	$untranslated_anchors .= $&;
8947    }
8948    if ($TITLE||$start_only) {
8949	$anchors .= &translate_commands($untranslated_anchors);
8950	$untranslated_anchors = '';
8951    }
8952    ($anchors.$untranslated_anchors,$search_text);
8953}
8954
8955# This routine must be called once on the text only,
8956# else it will "eat up" sensitive constructs.
8957sub text_cleanup {
8958    # MRO: replaced $* with /m
8959    s/(\s*\n){3,}/\n\n/gom;	# Replace consecutive blank lines with one
8960    s/<(\/?)P>\s*(\w)/<$1P>\n$2/gom;      # clean up paragraph starts and ends
8961    s/$O\d+$C//go;		# Get rid of bracket id's
8962    s/$OP\d+$CP//go;		# Get rid of processed bracket id's
8963    s/(<!)?--?(>)?/(length($1) || length($2)) ? "$1--$2" : "-"/ge;
8964    # Spacing commands
8965    s/\\( |$)/ /go;
8966    #JKR: There should be no more comments in the source now.
8967    #s/([^\\]?)%/$1/go;        # Remove the comment character
8968    # Cannot treat \, as a command because , is a delimiter ...
8969    s/\\,/ /go;
8970    # Replace tilde's with non-breaking spaces
8971    s/ *~/&nbsp;/g;
8972
8973    ### DANGEROUS ?? ###
8974    # remove redundant (not <P></P>) empty tags, incl. with attributes
8975    s/\n?<([^PD >][^>]*)>\s*<\/\1>//g;
8976    s/\n?<([^PD >][^>]*)>\s*<\/\1>//g;
8977    # remove redundant empty tags (not </P><P> or <TD> or <TH>)
8978    s/<\/(TT|[^PTH][A-Z]+)><\1>//g;
8979    s/<([^PD ]+)(\s[^>]*)?>\n*<\/\1>//g;
8980
8981
8982#JCL(jcl-hex)
8983# Replace ^^ special chars (according to p.47 of the TeX book)
8984# Useful when coming from the .aux file (german umlauts, etc.)
8985    s/\^\^([^0-9a-f])/chr((64+ord($1))&127)/ge;
8986    s/\^\^([0-9a-f][0-9a-f])/chr(hex($1))/ge;
8987}
8988
8989# This is useful for getting words from a title which are not cluttered
8990# with tex2html markers or HTML constructs
8991sub extract_pure_text {
8992    local($mode) = @_;
8993    &text_cleanup;		# Remove marking brackets
8994#
8995# HWS <hswan@perc.Arco.com>:  Conditionally doing the following
8996#     permits equations in section headings.
8997#
8998    if ($mode eq "strict") {
8999	s/$image_mark#[^#]*#//g;	# Remove image marker
9000	s/$bbl_mark#[^#]*#//g;		# Remove citations marker
9001        s/<tex2html_percent_mark>/%/g;  # BMcM: Retain % signs...
9002        s/<tex2html_ampersand_mark>/\&amp;/g;
9003	s/tex2html[\w\d]*//g; 	# Remove other markers
9004	}
9005
9006#
9007# HWS <hswan@perc.Arco.com>:  Replace next statement with the following two
9008#    to permit symbolic links and images to appear in section headings.
9009
9010#   s/<[^>]*>//go;			# Remove HTML constructs
9011    s/$OP[^#]*$CP//go;			# Remove <# * #> constructs
9012    s/<\s*>//go;			# Remove embedded whitespace
9013}
9014
9015############################ Misc ####################################
9016
9017# MRO: Print standardized header
9018sub banner {
9019    print <<"EOF";
9020This is LaTeX2HTML Version $TEX2HTMLVERSION
9021by Nikos Drakos, Computer Based Learning Unit, University of Leeds.
9022
9023EOF
9024}
9025
9026# MRO: Extract usage information from POD
9027sub usage {
9028    my $start  = 0;
9029    my $usage  = 'Usage: ';
9030    my $indent = '';
9031
9032    print (@_, "\n") if @_;
9033
9034#if @texlive@
9035    my $perldoc = "perldoc";
9036#else
9037    my $perldoc = "@PERLSCRIPTDIR@${dd}perldoc";
9038#fi
9039    my $script = $SCRIPT || $0;
9040    open(PIPE, "$perldoc -t $script |")
9041        || die "Fatal: can't open pipe: $!";
9042    while (<PIPE>) {
9043        if (/^\s*$/) {
9044            next;
9045        } elsif (/^SYNOPSIS/) {
9046            $start = 1;
9047        } elsif (/^\w/) {
9048            $start = 0;
9049        } elsif ($start == 1) {
9050            ($indent) = /^(\s*)/;
9051            s/^$indent/$usage/;
9052            $usage =~ s/./ /g;
9053            $start = 2;
9054            print $_;
9055        } elsif ($start == 2) {
9056            s/^$indent/$usage/;
9057            print $_;
9058        }
9059    }
9060    close PIPE;
9061    1;
9062}
9063
9064# The bibliographic references, the appendices, the lists of figures and tables
9065# etc. must appear in the contents table at the same level as the outermost
9066# sectioning command. This subroutine finds what is the outermost level and
9067# sets the above to the same level;
9068sub set_depth_levels {
9069    # Sets $outermost_level
9070    local($level);
9071    # scan the document body, not the preamble, for use of sectioning commands
9072    my ($contents) = $_;
9073    if ($contents =~ /\\begin\s*((?:$O|$OP)\d+(?:$C|$CP))document\1|\\startdocument/s) {
9074	$contents = $';
9075    }
9076    #RRM:  do not alter user-set value for  $MAX_SPLIT_DEPTH
9077    foreach $level ("part", "chapter", "section", "subsection",
9078		    "subsubsection", "paragraph") {
9079	last if (($outermost_level) = $contents =~ /\\($level)$delimiter_rx/);
9080	last if (($outermost_level) = $contents =~ /\\endsegment\s*\[\s*($level)\s*\]/s);
9081	if ($contents =~ /\\segment\s*($O\d+$C)[^<]+\1\s*($O\d+$C)\s*($level)\s*\2/s)
9082		{ $outermost_level = $3; last };
9083    }
9084    $level = ($outermost_level ? $section_commands{$outermost_level} :
9085	      do {$outermost_level = 'section'; 3;});
9086
9087    #RRM:  but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
9088    if ($REL_DEPTH && $MAX_SPLIT_DEPTH) {
9089	$MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
9090    } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };
9091
9092    %unnumbered_section_commands = (
9093          'tableofcontents', $level
9094	, 'listoffigures', $level
9095	, 'listoftables', $level
9096	, 'bibliography', $level
9097	, 'textohtmlindex', $level
9098        , %unnumbered_section_commands
9099        );
9100
9101    %section_commands = (
9102	  %unnumbered_section_commands
9103        , %section_commands
9104        );
9105}
9106
9107# Now ignores accents which cannot be translated to ISO-LATIN-1 characters
9108# Also replaces ?' and !' ....
9109sub replace_strange_accents {
9110    &real_replace_strange_accents(@_); # if ($CHARSET =~ /8859[_\-]1$/);
9111}
9112sub real_replace_strange_accents {
9113    # Modifies $_;
9114    s/\?`/&iso_map("iquest", "")/geo;
9115    s/!`/&iso_map("iexcl", "")/geo;
9116    s/\\\^\\i /&iso_map("icirc", "")/geo;
9117    my ($charset) = "${CHARSET}_character_map_inv";
9118    $charset =~ s/-/_/g;
9119    # convert upper 8-bit characters
9120    if (%$charset &&($CHARSET =~ /8859[_\-]1$/)) {
9121	s/([\200-\377])/
9122	    $tmp = $$charset{'&#'.ord($1).';'};
9123	    &mark_string($tmp) if ($tmp =~ m!\{!);
9124	    &translate_commands($tmp)
9125	/egos
9126    }
9127};
9128
9129# Creates a new directory or reuses old, perhaps after deleting its contents
9130sub new_dir {
9131    local($this_dir,$mode) = @_;
9132    local(@files)=();
9133    $this_dir = '.' unless $this_dir;
9134    $this_dir =~ s/[$dd$dd]+$//o;
9135    local($print_dir) = $this_dir.$dd;
9136    (!$mode && mkdir($this_dir, 0755)) ||
9137	do {
9138	    print "\nCannot create directory $print_dir: $!" unless ($mode);
9139	    if ($REUSE) {
9140		print ", reusing it.\n" unless ($mode);
9141		&reuse($this_dir,$print_dir);
9142	    } else {
9143	    	print "\n" unless ($mode);
9144		while (! ($answer =~ /^[dqr]$/)) {
9145		    if ($mode) {
9146			$answer = $mode;
9147		    } else {
9148		        print "(r) Reuse the images in the old directory OR\n"
9149			    . (($this_dir eq '.') ?
9150		"(d) *** DELETE *** the images in $print_dir  OR\n"
9151		: "(d) *** DELETE *** THE CONTENTS OF $print_dir  OR\n" )
9152			    . "(q) Quit ?\n:";
9153		        $answer = scalar(<STDIN>);
9154		    };
9155		    if ($answer =~ /^d$/) {
9156                        @files = ();
9157			if(opendir(DIR,$this_dir)) {
9158			    @files = readdir DIR;
9159			    closedir DIR;
9160                        } else {
9161                            print "\nError: Cannot read dir '$this_dir': $!\n";
9162                        }
9163			foreach (@files) {
9164			    next if /^\.+$/;
9165			    if (-d "$this_dir$dd$_") {
9166				&new_dir("$this_dir$dd$_",'d');
9167			    } elsif ($this_dir eq '.') {
9168				L2hos->Unlink($_) if (/\.(pl|gif|png)$/)
9169			    } else {
9170				L2hos->Unlink("$this_dir$dd$_");
9171			    };
9172			}
9173			return(1) if ($this_dir eq '.');
9174			if($mode) {
9175			  rmdir($this_dir);
9176			  rmdir($print_dir);
9177                        }
9178			if (!$mode) { &new_dir($this_dir,'r')};
9179			return(1);
9180		    } elsif ($answer =~ /^q$/) {
9181			die "Bye!\n";
9182		    } elsif ($answer =~ /^r$/) {
9183			&reuse($this_dir,$print_dir);
9184			return(1);
9185		    } else {print "Please answer r d or q!\n";};
9186		}
9187	    };
9188	};
9189    1;
9190}
9191
9192sub reuse {
9193    local($this_dir,$print_dir) = @_;
9194    $print_dir = $this_dir.$dd unless ($print_dir);
9195    if (-f "$this_dir$dd${PREFIX}images.pl") {
9196	print STDOUT "Reusing directory $print_dir:\n";
9197	local($key);
9198	require("$this_dir$dd${PREFIX}images.pl");
9199    }
9200}
9201
9202
9203# JCL(jcl-del) - use $CD rather than a space as delimiter.
9204# The commands might take white space, or not, depending on
9205# their definition. Eg. \relax takes white space, because it's a
9206# letter command, but \/ won't.
9207# TeX seems to have an internal separator: If \x is " x",
9208# and \y is "y", then \expandafter\y \x expands to "y x", TeX
9209# hasn't gobbled the space, meaning that spaces are gobbled once
9210# when the \y token is consumed, but then never again after \y.
9211#
9212# The actions below ensure to insert exactly one space after
9213# the command name.	# what happens to  `\ '  ?
9214# The substition is done twice to handle \one\delimits\another
9215# cases.
9216# The internal shortcut $CD is then turned into the single
9217# space we desire.
9218#
9219sub tokenize {
9220    # Modifies $_;
9221    local($rx) = @_;
9222    # $rx must be specially constructed, see &make_new_cmd_rx.
9223    if (length($rx)) {
9224	# $1: non-letter cmd, or $2: letter cmd
9225	s/$rx/\\$1$2$CD$4/g;
9226	s/$rx/\\$1$2$CD$4/g;
9227	s/$CD+/ /g;	# puts space after each command name
9228    }
9229}
9230
9231# When part of the input text contains special perl characters and the text
9232# is to be used as a pattern then these specials must be escaped.
9233sub escape_rx_chars {
9234    my($rx) = @_; # must use a copy of the string
9235    $rx =~ s:([\\(){}[\]\^\$*+?.|]):\\$1:g; $rx; }
9236
9237# Does not do much but may need it later ...
9238# The document environment has to be removed because it spans
9239# more than one sections (the translator can only deal with
9240# environments wholly contained with sections).
9241
9242# (Does a little more now ... the end of the preamble is now marked
9243# with an internally-generated command which causes all output
9244# erroneously generated from unrecognized commands in the preamble
9245# to vanish --- rst).
9246
9247sub remove_document_env {
9248#    s/\\begin$match_br_rx[d]ocument$match_br_rx/\\latextohtmlditchpreceding /o;
9249    if (/\\begin\s*${match_br_rx}document$match_br_rx/) {
9250        s/\\begin\s*$match_br_rx[d]ocument$match_br_rx/\\latextohtmlditchpreceding /
9251    }
9252#   s/\\end$match_br_rx[d]ocument$match_br_rx(.|\n)*//o;
9253    if (/\\end\s*${match_br_rx}document$match_br_rx/) { $_ = $` }
9254}
9255
9256# And here's the code to handle the marker ...
9257
9258sub do_cmd_latextohtmlditchpreceding {
9259    local($_) = @_;
9260    $ref_before = '';
9261    $_;
9262}
9263
9264print "\n"; # flushes a cache? This helps, for some unknown reason!!
9265
9266sub do_AtBeginDocument{
9267    local($_) = @_;
9268    eval $AtBeginDocument_hook;
9269    $_;
9270}
9271
9272sub cleanup {
9273    local($explicit) = @_;
9274    return unless $explicit || !$DEBUG;
9275
9276    if (opendir(DIR, '.')) {
9277	while (defined($_ = readdir(DIR))) {
9278	    L2hos->Unlink($_)
9279		if /\.ppm$/ || /^${PREFIX}images\.dvi$/ || /^(TMP[-._]|$$\_(image)?)/;
9280	}
9281	closedir (DIR);
9282    }
9283
9284    L2hos->Unlink("WARNINGS") if ($explicit &&(-f "WARNINGS"));
9285
9286    if ($TMPDIR && opendir(DIR, $TMPDIR)) {
9287	local(@files) = grep(!/^\.\.?$/,readdir(DIR));
9288	local($busy);
9289	foreach (@files) {
9290	    $busy .= $_." " unless (L2hos->Unlink("$TMPDIR$dd$_"));
9291	}
9292	closedir (DIR);
9293	if ($busy) {
9294	    print "\n\nFiles: $busy  are still in use.\n\n" if ($DEBUG);
9295	} else {
9296	    &write_warnings("\n\n Couldn't remove $TMPDIR : $!")
9297		unless (rmdir $TMPDIR);
9298	}
9299    }
9300    if (opendir(DIR, $TMP_)) {
9301	local(@files) = grep(!/^\.\.?$/,readdir(DIR));
9302	$busy = '';
9303	foreach (@files) {
9304	    $busy .= "$_ " unless (L2hos->Unlink("$TMP_$dd$_"));
9305	}
9306	closedir (DIR);
9307	local($full_dir) = L2hos->Make_directory_absolute($TMP_);
9308	if ($busy) {
9309	    print "\n\nFiles: $busy in $full_dir are still in use.\n\n"
9310	        if ($DEBUG);
9311	} else {
9312	    &write_warnings("\n\nCouldn't remove directory '$full_dir': $!")
9313		unless (rmdir $full_dir);
9314	}
9315    }
9316}
9317
9318sub handler {
9319    print "\nLaTeX2HTML shutting down.\n";
9320    kill ('INT', $child_pid) if ($child_pid);
9321    &close_dbm_database;
9322    &cleanup();
9323    exit(-1);
9324}
9325
9326# Given a filename or a directory it returns the file and the full pathname
9327# relative to the current directory.
9328sub get_full_path {
9329    local($file) = @_;
9330    local($path,$dir);
9331    if (-d $file) {	# $file is a directory
9332	$path = L2hos->Make_directory_absolute($file);
9333	$file = '';
9334
9335# JCL(jcl-dir)
9336    } elsif ($file =~ s|\Q$dd\E([^$dd$dd]*)$||o ) {
9337	$path = $file;
9338	$file = $1;
9339	$path = L2hos->Make_directory_absolute($path);
9340
9341#RRM: check within $TEXINPUTS directories
9342    } elsif (!($TEXINPUTS =~ /^\.$envkey$/)) {
9343	#check along directories in the $TEXINPUTS variable
9344	foreach $dir (split(/$envkey/,$TEXINPUTS)) {
9345	    $dir =~ s/[$dd$dd]$//o;
9346	    if (-f $dir.$dd.$file) {
9347		$path = L2hos->Make_directory_absolute($dir);
9348		last;
9349	    }
9350	}
9351    } else {
9352	$path = L2hos->Cwd();
9353    }
9354    ($path, $file);
9355}
9356
9357
9358# Given a directory name in either relative or absolute form, returns
9359# the absolute form.
9360# Note: The argument *must* be a directory name.
9361# The whole function has been moved to override.pm
9362
9363
9364
9365# Given a relative filename from the directory in which the original
9366# latex document lives, it tries to expand it to the full pathname.
9367sub fulltexpath {
9368    # Uses $texfilepath defined in sub driver
9369    local($file) = @_;
9370    $file =~ s/\s//g;
9371    $file = "$texfilepath$dd$file"
9372      unless (L2hos->is_absolute_path($file));
9373    $file;
9374}
9375
9376#RRM  Extended to allow customised filenames, set $CUSTOM_TITLES
9377#     or long title from the section-name, set $LONG_TITLES
9378#
9379sub make_name {
9380    local($sec_name, $packed_curr_sec_id) = @_;
9381    local($title,$making_name,$saved) = ('',1,'');
9382    if ($LONG_TITLES) {
9383	$saved = $_;
9384	&process_command($sections_rx, $_) if /^$sections_rx/;
9385	$title = &make_long_title($TITLE)
9386	    unless ((! $TITLE) || ($TITLE eq $default_title));
9387	$_ = $saved;
9388    } elsif ($CUSTOM_TITLES) {
9389	$saved = $_;
9390	&process_command($sections_rx, $_) if /^$sections_rx/;
9391	$title = &custom_title_hook($TITLE)
9392	    unless ((! $TITLE) || ($TITLE eq $default_title));
9393	$_ = $saved;
9394    }
9395    if ($title) {
9396	#ensure no more than 32 characters, including .html extension
9397	$title =~ s/^(.{1,27}).*$/$1/;
9398    	++$OUT_NODE;
9399	join("", ${PREFIX}, $title, $EXTN);
9400    } else {
9401    # Remove 0's from the end of $packed_curr_sec_id
9402	$packed_curr_sec_id =~ s/(_0)*$//;
9403	$packed_curr_sec_id =~ s/^\d+$//o; # Top level file
9404	join("",($packed_curr_sec_id ?
9405	    "${PREFIX}$NODE_NAME". ++$OUT_NODE : $sec_name), $EXTN);
9406    }
9407}
9408
9409#RRM: redefine this subroutine, to create customised file-names
9410#     based upon the actual section title.
9411#     The default is empty, so reverts to:  node1, node2, ...
9412#
9413sub custom_title_hook {
9414    local($_)= @_;
9415    "";
9416}
9417
9418
9419sub make_long_title {
9420    local($_)= @_;
9421    local($num_words) = $LONG_TITLES;
9422    #RRM:  scan twice for short words, due to the $4 overlap
9423    #      Cannot use \b , else words break at accented letters
9424    $_ =~ s/(^|\s)\s*($GENERIC_WORDS)(\'|(\s))/$4/ig;
9425    $_ =~ s/(^|\s)\s*($GENERIC_WORDS)(\'|(\s))/$4/ig;
9426    #remove leading numbering, unless that's all there is.
9427    local($sec_num);
9428    if (!(/^\d+(\.\d*)*\s*$/)&&(s/^\s*(\d+(\.\d*)*)\s*/$sec_num=$1;''/e))
9429	{ $num_words-- };
9430    &remove_markers; s/<[^>]*>//g; #remove tags
9431    #revert entities, etc. to TeX-form...
9432    s/([\200-\377])/"\&#".ord($1).";"/eg;
9433    $_ = &revert_to_raw_tex($_);
9434
9435    # get $LONG_TITLES number of words from what remains
9436    $_ = &get_first_words($_, $num_words) if ($num_words);
9437    # ...and cleanup accents, spaces and punctuation
9438    $_ = join('', ($SHOW_SECTION_NUMBERS ? $sec_num : ''), $_);
9439    s/\\\W\{?|\}//g; s/\s/_/g; s/\W/_/g; s/__+/_/g; s/_+$//;
9440    $_;
9441}
9442
9443
9444sub make_first_key {
9445    local($_);
9446    $_ = ('0 ' x keys %section_commands);
9447    s/^0/$THIS_FILE/ if ($MULTIPLE_FILES);
9448    chop;
9449    $_;
9450}
9451
9452# This copies the preamble into the variable $preamble.
9453# It also sets the LaTeX font size, if $FONT_SIZE is set.
9454sub add_preamble_head {
9455    $preamble = join("\n", $preamble, @preamble);
9456    $preamble = &revert_to_raw_tex($preamble);
9457    $preamble = join ("\n", &revert_to_raw_tex(/$preamble_rx/o),
9458				$preamble);
9459    local($savedRS) = $/; undef $/;
9460    # MRO: replaced $* with /m
9461    $preamble =~ /(\\document(style|class))\s*(\[[^]]*\])?\s*\{/sm;
9462    local($before,$after) = ($`.$1, '{'.$');
9463    $/ = $savedRS;
9464    local ($options) = $3;
9465    if ($FONT_SIZE) {
9466	$options =~ s/(1\dpt)\b//;
9467	$options =~ s/(\[|\])//g;
9468	$options = "[$FONT_SIZE".($options ? ",$options" : '').']';
9469	$preamble = join('', $before, $options, $after );
9470	&write_mydb_simple("preamble", $preamble);
9471	@preamble = split(/\n/, $preamble);
9472	$LATEX_FONT_SIZE = $FONT_SIZE;
9473    }
9474    if (($options =~ /(1\dpt)\b/)&&(!$LATEX_FONT_SIZE)) {
9475	$LATEX_FONT_SIZE = $1;
9476    }
9477    #RRM: need to know the font-size before the .aux file is read
9478    $LATEX_FONT_SIZE = '10pt' unless ($LATEX_FONT_SIZE);
9479}
9480
9481# It is necessary to filter some parts of the document back to raw
9482# tex before passing them to latex for processing.
9483sub revert_to_raw_tex {
9484    local($_) = @_;
9485    local($character_map) = "";
9486    if ( $CHARSET && $HTML_VERSION ge "2.1" ) {
9487	$character_map = $CHARSET;
9488	$character_map =~ tr/-/_/; }
9489    while (s/$O\s*\d+\s*$C/\{/o) { s/$&/\}/;}
9490    while (s/$O\s*\d+\s*$C/\{/o) { s/$&/\}/;} #repeat this.
9491    # The same for processed markers ...
9492    while ( s/$OP\s*\d+\s*$CP/\{/o ) { s/$&/\}/; }
9493    while ( s/$OP\s*\d+\s*$CP/\{/o ) { s/$&/\}/;} #repeat this.
9494
9495    s/<BR>/\\\\/g; # restores the \\ from \parbox's
9496
9497    # revert any math-entities
9498    s/\&\w+#(\w+);/\\$1/g;
9499    s/\&limits;/\\limits/g;
9500    s/\\underscore/\\_/g;
9501    s/\\circflex/\\^/g;
9502    s/\\space/\\ /g;
9503    s/;SPMthinsp;/\\,/g;
9504    s/;SPMnegsp;/\\!/g;
9505    s/;SPMsp;/\\:/g;
9506    s/;SPMthicksp;/\\;/g;
9507    s/;SPMgg;/\\gg /g;
9508    s/;SPMll;/\\ll /g;
9509    s/;SPMquot;/"/g;
9510
9511    # revert any super/sub-scripts
9512    s/<SUP>/\^\{/g;
9513    s/<SUB>/\_\{/g;
9514    s/<\/SU(B|P)>/\}/g;
9515
9516
9517#    #revert common character entities  ??
9518#    s/&#92;/\\/g;
9519
9520#    # revert special marks
9521#    s/$percent_mark/\\%/go;
9522##    s/$comment_mark(\d+)\n/%$comments{$1}\n/go;
9523    local($tmp,$tmp2);
9524#    s/$comment_mark(\d+)\n/$tmp=$verbatim{$1};chomp($tmp);$tmp."\n"/ego;
9525    s/$comment_mark(\d+)(\n|$|(\$))/$tmp=$verbatim{$1};$tmp2 = $3;
9526        ($tmp=~m!^\%!s ? '':'%').$tmp.(($tmp=~ m!\n\s*$!s)?'':"\n").$tmp2/sego;
9527    s/${verbatim_mark}tex2html_code(\d+)\#/$verbatim{$1}/go;
9528    s/^($file_mark|$endfile_mark).*\#\n//gmo;
9529    s/$comment_mark(\d*)\s*\n/%\n/go;
9530    s/$dol_mark/\$/go;
9531    s/$caption_mark//go;
9532
9533    # From &pre_process.
9534    # MRO: replaced $* with /m
9535    s/\\\\[ \t]*(\n)?/\\\\$1/gm;
9536
9537    # revert any array-cell delimiters
9538    s/$array_col_mark/\&/g;
9539    s/$array_row_mark/\\\\/g;
9540    s/$array_text_mark/\\text/g;
9541    s/$array_mbox_mark/\\mbox/g;
9542
9543    # Replace any verbatim and image markers ...
9544    &revert_verbatim_marks;
9545    &revert_verb_marks;
9546
9547
9548#    &replace_image_marks;
9549    s/$image_mark\#([^\#]+)\#/&recover_image_code($1)/eg;
9550
9551    # remove artificial environments and commands
9552
9553    s/(\n*)\\(begin|end)(($O|$OP)\d+($C|$CP))tex2html_b(egin)?group\3\n?/
9554	($1? "\n":'')."\\".($6? $2:(($2 =~ m|end|)? 'e':'b'))."group\n"
9555    /gem;
9556    s/\\(begin|end)(\{|(($O|$OP)\d+($C|$CP|\})))(tex2html|verbatim)_code(\}|\3)\n?//gm;
9557
9558    #take care not to concatenate \<cmd> with following letters
9559    local($tmp);
9560    s/(\\\w+)?$tex2html_wrap_rx([^\\\n])?/$tmp=$2;
9561        ((($tmp eq 'end')&&($1)&&!($5)&&($6))? "$1 $6":"$1$5$6")/egs;
9562    undef $tmp;
9563    s/\s*\\newedcommand\s*{/"%\n\\providecommand{\\"/gem;
9564    s/\\newedcommand\s*{/\\providecommand{\\/gom;
9565#    s/(\n*)\\renewedcommand{/($1? "\n":'')."\\renewcommand{\\"/geo;
9566    s/\s*\\providedcommand\s*{/"%\n\\providecommand{\\"/gem;
9567#    s/\\providedcommand{/\\providecommand{\\/go;
9568    s/\\renewedenvironment\s*/\\renewenvironment/gom;
9569    s/\\newedboolean\s*{/\\newboolean{/gom;
9570    s/\\newedcounter\s*{/\\newcounter{/gom;
9571    s/\\newedtheorem\s*{/\\newtheorem{/gom;
9572    s/\\xystar/\\xy\*/gom; # the * has a special meaning in Xy-pic
9573
9574    #fix-up the star'd environment names
9575    s/(\\(begin|end)(($O|$OP)\d+($C|$CP))[^<]*)star\3/$1\*$3/gm;
9576    s/(\\(begin|end)\{[^\}]*)star\}/$1\*\}/gm;
9577    s/\\(begin|end)\{[^\}]*begin(group)\}/\\$1$2/gm;
9578    s/\\(b|e)(egin|end)\{[^\}]*b(group)\}/\\$1$3/gm;
9579
9580    s/(\\(\w+)TeX)/($language_translations{$2}? "\\selectlanguage{$2}": $1)/egom;
9581
9582    if ($PREPROCESS_IMAGES) {
9583      while (/$pre_processor_env_rx/m) {
9584	$done .= $`; $pre_env = $5; $which =$1; $_ = $';
9585        if (($which =~ /begin/)&&($pre_env =~ /indica/)) {
9586	    ($indic, $dum) = &get_next_optional_argument;
9587	    $done .= "\#$indic";
9588        } elsif (($which =~ /begin/)&&($pre_env =~ /itrans/)) {
9589	    ($indic, $dum) = &get_next_optional_argument;
9590	    $done .= "\#$indic";
9591        } elsif (($which =~ /end/)&&($pre_env =~ /indica/)) {
9592	    $done .= '\#NIL';
9593        } elsif (($which =~ /end/)&&($pre_env =~ /itrans/)) {
9594	    $done .= "\#end$indic";
9595	} elsif ($which =~ /begin/) {
9596	    $done .= (($which =~ /end/)? $end_preprocessor{$pre_env}
9597		          : $begin_preprocessor{$pre_env} )
9598	}
9599	$_ = $done . $_;
9600      }
9601    }
9602    s/\\ITRANSinfo\{(\w+)\}\{([^}]*)\}/\#$1=$2/gm if $itrans_loaded;
9603
9604    s/\n{3,}/\n\n/gm; # remove multiple (3+) new-lines
9605    s/^\n+$//gs; # ...especially if that is all there is!
9606    if ($PREAMBLE) {
9607	s/$comment_mark(\d+\n?)?//g;
9608#	$preamble =~ s/\\par\n?/\n/g;
9609	s/\\par\b/\n/g;
9610	s/^\s*$//g; #remove blank lines in the preamble
9611    };
9612
9613    s/($html_specials_inv_rx)/$html_specials_inv{$1}/geo;
9614    # revert entities to TeX code, except if in {rawhtml} environments
9615    if (!($env =~ /rawhtml/)) {
9616        s/$character_entity_rx/( $character_map ?
9617	  eval "\$${character_map}_character_map_inv\{\"$1\"\}" :
9618	    $iso_8859_1_character_map_inv{$1} ||
9619	      $iso_10646_character_map_inv{$1})/geo;
9620        s/$named_entity_rx/( $character_map ?
9621	  eval "\$${character_map}_character_map_inv\{\$${character_map}_character_map{'$1'}}" :
9622	    $iso_8859_1_character_map_inv{$iso_8859_1_character_map{$1}} ||
9623	      $iso_10646_character_map_inv{$iso_10646_character_map{$1}})/geo;
9624
9625    } else {
9626        #RRM: check for invalid named entities in {rawhtml} environments
9627	s/($named_entity_rx)/&write_warnings(
9628	    "An unknown named entity ($1) appears in the source text.") unless (
9629		 $character_map && eval
9630	  "\$${character_map}_character_map_inv\{\$${character_map}_character_map{'$2'}}");
9631		     ";SPM$2;"/ego;
9632    }
9633
9634    #RRM: check for numbered character entity out-of-range
9635    if ($HTML_VERSION < 4.0) {
9636	s/$character_entity_rx/&write_warnings(
9637	    "An invalid character entity ($1) appears in the source text.")
9638	     if ($2 > 255);
9639	$1/ego; }
9640
9641    #RRM: check for invalid named entities outside {rawhtml} environments
9642    # --- these should have been caught already, but check again
9643    s/$named_entity_rx/&write_warnings(
9644	    "An unknown named entity ($1) appears in the source text.") unless (
9645	$character_map && eval
9646	  "\$${character_map}_character_map_inv\{\$${character_map}_character_map{'$1'}}");
9647		     $1/ego;
9648
9649    &revert_to_raw_tex_hook if (defined &revert_to_raw_tex_hook);
9650    $_;
9651}
9652
9653sub next_wrapper {
9654    local($dollar) = @_;
9655    local($_,$id);
9656    $wrap_toggle = (($wrap_toggle eq 'end') ? 'begin' : 'end');
9657    $id = ++$global{'max_id'};
9658    $_ = "\\$wrap_toggle$O$id$C"."tex2html_wrap$O$id$C";
9659    $_ = (($wrap_toggle eq 'end') ? $dollar.$_ : $_.$dollar);
9660    $_;
9661}
9662
9663sub make_wrapper {
9664    &make_any_wrapper($_[0], '', "tex2html_wrap");
9665}
9666
9667sub make_nowrapper {
9668    &make_any_wrapper($_[0], 1, "tex2html_nowrap");
9669}
9670
9671sub make_inline_wrapper {
9672    &make_any_wrapper($_[0], '', "tex2html_wrap_inline");
9673}
9674
9675sub make_deferred_wrapper {
9676    &make_any_wrapper($_[0], 1, "tex2html_deferred");
9677}
9678
9679sub make_nomath_wrapper {
9680    &make_any_wrapper($_[0], '', "tex2html_nomath_inline");
9681}
9682
9683sub make_any_wrapper {
9684    local($toggle,$break,$kind) = @_;
9685    local($max_id) = ++$global{'max_id'};
9686    '\\'. (($toggle) ? 'begin' : 'end')
9687	. "$O$max_id$C"."$kind$O$max_id$C"
9688	. (($toggle || !$break) ? '' : '');
9689}
9690
9691sub get_last_word {
9692    # Returns the last word in multi-line strings
9693    local($_) = @_;
9694    local ($word,$lastbit,$which);
9695#JCL(jcl-tcl)
9696# also remove anchors and other awkward HTML markup
9697#    &extract_pure_text("strict");
9698##    $_ = &purify($_);  ## No. what if it is a verbatim string or image?
9699#
9700#    while (/\s(\S+)\s*$/g) { $word = $lastbit = $1;}
9701
9702    if (!$_ && (defined $keep)) {
9703	# inside mathematics !
9704	$_ = $keep . $pre ;
9705    }
9706    if (!$_ && $ref_before) { $_ = $ref_before; }
9707    elsif (!$_) {
9708	# get it from last thing before the current environment
9709	$which = $#processedE;
9710	$_ = $processedE[$which];
9711    }
9712
9713    while (/((($O|$OP)\d+($C|$CP))[.\n]*\2|\s(\S+))\s*$/g)
9714	{ $word = $lastbit = $1 }
9715    if (($lastbit =~ s/\$\s*$//)||(defined $keep)) {
9716	local($br_idA) = ++$global{'max_id'};
9717	local($br_idB) = ++$global{'max_id'};
9718	$lastbit = join('', "\\begin $O$br_idA${C}tex2html_wrap_inline$O$br_idA$C\$"
9719		, $lastbit, "\$\\end $O$br_idB${C}tex2html_wrap_inline$O$br_idB$C");
9720	$lastbit = &translate_environments($lastbit);
9721	$lastbit = &translate_commands($lastbit);
9722	return ($lastbit);
9723    }
9724    if ($lastbit =~ s/($O|$OP)\d+($C|$CP)//g) { return ($lastbit); }
9725    elsif ($lastbit eq '') { return ($_) }
9726
9727    local($pre_bit);
9728    if ($lastbit =~/>([^>]*)$/) {
9729	$word = $1; $pre_bit = $`.'>';
9730	if ($pre_bit =~ /($verb_mark|$verbstar_mark)$/) {
9731	    $word = $lastbit;
9732	} elsif ($pre_bit =~ /<\w+_mark>$/) {
9733	    $word = $& . $word;
9734	} elsif (!($word)) {
9735	    if ($lastbit =~ s/<([^\/][^>]*)>$//o)
9736	        { $word=$1; $pre_bit = $`; }
9737	    elsif ($lastbit =~ s/>([^<]*)<\/[^>]*>//o)
9738	        { $word=$1; $pre_bit = $`.'>' }
9739	    else { $word = ";SPMnbsp;"; }
9740	}
9741#	if ($pre_bit =~ /<\w+_mark>$/) { $word = $& . $word }
9742     } else { $word = $lastbit };
9743    $word;
9744}
9745
9746#JCL(jcl-tcl)
9747# changed completely
9748#
9749# We take the first real words specified by $min from the string.
9750# Allow for simple HTML constructs like <I>...</I> (but not <H*>
9751# or <P*> and the like), math, or images to remain in the result,
9752# not counting as words.
9753# Take care that eg. <I>...</I> grouping tags are not broken.
9754# This is achieved by lifting the markup, removing superfluous
9755# words, re-inserting the markup, and throw empty markup away.
9756# In later versions images could be modified such that they become
9757# thumbnail sized.
9758#
9759# rawhtml or verbatim environments might introduce lots of awkward
9760# stuff, but yet we leave the according tex2html markers in.
9761#
9762sub get_first_words {
9763    local($_, $min) = @_;
9764    local($words,$i);
9765    local($id,%markup);
9766    #no limit if $min is negative
9767    $min = 1000 if ($min < 0);
9768
9769    &remove_anchors;
9770    #strip unwanted HTML constructs
9771    s/<\/?(P|BR|H)[^>]*>/ /g;
9772    #remove leading white space and \001 characters
9773    s/^\s+|\001//g;
9774    #lift html markup, numbered for recovery
9775    s/(<[^>]*>(#[^#]*#)?)/$markup{++$id}=$1; "\000$id\000"/ge;
9776
9777    foreach (split /\s+|\-{3,}/) {
9778        # count words (incl. HTML markup as part of the word)
9779        ++$i;
9780#	$words .= $_ . " " if (/\000/ || ($i <= $min));
9781	$words .= $_ . " " if ($i <= $min);
9782    }
9783    $_ = $words;
9784    chop;
9785
9786    #re-insert markup
9787    s/\000(\d+)\000/$markup{$1}/g;
9788    # remove empty markup
9789    # it's normalized, because generated by LaTeX2HTML only
9790    s/<([A-Z]+)[^>]*>\s*<\/\1>\s*//g;
9791    $_;
9792}
9793
9794sub replace_word {
9795    # Replaces the LAST occurrence of $old with $new in $str;
9796    local($str, $old, $new) = @_;
9797    substr($str,rindex($str,$old),length($old)) = $new;
9798    $str;
9799}
9800
9801# Returns the recognised sectioning commands as a string of alternatives
9802# for use in regular expressions;
9803sub get_current_sections {
9804    local($_, $key);
9805    foreach $key (keys %section_commands) {
9806	if ($key =~ /star/) {
9807	    $_ = $key . "|" . $_}
9808	else {
9809	    $_ .= "$key" . '[*]?|';
9810	}
9811    }
9812    chop;			# Remove the last "|".
9813    $_;
9814}
9815
9816sub numerically {
9817    local(@x) = split(' ',$a);
9818    local(@y) = split(' ',$b);
9819    local($i, $result);
9820    for($i=0;$i<$#x;$i++) {
9821       last if ($result = ($x[$i] <=> $y[$i]));
9822    }
9823    $result
9824}
9825
9826# Assumes that the files to be sorted are of the form
9827# <NAME><NUMBER>
9828sub file_sort {
9829    local($i,$j) = ($a,$b);
9830    $i =~ s/^[^\d]*(\d+)$/$1/;
9831    $j =~ s/^[^\d]*(\d+)$/$1/;
9832    $i <=> $j
9833}
9834
9835# If a normalized command name exists, return it.
9836sub normalize {
9837    # MRO: modified to use $_[1]
9838    # local($cmd,*after) = @_;
9839    my $cmd =$_[0];
9840    my $ncmd;
9841    # Escaped special LaTeX characters
9842    if ($cmd =~ /^($latex_specials_rx)/) {
9843#	$cmd =~ s/&(.*)$/&amp;$1/o;
9844	$cmd =~ s/&(.*)$/$ampersand_mark$1/o;
9845        $cmd =~ s/%/$percent_mark/o;
9846	$_[1] = join('', $cmd, $_[1]);
9847	$cmd = ""}
9848    elsif ($ncmd = $normalize{$cmd}) {
9849	$ncmd;
9850    }
9851    else {
9852 	$cmd =~ s/[*]$/star/;
9853 	$cmd =~ s/\@/_at_/g;
9854	$cmd;
9855    }
9856}
9857
9858sub normalize_sections {
9859    my $dummy = '';
9860    # MRO: s/$sections_rx/'\\' . &normalize($1.$2,*after) . $4/ge;
9861    s/$sections_rx/'\\' . &normalize($1.$2,$dummy) . $4/ge;
9862}
9863
9864sub embed_image {
9865    my ($url,$name,$external,$altst,$thumbnail,$map,$align,
9866	$usemap,$exscale,$exstr) = @_;
9867    my $imgID = '';
9868    my $urlimg = $url;
9869    my $ismap = $map ? " ISMAP" : '';
9870    print "\nembedding $url for $name, with $altst\n" if ($VERBOSITY > 1);
9871
9872    if (! ($NO_IMAGES || $PS_IMAGES)) {
9873	# for over-scaled GIFs with pre-determined sizes	# RRM 11-9-96
9874        my $size;
9875	if (($width{$name})&&(($exscale)||($EXTRA_IMAGE_SCALE))) {
9876	    $exscale = $EXTRA_IMAGE_SCALE unless ($exscale);
9877	    if ($name =~ /inline|indisplay|entity|equation|math|eqn|makeimage/){
9878		($size, $imgID) = &get_image_size($url, $exscale);
9879	    } else {
9880		($size, $imgID) = &get_image_size($url,'');
9881	    }
9882	} else {
9883	    ($size,$imgID) = &get_image_size($url,'');
9884	}
9885	$image_size{$url} = $size
9886	    unless ((! $size) || ($size eq "WIDTH=\"0\" HEIGHT=\"0\""));
9887	$url = &find_unique($url);
9888    }
9889
9890    $urlimg = $url;
9891    $urlimg =~ s/\.$IMAGE_TYPE$/.html/ if ($map);
9892    if ($exstr =~ s/align\s*=\s*(\"?)(\w+)\1($|\s|,)//io) { $align = $2; }
9893    my $usersize = '';
9894    if ($exstr =~ s/width\s*=\s*(\"?)([^\s,]+)\1($|\s|,)//io) {
9895	my ($pxs,$len) = &convert_length($2);
9896	$usersize = " WIDTH=\"$pxs\"";
9897    }
9898    if ($exstr =~ s/height\s*=\s*(\"?)([^\s,]+)\1($|\s|,)//io) {
9899	my ($pxs,$len) = &convert_length($2);
9900	$usersize .= " HEIGHT=\"$pxs\"";
9901    }
9902
9903    my $border = '';
9904    $border = "\" BORDER=\"0"
9905	unless (($HTML_VERSION < 2.2 )||($exstr =~ /BORDER/i));
9906
9907    my $aalign;
9908    if (($name =~ /figure|table|displaymath\d+|eqnarraystar/)&&(!$align)) {
9909    } elsif ($name =~ /displaymath_/) {
9910	$aalign = "MIDDLE".$border;
9911    } elsif (($name =~ /(equation|eqnarray)($|\d)/)&&(!$align)) {
9912	if ($HTML_VERSION >= 3.2) {
9913	    $aalign =  ($EQN_TAGS eq "L") ? "RIGHT" : "LEFT";
9914	}
9915    } elsif ($name =~ /inline|display|entity|xy|diagram/ && $depth{$name} != 0) {
9916	$aalign = "MIDDLE".$border;
9917    } elsif ($name =~ /inpar/m) {
9918	$aalign = "TOP".$border;
9919    } else {  $aalign = "BOTTOM".$border }
9920
9921    $aalign = "\U$align" if $align;
9922    my $ausemp = $usemap ? "\UUSEMAP=$usemap" : '';
9923
9924    #append any extra valid options
9925    $ismap .= &parse_keyvalues ($exstr, ("IMG")) if ($exstr);
9926
9927    $altst = '' if ($ismap =~ /(^|\s+)ALT\s*=/);
9928    if ($altst) {
9929	if ($altst =~ /\s*ALT="?([^\"]+)"?\s*/io) { $altst=$1 }
9930	$altst =~ s/[<>"&]/'&'.$html_special_entities{$&}.';'/eg;
9931	$altst = "\n ALT=\"$altst\"";
9932    }
9933
9934    my ($extern_image_mark,$imagesize);
9935    if ($thumbnail) {
9936	print "\nmaking thumbnail" if ($VERBOSITY > 1);
9937	if (($image_size{$thumbnail}) = &get_image_size($thumbnail,'')) {
9938	    $thumbnail = &find_unique($thumbnail);
9939	    $imagesize = " ".$image_size{$thumbnail};
9940	    if ($HTML_VERSION < 2.2 ) {
9941		# put the WIDTH/HEIGHT information into the ALT string
9942		# first removing the quotes
9943		my ($noquotes) = $imagesize;
9944		$noquotes =~ s/\"//g;
9945		$altst =~ s/"$/\% $noquotes "/m;
9946		$imagesize = '';
9947	    }
9948	    $extern_image_mark = join('',"<IMG"
9949		, "\n$imagesize"
9950		, (($aalign) ? " ALIGN=\"$aalign\"" : '')
9951		, ("$aalign$imagesize" ? "\n" : '' )
9952		, " SRC=\"$thumbnail\"$altst>");
9953	}
9954	$extern_image_mark =~ s/\s?BORDER="?\d+"?//
9955            unless ($exstr =~ /BORDER/i);
9956    } else {
9957        # MRO: dubious (&extern_image_mark takes only one arg)
9958        $extern_image_mark = &extern_image_mark($IMAGE_TYPE,$altst);
9959    }
9960
9961    my ($anch1,$anch2) = ('','');
9962    my $result;
9963    if ($external || $thumbnail || $EXTERNAL_IMAGES) {
9964	if ( $extern_image_mark ) {
9965	    $result = &make_href_noexpand($urlimg, $name , $extern_image_mark);
9966	    &save_image_map($url, $urlimg, $map, $name, $altst, $ausemp) if $map;
9967	}
9968    } else {
9969	if ($map) {
9970	    $anch1 = "<A HREF=\"$map\">";
9971	    $anch2 = "</A>";
9972	}
9973#	if ($aalign eq "CENTER") {
9974#	    if ($HTML_VERSION eq "2.0") {
9975#	        $anch1 .= "\n<P ALIGN=\"CENTER\">";
9976#	        $anch2 .= "</P>";
9977#	    } else {
9978#	        $anch1 .= "\n<DIV ALIGN=\"CENTER\">";
9979#	        $anch2 .= "</DIV>";
9980#	    }
9981#	}
9982
9983	$imagesize = $image_size{$url};
9984	$imagesize = $usersize if (($usersize)&&($HTML_VERSION > 2.1 ));
9985	if ($HTML_VERSION < 2.2 ) {
9986	    # put the WIDTH/HEIGHT information into the ALT string
9987	    # first removing the quotes
9988	    my ($noquotes) = $imagesize;
9989	    $noquotes =~ s/\"//g;
9990	    $altst =~ s/"$/\% $noquotes "/m;
9991	}
9992
9993	# include a stylesheet entry for each included image
9994	if ($USING_STYLES && $SCALABLE_IMAGES &&(!$imgID)) {
9995	    if ($url =~ /($dd|^)([^$dd$dd]+)\.$IMAGE_TYPE$/) {
9996		my $img_name = $2;
9997		$imgID = $img_name . ($img_name =~ /img/ ? '' : $IMAGE_TYPE);
9998		$img_style{"$imgID"} = ' ' unless $img_style{"$imgID"};
9999		$imgID = join('', ' CLASS="', $imgID, '"') if $imgID;
10000	    }
10001	}
10002
10003	### MEH Add width and height to IMG
10004	### Patched by <hswan@perc.Arco.com>:  Fixed \htmladdimg
10005	if ( $imagesize || $name eq "external image" || $NO_IMAGES || $PS_IMAGES) {
10006	    $imagesize = '' if ($HTML_VERSION < 2.2 );
10007	    if ($border =~ s/^"//) { $border .= '"' };
10008	    $result = join(''
10009		   , "<IMG$imgID"
10010		   , "\n", ($imagesize ? " $imagesize" : '')
10011		   , (($aalign)? " ALIGN=\"$aalign\"" : $border)
10012		   , $ismap );
10013	    if ($ausemp) { $result .= " $ausemp" }
10014	    $result .= "\n" unless (($result =~ /\n *$/m)|| !$imagesize);
10015	    $result .= " SRC=\"$url\"";
10016	    if ($altst) { $result .= $altst }
10017	    $result .= ">";
10018	}
10019    }
10020    join('',$anch1, $result, $anch2);
10021}
10022
10023# MRO: added PNG support
10024sub get_image_size { # clean
10025    my ($imagefile, $scale) = @_;
10026
10027    $scale = '' if ($scale == 1);
10028    my ($imgID,$size) = ('','');
10029    if (open(IMAGE, "<$imagefile")) {
10030        my ($buffer,$magic,$dummy,$width,$height) = ('','','',0,0);
10031	binmode(IMAGE); # not harmful un UNIX
10032        if ($IMAGE_TYPE =~ /gif/) {
10033	    read(IMAGE,$buffer,10);
10034	    ($magic,$width,$height) = unpack('a6vv',$buffer);
10035            # is this image sane?
10036	    unless($magic =~ /^GIF8[79]a$/ && ($width * $height) > 0) {
10037                $width = $height = 0;
10038	    }
10039        }
10040        elsif ($IMAGE_TYPE =~ /png/) {
10041            read(IMAGE,$buffer,24);
10042	    ($magic,$dummy,$width,$height) = unpack('a4a12NN',$buffer);
10043	    unless($magic eq "\x89PNG" && ($width * $height) > 0) {
10044                $width = $height = 0;
10045            }
10046	}
10047	close(IMAGE);
10048
10049	# adjust for non-trivial $scale factor.
10050        my ($img_w,$img_h) = ($width,$height);
10051	if ($scale && ($width * $height) > 0) {
10052            $img_w = int($width / $scale + .5);
10053            $img_h = int($height / $scale + .5);
10054	}
10055	$size = qq{WIDTH="$img_w" HEIGHT="$img_h"};
10056
10057	# allow height/width to be stored in the stylesheet
10058	my ($img_name,$imgID);
10059	if ($SCALABLE_IMAGES && $USING_STYLES) {
10060	    if ($imagefile =~ /(^|[$dd$dd])([^$dd$dd]+)\.(\Q$IMAGE_TYPE\E|old)$/o) {
10061		$img_name = $2;
10062		$imgID = $img_name . ($img_name =~ /img/ ? '' : $IMAGE_TYPE);
10063	    }
10064	    if ($imgID) {
10065		$width = $width/$LATEX_FONT_SIZE/$MATH_SCALE_FACTOR;
10066		$height = 1.8 * $height/$LATEX_FONT_SIZE/$MATH_SCALE_FACTOR;
10067		# How wide is an em in the most likely browser font ?
10068		if ($scale) {
10069		# How high is an ex in the most likely browser font ?
10070		    $width = $width/$scale; $height = $height/$scale;
10071		}
10072		$width = int(100*$width + .5)/100;
10073		$height = int(100*$height + .5)/100;
10074		$img_style{$imgID} = qq(width:${width}em ; height:${height}ex );
10075		#join('','width:',$width,'em ; height:',$height,'ex ');
10076		$imgID = qq{ CLASS="$imgID"};
10077	    }
10078	}
10079    }
10080    ($size, $imgID);
10081}
10082
10083sub find_unique { # clean
10084    my ($image1) = @_;
10085    local($/) = undef; # slurp in complete files
10086
10087    my $imagedata;
10088    if(open(IMG1,"<$image1")) {
10089	binmode(IMG1); # needed with .png under DOS
10090        $imagedata = <IMG1>;
10091        close(IMG1);
10092    } else {
10093        print "\nError: Cannot read '$image1': $!\n"
10094	    unless ($image1 =~ /^\s*$HTTP_start/i);
10095        return $image1;
10096    }
10097
10098    my ($image2,$result);
10099    foreach $image2 (keys(%image_size)) {
10100	if ( $image1 ne $image2 &&
10101	    $image_size{$image1} eq $image_size{$image2} ) {
10102	    if(open(IMG2,$image2)) {
10103		binmode(IMG2); # needed with .png under DOS
10104	        $result = ($imagedata eq <IMG2>);
10105	        close(IMG2);
10106            } else {
10107                print "\nWarning: Cannot read '$image2': $!\n"
10108		    unless ($image2 =~ /^\s*$HTTP_start/i);
10109            }
10110#
10111#  If we've found a match, rename the new image to a temporary one.
10112#  Then try to link the new name to the old image.
10113#  If the link fails, restore the temporary image.
10114#
10115	    if ( $result ) {
10116		my $tmp = "temporary.$IMAGE_TYPE";
10117		L2hos->Unlink($tmp);
10118		L2hos->Rename($image1, $tmp);
10119		if (L2hos->Link($image2, $image1)) {
10120                    L2hos->Unlink($tmp);
10121                } else {
10122                    L2hos->Rename($tmp, $image1);
10123                }
10124		return $image1;
10125	    }
10126	}
10127    }
10128    $image1;
10129}
10130
10131sub save_image_map { # clean
10132    my ($url, $urlimg, $map, $name, $altst, $ausemp) = @_;
10133    unless(open(IMAGE_MAP, ">$urlimg")) {
10134        print "\nError: Cannot write '$urlimg': $!\n";
10135        return;
10136    }
10137    ### HWS  Pass server map unchanged from user
10138    print IMAGE_MAP "<HTML>\n<BODY>\n<A HREF=\"$map\">\n";
10139    print IMAGE_MAP "<IMG\n SRC=\"$url\" ISMAP $ausemp $altst> </A>";
10140    print IMAGE_MAP "</BODY>\n</HTML>\n";
10141    close IMAGE_MAP;
10142}
10143
10144#  Subroutine used mainly to rename an old image file about to recycled.
10145#  But for active image maps, we must edit the auxiliary HTML file to point
10146#     to the newly renames image.
10147sub rename_html {
10148    local ($from, $to) = @_;
10149    local ($from_prefix, $to_prefix, $suffix);
10150    ($from_prefix, $suffix) = split(/\./, $from);
10151    ($to_prefix, $suffix) = split(/\./, $to);
10152    if ($EXTN =~ /$suffix$/) {
10153	if (open(FROM, "<$from") && open(HTMP, ">HTML_tmp")) {
10154	    while (<FROM>) {
10155		s/$from_prefix\.$IMAGE_TYPE/$to_prefix.$IMAGE_TYPE/g;
10156		print HTMP;
10157	    }
10158	    close (FROM);
10159	    close (HTMP);
10160	    L2hos->Rename ("HTML_tmp", $to);
10161	    L2hos->Unlink($from) unless ($from eq $to);
10162	}
10163	else {
10164	    &write_warnings("File $from is missing!\n");
10165	}
10166    }
10167    L2hos->Rename("$from_prefix.old", "$to_prefix.$IMAGE_TYPE");
10168    $to;
10169}
10170
10171sub save_captions_in_file {
10172    local ($type, $_) = @_;
10173    if ($_) {
10174	s/^\n//om;
10175	&replace_markers;
10176	&add_dir_to_href if ($DESTDIR);
10177	if(open(CAPTIONS, ">${PREFIX}$type.pl")) {
10178	    print CAPTIONS $_;
10179	    close (CAPTIONS);
10180        } else {
10181            print "\nError: Cannot write '${PREFIX}$type.pl': $!\n";
10182        }
10183    }
10184}
10185
10186sub add_dir_to_href {
10187    $_ =~ s/'/\\'/g;
10188    $_ =~ s/(<LI><A )(NAME\=\"tex2html\d+\")?\s*(HREF=\")/$1$3\'.\$dir.\'/og;
10189    $_ = join('', "\'", $_, "\'\n");
10190}
10191
10192sub save_array_in_file {
10193    local ($type, $array_name, $append, %array) = @_;
10194    local ($uutxt,$file,$prefix,$suffix,$done_file,$depth,$title);
10195    $prefix = $suffix = "";
10196    my $filespec = ($append ? '>>' : '>') . "${PREFIX}$type.pl";
10197    $prefix = q("$URL/" . )
10198	if ($type eq "labels") && !($array_name eq "external\_latex\_labels");
10199    $suffix = " unless (\$$array_name\{\$key\})"
10200	if (($type =~ /(sections|contents)/)||($array_name eq "printable\_key"));
10201    if ((%array)||($type eq "labels")) {
10202	print "\nSAVE_ARRAY:$array_name in FILE: ${PREFIX}$type.pl"
10203	    if ($VERBOSITY > 1);
10204	unless(open(FILE,$filespec)) {
10205            print "\nError: Cannot write '${PREFIX}$type.pl': $!\n";
10206            return;
10207        }
10208	if (($array_name eq "sub\_index") || ($array_name eq "printable\_key")) {
10209	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
10210	    print FILE "# Printable index-keys from $array_name array.\n\n";
10211	} elsif ($array_name eq "index\_labels") {
10212	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
10213	    print FILE "# labels from $array_name array.\n\n";
10214	} elsif ($array_name eq "index\_segment") {
10215	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
10216	    print FILE "# segment identifier from $array_name array.\n\n";
10217	} elsif ($array_name eq "external\_latex\_labels") {
10218	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
10219	    print FILE "# labels from $array_name array.\n\n";
10220	} else {
10221	    print FILE "# LaTeX2HTML $TEX2HTMLVERSION\n";
10222	    print FILE "# Associate $type original text with physical files.\n\n";
10223	}
10224	while (($uutxt,$file) = each %array) {
10225	    $uutxt =~ s|/|\\/|g;
10226	    $uutxt =~ s|\\\\/|\\/|g;
10227
10228	    if (!($array_name =~/images/)&&($file =~ /</)) {
10229		do { local $_ = $file;
10230		     &replace_markers;
10231		     $file = $_; undef $_;
10232		     $file =~ s/(\G|[^q])[\\\|]\|/$1\\Vert/sg;
10233		     $file =~ s/(\G|[^q])\|/$1\\vert/sg;
10234		};
10235	    }
10236
10237	    local ($nosave);
10238	    if ($MULTIPLE_FILES && $ROOTED &&
10239	    	    $type =~ /(sections|contents)/) {
10240		#RRM: save from $THIS_FILE only
10241	    	if ( $uutxt =~ /^$THIS_FILE /) {
10242		    #RRM: save from $THIS_FILE only
10243	    	    $nosave = ''
10244	    	} else { $nosave = 1 }
10245	    } else {
10246		#RRM: suppress info from other segments
10247	        $nosave = $noresave{$uutxt};
10248	    }
10249
10250	    if (!$nosave && ($file ne ''))  {
10251		print FILE "\n\$key = q/$uutxt/;\n";
10252
10253		$file =~ s/\|/\\\|/g; # RRM:  escape any occurrences of |
10254		$file =~ s/\\\\\|/\\\|/g; # unless already escaped as \|
10255		$file =~ s|\\\\|\\\\\\\\|g;
10256		$file =~ s/(SRC=")($HTTP_start)?/$1.($2 ? '' :"|.\"\$dir\".q|").$2/seg;
10257#
10258#
10259# added code for  $dir  with segmented docs;  RRM  15/3/96
10260#
10261		if ($type eq "contents") {
10262		    ($depth, $done_file) = split($delim, $file, 2 );
10263		    next if ($depth > $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
10264		    print FILE
10265    "\$$array_name\{\$key\} = '$depth$delim'.\"\$dir\".q|$done_file|$suffix; \n";
10266
10267		} elsif ($type eq "sections") {
10268		    ($depth, $done_file) = split($delim, $file, 2 );
10269		    next if ($depth > $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
10270		    print FILE
10271    "\$$array_name\{\$key\} = '$depth$delim'.\"\$dir\".q|$done_file|$suffix; \n";
10272
10273		} elsif ($type eq "internals") {
10274		    print FILE
10275    "\$$array_name\{\$key\} = \"\$dir\".q|$file|$suffix; \n";
10276
10277		} elsif ($array_name eq "sub_index") {
10278		    print FILE
10279    "\$$array_name\{\$key\} .= q|$file|$suffix; \n";
10280
10281		} elsif ($array_name eq "index") {
10282		    local($tmp_file) = '';
10283		    ($depth, $done_file) = split('HREF=\"', $file, 2 );
10284		    if ($done_file) {
10285			while ($done_file) {
10286			    $depth =~ s/\s*$/ / if ($depth);
10287			    $tmp_file .= "q|${depth}HREF=\"|.\"\$dir\".";
10288			    ($depth, $done_file) = split('HREF=\"', $done_file, 2 );
10289			}
10290			print FILE
10291    "\$$array_name\{\$key\} .= ${tmp_file}q|$depth|$suffix; \n";
10292
10293		    } else {
10294			print FILE
10295    "\$$array_name\{\$key\} .= q|$file|$suffix; \n";
10296		    }
10297		} elsif ($array_name eq "printable_key") {
10298		    print FILE
10299    "\$$array_name\{\$key\} = q|$file|$suffix; \n";
10300
10301		} else {
10302		    print FILE
10303    "\$$array_name\{\$key\} = ${prefix}q|$file|$suffix; \n";
10304		}
10305
10306		if ($type =~ /(figure|table|images)/) {} else {
10307		    print FILE "\$noresave\{\$key\} = \"\$nosave\";\n";
10308		}
10309
10310		if ($type eq "sections") {
10311		    ($depth, $done_file, $title) = split($delim, $file);
10312		    print FILE "\$done\{\"\$\{dir\}$done_file\"\} = 1;\n";
10313		}
10314	    }
10315	}
10316	print FILE "\n1;\n\n"  unless  ( $array_name =~ /index/ );
10317	close (FILE);
10318    } else {
10319	print "\nSAVE_FILE:$array_name: ${PREFIX}$type.pl  EMPTY " if ($VERBOSITY > 1);
10320    }
10321}
10322
10323# returns true if $AUTO_NAVIGATION is on and there are more words in $_
10324# than $WORDS_IN_PAGE
10325sub auto_navigation {
10326    # Uses $_;
10327    local(@tmp) = split(/\W*\s+\W*/, $_);
10328    ($AUTO_NAVIGATION && ( (scalar @tmp) > $WORDS_IN_PAGE));
10329}
10330
10331# Returns true if $f1 is newer than $f2
10332sub newer {
10333    ($f1,$f2) = @_;
10334    local(@f1s) = stat($f1);
10335    local(@f2s) = stat($f2);
10336    ($f1s[9] > $f2s[9]);
10337};
10338
10339sub iso_map {
10340    local($char, $kind, $quiet) = @_;
10341    my($character_map,$enc);
10342    local ($this);
10343
10344    if ( $CHARSET && $HTML_VERSION ge "2.1" ) {
10345	# see if it is a character in the charset
10346	$character_map = ((($charset =~ /utf/)&&!$NO_UTF)?
10347			  'iso_10646' : $CHARSET );
10348	$character_map =~ tr/-/_/;
10349	eval "\$enc = \$${character_map}_character_map\{\"$char$kind\"\}";
10350	print "\n no support for $CHARSET: $@ " if ($@);
10351    }
10352    if ($USE_ENTITY_NAMES && $enc) { return(";SPM$char$kind;") }
10353
10354    if ($enc) {
10355	$enc =~ /^\&\#(\d{3});$/;
10356	# maybe convert it to an 8-bit character
10357	if ($NO_UTF && !$USE_UTF && ($1<=255)) { $enc = chr($1) }
10358#	elsif (!$USE_UTF &&($1>127)&&($1<160)) { $enc = chr($1) }
10359	elsif ($character_map !~ /^iso_(8859_1|10646)/) {
10360	# get its latin1 or unicode entity encoding
10361	    $enc = $iso_8859_1_character_map{"$char$kind"}
10362	        ||$iso_8859_1A_character_map{"$char$kind"}
10363	        ||$iso_10646_character_map{"$char$kind"}
10364	}
10365     } else {
10366	# get its latin1 or unicode entity encoding, if available
10367	$enc = $iso_8859_1_character_map{"$char$kind"}
10368	    ||$iso_8859_1A_character_map{"$char$kind"}
10369	    ||$iso_10646_character_map{"$char$kind"};
10370    }
10371
10372    if ($enc) {
10373	$ISOLATIN_CHARS = 1; $enc;
10374    } elsif (!$image_made{"$char$kind"}) {
10375	print "\ncouldn't convert character $char$kind into available encodings"
10376	    if (!quiet &&($VERBOSITY > 1));
10377	&write_warnings(
10378	    "couldn't convert character $char$kind into available encodings"
10379	    . ($ACCENT_IMAGES ? ', using image' : '')) unless ($quiet);
10380	$image_made{"$char$kind"} = 1;
10381	'';
10382    } else {''}
10383}
10384
10385sub titles_language {
10386    local($_) = @_;
10387    local($lang) = $_ . "_titles";
10388    if (defined(&$lang)) { &$lang }
10389    else {
10390	&english_titles;
10391	&write_warnings(
10392	    "\nThere is currently no support for the $tmp language." .
10393	    "\nSee the file $CONFIG_FILE for examples on how to add it\n\n");
10394    }
10395}
10396
10397sub translate_titles {
10398    $toc_title = &translate_commands($toc_title) if ($toc_title =~ /\\/);
10399    $lof_title = &translate_commands($lof_title) if ($lof_title =~ /\\/);
10400    $lot_title = &translate_commands($lot_title) if ($lot_title =~ /\\/);
10401    $idx_title = &translate_commands($idx_title) if ($idx_title =~ /\\/);
10402    $ref_title = &translate_commands($ref_title) if ($ref_title =~ /\\/);
10403    $bib_title = &translate_commands($bib_title) if ($bib_title =~ /\\/);
10404    $abs_title = &translate_commands($abs_title) if ($abs_title =~ /\\/);
10405    $app_title = &translate_commands($app_title) if ($app_title =~ /\\/);
10406    $pre_title = &translate_commands($pre_title) if ($pre_title =~ /\\/);
10407    $foot_title = &translate_commands($foot_title) if ($foot_title =~ /\\/);
10408    $fig_name = &translate_commands($fig_name) if ($fig_name =~ /\\/);
10409    $tab_name = &translate_commands($tab_name) if ($tab_name =~ /\\/);
10410    $prf_name = &translate_commands($prf_name) if ($prf_name =~ /\\/);
10411    $page_name = &translate_commands($page_name) if ($page_name =~ /\\/);
10412    $child_name = &translate_commands($child_name) if ($child_name =~ /\\/);
10413    $info_title = &translate_commands($info_title) if ($info_title =~ /\\/);
10414    $part_name = &translate_commands($part_name) if ($part_name =~ /\\/);
10415    $chapter_name = &translate_commands($chapter_name)
10416	if ($chapter_name =~ /\\/);
10417    $section_name = &translate_commands($section_name)
10418	if ($section_name =~ /\\/);
10419    $subsection_name = &translate_commands($subsection_name)
10420	if ($subsection_name =~ /\\/);
10421    $subsubsection_name = &translate_commands($subsubsection_name)
10422	if ($subsubsection_name =~ /\\/);
10423    $paragraph_name = &translate_commands($paragraph_name)
10424	if ($paragraph_name =~ /\\/);
10425    $see_name = &translate_commands($see_name) if ($see_name =~ /\\/);
10426    $also_name = &translate_commands($also_name) if ($also_name =~ /\\/);
10427    $next_name = &translate_commands($next_name) if ($next_name =~ /\\/);
10428    $prev_name = &translate_commands($prev_name) if ($prev_name =~ /\\/);
10429    $up_name = &translate_commands($up_name) if ($up_name =~ /\\/);
10430    $group_name = &translate_commands($group_name) if ($group_name =~ /\\/);
10431    $encl_name = &translate_commands($encl_name) if ($encl_name =~ /\\/);
10432    $headto_name = &translate_commands($headto_name) if ($headto_name =~ /\\/);
10433    $cc_name = &translate_commands($cc_name) if ($cc_name =~ /\\/);
10434    $default_title = &translate_commands($default_title)
10435	if ($default_title =~ /\\/);
10436}
10437####################### Code Generation Subroutines ############################
10438# This takes a string of commands followed by optional or compulsory
10439# argument markers and generates a subroutine for each command that will
10440# ignore the command and its arguments.
10441# The commands are separated by newlines and have the format:
10442##      <cmd_name>#{}# []# {}# [] etc.
10443# {} marks a compulsory argument and [] an  optional one.
10444sub ignore_commands {
10445    local($_) = @_;
10446    foreach (/.*\n?/g) {
10447	s/\n//g;
10448	# For each line
10449	local($cmd, @args) = split('\s*#\s*',$_);
10450	next unless $cmd;
10451	$cmd =~ s/ //;
10452	++$ignore{$cmd};
10453	local ($body, $code, $thisone) = ("", "");
10454
10455	# alter the pattern here to debug particular commands
10456	$thisone = 1 if ($cmd =~ /let/);
10457
10458	if (@args) {
10459	    print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
10460	    # Replace the argument markers with appropriate patterns
10461	    foreach $arg (@args) {
10462		print "\nARG: $arg" if ($thisone);
10463		if ($arg =~ /\{\}/) {
10464		    $body .= 'local($cmd) = '."\"$cmd\"".";\n";
10465		    $body .= '$args .= &missing_braces'."\n ".'unless (';
10466		    $body .= '(s/$next_pair_pr_rx/$args .= $2;\'\'/eo)'."\n";
10467		    $body .= '  ||(s/$next_pair_rx/$args .= $2;\'\'/eo));'."\n";
10468		    print "\nAFTER:$'" if (($thisone)&&($'));
10469		    $body .= $' if ($');
10470		} elsif ($arg =~ /\[\]/) {
10471		    $body .= '($dummy, $pat) = &get_next_optional_argument;'
10472			. '$args .= $pat;'."\n";
10473		    print "\nAFTER:$'" if (($thisone)&&($'));
10474		    $body .= $' if ($');
10475		} elsif ($arg =~ /^\s*\\/) {
10476		    $body .= '($dummy, $pat) = &get_next_tex_cmd;'
10477			. '$args .= $pat;'."\n";
10478		    print "\nAFTER:$'" if (($thisone)&&($'));
10479		    $body .= $' if ($');
10480		} elsif ($arg =~ /<<\s*([^>]*)[\b\s]*>>/) {
10481		    local($endcmd, $after) = ($1,$');
10482		    $after =~ s/(^\s*|\s*$)//g;
10483		    $endcmd = &escape_rx_chars($endcmd);
10484		    $body .= 'if (/'.$endcmd.'/o) { $args .= $`; $_ = $\' };'."\n";
10485		    print "\nAFTER:$after" if (($thisone)&&($after));
10486		    $body .= "$after" if ($after);
10487		} else {
10488		    print "\nAFTER:$'" if (($thisone)&&($arg));
10489		    $body .= $arg ;
10490		}
10491	    }
10492	    # Generate a new subroutine
10493#	    $code = "sub do_cmd_$cmd {\n".'local($_) = @_;'. join('',@args) .'$_}';
10494	    $code = "sub do_cmd_$cmd {\n"
10495		. 'local($_,$ot) = @_; '
10496		. 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R; '
10497		. 'local($args); '
10498		. "\n" . $body . (($body)? ";\n" : '')
10499		. (($thisone)? "print \"\\n$cmd:\".\$args.\"\\n\";\n" : '')
10500		. (($arg)? $arg : '$_') . "}";
10501	    print STDOUT "\n$code\n" if ($thisone); # for error-checking
10502	    eval ($code); # unless ($thisone);
10503	    print STDERR "\n\n*** sub do_cmd_$cmd failed: $@\n" if ($@);
10504	} else {
10505	    $code = "sub do_cmd_$cmd {\n".'$_[0]}';
10506	    print "\n$code\n" if ($thisone); # for error-checking
10507	    eval ($code); # unless ($thisone);
10508	    print STDERR "\n\n*** sub do_cmd_$cmd failed: $@\n" if ($@);
10509        }
10510    }
10511}
10512
10513
10514sub ignore_numeric_argument {
10515    # Chop this off
10516    #RRM: 2001/11/8: beware of taking too much, when  <num> <num>
10517    local($num) = '(^|width|height|plus|minus)\s*[+-]?[\d\.]+(cm|em|ex|in|pc|pt|mm)?\s*';
10518    do { s/^\s*=?\s*//so; s/^($num)*//so } unless (/^(\s*\<\<\d+\>\>|$)/);
10519}
10520
10521sub get_numeric_argument {
10522    my ($num_rx,$num) = ('','');
10523    # Collect the numeric part
10524    #RRM: 2001/11/8: beware of taking too much, when  <num> <num>
10525    $num_rx = '(^|width|height|plus|minus)\s*[+-]?[\d\.]+(cm|em|ex|in|pc|pt|mm)?\s*';
10526    do { s/^\s*=?\s*//so; s/($num_rx)*/$num=$&;''/soe } unless (/^(\s*\<\<\d+\>\>|$)/);
10527    $num;
10528}
10529
10530sub process_in_latex_helper {
10531    local($ctr,$val,$cmd) = @_;
10532    ($ASCII_MODE ? "[$cmd]" :
10533	&process_in_latex("\\setcounter{$ctr}{$val}\\$cmd"))
10534}
10535
10536sub do_cmd_catcode {
10537    local($_) = @_;
10538    s/^\s*[^=]+(=?\s*\d+\s|\\active)\s?//;
10539    $_;
10540}
10541
10542sub do_cmd_string {
10543    local($_) = @_;
10544    local($tok);
10545    s/^\s*(\\([a-zA-Z]+|.)|[&;]\w+;(#\w+;)?|.)/$tok=$1;''/e;
10546    if ($2) {$tok = "\&#92;$2"};
10547    "$tok".$_
10548}
10549
10550sub do_cmd_boldmath {
10551    local($_) = @_;
10552    $BOLD_MATH = 1;
10553    $_;
10554}
10555
10556sub do_cmd_unboldmath {
10557    local($_) = @_;
10558    $BOLD_MATH = 0;
10559    $_;
10560}
10561
10562sub do_cmd_lq {
10563    local($_) = @_ ;
10564    local($lquote);
10565    # check for double quotes
10566    if (s/^\s*\\lq(\b|$|[^A-Za-z])/$1/) {
10567	$lquote = ((($HTML_VERSION < 4)&&!($charset =~ /utf/)) ? '``'
10568		: &do_leftquotes($_));
10569    } else {
10570	$lquote = ((($HTML_VERSION < 4)&&!($charset =~ /utf/)) ? '`'
10571		: &do_leftquote($_));
10572    }
10573    $lquote . $_;
10574}
10575
10576sub do_leftquote {
10577    # MRO: use $_[0] : local(*_) = @_;
10578    local($quote,$lquo) = ('',($HTML_VERSION<5)? '&#8216;' : ';SPMlsquo;');
10579    # select whole quotation, if \lq matches \rq
10580    if ($_[0] =~ /^(.*)((\\rq\\rq|'')*)(\\rq)/) {
10581	$quote = $1.$2; $_[0] = $';
10582	local($rquo) = &do_rightquote();
10583	&process_quote($lquo,$quote,$rquo);
10584    } else { $lquo; }
10585}
10586
10587sub do_leftquotes {
10588    # MRO: use $_[0] : local(*_) = @_;
10589    local($quote,$lquo) = ('',($HTML_VERSION<5)? '&#8220;' : ';SPMldquo;');
10590    # select whole quotation, if \lq\lq matches \rq\rq or ''
10591    if ($_[0] =~ /^(.*)(\\rq\\rq|'')/) {
10592	$quote = $1; $_[0] = $';
10593	local($rquo) = &do_rightquotes();
10594	&process_quote($lquo,$quote,$rquo);
10595    } else { $lquo; }
10596}
10597
10598# RRM: By default this just concatenates the strings; e.g. ` <quote> '
10599# This can be overridden in a html-version file
10600sub process_quote { join ('', @_) }
10601
10602sub do_cmd_rq {
10603    local($_) = @_ ;
10604    local($rquote);
10605    if ($_ =~ s/^\s*\\rq\b//) {
10606	$rquote = ((($HTML_VERSION < 4)&&!($charset =~ /utf/)) ? "''"
10607		: &do_rightquotes());
10608    } else {
10609	$rquote = ((($HTML_VERSION < 4)&&!($charset =~ /utf/)) ? "'"
10610		: &do_rightquote());
10611    }
10612    $rquote . $_;
10613}
10614
10615sub do_rightquote { (($HTML_VERSION < 5)? '&#8217;' : ';SPMrsquo;') }
10616sub do_rightquotes { (($HTML_VERSION < 5)? '&#8221;' : ';SPMrdquo;') }
10617
10618sub do_cmd_parbox {
10619    local($_) = @_;
10620    local($args, $contents, $dum, $pat);
10621    ($dum,$pat) = &get_next_optional_argument; # discard this
10622    ($dum,$pat) = &get_next_optional_argument; # discard this
10623    ($dum,$pat) = &get_next_optional_argument; # discard this
10624    $args .= $pat if ($pat);
10625    $pat = &missing_braces unless (
10626	(s/$next_pair_pr_rx/$pat=$2;''/eom)
10627	||(s/$next_pair_rx/$pat=$2;''/eom));
10628    $args .= "{".$`.$pat."}";
10629    $contents = &missing_braces unless (
10630	(s/$next_pair_pr_rx/$contents=$2;''/eom)
10631	||(s/$next_pair_rx/$contents=$2;''/eom));
10632    $args .= "{".$`.$contents."}";
10633    if ($NO_PARBOX_IMAGES) {
10634	$contents = join ('', &do_cmd_par(), $contents, '</P>' );
10635    } else {
10636	$contents = &process_math_in_latex('','text',0,"\\parbox$args")
10637	    if ($contents);
10638    }
10639    $contents . $_;
10640}
10641
10642
10643sub do_cmd_mbox {
10644    local($_) = @_;
10645    local($text,$after)=('','');
10646    $text = &missing_braces unless (
10647	(s/$next_pair_pr_rx/$text = $2;''/eo)
10648	||(s/$next_pair_rx/$text = $2;''/eo));
10649    $after = $_;
10650
10651    # incomplete macro replacement
10652    if ($text =~ /(^|[^\\<])#\d/) { return($after) }
10653
10654    if ($text =~ /(tex2html_wrap_inline|\$$OP(\d+)$CP$OP\2$CP\$|\$$O(\d+)$C$O\2$C\$)/) {
10655	if ($text =~
10656	    /$image_mark#([^#]+)#([\.,;:\)\]])?(\001)?([ \t]*\n?)(\001)?/) {
10657	    local($mbefore, $mtext, $mafter) = ($`, $&, $');
10658	    $mbefore = &translate_commands($mbefore) if ($mbefore =~ /\\/);
10659	    $mafter = &translate_commands($mafter) if ($mafter =~ /\\/);
10660	    join('', $mbefore, $mtext, $mafter, $after);
10661	} else {
10662	    join ('', &process_math_in_latex('','','',"\\hbox{$text}"), $after )
10663	}
10664    } else {
10665	$text = &translate_environments($text);
10666	$text = &translate_commands($text);
10667	join('', $text, $after);
10668    }
10669}
10670
10671
10672
10673# *Generates* subroutines to handle each of the declarations
10674# like \em, \quote etc., in case they appear with the begin-end
10675# syntax.
10676sub generate_declaration_subs {
10677    local($key, $val, $pre, $post, $code );
10678    print "\n *** processing declarations ***\n";
10679    while ( ($key, $val) = each %declarations) {
10680	if ($val) {
10681	    ($pre,$post) = ('','');
10682	    $val =~ m|</.*$|;
10683	    do {$pre = $`; $post = $& } unless ($` =~ /^<>/);
10684	    $pre =~ s/"/\\"/g; $post =~ s/"/\\"/g;
10685	    $code = "sub do_env_$key {"
10686#		. 'local($_) = @_;' . "\n"
10687#		. 'push(@$open_tags_R, $key);'. "\n"
10688#		. '$_ = &translate_environments($_);'. "\n"
10689#		. '$_ = &translate_commands($_);'. "\n"
10690#		. "join('',\"$pre\",\"\\n\"," .'$_' .",\"$post\");\n};";
10691		. '&declared_env('.$key.',@_)};';
10692	    eval $code;
10693	    if ($@) {print "\n *** $key ".  $@ };
10694	}
10695    }
10696}
10697
10698# *Generates* subroutines to handle each of the sectioning commands.
10699sub generate_sectioning_subs {
10700    local($key, $val, $cmd, $body);
10701    while ( ($key, $val) = each %standard_section_headings) {
10702	$numbered_section{$key} = 0;
10703	eval "sub do_cmd_$key {"
10704	    . 'local($after,$ot) = @_;'
10705	    . 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'
10706            . '&reset_dependents('. $key . ');'
10707            . '&do_cmd_section_helper('.$val.','.$key.');}';
10708	print STDERR "\n*** sub do_cmd_$key failed:\n$@\n" if ($@);
10709	# Now define the *-form of the same commands. The difference is that the
10710	# $key is not passed as an argument.
10711	eval "sub do_cmd_$key" . "star {"
10712	    . 'local($after,$ot) = @_;'
10713	    . 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'
10714	    . '&do_cmd_section_helper(' . $val . ');}';
10715	print STDERR "\n*** sub do_cmd_${key}star failed:\n$@\n" if ($@);
10716	# Now define the macro  \the$key
10717	&process_commands_wrap_deferred("the$key \# {}\n");
10718###	local($_) = "<<1>>$key<<1>>";
10719	$body = "<<1>>$key<<1>>";
10720	&make_unique($body);
10721	$cmd = "the$key";
10722	eval "sub do_cmd_$cmd {"
10723	    . 'local($after,$ot) = @_;'
10724	    . 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'
10725	    . '&do_cmd_arabic(' . "\"$body\"" . ").\$after;};";
10726	print STDERR "\n*** sub do_cmd_$cmd failed:\n$@\n" if ($@);
10727	$raw_arg_cmds{$cmd} = 1;
10728    }
10729    &addto_dependents('chapter','section');
10730    &addto_dependents('section','subsection');
10731    &addto_dependents('subsection','subsubsection');
10732    &addto_dependents('subsubsection','paragraph');
10733    &addto_dependents('paragraph','subparagraph');
10734}
10735
10736sub addto_dependents {
10737    local($ctr, $dep) = @_;
10738    local($tmp, $depends);
10739    if ($depends = $depends_on{$dep}) {
10740	&remove_dependency($depends, $dep) }
10741    $depends_on{$dep} = $ctr;
10742
10743    $tmp = $dependent{$ctr};
10744    if ($tmp) {
10745	$dependent{$ctr} = join($delim, $tmp, $dep);
10746    } else { $dependent{$ctr} = $dep }
10747}
10748
10749sub remove_dependency {
10750    local($ctr, $dep) = @_;
10751    local(@tmp, $tmp, $dtmp);
10752    print "\nremoving dependency of counter {$dep} from {$ctr}\n";
10753    foreach $dtmp (split($delim, $dependent{$ctr})) {
10754	push(@tmp, $dtmp) unless ($dtmp =~ /$dep/);
10755    }
10756    $dependent{$ctr} = join($delim, @tmp);
10757}
10758
10759
10760# Uses $after which is defined in the caller (the caller is a generated subroutine)
10761# Also uses @curr_sec_id
10762#
10763#JCL(jcl-tcl) (changed almost everything)
10764#
10765sub do_cmd_section_helper {
10766    local($H,$key) = @_;
10767    local($section_number, $titletext, $title_key, @tmp, $align, $dummy);
10768    local($anchors,$pre,$run_title,$_) = ('', "\n", '', $after);
10769    local($open_tags_R) = [];
10770
10771    # if we have a $key the current section is not of the *-form, so we need
10772    # to update the counters.
10773    &do_cmd_stepcounter("${O}0$C$key${O}0$C")
10774#	if ($key && !$making_name);
10775#	if ($key && !($unnumbered_section_commands{$key}) && !$making_name);
10776	if ($key && !($unnumbered_section_commands{$key}));
10777#   $latex_body .= "\\stepcounter{$key}\n" if $key;
10778#   &reset_dependents($key) if ($dependent{$key});
10779
10780    local($br_id);
10781#    if ($USING_STYLES) {
10782#	$txt_style{"H$H.$key"} = " " unless $txt_style{"H$H.$key"};
10783#	$H .= " CLASS=\"$key\";
10784#    };
10785
10786    local ($align, $dummy)=&get_next_optional_argument;
10787    if (($align =~/^(left|right|center)$/i)&&($HTML_VERSION > 2.0)) {
10788        $align = "ALIGN=\"$1\"";
10789    } elsif ($align) {
10790	# data was meant to be a running-head !
10791	$br_id = ++$global{'max_id'};
10792	$run_title = &translate_environments("$O$br_id$C$align$O$br_id$C");
10793	$run_title = &translate_commands($run_title) if ($run_title =~ /\\/);
10794	$run_title =~ s/($O|$OP)\d+($C|$CP)//g;
10795	$align = '';
10796    } else {
10797    }
10798    $titletext = &missing_braces
10799	unless s/$next_pair_rx/$titletext=$2;''/eo;
10800    $br_id = ++$global{'max_id'};
10801    $titletext = &translate_environments("$O$br_id$C$titletext$O$br_id$C");
10802
10803    $title_key = $run_title || $titletext;
10804    $title_key =~ s/$image_mark\#([^\#]+)\#(\\space)?/&purify_caption($1)/e;
10805    # This should reduce to the same information as contained in the .aux file.
10806    $title_key = &sanitize(&simplify($title_key));
10807
10808    # RRM: collect all anchors from \label and \index commands
10809    ($anchors,$titletext) = &extract_anchors($titletext);
10810    local($saved_title) = $titletext;
10811    do {
10812        # to ensure a style ID is not saved and re-used in (mini-)TOCs
10813	local($USING_STYLES) = 0;
10814	$titletext = &translate_environments($titletext);
10815	$titletext = &translate_commands($titletext)
10816	    if ($titletext =~/\\/);
10817    };
10818    # but the style ID can be used for the title on the HTML page
10819    if (!($titletext eq $saved_title)) {
10820	$saved_title = &translate_environments($saved_title);
10821	$saved_title = &translate_commands($saved_title)
10822	    if ($saved_title =~/\\/);
10823	$saved_title = &simplify($saved_title);
10824    }
10825    local($closures) = &close_all_tags();
10826    $saved_title .= $closures;
10827    $title_text .= $closures;
10828
10829    # This is the LaTeX section number read from the $FILE.aux file
10830    @tmp = split(/$;/,$encoded_section_number{$title_key});
10831    $section_number = shift(@tmp);
10832    $section_number = "" if ($section_number eq "-1");
10833    $encoded_section_number{$title_key} = join($;, @tmp)
10834#	unless (defined $title);
10835	unless ($title);
10836
10837    # need to check also &{wrap_cmd_... also, if \renewcommand has been used;
10838    # thanks Bruce Miller
10839    local($thehead,$whead) = ("do_cmd_the$key","wrap_cmd_the$key");
10840#    $thehead = ((defined &$thehead)?
10841#	&translate_commands("\\the$key") : '');
10842    $thehead = ((defined &$thehead)||(defined &$whead)
10843	? &translate_commands("\\the$key") : '');
10844    $thehead .= $SECNUM_PUNCT
10845	if ($SECNUM_PUNCT &&($thehead)&& !($thehead =~ /\./));
10846    $section_number = $thehead if (($thehead)&&($SHOW_SECTION_NUMBERS));
10847
10848    #JKR: Don't prepend whitespace
10849    if ($section_number) {
10850	$titletext = "$section_number " . $titletext;
10851	$saved_title = "$section_number " . $saved_title;
10852	$run_title = "$section_number " . $run_title if $run_title;
10853    }
10854
10855#    $toc_sec_title = $titletext;
10856#    $toc_sec_title = &purify($titletext);
10857    $toc_sec_title = &simplify($titletext);
10858    $titletext = &simplify($titletext);
10859#    $TITLE = &purify($titletext);
10860    local($after) = $_;
10861    do {
10862	local($_) = $titletext; &remove_anchors;
10863	if ($run_title) {
10864	    $TITLE = $run_title;
10865	} elsif ($_) {
10866	    $TITLE = $_
10867	} else { $TITLE = '.' };
10868    };
10869    $global{$key}-- if ($key && $making_name);
10870    return ($TITLE) if (defined $title);
10871
10872    #RRM: no preceding \n when this is the first section-head on the page.
10873    if (! $key || $key < $MAX_SPLIT_DEPTH) { $pre = '' };
10874    if ( defined &make_pre_title) {
10875	$pre = &make_pre_title($saved_title, $H);
10876    }
10877
10878    undef $open_tags_R;
10879    $open_tags_R = [ @save_open_tags ];
10880
10881    join('', $pre, &make_section_heading($saved_title, $H, $align.$anchors)
10882	, $open_all, $_);
10883}
10884
10885sub do_cmd_documentclass {
10886    local($_) = @_;
10887    local ($docclass)=('');
10888    local ($cloptions,$dum)=&get_next_optional_argument;
10889    $docclass = &missing_braces unless (
10890	(s/$next_pair_pr_rx/$docclass = $2;''/eo)
10891	||(s/$next_pair_rx/$docclass = $2;''/eo));
10892    local($rest) = $';
10893    &do_require_package($docclass);
10894    if (! $styles_loaded{$docclass}) {
10895	&no_implementation("document class",$docclass);
10896    } else {
10897	if($cloptions =~ /\S+/) { # are there any options?
10898	    &do_package_options($docclass,$cloptions);
10899	}
10900    }
10901    $rest;
10902}
10903sub do_cmd_documentstyle { &do_cmd_documentclass($_[0]); }
10904
10905sub do_cmd_usepackage {
10906    local($_) = @_;
10907    # RRM:  allow lists of packages and options
10908    local ($package, $packages)=('','');
10909    local ($options,$dum)=&get_next_optional_argument;
10910    $packages = &missing_braces unless (
10911	(s/$next_pair_pr_rx/$packages = $2;''/eo)
10912	||(s/$next_pair_rx/$packages = $2;''/eo));
10913    local($rest) = $_;
10914    # MRO: The files should have already been loaded by
10915    #      TMP_styles, but we better make it sure.
10916    foreach $package (split (',',$packages)) {	# allow multiple packages
10917	$package =~ s/\s|\%|$comment_mark\d*//g; # remove whitespace
10918	$package =~ s/\W/_/g; # replace non-alphanumerics
10919	&do_require_package($package);
10920	if (! $styles_loaded{$package}) {
10921	    &no_implementation("package",$package);
10922	} else {
10923	    if($options =~ /\S+/) { # are there any options?
10924		&do_package_options($package,$options);
10925	    }
10926	}
10927    }
10928    $rest;
10929}
10930
10931
10932sub no_implementation {
10933    local($what,$which)= @_;
10934    print STDERR "\nWarning: No implementation found for $what: $which";
10935}
10936
10937sub do_cmd_RequirePackage {
10938    local($_)= @_;
10939    local($file);
10940    local($options,$dum)=&get_next_optional_argument;
10941    $file = &missing_braces unless (
10942	(s/$next_pair_pr_rx/$file = $2;''/eo)
10943	||(s/$next_pair_rx/$file = $2;''/eo));
10944    local($rest) = $_;
10945    $file =~ s/^[\s\t\n]*//o;
10946    $file =~ s/[\s\t\n]*$//o;
10947    # load the package, unless that has already been done
10948    &do_require_package($file) unless ($styles_loaded{$file});
10949    # process any options
10950    if (! $styles_loaded{$file}) {
10951	    &no_implementation("style",$file);
10952    } else {
10953	# process any options
10954	&do_package_options($file,$options) if ($options);
10955    }
10956    $_ = $rest;
10957    # ignore trailing optional argument
10958    local($date,$dum)=&get_next_optional_argument;
10959    $_;
10960}
10961
10962sub do_cmd_PassOptionsToPackage {
10963    local($_) = @_;
10964    local($options,$file);
10965    $options = &missing_braces unless (
10966        (s/$next_pair_pr_rx/$options = $2;''/eo)
10967        ||(s/$next_pair_rx/$options = $2;''/eo));
10968    $file = &missing_braces unless (
10969        (s/$next_pair_pr_rx/$file = $2;''/eo)
10970        ||(s/$next_pair_rx/$file = $2;''/eo));
10971    $passedOptions{$file} = $options;
10972    $_;
10973}
10974sub do_cmd_PassOptionsToClass{ &do_cmd_PassOptionsToPackage(@_)}
10975
10976sub do_package_options {
10977    local($package,$options)=@_;
10978    local($option);
10979    if ($passedOptions{$package}) { $options = $passedOptions{$package}.'.'.$options };
10980    foreach $option (split (',',$options)) {
10981        $option =~ s/^[\s\t\n]*//o;
10982        $option =~ s/[\s\t\n]*$//o;
10983	$option =~ s/\W/_/g; # replace non-alphanumerics
10984	next unless ($option);
10985        if (!($styles_loaded{$package."_$option"})) {
10986            &do_require_packageoption($package."_$option");
10987            if (!($styles_loaded{$package."_$option"})) {
10988		&no_implementation("option","\`$option\' for \`$package\' package\n");
10989	    }
10990	}
10991    }
10992    $rest;
10993}
10994
10995sub do_class_options {
10996    local($class,$options)=@_;
10997    local($option);
10998    if ($passedOptions{$class}) { $options = $passedOptions{$class}.'.'.$options };
10999    foreach $option (split (',',$options)) {
11000        $option =~ s/^[\s\t\n]*//o;
11001        $option =~ s/[\s\t\n]*$//o;
11002	$option =~ s/\W/_/g; # replace non-alphanumerics
11003	next unless ($option);
11004        &do_require_package($option);
11005        if (!($styles_loaded{$class."_$option"})) {
11006            &do_require_packageoption($class."_$option");
11007            if (!($styles_loaded{$class."_$option"})) {
11008		&no_implementation("option","\`$option\' for document-class \`$class\'\n");
11009	    }
11010	}
11011    }
11012    $rest;
11013}
11014
11015sub do_require_package {
11016    local($file)= @_;
11017    local($dir);
11018    #RRM: make common ps/eps-packages use  epsfig.perl
11019    $file = 'epsfig' if ($file =~ /^(psfig|epsf)$/);
11020
11021    if ($file =~ /^graphicx$/) {
11022	# work-around the CVS repository bug: use graphixx , not graphicx
11023	foreach $dir (split(/$envkey/,$LATEX2HTMLSTYLES)) {
11024	    if (-f "$dir${dd}graphixx.perl") {
11025		$file = 'graphixx';
11026		last;
11027	    }
11028	}
11029    }
11030
11031
11032    if (! $styles_loaded{$file}) {
11033	# look for a file named ${file}.perl
11034	# MRO: use $texfilepath instead of `..'
11035	if ((-f "$texfilepath$dd${file}.perl") && ! $styles_loaded{$file}){
11036	    print STDOUT "\nPackage: loading $texfilepath$dd${file}.perl";
11037	    require("$texfilepath$dd${file}.perl");
11038	    $styles_loaded{$file} = 1;
11039	} else {
11040	    foreach $dir (split(/$envkey/,$LATEX2HTMLSTYLES)) {
11041		if ((-f "$dir$dd${file}.perl") && ! $styles_loaded{$file}){
11042		    print STDOUT "\nPackage: loading $dir$dd${file}.perl";
11043		    require("$dir$dd${file}.perl");
11044	    	    $styles_loaded{$file} = 1;
11045		    last;
11046		}
11047	    }
11048	}
11049    }
11050}
11051
11052sub do_require_extension {
11053    local($file)= @_;
11054    local($dir);
11055
11056    if (! $styles_loaded{$file}) {
11057	# look for a file named ${file}.pl
11058	# MRO: use $texfilepath instead of `..'
11059	if (-f "$texfilepath$dd${file}.pl") {
11060	    print STDOUT "\nExtension: loading $texfilepath$dd${file}.pl";
11061	    require("$texfilepath$dd${file}.pl");
11062	    ++$styles_loaded{$file};
11063	    $NO_UTF = 1 if (($file =~ /latin/)&&($charset =~/utf/));
11064	} else {
11065	    foreach $dir (split(/$envkey/,$LATEX2HTMLVERSIONS)) {
11066		if (-f "$dir$dd${file}.pl"){
11067		    print STDOUT "\nExtension: loading $dir$dd${file}.pl";
11068		    require("$dir$dd${file}.pl");
11069		    ++$styles_loaded{$file};
11070		    $NO_UTF = 1 if (($file =~ /latin/)&&($charset =~/utf/));
11071		    last;
11072		}
11073	    }
11074	}
11075    } else {
11076	if (($file =~ /latin|hebrew/)&&($charset =~/utf|10646/)
11077			&& $loading_extensions) {
11078	    $NO_UTF = 1;
11079	    $USE_UTF = 0;
11080	    print STDOUT "\n\n ...producing $CHARSET output\n";
11081	    $charset = $CHARSET;
11082	}
11083    }
11084}
11085
11086sub do_require_packageoption {
11087    local($option)= @_;
11088    local($do_option);
11089    # first look for a file named ${option}.perl
11090    &do_require_package($option) unless ($styles_loaded{$option});
11091    # next look for a subroutine named  do_$option
11092    $do_option = "do_$option";
11093    if (!($styles_loaded{$option}) && defined(&$do_option)) {
11094	&$do_option();
11095	$styles_loaded{$option} = 1;
11096    }
11097}
11098
11099############################ Environments ################################
11100
11101# This is a dummy environment used to synchronise the expansion
11102# of order-sensitive macros.
11103sub do_env_tex2html_deferred {
11104    local($_) = @_;
11105    local($tex2html_deferred) = 1;
11106    $_ = &process_command($single_cmd_rx,$_);
11107}
11108
11109# catch wrapped commands that need not have been
11110sub do_env_tex2html_nomath_inline {
11111    local($_) = @_;
11112    s/^\s+|\s+$//gs;
11113    my($cmd) = $_;
11114    if ($cmd=~s/^\\([a-zA-Z]+)//s) { $cmd = $1 };
11115    return (&translate_commands($_)) if ($raw_arg_cmds{$cmd}<1);
11116    &process_undefined_environment($env, $id, $_);
11117}
11118
11119# The following list environment subroutines still do not handle
11120# correctly the case where the list counters are modified (e.g. \alph{enumi})
11121# and the cases where user defined bullets are mixed with the default ones.
11122# e.g. \begin{enumerate} \item[(1)] one \item two \end{enumerate} will
11123# not produce the same bullets as in the dvi output.
11124sub do_env_itemize {
11125    local($_) = @_;
11126    $itemize_level++;
11127    #RRM - catch nested lists
11128    &protect_useritems($_);
11129    $_ = &translate_environments($_);
11130
11131    local($bullet,$bulletx)=('&nbsp;','');
11132    SWITCH: {
11133	if ($itemize_level==1) { $bulletx = "\\bullet"; last SWITCH; }
11134	if ($itemize_level==2) { $bulletx = "\\mathbf{\\circ}"; last SWITCH; }
11135	if ($itemize_level==3) { $bulletx = "\\mathbf{\\ast}"; last SWITCH; }
11136    }
11137    $itemize_level--;
11138
11139    if (/\s*$item_description_rx/) {
11140	# Contains user defined optional labels
11141	$bulletx = &do_cmd_mbox("${O}1$C\$$bulletx\$${O}1$C") if $bulletx;
11142	&do_env_description($_, " COMPACT", $bullet.$bulletx)
11143    } else { &list_helper($_,'UL'); }
11144}
11145
11146sub do_env_enumerate {
11147    local($_) = @_;
11148# Reiner Miericke provided the main code; integrated by RRM: 14/1/97
11149# works currently only with 'enumerate' and derived environments
11150# explicit styled labels are computed for each \item
11151# ultimately the environment is done as:  &do_env_description($_, " COMPACT")
11152    ++$enum_level;
11153    local(%enum) = %enum;		# to allow local changes
11154# Reiner: \begin{enumerate}[<standard_label>]
11155    local($standard_label) = "";
11156    local(@label_fields);
11157    local($label_func, $preitems, $enum_type);
11158    local($rlevel) = &froman($enum_level); # e.g. 3 => iii
11159
11160    # \begin{enumerate}[$standard_label]
11161    if (s/^$standard_label_rx//s) {		# multiline on/off ?
11162	# standard label should be used later to modify
11163	# entries in %enum
11164	$standard_label = $1;		# save the standard label
11165#	s/^$standard_label_rx//;	# and cut it off
11166	$standard_label =~ s/([\\\[\]\(\)])/\\$1/g; # protect special chars
11167
11168	# Search for [aAiI1] which is not between a pair of { }
11169	# Other cases like "\theenumi" are not handled
11170	@label_fields = $standard_label =~ /$enum_label_rx/;
11171	if (($standard_label =~ /^[aAiI1]$/)&&(not(/item\s*\[/))) {
11172	    $enum_type = ' TYPE="'.$standard_label.'"';
11173	    $standard_label = '';
11174	} else {
11175	    $label_func = $enum_label_funcs{$label_fields[$#label_fields-1]} .
11176		"(\'enum" . $rlevel . "\')";
11177	    $enum{'theenum' . $rlevel} = "\&$label_func";
11178#	local($thislabel) = "\&$label_func";
11179#	do { local($_) = $thislabel; &make_unique($_);
11180#	     $enum{'theenum' . $rlevel} = $_; };
11181	    $standard_label =
11182		"\"$label_fields[0]\" . eval(\$enum{\"theenum$rlevel\"})"
11183		. ".\"$label_fields[$#label_fields]\"";
11184	    $enum{'labelenum' . $rlevel} = $standard_label;
11185	}
11186    }  elsif (s/^((.|\n)+?)\\item/$preitems=$1;"\\item"/es) {
11187	my $pre_preitems; local($cmd); $label_part;
11188	my $num_styles = join('|', values %enum_label_funcs );
11189	while ($preitems =~
11190	    /\s*\\renew(ed)?command\s*(($O|$OP)\d+($C|$CP))\\?((label|the)enum(\w+))\s*\2/) {
11191	    # this catches one  \renewcommand{\labelenum}{....}
11192	    $pre_preitems .= $`; $preitems = $'; $cmd = $5;
11193	    &missing_braces unless (
11194	        ($preitems=~s/$next_pair_pr_rx\s*/$label_part=$2;''/oe)
11195	        ||($preitems=~s/$next_pair_rx\s*/$label_part=$2;''/oe));
11196	    $cmd =~ s/^label/the/;
11197	    $label_part=~s/\\($num_styles)\s*(($O|$OP)\d+($C|$CP))(\w+)\2/".\&$1\(\'$5\'\)."/g;
11198	    $label_part = '"'.$label_part.'"';
11199	    $enum{$cmd} = $label_part;
11200        }
11201	$standard_label =
11202	    "\"$label_fields[0]\" . eval(\$enum{\"theenum$rlevel\"})"
11203	    . ".\"$label_fields[$#label_fields]\"" if ($cmd);
11204	$_ = $pre_preitems . $preitems . $_ if ($pre_preitems||$preitems);
11205    } else {
11206	@enum_default_type = ('A', '1', 'a', 'i', 'A') unless (@enum_default_type);
11207	$enum_type = $enum_level%4;
11208	$enum_type = ' Type="'.@enum_default_type[$enum_type].'"';
11209    }
11210
11211    # enclose contents of user-defined labels within a group,
11212    # in case of style-change commands, which could bleed outside the label.
11213    &protect_useritems($_);
11214    $_ = &translate_environments($_);	#catch nested lists
11215
11216    local($enum_result);
11217    if (($standard_label)||(/\\item\[/)) {
11218	# split it into items
11219	@items = split(/\\item\b/,$_);
11220	# save anything (non-blank) before the items actually start
11221	$preitems = shift(@items);
11222	$preitems =~ s/^\s*$//;
11223	local($enum_label);
11224	# prepend each item with an item label: \item => \item[<label>]
11225	foreach $item (@items) {
11226#	  unless ( $item =~ /^\s*$/ ) { # first line may be empty
11227	    $enum{"enum" . $rlevel}++;	# increase enumi
11228	    $enum_label = eval("$enum{'labelenum' . $rlevel}");
11229	    # insert a label, removing preceding space, BUT...
11230	    # do NOT handle items with existing labels
11231	    $item =~ s/^\s*//;
11232	    if ($item =~ s/^\s*\[([^]]*)\]//) {
11233		$enum{"enum" . $rlevel}--;
11234		$enum_label = "$1";
11235		local($processed) = ($enum_label =~/$OP/);
11236		$enum_label = join('',($processed ? "<#0#>" : "<<0>>")
11237		    ,$enum_label ,($processed ? "<#0#>" : "<<0>>"))
11238			if ($enum_label =~ /\\/);
11239		if ($processed) { &make_unique_p($enum_label) }
11240		elsif ($enum_label =~ /$O/) { &make_unique($enum_label) };
11241		$item = "[${enum_label}]".$item;
11242	    } else {
11243		local($processed) = ($enum_label =~/$OP/);
11244		$enum_label = join('',($processed ? "<#0#>" : "<<0>>")
11245		    ,$enum_label ,($processed ? "<#0#>" : "<<0>>"))
11246			if ($enum_label =~ /\\/);
11247		if ($processed) { &make_unique_p($enum_label) }
11248		elsif ($enum_label =~ /$O/) { &make_unique($enum_label) };
11249		$item = "[$enum_label\]$item";
11250		$enum_label =~ s/\.$//;
11251	    }
11252	    if ($standard_label) {
11253	        $item =~ s/(\\labelitem$rlevel|$standard_label)/$enum_label/g
11254	    } else {
11255	        $item =~ s/(\\labelitem$rlevel)/$enum_label/g
11256	    }
11257	};
11258	$_ = join("\\item ", $preitems, @items);
11259
11260	# Original, but $enum_result
11261	$enum_result = &do_env_description($_, " COMPACT");
11262    } else {
11263	$enum_result = &list_helper($_, "OL$enum_type", '', '');
11264    }
11265
11266    #clean-up and revert the $enum_level
11267    $enum{"enum" . $rlevel} = 0;
11268    $enum{"enum" . &froman($enum_level)} = 0;
11269    --$enum_level;
11270    $enum_result;
11271}
11272
11273sub do_env_list {
11274    local ($_) = @_;
11275    local ($list_type,$labels,$lengths) = ('UL','','');
11276
11277    $labels = &missing_braces unless	 ( # get the label specifier
11278	(s/$next_pair_pr_rx/$labels=$2;''/e)
11279	||(s/$next_pair_rx/$labels=$2;''/e));
11280
11281    $lengths = &missing_braces unless ( # get the length declarations
11282	(s/$next_pair_pr_rx/$lengths=$2;''/e)
11283	||(s/$next_pair_rx/$lengths=$2;''/e));
11284    # switch to enumerated style if they include a \usecounter.
11285    $list_type = 'OL' if $lengths =~ /\\usecounter/;
11286
11287    /\\item\b/; local($preitems) = $`;
11288	$_ =~ s/^\Q$preamble//s if ($preitems);
11289    $preitems =~s/^\s*|\s*$//g;
11290    if ($preitems) {
11291	$preitems = &translate_environments($preitems);
11292	$preitems = &translate_commands($preitems) if ($preitems =~ /\\/);
11293#	&write_warnings("\nDiscarding: $preitems before 1st item in list")
11294#	    if ($preitems);
11295    }
11296
11297    #RRM - catch nested lists
11298    #RRM unfortunately any uses of the \\usecounter  within \item s
11299    #    may be broken --- sigh.
11300    &protect_useritems($_);
11301    $_ = &translate_environments($_);
11302
11303    if (($list_type =~ /OL/)&&($labels)) {
11304	local($br_ida,$br_idb,$label,$aft);
11305	$br_ida = ++$global{'max_id'};
11306	$lengths =~ s/\\usecounter((($O|$OP)\d+($C|$CP))[^<]+\2)/
11307		&make_nowrapper(1)."\\stepcounter$1".&make_nowrapper(0)/e;
11308	$labels = "$O$br_ida$C$lengths$O$br_ida$C".$labels;
11309
11310#	s/\\item\b\s*([^\[])/do {
11311#		$label = $labels; $aft = $1;
11312#		$br_id = ++$global{'max_id'};
11313#		$label = &translate_environments(
11314#			"$O$br_id$C$label$O$br_id$C");
11315#		join('',"\\item\[" , $label, "\]$aft" );
11316#	    }/eg;
11317#	$labels ='';
11318    }
11319
11320    if (($labels)||(/\\item\[/)) {
11321	$_ = &list_helper($_, 'DL', $labels, $lengths)
11322    } else {
11323	$_ = &list_helper($_, $list_type, '', $lengths)
11324    }
11325    $_;
11326}
11327
11328sub do_env_trivlist {
11329    local($_) = @_;
11330    local($compact,$item_sep,$pre_items) = ' COMPACT';
11331    &protect_useritems($_);
11332
11333    # assume no styles initially for this list
11334    local($close_tags,$reopens) = &close_all_tags();
11335    local($open_tags_R) = [];
11336    local(@save_open_tags) = ();
11337
11338    # include \label anchors from [...] items
11339    s/$item_description_rx\s*($labels_rx8)?\s*/
11340	(($9)? "<A NAME=\"$9\">$1<\/A>" : $1 ) ."\n"/eg;
11341    # remove unwanted space before \item s
11342    s/[ \t]*\\item\b/\\item/g;
11343
11344    local($this_item,$br_id) = ('','');
11345    local($this_sitem,$this_eitem) = ("\n<P>","</P>\n",'');
11346
11347    # assume no sub-lists, else...  why use {trivlist} ?
11348    # extract up to the 1st \item
11349    local(@items) = split(/\\item\b/, $_);
11350    $pre_items = shift @items;
11351    $_ = '';
11352    while (@items) {
11353	$br_id = ++$global{'max_id'};
11354	$this_item = shift @items;
11355	$this_item = &translate_environments(
11356	     "$O$br_id$C".$pre_items.$this_item."$O$br_id$C" );
11357	if ($this_item =~ /\\/) {
11358	    $this_item = &translate_commands($this_item);
11359	    $_ .= join('' , $this_sitem
11360		       , $this_item
11361		       # , $this_eitem
11362		       )
11363	} else { $_ .= $this_sitem . $this_item }
11364    }
11365
11366    $_ = &translate_environments($_);
11367    $_ = &translate_commands($_);
11368
11369    join('' , $close_tags , $_ , $reopens);
11370
11371}
11372
11373# enclose the contents of any user-defined labels within a group,
11374# else any style-change commands may bleed outside the label.
11375sub protect_useritems {
11376    # MRO: use $_[0] instead: local(*_) = @_;
11377    local($preitems, $thisitem);
11378    $_[0] =~ s/^$par_rx\s*//s; # discard any \par before 1st item
11379
11380    # locate \item with optional argument
11381    local($saveRS) = $/; undef $/;
11382    local(@preitems);
11383    # allow one level of nested []
11384    # MRO: Caution! We have a double-wildcarded RX here, this may cause
11385    # trouble. Should be re-coded.
11386    $_[0] =~ s/\\item[\s\r]*(\b(\[(([^\[\]]|\[[^]]*\])*)\])?|[^a-zA-Z\s])/
11387	$thisitem = " $1";
11388	if ($2) {
11389	    $br_id = ++$global{'max_id'};
11390	    $thisitem = '['.$O.$br_id.$C.$3.$O.$br_id.$C.']';
11391	};
11392	"\\item".$thisitem
11393    /egm;
11394
11395    $/ = $saveRS;
11396    $_[0] = join(@preitems, $_[0]);
11397}
11398
11399sub do_env_description {
11400    local($_, $compact, $bullet) = @_;
11401    #RRM - catch nested lists
11402    &protect_useritems($_);
11403    $_ = &translate_environments($_) unless ($bullet);
11404
11405    # MRO: replaced $* with /m
11406    $compact = "" unless $compact;
11407    if ($compact) {		# itemize/enumerate with optional labels
11408	s/\n?$item_description_rx\s*($labels_rx8)?\s*/"\n<\/DD>\n<DT>".
11409	    (($9)? "<A NAME=\"$9\">$1<\/A>" : $1 ) ."<\/DT>\n<DD>"/egm;
11410    } else {
11411	s/\n?$item_description_rx\s*($labels_rx8)?\s*/"\n<\/DD>\n<DT>".
11412	    (($9)? "<A NAME=\"$9\"><STRONG>$1<\/STRONG><\/A>"
11413	     : "<STRONG>$1<\/STRONG>") ."<\/DT>\n<DD>"/egm;
11414    }
11415    # and just in case the description is empty ...
11416#JCL(jcl-del) - $delimiter_rx -> ^$letters
11417    s/\n?\\item\b\s*([^$letters\\]|)\s*/\n<\/DD>\n<DT>$bullet<\/DT>\n<DD>$1/gm;
11418    s/^\s+//m;
11419
11420    $_ = '<DD>'.$_ unless ($_ =~ s/^\s*<\/D(T|D)>\n?//s);
11421    $_ =~ s/\n$//s;
11422    "<DL$compact>\n$_\n</DD>\n</DL>";
11423}
11424
11425sub list_helper {
11426    local($_, $tag, $labels, $lengths) = @_;
11427    local($item_sep,$pre_items,$compact,$etag,$ctag);
11428    $ctag = $tag; $ctag =~ s/^(.*)\s.*$/$1/;
11429
11430    # assume no styles initially for this list
11431    local($close_tags,$reopens) = &close_all_tags();
11432    local($open_tags_R) = [];
11433    local(@save_open_tags) = ();
11434
11435#    #RRM: cannot have anything before the first <LI>
11436#    local($savedRS) = $/; $/='';
11437#    $_ =~ /\\item[\b\r]/s;
11438#    if ($`) {
11439#	$preitems = $`; $_ = $&.$';
11440#	$preitems =~ s/<P( [^>]*)?>//g;
11441#	$close_tags .= "\n".$preitems if $preitems;
11442#    }
11443#    $/ = $savedRS;
11444#
11445
11446    if (($tag =~ /DL/)&&$labels) {
11447	local($label,$aft,$br_id);
11448	s/\\item\b[\s\r]*([^\[])/do {
11449		$label = $labels; $aft = $1;
11450		$br_id = ++$global{'max_id'};
11451		$label = &translate_environments(
11452			"$O$br_id$C$label$O$br_id$C");
11453		join('',"\\item\[" , $label, "\]$aft" );
11454	    }/egm;
11455    }
11456
11457    # This deals with \item[xxx] ...
11458    if ($tag =~ /DL/) {
11459	$compact = ' COMPACT';
11460	# include \label anchors in the <DT> part
11461	# and  $pre_item  tags in the <DD> part:
11462	if ($labels && $lengths) {
11463	    $item_sep = "\n</DD>\n<DT>";
11464	} else {
11465	    $item_sep = ($labels ? "<DT>$labels\n" : '') ."</DT>\n<DD>";
11466	}
11467	$etag = "\n</DD>";
11468	s/$item_description_rx[\r\s]*($labels_rx8)?[\r\s]*/"<DT>" .
11469	    (($9)? "<A NAME=\"$9\">$1<\/A>" : $1 ) ."\n<DD>"/egm;
11470    } else {
11471	$item_sep = "\n</LI>\n<LI>";
11472	$etag = "\n</LI>";
11473    }
11474
11475    # remove unwanted space before \item s
11476    s/[ \t]*\\item\b/\\item/gm;
11477
11478    #JCL(jcl-del) - $delimiter_rx -> ^$letters
11479    s/\n?\\item\b[\r\s]*/$item_sep/egm;
11480
11481    #RRM: cannot have anything before the first <LI>
11482    local($savedRS) = $/; $/='';
11483    $_ =~ /\Q$item_sep\E|<DT>|<LI>/s;
11484    #RRM: ...try putting it before the list-open tag
11485    if ($`) {
11486	$preitems = $`; $_ = $&.$';
11487	$preitems =~ s/<P( [^>]*)?>//gm;
11488	$close_tags .= "\n".$preitems if $preitems;
11489    }
11490    $_ =~ s/^\s*<\/[^>]+>\s*//s;
11491
11492    # remove \n from end of the last item
11493    $_ =~ s/\n$//s;
11494    $/ = $savedRS;
11495
11496    join('' , $close_tags , "\n<$tag$compact>\n"
11497	 , $_ , "$etag\n</$ctag>" , $reopens);
11498}
11499
11500
11501# RRM:  A figure environment generates a picture UNLESS it contains a
11502# {makeimage} sub-environment; in which case it creates a <DIV>
11503# inside which the contents are interpreted as much as is possible.
11504# When there are captions, this modifies $before .
11505sub do_env_figure {
11506    local($_) = @_;
11507    local($halign, $anchors) = ('CENTER','');
11508    local ($border, $attribs );
11509    local($cap_width) = $cap_width;
11510    my ($opt, $dummy) = &get_next_optional_argument;
11511
11512    my $abovedisplay_space = $ABOVE_DISPLAY_SPACE||"<P></P>\n";
11513    my $belowdisplay_space = $BELOW_DISPLAY_SPACE||"<P></P>\n";
11514
11515    ($_,$anchors) = &extract_labels($_); # extract labels
11516    # Try to establish the alignment
11517    if (/^(\[[^\]]*])?\s*\\begin\s*<<\d*>>(\w*)<<\d*>>|\\(\w*)line/) {
11518	$halign = $2.$3;
11519	if ($halign =~ /right/i)  { $halign = 'RIGHT' }
11520	elsif ($halign =~ /left/i) { $halign = 'LEFT' }
11521	elsif ($halign =~ /center/i) { $halign = 'CENTER' }
11522	else { $halign = 'CENTER' }
11523    }
11524
11525    # allow caption-alignment to be variable
11526    local($cap_align);
11527    if ($FIGURE_CAPTION_ALIGN =~ /^(TOP|BOTTOM|LEFT|RIGHT)/i) {
11528	$cap_align = join('', ' ALIGN="', $&, $','"')};
11529
11530    local($cap_env, $captions,$has_minipage) = ('figure','');
11531    if ((/\\begin\s*($O\d+$C)\s*(makeimage|minipage)\s*\1|\\docode/)||
11532	(/\\includegraphics/&&(!/$htmlborder_rx|$htmlborder_pr_rx|\\htmlimage/))){
11533	$has_minipage = ($2 =~ /minipage/sg );
11534	$_ = &translate_environments($_);
11535	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11536	elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11537	do { local($contents) = $_;
11538	    &extract_captions($cap_env); $_ = $contents;
11539	} if (/\\caption/);
11540	$_ = &translate_commands($_);
11541	while ($_ =~ s/(^\s*<BR>\s*|\s*<BR>\s*$)//sg){}; # remove unneeded breaks
11542    } else {
11543	do { local($contents) = $_;
11544	    # MRO: no effect: &extract_captions($cap_env, *cap_width); $_ = $contents;
11545	    &extract_captions($cap_env); $_ = $contents;
11546	} if (/\\caption/);
11547	# Generate picture of the whole environment
11548	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11549	elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11550	$_ = &process_undefined_environment($env, $id, $_);
11551	$_ = &post_latex_do_env_figure($_);
11552	$_ =~ s/\s*<BR>\s*$//g;
11553    }
11554
11555    if ($captions) {
11556        # MRO: replaced $* with /m
11557        $captions =~ s/^\n//m;
11558        $captions =~ s/\n$//m;
11559    }
11560    s/$caption_mark//g;
11561
11562    local($close_tags) = &close_all_tags;
11563    $_ .= $close_tags;
11564
11565    # place all the pieces inside a TABLE, if available
11566    if ($HTML_VERSION > 2.1) {
11567	if ($captions) {
11568	    local($pxs,$len) = &convert_length($cap_width,$MATH_SCALE_FACTOR)
11569		if $cap_width;
11570	    local($table) = "<TABLE$env_id"; # WIDTH="65%"';
11571	    $table .= " WIDTH=\"$pxs\"" if ($pxs);
11572	    if ($border) { $table .= " BORDER=\"$border\"" } # no checking !!
11573	    $table .= ">";
11574	    s/^\s*|\s*$//g;
11575	    join (''
11576		    , $above_display_space
11577		    , "\n<DIV", ($halign ? " ALIGN=\"$halign\"" :'')
11578		    , '>', $anchors , $cap_anchors
11579		    , "\n$table\n<CAPTION", $cap_align, '>'
11580		    , $captions , "</CAPTION>\n<TR><TD>"
11581		    , ($cap_width ? '</TD><TD>' : '')
11582		    , $_ , '</TD>'
11583		    , ($cap_width ? '<TD></TD>' : '')
11584		    , "</TR>\n</TABLE>\n</DIV>\n"
11585		    , $below_display_space
11586	    )
11587	} elsif ($halign) {
11588	    if ($border||($attributes)||$env_id) {
11589		&make_table( $border, $attribs, $anchors, '', $halign, $_ );
11590	    } else {
11591		join (''
11592			, $above_display_space
11593			, "\n<DIV ALIGN=\"$halign\">\n"
11594			, ($anchors ? "\n<P>$anchors</P>" : '')
11595			, $_
11596			, "\n</DIV>"
11597			, $below_display_space
11598		)
11599	    }
11600	} else {
11601	    if ($border||($attributes)||$env_id) {
11602		join (''
11603			, $above_display_space
11604			, "\n<DIV", ($halign ? " ALIGN=\"$halign\"":'')
11605			, '>'
11606			, &make_table( $border, $attribs, $anchors, '', $halign, $_ )
11607			, "\n</DIV><BR"
11608			, (($HTML_VERSION > 3.1)? " CLEAR=\"ALL\"" :'')
11609			, '>'
11610			, $below_display_space
11611		);
11612	    } else {
11613		join (''
11614			, $above_display_space
11615			, "\n<DIV", ($halign ? " ALIGN=\"$halign\"":'')
11616			, ">$anchors\n" , $_ , "\n</DIV><BR"
11617			, (($HTML_VERSION > 3.1)? " CLEAR=\"ALL\"" :'')
11618			, '>'
11619			, $below_display_space
11620		);
11621	    }
11622	}
11623    } else {
11624	# MRO: replaced $* with /m
11625        s/^\n//m;
11626        s/\n$//m;
11627	if ($captions) {
11628	    join('', "\n<BR>\n", (($anchors) ? "$anchors" : '')
11629		, "$cap_anchors\n$captions\n<BR>"
11630		, "\n<P", ($halign ? " ALIGN=\"$halign\"":'')
11631		, '>', $_ , "\n</P>");
11632	} elsif ($halign) {
11633	    join ('', "<BR>\n$anchors", $_ , "\n<BR>" )
11634	} else {
11635	    join('', "<BR>\n<P", ($halign ? " ALIGN=\"$halign\"":'')
11636		, ">$anchors\n" , $_ , "\n</P><BR>");
11637	}
11638    }
11639}
11640
11641sub do_env_figurestar { &do_env_figure(@_) }
11642
11643sub do_env_table {
11644    local($_) = @_;
11645    local($halign, $anchors) = ('','');
11646    local ( $border, $attribs );
11647    &get_next_optional_argument;
11648
11649    # Try to establish the alignment
11650    if (/^(\[[^\]]*])?\s*\\begin\s*<<\d*>>(\w*)<<\d*>>|\\(\w*)line/) {
11651	$halign = $2.$3;
11652	if ($halign =~ /right/i)  { $halign = 'RIGHT' }
11653	elsif ($halign =~ /left/i) { $halign = 'LEFT' }
11654	elsif ($halign =~ /center/i) { $halign = 'CENTER' }
11655	else { $halign = '' }
11656    }
11657
11658    local($cap_env, $captions) = ('table','');
11659
11660    # allow caption-alignment to be variable
11661    local($cap_align);
11662    if ($TABLE_CAPTION_ALIGN =~ /^(TOP|BOTTOM|LEFT|RIGHT)/i) {
11663	$cap_align = join('', ' ALIGN="', $&, $','"')};
11664
11665    if ((/\\(begin|end)\s*($O\d+$C)\s*makeimage\s*\2/)||
11666	    ($HTML_VERSION > 2.0 && (
11667	        /\\begin\s*($O\d+$C)\s*((super)?tabular|longtable)\s*\1/))) {
11668	$_ = &translate_environments($_);
11669	($_,$anchors) = &extract_labels($_); # extract labels
11670	do { local($contents) = $_;
11671	    &extract_captions($cap_env); $_ = $contents;
11672	} if (/\\caption/);
11673	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11674	elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11675	$_ = &translate_commands($_);
11676	while ($_ =~ s/(^\s*<BR>\s*|\s*<BR>\s*$)//g){};
11677    } else {
11678	# Make an image of the whole environment.
11679	($_,$anchors) = &extract_labels($_); # extract labels
11680	do { local($contents) = $_;
11681	    &extract_captions($cap_env); $_ = $contents;
11682	} if (/\\caption/);
11683	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11684	elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11685	$_ = &process_undefined_environment($env, $id, $_);
11686	$_ = &post_latex_do_env_table($_);
11687	$_ =~ s/\s*<BR>\s*$//g;
11688    }
11689
11690    if ($captions) {
11691        # MRO: replaced $* with /m
11692        $captions =~ s/^\n//m;
11693        $captions =~ s/\n$//m;
11694    }
11695    s/$caption_mark//g;
11696
11697    local($close_tags) = &close_all_tags;
11698    $_ .= $close_tags;
11699
11700    #  when $captions remain place all the pieces inside a TABLE, if available
11701    if ($HTML_VERSION > 2.1) {
11702	if ($captions) {
11703	    $halign = 'CENTER' unless $halign;
11704	    local($table) = '<TABLE';
11705	    if ($border) { $table .= " BORDER=\"$border\"" } # no checking !!
11706	    $table .= ">";
11707	    join ('', "<BR><P></P>\n<DIV$env_id ALIGN=\"$halign\">"
11708		, "$anchors$cap_anchors\n$table\n<CAPTION", $cap_align, '>'
11709		, $captions , "</CAPTION>\n<TR><TD>"
11710		, $_ , "</TD></TR>\n</TABLE>\n</DIV><P></P><BR>" )
11711	} elsif ($halign) {
11712	    if ($halign) {
11713		# MRO: replaced $* with /m
11714		s/^\s*(<(P|DIV)$env_id ALIGN=\"\w+[^>]+>)/$1$anchors/m
11715                    if ($anchors);
11716		join('', "<BR>", $_, "\n<BR>" )
11717	    } else {
11718		join ('', "<BR>\n$anchors", $_ , "\n<BR>" )
11719	    }
11720        } else {
11721            join ('', "<BR><P></P>\n<DIV$env_id ALIGN=\"CENTER\">$anchors\n", $_ , "\n</DIV><BR>" )
11722        }
11723    } else {
11724        # MRO: replaced $* with /m
11725        s/^\n//m;
11726        s/\n$//m;
11727        if ($captions) {
11728            join('', "<BR>\n", (($anchors) ? "$anchors" : ''), "$cap_anchors\n$captions\n<BR>"
11729                , "\n<P ALIGN=\"$halign\">", $_, "\n</P><BR>");
11730        } elsif ($halign) {
11731            join ('', "<BR><P></P>\n$anchors", $_ , "\n<P></P>" )
11732        } else {
11733            join('', "<BR>\n<P ALIGN=\"CENTER\">$anchors\n", $_, "\n</P><BR>");
11734        }
11735    }
11736}
11737
11738sub do_env_tablestar { &do_env_table(@_) }
11739
11740# RRM:  A makeimage environment generates a picture of its entire contents,
11741#  UNLESS it is empty.
11742#
11743sub do_env_makeimage {
11744    local($_) = @_;
11745    local($attribs, $border);
11746    s/^\s*//;
11747    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11748    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11749    if (/^((\\begin\s*(($O|$OP)\d+($C|$CP))tex2html_deferred\3)?\\par(\\end(($O|$OP)\d+($C|$CP))tex2html_deferred\7)?\%?\s*\n)+$/s) { return("\n<BR>\n") }
11750    if (/^(\s\%?\n)+$/s) { return() }
11751    $_ = &process_undefined_environment($env, $id, $_);
11752    if (($border||($attributes))&&($HTML_VERSION > 2.1 ))
11753	{ $_ = &make_table( $border, $attribs, '', '', '', $_ ) }
11754    $_ . ((!$_=~/^\s*$/)? "\n<BR>\n" :'');
11755}
11756
11757sub do_env_abstract { &make_abstract($_[0]) }
11758
11759sub do_env_minipage {
11760    local($_) = @_;
11761    &get_next_optional_argument;
11762    local($width);
11763    $width = &missing_braces unless (
11764    	(s/$next_pair_pr_rx/$width=$2;''/e)
11765    	||(s/$next_pair_rx/$width=$2;''/e));
11766    local($pxs,$len) = &convert_length($width,$MATH_SCALE_FACTOR) if $width;
11767    $width = " WIDTH=\"$pxs\"";
11768
11769    local ( %mpfootnotes, $mpfootnotes ) unless ($MINIPAGE);
11770    local ( $border, $attribs, $footfile);
11771    $global{'mpfootnote'} = 0 unless ($MINIPAGE);
11772    $MINIPAGE++;
11773    print "\n *** doing minipage *** " if ($VERBOSITY > 1);
11774    local($open_tags_R) = [ @$open_tags_R ];
11775    local($close_tags,$reopens) = &close_all_tags();
11776    local(@save_open_tags) = @$open_tags_R;
11777
11778    local($minipage_caption) if $cap_env;
11779    if ($cap_env &&($HTML_VERSION>2.1)) {
11780	do {
11781	    local($captions);
11782	    local($contents) = $_;
11783	    &extract_captions($cap_env) if ($_ =~ /\\caption/m);
11784	    $minipage_caption = $captions;
11785	    $_ = $contents;
11786	    undef $contents; undef $captions;
11787	};
11788    }
11789
11790    if (s/^\s*$htmlborder_rx//so) {
11791	$attribs = $2; $border = (($4)? "$4" : 1)
11792    } elsif (s/^\s*$htmlborder_pr_rx//so) {
11793	$attribs = $2; $border = (($4)? "$4" : 1)
11794    }
11795    if (/^\s*\\/) {
11796	local($tmp) = ++$global{'max_id'};
11797	$_ = $O.$tmp.$C.$_.$O.$tmp.$C
11798    }
11799    $_ = &translate_environments($_);
11800    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11801    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11802    $_ = &translate_commands($_);
11803    $MINIPAGE--; $MINIPAGE='' if ($MINIPAGE==0);
11804
11805    $_ .= &balance_tags();
11806    $attribs .= $width unless ($attribs =~ /WIDTH/i);
11807#    if (($border||$attribs)&&$MINIPAGE&&($HTML_VERSION>2.1)) {
11808    if (($border||$attribs||$env_id)&&$MINIPAGE&&($HTML_VERSION>2.1)) {
11809	$_ = &make_table( $border, $attribs, '', '', '', $_ );
11810    } elsif ($MINIPAGE) {
11811	$_ = join ('', '<BR><HR>', $_ , '<BR><HR><BR>' );
11812    } elsif (($border||($attribs)||$minipage_caption)&&($HTML_VERSION > 2.1 )) {
11813	$mpfootnotes = '<DL>'.$mpfootnotes.'</DL>' if $mpfootnotes;
11814	$_ = &make_table( $border, $attribs, '', $mpfootnotes, '', $_ );
11815	$_ = join('','<BR><HR'
11816		, (($HTML_VERSION > 3.0)? ' WIDTH="50\%" ALIGN="CENTER"' : '')
11817		, '>', $_ , '<BR><HR'
11818		, (($HTML_VERSION > 3.0)? ' WIDTH="50\%" ALIGN="CENTER"' : '')
11819		, '><BR>') unless ($border||$attribs||$mpfootnotes);
11820    } else {
11821	$global{'mpfootnote'} = 0;
11822	if ($mpfootnotes) {
11823	    $mpfootnotes = '<DD>'.$mpfootnotes unless ($mpfootnotes =~ /^\s*<D(T|D)>/);
11824	    $_ = join('','<BR><HR>', $_ , '<BR><HR'
11825		, (($HTML_VERSION > 3.0)? ' WIDTH="200" ALIGN="LEFT"' : '')
11826		, '><DL>', $mpfootnotes , '</DL><HR><BR'
11827		, (($HTML_VERSION > 3.0)? ' CLEAR="all"' : '')
11828		, '>' );
11829	} else {
11830	    $_ = join ('', '<BR><HR><P></P>', $_ , '<BR><HR><BR>' );
11831	}
11832    }
11833    join('', $close_tags, $_, $reopens);
11834}
11835
11836if (($HTML_VERSION > 2.1)&&($HTML_VERSION < 4.0)) {
11837    $TABLE_attribs = ",ALIGN,";
11838    $TABLE__ALIGN = ",left,right,center,";
11839    $TABLE_attribs_rx_list = ",CELLPADDING,BORDER,WIDTH,CELLSPACING,";
11840    $TABLE__WIDTH_rx = "\^\\d+%?";
11841    $TABLE__BORDER_rx = $TABLE__CELLSPACING_rx = $TABLE__CELLPADDING_rx = "\^\\d+";
11842}
11843
11844sub make_table {
11845    local($border, $attribs, $anchors, $extra_cell, $halign, $_) = @_;
11846    local($table,$caption,$div,$end,$Tattribs);
11847    $caption = join('',"<CAPTION$cap_align>"
11848	, $minipage_caption
11849	,'</CAPTION>') if ($minipage_caption);
11850    $end = "</TD></TR>\n</TABLE>";
11851    $table = join('', "<TABLE$env_id"
11852	, ((($caption)&&!($attribs =~/WIDTH/i)) ? " WIDTH=\"100\%\"" : '')
11853	, ((($border)&&!($attribs =~/BORDER/i)) ? " BORDER=\"$border\"" : '')
11854	);
11855    if ($attribs) {
11856	if (!($attribs =~ /=/)) {
11857	    $Tattribs = &parse_valuesonly($attribs,"TABLE");
11858	} else {
11859	    $Tattribs = &parse_keyvalues($attribs,"TABLE");
11860	}
11861	$table .= " $Tattribs" if ($Tattribs);
11862    }
11863    print STDOUT "\nTABLE: $table>" if ($VERBOSITY >2 );
11864    $table .= ">".$caption."\n<TR><TD>";
11865    if ($extra_cell) {
11866	local($sep) = "</TD></TR>\n<TR ALIGN=\"LEFT\">\n<TD>";
11867	join ('', $div, $anchors, $table, $_ , $sep, $extra_cell, $end );
11868    } else {
11869	join ('', $div, $anchors, $table, $_ , $end );
11870    }
11871}
11872
11873sub do_cmd_etalchar {
11874    local($_) = @_;
11875    my $etalchar;
11876    $etalchar = &missing_braces unless (
11877	(s/$next_pair_pr_rx/$etalchar = $2;''/eo)
11878	||(s/$next_pair_rx/$etalchar = $2;''/eo));
11879    $etalchar = &translate_commands($etalchar) if ($etalchar =~ /\\/);
11880    if ($HTML_VERSION < 3.0) {
11881	$etalchar = &process_in_latex("\$^\{$etalchar\}\$");
11882    } else {
11883	$etalchar = '<SUP>'.$etalchar.'</SUP>';
11884    }
11885    $etalchar . $_
11886}
11887
11888sub do_env_thebibliography {
11889    # Sets $citefile and $citations defined in translate
11890    local($_) = @_;
11891    $bibitem_counter = 0;
11892    $citefile = $CURRENT_FILE;
11893    $citefiles{$bbl_nr} = $citefile;
11894    local($dummy,$title);
11895    $dummy = &missing_braces unless (
11896	(s/$next_pair_pr_rx/$dummy=$2;''/e)
11897	||(s/$next_pair_rx/$dummy=$2;''/e));
11898    # MRO: replaced $* with /m
11899    s/^\s*$//gm; # Remove empty lines (otherwise will have paragraphs!)
11900    s/^\s*//m;
11901
11902    # Replace non-breaking spaces, particularly in author names.
11903#    s/([^\\])~/$1 /g; # Replace non-breaking spaces.
11904
11905    $_ = &translate_environments($_);
11906    $_ = &translate_commands($_);
11907
11908    # RRM: collect all anchors from initial \label and \index commands
11909    local($anchors) = &extract_anchors('',1);
11910    $_ = '<DD>'.$_ unless ($_ =~ /^\s*<D(T|D)>/);
11911    $citations = join('',"<DL COMPACT>", $_, "</DL>");
11912    $citations{$bbl_nr} = $citations;
11913    local($br_id);
11914    if ((defined &do_cmd_bibname)||$new_command{'bibname'}) {
11915	$br_id=++$global{'max_id'};
11916	$title = &translate_environments("$O$br_id$C\\bibname$O$br_id$C");
11917    } else { $title = $bib_title }
11918    if (! $title ) {
11919	if ((defined &do_cmd_refname)||$new_command{'refname'}) {
11920	    $br_id=++$global{'max_id'};
11921	    $title = &translate_environments("$O$br_id$C\\refname$O$br_id$C");
11922	} else { $title = $ref_name }
11923    }
11924    local($closures,$reopens) = &preserve_open_tags();
11925    $toc_sec_title = $title ;
11926    local $bib_head = $section_headings{'bibliography'};
11927    $_ = join('', $closures
11928	    , &make_section_heading($title, $bib_head, $anchors)
11929	    , "$bbl_mark#$bbl_nr#" , $reopens );
11930    $bbl_nr++ if $bbl_cnt > 1;
11931    $_ =~ s/;SPMnbsp;/ /g;  # replace non-breaking spaces with real ones
11932    $_;
11933}
11934
11935# IGNORE - We construct our own index
11936sub do_env_theindex { "" }
11937
11938# This is defined in html.sty
11939sub do_env_comment { "" }
11940
11941
11942sub do_env_equation{
11943    local($_)=@_;
11944    local($attribs, $border, $no_num);
11945    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11946    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11947    if (/\\nonumber/) {
11948	$no_num = 1;
11949	$_ = &process_undefined_environment($env,$id,$_);
11950    } else {
11951	$latex_body .= join('', "\n\\setcounter{equation}{"
11952			, $global{'eqn_number'}, "}\n");
11953
11954	#include equation-number into the key, with HTML 2.0
11955#	$_ = join("\n", "%EQNO:".$global{'eqn_number'}, $_)
11956	$_ .= "%EQNO:".$global{'eqn_number'}."\n" if ($HTML_VERSION < 2.2);
11957
11958	$_ = &process_undefined_environment($env,$id,$_);
11959	$global{'eqn_number'}++;
11960	local($save) = $_;
11961	$_ = join('', $save, &post_latex_do_env_equation($eqno_prefix));
11962    }
11963    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) {
11964	join('',"<BR>\n<DIV$env_id ALIGN=\"CENTER\">\n"
11965	    , &make_table( $border, $attribs, '', '', '', $_ )
11966	    , "\n<BR CLEAR=\"ALL\">");
11967    } elsif ($HTML_VERSION < 2.2 ) {
11968	join('', "\n<P>", $_ , "\n<BR></P>" )
11969    } elsif ($HTML_VERSION > 2.1 ) {
11970	join('', "\n<P ALIGN="
11971	    , ((!$no_num &&($EQN_TAGS =~ /L/))?
11972		'"LEFT"':($no_num ?'"CENTER"':'"RIGHT"'))
11973	    , '>', $_ , "\n<BR></P>" )
11974    } else { $_ }
11975}
11976
11977sub do_env_eqnarray{
11978    local($_)=@_;
11979    local($attribs, $border, $no_num);
11980    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11981    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
11982    local($contents) = $_;
11983#    $_ = join("\n", "%EQNO:".$global{'eqn_number'}, $_)
11984#	if ($HTML_VERSION < 3.2);  #include equation-number into the key.
11985    $_ .= "%EQNO:".$global{'eqn_number'}."\n" if ($HTML_VERSION < 2.2);
11986    $_ = &process_undefined_environment($env,$id,$_);
11987    $_ .= &post_latex_do_env_eqnarray($eqno_prefix,$contents);
11988    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) {
11989	join('',"<BR>\n<DIV ALIGN=\"CENTER\">\n"
11990            , &make_table( $border, $attribs, '', '', '', $_ )
11991	    , "\n<BR CLEAR=\"ALL\">");
11992    } elsif ($HTML_VERSION < 2.2 ) {
11993	join('', "\n<P>", $_ , "\n<BR></P>" )
11994    } elsif ($HTML_VERSION > 3.1 ) {
11995	join('',"<BR>\n<DIV ALIGN=\"CENTER\">\n", $_
11996	     , "\n</DIV><BR CLEAR=\"ALL\">" );
11997    } else {
11998	join('', "\n<P ALIGN="
11999	     , (($EQN_TAGS =~ /L/)? '"LEFT"' : '"RIGHT"')
12000	     , '>' , $_ , "\n<BR></P>" )
12001    }
12002}
12003
12004#RRM: these are needed with later versions, when {eqnarray}
12005#  environments are split into <TABLE> cells.
12006
12007sub protect_array_envs {
12008    local($_) = @_;
12009    local($cnt, $arraybit, $thisbit, $which) = (0,'','','');
12010    # MRO: replaced $* with /m
12011    while (/\\(begin|end)\s*(<(<|#)\d+(#|>)>)($sub_array_env_rx)(\*|star)?\2/m ) {
12012        $thisbit = $` . $&; $_ = $'; $which = $1;
12013        do {
12014            # mark rows/columns in nested arrays
12015            $thisbit =~ s/;SPMamp;/$array_col_mark/g;
12016            $thisbit =~ s/\\\\/$array_row_mark/g;
12017            $thisbit =~ s/\\text/$array_text_mark/g;
12018            $thisbit =~ s/\\mbox/$array_mbox_mark/g;
12019        } if ($cnt > 0);
12020        $arraybit .= $thisbit;
12021        if ($which =~ /begin/) {$cnt++} else {$cnt--};
12022    }
12023    $_ = $arraybit . $_;
12024
12025    local($presub,$thisstack) = '';
12026    for (;;) {
12027      # find \\s needing protection within \substack commands
12028      # a while-loop is simpler syntax, but uses longer strings
12029      if ( /(\\substack\s*(<(<|#)\d+(#|>)>)(.|\n)*)\\\\((.|\n)*\2)/m ) {
12030        $presub .= $`; $thisstack =$1.${array_row_mark}.$6; $_ = $';
12031        # convert all \\s in the \substack
12032        $thisstack =~ s/\\\\/${array_row_mark}/og;
12033        $presub .= $thisstack;
12034        } else { last }
12035    }
12036    $_ = $presub . $_ if ($presub);
12037    $_;
12038}
12039
12040sub revert_array_envs {
12041    local($array_contents) = @_;
12042    $array_contents =~ s/$array_col_mark/$html_specials{'&'}/go;
12043    $array_contents =~ s/$array_row_mark/\\\\/go;
12044    $array_contents =~ s/$array_text_mark/\\text/go;
12045    $array_contents =~ s/$array_mbox_mark/\\mbox/go;
12046    $array_contents;
12047}
12048
12049
12050
12051sub do_env_tabbing {
12052    local($_) = @_;
12053    local($attribs, $border);
12054    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
12055    elsif (s/$htmlborder_pr_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
12056    $_ = &tabbing_helper($_);
12057    if (/$image_mark/) {
12058	local($tab_warning) =
12059	   "*** Images are not strictly valid within HTML <pre> tags\n"
12060	   . "Please change your use of {tabbing} to a {tabular} environment.\n\n";
12061	   &write_warnings("\n".$tab_warning);
12062	   print "\n\n **** invalid tabbing environment ***\n";
12063	   print $tab_warning;
12064    }
12065    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) {
12066	join('',"<BR>\n<DIV$env_id ALIGN=\"CENTER\">\n"
12067            , &make_table( $border, $attribs, '', '', '', $_ )
12068	    , "\n</DIV><BR CLEAR=\"ALL\">");
12069    } else { $_ }
12070}
12071
12072sub tabbing_helper {
12073    local($_) = @_;
12074    s/\\=\s*//go;  # cannot alter the tab-stops
12075    s/\t/ /g;      # convert any tabs to spaces
12076    # MRO: replaced $* with /m
12077    s/(^|\n)[^\n]*\\kill *\n/\n/gm;
12078    s/( )? *\n/$1/gm; # retain at most 1 space for a \n
12079    # replace \\ by \n ... , ignoring any trailing space
12080#    s/\\\\ */\n/gm;
12081    # ...but make sure successive \\ do not generate a <P> tag
12082#    s/\n( *)?\n/\n&nbsp;\n/gm;
12083    s/\\\&gt;//go;
12084    s/(^| *([^\\]))\\[>]/$2\t\t/go;
12085    s/([^\\])\\>/$1\t\t/go;
12086    s/\n$//; s/^\n//;           # strip off leading/trailing \n
12087    local($inside_tabbing) = 1;
12088    $_ = &translate_commands(&translate_environments($_));
12089    "<PRE><TT>\n$_\n</TT></PRE>";
12090}
12091
12092################# Post Processing Latex Generated Images ################
12093
12094# A subroutine of the form post_latex_do_env_<ENV> can be used to
12095# format images that have come back from latex
12096
12097# Do nothing (avoid the paragraph breaks)
12098sub post_latex_do_env_figure { $_[0] }
12099sub post_latex_do_env_figurestar { &post_latex_do_env_figure(@_) }
12100
12101sub post_latex_do_env_table { $_[0] }
12102sub post_latex_do_env_tablestar { &post_latex_do_env_table(@_) }
12103
12104sub post_latex_do_env_equation {
12105    local($prefix) = @_;
12106    $global{'eqn_number'}+=1;
12107    # include equation number at the side of the image -- HTML 3.2
12108    if ($HTML_VERSION >= 3.2){
12109	join('',"<P ALIGN=\"" , (($EQN_TAGS eq "L") ? "left" : "right")
12110		, "\">$EQNO_START" , $prefix
12111		, &translate_commands('\theequation')
12112		, "$EQNO_END</P>\n<BR CLEAR=\"all\">" );
12113    # </P> creates unwanted space in some browsers, but others need it.
12114    } else { "" }
12115}
12116
12117sub do_cmd_theequation {
12118    if ($USING_STYLES) {
12119	$txt_style{'eqn-number'} = " " unless ($txt_style{'eqn-number'});
12120	join('', "<SPAN CLASS=\"eqn-number\">"
12121		,&get_counter_value('eqn_number'),"</SPAN>", $_[0]);
12122    } else { join('',&get_counter_value('eqn_number'), $_[0]); }
12123}
12124
12125sub post_latex_do_env_eqnarray {
12126    local($prefix,$body) = @_;
12127    local($num_string,$line,@lines) = '';
12128    local($side) = (($EQN_TAGS eq "L") ? "\"left\"" : "\"right\"" );
12129    # MRO: replaced $* with /m
12130    @lines = split(/\\\\\\\\/m, $body);
12131    $line = pop(@lines);
12132    if (!($line=~/^\s*$/)&&!($line =~/\\nonumber/)) {
12133	$global{'eqn_number'}++;
12134	$num_string .= join('', "<BR><BR>\n" , $EQNO_START , $prefix
12135	    , &translate_commands('\theequation')
12136	    , $EQNO_END);
12137    }
12138    foreach $line (@lines) {
12139	next if ($line=~/^\s*$/);
12140	$num_string .= "\n<BR>". (($MATH_SCALE_FACTOR > 1.3)? '<BR>' : '')
12141	                . "<BR CLEAR=$side>";
12142	if (!($line =~/\\(nonumber|(no)?tag)/)) {
12143	    $global{'eqn_number'}+=1;
12144	    $num_string .= join('', $EQNO_START , $prefix
12145		, &translate_commands('\theequation')
12146		, $EQNO_END);
12147	 }
12148    }
12149    # include equation numbers at the side of the image -- HTML 3.2
12150    if ($HTML_VERSION >= 3.2){
12151	"<P ALIGN=\"" . (($EQN_TAGS eq "L") ? "left" : "right")
12152	    . "\">" . (($DISP_SCALE_FACTOR >= 1.2 ) ? '<BIG>' : '')
12153	    . ${num_string}
12154	    . (($DISP_SCALE_FACTOR >= 1.2 ) ? '</BIG>' : '')
12155	    . "</P>\n<BR CLEAR=\"all\">"
12156    # </P> creates unwanted space in some browsers, but others need it.
12157    } else { "" };
12158}
12159
12160sub post_latex_do_env_eqnarraystar {
12161    local($_) = @_;
12162    if (($HTML_VERSION >= 3.2)&&(!$NO_SIMPLE_MATH)){
12163	join('', "<BR>\n<DIV ALIGN=\"CENTER\">\n"
12164	    , $_ , "\n<BR CLEAR=\"ALL\">\n<P>");
12165    } elsif (($HTML_VERSION >= 2.2)&&(!$NO_SIMPLE_MATH)) {
12166	join('', "\n<BR><P ALIGN=\"CENTER\">\n", $_ , "\n<BR></P>\n<P>");
12167    } else {
12168	join('', "\n<BR><P>\n", $_ , "\n<BR></P>\n<P>");
12169    }
12170}
12171
12172############################ Grouping ###################################
12173
12174sub do_cmd_begingroup { $latex_body .= "\n\\begingroup\n"; $_[0] }
12175sub do_cmd_endgroup { $latex_body .= "\\endgroup\n\n"; $_[0] }
12176sub do_cmd_bgroup { $latex_body .= "\n\\bgroup\n"; $_[0] }
12177sub do_cmd_egroup { $latex_body .= "\\egroup\n\n"; $_[0] }
12178
12179sub do_env_tex2html_begingroup {
12180    local($_) = @_;
12181    $latex_body .= "\\begingroup ";
12182    $_ = &translate_environments($_);
12183    $_ = &translate_commands($_);
12184    $latex_body .= "\\endgroup\n";
12185    $_;
12186}
12187
12188sub do_env_tex2html_bgroup {
12189    local($_) = @_;
12190    $latex_body .= "\\bgroup ";
12191    $_ = &translate_environments($_);
12192    $_ = &translate_commands($_);
12193    $latex_body .= "\\egroup\n";
12194    $_;
12195}
12196
12197
12198############################ Commands ###################################
12199
12200# Capitalizes what follows the \sc declaration
12201# *** POTENTIAL ERROR ****
12202# (This is NOT the correct meaning of \sc in the cases when it
12203# is followed by another declaration (e.g. \em).
12204# The scope of \sc should be limited to the next occurence of a
12205# declaration.
12206#sub do_cmd_sc {
12207#    local($_) = @_;
12208#    local(@words) = split(" ");
12209# Capitalize the words which are not commands and do not contain any markers
12210#   grep (do {tr/a-z/A-Z/ unless /(^\\)|(tex2html)/}, @words);
12211#    grep (do {s/([a-z]+)/<small>\U$1\E<\/small>/g unless /(^\\)|(tex2html)/}, @words);
12212#    join(" ", @words);
12213#}
12214sub do_cmd_sc { &process_smallcaps(@_) }
12215sub do_cmd_scshape { &do_cmd_sc(@_) }
12216
12217# This is supposed to put the font back into roman.
12218# Since there is no HTML equivalent for reverting
12219# to roman we keep track of the open font tags in
12220# the current context and close them.
12221# *** POTENTIAL ERROR ****#
12222# This will produce incorrect results in the exceptional
12223# case where \rm is followed by another context
12224# containing font tags of the type we are trying to close
12225# e.g. {a \bf b \rm c {\bf d} e} will produce
12226#       a <b> b </b> c   <b> d   e</b>
12227# i.e. it should move closing tags from the end
12228sub do_cmd_rm { # clean
12229    my ($str, $ot) = @_;
12230    $ot = $open_tags_R unless(defined $ot);
12231    return("<\#rm\#>".$str) if ($inside_tabular);
12232
12233    my ($size,$color,$tags);
12234    while (@$ot) {
12235	my $next = pop (@$ot);
12236	print STDOUT "\n</$next>" if $VERBOSITY > 2;
12237	if ($next =~ /$sizechange_rx/) {
12238	    $size = $next unless ($size);
12239	}
12240#	if ($next =~ /$colorchange_rx/) {
12241#	    $color = $next unless ($color);
12242#	}
12243	$declarations{$next} =~ m|</.*$|;
12244	$tags .= $& unless ($` =~ /^<>/);
12245    }
12246    if ($size) {
12247	$declarations{$size} =~ m|</.*$|;
12248	$tags .= $` unless ($` =~ /^<>/);
12249	push (@$ot,$size);
12250	print STDOUT "\n<$size>" if $VERBOSITY > 2;
12251    }
12252    $tags.$str;
12253}
12254
12255sub do_cmd_rmfamily{ &do_cmd_rm(@_) }
12256
12257sub do_cmd_textrm {
12258    local($_) = @_;
12259    local($text,$br_id)=('','0');
12260    $text = &missing_braces unless (
12261	(s/$next_pair_pr_rx/$text=$2;$br_id=$1;''/eo)
12262	||(s/$next_pair_rx/$text=$2;$br_id=$1;''/eo));
12263    join ('' ,
12264	  &translate_environments("$O$br_id$C\\rm $text$O$br_id$C")
12265	  , $_ );
12266}
12267
12268sub do_cmd_emph {
12269    local($_) = @_;
12270    local($ifstyle,$join_tags) = ('',join(',',@$open_tags_R));
12271    $join_tags =~ s/(^|,)(text)?(it|rm|normalfont)/$if_style=$3;''/eg;
12272    if ($if_style =~ /it/) {
12273	($ifstyle,$join_tags) = ('',join(',',@$open_tags_R));
12274	$join_tags =~ s/(^|,)(text)?(bf|rm|normalfont)/$if_style=$3;''/eg;
12275	if ($if_style =~ /bf/) { &do_cmd_textrm(@_) }
12276	else { &do_cmd_textbf(@_) }
12277    } else { &do_cmd_textit(@_) }
12278}
12279
12280#RRM: These cope with declared commands for which one cannot
12281#     simply open a HTML single tag.
12282#     The do_cmd_... gets found before the $declaration .
12283
12284sub do_cmd_upshape{&declared_env('upshape',$_[0],$tex2html_deferred)}
12285sub do_cmd_mdseries{&declared_env('mdseries',$_[0],$tex2html_deferred)}
12286sub do_cmd_normalfont{&declared_env('normalfont',$_[0],$tex2html_deferred)}
12287
12288
12289# This is supposed to put the font back into normalsize.
12290# Since there is no HTML equivalent for reverting
12291# to normalsize we keep track of the open size tags in
12292# the current context and close them.
12293sub do_cmd_normalsize { # clean
12294    my ($str, $ot) = @_;
12295    $ot = $open_tags_R unless(defined $ot);
12296
12297    my ($font,$fontwt,$closures,$reopens,@tags);
12298
12299    while (@$ot) {
12300	my $next = pop @$ot;
12301	$declarations{$next} =~ m|</.*$|;
12302	my ($pre,$post) = ($`,$&);
12303	if ($post =~ /$block_close_rx|$all_close_rx/ ) {
12304	    push (@$ot, $next);
12305	    last;
12306	}
12307	$closures .= $post unless ($pre =~ /^<>/);
12308	print STDOUT "\n</$next>" if $VERBOSITY > 2;
12309
12310	if ($next =~ /$fontchange_rx/) {
12311	    $font = $next unless ($font);
12312	} elsif ($next =~ /$fontweight_rx/) {
12313	    $fontwt = $next unless ($fontwt);
12314	} elsif ($next =~ /$sizechange_rx/) {
12315	    # discard it
12316	} else {
12317	    unshift (@tags, $next);
12318	    print STDOUT "\n<<$next>" if $VERBOSITY > 2;
12319	    $reopens .= $pre unless ($pre =~ /^<>/);
12320	}
12321    }
12322    push (@$ot, @tags);
12323    if ($font) {
12324	$declarations{$font} =~ m|</.*$|;
12325	$reopens .= $` unless ($` =~ /^<>/);
12326	push (@$ot,$font);
12327	print STDOUT "\n<$font>" if $VERBOSITY > 2;
12328    }
12329    if ($fontwt) {
12330	$declarations{$fontwt} =~ m|</.*$|;
12331	$reopens .= $` unless ($` =~ /^<>/);
12332	push (@$ot,$fontwt);
12333	print STDOUT "\n<$fontwt>" if $VERBOSITY > 2;
12334    }
12335    join('', $closures, $reopens, $str);
12336}
12337
12338
12339
12340#JCL(jcl-tcl)
12341# changed everything
12342#
12343sub do_cmd_title {
12344    local($_) = @_;
12345    &get_next_optional_argument;
12346    local($making_title,$next) = (1,'');
12347    $next = &missing_braces unless (
12348	(s/$next_pair_pr_rx/$next = $2;''/eo)
12349	||(s/$next_pair_rx/$next = $2;''/eo));
12350    $t_title = &translate_environments($next);
12351    $t_title = &translate_commands($t_title);
12352#    $toc_sec_title = &simplify(&translate_commands($next));
12353    $toc_sec_title = &purify(&translate_commands($next));
12354    $TITLE = (($toc_sec_title)? $toc_sec_title : $default_title)
12355	unless ($TITLE && !($TITLE =~ /^($default_title|\Q$FILE\E)$/));
12356#    $TITLE = &purify($TITLE);
12357
12358    #RRM: remove superscripts inserted due to \thanks
12359    $TITLE =~ s/<A[^>]*><SUP>\d+<\/SUP><\/A>/$1/g;
12360    $_;
12361}
12362
12363sub do_cmd_author {
12364    local($_) = @_;
12365    &get_next_optional_argument;
12366    my $next;
12367    $next = &missing_braces unless (
12368	(s/$next_pair_pr_rx/$next = $2;''/seo)
12369	||(s/$next_pair_rx/$next = $2;''/seo));
12370    local($after) = $_;
12371    if ($next =~ /\\and/) {
12372	my @author_list = split(/\s*\\and\s*/, $next);
12373	my $t_author, $t_affil, $t_address;
12374	foreach (@author_list) {
12375	    $t_author = &translate_environments($_);
12376	    $t_author =~ s/\s+/ /g;
12377	    $t_author = &simplify(&translate_commands($t_author));
12378	    ($t_author,$t_affil,$t_address) = split (/\s*<BR>s*/, $t_author);
12379	    push @authors, $t_author;
12380	    push @affils, $t_affil;
12381	    push @addresses, $t_address;
12382	}
12383    } else {
12384	$_ = &translate_environments($next);
12385	$next = &translate_commands($_);
12386	($t_author) = &simplify($next);
12387	($t_author,$t_affil,$t_address) = split (/\s*<BR>s*/, $t_author);
12388	push @authors, $t_author;
12389	push @affils, $t_affil if $t_affil;
12390	push @addresses, $t_address if $t_address;
12391    }
12392    $after;
12393}
12394
12395sub do_cmd_address {
12396    local($_) = @_;
12397    &get_next_optional_argument;
12398    local($next);
12399    $next = &missing_braces unless (
12400	(s/$next_pair_pr_rx/$next = $&;''/eo)
12401	||(s/$next_pair_rx/$next = $&;''/eo));
12402    ($t_address) = &simplify(&translate_commands($next));
12403    push @addresses, $t_address;
12404    $_;
12405}
12406
12407sub do_cmd_institute {
12408    local($_) = @_;
12409    &get_next_optional_argument;
12410    local($next);
12411    $next = &missing_braces unless (
12412	(s/$next_pair_pr_rx/$next = $&;''/eo)
12413	||(s/$next_pair_rx/$next = $&;''/eo));
12414    ($t_institute) = &simplify(&translate_commands($next));
12415    push @affils, $t_institute;
12416    $_;
12417}
12418
12419sub do_cmd_dedicatory {
12420    local($_) = @_;
12421    &get_next_optional_argument;
12422    local($next);
12423    $next = &missing_braces unless (
12424	(s/$next_pair_pr_rx/$next = $&;''/eo)
12425	||(s/$next_pair_rx/$next = $&;''/eo));
12426    ($t_affil) = &simplify(&translate_commands($next));
12427    push @affils, $t_affil;
12428    $_;
12429}
12430
12431sub do_cmd_email {
12432    local($_) = @_;
12433    local($next,$target)=('','notarget');
12434    $next = &missing_braces unless (
12435	(s/$next_pair_pr_rx/$next = $2;''/eo)
12436	||(s/$next_pair_rx/$next = $2;''/eo));
12437    local($mail) = &translate_commands($next);
12438    ($t_email) = &make_href("mailto:$mail","$mail");
12439    push @emails, $t_email;
12440    $_;
12441}
12442
12443sub do_cmd_authorURL {
12444    local($_) = @_;
12445    local($next);
12446    $next = &missing_braces unless (
12447	(s/$next_pair_pr_rx/$next = $2;''/eo)
12448	||(s/$next_pair_rx/$next = $2;''/eo));
12449    ($t_authorURL) =  &translate_commands($next);
12450    push @authorURLs, $t_authorURL;
12451    $_;
12452}
12453
12454sub do_cmd_date {
12455    local($_) = @_;
12456    local($next);
12457    $next = &missing_braces unless (
12458	(s/$next_pair_pr_rx/$next = $&;''/eo)
12459	||(s/$next_pair_rx/$next = $&;''/eo));
12460    ($t_date) = &translate_commands($next);
12461    $_;
12462}
12463
12464sub make_multipleauthors_title {
12465    local($alignc, $alignl) = (@_);
12466    local($t_author,$t_affil,$t_institute,$t_date,$t_address,$t_email,$t_authorURL)
12467	= ('','','','','','','');
12468    local ($t_title,$auth_cnt) = ('',0);
12469    if ($MULTIPLE_AUTHOR_TABLE) {
12470	$t_title = '<TABLE' .($USING_STYLES? ' CLASS="author_info_table"' : '')
12471		.' WIDTH="90%" ALIGN="CENTER" CELLSPACING=15>'
12472		."\n<TR VALIGN=\"top\">";
12473    }
12474    foreach $t_author (@authors) {
12475	$t_affil = shift @affils;
12476	$t_institute = ''; # shift @institutes;
12477	$t_address = shift @addresses;
12478	$t_email = shift @emails;
12479	$t_authorURL = shift @authorURLs;
12480	if ($MULTIPLE_AUTHOR_TABLE) {
12481	    if ($auth_cnt == $MAX_AUTHOR_COLS) {
12482		$t_title .= join("\n", '</TR><TR>', '');
12483		$auth_cnt -= $MAX_AUTHOR_COLS;
12484	    }
12485	    $t_title .= join("\n"
12486		, '<TD>'
12487		, &make_singleauthor_title($alignc, $alignl ,$t_author
12488		    , $t_affil,$t_institute,$t_date,$t_address,$t_email,$t_authorURL)
12489		, '</TD>' );
12490	    ++$auth_cnt;
12491	} else {
12492	    $t_title .= &make_singleauthor_title($alignc, $alignl ,$t_author
12493		, $t_affil,$t_institute,$t_date,$t_address,$t_email,$t_authorURL);
12494	}
12495    }
12496    if ($MULTIPLE_AUTHOR_TABLE) {
12497	$t_title .= "\n</TR></TABLE>\n";
12498    }
12499    $t_title;
12500}
12501
12502sub do_cmd_maketitle {
12503    local($_) = @_;
12504    local($the_title) = '';
12505    local($alignc, $alignl);
12506    if ($HTML_VERSION > 2.1) {
12507	$alignc = " ALIGN=\"CENTER\"";
12508	$alignl = " ALIGN=\"LEFT\"";
12509	$alignl = $alignc if ($MULTIPLE_AUTHOR_TABLE);
12510    }
12511    if ($t_title) {
12512	$the_title .= "<H1$alignc>$t_title</H1>";
12513    } else { &write_warnings("\nThis document has no title."); }
12514    if (($#authors >= 1)||$MULTIPLE_AUTHOR_TABLE) {
12515	$the_title .= &make_multipleauthors_title($alignc,$alignl);
12516	if ($t_date&&!($t_date=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12517	    $the_title .= "\n<P$alignc><STRONG>$t_date</STRONG></P>";}
12518    } else {
12519	$the_title .= &make_singleauthor_title($alignc,$alignl ,$t_author
12520	    , $t_affil,$t_institute,$t_date,$t_address,$t_email,$t_authorURL);
12521    }
12522    $the_title . $_ ;
12523}
12524
12525sub make_singleauthor_title {
12526    local($alignc, $alignl , $t_author
12527	, $t_affil,$t_institute,$t_date,$t_address,$t_email,$t_authorURL) = (@_);
12528    my $t_title = '';
12529    my ($s_author_info, $e_author_info) = ('<DIV','</DIV>');
12530    $s_author_info .= ($USING_STYLES ? ' CLASS="author_info"' : '').'>';
12531
12532    if ($t_author) {
12533	if ($t_authorURL) {
12534	    local($href) = &translate_commands($t_authorURL);
12535	    $href = &make_named_href('author'
12536			, $href, "<STRONG>${t_author}</STRONG>");
12537	    $t_title .= "\n<P$alignc>$href</P>";
12538	} else {
12539	    $t_title .= "\n<P$alignc><STRONG>$t_author</STRONG></P>";
12540	}
12541    } else { &write_warnings("\nThere is no author for this document."); }
12542
12543    if ($t_institute&&!($t_institute=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12544	$t_title .= "\n<P$alignc><SMALL>$t_institute</SMALL></P>";}
12545    if ($t_affil&&!($t_affil=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12546	$t_title .= "\n<P$alignc><I>$t_affil</I></P>";}
12547    if ($t_date&&!($t_date=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12548	$t_title .= "\n<P$alignc><STRONG>$t_date</STRONG></P>";}
12549    if ($t_address&&!($t_address=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12550	$t_title .= "\n<P$alignl><SMALL>$t_address</SMALL></P>";
12551    }  # else { $t_title .= "\n<P$alignl>"}
12552    if ($t_email&&!($t_email=~/^\s*(($O|$OP)\d+($C|$CP))\s*\1\s*$/)) {
12553	$t_title .= "\n<P$alignl><SMALL>$t_email</SMALL></P>";
12554    }  # else { $t_title .= "</P>" }
12555    join("\n", $s_author_info, $t_title, $e_author_info);
12556}
12557
12558sub do_cmd_abstract {
12559    local($_) = @_;
12560    local($abstract);
12561    $abstract = &missing_braces unless (
12562	(s/$next_pair_pr_rx/$abstract = $&;''/eo)
12563	||(s/$next_pair_rx/$abstract = $&;''/eo));
12564    join('', &make_abstract($abstract), $_);
12565}
12566
12567sub make_abstract {
12568    local($_) = @_;
12569    # HWS  Removed emphasis (hard to read)
12570    $_ = &translate_environments($_);
12571    $_ = &translate_commands($_);
12572    local($title);
12573    if ((defined &do_cmd_abstractname)||$new_command{'abstractname'}) {
12574	local($br_id)=++$global{'max_id'};
12575	$title = &translate_environments("$O$br_id$C\\abstractname$O$br_id$C");
12576    } else { $title = $abs_title }
12577    local($env_id) = " CLASS=\"ABSTRACT\"" if ($USING_STYLES);
12578    join('',"\n<H3>", $title, ":</H3>\n"
12579	, (($HTML_VERSION > 3)? "<DIV$env_id>" : "<P>"), $_
12580	, (($HTML_VERSION > 3)? "</DIV>" : "</P>"), "\n<P>");
12581}
12582
12583sub set_default_language {
12584    # MRO: local($lang,*_) = @_;
12585    my $lang = shift;
12586    push(@language_stack, $default_language);
12587    $default_language = $lang;
12588    $_[0] .= '\popHtmlLanguage';
12589}
12590
12591sub do_cmd_popHtmlLanguage {
12592    $default_language = pop(@language_stack);
12593    $_[0];
12594}
12595
12596sub do_cmd_today {
12597    local($lang);
12598    if ($PREAMBLE) {
12599	$lang = $TITLES_LANGUAGE || $default_language ;
12600    } else {
12601	$lang = $current_language || $default_language ;
12602    }
12603    local($today) = $lang . '_today';
12604    if (defined &$today) { join('', eval "&$today()", $_[0]) }
12605    else { join('', &default_today(), $_[0]) }
12606}
12607
12608sub default_today {
12609    #JKR: Make it more similar to LaTeX
12610    ## AYS: moved french-case to styles/french.perl
12611    my $today = &get_date();
12612
12613    $today =~ s|(\d+)/0?(\d+)/|$Month[$1] $2, |;
12614    join('',$today,$_[0]);
12615}
12616
12617sub do_cmd_textbackslash { join('','&#92;', $_[0]);}
12618sub do_cmd_textbar { join('','|', $_[0]);}
12619sub do_cmd_textless { join('',';SPMlt;', $_[0]);}
12620sub do_cmd_textgreater { join('',';SPMgt;', $_[0]);}
12621sub do_cmd_textasciicircum { join('','&#94;', $_[0]);}
12622sub do_cmd_textasciitilde { join('','&#126;', $_[0]);}
12623sub do_cmd_textquoteleft { join('','&#96;', $_[0]);}
12624sub do_cmd_textquoteright { join('','&#39;', $_[0]);}
12625
12626sub do_cmd_textcompwordmark { join('','', $_[0]);}
12627sub do_cmd_texttrademark { join('','<SUP><SMALL>TM</SMALL></SUP>', $_[0]);}
12628
12629sub do_cmd_textsubscript   { &make_text_supsubscript('SUB',$_[0]);}
12630sub do_cmd_textsuperscript { &make_text_supsubscript('SUP',$_[0]);}
12631
12632sub make_text_supsubscript {
12633    local ($supsub, $_) = (@_);
12634    my $arg = '';
12635    $arg = &missing_braces unless (
12636	(s/$next_pair_pr_rx/$arg = $&;''/eo)
12637	||(s/$next_pair_rx/$arg = $&;''/eo));
12638    $arg = &translate_commands($arg) if ($arg =~ m!\\!);
12639    join('', "<$supsub>", $arg, "</$supsub>", $_);
12640}
12641
12642sub do_cmd_textcircled {
12643    local ($_) = (@_);
12644    my $arg = '';
12645    $arg = &missing_braces unless (
12646	(s/$next_pair_pr_rx/$arg = $&;''/eo)
12647	||(s/$next_pair_rx/$arg = $&;''/eo));
12648    my $after = $_;
12649    join('', &process_undefined_environment("tex2html_nomath_inline"
12650	   , ++$global{'max_id'}
12651	   , "\\vbox{\\kern3pt\\textcircled{$arg}}" )
12652	, $after );
12653}
12654
12655# these can be overridded in charset (.pl) extension files:
12656sub do_cmd_textemdash { join('','---', $_[0]);}
12657sub do_cmd_textendash { join('','--', $_[0]);}
12658#sub do_cmd_exclamdown { join('','', $_[0]);}
12659#sub do_cmd_questiondown { join('','', $_[0]);}
12660sub do_cmd_textquotedblleft { join('',"``", $_[0]);}
12661sub do_cmd_textquotedblright { join('',"''", $_[0]);}
12662sub do_cmd_textbullet { join('','*', $_[0]);}
12663sub do_cmd_textvisiblespace { join('','_', $_[0]);}
12664
12665sub do_cmd_ldots {
12666    join('',(($math_mode&&$USE_ENTITY_NAMES) ? ";SPMldots;" : "..."),$_[0]);
12667}
12668
12669sub do_cmd_dots {
12670    join('',(($math_mode&&$USE_ENTITY_NAMES) ? ";SPMldots;" : "..."),$_[0]);
12671}
12672
12673sub do_cmd_hrule {
12674    local($_) = @_;
12675    &ignore_numeric_argument;
12676    #JKR: No need for <BR>
12677    local($pre,$post) = &minimize_open_tags('<HR>');
12678    join('',$pre,$_);
12679}
12680
12681#sub do_cmd_hrulefill {
12682#    "<HR ALIGN=\"right\">\n<BR CLEAR=\"right\">";
12683#}
12684
12685sub do_cmd_linebreak {
12686    local($num,$dum) = &get_next_optional_argument;
12687    if (($num)&&($num<4)) { return $_[0] }
12688    join('',"<BR>", $_[0]);
12689}
12690
12691sub do_cmd_pagebreak {
12692    local($_) = @_;
12693    local($num,$dum) = &get_next_optional_argument;
12694    if (($num)&&($num<4)) { return($_) }
12695    elsif (/^ *\n *\n/) {
12696	local($after) = $';
12697	local($pre,$post) = &minimize_open_tags("<BR>\n<P>");
12698	join('',$pre, $')
12699    } else { $_ }
12700}
12701
12702
12703sub do_cmd_newline { join('',"<BR>", $_[0]); }
12704# this allows for forced newlines in tables, etc.
12705sub do_cmd_endgraf { join('',"<BR>", $_[0]); }
12706
12707sub do_cmd_space { join(''," ",$_[0]); }
12708sub do_cmd_enspace { join('',"\&nbsp;",$_[0]); }
12709sub do_cmd_quad { join('',"\&nbsp;"x4,$_[0]); }
12710sub do_cmd_qquad { join('',"\&nbsp;"x8,$_[0]); }
12711
12712sub do_cmd_par {
12713    local ($_) = @_;
12714    my ($pre,$post) = &preserve_open_tags();
12715    my ($spar, $lcode) = ("\n<P", '');
12716    if (($USING_STYLES) &&(!($default_language eq $TITLES_LANGUAGE))) {
12717	$lcode = &get_current_language();
12718	$spar .= $lcode if $lcode;
12719    }
12720    join('', $pre, $spar, ">\n",$post,$_);
12721}
12722
12723sub do_cmd_medskip {
12724    local ($_) = @_;
12725    local($pre,$post) = &preserve_open_tags();
12726    join('',$pre,"\n<P><BR>\n",$post,$_);
12727}
12728
12729sub do_cmd_smallskip {
12730    local ($_) = @_;
12731    local($pre,$post) = &preserve_open_tags();
12732    join('',$pre,"\n<P></P>\n",$post,$_);
12733}
12734
12735sub do_cmd_bigskip {
12736    local ($_) = @_;
12737    local($pre,$post) = &preserve_open_tags();
12738    join('',$pre,"\n<P><P><BR>\n",$post,$_);
12739}
12740
12741# MEH: Where does the slash command come from?
12742# sub do_cmd_slash {
12743#    join('',"/",$_[0]);
12744#}
12745sub do_cmd_esc_slash { $_[0]; }
12746sub do_cmd_esc_hash { "\#". $_[0]; }
12747sub do_cmd_esc_dollar { "\$". $_[0]; }
12748sub do_cmd__at_ { $_[0]; }
12749sub do_cmd_lbrace { "\{". $_[0]; }
12750sub do_cmd_rbrace { "\}". $_[0]; }
12751sub do_cmd_Vert { "||". $_[0]; }
12752sub do_cmd_backslash { "\\". $_[0]; }
12753
12754#RRM: for subscripts outside math-mode
12755# e.g. in Chemical formulae
12756sub do_cmd__sub {
12757    local($_) = @_;
12758    local($next);
12759    $next = &missing_braces unless (
12760        (s/$next_pair_pr_rx/$next = $2;''/e)
12761        ||(s/$next_pair_rx/$next = $2;''/e));
12762    join('',"<SUB>",$next,"</SUB>",$_);
12763}
12764
12765#JCL(jcl-del) - the next two ones must only have local effect.
12766# Yet, we don't have a mechanism to revert such changes after
12767# a group has closed.
12768#
12769sub do_cmd_makeatletter {
12770    $letters =~ s/@//;
12771    $letters .= '@';
12772    &make_letter_sensitive_rx;
12773    $_[0];
12774}
12775
12776sub do_cmd_makeatother {
12777    $letters =~ s/@//;
12778    &make_letter_sensitive_rx;
12779    $_[0];
12780}
12781
12782
12783################## Commands to be processed by Latex #################
12784#
12785# The following commands are passed to Latex for processing.
12786# They cannot be processed at the same time as normal commands
12787# because their arguments must be left untouched by the translator.
12788# (Normally the arguments of a command are translated before the
12789# command itself).
12790#
12791# In fact, it's worse:  it is not correct to process these
12792# commands after we process environments, because some of them
12793# (for instance, \parbox) may contain unknown or wrapped
12794# environments.  If math mode occurs in a parbox, the
12795# translate_environments routine should *not* process it, lest
12796# we encounter the lossage outlined above.
12797#
12798# On the other hand, it is not correct to process these commands
12799# *before* we process environments, or figures containing
12800# parboxes, etc., will be mishandled.
12801#
12802# RRM: (added for V97.1)
12803#  \parbox now uses the  _wrap_deferred  mechanism, and has a  do_cmd_parbox
12804#  subroutine defined. This means that environments where parboxes are
12805#  common (.g. within table cells), can detect the \parbox command and
12806#  adjust the processing accordingly.
12807#
12808# So, the only way to handle these commands is to wrap them up
12809# in null environments, as for math mode, and let translate_environments
12810# (which can handle nesting) figure out which is the outermost.
12811#
12812# Incidentally, we might as well make these things easier to configure...
12813
12814sub process_commands_in_tex {
12815    local($_) = @_;
12816    local($arg,$tmp);
12817    foreach (/.*\n?/g) {
12818	chop;
12819	# For each line
12820	local($cmd, @args) = split('#',$_);
12821	next unless $cmd;
12822	$cmd =~ s/ //g;
12823
12824	# skip if a proper implementation already exists
12825	$tmp = "do_cmd_$cmd";
12826	next if (defined &$tmp);
12827
12828	# Build routine body ...
12829	local ($body, $code, $thisone) = ("", "");
12830
12831	# alter the pattern here to debug particular commands
12832#	$thisone = 1 if ($cmd =~ /mathbb/);
12833
12834	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
12835	foreach $arg (@args) {
12836	    print "\nARG: $arg" if ($thisone);
12837	    print "\nARG: $next_pair_rx" if ($thisone);
12838	    if ($arg =~ /\{\}/) {
12839# RRM: the $` is surely wrong, allowing no error-checking.
12840# Use <<...>> for specific patterns
12841#		$body .= '$args .= "$`$&" if s/$next_pair_rx//o;'."\n";
12842		$body .= '$args .= join("","{", &missing_braces, "}") unless ('."\n";
12843		$body .= '  (s/$next_pair_pr_rx/$args.=$`.$&;""/es)'."\n";
12844		$body .= '  ||(s/$next_pair_rx/$args.=$`.$&;""/es));'."\n";
12845		print "\nAFTER:$'" if (($thisone)&&($'));
12846		$body .= $' if ($');
12847	    } elsif ($arg =~ /\[\]/) {
12848		$body .= '($dummy, $pat) = &get_next_optional_argument;'
12849		    . '$args .= $pat;'."\n";
12850		print "\nAFTER:$'" if (($thisone)&&($'));
12851		$body .= $' if ($');
12852	    } elsif ($arg =~ /^\s*\\/) {
12853		$body .= '($dummy, $pat) = &get_next_tex_cmd;'
12854		    . '$args .= $pat;'."\n";
12855		print "\nAFTER:$'" if (($thisone)&&($'));
12856		$body .= $' if ($');
12857	    } elsif ($arg =~ /<<\s*/) {
12858		$arg = $';
12859		if ($arg =~ /\s*>>/) {
12860                    # MRO: replaced $* with /m
12861		    $body .= '$args .= "$`$&" if (/\\'.$`.'/m);' . "\n"
12862#		    $body .= '$args .= "$`$&" if (/\\\\'.$`.'/);' . "\n"
12863			. "\$_ = \$\';\n";
12864		    print "\nAFTER:$'" if (($thisone)&&($'));
12865		    $body .= $' if ($');
12866		} else { $body .= $arg ; }
12867	    } else {
12868	        print "\nAFTER:$'" if (($thisone)&&($arg));
12869		$body .= $arg ;
12870	    }
12871	}
12872
12873	# Generate a new subroutine
12874	local($padding) = " ";
12875	$padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
12876	$code = "sub wrap_cmd_$cmd {" . "\n"
12877	    . 'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";' . "\n"
12878	    . $body
12879	    . (($thisone)? "print STDERR \"\\n$cmd:\".\$args.\"\\n\";\n" : '')
12880	    . '(&make_wrapper(1).$cmd'
12881	    . ($padding ? '"'.$padding.'"' : '')
12882	    . '.$args.&make_wrapper(0), $_)}'
12883	    . "\n";
12884	print "\nWRAP_CMD: $code " if ($thisone); # for debugging
12885	eval $code; # unless ($thisone);
12886	print STDERR "\n*** sub wrap_cmd_$cmd  failed: $@" if ($@);
12887
12888	# And make sure the main loop will catch it ...
12889#	$raw_arg_cmds{$cmd} = 1;
12890	++$raw_arg_cmds{$cmd};
12891    }
12892}
12893
12894sub process_commands_nowrap_in_tex {
12895    local($_) = @_;
12896    local($arg);
12897    foreach (/.*\n?/g) {
12898	chop;
12899	local($cmd, @args) = split('#',$_);
12900	next unless $cmd;
12901	$cmd =~ s/ //g;
12902	# Build routine body ...
12903	local ($bodyA, $codeA, $bodyB, $codeB, $thisone) = ("", "", "", "");
12904
12905	# alter the pattern here to debug particular commands
12906#	$thisone = 1 if ($cmd =~ /epsf/);
12907
12908	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
12909	foreach $arg (@args) {
12910	    print "\nARG: $arg" if ($thisone);
12911	    if ($arg =~ /\{\}/) {
12912#		$bodyA .= '$args .= "$`"."$&" if (s/$any_next_pair_rx//);'."\n";
12913		$bodyA .= 'if (s/$next_pair_rx//s){$args.="$`"."$&"; $_='."\$'};\n";
12914		$bodyB .= '$args .= &missing_braces'."\n unless (";
12915		$bodyB .= '(s/$any_next_pair_pr_rx/$args.=$`.$&;\'\'/eo)'."\n";
12916		$bodyB .= '  ||(s/$any_next_pair_rx/$args.=$`.$&;\'\'/eo));'."\n";
12917		print "\nAFTER:$'" if (($thisone)&&($'));
12918#		$bodyA .= $'.";\n" if ($');
12919		$bodyB .= $'.";\n" if ($');
12920	    } elsif ($arg =~ /\[\]/) {
12921		$bodyA .= '($dummy, $pat) = &get_next_optional_argument;'
12922		    . '$args .= $pat;'."\n";
12923		print "\nAFTER:$'" if (($thisone)&&($'));
12924#		$bodyA .= $'.";\n" if ($');
12925		$bodyB .= $'.";\n" if ($');
12926	    } elsif ($arg =~ /^\s*\\/) {
12927		$bodyA .= '($dummy, $pat) = &get_next_tex_cmd;'
12928		    . '$args .= $pat;'."\n";
12929		$bodyB .= '($dummy, $pat) = &get_next_tex_cmd;'
12930		    . '$args .= $pat;'."\n";
12931		print "\nAFTER:$'" if (($thisone)&&($'));
12932		$bodyA .= $'.";\n" if ($');
12933		$bodyB .= $'.";\n" if ($');
12934	    } elsif ($arg =~ /<<\s*/) {
12935		$arg = $';
12936		if ($arg =~ /\s*>>/) {
12937                    # MRO: replaced $* with /m
12938		    $bodyA .= '$args .= "$`$&" if (/\\'.$`.'/m);' . "\n"
12939#		    $bodyA .= '$args .= $`.$& if (/\\\\'.$`.'/);' . "\n"
12940			. "\$_ = \$\';\n";
12941		    $bodyB .= '$args .= "$`$&" if (/\\'.$`.'/m);' . "\n"
12942			. "\$_ = \$\';\n";
12943		    print "\nAFTER:$'" if (($thisone)&&($'));
12944#		    $bodyA .= $'.";\n" if ($');
12945		    $bodyB .= $'.";\n" if ($');
12946		} else {
12947		    print "\nAFTER:$arg" if (($thisone)&&($arg));
12948#		    $bodyA .= $arg.";\n" if ($arg);
12949		    $bodyB .= $arg.";\n" if ($arg);
12950		}
12951	    } else {
12952		print "\nAFTER:$arg" if (($thisone)&&($arg));
12953		$bodyA .= '$args .= '.$arg.";\n" if ($');
12954		$bodyB .= $arg.";\n" if ($');
12955	    }
12956	}
12957	local($padding) = " ";
12958	$padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
12959	# Generate 2 new subroutines
12960	$codeA = "sub wrap_cmd_$cmd {" . "\n"
12961	    .'local($cmd, $_) = @_; local($args, $dummy, $pat) = "";'."\n"
12962	    . $bodyA
12963	    . (($thisone)? "print \"\\nwrap $cmd:\\n\".\$args.\"\\n\";\n" : '')
12964	    . '(&make_nowrapper(1)."\n".$cmd.'."\"$padding\""
12965	    . '.$args.&make_nowrapper(0)," ".$_)}'
12966	    ."\n";
12967	print "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
12968	eval $codeA;
12969	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
12970	$codeB = "do_cmd_$cmd";
12971	do {
12972	    $bodyB = '"";' if !($bodyB);
12973	    $codeB = "sub do_cmd_$cmd {" . "\n"
12974		. 'local($_,$ot) = @_;'."\n"
12975		. 'local($open_tags_R) = defined $ot ? $ot : $open_tags_R;'."\n"
12976		. 'local($cmd,$args,$dummy,$pat)=("'.$cmd.'","","","");'."\n"
12977		. $bodyB
12978		. (($thisone)? "print \"\\ndo $cmd:\".\$args.\"\\n\";\n" : '')
12979#		. '$latex_body.="\\n".&revert_to_raw_tex("'."\\\\$cmd$padding".'$args")."\\n\\n";'
12980		. "\$_;}\n";
12981	    print STDOUT "\nDO_CMD: $codeB " if ($thisone); # for debugging
12982	    eval $codeB;
12983	    print STDERR "\n\n*** sub do_cmd_$cmd  failed: $@\n" if ($@);
12984	} unless (defined &$codeB );
12985
12986	# And make sure the main loop will catch it ...
12987#	$raw_arg_cmds{$cmd} = 1;
12988	++$raw_arg_cmds{$cmd};
12989    }
12990}
12991
12992sub process_commands_wrap_deferred {
12993    local($_) = @_;
12994    local($arg,$thisone);
12995    foreach (/.*\n?/g) {
12996	chop;
12997	local($cmd, @args) = split('#',$_);
12998	next unless $cmd;
12999	$cmd =~ s/ //g;
13000	# Build routine body ...
13001	local ($bodyA, $codeA, $bodyB, $codeB, $after, $thisone);
13002
13003	# alter the pattern here to debug particular commands
13004#	$thisone = 1 if ($cmd =~ /selectlanguage/);
13005
13006	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
13007	foreach $arg (@args) {
13008	    print "\nARG: $arg" if ($thisone);
13009	    if ($arg =~ /\{\}/) {
13010#		$bodyA .= '$args .= "$`$&" if (s/$any_next_pair_rx//o);';
13011		$bodyA .= '$args .= "$`$&" if (s/$next_pair_rx//so);';
13012		$after = $';
13013		print "\nAFTER:$'" if (($thisone)&&($'));
13014	    } elsif ($arg =~ /\[\]/) {
13015		$bodyA .= '($dummy, $pat) = &get_next_optional_argument;' .
13016		    "\n". '$args .= $pat;';
13017		$after = $';
13018		print "\nAFTER:$'" if (($thisone)&&($'));
13019	    } elsif ($arg =~ /^\s*\\/) {
13020		$bodyA .= '($dummy, $pat) = &get_next_tex_cmd;'
13021		    . '$args .= $pat;'."\n";
13022		print "\nAFTER:$'" if (($thisone)&&($'));
13023		$bodyA .= $'.";\n" if ($');
13024	    } elsif (/<<\s*([^>]*)[\b\s]*>>/) {
13025		local($endcmd, $afterthis) = ($1,$');
13026		$afterthis =~ s/(^\s*|\s*$)//g;
13027		$endcmd =~ s/\\/\\\\/g;
13028		$bodyA .= "\n". 'if (/'.$endcmd.'/) { $args .= $`.$& ; $_ = $\' };';
13029		$after .= $afterthis if ($afterthis);
13030		print "\nAFTER:$'" if (($thisone)&&($'));
13031	    } else {
13032		print "\nAFTER:$arg" if (($thisone)&&($arg));
13033                $bodyB .= $arg.";\n" ; $after = ''
13034            }
13035	    $after =~ s/(^\s*|\s*$)//g if ($after);
13036	    $bodyB .= $after . ";" if ($after);
13037	    $bodyA .= "\$args .= ".$after . ";" if ($after);
13038	}
13039	local($padding) = " ";
13040	$padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
13041	# Generate 2 new subroutines
13042	$codeA = "sub wrap_cmd_$cmd {" . "\n"
13043	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'."\n"
13044	    . $bodyA #. ($bodyA ? "\n" : '')
13045	    . (($thisone)? ";print \"\\nwrap $cmd:\".\$args.\"\\n\";\n" : '')
13046	    .'(&make_deferred_wrapper(1).$cmd.'.$padding
13047		.'$args.&make_deferred_wrapper(0),$_)}'
13048	    ."\n";
13049	print STDERR "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
13050	eval $codeA;
13051	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
13052
13053	#RRM: currently these commands only go to LaTeX or access counters.
13054	#   They could be implemented more generally, as below with  do_dcmd_$cmd
13055	#   requiring replacement to be performed before evaluation.
13056	$codeB = "sub do_dcmd_$cmd {" . "\n"
13057	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'."\n"
13058	    . $bodyA . "\n"
13059            . (($thisone)? ";print \"\\ndo_def $cmd:\".\$args.\"\\n\";\n" : '')
13060            . $bodyB . "}" . "\n";
13061	print "\nDEF_CMD: $codeB " if ($thisone); # for debugging
13062	local($tmp) = "do_cmd_$cmd";
13063	eval $codeB unless (defined &$tmp);
13064	print STDERR "\n\n*** sub do_dcmd_$cmd  failed: $@\n" if ($@);
13065
13066	# And make sure the main loop will catch it ...
13067#	$raw_arg_cmds{$cmd} = 1;
13068	++$raw_arg_cmds{$cmd};
13069    }
13070}
13071
13072sub process_commands_inline_in_tex {
13073    local($_) = @_;
13074    foreach (/.*\n?/g) {
13075	chop;
13076	local($cmd, @args) = split('#',$_);
13077	next unless $cmd;
13078	$cmd =~ s/ //g;
13079	# Build routine body ...
13080	local ($body, $code, $thisone) = ("", "");
13081
13082	# uncomment and alter the pattern here to debug particular commands
13083#	$thisone = 1 if ($cmd =~ /L/);
13084
13085	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
13086	foreach (@args) {
13087	    print "\nARG: $_" if ($thisone);
13088	    if (/\{\}/) {
13089#		$body .= '$args .= $`.$& if (/$any_next_pair_rx/);' . "\n"
13090#		    . "\$_ = \$\';\n";
13091		$body .= '$args .= $`.$& if (s/$next_pair_rx//s);' . "\n"
13092	    } elsif (/\[\]/) {
13093		$body .= 'local($dummy, $pat) = &get_next_optional_argument;' .
13094		    "\n". '$args .= $pat;';
13095	    } elsif ($arg =~ /^\s*\\/) {
13096		$body .= '($dummy, $pat) = &get_next_tex_cmd;'
13097		    . '$args .= $pat;'."\n";
13098		print "\nAFTER:$'" if (($thisone)&&($'));
13099		$body .= $'.";\n" if ($');
13100	    } elsif (/<<\s*/) {
13101		$_ = $';
13102		if (/\s*>>/) {
13103                    # MRO: replaced $* with /m
13104		    $body .= '$args .= "$`$&" if (/\\'.$`.'/m);' . "\n"
13105			. "\$_ = \$\';\n"
13106		} else { $body .= $_.";\n" ; }
13107	    } else { $body .= $_.";\n" ; }
13108	}
13109	local($padding) = " ";
13110	$padding = '' if (($cmd =~ /\W$/)||(!$args)||($args =~ /^\W/));
13111	# Generate a new subroutine
13112	my $itype = ($cmd =~ /^f.*box$/ ? 'inline' : 'nomath');
13113	$code = "sub wrap_cmd_$cmd {" . "\n"
13114	    .'local($cmd, $_) = @_; local ($args) = "";' . "\n"
13115	    . $body . "\n"
13116            . (($thisone)? ";print \"\\ndo $cmd:\".\$args.\"\\n\";\n" : '')
13117	    .'(&make_'.$itype.'_wrapper(1).$cmd.$padding.$args.'
13118	    . '&make_'.$itype.'_wrapper(0),$_)}'
13119	    ."\n";
13120	print "\nWRAP_CMD:$raw_arg_cmds{$cmd}: $code "
13121		if ($thisone); # for debugging
13122	eval $code;
13123	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
13124	# And make sure the main loop will catch it ...
13125#	$raw_arg_cmds{$cmd} = 1;
13126	++$raw_arg_cmds{$cmd};
13127    }
13128}
13129
13130
13131# Invoked before actual translation; wraps these commands in
13132# tex2html_wrap environments, so that they are properly passed to
13133# TeX in &translate_environments ...
13134# JCL(jcl-del) - new usage of $raw_arg_cmd_rx
13135sub wrap_raw_arg_cmds {
13136    local ($processed_text, $cmd, $wrapper, $wrap, $after);
13137    print "\nwrapping raw arg commands " if ($VERBOSITY>1);
13138    local($seg, $par_wrap, $teststar, @processed);
13139#   local(@segments) = split(/\\par\b/,$_);
13140#   foreach (@segments) {
13141#      $par_wrap = join('',&make_deferred_wrapper(1), "\\par"
13142#			, &make_deferred_wrapper(0));
13143#     push(@processed, $par_wrap ) if ($seg); ++$seg;
13144    if (%renew_command) {
13145	local($key);
13146	foreach $key (keys %renew_command) {
13147	    $raw_arg_cmds{$key} = 1;
13148	    $raw_arg_cmd_rx =~ s/^(\(\)\\\\\()/$1$key\|/;
13149	}
13150    }
13151    print "\n" if (/$raw_arg_cmd_rx/);
13152
13153    # MRO: replaced $* with /m
13154    while (/$raw_arg_cmd_rx/m) {
13155	local($star);
13156	push (@processed, $`); print "\@";
13157	$after = $';
13158	#JCL(jcl-del) - status of starred raw arg cmds yet unclear
13159	($cmd, $star) = ($1.$2,$4);
13160	if ($star eq '*') { $star = 'star';}
13161	else { $after = $star.$after; $star = ''; }
13162	$wrapper = "wrap_cmd_$cmd"; $teststar = $wrapper.'star';
13163	if ($star && defined &$teststar) { $wrapper = $teststar; $star = '*'; }
13164        # MRO: make {\bf**} work
13165	elsif($star) { $after = '*'.$after; $star = '' }
13166	print "\nWRAPPED: $cmd as $wrapper" if ($VERBOSITY > 5);
13167
13168	# ensure that the result is separated from following words...
13169	my $padding = ($after =~ /^[a-zA-Z]/s)? ($cmd =~ /\W$/ ? '':' '):'';
13170
13171	if ($raw_arg_cmds{$cmd} && defined &$wrapper) {
13172	    ($wrap, $_) = &$wrapper("\\$cmd$star", $padding . $after);
13173	    # ...but don't leave an unwanted space at the beginning
13174	    $_ =~ s/^ //s if($padding && $wrap !~ /\w$/m
13175	    	&& (length($_) == length($after)+1) );
13176	    push (@processed, $wrap);
13177	} elsif ($raw_arg_cmds{$cmd}) {
13178	    print STDERR "\n*** $wrapper not defined, cannot wrap \\$cmd";
13179	    &write_warnings("\n*** $wrapper not defined, cannot wrap \\$cmd ");
13180	    push (@processed, "\\$cmd$padding");
13181	    $_ = $after;
13182	} else {
13183	    push (@processed, "\\$cmd$padding");
13184	    $_ = $after;
13185	}
13186        last unless ($after =~ /\\/);
13187    }
13188
13189    # recombine the pieces
13190    $_ = join('',@processed, $_);
13191}
13192
13193#########################################################################
13194
13195# To make a table of contents, list of figures and list of tables commands
13196# create a link to corresponding files which do not yet exist.
13197# The binding of the file variable in each case acts as a flag
13198# for creating the actual file at the end, after all the information
13199# has been gathered.
13200
13201sub do_cmd_tableofcontents { &do_real_tableofcontents(@_) }
13202sub do_real_tableofcontents {
13203#    local($_) = @_;
13204    if ((defined &do_cmd_contentsname)||$new_command{'contentsname'}) {
13205	local($br_id)=++$global{'max_id'};
13206	$TITLE = &translate_environments("$O$br_id$C\\contentsname$O$br_id$C");
13207    } else { $TITLE = $toc_title }
13208    $toc_sec_title = $TITLE;
13209    $tocfile = $CURRENT_FILE;  # sets  $tocfile  this globally
13210    local $toc_head = $section_headings{'tableofcontents'};
13211    if ($toc_style) {
13212	$toc_head .= " CLASS=\"$toc_style\"";
13213	$env_style{"$toc_head.$toc_style"} = " "
13214	    unless ($env_style{"$toc_head.$toc_style"});
13215    }
13216    local($closures,$reopens) = &preserve_open_tags();
13217    join('', "<BR>\n", $closures
13218	, &make_section_heading($TITLE, $toc_head), $toc_mark
13219	, $reopens, @_[0]);
13220}
13221sub do_cmd_listoffigures {
13222    local($_) = @_;
13223    local($list_type) = ($SHOW_SECTION_NUMBERS ? 'UL' : 'OL' );
13224    if ((defined &do_cmd_listfigurename)||$new_command{'listfigurename'}) {
13225	local($br_id)=++$global{'max_id'};
13226	$TITLE = &translate_environments("$O$br_id$C\\listfigurename$O$br_id$C");
13227    } else { $TITLE = $lof_title }
13228    $toc_sec_title = $TITLE;
13229    $loffile = $CURRENT_FILE;  # sets  $loffile  this globally
13230    local $lof_head = $section_headings{'listoffigures'};
13231    local($closures,$reopens) = &preserve_open_tags();
13232    join('', "<BR>\n", $closures
13233	 , &make_section_heading($TITLE, $lof_head)
13234	 , "<$list_type>", $lof_mark, "</$list_type>"
13235	 , $reopens, $_);
13236}
13237sub do_cmd_listoftables {
13238    local($_) = @_;
13239    local($list_type) = ($SHOW_SECTION_NUMBERS ? 'UL' : 'OL' );
13240    if ((defined &do_cmd_listtablename)||$new_command{'listtablename'}) {
13241	local($br_id)=++$global{'max_id'};
13242	$TITLE = &translate_environments("$O$br_id$C\\listtablename$O$br_id$C");
13243    } else { $TITLE = $lot_title }
13244    $toc_sec_title = $TITLE;
13245    $lotfile = $CURRENT_FILE;  # sets  $lotfile  this globally
13246    local $lot_head = $section_headings{'listoftables'};
13247    local($closures,$reopens) = &preserve_open_tags();
13248    join('', "<BR>\n", $closures
13249	 , &make_section_heading($TITLE, $lot_head)
13250	 , "<$list_type>", $lot_mark, "</$list_type>"
13251	 , $reopens, $_);
13252}
13253
13254# Indicator for where to put the CHILD_LINKS table.
13255sub do_cmd_tableofchildlinks {
13256    local($_) = @_;
13257    local($thismark) = $childlinks_mark;
13258    local($option,$dum) = &get_next_optional_argument;
13259    $thismark = &check_childlinks_option($option) if ($option);
13260    local($pre,$post) = &minimize_open_tags("$thismark\#0\#");
13261    join('', "<BR>", $pre, $_);
13262}
13263
13264# leave out the preceding <BR>
13265sub do_cmd_tableofchildlinksstar {
13266    local($_) = @_;
13267    local($thismark) = $childlinks_mark;
13268    local($option,$dum) = &get_next_optional_argument;
13269    $thismark = &check_childlinks_option($option) if ($option);
13270    local($pre,$post) = &minimize_open_tags("$thismark\#1\#");
13271    join('', $pre, $_);
13272}
13273
13274sub check_childlinks_option {
13275    local($option) = @_;
13276    if ($option =~ /none/i) {
13277	$childlinks_mark = $childlinks_null_mark;
13278	$childlinks_null_mark }
13279    elsif ($option =~ /off/i) { $childlinks_null_mark }
13280    elsif ($option =~ /all/i) {
13281	$childlinks_mark = $childlinks_on_mark;
13282	$childlinks_on_mark }
13283    elsif ($option =~ /on/i) { $childlinks_on_mark }
13284}
13285
13286sub remove_child_marks {
13287    # Modifies $_
13288    s/($childlinks_on_mark|$childlinks_null_mark)\#\d\#//go;
13289}
13290
13291
13292sub do_cmd_htmlinfo {
13293    local($_) = @_;
13294    local($option,$dum) = &get_next_optional_argument;
13295    if ($option =~ /^(off|none)/i) { $INFO = 0; return ($_) }
13296    local($pre,$post) = &minimize_open_tags($info_title_mark.$info_page_mark);
13297    join('', "<BR>", $pre, $_);
13298}
13299sub do_cmd_htmlinfostar {
13300    local($_) = @_;
13301    local($option,$dum) = &get_next_optional_argument;
13302    if ($option =~ /^(off|none)/i) { $INFO = 0; return ($_) }
13303    local($pre,$post) = &minimize_open_tags($info_page_mark);
13304    join('', $pre, $_);
13305}
13306
13307# $idx_mark will be replaced with the real index at the end
13308sub do_cmd_textohtmlindex {
13309    local($_) = @_;
13310    if ((defined &do_cmd_indexname )||$new_command{'indexname'}) {
13311	local($br_id)=++$global{'max_id'};
13312	$TITLE = &translate_environments("$O$br_id$C\\indexname$O$br_id$C");
13313    } else { $TITLE = $idx_title }
13314    $toc_sec_title = $TITLE;
13315    $idxfile = $CURRENT_FILE;
13316    if (%index_labels) { &make_index_labels(); }
13317    if (($SHORT_INDEX) && (%index_segment)) { &make_preindex(); }
13318    else { $preindex = ''; }
13319    local $idx_head = $section_headings{'textohtmlindex'};
13320    local($heading) = join(''
13321	, &make_section_heading($TITLE, $idx_head)
13322	, $idx_mark );
13323    local($pre,$post) = &minimize_open_tags($heading);
13324    join('',"<BR>\n" , $pre, $_);
13325}
13326
13327#RRM: added 17 May 1996
13328# allows labels within the printable key of index-entries,
13329# when using  makeidx.perl
13330sub make_index_labels {
13331    local($key, @keys);
13332    @keys = keys %index_labels;
13333    foreach $key (@keys) {
13334	if (($ref_files{$key}) && !($ref_files{$key} eq "$idxfile")) {
13335	    local($tmp) = $ref_files{$key};
13336	    &write_warnings("\nmultiple label $key , target in $idxfile masks $tmp ");
13337	}
13338	$ref_files{$key} .= "$idxfile";
13339    }
13340}
13341#RRM: added 17 May 1996
13342# constructs a legend for the SHORT_INDEX, with segments
13343# when using  makeidx.perl
13344sub make_preindex { &make_real_preindex }
13345sub make_real_preindex {
13346    local($key, @keys, $head, $body);
13347    $head = "<HR>\n<H4>Legend:</H4>\n<DL COMPACT>";
13348    @keys = keys %index_segment;
13349    foreach $key (@keys) {
13350	local($tmp) = "segment$key";
13351	$tmp = $ref_files{$tmp};
13352	$body .= "\n<DT>$key<DD>".&make_named_href('',$tmp,$index_segment{$key});
13353#	$body .= "\n<DT>$key<DD>".&make_named_href('',
13354#		$tmp."\#CHILD\_LINKS",$index_segment{$key})
13355#	             unless ($CHILD_STAR);
13356    }
13357    $preindex = join('', $head, $body, "\n</DL>") if ($body);
13358}
13359
13360sub do_cmd_printindex { &do_real_printindex(@_); }
13361sub do_real_printindex {
13362    local($_) = @_;
13363    local($which) = &get_next_optional_argument;
13364    $idx_name = $index_names{$which}
13365	if ($which && $index_names{$which});
13366    @_;
13367}
13368
13369sub do_cmd_newindex {
13370    local($_) = @_;
13371    local($dum,$key,$title);
13372    $key = &missing_braces unless (
13373	(s/$next_pair_pr_rx/$key=$2;''/eo)
13374	||(s/$next_pair_rx/$key=$2;''/eo));
13375    $dum = &missing_braces unless (
13376	(s/$next_pair_pr_rx/$dum=$2;''/eo)
13377	||(s/$next_pair_rx/$dum=$2;''/eo));
13378    $dum = &missing_braces unless (
13379	(s/$next_pair_pr_rx/$dum=$2;''/eo)
13380	||(s/$next_pair_rx/$dum=$2;''/eo));
13381    $title = &missing_braces unless (
13382	(s/$next_pair_pr_rx/$title=$2;''/eo)
13383	||(s/$next_pair_rx/$title=$2;''/eo));
13384    $index_names{$key} = $title if ($key && $title);
13385    @_;
13386}
13387
13388# FOOTNOTES , also within Mini-page environments
13389# allow easy way to override and inherit; e.g. for frames
13390
13391sub do_cmd_footnotestar { &do_real_cmd_footnote(@_) }
13392sub do_cmd_footnote { &do_real_cmd_footnote(@_) }
13393sub do_real_cmd_footnote {
13394    local($_) = @_;
13395    local($cnt,$marker,$smark,$emark)=('', $footnote_mark);
13396    local($mark,$dum) = &get_next_optional_argument;
13397    local($anchor_name);
13398
13399    $footfile = "${PREFIX}$FOOT_FILENAME$EXTN"
13400	unless ($footfile||$MINIPAGE||$NO_FOOTNODE);
13401
13402    if ($mark) {
13403	$cnt = $mark;
13404	if ($MINIPAGE) { $global{'mpfootnote'} = $cnt }
13405	else { $global{'footnote'} = $cnt }
13406    } else {
13407	$cnt = (($MINIPAGE)? ++$global{'mpfootnote'} : ++$global{'footnote'});
13408    }
13409    local($br_id, $footnote)=(++$global{'max_id'},'');
13410    $footnote = &missing_braces unless (
13411        (s/$next_pair_pr_rx/${br_id}=$1; $footnote=$2;''/eo)
13412	||(s/$next_pair_rx/${br_id}=$1; $footnote=$2;''/eo));
13413    $br_id = "mp".$br_id if ($MINIPAGE);
13414    $marker = &get_footnote_mark($MINIPAGE);
13415    local($last_word) = &get_last_word();
13416    local($href) = &make_href("$footfile#foot$br_id",$marker);
13417    if ($href =~ /NAME="([^"]*)"/) { $anchor_name=$1 }
13418    $last_word .= $marker unless ($anchor_name);
13419    &process_footnote($footnote,$cnt,$br_id,$last_word,$mark
13420	      ,($MINIPAGE? $marker : '')
13421	      ,($MINIPAGE? '' : "$marker:$anchor_name") );
13422    # this may not work if there is a <BASE> tag and !($file) !!! #
13423#   join('',&make_href("$file#foot$br_id",$marker),$_);
13424    $href . $_
13425}
13426
13427sub process_image_footnote {
13428    # MRO: modified to use $_[0]
13429    # local(*math) = @_;
13430    local($in_image, $keep, $pre, $this_anchor, $out, $foot_counters_recorded, @foot_anchors) = (1,'','');
13431    local($image_contents) = $_[0];
13432    $image_contents =~ s/\\(begin|end)(($O|$OP)\d+($C|$CP))tex2html_\w+\2//go;
13433    $image_contents =~ s!(\\footnote(mark\b\s*(\[[^\]]*\])?|\s*(\[[^\]]*\])?\s*(($O|$OP)\d+($C|$CP))(.*)\5))!
13434	$keep = $`; $out = '\footnotemark '.$3.$4;
13435        #MRO: $*=1; local($saveRS) = $/; $/='';
13436	if ($8) {
13437	    $this_anchor = &do_cmd_footnote($2);
13438	} else {
13439	    $this_anchor = &do_cmd_footnotemark($3);
13440	}
13441        #MRO: $*=0; $/ = $saveRS;
13442	$foot_counters_recorded = 1;
13443	push(@foot_anchors, $this_anchor);
13444	$out!oesg;
13445    $_[0] = $image_contents;
13446    @foot_anchors;
13447}
13448
13449sub do_cmd_thanks { &do_cmd_footnote(@_); }
13450
13451sub get_footnote_mark {
13452    local($mini) = @_;
13453    return($footnote_mark) if ($HTML_VERSION < 3.0 );
13454    local($cmd,$tmp,@tmp,$marker);
13455    $cmd = "the". (($mini)? 'mp' : '') . "footnote";
13456    if ($new_command{$cmd}) {
13457	$tmp = "do_cmd_$cmd";
13458	@tmp = split (':!:', $new_command{$cmd});
13459	pop @tmp; $tmp = pop @tmp;
13460	if ($tmp =~ /$O/) {
13461###	    local($_) = &translate_commands($tmp);
13462	    $marker = &translate_commands(&translate_environments($tmp));
13463	    &make_unique($marker);
13464###	    $marker = $_;
13465	} else { $marker = &translate_commands(&translate_environments($tmp)); }
13466    } elsif ($mini) {
13467    	$marker = &translate_commands('\thempfootnote');
13468    } elsif ((defined &do_cmd_thefootnote)||$new_command{'thefootnote'}) {
13469	local($br_id)=++$global{'max_id'};
13470	$marker = &translate_environments("$O$br_id$C\\thefootnote$O$br_id$C");
13471    } else { $marker = $footnote_mark; }
13472    join('','<SUP>',$marker,'</SUP>');
13473}
13474
13475sub make_numbered_footnotes {
13476    eval "sub do_cmd_thefootnote {\&numbered_footnotes}" }
13477sub numbered_footnotes { &do_cmd_arabic('<<0>>footnote<<0>>');}
13478
13479# default numbering style for minipage notes
13480sub do_cmd_thempfootnote { &do_cmd_arabic('<<0>>mpfootnote<<0>>'); }
13481
13482sub do_cmd_footnotemark { &do_real_cmd_footnotemark(@_) }
13483sub do_real_cmd_footnotemark {
13484    local($_) = @_;
13485    local($br_id, $footnote,$marker,$mpnote,$tmp,$smark,$emark);
13486    # Don't use ()'s for the optional argument!
13487    local($mark,$dum) = &get_next_optional_argument;
13488    local ($cnt,$text_known) = ('','');
13489    if ($mark) {
13490	$cnt = (($mark =~ /\\/)? &translate_commands($mark) : $mark);
13491	if (($MINIPAGE)&&($mpfootnotes{$cnt})) {
13492	    $mpnote = 1;
13493	    $br_id  = $mpfootnotes{$cnt};
13494	    $text_known = 1;
13495	} else {
13496	    $global{'footnote'} = $cnt;
13497	    local($tmp) = $footnotes{$cnt};
13498	    if ($tmp) {
13499		$br_id  = $tmp;
13500		$text_known = 1;
13501	    } else { $footnotes{$cnt} = $br_id }
13502	}
13503    } else {
13504	$cnt = ++$global{'footnote'};
13505	$text_known = 1 if ($footnotes{$cnt});
13506    }
13507    if ($text_known) {
13508	$br_id = ($MINIPAGE ? $mpfootnotes{$cnt} : $footnotes{$cnt});
13509	$marker = &get_footnote_mark($mpnote);
13510	return (join('', &make_href("$footfile#foot$br_id",$marker),$_));
13511    }
13512
13513    local($last_word) = &get_last_word() unless ($mpnote);
13514
13515    # Try to find a  \footnotetext  further on.
13516    do {
13517	if (s/\\footnotetext\s*\[\s*$cnt\s*]*\]\s*$any_next_pair_pr_rx//o) {
13518	    ($br_id, $footnote) = ($2, $3);
13519	} else {
13520	    $br_id = "fnm$cnt";
13521	    $footnotes{$cnt} = $br_id;
13522	}
13523    } unless ($br_id);
13524
13525    $marker = &get_footnote_mark($mpnote);
13526    $last_word .= $marker unless ($marker =~ /$footnote_mark/ );
13527    if ($footnote) {
13528	# found a  \footnotetext  further on
13529	&process_footnote($footnote,$cnt,$br_id,$last_word,$mark);
13530	join('',&make_named_href("foot$br_id","$footfile#$br_id",$marker),$_);
13531    } elsif ($br_id =~ /fnm/) {
13532	# no  \footnotetext  yet, so make the entry in $footnotes
13533	&process_footnote('',$cnt,$br_id,$last_word,$mark);
13534	# this may not work if there is a <BASE> tag and !($footfile) !!! #
13535	join('',&make_named_href("foot$br_id","$footfile#$br_id",$marker),$_);
13536    } elsif ($br_id) {
13537	# \footnotetext  already processed
13538	if ($mpnote) {
13539	    $mpfootnotes =~ s/(=\"$br_id\">...)(<\/A>)/$1$last_word$3/
13540		if ($last_word);
13541	    # this may not work if there is a <BASE> tag !!! #
13542	    join('',&make_named_href("foot$br_id","#$br_id",$marker),$_);
13543	} else {
13544	    $footnotes =~ s/(=\"$br_id\">...)(<\/A>)/$1$last_word$3/;
13545	    # this may not work if there is a <BASE> tag and !($footfile) !!! #
13546	    join(''
13547		,&make_named_href("foot$br_id","$footfile#$br_id",$marker),$_);
13548	}
13549    } else {
13550	print "\nCannot find \\footnotetext for \\footnotemark $cnt";
13551	# this may not work if there is a <BASE> tag and !($footfile) !!! #
13552	join('',&make_named_href("foot$br_id","$footfile",$marker),$_);
13553    }
13554}
13555
13556# Under normal circumstances this is never executed. Any commands \footnotetext
13557# should have been processed when the corresponding \footnotemark was
13558# encountered. It is possible however that when processing pieces of text
13559# out of context (e.g. \footnotemarks in figure and table captions)
13560# the pair of commands gets separated. Until this is fixed properly,
13561# this command just puts the footnote in the footnote file in the hope
13562# that its context will be obvious ....
13563sub do_cmd_footnotetext {
13564    local($_) = @_;
13565    local($mark,$dum) = &get_next_optional_argument;
13566    local($br_id, $footnote, $prev, $key)=(1,'','','');
13567    $footnote = &missing_braces unless (
13568	(s/$next_pair_pr_rx/($br_id,$footnote)=($1,$2);''/eo)
13569	||(s/$next_pair_rx/($br_id,$footnote)=($1,$2);''/eo));
13570
13571    $mark = $global{'footnote'} unless $mark;
13572    $prev = $footnotes{$mark};
13573    if ($prev) {
13574	$prev = ($MINIPAGE ? 'mp' : '') . $prev;
13575	# first prepare the footnote-text
13576	$footnote = &translate_environments("${OP}$br_id$CP$footnote${OP}$br_id$CP")
13577            if ($footnote);
13578	$footnote = &translate_commands($footnote) if ($footnote =~ /\\/);
13579
13580	# now merge it onto the Footnotes page
13581	$footnotes =~ s/(=\"$prev\">\.\.\.)(.*<\/A>)(<\/DT>\n<DD>)\n/
13582		$1.'<html_this_mark>'.$3.$footnote/e;
13583	local($this_mark) = $2;
13584	$this_mark =~ s|(<SUP>)(?:<#\d+#>)?(\d+)(?:<#\d+#>)?(<\/SUP>)(<\/A>)$|
13585		"$4<A\n HREF=\"$CURRENT_FILE\#foot$prev\">$1$2$3$4"|e;
13586	$footnotes =~ s/<html_this_mark>/$this_mark/;
13587    } else {
13588	&process_footnote($footnote,$mark,$br_id,'','') if $footnote;
13589    }
13590    $_;
13591}
13592
13593
13594sub process_footnote {
13595    # Uses $before
13596    # Sets $footfile defined in translate
13597    # Modifies $footnotes defined in translate
13598    local($footnote, $cnt, $br_id, $last_word, $mark, $mini, $same_page) = @_;
13599    local($target) = $target;
13600
13601    # first prepare the footnote-text
13602    local($br_idd, $fcnt); $br_id =~ /\D*(\d+)/; $br_idd = $1;
13603    $footnote = &translate_environments("$O$br_idd$C$footnote$O$br_idd$C")
13604	if ($footnote);
13605    $footnote = &translate_commands($footnote) if ($footnote =~ /\\/);
13606
13607    local($space,$sfoot_style,$efoot_style) = ("\n",'','');
13608    if ((!$NO_FOOTNODE)&&(!$mini)&&(!$target)) {
13609	$footfile = "${PREFIX}$FOOT_FILENAME$EXTN";
13610	$space = ".\n" x 30;
13611	$space = "\n<PRE>$space</PRE>";
13612    } elsif ($target) {
13613	$target = $frame_body_name
13614	    if (($frame_body_name)&&($target eq $frame_foot_name));
13615	$sfoot_style = '<SMALL>';
13616	$efoot_style = '</SMALL>';
13617    }
13618
13619    if ($mark) {
13620	if ($mini) {
13621	    $cnt = $mpfootnotes{$mark};
13622	    if ($in_image) {
13623		$fcnt = $global{'mpfootnote'}; --$fcnt if $fcnt;
13624		$latex_body .= '\setcounter{mpfootnote}{'.($fcnt||"0")."}\n"
13625		    unless ($foot_counters_recorded);
13626	    }
13627	} else {
13628	    $cnt = $footnotes{$mark};
13629	    if ($in_image) {
13630		$fcnt = $global{'footnote'}; --$fcnt if $fcnt;
13631		$latex_body .= '\setcounter{footnote}{'.($fcnt||"0")."}\n"
13632		    unless ($foot_counters_recorded);
13633	    }
13634	}
13635	if ($cnt) {
13636	    &write_warnings("\nredefined target for footnote $mark" )
13637		unless ( $cnt eq $br_id )
13638	}
13639	if ($mini) { $mpfootnotes{$mark} = "$br_id" }
13640	elsif ($br_id =~ /fnm\d+/) {
13641	    $mark = "$footnotes{$cnt}";
13642	    $footnotes{$cnt} = "$br_id";
13643#	    $footnotes .= "\n<DT>$sfoot_style<A NAME=\"foot$br_id\">..."
13644	    $footnotes .= "\n<DT>$sfoot_style<A NAME=\"$br_id\">..."
13645		. $last_word . "</A>$efoot_style</DT>\n<DD>\n"
13646		. $space . "\n</DD>";
13647	    return;
13648	} else { $footnotes{$mark} = "$br_id" }
13649    } else {
13650	if ($mini) {
13651	    $mpfootnotes{$cnt} = "$br_id";
13652	    if ($in_image) {
13653		$fcnt = $global{'mpfootnote'}; --$fcnt if $fcnt;
13654		$latex_body .= '\setcounter{mpfootnote}{'.($fcnt||"0")."}\n"
13655		    unless ($foot_counters_recorded);
13656	    }
13657	} else {
13658	    $footnotes{$cnt} = "$br_id";
13659	    if ($in_image) {
13660		$fcnt = $global{'footnote'}; --$fcnt if $fcnt;
13661		$latex_body .= '\setcounter{footnote}{'.($fcnt||"0")."}\n"
13662		    unless ($foot_counters_recorded);
13663	    }
13664	}
13665    }
13666
13667    # catch a \footnotemark *after* the \footnotetext
13668    if ((!$footnote)&&($last_word)&&(!$mini)) {
13669#	$footnotes .= "\n<DT>$sfoot_style<A NAME=\"foot$br_id\">..."
13670	$footnotes .= "\n<DT>$sfoot_style<A NAME=\"$br_id\">..."
13671	    . $last_word
13672	    . "</A>$efoot_style</DT>\n<DD>\n" . $space . "\n</DD>";
13673
13674    } elsif ($mini) {
13675	if ($HTML_VERSION < 3.0) { $mini .= "." }
13676	$mpfootnotes .= "\n<DD>$sfoot_style<A NAME=\"foot$br_id\">$mini</A> " .
13677	    $footnote . $efoot_style . "\n</DD>\n";
13678    } elsif ($same_page) {
13679	local($link,$text);
13680	$same_page =~ s/:/$text=$`;$link=$';''/e;
13681	$same_page = &make_named_href("","$CURRENT_FILE\#$link",$text) if($link);
13682	$footnotes .= "\n<DT>$sfoot_style<A NAME=\"foot$br_id\">...$last_word</A>"
13683	    . $same_page . $efoot_style . "</DT>\n<DD>" . $sfoot_style
13684	    . $footnote . $efoot_style . "\n". $space . "\n</DD>";
13685    } else {
13686	$footnotes .= "\n<DT>$sfoot_style<A NAME=\"foot$br_id\">...$last_word</A>"
13687		. $efoot_style . "</DT>\n<DD>" . $sfoot_style
13688		. $footnote . "$efoot_style\n" . $space . "\n</DD>";
13689    }
13690}
13691
13692
13693sub do_cmd_appendix {
13694    $latex_body .= "\\appendix\n";
13695    if ($section_commands{$outermost_level} == 3) {
13696	$global{'section'} = 0;
13697	&reset_dependents('section');
13698	eval "sub do_cmd_thesection{ &do_cmd_the_appendix(3,\@_) }";
13699    } else {
13700	$global{'chapter'} = 0;
13701	&reset_dependents('chapter');
13702	eval "sub do_cmd_thechapter{ &do_cmd_the_appendix(2,\@_) }";
13703    }
13704    $_[0];
13705}
13706
13707sub do_cmd_the_appendix {
13708    local($val,$level) = (0,$_[0]);
13709    if ($level == 3) { $val=$global{'section'} }
13710    elsif ($level == 2) { $val=$global{'chapter'} }
13711    join('', &fAlph($val), '.', $_[1]);
13712}
13713
13714sub do_cmd_appendixname { $app_title . $_[0] }
13715sub do_cmd_abstractname { $abs_title . $_[0] }
13716sub do_cmd_keywordsname { $key_title . $_[0] }
13717sub do_cmd_subjclassname { $sbj_title . $_[0] }
13718sub do_cmd_indexname { $idx_title . $_[0] }
13719sub do_cmd_contentsname { $toc_title . $_[0] }
13720sub do_cmd_datename { $date_name . $_[0] }
13721sub do_cmd_refname { $ref_title . $_[0] }
13722sub do_cmd_bibname { $bib_title . $_[0] }
13723sub do_cmd_figurename { $fig_name . $_[0] }
13724sub do_cmd_listfigurename { $lof_title . $_[0] }
13725sub do_cmd_tablename { $tab_name . $_[0] }
13726sub do_cmd_listtablename { $lot_title . $_[0] }
13727sub do_cmd_partname { $part_name . $_[0] }
13728sub do_cmd_chaptername { $chapter_name . $_[0] }
13729sub do_cmd_sectionname { $section_name . $_[0] }
13730sub do_cmd_subsectionname { $subsection_name . $_[0] }
13731sub do_cmd_subsubsectionname { $subsubsection_name . $_[0] }
13732sub do_cmd_paragraphname { $paragraph_name . $_[0] }
13733sub do_cmd_thmname { $thm_title . $_[0] }
13734sub do_cmd_proofname { $prf_name . $_[0] }
13735sub do_cmd_footnotename { $foot_title . $_[0] }
13736sub do_cmd_childlinksname { '<STRONG>'.$child_name.'</STRONG>'. $_[0] }
13737sub do_cmd_infopagename { $info_title . $_[0] }
13738
13739
13740sub do_cmd_ref {
13741    local($_) = @_;
13742    &process_ref($cross_ref_mark,$cross_ref_mark);
13743}
13744
13745sub do_cmd_eqref {
13746    local($_) = @_;
13747    join('','(',&process_ref($cross_ref_mark,$cross_ref_mark,'',')'));
13748}
13749
13750sub do_cmd_pageref {
13751    local($_) = @_;
13752    &process_ref($cross_ref_mark,$cross_ref_visible_mark);
13753}
13754
13755# This is used by external style files ...
13756sub process_ref {
13757    local($ref_mark, $visible_mark, $use_label, $after_label) = @_;
13758    $use_label = &balance_inner_tags($use_label)
13759	if $use_label =~ (/<\/([A-Z]+)>($math_verbatim_rx.*)<\1>/);
13760    $use_label = &translate_environments($use_label);
13761    $use_label = &simplify(&translate_commands($use_label))
13762	if ($use_label =~ /\\/ );
13763    local($label,$id);
13764    local($pretag) = &get_next_optional_argument;
13765    $pretag = &translate_commands($pretag) if ($pretag =~ /\\/);
13766    $label = &missing_braces unless (
13767	(s/$next_pair_pr_rx/($id, $label) = ($1, $2);''/eo)
13768	||(s/$next_pair_rx/($id, $label) = ($1, $2);''/eo));
13769    if ($label) {
13770	$label =~ s/<[^>]*>//go ; #RRM: Remove any HTML tags
13771	$label =~ s/$label_rx/_/g;	# replace non alphanumeric characters
13772
13773	$symbolic_labels{"$pretag$label$id"} = $use_label if ($use_label);
13774	if (($symbolic_labels{$pretag.$label})&&!($use_label)) {
13775	    $use_label = $symbolic_labels{$pretag.$label}
13776	}
13777#	if (!($use_label eq $label)) {
13778#	    $symbolic_labels{"$label$id"} = $use_label;
13779#	};
13780     	# if $use_label is empty then $label is used as the cross_ref_mark
13781	# elseif $use_label is a string then $use_label is used
13782        # else the usual mark will be used
13783	$use_label = ( (!$use_label && $label) || $use_label);
13784
13785	print "\nLINK: $ref_mark\#$label\#$id  :$use_label:" if ($VERBOSITY > 3);
13786	# The quotes around the HREF are inserted later
13787	join('',"<A HREF=$ref_mark#$label#$id>$visible_mark<\/A>",$after_label, $_);
13788    }
13789    else {
13790	print "Cannot find label argument after <$last_word>\n" if $last_word;
13791	$after_label . $_;
13792    }
13793}
13794
13795#RRM:  This removes unbalanced tags, due to closures for math inside
13796#      the label-text for an <A> anchor.
13797sub balance_inner_tags {
13798    local($text) = @_;
13799    return($text) unless ($text =~ /<\/([A-Z]+)>(\s*$math_verbatim_rx.*)(<\1( [^>]*)?>)/);
13800    local($beforeT,$afterT,$tag,$math_verb,$stag) = ($`,$',$1,$2,$3);
13801    if (!($beforeT =~ /<$tag>/)) {
13802	$text = join('', $beforeT, $math_verb, $afterT);
13803	return (&balance_inner_tags($text));
13804    }
13805    local(@pieces) = split (/<$tag>/, $beforeT );
13806    $beforeT = shift (@pieces);
13807    local($cnt,$this) = (0,'');
13808    while (@pieces) {
13809	$this = shift @pieces;
13810	$cnt++;
13811	$beforeT .= "<$tag>".$this;
13812	$cnt = $cnt - ($this =~ /<\/$tag>/g);
13813    }
13814    if ($cnt) {
13815	$beforeT .= "<\/$tag>" . $math_verb . $stag;
13816	$text = $beforeT . $afterT;
13817    } else {
13818	$beforeT .= $math_verb;
13819	$text = join('', $beforeT, $math_verb, $afterT);
13820	return (&balance_inner_tags($text));
13821    }
13822    $text;
13823}
13824
13825# Uses $CURRENT_FILE defined in translate
13826sub do_cmd_label {
13827    local($_) = @_;
13828    local($label);
13829    $label = &missing_braces unless (
13830	(s/$next_pair_pr_rx\n?/$label = $2;''/eo)
13831	||(s/$next_pair_rx\n?/$label = $2;''/eo));
13832    &anchor_label($label,$CURRENT_FILE,$_);
13833}
13834
13835# This subroutine is also used to process labels in undefined environments
13836sub anchor_label { &real_anchor_label(@_) }
13837sub real_anchor_label {
13838    # Modifies entries in %ref_files defined in translate
13839    local($label,$filename,$context) = @_;
13840    $label =~ s/<[^>]*>//go;	#RRM: Remove any HTML tags
13841    $label =~ s/$label_rx/_/g;	# replace non alphanumeric characters
13842    # Associate the label with the current file
13843    if ($ref_files{$label} ne $filename) {
13844	$ref_files{$label} = $filename;
13845	$noresave{$label} = 0; $changed = 1; }
13846    print "<LABEL: $label>" if ($VERBOSITY > 3);
13847    join('',"<A NAME=\"$label\">$anchor_mark</A>",$context);
13848}
13849
13850sub do_cmd_cite {
13851    local($_) = @_;
13852    &process_cite('','');
13853}
13854
13855
13856# This just creates a link from a label (yet to be determined) to the
13857# cite_key in the citation file.
13858sub process_cite { &process_real_cite(@_) }
13859sub process_real_cite {
13860    local($mode,$text) = @_;
13861    my $has_text = (($text)? 1 : 0);
13862#    local($target) = 'contents';print "\nCITE:$text";
13863    # process the text from \htmlcite or \hypercite
13864    if ($has_text) {
13865	$text = &balance_inner_tags($text)
13866	    if $use_label =~ (/<\/([A-Z]+)>($math_verbatim_rx.*)<\1>/);
13867	$text = &translate_environments($text);
13868	$text = &simplify(&translate_commands($text))
13869	    if ($use_label =~ /\\/ );
13870    }
13871
13872    my $label, $cite_key, $pretag, @cite_keys;
13873    local($optional_text,$dummy) =  &get_next_optional_argument;
13874    if ($mode =~ /external/) {
13875#	$target = '';
13876	$pretag = $optional_text; $optional_text = '';
13877	$pretag = &translate_commands($pretag) if ($pretag =~ /\\/);
13878    } else {
13879	$optional_text = ", $optional_text" if $optional_text;
13880    }
13881    s/^\s*\\space//o;		# Hack - \space is inserted in .aux
13882    s/$next_pair_pr_rx//o||s/$next_pair_rx//o;
13883    if (!($cite_key = $2)) {
13884	print "\n *** Cannot find citation argument\n";
13885	return ($_);
13886    }
13887    @cite_keys = (split(/,/,$cite_key));
13888    my ($citations, $join) = ('',',');
13889    $join  = '' if ($text);
13890    foreach $cite_key (@cite_keys) {
13891	$cite_key =~ s/(^\s+|\s+$)//g;
13892	$cite_key =~ s/(^\s+|\s+$)//g;
13893    # RRM:  if the URL and printable-key are known already, then use them...
13894	$cite_key =~ s/$label_rx/_/g;
13895	$label = $cite_key;
13896	if ($mode eq "nocite") {
13897	    # nothing more to do, no citations
13898	} elsif ( ($SEGMENT) && ($cite_info{$cite_key})
13899		&& ($ref_files{"cite_$cite_key"}) ) {
13900	    $join  = "," unless ($text);
13901	    $text = $cite_info{$cite_key} unless ($text);
13902	    $citations .= join('', $join
13903		, &make_named_href($label,$ref_files{'cite_'."$cite_key"},$text));
13904	} elsif (($mode eq "external")&&($external_labels{$pretag."cite_$cite_key"})) {
13905	    $join  = "," unless ($text);
13906	    $text = $cross_ref_visible_mark unless ($text);
13907	    $citations .= join('', $join
13908		, &make_named_href($label
13909		    , $external_labels{$pretag.'cite_'."$cite_key"}."\#$label"
13910		    , $text)
13911		);
13912	} elsif ($mode eq 'external') {
13913	    $join  = "," unless ($text);
13914	    &write_warnings("\nExternal reference missing for citation: $pretag$cite_key");
13915	    $citations .= "$text$join#!$pretag$cite_key!#";
13916        } else {
13917	    $join  = "," unless ($text);
13918	    #Replace the key...
13919	    $citations .= "$join#$cite_key#$cite_mark#$bbl_nr#$text#$cite_mark#";
13920        }
13921	$text = '';
13922    }
13923    $citations =~ s/^\s*,\s*//;
13924    if ($has_text) { join('', $citations,  $optional_text, $_) }
13925    else { join('', "[", $citations,  $optional_text, "]", $_) }
13926}
13927
13928sub do_cmd_index { &do_real_index(@_) }
13929sub do_real_index {
13930    local($_) = @_;
13931    local($br_id, $str);
13932    local($idx_option) = &get_next_optional_argument;
13933    $str = &missing_braces unless (
13934	(s/$next_pair_pr_rx/($br_id, $str) = ($1, $2);''/eo)
13935	||(s/$next_pair_rx/($br_id, $str) = ($1, $2);''/eo));
13936    join('',&make_index_entry($br_id,$str),$_);
13937}
13938sub do_cmd_indexstar { &do_cmd_index(@_) }
13939
13940# RRM: \bibcite supplies info via the .aux file; necessary with segmented docs.
13941sub do_cmd_bibcite {
13942    local($_) = @_;
13943    local($br_id, $cite_key,$print_key);
13944    $cite_key = &missing_braces unless (
13945	(s/$next_pair_pr_rx/($br_id, $cite_key) = ($1, $2);''/eo)
13946	||(s/$next_pair_rx/($br_id, $cite_key) = ($1, $2);''/eo));
13947    $print_key = &missing_braces unless (
13948	(s/$next_pair_pr_rx/($br_id, $print_key) = ($1, $2);''/eo)
13949	||(s/$next_pair_rx/($br_id, $print_key) = ($1, $2);''/eo));
13950    $cite_key =~ s/$label_rx/_/g;
13951    $cite_info{$cite_key} = $print_key;
13952    $_;
13953}
13954
13955# This command will only be encountered inside a thebibliography environment.
13956sub do_cmd_bibitem { &do_real_bibitem($CURRENT_FILE, @_) }
13957sub do_real_bibitem {
13958    local($thisfile, $_) = @_;
13959    # The square brackets may contain the label to be printed
13960    local($label, $dummy) = &get_next_optional_argument;
13961    # Support for the "named" bibliography style
13962    if ($label) {
13963 	$label =~ s/\\protect//g;
13964 	$label = &translate_commands($label) if ($label =~ /\\/);
13965    }
13966    local($cite_key);
13967    $cite_key = &missing_braces unless (
13968	( s/$next_pair_pr_rx/$cite_key=$2;''/e )
13969	||( s/$next_pair_rx/$cite_key=$2;''/e ));
13970
13971    $cite_key =~ s/$label_rx/_/g;
13972    $label = $cite_info{$cite_key} unless $label; # read from .aux file
13973    $label = ++$bibitem_counter unless $label; # Numerical labels
13974
13975    if ($cite_key) {
13976	# Associate the cite_key with the printed label.
13977	# The printed label will be substituted back into the document later.
13978	$cite_info{$cite_key} = &translate_commands($label);
13979	if (!($ref_files{'cite_'."$cite_key"} eq $thisfile)) {
13980	    $ref_files{'cite_'."$cite_key"} = $thisfile;
13981	    $changed = 1; }
13982
13983        #RRM: apply any special styles, as defined below
13984	$label = &bibitem_style($label) if (defined &bibitem_style);
13985	# Create an anchor around the citation
13986	join('',"<P></P><DT><A NAME=\"$cite_key\">$label</A>\n<DD>", $_);
13987
13988    } else {
13989	print "Cannot find bibitem labels: $label\n";
13990
13991	#RRM: apply any special styles, as defined below
13992	$label = &bibitem_style($label) if (defined &bibitem_style);
13993	join('',"<P></P><DT>$label\n<DD>", $_); # AFEB added this line
13994    }
13995}
13996
13997#RRM: override this with a personal style, defined in  .latex2html-init
13998#sub bibitem_style { join('','<STRONG>',$_[0],'</STRONG>') }
13999sub bibitem_style {
14000    return ($_[0]) unless $BIBITEM_STYLE;
14001    local($text) = join(''
14002	,"${O}0$C",$BIBITEM_STYLE,"${O}1$C", @_, "${O}1$C","${O}0$C");
14003    $text = &translate_environments($text);
14004    &translate_commands($text);
14005}
14006
14007sub do_cmd_newblock {
14008    "<BR>".$_[0]
14009}
14010
14011# This just reads in the $FILE.bbl file if it is available and appends
14012# it to the items that are still to be processed.
14013# The $FILE.bbl should contain a thebibliography environment which will
14014# cause its contents to be processed later in the appropriate way.
14015# (Note that it might be possible for both the \bibliography command and
14016# the thebibliography environment to be present as the former may have been
14017# added by the translator as a sectioning command. In this case (both present)
14018# the $citefile would have already been set by the thebibliography environment)
14019
14020sub do_cmd_bibliography { &do_real_bibliography($CURRENT_FILE, @_) }
14021sub do_real_bibliography {
14022    local($thisfile, $after) = @_;
14023    if ((defined &do_cmd_bibname)||$new_command{'bibname'}) {
14024	local($br_id)=++$global{'max_id'};
14025	$TITLE = &translate_environments("$O$br_id$C\\bibname$O$br_id$C");
14026    } else { $TITLE = $bib_title }
14027    $toc_sec_title = $TITLE;
14028    return($_[0]) if ($making_name);
14029    local($bibfile);
14030    $bibfile = &missing_braces unless (
14031	($after =~ s/$next_pair_rx/$bibfile=$2;''/eo)||
14032	($after =~ s/$next_pair_rx_rx/$bibfile=$2;''/eo));
14033
14034    do {
14035	unless ($citefile) {
14036	    $citefile = $thisfile;
14037	    if (&process_ext_file("bbl")) { # *** BINDS $_ as a side effect ***
14038		$after = join('',$_,$after);}
14039	    else {
14040		print "\nCannot open $FILE.bbl $!\n";
14041		&write_warnings("\nThe bibliography file was not found.");
14042		$after = join('',"\n<H2>No References!</H2>", $after);
14043	    }
14044	}
14045    print "\n";
14046    } if $bibfile;
14047    $after;
14048}
14049
14050# allow for customised info-pages, for different languages
14051sub do_cmd_textohtmlinfopage {
14052    local($_) = @_;
14053    local($linfo) = $TITLES_LANGUAGE . '_infopage';
14054    if (defined &$linfo) { eval "&$linfo"; }
14055    else { &default_textohtmlinfopage }
14056}
14057
14058sub default_textohtmlinfopage {
14059    local($_) = @_;
14060    local($argv) = $argv;
14061    if (-f "../$argv") { $argv = &make_href ("../$argv", $argv, ); }
14062    $_ = ($INFO && $INFO =~ /^\d+$/
14063      ? join('', $close_all
14064	, "<STRONG>$t_title</STRONG><P>\nThis document was generated using the\n"
14065	, "<A HREF=\"$TEX2HTMLADDRESS\"><STRONG>LaTeX</STRONG>2<tt>HTML</tt></A>"
14066	, " translator Version $TEX2HTMLVERSION\n"
14067	, "<P>Copyright &#169; 1993, 1994, 1995, 1996,\n"
14068	, "Nikos Drakos, \n"
14069	, "Computer Based Learning Unit, University of Leeds.\n"
14070	, "<BR>Copyright &#169; 1997, 1998, 1999,\n"
14071	, "<A HREF=\"$AUTHORADDRESS2\">Ross Moore</A>, \n"
14072	, "Mathematics Department, Macquarie University, Sydney.\n"
14073	, "<P>The command line arguments were: <BR>\n "
14074	, "<STRONG>latex2html</STRONG> <TT>$argv</TT>\n"
14075	, (($SHOW_INIT_FILE && ($INIT_FILE ne ''))?
14076	   "\n<P>with initialization from: <TT>$INIT_FILE</TT>\n$init_file_mark\n" :'')
14077	, "<P>The translation was initiated by $address_data[0] on $address_data[1]"
14078	, $open_all, $_)
14079      : join('', $close_all, "$INFO\n", $open_all, $_));
14080    $_;
14081}
14082
14083
14084# Try to translate LaTeX vertical space in a number of <BR>'s.
14085# Eg. 1cm results in one + two extra <BR>'s.
14086# To help the browser rendering is quite ugly, but why not.
14087#
14088sub get_vspace {
14089    local($_) = @_;
14090    local($vh) = 0;
14091
14092    return("<BR>") if /-/;
14093
14094    $vh = int($1 * $vspace_12pt{$2} + 0.5)
14095	if (/([0-9.]+)\s*([a-z]+)/);
14096    join('',"<BR>","\n<BR>" x $vh);
14097}
14098
14099sub do_cmd_vskip {
14100    local($_) = @_;
14101    &ignore_numeric_argument;
14102    join('',&get_vspace($1),$_);
14103}
14104
14105sub do_cmd_break {
14106    local($_) = @_;
14107    join('',"<BR>",$_);
14108}
14109
14110sub do_cmd_vspace {
14111    local($_) = @_;
14112    local($how_much);
14113    $how_much = &missing_braces unless (
14114	(s/$next_pair_pr_rx/$how_much = $2;''/e)
14115	||(s/$next_pair_rx/$how_much = $2;''/e));
14116    join('',&get_vspace($how_much),$_);
14117}
14118
14119sub do_cmd_vspacestar {
14120    &do_cmd_vspace;
14121}
14122
14123sub do_cmd_d_backslash {
14124    local($_) = @_;
14125
14126    # Eat space from &pre_process.
14127    # We could also modifiy $single_cmd_rx and %normalize, but why not here.
14128    s/^ \*?//;
14129    local($spc,$dum)=&get_next_optional_argument;
14130    # If the [...] occurs on the next line, then it is *not* an argument to \\ .
14131    # MRO: replaced $* with /m
14132    if ($dum =~ /\n/m) {
14133	$spc = $`;
14134        $spc =~ s/\s//gm;
14135        $_ = $'.$_
14136    }
14137    join('',(($spc)? &get_vspace($spc): "\n<BR>"),$_);
14138}
14139
14140
14141################## Commands used in the $FILE.aux file #######################
14142
14143sub do_cmd_jobname { $FILE . $_[0] }
14144
14145# This is used in $FILE.aux
14146sub do_cmd_newlabel {
14147    local($_) = @_;
14148    local($label,$val,$tmp);
14149    $label = &missing_braces unless (
14150	(s/$next_pair_pr_rx/$label = $2;''/eo)
14151	||(s/$next_pair_rx/$label = $2;''/eo));
14152    $tmp = &missing_braces unless (
14153	(s/$next_pair_pr_rx/$tmp=$2;''/eo)
14154	||(s/$next_pair_rx/$tmp=$2;''/eo));
14155    $val = &missing_braces unless (
14156	($tmp =~ s/$next_pair_pr_rx/$val=$2;''/eo)
14157	||($tmp =~ s/$next_pair_rx/$val=$2;''/eo));
14158    $val =~ s/(^\s+|\s+$)//gs;
14159    $label =~ s/$label_rx/_/g;	# Replace non alphanumeric characters
14160    $latex_labels{$label} = $val;
14161    &do_labels_helper($label);
14162    $_;
14163}
14164sub do_cmd_oldnewlabel { &do_cmd_newlabel(@_) }
14165
14166#
14167# Sets %encoded_(section|figure|table)_number, which maps encoded
14168# section titles to LaTeX numbers
14169# .= \$number . \"$;\"";
14170sub do_cmd_oldcontentsline { &do_cmd_contentsline(@_) }
14171sub do_cmd_contentsline {
14172    local($_) = @_;
14173    local($arg,$after,$title,$number,$hash,$stype,$page);
14174    # The form of the expression is:
14175    # \contentsline{SECTION} {... {SECTION_NUMBER} TITLE}{PAGE}
14176    $stype = &missing_braces unless (
14177        (s/$next_pair_pr_rx/$stype = $2;''/e)
14178        ||(s/$next_pair_rx/$stype = $2;''/e));
14179    $arg = &missing_braces unless (
14180        (s/$next_pair_pr_rx/$arg = $2;''/e)
14181        ||(s/$next_pair_rx/$arg = $2;''/e));
14182    $page = &missing_braces unless (
14183        (s/$next_pair_pr_rx/$page = $2;''/e)
14184        ||(s/$next_pair_rx/$page = $2;''/e));
14185
14186#    s/$any_next_pair_pr_rx/$stype = $2;''/eo; # Chop off {SECTION}
14187#    s/$any_next_pair_pr_rx/$arg   = $2;''/eo; # Get {... {SECTION_NUMBER} TITLE}
14188#    s/$any_next_pair_pr_rx/$page  = $2;''/eo; # Get page number
14189    $hash = $stype if (($stype =~ /^(figure|table)$/)||($SHOW_SECTION_NUMBERS));
14190    $hash =~ s/(sub)*(section|chapter|part)/section/;
14191    $after = $_;
14192    if ($hash) {
14193	if ($arg =~ /^$OP/) {
14194	    $number = &missing_braces unless (
14195		($arg =~ s/$next_pair_pr_rx/$number = $2;''/eo)
14196		||($arg =~ s/$next_pair_rx/$number = $2;''/eo));
14197	}
14198	if ($stype eq "part") {
14199 	    while ($arg =~ s/$next_pair_pr_rx//o) {};
14200  	    $number =~ tr/a-z/A-Z/;
14201   	    $number = "Part $number:"}
14202	# This cause problem when picking figure numbers...
14203	# while ($tmp =~ s/$next_pair_pr_rx//o) {};
14204	$number = -1 unless $number;
14205#JCL(jcl-tcl)
14206##	$_ = $arg;
14207#	$title = &sanitize($arg);
14208##	&text_cleanup;
14209##	$title = &encode_title($_);
14210##
14211	#remove surrounding brace-numbering
14212	$arg =~ s/^($O|$OP)\d+($C|$CP)|($O|$OP)\d+($C|$CP)$//g;
14213	$arg =~ s/\\footnote(mark|text)?//g;
14214	# \caption arguments should have had environments translated already
14215	$arg = &translate_environments($arg) if ($arg =~ /\\begin/);
14216	#replace image-markers by the image params
14217	$arg =~ s/$image_mark\#([^\#]+)\#/&purify_caption($1)/e;
14218
14219	#RRM: resolve any embedded cross-references first
14220	local($checking_caption) = 1;
14221	$title = &simplify($arg);
14222	$title = &sanitize($title);
14223	$checking_caption = '';
14224	eval "\$encoded_${hash}_number{\$title} .= \$number . \"$;\"";
14225    }
14226    $after;
14227}
14228
14229#
14230#  Before normalizing this was \@input.  Used in .aux files.
14231#
14232sub do_cmd__at_input {
14233    local ($_) = @_;
14234    local ($file, $after);
14235    $file = &missing_braces unless (
14236	(s/$next_pair_pr_rx/$file=$2;''/eo)
14237	||(s/$next_pair_rx/$file=$2;''/eo));
14238    local($prefix, $suffix) = split(/\./, $file);
14239    $after = $_;
14240    local($EXTERNAL_FILE) = $prefix;
14241    &process_ext_file($suffix);
14242    $after;
14243}
14244
14245
14246########################### Counter Commands #################################
14247# Removes the definition from the input string, adds to the preamble
14248# and stores the body in %new_counter;
14249sub get_body_newcounter {
14250#    local(*_) = @_;
14251    local($after_R) = @_;
14252    local($_) = $$after_R;
14253    local($within,$ctr,$cmd,$tmp,$body,$pat);
14254    local($new_ctr) = 'counter';
14255    ($ctr,$pat) = &get_next(1);	# Get counter name
14256    &write_warnings ("\n*** LaTeX Error: backslash found in counter-name: $ctr")
14257	if ($pat =~ s/\\//);
14258    $ctr =~ s/^\s*\\//;
14259    $new_ctr .= $pat;
14260
14261    ($within,$pat) = &get_next(0);	# Get optional within, currently ignored
14262    &addto_dependents($within,$ctr);
14263    $new_ctr .= $pat;
14264    do {
14265###	local($_) = "\\arabic<<1>>$ctr<<1>>";
14266	$body = "\\arabic<<1>>$ctr<<1>>";
14267	&make_unique($body);
14268	$cmd = "the$ctr";
14269	$tmp = "do_cmd_$cmd";
14270	$new_command{$cmd} = join(':!:',0,$body,'}') unless (defined &$tmp);
14271	    &write_mydb("new_command", $cmd, $new_command{$cmd});
14272	undef $body;
14273    };
14274    &do_body_newcounter($ctr);
14275
14276    $$after_R = $_;
14277    if (!$PREAMBLE) {
14278	my $new_cmd = join(''
14279	    , "counter{$ctr}", ($within ? "[$within]" : '') );
14280	&add_to_preamble('counter','\\new'.$new_cmd);
14281	return ();
14282    }
14283    'newed'.$new_ctr;
14284}
14285
14286sub do_body_newcounter {
14287    local($ctr) = @_;
14288    $latex_body .= &revert_to_raw_tex("\\newcounter{$ctr}\n")
14289	unless ($preamble =~ /\\new(counter|theorem){$ctr}/);
14290    $global{$ctr} = 0;
14291    &process_commands_wrap_deferred("the$ctr ");
14292    $_;
14293}
14294
14295
14296#RRM: This doesn't work properly yet.
14297#     The new booleans need to be stored for use in all partitions.
14298#     \if... \else  \fi  is not yet implemented.
14299
14300sub get_body_newboolean {
14301#    local(*_) = @_;
14302    local($after_R) = @_;
14303    local($_) = $$after_R;
14304    my $bool;
14305    $bool = &missing_braces unless (
14306	(s/$next_pair_pr_rx/$bool=$2;''/e)
14307	||(s/$next_pair_rx/$bool=$2;''/e));
14308    $bool =  &process_body_newif('',$bool);
14309    $$after_R = $_;
14310    'newed'.$bool;
14311}
14312
14313sub get_body_newif {
14314#    local(*_) = @_;
14315    local($after_R) = @_;
14316    local($_) = $$after_R;
14317    local($bool);
14318    if (!(s/^\s*\\if([a-zA-Z]+)//)) {
14319	$$after_R = $_;
14320	return();
14321    }
14322    $bool = $1;
14323    $$after_R = $_;
14324    join('','newed', &process_body_newif('', $bool));
14325}
14326
14327
14328sub process_body_newif {
14329    local($texif, $bool) = @_;
14330    local($body,$ifbool,$cmd,$tmp,$pat);
14331
14332#    ($bool,$pat) = &get_next(1);	# Get boolean name
14333
14334#    # change the brace-type around the command-name
14335#    $pat =~ s/$O/$OP/; $pat =~ s/$C/$CP/; $new_cmd .= $pat;
14336
14337    $ifbool = "if".$bool;
14338    $global{$ifbool} = 0;
14339
14340    do {
14341	$body = "\$global{'$ifbool'} = 1;";
14342	$cmd = $bool."true";
14343	$code = "sub do_cmd_$cmd { ".$body." \$_[0];}";
14344	eval $code;
14345	print STDERR "\n*** sub do_cmd_$cmd failed:\n$@\n" if ($@);
14346	$raw_arg_cmds{$cmd} = 1;
14347
14348	$body = "\$global{$ifbool} = 0;";
14349	$cmd = $bool."false";
14350	$code = "sub do_cmd_$cmd { ".$body." \$_[0];}";
14351	eval $code;
14352	print STDERR "\n*** sub do_cmd_$cmd failed:\n$@\n" if ($@);
14353	$raw_arg_cmds{$cmd} = 1;
14354
14355	undef $body;
14356    };
14357    &process_commands_wrap_deferred("${bool}true\n${bool}false\nif$bool\n");
14358
14359#    $latex_body .= &revert_to_raw_tex("\\newif\\$ifbool\n")
14360#	unless ($preamble =~ /\\newif\s*\\$ifbool/);
14361
14362    if (!$PREAMBLE) {
14363	local($new_cmd) = "boolean{\\$bool}";
14364	&add_to_preamble ('newif', "\\new$new_cmd" );
14365	return ();
14366    }
14367    local($br_id) = ++$global{'max_id'};
14368    'boolean'."$O$br_id$C$bool$O$br_id$C";
14369}
14370
14371
14372sub do_cmd_value {
14373    local($_) = @_;
14374    local($ctr,$val);
14375    $ctr = &missing_braces
14376	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
14377	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
14378    $val = &get_counter_value($ctr);
14379    if ($val) { $val.$_ }
14380    else { join(''," 0",$_) }
14381}
14382
14383sub do_cmd_boolean {
14384    local($_) = @_;
14385    local($bool,$val);
14386    $bool = &missing_braces
14387	unless ((s/$next_pair_pr_rx/$bool = $2;''/eo)
14388	      ||(s/$next_pair_rx/$bool = $2;''/eo));
14389    $val = &get_boolean_value($bool);
14390    if ($val) { $val.$_ }
14391    else { "0".$_ }
14392}
14393
14394sub get_counter_value {
14395    local($ctr) = @_;
14396    local($val,$index);
14397    $ctr = 'eqn_number' if ($ctr eq "equation");
14398    $index = $section_commands{$ctr};
14399
14400    if (defined $global{$ctr}) { $val= $global{$ctr}; }
14401    elsif (($SEGMENT)&&($index)) {
14402	$val = $segment_sec_id[$index]
14403#    if ($index) {
14404#	if ($SEGMENT) { $val = $segment_sec_id[$index] }
14405#	else { $val = $curr_sec_id[$index] }
14406    } else {
14407	&write_warnings ("\ncounter $ctr not defined");
14408	$val= 0;
14409    }
14410    print "\nVAL:$ctr: $val " if ($VERBOSITY > 3);
14411    $val;
14412}
14413
14414sub get_boolean_value {
14415    local($bool) = @_;
14416    local($val,$index);
14417    if (defined $global{$bool}) { $val= $global{$bool} }
14418    else {
14419	&write_warnings ("boolean $bool not defined\n");
14420	$val="0";
14421    }
14422    print "\nBOOL:$bool: $val " if ($VERBOSITY > 3);
14423    $val;
14424}
14425
14426sub do_cmd_addtocounter {
14427    local($_) = @_;
14428    local($ctr,$num,$index);
14429    $ctr = &missing_braces
14430	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
14431	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
14432    $num = &missing_braces
14433	unless ((s/$next_pair_rx/$num = $2;''/eo)
14434	      ||(s/$next_pair_pr_rx/$num = $2;''/eo));
14435
14436    $num = &translate_commands($num) if ($num =~ /\\/);
14437    if ($num !~ /^\s*(\+|-)?\d+\s*$/) {
14438        print STDERR "\n*** cannot set counter $ctr to $num ***\n";
14439        return($_);
14440    }
14441
14442    $latex_body .= &revert_to_raw_tex("\\addtocounter{$ctr}{$num}\n");
14443    $index = $section_commands{$ctr};
14444
14445    if (defined $global{$ctr}) { $global{$ctr} += $num }
14446    elsif ($index) {
14447	if ($SEGMENT) { $segment_sec_id[$index] += $num }
14448	else { $curr_sec_id[$index] += $num }
14449	$global{$ctr} += $num;
14450    } elsif ($ctr eq "equation") {
14451	$global{'eqn_number'} += $num
14452    } else { $global{$ctr} += $num };
14453    print "\nADD:$ctr:+$num= ". $global{$ctr}." " if ($VERBOSITY > 3);
14454#    &reset_dependents($ctr) if ($dependent{$ctr});
14455    $_;
14456}
14457
14458sub do_cmd_setcounter {
14459    local($_) = @_;
14460    local($ctr,$num,$index,$sctr);
14461    $ctr = &missing_braces
14462	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
14463	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
14464    $num = &missing_braces
14465	unless ((s/$next_pair_rx/$num = $2;''/eo)
14466	      ||(s/$next_pair_pr_rx/$num = $2;''/eo));
14467
14468    $num = &translate_commands($num) if ($num =~ /\\/);
14469    if ($num !~ /^\s*(\+|-)?\d+\s*$/) {
14470	print STDERR "\n*** cannot set counter $ctr to $num ***\n";
14471	return($_);
14472    }
14473    if ($ctr =~ /^l/) {
14474	$sctr = $';
14475	$ctr = $sctr if $section_commands{$sctr};
14476    }
14477    if (! $AUX_FILE && !($ctr =~ /page/ )) {
14478	$latex_body .= &revert_to_raw_tex("\\setcounter{$ctr}{$num}\n");
14479	$index = $section_commands{$ctr};
14480	if ($index) {
14481	    if ($curr_sec_id[$index] <= $num ) {
14482		$curr_sec_id[$index] = $num
14483	    } else {
14484		print "\nignoring \\setcounter{$ctr}{$num} currently at ",$curr_sec_id[$index] ;
14485		&write_warnings(join('',"\n\\setcounter{$ctr}{$num} ignored,"
14486			," cannot reduce from ",$curr_sec_id[$index]));
14487	    }
14488	    $global{$ctr} = $num;
14489	} elsif ($ctr eq "equation") {$global{'eqn_number'} = $num }
14490	else { $global{$ctr} = $num };
14491    }
14492    print "\nSET:$ctr: = $num" if ($VERBOSITY > 3);
14493#    &reset_dependents($ctr) if ($dependent{$ctr});
14494    $_;
14495}
14496
14497sub do_cmd_setlength {
14498    local($_) = @_;
14499    local($dimen,$value,$index,$sctr);
14500    $dimen = &missing_braces
14501	unless ((s/$next_pair_rx/$dimen = $2;''/eo)
14502	      ||(s/$next_pair_pr_rx/$dimen = $2;''/eo));
14503    $value = &missing_braces
14504	unless ((s/$next_pair_rx/$value = $2;''/eo)
14505	      ||(s/$next_pair_pr_rx/$value = $2;''/eo));
14506
14507    # recognise specific length-parameters
14508    if ($dimen =~ /captionwidth/) {
14509	local($pxs,$len) = &convert_length($value, $MATH_SCALE_FACTOR);
14510	$cap_width = $pxs if ($pxs &&($dimen =~ /captionwidth/));
14511    }
14512    if ((! $AUX_FILE)&&(! $PREAMBLE)) {
14513	$latex_body .= &revert_to_raw_tex("\\setlength{$dimen}{$value}\n");
14514	print "\nSETLENGTH:$dimen = $value" if ($VERBOSITY > 3);
14515    }
14516    $_;
14517}
14518
14519sub do_cmd_setboolean {
14520    local($_) = @_;
14521    local($bool,$val);
14522    $bool = &missing_braces
14523	unless ((s/$next_pair_rx/$bool = $2;''/eo)
14524	      ||(s/$next_pair_pr_rx/$bool = $2;''/eo));
14525    $val = &missing_braces
14526	unless ((s/$next_pair_rx/$val = $2;''/eo)
14527	      ||(s/$next_pair_pr_rx/$val = $2;''/eo));
14528    if (! $AUX_FILE) {
14529	$latex_body .= &revert_to_raw_tex("\\setboolean{$bool}{$val}\n");
14530	$global{"if$bool"} = (($val = ~/true/) ? 1 : 0);
14531	print "\nSETBOOL:$bool = $val" if ($VERBOSITY > 3);
14532    }
14533    $_;
14534}
14535
14536sub do_cmd_endsegment {
14537    local($_) = @_;
14538    local($ctr,$dum) = &get_next_optional_argument;
14539    local($index,$steps) = ('',1);
14540#    $steps = &missing_braces unless (
14541#	(s/$next_pair_pr_rx/$steps = $2;''/e)
14542#	||(s/$next_pair_rx/$steps = $2;''/e));
14543    $index = $section_commands{$ctr} if $ctr;
14544#    if ($index) { $curr_sec_id[$index] += $steps }
14545#    if ($index) { ($after_segment,$after_seg_num) = ($index,$steps) }
14546    if ($index) { ($after_segment,$after_seg_num) = ($index,1) }
14547    $_;
14548}
14549
14550sub do_cmd_stepcounter {
14551    local($_) = @_;
14552    local($ctr,$index);
14553    $ctr = &missing_braces
14554	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
14555	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
14556    if (! $AUX_FILE) {
14557	$latex_body .= &revert_to_raw_tex("\\stepcounter{$ctr}\n");
14558	$index = $section_commands{$ctr};
14559	if ($index) {
14560#	    if ($SEGMENT) { $segment_sec_id[$index] += 1 }
14561#	    else { $curr_sec_id[$index] += 1 }
14562	    $global{$ctr} += 1;
14563	} elsif ($ctr eq "equation") { $global{'eqn_number'} += 1 }
14564	else { $global{$ctr} += 1 };
14565    }
14566    print "\nSTP:$ctr:+1" if ($VERBOSITY > 3);
14567    &reset_dependents($ctr) if ($dependent{$ctr});
14568    $_;
14569}
14570
14571#RRM:   dependent counters are stored as a comma-separated list
14572#       in the %dependent hash.
14573sub reset_dependents {
14574    local($ctr) = @_;
14575    local($dep,$subdep,%dependents);
14576    @dependents = (split($delim, $dependent{$ctr}));
14577    print "\n" if (($VERBOSITY > 3)&&(@dependents));
14578    while (@dependents) {
14579	$dep = pop(@dependents);
14580	print "RESET $dep to 0\n" if ($VERBOSITY > 3);
14581	if ($global{$dep}) { $global{$dep} = 0 }
14582	elsif ($dep =~ /equation/) { $global{'eqn_number'} = 0 }
14583	if ($dependent{$dep}) {
14584	    push(@dependents,split($delim,$dependent{$dep}));
14585	}
14586    }
14587}
14588
14589sub do_cmd_numberwithin {
14590    local($_) = @_;
14591    local($ctr,$within);
14592    $ctr = &missing_braces
14593	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
14594	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
14595    $within = &missing_braces
14596	unless ((s/$next_pair_rx/$within = $2;''/eo)
14597	      ||(s/$next_pair_pr_rx/$within = $2;''/eo));
14598
14599    # record the counter dependency
14600    &addto_dependents($within,$ctr) if ($within);
14601    local($newsub) = "sub do_cmd_the$ctr {"
14602	. "\$global{'max_id'}++;\n"
14603#        . "local(\$super)=\&do_cmd_the$within();\n"
14604	. "local(\$super)=\&translate_commands('\\the$within');\n"
14605	. "\$super .= '.' unless (\$super =~/\\.\$/);\n"
14606	. "\$super .\&do_cmd_value('<<'.\$global{'max_id'}.'>>"
14607	. $ctr . "<<'.\$global{'max_id'}.'>>')}\n";
14608    eval $newsub;
14609    print " *** sub do_cmd_the$ctr unchanged *** $@ " if ($@);
14610    $_;
14611}
14612
14613sub do_cmd_refstepcounter {
14614    local($_) = @_;
14615    local($ctr);
14616    $ctr = &missing_braces
14617	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
14618	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
14619    if (! $AUX_FILE) {
14620	$latex_body .= &revert_to_raw_tex("\\refstepcounter{$ctr}\n");
14621	$index = $section_commands{$ctr};
14622	if (defined $global{$ctr}) { $global{$ctr} += 1 }
14623	elsif ($index) {
14624	    if ($SEGMENT) { $segment_sec_id[$index] += 1 }
14625	    else { $curr_sec_id[$index] += 1 }
14626	} elsif ($ctr eq "equation") { $global{'eqn_number'} += 1 }
14627	else { $global{$ctr} += 1 };
14628    }
14629    print "\nSTP: $ctr : +1" if ($VERBOSITY > 3);
14630    &reset_dependents($ctr) if ($dependent{$ctr});
14631    $_;
14632}
14633
14634sub read_counter_value {
14635    local($_) = @_;
14636    local($ctr,$br_id,$val);
14637    $ctr = &missing_braces
14638        unless ((s/$next_pair_pr_rx/$br_id = $1; $ctr = $2;''/eo)
14639              ||(s/$next_pair_rx/$br_id = $1; $ctr = $2;''/eo));
14640    $val = &get_counter_value($ctr);
14641    ($ctr, $val, $br_id, $_)
14642}
14643
14644sub styled_number_text {
14645    local($num_style, $val, $txtID) = @_;
14646    if ($USING_STYLES) {
14647        $txt_style{$num_style} = " " unless ($txt_style{$num_style});
14648        join('',"<SPAN CLASS=\"$num_style\">", $val, "</SPAN>", $_);
14649    } else { $val.$_ }
14650}
14651
14652sub do_cmd_arabic {
14653    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14654    $val = ($val ? &farabic($val) : "0");
14655    &styled_number_text('arabic', $val, $id);
14656}
14657
14658sub do_cmd_roman {
14659    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14660    if ($val < 0 ) { $val = join('',"-",&froman(-$val)); }
14661    elsif ($val) { $val = &froman($val) }
14662    else { $val = "0"; }
14663    &styled_number_text('roman', $val, $id);
14664}
14665
14666sub do_cmd_Roman {
14667    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14668    if ($val < 0 ) { $val = join('',"-",&fRoman(-$val)); }
14669    elsif ($val) { $val = &fRoman($val) }
14670    else { $val = "0"; }
14671    &styled_number_text('Roman', $val, $id);
14672}
14673
14674sub do_cmd_alph {
14675    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14676    if ($val < 0 ) { $val = join('',"-",&falph(-$val)); }
14677    elsif ($val) { $val = &falph($val) }
14678    else { $val = "0"; }
14679    &styled_number_text('alph', $val, $id);
14680}
14681
14682sub do_cmd_Alph {
14683    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14684    if ($val < 0 ) { $val = join('',"-",&fAlph(-$val)); }
14685    elsif ($val) { $val = &fAlph($val) }
14686    else { $val = "0"; }
14687    &styled_number_text('Alph', $val, $id);
14688}
14689
14690
14691sub do_cmd_fnsymbol {
14692    local($ctr, $val, $id, $_) = &read_counter_value($_[0]);
14693    $val = &process_in_latex_helper($ctr, $val, "fnsymbol{$ctr}");
14694    &styled_number_text('Alph', $val, $id);
14695}
14696
14697
14698
14699# This is a general command for getting counter values;
14700# e.g. for section-numbers.
14701
14702sub do_cmd_thecounter {
14703    local($_) = @_;
14704    # Uses $counter bound by the caller
14705    local($val) = &get_counter_value($counter);
14706    $val = &process_in_latex_helper($counter,$val,"the$counter");
14707    &styled_number_text($counter, $val, '');
14708#   join('',&process_in_latex_helper($counter,$val,"the$counter"),$_[0]);
14709}
14710
14711
14712################# Special Naming Macros ##################################
14713
14714sub do_cmd_LaTeX {
14715    local($_) = @_;
14716    if ($USING_STYLES) {
14717	$env_style{'LaTeX'} = ' ' unless ($env_style{'LaTeX'});
14718	$env_style{'logo-LaTeX'} = ' ' unless ($env_style{'logo-LaTeX'});
14719	join('',"<SPAN CLASS=\"logo,LaTeX\">",$Laname, $TeXname,"</SPAN>",$_);
14720    } else { join('',$Laname, $TeXname, $_); }
14721}
14722
14723sub do_cmd_LaTeXe {
14724    local($_) = @_;
14725    if ($USING_STYLES) {
14726	$env_style{'LaTeX2e'} = ' ' unless ($env_style{'LaTeX2e'});
14727	$env_style{'logo-LaTeX2e'} = ' ' unless ($env_style{'logo-LaTeX2e'});
14728	join('',"<SPAN CLASS=\"logo,LaTeX2e\">"
14729		,$Laname, $TeXname,'2<SUB>e</SUB>',"</SPAN>",$_);
14730    } else { join('',$Laname,$TeXname
14731		,(($HTML_VERSION >= 3.0)? '2<SUB>e</SUB>':'2e'),$_);
14732    }
14733}
14734
14735sub do_cmd_latextohtml {
14736    local($_) = @_;
14737    if ($USING_STYLES) {
14738	$env_style{'LaTeX2HTML'} = ' ' unless ($env_style{'LaTeX2HTML'});
14739	$env_style{'logo-LaTeX2HTML'} = ' ' unless ($env_style{'logo-LaTeX2HTML'});
14740	join('',"<SPAN CLASS=\"logo,LaTeX2HTML\">"
14741		,$Laname, $TeXname,"2<TT>HTML</TT>","</SPAN>",$_);
14742    } else { join('',$Laname,$TeXname,"2<TT>HTML</TT>",$_);}
14743}
14744
14745sub do_cmd_TeX {
14746    local($_) = @_;
14747    if ($USING_STYLES) {
14748	$env_style{'logo-TeX'} = ' ' unless ($env_style{'logo-TeX'});
14749	join('',"<SPAN CLASS=\"logo-TeX\">",$TeXname,"</SPAN>",$_);
14750    } else { join('',$TeXname, $_);}
14751}
14752
14753sub do_cmd_MF {
14754    local($_) = @_;
14755    if ($USING_STYLES) {
14756	$env_style{'logo-Metafont'} = ' ' unless ($env_style{'logo-Metafont'});
14757	join('',"<SPAN CLASS=\"logo-Metafont\">",$MFname,"</SPAN>",$_);
14758    } else { join('', $MFname, $_);}
14759}
14760
14761sub do_cmd_Xy {
14762    local($_) = @_;
14763    if ($USING_STYLES) {
14764	$env_style{'logo-Xy-pic'} = ' ' unless ($env_style{'logo-Xy-pic'});
14765	join('',"<SPAN CLASS=\"logo-Xy-pic\">",$Xyname,"</SPAN>",$_);
14766    } else { join('',$Xyname, $_);}
14767}
14768
14769sub do_cmd_AmS {
14770    local($_) = @_;
14771    if ($USING_STYLES) {
14772	$env_style{'logo-AMS'} = ' ' unless ($env_style{'logo-AMS'});
14773	join('',"<SPAN CLASS=\"logo-AMS\">",$AmSname,"</SPAN>",$_);
14774    } else { join('',$AmSname, $_);}
14775}
14776
14777sub do_cmd_AmSTeX {
14778    local($_) = @_;
14779    if ($USING_STYLES) {
14780	$env_style{'logo-AMS'} = ' ' unless ($env_style{'logo-AMS'});
14781	join('',"<SPAN CLASS=\"logo-AMS\">",$AmSname,"-$TeXname","</SPAN>",$_);
14782    } else { join('',$AmSname, "-", $TeXname, $_);}
14783}
14784
14785sub do_cmd_char {
14786    local($_) = @_;
14787# some special characters are already turned into l2h internal
14788# representation.
14789# Get its represention from the table and use it like as regexp form.
14790    local($spmquot) = &escape_rx_chars($html_specials{'"'});
14791# Get all internal special char representations as implied during
14792# preprocessing.
14793    local($spmrx) = join("\000",values %html_specials);
14794# escape regexp special chars (not really necessary yet, but why not)
14795    $spmrx = &escape_rx_chars($spmrx); #~ s:([\\(){}[\]\^\$*+?.|]):\\$1:g;
14796    $spmrx =~ s/\000/|/g;
14797    $spmrx = "(.)" unless $spmrx =~ s/(.+)/($1|.)/;
14798
14799    s/^[ \t]*(\d{1,3})[ \t]*/&#$1;/ &&
14800	return($_);
14801
14802    s/^[ \t]*\'(\d{1,3})[ \t]*/"&#".oct($1).";"/e &&
14803	return($_);
14804
14805    s/^[ \t]*$spmquot(\d{1,2})[ \t]*/"&#".hex($1).";"/e &&
14806	return($_);
14807
14808# This is a kludge to work together with german.perl. Brrr.
14809    s/^[ \t]*\'\'(\d{1,2})[ \t]*/"&#".hex($1).";"/e &&
14810	return($_);
14811# If l2h's special char marker represents more than one character,
14812# it's already in the &#xxx; form. Else convert the single character
14813# into &#xxx; with the ord() command.
14814    s/^[ \t]*\`\\?$spmrx[ \t]*/
14815	(length($html_specials_inv{$1}) > 1 ?
14816	 $html_specials_inv{$1} : "&#".ord($html_specials_inv{$1}||$1).";")/e &&
14817	     return($_);
14818    &write_warnings(join('',
14819			 "Could not find character number in \\char",
14820			 (/\n/ ? $` : $_), " etc.\n"));
14821    $_;
14822}
14823
14824
14825sub do_cmd_symbol {
14826    local($_) = @_;
14827    local($char);
14828    $char = &missing_braces
14829	unless ((s/$next_pair_pr_rx/$char = $2;''/eo)
14830		||(s/$next_pair_rx/$char = $2;''/eo));
14831    join('',&do_cmd_char($char),$_);
14832}
14833
14834################# Accent and Special Symbols ##################################
14835
14836# Generate code for the accents handling commands that are never
14837# applied to i or j.
14838# MEH: Now all accents are safe for dotless i or j
14839# MEH: Math accents supported as well
14840sub generate_accent_commands {
14841    local($accent,$accent_cmd);
14842    local(%accents) = ("c", "cedil", "pc", "cedil", "d", "bdot", "b", "b",
14843		       "tilde", "tilde", "dot", "dot", "bar", "macr",
14844		       "hat", "circ", "u", "breve", "v", "caron",
14845		       "H", "dblac", "t", "t", "grave", "grave",
14846		       "acute", "acute", "ddot", "uml", "check", "caron",
14847		       "breve", "breve", "vec", "vec",
14848		       "k", "ogon", "r", "ring");
14849    foreach $accent (keys(%accents))  {
14850	$accent_cmd = "sub do_cmd_$accent {" . 'local($_) = @_;'  .
14851	    "&accent_safe_for_ij('$accents{$accent}','$accent');" . '$_}';
14852	eval $accent_cmd;
14853	$accent_cmd = "do_cmd_$accent";
14854	print STDERR "\n*** sub do_cmd_$accent failed:\nPERL: $@\n" if ($@);
14855    }
14856}
14857
14858# These handle accents, taking care of the dotless i's and j's that
14859# may follow (even though accented j's are not part of any alphabet
14860# that I know).
14861#
14862# Note that many forms of accents over dotless i's and j's are
14863# handled:
14864#   "\^\i rest"
14865#   "\^\i
14866#    rest"
14867#   "\^{\i}rest"
14868#   "\^\i{}rest"
14869# They all produce "&#238;rest".
14870# MEH: now also handles
14871#   "\^{}rest"
14872#   "\^,rest"
14873# and many more
14874
14875sub accent_safe_for_ij {
14876    local($type,$acc_cmd) = @_;
14877    local($arg, $first_char,$ij_cmd);
14878    #print STDOUT "\nACCENT: $type <$_>\n" ;
14879    s/^[ \t]*\n?[ \t]*(\S)/$1/;	# Remove whitespace
14880    if (s/^\\([ij])([^a-zA-Z]|$)/$2/) {
14881	# Accent of this form: "\^\i rest" or "\^\i{}rest"
14882	($arg) =  $1; $ij_cmd = "\\$1";
14883	s/^[ \t]+//o;		# Get rid of whitespaces after \i
14884	if (substr($_, 0, 2) =~ /[\n\r][^\n\r]/) {
14885	    $_ = substr($_, 1); # Get rid of 1 newline after \i
14886	}
14887    } else {
14888	# Accent of this form: "\^{\i}rest" or not an accent on i nor j
14889	($arg) =  &get_next_pair_or_char_pr;
14890    }
14891    $arg =~ s/([^\s\\<])/$first_char = $1; ''/eo;
14892#   print STDOUT "\nACCENT1 type:$type arg:|${arg}| first_char: |$first_char| $ij_cmd
14893#	, $ACCENT_IMAGES\n";
14894
14895    local($aafter) = $_;
14896    local($iso) = &iso_map($first_char,$type);
14897    if ($iso) { $_ = join('', $iso, $arg, $aafter) }
14898    elsif ((!($ACCENT_IMAGES))&&(!($ij_cmd))) {
14899	local($err_string) =
14900	    "\nNo available accent for $first_char$type , using just \"$first_char$arg\"";
14901	print $err_string if ($DEBUG||$VERBOSITY > 1);
14902	&write_warnings("\n ...set \$ACCENT_IMAGES  to get an image ");
14903	$_ = join('', $first_char, $arg, $aafter) }
14904    else {
14905	print ", making image of accent: $first_char$type " if ($VERBOSITY > 1);
14906	$_ = join('', &mbox_accent($acc_cmd, $first_char, $ij_cmd) , $arg, $aafter)
14907    }
14908}
14909
14910sub mbox_accent {
14911    local($type, $char, $ij_cmd) = @_;
14912    if (length($type) > 1 ) {
14913	if ($text_accent{$type}) {
14914	    $type = $text_accent{$type};
14915	} elsif ($type =~ /^(math)?accent/) {
14916	} else {
14917	    print "\n unrecognised accent $type for `$char' ";
14918	    return $char;
14919	}
14920    }
14921    local(@styles);
14922    local($cmd,$style,$bstyle,$estyle) = ('','','','');
14923    local(@styles) = split(',',$ACCENT_IMAGES);
14924    foreach $style (@styles) {
14925	$style =~ s/(^\s*\\|\s*)//g;
14926	$cmd = "do_cmd_$style";
14927	if (defined &$cmd) {
14928	    $bstyle .= "\\$style\{" ;
14929	    $estyle .= "\}";
14930	} else {
14931	    &write_warnings("\nunrecognized style \\$style for accented characters");
14932	}
14933    }
14934    if (!($bstyle)) {
14935	$bstyle = "\{";
14936	$estyle = "\}";
14937    } elsif ($bstyle =~ /textit|itshape/) {
14938	$bstyle = '\raise.5pt\hbox{' . $bstyle ;
14939	$estyle .= "\}";
14940    }
14941    $char = $ij_cmd if ($ij_cmd);
14942    print STDOUT "\nACCENT: $type, $char" if ($VERBOSITY > 2);
14943    local($afterkern); # serifs extend too far on some letters...
14944    $afterkern = "\\kern.05em" if (($char =~ /m|n/)||($type=~/[Hv]/));
14945    # ...or the accent is wider than the letter, so pad it out a bit
14946    $afterkern = "\\kern.15em" if ($char =~ /i|l/); #||($type=~/v/));
14947
14948    &process_undefined_environment("tex2html_accent_inline"
14949        , ++$global{'max_id'}, "${bstyle}\\${type}\{$char\\/\}$estyle$afterkern");
14950}
14951
14952# MEH: Actually tries to find a dotless i or j
14953sub do_cmd_i { join('',&iso_map('i', 'nodot') || 'i', $_[0]) }
14954sub do_cmd_j { join('',&iso_map('j', 'nodot') || 'j', $_[0]) }
14955
14956sub do_cmd_accent {
14957    local($_) = @_;
14958    local($number);
14959    if (s/\s*(\d+)\s*//o) {$number = $1}
14960    elsif (s/\s*&SMPquot;(\d)(\d)\s*//o) { $number = $1*16 + $2 }
14961    elsif (s/\s*\'(\d)(\d)(\d)\s*//o) { $number = $1*64 + $2*8 + $3 }
14962    else { s/\s*(\W\w+)([\s\W])/$2/o;  $number = $1 }
14963    local($type) = $accent_type{uc($number)};
14964    #print STDOUT "\ndo_cmd_accent: $number ($type) |$_|\n";
14965    if (! $type) {
14966	&write_warnings("Accent number $number is unknown.\n");
14967	return $_;
14968    }
14969    &accent_safe_for_ij($type , 'accent$number' );
14970    $_;
14971}
14972
14973sub do_cmd_ae { join('', &iso_map("ae", "lig"), $_[0]);}
14974sub do_cmd_AE { join('', &iso_map("AE", "lig"), $_[0]);}
14975sub do_cmd_aa { join('', &iso_map("a", "ring"), $_[0]);}
14976sub do_cmd_AA { join('', &iso_map("A", "ring"), $_[0]);}
14977sub do_cmd_o { join('', &iso_map("o", "slash"), $_[0]);}
14978sub do_cmd_O { join('', &iso_map("O", "slash"), $_[0]);}
14979sub do_cmd_ss { join('', &iso_map("sz", "lig"), $_[0]);}
14980sub do_cmd_DH { join('', &iso_map("ETH", ""), $_[0]);}
14981sub do_cmd_dh { join('', &iso_map("eth", ""), $_[0]);}
14982sub do_cmd_TH { join('', &iso_map("THORN", ""), $_[0]);}
14983sub do_cmd_th { join('', &iso_map("thorn", ""), $_[0]);}
14984
14985sub do_cmd_pounds { join('', &iso_map("pounds", ""), $_[0]);}
14986sub do_cmd_S { join('', &iso_map("S", ""), $_[0]);}
14987sub do_cmd_copyright { join('', &iso_map("copyright", ""), $_[0]);}
14988sub do_cmd_P { join('', &iso_map("P", ""), $_[0]);}
14989
14990
14991sub brackets { ($OP, $CP);}
14992
14993sub get_date {
14994    local($format,$order) = @_;
14995    local(@lt) = localtime;
14996    local($d,$m,$y) = @lt[3,4,5];
14997    if ($format =~ /ISO/) {
14998	sprintf("%4d-%02d-%02d", 1900+$y, $m+1, $d);
14999    } elsif ($format) {
15000	if ($order) { eval "sprintf(".$format.",".$order.")"; }
15001	else { sprintf($format, $d, $m+1, 1900+$y); }
15002    } else { sprintf("%d/%d/%d", $m+1, $d, 1900+$y); }
15003}
15004
15005sub address_data {
15006    local($user, $date, $_);
15007    local($format,$order) = @_;
15008    # Get author, (email address) and current date.
15009    ($user = L2hos->fullname()) =~ s/,.*//;
15010    ($user, &get_date($format,$order));
15011}
15012
15013
15014#################################### LaTeX2e ##################################
15015
15016sub missing_braces {
15017#    local($cmd) = @_;
15018    local($next, $revert, $thisline);
15019    local($this_cmd) = $cmd;
15020    $this_cmd =~ s/^\\// unless ($cmd eq "\\");
15021    &write_warnings("\n? brace missing for \\$this_cmd");
15022    if (/^[\s%]*([^\n]*)\n/ ) {
15023	$thisline = &revert_to_raw_tex($1)
15024    } else {
15025	$thisline = &revert_to_raw_tex($_);
15026    }
15027    print "\n\n*** no brace for \\$this_cmd , before:\n$thisline";
15028    s/^\s*//;
15029    if ($_ =~ s/$next_token_rx//) { $next = $& };
15030    $next =~ s/$comment_mark(\d+\n?)?//g;
15031#    $next = &translate_commands($next) if ($next =~ /^\\/);
15032    if ($next =~ /^\\(\W|\d|[a-zA-z]*\b)/) {
15033	$revert = $next = "\\".$1;
15034    } elsif ($next =~ /\W/) {
15035	$revert = &revert_to_raw_tex($next);
15036    } else { $revert = $next };
15037    print "\n*** using \"$revert\" as the argument instead; is this correct?  ***\n\n";
15038    $next;
15039}
15040
15041#RRM:
15042#     &styled_text_chunk  provides an interface for pieces of styled text,
15043# within a single paragraph. The visual markup can be obtained through either
15044# 1. link to a stylesheet (CSS)
15045# 2. direct markup placed into the output
15046# 3. calling another function to process the text
15047#
15048# parameters (in order):
15049#  $def_tag   : markup tag to be used, unless $USING_STYLES or no $property given,
15050#		attributes can be included, only 1st word is used for closing-tag;
15051#  $prefix    : prefix for the Unique ID identifier, defaults to 'txt'
15052#           OR  contains  CLASS= identifier  when $property is empty(see below);
15053#  $type      : general type of the style-sheet information
15054#  $class     : specific type of the style-sheet information
15055#  $property  : value to be set, applicable to the $type & $class
15056#  $alt_proc  : name of procedure to use, if $USING_STYLES == 0, and no $def_tag
15057#  $_         : current data-stream
15058#  $open_tags_R : current open-tags (not used in this procedure)
15059
15060sub styled_text_chunk {
15061    local($def_tag, $prefix, $type, $class, $property, $alt_proc, $_,
15062        $ot) = @_;
15063    local($open_tags_R) = defined $ot ? $ot : $open_tags_R;
15064    local($text, $env_id, $def_end);
15065    local($span_tag) = 'SPAN';
15066    $text = &missing_braces
15067        unless ((s/$next_pair_pr_rx/$text = $2; $env_id = $1;''/eo)
15068	    || (s/$next_pair_rx/$text = $2; $env_id = $1;''/eo));
15069    $text = &balance_inner_tags($text);
15070
15071    #start from no open tags
15072    local(@save_open_tags) = ();
15073    local($open_tags_R) = [];
15074
15075#    local($decl);
15076#    if ($prefix =~ /CLASS="(\w+)"/ ) {
15077#	$decl=$1;
15078#	push (@$open_tags_R, $decl);
15079#    }
15080#    push (@$open_tags_R, $color_env) if $color_env;
15081    if (!$inside_math) {
15082	$text = &translate_environments($text);
15083	$text = &translate_commands($text) if ($text =~ /\\/);
15084	$text .= &balance_tags;
15085    }
15086
15087    if (($USING_STYLES)&&($env_id =~ /^\d+$/)&&($property)) {
15088	$prefix = 'txt' unless ($prefix);
15089	$env_id = $prefix.$env_id;
15090	$styleID{$env_id} = join('',"$type", ($class ? "-$class" : '')
15091				 ,": ", $property,"; ");
15092	return(join('',"<$span_tag ID=\"$env_id\">",$text,"<\/$span_tag>", $_));
15093    }
15094
15095    if (($USING_STYLES)&&($prefix =~ /($span_tag )?CLASS=\"(\w+)\"/o)) {
15096	local($span_class) = $2;
15097	$def_tag = (($1)? $1 : $span_tag." ");
15098	$txt_style{$span_class} = "$type: $class "
15099	    unless ($txt_style{$span_class});
15100	return(join('',"<$def_tag CLASS=\"$span_class\">"
15101		, $text,"<\/$span_tag>", $_));
15102    }
15103
15104    if (($def_tag) && (!$USING_STYLES)) {
15105	$def_tag =~ s/^($span_tag)?CLASS=\"(\w+)\"$// ;
15106    }
15107
15108    if ($def_tag =~ /^(\w+)/) {
15109	$def_end = $1;
15110	return(join('',"<$def_tag>",$text,"<\/$def_end>", $_));
15111    }
15112
15113    return (join('', eval ("&$alt_proc(\$text)") , $_)) if (defined "&$alt_proc");
15114
15115    &write_warnings(
15116	"\ncannot honour request for $type-$class:$property style at br$env_id");
15117    join('', $text, $_);
15118}
15119
15120sub multi_styled_text_chunk {
15121    local($def_tag, $prefix, $type, $class, $property, $_, $ot) = @_;
15122    local($open_tags_R) = defined $ot ? $ot : $open_tags_R;
15123    $prefix = 'txt' unless ($prefix);
15124    my(@def_tags) = split(',',$def_tag);
15125    my(@types) = split(',',$type);
15126    my(@classes) = split(',',$class);
15127    my(@properties) = split(',',$property);
15128    $text = &missing_braces
15129        unless ((s/$next_pair_pr_rx/$text = $2; $env_id = $1;''/eo)
15130	    || (s/$next_pair_rx/$text = $2; $env_id = $1;''/eo));
15131    if (($USING_STYLES)&&($env_id =~ /^\d+$/)&&($property)) {
15132        # $1 contains the bracket-id
15133	$env_id = $prefix.$env_id;
15134	while (@properties) {
15135	    $class = shift @classes;
15136	    $property = shift @properties;
15137	    $styleID{$env_id} .= join(''
15138		, shift @types,
15139		, ($class ? "-".$class : '')
15140		, ($property ? " : $property" : ''), " ; ");
15141	    $styleID{$env_id} .= "\n\t\t  " if (@properties);
15142	}
15143    }
15144    join('',"<SPAN ID=\"$env_id\">",$text,"<\/SPAN>", $_);
15145}
15146
15147#RRM:
15148#   This one takes care of commands with argument that really should be
15149#   environments; e.g.  \centerline, \rightline, etc.
15150#   Note that styles are inherited also from the existing @$open_tags_R.
15151#
15152sub styled_text_block {
15153    local($def_tag, $attrib, $value, $class, $_, $ot) = @_;
15154    local($open_tags_R) = defined $ot ? $ot : $open_tags_R;
15155    local($text, $env_id, $attribs);
15156    if ($attribs =~ /,/ ) {
15157        local(@attribs) = split(',',$attrib);
15158	local(@values) = split(',',$value);
15159	while (@attribs) {
15160            $attribs .= join('', " " , shift @attribs
15161	        ,"=\"" , shift @values, "\"") }
15162    } elsif($value) {
15163        $attribs = join(''," ",$attrib,"=\"",$value,"\"")
15164    } else { $attribs = " " . $attrib }
15165
15166    local(@save_open_tags) = @$open_tags_R;
15167    local($closures) = &close_all_tags();
15168    local($reopens)=&balance_tags();
15169    $text = &missing_braces
15170        unless ((s/$next_pair_pr_rx/$text = $2; $env_id = $1;''/eo)
15171	    || (s/$next_pair_rx/$text = $2; $env_id = $1;''/eo));
15172    if (($USING_STYLES)&&($env_id =~ /^\d+$/)) {
15173	$env_id = ++$global{'max_id'};
15174	$env_id = "par".$env_id;
15175	$styleID{$env_id} = " ";
15176	$env_style{$class} = " " if (($class)&&!($env_style{$class}));
15177	$class = " CLASS=\"$class\"" if ($class);
15178	$env_id = " ID=\"$env_id\"";
15179    } else { $class = ''; $env_id = '' };
15180
15181    $text = &translate_environments($text);
15182    $text = &translate_commands($text);
15183
15184    local($closuresA)=&close_all_tags();
15185    local($reopensA) = &balance_tags();
15186    $text =~ s/^\n?/\n/o;
15187    join('', $closures
15188        , "<$def_tag$class$env_id$attribs>"
15189        , $reopens, $text, $closuresA
15190        , "</$def_tag>\n", $reopensA,  $_);
15191}
15192
15193
15194# this gives a separate ID for each instance
15195#sub do_cmd_textbf { &styled_text_chunk('B','','font','weight'
15196#		    ,'bold', '', @_); }
15197#
15198# this uses a single CLASS for all instances
15199sub do_cmd_textbf { &styled_text_chunk('B','CLASS="textbf"'
15200		    ,'font-weight','bold', '', '', @_); }
15201
15202
15203# this gives a separate ID for each instance
15204sub do_cmd_texttt { &styled_text_chunk('TT','','font','','', '', @_); }
15205
15206# this uses a single CLASS for all instances
15207#sub do_cmd_textit { &styled_text_chunk('TT','CLASS="textit"'
15208#		    ,'font-family','monospace', '', '', @_); }
15209#
15210# this gives a separate ID for each instance
15211#sub do_cmd_textit { &styled_text_chunk('I','','font','style'
15212#		    ,'italic', '', @_); }
15213#
15214# this uses a single CLASS for all instances
15215sub do_cmd_textit { &styled_text_chunk('I','CLASS="textit"'
15216		    ,'font-style','italic', '', '', @_); }
15217
15218
15219
15220# this gives a separate ID for each instance
15221#sub do_cmd_textsl { &styled_text_chunk('I','','font','style'
15222#		    ,'oblique', '', @_); }
15223#
15224# this uses a single CLASS for all instances
15225#sub do_cmd_textsl { &styled_text_chunk('I','CLASS="textsl"'
15226#		    ,'font-style','oblique', '', '', @_); }
15227#
15228# ... NS4 implements Italic, not oblique
15229sub do_cmd_textsl { &styled_text_chunk('I','CLASS="textsl"'
15230		    ,'font-style','italic', '', '', @_); }
15231
15232
15233# this gives a separate ID for each instance
15234#sub do_cmd_textsf { &styled_text_chunk('I','','font','family'
15235#		    ,'sans-serif', '', @_); }
15236#
15237# this uses a single CLASS for all instances
15238#sub do_cmd_textsf { &styled_text_chunk('I','CLASS="textsf"'
15239#		    ,'font-family','sans-serif', '', '', @_); }
15240#
15241# ... NS4 doesn't implement sans-serif
15242sub do_cmd_textsf { &styled_text_chunk('I','CLASS="textsf"'
15243		    ,'font-style','italic', '', '', @_); }
15244
15245
15246#sub do_cmd_textsc {
15247#    local($_) = @_;
15248#    local($text, $next, $scstr, $before, $special);
15249#    $text = &missing_braces
15250#        unless ((s/$next_pair_pr_rx/$text = $2;''/eo)
15251#	    || (s/$next_pair_rx/$text = $2;''/eo));
15252#    join('', &process_smallcaps($text), $_);
15253#}
15254
15255sub lowercase_entity {
15256    local($char) = @_;
15257    local($exent);
15258    if ($exent = $low_entities{$char}) { "\&#$exent;" }
15259    elsif ($exent = $extra_small_caps{$char}) { $exent }
15260    else { "\&#$char;" }
15261}
15262
15263sub process_smallcaps {
15264    local($text) = @_;
15265    local($next, $scstr, $scbef, $special, $char);
15266    # is this enough for \sc and \scshape ?
15267    $text = &translate_environments($text);
15268
15269    # MRO: replaced $* with /m
15270    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;|<[^>]+>)+/m ) {
15271	$scbef = $`; $special = $&; $text = $';
15272	while ( $scbef =~ /(&#\d+;|[a-z$sclower])+[a-z\W\d$sclower]*/m) {
15273	    $scstr .= $`; $scbef = $';
15274	    $next = $&;
15275	    $next =~ s/&#(\d+);/&lowercase_entity($1)/egm;
15276	    eval "\$next =~ $scextra" if ($scextra);
15277	    eval "\$next =~ tr/a-z$sclower/A-Z$scupper/";
15278	    $scstr .= "<SMALL>" . $next ."<\/SMALL>";
15279	}
15280	$scstr .= $scbef . $special;
15281    }
15282    if ($text) {
15283	while ( $text =~ /(&#\d+;|[a-z$sclower])+[a-z\W\d$sclower]*/m) {
15284	    $scstr .= $`; $text = $';
15285	    $next = $&;
15286	    $next =~ s/&#(\d+);/&lowercase_entity($1)/egm;
15287	    eval "\$next =~ $scextra" if ($scextra);
15288	    eval "\$next =~ tr/a-z$sclower/A-Z$scupper/";
15289	    $scstr .= "<SMALL>" . $next ."<\/SMALL>";
15290	}
15291	$scstr .= $text;
15292    }
15293    $scstr;
15294}
15295
15296# this gives a separate ID for each instance
15297#sub do_cmd_textsc { &styled_text_chunk('','','font','variant'
15298#		    ,'small-caps', 'process_smallcaps', @_); }
15299#
15300# this uses a single CLASS for all instances
15301#sub do_cmd_textsc { &styled_text_chunk('', 'CLASS="textsc"'
15302#		    ,'font-variant','small-caps','', 'process_smallcaps', @_); }
15303#
15304# ...but NS 4.03 doesn't implement  small-caps !!!
15305sub do_cmd_textsc { &styled_text_chunk('',''
15306		    ,'font-variant','small-caps','', 'process_smallcaps', @_); }
15307
15308
15309#sub do_cmd_emph { &styled_text_chunk('EM','em','font','variant','','', @_); }
15310
15311
15312# this gives a separate ID for each instance
15313#sub do_cmd_underline { &styled_text_chunk('U','','text','decoration','underline','', @_); }
15314
15315# this uses a single CLASS for all instances
15316sub do_cmd_underline { &styled_text_chunk('U','CLASS="underline"'
15317		       ,'text-decoration','underline','','', @_); }
15318sub do_cmd_underbar { &do_cmd_underline(@_) }
15319
15320
15321# this gives a separate ID for each instance
15322#sub do_cmd_strikeout { &styled_text_chunk('STRIKE',''
15323#		       ,'text','decoration','line-through','', @_); }
15324
15325# this uses a single CLASS for all instances
15326sub do_cmd_strikeout { &styled_text_chunk('STRIKE','CLASS="strikeout"',
15327		       'text-decoration','line-through','','', @_); }
15328
15329
15330sub do_cmd_uppercase {
15331    local($_) = @_;
15332    local($text,$next,$done,$special,$after);
15333    $text = &missing_braces unless (
15334	    (s/$next_pair_pr_rx/$text = $2;''/eo)
15335	    ||(s/$next_pair_rx/$text = $2;''/eo));
15336    $after = $_;
15337    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;)/ ) {
15338	$next = $`;
15339	$special = $&;
15340	$text = $';
15341	$next =~ tr /a-z/A-Z/ if ($next);
15342	$done .= $next . $special;
15343    }
15344    $text =~ tr /a-z/A-Z/ if ($text);
15345    $done .= $text;
15346    $done = &convert_iso_latin_chars($done) if ($done);
15347    join('',$done,$after);
15348}
15349
15350sub do_cmd_lowercase {
15351    local($_) = @_;
15352    local($text,$next,$done,$special,$after);
15353    $text = &missing_braces
15354        unless ((s/$next_pair_pr_rx/$text = $2;''/seo)
15355	    || (s/$next_pair_rx/$text = $2;''/seo));
15356    $after = $_;
15357    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;)/ ) {
15358	$next = $`;
15359	$special = $&;
15360	$text = $';
15361	$next =~ tr /A-Z/a-z/ if ($next);
15362	$done .= $next . $special;
15363    }
15364    $text =~ tr /A-Z/a-z/ if ($text);
15365    $done .= $text;
15366    $done = &convert_iso_latin_chars($done) if ($done);
15367    join('',$done,$after);
15368}
15369
15370sub do_cmd_MakeUppercase { &do_cmd_uppercase(@_) }
15371sub do_cmd_MakeLowercase { &do_cmd_lowercase(@_) }
15372
15373
15374
15375sub do_cmd_ensuremath {
15376    local($_) = @_;
15377    local ($id, $value);
15378    $value = &missing_braces unless (
15379	(s/$next_pair_pr_rx/$value=$2;''/eo)
15380	||(s/$next_pair_rx/$value=$2;''/eo));
15381    join('', &simple_math_env($value), $');
15382}
15383
15384#
15385#  This is mainly for \special{header=PostScript_Prologue},
15386#	and \graphicspath{path} which occur OUTSIDE of an environment
15387#	passed to TeX.  \special's INSIDE such environments are, of
15388#	course, left alone.
15389
15390sub do_cmd_special {
15391    local($_) = @_;
15392    local ($id, $value);
15393    $value = &missing_braces unless (
15394	(s/$next_pair_pr_rx/$value=$2;''/eo)
15395	||(s/$next_pair_rx/$value=$2;''/eo));
15396    local($special_cmd) = &revert_to_raw_tex($value);
15397    &add_to_preamble($cmd,"\\$cmd\{$special_cmd\}");
15398    $_;
15399}
15400
15401
15402########################## Input and Include commands #########################
15403
15404sub do_cmd_input {
15405    local($_) = @_;
15406    local($file,$output);
15407    (s/\s*(.*)\s*\n/$file =$1;''/s) unless (
15408	(s/$next_pair_pr_rx/$file=$2;''/eo)
15409	||(s/$next_pair_rx/$file=$2;''/eo));
15410    local($after) = $_;
15411    $file = &revert_to_raw_tex("\\input{$file}\n") if $file;
15412    if ($PREAMBLE) { &add_to_preamble('include',$file)}
15413    elsif (!($file=~/^\s*$/)) {
15414	$output = &process_undefined_environment('center'
15415		, ++$global{'max_id'},"\\vbox{$file}");
15416    }
15417    $output.$after;
15418}
15419
15420sub do_cmd_include {
15421    local($_) = @_;
15422    local($file,$output);
15423    $file = &missing_braces unless (
15424	(s/$next_pair_pr_rx/$file=$2;''/eo)
15425	||(s/$next_pair_rx/$file=$2;''/eo));
15426    local($after) = $_;
15427    $file = &revert_to_raw_tex("\\include{$file}\n") if $file;
15428    if ($PREAMBLE) { &add_to_preamble('include',$file)}
15429    else {
15430	$output = &process_undefined_environment('figure'
15431		, ++$global{'max_id'},"\\vbox{$file}");
15432    }
15433    $output.$after;
15434}
15435
15436########################## Messages #########################
15437
15438sub do_cmd_message {
15439    local($_) = @_;
15440    local($message);
15441    $message = &missing_braces unless (
15442	(s/$next_pair_pr_rx/$message=$2;''/eo)
15443	||(s/$next_pair_rx/$message=$2;''/eo));
15444    local($after) = $_;
15445    $message = &translate_commands($message);
15446    $message =~ s/$comment_mark(\d+)//og;
15447    print STDOUT "\n*** $message ***\n";
15448    $after;
15449}
15450
15451sub do_cmd_typeout {
15452    print STDOUT "\n";
15453    local($_) = &do_cmd_message(@_);
15454    print STDOUT "\n";
15455    $_;
15456}
15457
15458sub do_cmd_expandafter {
15459    local($_) = @_;
15460    print "\nEXPANDAFTER: " if ($VERBOSITY >3);
15461    return($_) unless (s/^\s*(\\\w+)\s*\\//o);
15462    print " delaying $1 " if ($VERBOSITY >3);
15463    local($delay,$cmd) = ($1,'');
15464    s/^(\w+|\W)/$cmd=$1;''/eo;
15465    local($nextcmd) = "do_cmd_$cmd";
15466    if (defined &$nextcmd) { $_ = &$nextcmd($_) }
15467    elsif ($new_command{$cmd}) {
15468        local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
15469	do { ### local($_) = $body;
15470	    &make_unique($body);
15471	} if ($body =~ /$O/);
15472	if ($argn) {
15473	    do {
15474		local($before) = '';
15475		local($after) = "\\$cmd ".$_;
15476		$after = &substitute_newcmd;   # may change $after
15477                $after =~ s/\\\@#\@\@/\\/o unless ($after);
15478            };
15479	} else { $_ = $body . $_; }
15480    } else { print "\nUNKNOWN COMMAND: $cmd "; }
15481
15482    # now put the delayed function back for processing
15483    join('',$delay, " ", $_);
15484}
15485
15486sub do_cmd_tracingall {
15487    print "\nTRACING:\n$ref_contents\n$after\n";
15488    $VERBOSITY = 8; ""; }
15489
15490sub do_cmd_htmltracenv { &do_cmd_htmltracing }
15491
15492sub do_cmd_htmltracing {
15493    local($_) = @_;
15494    local($value);
15495    $value = &missing_braces
15496        unless ((s/$next_pair_rx/$value = $2;''/eo)
15497	    ||(s/$next_pair_pr_rx/$value = $2;''/eo));
15498    if ($value =~ /^\s*(\d+)\s*$/) {
15499	$VERBOSITY = $1;
15500	if ($VERBOSITY) {
15501	    print "\n\n *** setting trace-level to $VERBOSITY ***\n";
15502	} else {
15503	    print "\n\n *** cancelling all tracing ***\n\n";
15504	}
15505    } else {
15506	&write_warnings("argument to \\htmltracing must be a number");
15507     }
15508    $_ ;
15509}
15510
15511
15512############################ Initialization ####################################
15513
15514sub initialise {
15515    ############################ Global variables ###############################
15516    $PREAMBLE = 2;		# 1 while translating preamble, 0 while translating body
15517    $NESTING_LEVEL = undef;	#counter for TeX group nesting level
15518    $OUT_NODE = 0;		# Used in making filenames of HTML nodes unique
15519    $eqno_prefix = '';		# default prefix on equation numbers
15520    ($O , $C, $OP, $CP) = ('<<' , '>>', '<#', '#>'); # Open/Close Markers
15521    $href_name = 0;		# Used in the HREF NAME= field
15522    $wrap_toggle = 'end';
15523    $delim = '%:%';		# Delimits items of sectioning information
15524				# stored in a string
15525
15526    $LATEX2HTML_META = '<META NAME="Generator" CONTENT="LaTeX2HTML v'.$TEX2HTMLV_SHORT.'">'
15527	. "\n<META HTTP-EQUIV=\"Content-Style-Type\" CONTENT=\"text/css\">"
15528	      unless ($LATEX2HTML_META);
15529
15530    $TeXname = (($HTML_VERSION ge "3.0")? "T<SMALL>E</SMALL>X" : "TeX");
15531    $Laname = (($HTML_VERSION ge "3.0")? "L<SUP><SMALL>A</SMALL></SUP>" : "La");
15532    $MFname = (($HTML_VERSION ge "3.0")? "M<SMALL>ETAFONT</SMALL>" : "Metafont");
15533    $Xyname = (($HTML_VERSION ge "3.0")? "X<SUB><BIG>Y</BIG></SUB>" : "Xy");
15534    $AmSname = (($HTML_VERSION ge "3.0")? "A<SUB><BIG>M</BIG></SUB>S" : "AmS");
15535
15536    $EQN_TAGS = "R" unless ($EQN_TAGS);
15537    $EQNO_START = "(";
15538    $EQNO_END   = ")";
15539
15540    $AtBeginDocument_hook  = "\$AtBeginDocument_hook\=\'\'; "
15541	unless $AtBeginDocument_hook;
15542    $cross_ref_mark = '<tex2html_cr_mark>';
15543    $external_ref_mark = '<tex2html_ext_cr_mark>';
15544    $cite_mark = '<tex2html_cite_mark>';
15545    $hash_mark = '<tex2html_hash_mark>';
15546    $protected_hash = '<tex2html_protected_hash>';
15547    $param_mark = '<tex2html_param_mark>';
15548    $bbl_mark = '<tex2html_bbl_mark>';
15549    $toc_mark = '<tex2html_toc_mark>';
15550    $lof_mark = '<tex2html_lof_mark>';
15551    $lot_mark = '<tex2html_lot_mark>';
15552    $info_page_mark = '<tex2html_info_page_mark>';
15553    $info_title_mark = '<tex2html_info_title_mark>';
15554    $init_file_mark = '<tex2html_init_file_mark>';
15555    $childlinks_on_mark = '<tex2html_childlinks_mark>';
15556    $childlinks_null_mark = '<tex2html_childlinks_null_mark>';
15557    $childlinks_mark = $childlinks_on_mark;
15558    $more_links_mark = '<tex2html_morelinks_mark>';
15559    $idx_mark = '<tex2html_idx_mark>';
15560    $verbatim_mark = '<tex2html_verbatim_mark>';
15561    $unfinished_mark = '<tex2html_unfinished_mark>';
15562    $verb_mark = '<tex2html_verb_mark>';
15563    $verbstar_mark = '<tex2html_verbstar_mark>';
15564    $image_mark = '<tex2html_image_mark>';
15565    $mydb_mark =  '<tex2html_mydb_mark>';
15566    $percent_mark = '<tex2html_percent_mark>';
15567    $ampersand_mark = '<tex2html_ampersand_mark>';
15568    $dol_mark = '<tex2html_lone_dollar>';
15569    $comment_mark = '<tex2html_comment_mark>';
15570    $caption_mark = '<tex2html_caption_mark>';
15571    $array_col_mark = '<tex2html_col_mark>';
15572    $array_row_mark = '<tex2html_row_mark>';
15573    $array_text_mark = '<tex2html_text_mark>';
15574    $array_mbox_mark = '<tex2html_mbox_mark>';
15575
15576    $bibitem_counter = 0;
15577    $undef_mark = '<tex2html_undef_mark>';
15578    $file_mark = '<tex2html_file>';
15579    $endfile_mark = '<tex2html_endfile>';
15580
15581    # This defines textual markers for all the icons
15582    # e.g. $up_visible_mark = '<tex2html_up_visible_mark>';
15583    # They will be replaced with the real icons at the very end.
15584    foreach $icon (keys %icons) {eval "\$$icon = '<tex2html_$icon>'"};
15585
15586    # Make sure $HTML_VERSION is in the right range and in the right format.
15587#    $HTML_VERSION =~ /[\d.]*/;
15588#    $HTML_VERSION = 0.0 + $&;
15589#    $HTML_VERSION = 2 if ( $HTML_VERSION < 2 );
15590#    $HTML_VERSION = 9 if ( $HTML_VERSION > 9 );
15591#    $HTML_VERSION = sprintf("%3.1f",$HTML_VERSION);
15592
15593    &banner();
15594    print "Revised and extended by:"
15595	. "\n Marcus Hennecke, Ross Moore, Herb Swan and others\n";
15596
15597    # Collect HTML options and figure out HTML version
15598    $HTML_OPTIONS = '' unless ($HTML_OPTIONS);
15599    $HTML_VERSION =~ s/^html|\s+//g;
15600    local(@HTML_VERSION) = split(/,/, $HTML_VERSION);
15601    foreach ( @HTML_VERSION ) {
15602	if (/^[\d\.]+$/) {
15603	    # Make sure $HTML_VERSION is in the right range and in the right format.
15604	    $HTML_VERSION = 0.0 + $_;
15605	    $HTML_VERSION = 2 if ( $HTML_VERSION < 2 );
15606	    $HTML_VERSION = 9 if ( $HTML_VERSION > 9 );
15607	    $HTML_VERSION = sprintf("%3.1f",$HTML_VERSION);
15608	} else {
15609	    $HTML_OPTIONS .= "$_,";
15610	}
15611    }
15612    $HTML_OPTIONS =~ s/\W$//;  # remove any trailing punctuation
15613
15614    print "...producing markup for HTML version $HTML_VERSION  ";
15615    print ($HTML_OPTIONS ? "with $HTML_OPTIONS extensions\n\n\n" : "\n\n\n");
15616
15617    # load the character defs for latin-1, but don't set the charset yet
15618    &do_require_extension('latin1');
15619    $charset = $CHARSET = $PREV_CHARSET = '';
15620
15621    if ($HTML_VERSION =~ /(2.0|3.0|3.2|4.0|4.1)/) {
15622	# Require the version specific file
15623	do { $_ = "$LATEX2HTMLVERSIONS${dd}html$1.pl";
15624	     if (!(-f $_)) {  s/(\d).(\d.pl)$/$1_$2/ };
15625	     if (!(-f $_)) {  s/(\d)_(\d.pl)$/$1-$2/ };
15626	     require $_ || die "\n*** Could not load $_ ***\n";
15627	     print "\nHTML version: loading $_\n";
15628	} unless ($HTML_VERSION =~ /2.0/);
15629	$DOCTYPE = "-//".(($HTML_VERSION eq "2.0")? "IETF" : "W3C")
15630	    . "//DTD HTML $HTML_VERSION"
15631	    .(($HTML_VERSION eq "3.2")? " Final" : "")
15632	    .(($HTML_VERSION eq "4.0")? " Transitional" : "");
15633
15634	if ($HTML_OPTIONS) {
15635	    local($ext);
15636	    local($loading_extensions) = 1;
15637	    # Require the option specific files
15638	    @HTML_VERSION = split(/,/, $HTML_OPTIONS);
15639	    foreach $ext ( @HTML_VERSION ) {
15640		&do_require_extension($ext);
15641#		do {
15642#		    print "\nLoading $LATEX2HTMLVERSIONS$dd$ext.pl";
15643#		    require "$LATEX2HTMLVERSIONS$dd$ext.pl";
15644#		} if (-f "$LATEX2HTMLVERSIONS$dd$ext.pl");
15645	    }
15646	    undef $loading_extensions;
15647	}
15648    } else {
15649	print "\n You specified an invalid version: $HTML_VERSION\n"
15650	    . "In future please request extensions by name:\n"
15651	    . "  i18n  table  math  frame  latin1  unicode  etc.\n";
15652
15653    # Require all necessary version specific files
15654	foreach ( sort <$LATEX2HTMLVERSIONS${dd}html[1-9].[0-9].pl> ) {
15655	    last if ( $_ gt "$LATEX2HTMLVERSIONS${dd}html$HTML_VERSION.pl" );
15656	    do { print "\nloading $_" if ($DEBUG);
15657		 require $_; } unless (
15658		($NO_SIMPLE_MATH)&&($_ eq "$LATEX2HTMLVERSIONS${dd}html3.1.pl"));
15659	};
15660	$STRICT_HTML = 0;
15661    }
15662
15663    # packages automatically implemented, or clearly irrelevant
15664    %styles_loaded =
15665     ( 'theorem' , 1 , 'enumerate', 1 , 'a4paper' , 1 , 'b5paper' , 1
15666     , '10pt' , 1 , '11pt' , 1 , '12pt' , 1
15667     , %styles_loaded );
15668
15669
15670    %declarations =
15671    ('em' , '<EM></EM>',
15672     'it' , '<I></I>',
15673     'bf' , '<B></B>',
15674     'tt' , '<TT></TT>',
15675     'sl' , '<I></I>',		# Oops!
15676     'sf' , '<I></I>',		# Oops!
15677     'rm' ,  '<></>',
15678     'rmfamily'   ,'<></>',     # see $fontchange_rx
15679     'normalfont' ,'<></>',     # see $fontweight_rx and $fontchange_rx
15680     'mdseries'   ,'<></>',     # see $fontweight_rx
15681     'upshape'    ,'<></>',     # see $fontchange_rx
15682     'itshape' ,  '<I></I>',
15683     'bfseries' , '<B></B>',
15684     'ttfamily' , '<TT></TT>',
15685     'slshape' ,  '<I></I>',	# Oops!
15686     'sffamily' , '<I></I>',	# Oops!
15687##     'scshape' ,  '<I></I>',	# Oops!
15688#     'boldmath' , '<B></B>',
15689#     'quote', '<BLOCKQUOTE></BLOCKQUOTE>',
15690#     'quotation', '<BLOCKQUOTE></BLOCKQUOTE>',
15691     %declarations	# Just in case someone extends it in the init file
15692     );
15693
15694
15695%declarations = (
15696     'tiny', '<FONT SIZE="-2"></FONT>',
15697     'Tiny', '<FONT SIZE="-2"></FONT>',
15698     'scriptsize', '<FONT SIZE="-2"></FONT>',
15699     'small', '<FONT SIZE="-1"></FONT>',
15700     'Small', '<FONT SIZE="-1"></FONT>',
15701     'SMALL', '<FONT SIZE="-1"></FONT>',
15702     'smaller', '<SMALL></SMALL>',
15703     'footnotesize', '<FONT SIZE="-1"></FONT>',
15704     'larger', '<BIG></BIG>',
15705     'large', '<FONT SIZE="+1"></FONT>',
15706     'Large', '<FONT SIZE="+2"></FONT>',
15707     'LARGE', '<FONT SIZE="+2"></FONT>',
15708     'huge', '<FONT SIZE="+3"></FONT>',
15709     'Huge', '<FONT SIZE="+4"></FONT>',
15710#     'centering', '<DIV ALIGN="CENTER"></DIV>',
15711#     'center', '<DIV ALIGN="CENTER"></DIV>',
15712#     'flushleft', '<DIV ALIGN="LEFT"></DIV>',
15713#     'raggedright', '<DIV ALIGN="LEFT"></DIV>',
15714#     'flushright', '<DIV ALIGN="RIGHT"></DIV>',
15715#     'raggedleft', '<DIV ALIGN="RIGHT"></DIV>',
15716     %declarations
15717    ) if ($HTML_VERSION > 2.0 );
15718
15719#  no alignment in HTML 2.0
15720#%declarations = (
15721#     'centering', '<P ALIGN="CENTER"></P>',
15722#     'center', '<P ALIGN="CENTER"></P>',
15723#     'flushleft', '<P ALIGN="LEFT"></P>',
15724#     'raggedright', '<P ALIGN="LEFT"></P>',
15725#     'flushright', '<P ALIGN="RIGHT"></P>',
15726#     'raggedleft', '<P ALIGN="RIGHT"></P>',
15727
15728%declarations = (
15729#     'centering', '<P></P>',
15730     'center', '<P></P>',
15731     'flushleft', '<P></P>',
15732     'raggedright', '<P></P>',
15733     'flushright', '<P></P>',
15734     'raggedleft', '<P></P>',
15735     'quote', '<BLOCKQUOTE></BLOCKQUOTE>',
15736     'quotation', '<BLOCKQUOTE></BLOCKQUOTE>',
15737     'verse', '<BLOCKQUOTE></BLOCKQUOTE>',
15738     'preform', '<PRE></PRE>',
15739     'unord', '<UL></UL>',
15740     'ord', '<OL></OL>',
15741     'desc', '<DL></DL>',
15742     'list', '',
15743     'par', '<P></P>'
15744    ) if ($HTML_VERSION == 2.0 );
15745
15746    &generate_declaration_subs;	# Generate code to handle declarations
15747
15748    # ...but these block-level divisions must be handled differently...
15749%declarations = (
15750     'quote', '<BLOCKQUOTE></BLOCKQUOTE>',
15751     'quotation', '<BLOCKQUOTE></BLOCKQUOTE>',
15752     'verse', '<BLOCKQUOTE></BLOCKQUOTE>',
15753     'preform', '<PRE></PRE>',
15754     'unord', '<UL></UL>',
15755     'ord', '<OL></OL>',
15756     'desc', '<DL></DL>',
15757#     'list', '<DIV></DIV>',
15758     'par', '<P></P>',
15759     'samepage', '',
15760#     'centering', '<DIV ALIGN="CENTER"></DIV>',
15761     'center', '<DIV ALIGN="CENTER"></DIV>',
15762     'flushleft', '<DIV ALIGN="LEFT"></DIV>',
15763     'raggedright', '<DIV ALIGN="LEFT"></DIV>',
15764     'flushright', '<DIV ALIGN="RIGHT"></DIV>',
15765     'raggedleft', '<DIV ALIGN="RIGHT"></DIV>',
15766     %declarations
15767    ) if ($HTML_VERSION > 2.0 );
15768
15769
15770    %section_commands =
15771	('partstar' , '1' , 'chapterstar', '2', 'sectionstar', '3'
15772	, 'subsectionstar', '4', 'subsubsectionstar', '5', 'paragraphstar'
15773	, '6', 'subparagraphstar', '7'
15774	, 'part' , '1' , 'chapter', '2', 'section', '3','subsection', '4'
15775	, 'subsubsection', '5', 'paragraph', '6', 'subparagraph', '7'
15776	, 'slidehead', '3', %section_commands);
15777    # The tableofcontents, listoffigures, listoftables, bibliography and
15778    # textohtmlindex are set after determining what is the outermost level
15779    # in sub set_depth_levels. Appendix is implemented as a command.
15780
15781    %standard_section_headings =
15782	('part' , 'H1' , 'chapter' , 'H1', 'section', 'H1', 'subsection', 'H2'
15783	, 'subsubsection', 'H3', 'paragraph', 'H4', 'subparagraph', 'H5'
15784	, %standard_section_headings );
15785
15786    # Generates code to handle sectioning commands
15787    # for those sections which take an argument.
15788    &generate_sectioning_subs;
15789
15790    %section_headings =
15791	('partstar' , 'H1' , 'chapterstar' , 'H1', 'sectionstar', 'H1'
15792	, 'subsectionstar', 'H2', 'subsubsectionstar', 'H3', 'paragraphstar'
15793	, 'H4', 'subparagraphstar', 'H5', %section_headings);
15794
15795    # These need their own custom code but are treated as sectioning commands
15796    %section_headings =
15797	('tableofcontents', 'H2', 'listoffigures', 'H2', 'listoftables', 'H2'
15798	, 'bibliography', 'H2', 'textohtmlindex', 'H2'
15799	, %standard_section_headings
15800	, %section_headings);
15801
15802    &generate_accent_commands;	# Code to handle accent commands
15803
15804    # These are replaced as soon as the text is read in.
15805    %html_specials = (  '<', ';SPMlt;'
15806		,  '>', ';SPMgt;'
15807		,  '&', ';SPMamp;'
15808#		,  '``', '\lq\lq '  # probably not a good idea
15809#		,  "''", '\rq\rq ',  # probably not a good idea
15810		,  '"', ';SPMquot;'
15811		);
15812
15813    %html_specials = ( %html_specials
15814		, '``', ';SPMldquo;', "''", ';SPMrdquo;'
15815		) if ($HTML_VERSION >= 5 );
15816
15817    # This mapping is needed in sub revert_to_raw_tex
15818    # before passing stuff to latex for processing.
15819    %html_specials_inv = (
15820    		 ';SPMlt;' ,'<'
15821		, ';SPMgt;','>'
15822		, ';SPMamp;','&'
15823		, ';SPMquot;','"'
15824		, ';SPMldquo;','``'
15825		, ';SPMrdquo;',"''"
15826		, ';SPMdollar;', '$'	# for alltt
15827		, ';SPMpct;', '%'
15828		, ';SPMtilde;', '&#126;'
15829		);
15830
15831    # normalsize vertical dimension factors for 12pt (1.0 <=> <BR>)
15832    %vspace_12pt = ('ex', 1.0, 'em', 1.0, 'pt', 0.1, 'pc', 1.0,
15833	'in', 6.0, 'bp', 0.1, 'cm', 2.3, 'mm', 0.2, 'dd', 0.1,
15834	'cc', 1.0, 'sp', 0.0);
15835
15836    # For some commands such as \\, \, etc it is not possible to define
15837    # perl subroutines because perl does not allow some non-ascii characters
15838    # in subroutine names. So we define a table and a subroutine to relate
15839    # such commands to ascii names.
15840    %normalize = ('\\', 'd_backslash'
15841		  , '/', 'esc_slash', "`", 'grave'
15842		  , "'", 'acute', "^", 'hat', '"', 'ddot'
15843		  , '~', 'tilde', '.', 'dot', '=', 'bar'
15844		  , '{', 'lbrace' , '}', 'rbrace', '|', 'Vert'
15845		  , '#', 'esc_hash', '$', 'esc_dollar'
15846                 );
15847
15848    %text_accent = (  'cedil','c', 'bdot','d', 'b','b' , 'tilde','~'
15849                    , 'circ' ,'^', 'hat','^', 'check','v' , 'caron','v'
15850                    , 'acute','\'' , 'grave','`' , 'dot','.' , 'breve','u'
15851                    , 'ddot','"' , 'uml','"' , 'bar','=','macr','='
15852                    , 'dblacc','H' , 't','t' , 'ogon','k' , 'ring','r'
15853                  );
15854
15855    # %languages_translations holds for each known language the
15856    # appropriate translation function. The function is called in
15857    # slurp_input.
15858    # The translation functions subtitute LaTeX macros
15859    # with ISO-LATIN-1 character references
15860    %language_translations = (
15861	   'english',	'english_translation'
15862	 , 'USenglish',	'english_translation'
15863	 , 'original',	'english_translation'
15864	 , 'german',	'german_translation'
15865	 , 'austrian',	'german_translation'
15866	 , 'finnish',	'finnish_translation'
15867	 , 'french',	'french_translation'
15868	 , 'spanish',	'spanish_translation'
15869	 , 'swedish',	'swedish_translation'
15870	 , 'turkish',	'turkish_translation'
15871	);
15872
15873# Reiner:
15874#    $standard_label_rx =
15875#	"\\s*[[]\\s*(((\$any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";
15876#    $enum_label_rx = "^((({[^{}]*})|([^{}]))*)([aAiI1])(.*)";
15877#    $enum_level = 0;	# level for enumerate (1-4, i-iv)
15878    %enum = (
15879		'enumi',	0,			# counter for level 1
15880		'enumii',	0,			# counter for level 2
15881		'enumiii',	0,
15882		'enumiv',	0,
15883		'theenumi',	"&arabic('enumi')",	# eval($enum{"theenumi"})
15884		'theenumii',	"&alph('enumii')",
15885		'theenumiii',	"&roman('enumiii')",
15886		'theenumiv',	"&Alph('enumiv')",
15887			# e.g. eval("$enum{'labelenumi'}")
15888		'labelenumi',	'eval($enum{"theenumi"}) . "."',
15889		'labelenumii',	'"(" . eval($enum{"theenumii"}) . ")"',
15890		'labelenumiii',	'eval($enum{"theenumiii"}) . "."',
15891		'labelenumiv',	'eval($enum{"theenumiv"}) . "."'
15892		);
15893
15894    %RomanI = ( '1',"I",'2',"II",'3',"III",'4',"IV"
15895		    ,'5',"V",'6',"VI",'7',"VII", '8',"VIII",'9',"IX");
15896    %RomanX = ( '1',"X",'2',"XX",'3',"XXX",'4',"XL"
15897		    ,'5',"L",'6',"LX",'7',"LXX", '8',"LXXX",'9',"XC");
15898    %RomanC = ( '1',"C",'2',"CC",'3',"CCC",'4',"CD"
15899		    ,'5',"D",'6',"DC",'7',"DCC", '8',"DCCC",'9',"CM");
15900    %RomanM = ( '1',"M",'2',"MM",'3',"MMM",'4',"MH"
15901		    ,'5',"H",'6',"HM",'7',"HMM",'8',"HMMM");
15902
15903    %enum_label_funcs = (
15904	"a", "alph", "A", "Alph", "i", "roman", "I", "Roman", "1", "arabic" );
15905
15906sub farabic{
15907    local($_)=@_;
15908    $_;
15909}
15910sub arabic{
15911    local($_)=@_;
15912    eval($enum{$_});
15913}
15914
15915sub falph{
15916    local($num)=@_;
15917#    chr($num+64);
15918    substr(" abcdefghijklmnopqrstuvwxyz",$num,1)
15919}
15920sub alph{
15921    local($num)=@_;
15922    &falph(eval($enum{$num}));
15923}
15924sub fAlph{
15925    local($num)=@_;
15926#    chr($num+32);
15927    substr(" ABCDEFGHIJKLMNOPQRSTUVWXYZ",$num,1)
15928}
15929sub Alph{
15930    local($num)=@_;
15931    &falph(eval($enum{$num}));
15932}
15933
15934sub Roman{
15935    local($num)=@_;
15936    &fRoman(eval($enum{$num}));
15937}
15938sub fRoman{
15939    local($num)=@_;
15940    local($RmI)= $num%10; ($RmI) = (($RmI) ? $RomanI{"$RmI"} : '' );
15941    $num = $num/10; local($RmX)= $num%10; ($RmX) = (($RmX) ? $RomanX{"$RmX"} : '' );
15942    $num = $num/10; local($RmC)= $num%10; ($RmC) = (($RmC) ? $RomanC{"$RmC"} : '' );
15943    $num = $num/10; local($RmM)= $num%10; ($RmM) = (($RmM) ? $RomanM{"$RmM"} : '' );
15944    "$RmM" . "$RmC" . "$RmX" . "$RmI";
15945}
15946sub froman{
15947    local($_)=@_;
15948    $_ = &fRoman($_);
15949    $_ =~ tr/A-Z/a-z/;
15950    $_;
15951}
15952sub roman{
15953    local($num)=@_;
15954    &froman(eval($enum{$num}));
15955}
15956
15957
15958    %unitscale = ("in",72,"pt",72.27/72,"pc",12,"mm",72/25.4,"cm",72/2.54
15959		  ,"\\hsize",100,"\\vsize",100
15960		  ,"\\textwidth",100,"\\textheight",100
15961		  ,"\\pagewidth",100,"\\linewidth",100
15962		  );
15963    %units = ("in","in","pt","pt","pc","pi","mm","mm","cm","cm"
15964	      ,"\\hsize","%","\\vsize","%","\\textwidth","%","\\textheight","%");
15965
15966sub convert_length { # clean
15967    my ($this,$scale) = @_;
15968    $scale = 1 unless $scale;
15969    my ($pxs,$len,$full);
15970    if ( $this =~ /([\d.]*)\s*(in|pt|pc|mm|cm|\\[hv]size|\\\w+(width|height))?/ ) {
15971	$len = ($1 ? $1 : 1); $full = $2;
15972	if ($full &&($full =~ /\\([hv]size|\w+(width|height))/)) { $scale = 1;};
15973	$pxs = (($full) ? int($len * $unitscale{$full}*$scale + 0.5)
15974		 : int($len*$scale + .5) );
15975	if ( $full =~ /\\([hv]size|\w+(width|height))/) { $pxs .= '%';};
15976    };
15977    ($pxs,$len);
15978}
15979
15980
15981
15982
15983    # Inclusion in this list will cause a command or an environment to be ignored.
15984    # This is suitable for commands without arguments and for environments.
15985    # If however a do_env|cmd_<env|cmd> exists then it will be used.
15986    %ignore = ('sloppypar', 1,  'document', 1, 'newblock', 1,
15987	       ',', 1,  '@', 1, ' ', 1,  '-', 1,
15988               'sloppy', 1,
15989	       'hyphen', 1, 'titlepage', 1, 'htmlonly', 1,
15990	       'flushleft', 1, 'flushright', 1, 'slide', 1,
15991	       'tiny', 1, 'Tiny', 1, 'scriptsize', 1, 'footnotesize', 1,
15992	       'small', 1, 'normalsize', 1, 'large', 1, 'Large', 1,
15993	       'LARGE', 1, 'huge', 1, 'Huge', 1,
15994	       %ignore);
15995
15996    # Specify commands with arguments that should be ignored.
15997    # Arbitrary code can be placed between the arguments
15998    # to be executed while processing the command.
15999    #
16000# Note that some commands MAY HAVE ARGUMENTS WHICH SHOULD BE LEFT AS TEXT
16001    # EVEN THOUGH THE COMMAND IS IGNORED (e.g. hbox, center, etc)
16002
16003&ignore_commands( <<_IGNORED_CMDS_);
16004NeedsTeXFormat # {} # []
16005ProvidesClass # {} # []
16006ProvidesFile # {} # []
16007ProvidesPackage # {} # []
16008abovedisplayskip # &ignore_numeric_argument
16009abovedisplayshortskip # &ignore_numeric_argument
16010addcontentsline # {} # {} # {}
16011addtocontents # {} # {}
16012addvspace # {} # &ignore_numeric_argument
16013and
16014and # \$_ = join(''," - ",\$_)
16015backmatter
16016baselineskip # &ignore_numeric_argument
16017belowdisplayskip # &ignore_numeric_argument
16018belowdisplayshortskip # &ignore_numeric_argument
16019bibdata
16020bibliographystyle # {}
16021bibstyle # {}
16022bigskipamount # &ignore_numeric_argument
16023smallskipamount # &ignore_numeric_argument
16024medskipamount # &ignore_numeric_argument
16025center
16026citation # {}
16027citeauthoryear
16028clearpage
16029cline # {}
16030#documentclass # [] # {}
16031#documentstyle # [] # {}
16032#end # {}
16033enlargethispage # {}
16034evensidemargin # &ignore_numeric_argument
16035filecontents
16036filbreak
16037fil
16038fill
16039flushbottom
16040fontsize # {} # {}
16041footheight # &ignore_numeric_argument
16042footskip  # &ignore_numeric_argument
16043frontmatter
16044fussy
16045global
16046goodbreak
16047hbox
16048headheight # &ignore_numeric_argument
16049headsep # &ignore_numeric_argument
16050hfil
16051hfill
16052hfuzz # &ignore_numeric_argument
16053hline
16054hspace # {} # \$_ = join(''," ",\$_)
16055hspacestar # {} # \$_ = join(''," ",\$_)
16056html
16057ifcase
16058ignorespaces
16059indent
16060itemindent # &ignore_numeric_argument
16061itemsep # &ignore_numeric_argument
16062labelsep # &ignore_numeric_argument
16063labelwidth # &ignore_numeric_argument
16064leavevmode
16065leftmargin # &ignore_numeric_argument
16066listparindent # &ignore_numeric_argument
16067lower # &ignore_numeric_argument
16068long
16069mainmatter
16070makebox # [] # []
16071makeindex
16072marginpar # {}
16073marginparsep # &ignore_numeric_argument
16074marginparwidth # &ignore_numeric_argument
16075markboth # {} # {}
16076markright # {}
16077mathord
16078mathbin
16079mathindent # &ignore_numeric_argument
16080mathrel
16081mathop
16082mathtt
16083#mdseries
16084newpage
16085#newedboolean # {}
16086#newedcommand # {} # [] # [] # {}
16087#newedcounter # {} # []
16088#newedenvironment # {} # [] # [] # {} # {}
16089#newedtheorem # {} # [] # {} # []
16090#providedcommand # {} # [] # [] # {}
16091#renewedcommand # {} # [] # [] # {}
16092#renewedenvironment # {} # [] # [] # {} # {}
16093nobreakspace # \$_ = join('',";SPMnbsp;",\$_)
16094nonbreakingspace # \$_ = join('',";SPMnbsp;",\$_)
16095noalign
16096nobreak
16097nocite # {}
16098noindent
16099nolinebreak# []
16100nopagebreak #[]
16101normalmarginpar
16102numberline
16103oddsidemargin # &ignore_numeric_argument
16104omit
16105onecolumn
16106outer
16107pagenumbering #{}
16108pagestyle # {}
16109parindent # &ignore_numeric_argument
16110parsep # &ignore_numeric_argument
16111parskip # &ignore_numeric_argument
16112partopsep # &ignore_numeric_argument
16113penalty # &ignore_numeric_argument
16114phantom # {}
16115protect
16116raggedright
16117raggedbottom
16118raise # &ignore_numeric_argument
16119raisebox # {} # [] # []
16120relax
16121reversemarginpar
16122rightmargin # &ignore_numeric_argument
16123#rmfamily
16124rule # [] # {} # {}
16125samepage
16126selectfont
16127startdocument # \$SEGMENT=1;\$SEGMENTED=1; \$_
16128strut
16129suppressfloats # []
16130textheight # &ignore_numeric_argument
16131textwidth # &ignore_numeric_argument
16132textnormal
16133#textrm
16134textup
16135theorempreskipamount # &ignore_numeric_argument
16136theorempostskipamount # &ignore_numeric_argument
16137thispagestyle # {}
16138topmargin # &ignore_numeric_argument
16139topsep # &ignore_numeric_argument
16140topskip # &ignore_numeric_argument
16141twocolumn
16142unskip
16143#upshape
16144vfil
16145vfill
16146vfilll
16147vline
16148_IGNORED_CMDS_
16149
16150    # Commands which need to be passed, ALONG WITH THEIR ARGUMENTS, to TeX.
16151    # Note that this means that the arguments should *not* be translated,
16152    # This is handled by wrapping the commands in the dummy tex2html_wrap
16153    # environment before translation begins ...
16154
16155    # Also it can be used to specify environments which may be defined
16156    # using do_env_* but whose contents will be passed to LaTeX and
16157    # therefore should not be translated.
16158    # Note that this code squeezes spaces out of the args of psfig;
16159
16160
16161    # Images are cropped to the minimum bounding-box for these...
16162
16163&process_commands_in_tex (<<_RAW_ARG_CMDS_);
16164psfig # {} # \$args =~ s/ //g;
16165usebox # {}
16166framebox # [] # [] # {}
16167_RAW_ARG_CMDS_
16168
16169    # ... but these are set in a box to measure height/depth
16170    # so that white space can be preserved in the images.
16171
16172&process_commands_inline_in_tex (<<_RAW_ARG_CMDS_);
16173#etalchar # {} \$args =~ s/(.*)/\$\^\{\$1\}\\\$/o;
16174fbox # {}
16175#frac # [] # {} # {}
16176dag
16177ddag
16178l
16179L
16180oe
16181OE
16182textexclamdown
16183textquestiondown
16184textregistered
16185textperiodcentered
16186#textcircled # {}
16187#raisebox # {} # [] # [] # {}
16188_RAW_ARG_CMDS_
16189
16190
16191
16192# These are handled by wrapping the commands in the dummy tex2html_nowrap
16193# environment before translation begins. This environment will be
16194# stripped off later, when the commands are put into  images.tex  ...
16195
16196&process_commands_nowrap_in_tex (<<_RAW_ARG_NOWRAP_CMDS_);
16197#begingroup
16198#endgroup
16199#bgroup
16200#egroup
16201errorstopmode
16202nonstopmode
16203scrollmode
16204batchmode
16205psfigurepath # {}
16206pssilent
16207psdraft
16208psfull
16209thinlines
16210thicklines
16211linethickness # {}
16212hyphenation # {}
16213hyphenchar # \\ # &get_numeric_argument
16214hyphenpenalty # &get_numeric_argument
16215#let # \\ # <<\\(\\W|\\w+)>>
16216newedboolean # {}
16217newedcommand # {} # [] # [] # {}
16218newedcounter # {} # []
16219newedenvironment # {} # [] # [] # {} # {}
16220newedtheorem # {} # [] # {} # []
16221#providedcommand # {} # [] # [] # {}
16222#renewedcommand # {} # [] # [] # {}
16223#renewedenvironment # {} # [] # [] # {} # {}
16224DeclareMathAlphabet # {} # {} # {} # {} # {}
16225SetMathAlphabet # {} # {} # {} # {} # {} # {}
16226DeclareMathSizes # {} # {} # {} # {}
16227DeclareMathVersion # {}
16228DeclareSymbolFont # {} # {} # {} # {} # {}
16229DeclareSymbolFontAlphabet # {} # {}
16230DeclareMathSymbol # {} # {} # {} # {}
16231SetSymbolFont # {} # {} # {} # {} # {} # {}
16232DeclareFontShape # {} # {} # {} # {} # {} # {}
16233DeclareFontFamily # {} # {} # {}
16234DeclareFontEncoding # {} # {} # {}
16235DeclareFontSubstitution # {} # {} # {} # {}
16236mathversion # {}
16237#newfont # {} # {}
16238#normalfont
16239#rmfamily
16240#mdseries
16241newlength # {}
16242setlength # {} # {}
16243addtolength # {} # {}
16244settowidth # {}# {}
16245settoheight # {} # {}
16246settodepth # {} # {}
16247newsavebox # {}
16248savebox # {} # [] # {}
16249sbox # {} # {}
16250setbox # {}
16251TagsOnLeft  # \$EQN_TAGS = \"L\" if \$PREAMBLE;
16252TagsOnRight # \$EQN_TAGS = \"R\" if \$PREAMBLE;
16253_RAW_ARG_NOWRAP_CMDS_
16254
16255
16256&process_commands_wrap_deferred (<<_RAW_ARG_DEFERRED_CMDS_);
16257alph # {}
16258Alph # {}
16259arabic # {}
16260author # [] # {}
16261boldmath
16262unboldmath
16263captionstar # [] # {}
16264caption # [] # {}
16265#endsegment # []
16266#segment # [] # {} # {} # {}
16267fnsymbol # {}
16268footnote # [] # {}
16269footnotemark # []
16270footnotetext # [] # {}
16271#thanks # {}
16272roman # {}
16273Roman # {}
16274#mbox # {}
16275parbox # [] # [] # [] # {} # {}
16276#selectlanguage # [] # {}
16277setcounter # {} # {}
16278addtocounter # {} # {}
16279stepcounter # {}
16280refstepcounter # {}
16281value # {}
16282par
16283hrule # &ignore_numeric_argument
16284linebreak # []
16285pagebreak # []
16286newfont # {} # {}
16287smallskip
16288medskip
16289bigskip
16290centering
16291raggedright
16292raggedleft
16293itshape
16294#textit # {}
16295upshape
16296slshape
16297#scshape
16298rmfamily
16299sffamily
16300ttfamily
16301mdseries
16302bfseries
16303#textbf # {}
16304em
16305normalfont
16306it
16307rm
16308sl
16309bf
16310tt
16311sf
16312Tiny
16313tiny
16314scriptsize
16315footnotesize
16316small
16317Small
16318SMALL
16319normalsize
16320large
16321Large
16322LARGE
16323huge
16324Huge
16325lowercase # {}
16326uppercase # {}
16327MakeLowercase # {}
16328MakeUppercase # {}
16329htmlinfo # []
16330htmlinfostar # []
16331tableofchildlinks # []
16332tableofchildlinksstar # []
16333tableofcontents
16334listoffigures
16335listoftables
16336thepart
16337thepage
16338thechapter
16339thesection
16340thesubsection
16341thesubsubsection
16342theparagraph
16343thesubparagraph
16344theequation
16345htmltracenv # {}
16346HTMLsetenv # [] # {} # {}
16347#newedboolean # {}
16348#newedcounter # {} # []
16349#newedcommand # {} # [] # [] # {}
16350#newedtheorem # {} # [] # {} # []
16351#newedenvironment # {} # [] # [] # {} # {}
16352providedcommand # {} # [] # [] # {}
16353renewedcommand # {} # [] # [] # {}
16354renewedenvironment # {} # [] # [] # {} # {}
16355url # {}
16356htmlurl # {}
16357latextohtml
16358TeX
16359LaTeX
16360LaTeXe
16361LaTeXiii
16362Xy
16363MF
16364AmS
16365AmSTeX
16366textcircled # {}
16367_RAW_ARG_DEFERRED_CMDS_
16368
16369
16370#rrm
16371# implement the XBit-Hack for Apache servers, to handle
16372# Server-Side Includes (SSIs) with .html filename extension
16373#
16374sub check_htaccess {
16375    my $access_file = '.htaccess';
16376    my $has_access = '';
16377    local $_;
16378    print "\nChecking for .htaccess  file";
16379    if (-f $access_file) {
16380	print STDOUT " ... found";
16381	open(HTACCESS, "<$access_file");
16382	while (<HTACCESS>) {
16383	    if (/^\s*XBitHack\s*on\s*$/) {
16384		print STDOUT " with XBitHack on";
16385		$has_access =1; last;
16386	    };
16387	}
16388	print STDOUT "\n";
16389	close HTACCESS;
16390	return() if $has_access;
16391	open (HTACCESS, ">>$access_file");
16392	&write_warnings("appended to .htaccess in $DESTDIR");
16393    } else {
16394	open (HTACCESS, ">$access_file");
16395	chmod 0644, $access_file;
16396	&write_warnings("created .htaccess file in $DESTDIR");
16397    }
16398    print HTACCESS "\nXBitHack on\n";
16399    close HTACCESS;
16400}
16401
16402# This maps the HTML mnemonic names for the ISO-LATIN-1 character references
16403# to their numeric values. When converting latex specials characters to
16404# ISO-LATIN-1 equivalents I use the numeric values because this makes any
16405# conversion back to latex (using revert_raw_tex) more reliable (in case
16406# the text contains "&mnemonic_name"). Errors may occur if an environment
16407# passed to latex (e.g. a table) contains the numeric values of character
16408# references.
16409
16410# RRM: removed this portion; load from  latin1.pl instead
16411#&do_require_extension('latin1');
16412
16413sub make_isolatin1_rx {
16414    local($list) = &escape_rx_chars(join($CD,(values %iso_8859_1_character_map_inv)));
16415    $list =~ s/$CD/|/g;
16416    $isolatin1_rx = "($list)";
16417}
16418
16419
16420    ################### Frequently used regular expressions ###################
16421    # $1 : preamble
16422
16423    $preamble_rx = "(^[\\s\\S]*)(\\\\begin\\s*$O\\d+$C\\s*document\\s*$O\\d+$C|\\\\startdocument)";
16424
16425    # \d (number) should sometimes also be a delimiter but this causes
16426    # problems with command names  that are allowed to contain numbers (eg tex2html)
16427    # \d is a delimiter with commands which take numeric arguments?
16428    # JCL: I can't see that. \tex2html is also no valid LaTeX (or TeX).
16429    # It is parsed \tex 2html, and \tex may take 2html as argument, but this
16430    # is invalid LaTeX. \d must be treated as delimiter.
16431
16432# JCL(jcl-del) - Characters to be treated as letters, everything else
16433# is a delimiter.
16434    # internal LaTeX command separator, shouldn't be equal to $;
16435    $CD = "\001";
16436    &make_cmd_spc_rx; # determines space to follow a letter command
16437#old    $delimiters = '\'\\s[\\]\\\\<>(=).,#;:~\/!-';
16438    $letters = 'a-zA-Z';
16439    $delimiter_rx = "([^$letters])";
16440#
16441
16442    # liberalized environment names (white space, optional arg, interpunctuation signs etc.)
16443    # $1 : br_id, $2 : <environment>
16444    $begin_env_rx="(\\\\protect)?\\\\begin\\s*(\\[([^\\]]*)])?$O(\\d+)$C\\s*([^'[\\]\\\\#~]+)\\s*$O\\4$C";
16445    $begin_env_pr_rx="(\\\\protect)?\\\\begin\\s*(\\[([^\\]]*)])?$OP(\\d+)$CP\\s*([^'[\\]\\\\#~]+)\\s*$OP\\4$CP";
16446
16447    $mbox_rx = "\\\\mbox\\s*";
16448
16449    $match_br_rx = "\\s*$O\\d+$C\\s*";
16450
16451    $opt_arg_rx = "\\s*\\[([^\\]]*)\\]\\s*";	# Cannot handle nested []s!
16452    $optional_arg_rx = "^\\s*\\[([^]]*)\\]";	# Cannot handle nested []s!
16453
16454    $block_close_rx = "^<\\/(DIV|P|BLOCKQUOTE)>\$";
16455    $all_close_rx = "^<\\/(BODY|PRE|OL|UL|DL|FORM|ADDRESS)>\$";
16456
16457    # Matches a pair of matching brackets
16458    # $1 : br_id
16459    # $2 : contents
16460    $next_pair_rx = "^[\\s%]*$O(\\d+)$C([\\s\\S]*)$O\\1$C($comment_mark\\d*\\n?)?";
16461
16462    # will comments be a problem after these ???
16463    $any_next_pair_rx = "$O(\\d+)$C([\\s\\S]*)$O\\1$C";
16464    $any_next_pair_rx4 = "$O(\\d+)$C([\\s\\S]*)$O\\4$C";
16465    $any_next_pair_pr_rx4 = "$OP(\\d+)$CP([\\s\\S]*)$OP\\4$CP";
16466    $any_next_pair_rx5 = "$O(\\d+)$C([\\s\\S]*)$O\\5$C";
16467    $any_next_pair_rx6 = "$O(\\d+)$C([\\s\\S]*)$O\\6$C";
16468
16469    # used for labels in {enumerate} environments
16470    $standard_label_rx =
16471	"\\s*[[]\\s*((($any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";
16472    $enum_label_rx = "^((({[^{}]*})|([^{}]))*)([aAiI1])(.*)";
16473    $enum_level = 0;	# level for enumerate (1-4, i-iv)
16474
16475
16476    # Matches the \ensuremath command
16477    $enspair = "\\\\ensuremath\\s*" . $any_next_pair_rx;
16478#    $enspair = "\\\\ensuremath\\s*$O(\\d+)$C([\\s\\S]*[\\\\\$&]+[\\s\\S]*)$O\\1$C";
16479
16480    # Matches math comments, from  math.pl
16481    $math_verbatim_rx = "$verbatim_mark#math(\\d+)#";
16482    $mathend_verbatim_rx = "$verbatim_mark#mathend([^#]*)#";
16483
16484    # Matches math array environments
16485    $array_env_rx = "array|cases|\\w*matrix";
16486
16487    # initially empty; has a value in HTML 3.2 and 4.0
16488    $math_class = '' unless ($math_class);
16489    $eqno_class = '' unless ($eqno_class);
16490
16491    # Matches to end-of-line and subsequent spaces
16492    $EOL = "[ \\t]*\\n?";
16493
16494    # Matches wrapped \par command
16495    $par_rx = "\\n?\\\\begin(($O|$OP)\\d+($C|$CP))tex2html_deferred\\1\\\\par\\s\*"
16496        . "\\\\end(($O|$OP)\\d+($C|$CP))tex2html_deferred\\4\\n?";
16497
16498    # $1 : br_id
16499    $begin_cmd_rx = "$O(\\d+)$C";
16500
16501    # $1 : image filename prefix
16502    $img_rx = "(\\w*T?img\\d+)";
16503
16504    # $1 : largest argument number
16505    $tex_def_arg_rx = "^[#0-9]*#([0-9])($O|$OP)";
16506
16507    #   only some non-alphanumerics are allowed in labels,  Why?
16508    $label_rx = "[^\\w\.\\\-\\\+\\\:]";
16509
16510#JCL(jcl-del) - new face, see also &do_cmd_makeatletter et.al.
16511#    $cmd_delims = q|-#,.~/\'`^"=\$%&_{}@|; # Commands which are also delimiters!
16512#    $single_cmd_atletter_rx = "\\\\([a-zA-Z\\\@]+\\*?|[$cmd_delims]|\\\\)";
16513#    $single_cmd_atother_rx = "\\\\([a-zA-Z]+\\*?|[$cmd_delims]|\\\\)";
16514    # $1 : declaration or command or newline (\\)
16515    &make_single_cmd_rx;
16516#
16517
16518    # $1 : description in a list environment
16519    $item_description_rx =
16520#	"\\\\item\\s*[[]\\s*((($any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";
16521	"\\\\item\\s*[[]\\s*((($any_next_pair_pr_rx4)|([[][^]]*[]])|[^]])*)[]]";
16522
16523    $fontchange_rx = 'rm|em|it|sl|sf|tt|sc|upshape|normalfont';
16524    $fontweight_rx = 'bf|mdseries|normalfont';
16525    $colorchange_rx = "(text)?color\\s*(\#\\w{6})?";
16526    $sizechange_rx = 'tiny|Tiny|scriptsize|footnotesize|small|Small|SMALL' .
16527	'|normalsize|large|Large|LARGE|huge|Huge';
16528
16529#    $image_switch_rx = "makeimage";
16530    $image_switch_rx = "makeimage|scshape|sc";
16531    $env_switch_rx = "writetolatex";
16532    $raw_arg_cmds{'font'} = 1;
16533
16534    # Matches the \caption command
16535    # $1 : br_id
16536    # $2 : contents
16537     $caption_suffixes = "lof|lot";
16538#    $caption_rx = "\\\\caption\\s*([[]\\s*((($any_next_pair_rx5)|([[][^]]*[]])|[^]])*)[]])?$O(\\d+)$C([\\s\\S]*)$O\\8$C$EOL";
16539
16540    $caption_rx = "\\\\(top|bottom|table)?caption\\s*\\\*?\\s*([[]\\s*((($any_next_pair_rx6)|([[][^]]*[]])|[^]])*)[]])?$O(\\d+)$C([\\s\\S]*)$O\\9$C$EOL";
16541    $caption_width_rx = "\\\\setlength\\s*(($O|$OP)\\d+($C|$CP))\\\\captionwidth\\1\\s*(($O|$OP)\\d+($C|$CP))([^>]*)\\4";
16542
16543    # Matches the \htmlimage command
16544    # $1 : br_id
16545    # $2 : contents
16546    $htmlimage_rx = "\\\\htmlimage\\s*$O(\\d+)$C([\\s\\S]*)$O\\1$C$EOL";
16547    $htmlimage_pr_rx = "\\\\htmlimage\\s*$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP$EOL";
16548
16549    # Matches the \htmlborder command
16550    # $1 : optional argument...
16551    # $2 : ...contents  i.e. extra attributes
16552    # $3 : br_id
16553    # $4 : contents i.e. width
16554    $htmlborder_rx = "\\\\htmlborder\\s*(\\[([^]]*)\\])?\\s*$O(\\d+)$C(\\d*)$O\\3$C$EOL";
16555    $htmlborder_pr_rx = "\\\\htmlborder\\s*(\\[([^]]*)\\])?\\s*$OP(\\d+)$CP(\\d*)$OP\\3$CP$EOL";
16556
16557    # Matches a pair of matching brackets
16558    # USING PROCESSED DELIMITERS;
16559    # (the delimiters are processed during command translation)
16560    # $1 : br_id
16561    # $2 : contents
16562#    $next_pair_pr_rx = "^[\\s%]*$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP";
16563    $next_pair_pr_rx = "^[\\s%]*$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP($comment_mark\\d*\\n?)?";
16564    $any_next_pair_pr_rx = "$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP($comment_mark\\d*\\n?)?";
16565    $next_token_rx = "^[\\s%]*(\\\\[A-Za-z]+|\\\\[^a-zA-Z]|.)";
16566
16567    $HTTP_start = 'http:';
16568
16569    # This will be used to recognise escaped special characters as such
16570    # and not as commands
16571    $latex_specials_rx = '[\$]|&|%|#|{|}|_';
16572    $html_escape_chars = '<>&';
16573
16574    # This is used in sub revert_to_raw_tex before handing text to be processed
16575    # by latex.
16576    $html_specials_inv_rx = join("|", keys %html_specials_inv);
16577
16578    # These are used for direct replacements in/from  ALT=... strings
16579    %html_special_entities = ('<','lt','>','gt','"','quot','&','amp');
16580    %html_spec_entities_inv = ('lt','<','gt','>','quot','"','amp','&');
16581
16582    # This is also used in sub revert_to_raw_tex
16583    $character_entity_rx = '(&#(\d+);)';
16584    $named_entity_rx = '&(\w+);';
16585
16586    #commands for altering theorem-styles
16587    $theorem_cmd_rx = 'theorem(style|(header|body)font)';
16588
16589
16590    # Matches a \begin or \end {tex2html_wrap}. Also used by revert_to_raw_tex
16591    $tex2html_wrap_rx = '\\\\(begin|end)\\s*\{\\s*(tex2html_(wrap|nowrap|deferred|nomath|preform|\\w*_inline)[_a-z]*|makeimage)\\s*\}'."($EOL)";
16592    $tex2html_deferred_rx = '\\\\(begin|end)(<<\\d+>>)tex2html_deferred\\2';
16593    $tex2html_deferred_rx2 = '\\\\(begin|end)(<<\\d+>>)tex2html_deferred\\4';
16594    $tex2html_envs_rx = "\\\\(begin|end)\\s*(($O|$OP)\\d+($C|$CP))\\s*(tex2html_(wrap|nowrap|deferred|nomath|preform|\w+_inline)[_a-z]*||makeimage)\\s*\\2";
16595
16596    # The first empty parenthese pair is for non-letter commands.
16597    # $2: meta command, $4: delimiter (may be empty)  ignore the *-version distinction
16598#    $meta_cmd_rx = "()\\\\(providecommand|renewcommand|renewenvironment|newcommand|newenvironment|newtheorem|newcounter|newboolean|newif|let)(([^$letters$cmd_spc])|$cmd_spcs_rx)";
16599    $meta_cmd_rx = "()\\\\(providecommand|renewcommand|renewenvironment|newcommand|newenvironment|newtheorem|newcounter|newboolean|newif|DeclareRobustCommand|DeclareMathOperator\\*?)\\\*?(([^$letters$cmd_spc])|$cmd_spcs_rx)";
16600
16601    &make_counters_rx;
16602
16603    # Matches a label command and its argument
16604    $labels_rx = "\\\\label\\s*$O(\\d+)$C([\\s\\S]*)$O\\1$C$EOL";
16605    $labels_rx8 = "\\\\label\\s*$O(\\d+)$C([\\s\\S]*)$O\\8$C$EOL";
16606
16607    # Matches environments that should not be touched during the translation
16608#   $verbatim_env_rx = "\\s*{(verbatim|rawhtml|LVerbatim)[*]?}";
16609    $verbatim_env_rx = "\\s*(\\w*[Vv]erbatim|rawhtml|imagesonly|tex2html_code)[*]?";
16610    $image_env_rx = "\\s*(picture|xy|diagram)[*]?";
16611    $keepcomments_rx = "\\s*(picture|makeimage|xy|diagram)[*]?";
16612
16613    # names of different math environment types
16614    $display_env_rx = "displaymath|makeimage|eqnarray|equation";
16615    $inline_env_rx = "inline|indisplay|entity|xy|diagram";
16616    $sub_array_env_rx = "array|(small|\\w)\?matrix|tabular|cases";
16617
16618    # Matches environments needing pre-processing for images
16619    $pre_processor_env_rx = "\\\\(begin|end)\\s*(($O|$OP|\{)\\d+($C|$CP|\}))pre_(\\w+)\\2";
16620
16621    # Matches icon markers
16622    $icon_mark_rx = "<tex2html_(" . join("|", keys %icons) . ")>";
16623
16624    $start_time = time;
16625    print STDOUT join(" ", "Starting at", $start_time, "seconds\n")
16626        if ($TIMING||$DEBUG||($VERBOSITY>2));
16627
16628}	# end of &initialise
16629
16630# Frequently used regular expressions with arguments
16631sub make_end_env_rx {
16632    local($env) = @_;
16633    $env = &escape_rx_chars($env);
16634    "\\\\end\\s*$O(\\d+)$C\\s*$env\\s*$O\\1$C".$EOL;
16635}
16636
16637sub make_begin_end_env_rx {
16638    local($env) = @_;
16639    $env = &escape_rx_chars($env);
16640    "\\\\(begin|end)\\s*$O(\\d+)$C\\s*$env\\s*$O\\3$C(\\s*\$)?";
16641}
16642
16643sub make_end_cmd_rx {
16644    local($br_id) = @_;
16645    "$O$br_id$C";
16646}
16647
16648#JCL(jcl-del) - see also &tokenize.
16649# Arrange commands into a regexp for tokenisation.
16650# Any letter command will gobble spaces, but avoids to match
16651# on ensuing letters (\foo won't match on \foox).
16652# Any non-letter command retains spaces and matches always
16653# by itself (\| matches \|... regardless of ...).
16654#
16655# This all is a huge kludge. The commands names should stay fix,
16656# regardless of changing catcodes. If we have \makeatletter,
16657# and LaTeX2HTML marks \@foo, then \@foo will be expanded
16658# properly before \makeatother, but does weird things on \@foo
16659# after \makeatother (\@foo in LaTeX is then \@ and foo, which
16660# isn't recognized as such).
16661# The reason is that the text to match the command \@foo
16662# in LaTeX mustn't be \@foo at all, because any text in LaTeX
16663# is also attributed with the category codes.
16664#
16665# But at least we have proper parsing of letter and non-letter
16666# commands as long as catcoding won't upset LaTeX2HTML too much.
16667#
16668sub make_new_cmd_rx {
16669    return("") if $#_ < 0; # empty regexp if list is empty!
16670
16671    # We have a subtle treatment of ambivalent commands like
16672    # \@foo in situations depicted above!
16673    # Get every command that contains no letters ...
16674    local($nonlettercmds) =
16675	&escape_rx_chars(join($CD, grep(!/[$letters]/,@_)));
16676    # and every command that contains a letter
16677    local($lettercmds) =
16678	&escape_rx_chars(join($CD, grep(/[$letters]/,@_)));
16679
16680    if (%renew_command) {
16681	local($renew);
16682	foreach $renew (keys %renew_command) {
16683	    $lettercmds =~ s/(^|$CD)$renew//; }
16684        $lettercmds =~ s/^$CD$//;
16685    }
16686
16687    # replace the temporary $CD delimiter (this enables eg. \| command)
16688    $nonlettercmds =~ s/$CD/|/g;
16689    $lettercmds =~ s/$CD/|/g;
16690
16691    # In case we have no non-letter commands, insert empty parentheses
16692    # to align match strings.
16693    #
16694    $nonlettercmds =~ s/^\||\|$//g;
16695    $lettercmds =~ s/^\||\|$//g;
16696    local($rx) = (length($nonlettercmds) ? "\\\\($nonlettercmds)" : "");
16697    if (length($lettercmds)) {
16698	$rx .= ( length($rx) ? "|" : "()" );
16699	$rx .= "\\\\($lettercmds)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
16700    }
16701    # $1: non-letter cmd, $2: letter cmd, $4: delimiter
16702    # Eg. \\(\@|...|\+)|\\(abc|...|xyz)(([^a-zA-Z \t])|[ \t]+)
16703    # $1 and $2 are guaranteed to alternate, $4 may be empty.
16704    $rx;
16705}
16706
16707# Build a simple regexp to use after tokenisation for
16708# faster translation.
16709sub make_new_cmd_no_delim_rx {
16710    return("") if $#_ < 0; # empty regexp if list is empty!
16711    # Get every command that contains no letters ...
16712    local($_) = &escape_rx_chars(join($CD, @_));
16713    s/$CD/|/g;
16714
16715    join('',"\\\\(",$_,")");
16716}
16717
16718
16719#JCL(jcl-del) - new face: w/o arg (was 'begin' only), escapes env names
16720sub make_new_env_rx {
16721    local($envs) = &escape_rx_chars(join($CD, keys %new_environment));
16722    $envs =~ s/$CD/|/g;
16723    length($envs) ? "\\\\begin\\s*$O(\\d+)$C\\s*($envs)\\s*$O\\1$C\\s*" : "";
16724}
16725
16726sub make_new_end_env_rx {
16727    local($envs) = &escape_rx_chars(join($CD, keys %new_environment));
16728    $envs =~ s/$CD/|/g;
16729    length($envs) ? "\\\\end\\s*$O(\\d+)$C\\s*($envs)\\s*$O\\1$C\\s*" : "";
16730}
16731
16732#JCL(jcl-del) - $delimiter_rx -> ^$letters
16733# don't care for $cmd_spc_rx; space after sectioning commands
16734# is unlikely and I don't want to try too much new things
16735#
16736sub make_sections_rx {
16737    local($section_alts) = &get_current_sections;
16738    # $section_alts includes the *-forms of sectioning commands
16739    $sections_rx = "()\\\\($section_alts)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
16740#    $sections_rx = "()\\\\($section_alts)([^$letters])";
16741}
16742
16743sub make_order_sensitive_rx {
16744    local(@theorem_alts, $theorem_alts);
16745    @theorem_alts = ($preamble =~ /\\newtheorem\s*{([^\s}]+)}/og);
16746    $theorem_alts = join('|',@theorem_alts);
16747#
16748#  HWS: Added kludge to require counters to be more than 2 characters long
16749#	in order to be flagged as order-sensitive.  This will permit equations
16750#	with \theta to remain order-insensitive.  Also permit \alpha and
16751#	the eqnarray* environment to remain order-insensitive.
16752#
16753    $order_sensitive_rx =
16754#        "(equation|eqnarray[^*]|\\\\caption|\\\\ref|\\\\the[a-z]{2,2}[a-z]|\\\\stepcounter" .
16755        "(\\\\caption|\\\\ref|\\\\the[a-z]{2,2}[a-z]|\\\\stepcounter" .
16756        "|\\\\arabic|\\\\roman|\\\\Roman|\\\\alph[^a]|\\\\Alph|\\\\fnsymbol)";
16757    $order_sensitive_rx =~ s/\)/|$theorem_alts)/ if $theorem_alts;
16758}
16759
16760sub make_language_rx {
16761    local($language_alts) = join("|", keys %language_translations);
16762#    $setlanguage_rx = "\\\\se(lec)?tlanguage\\s*{\\\\?($language_alts)}";
16763    $setlanguage_rx = "\\\\setlanguage\\s*{\\\\?($language_alts)}";
16764    $language_rx = "\\\\($language_alts)TeX";
16765    $case_change_rx = "(\\\\(expandafter|noexpand)\s*)?\\\\((Make)?([Uu]pp|[Ll]ow)ercase)\s*";
16766}
16767
16768sub addto_languages {
16769    local($lang) = @_;
16770    local($trans) = "main'".$lang.'_translation';
16771    if (defined &$trans) {
16772	$language_translations {$lang} = $lang.'_translation';
16773    }
16774}
16775
16776# JCL(jcl-del) - new rexexp type
16777sub make_raw_arg_cmd_rx {
16778    # $1 or $2 : commands to be processed in latex (with arguments untouched)
16779    # $4 : delimiter
16780    $raw_arg_cmd_rx = &make_new_cmd_rx(keys %raw_arg_cmds);
16781    $raw_arg_cmd_rx;
16782}
16783
16784# There are probably more.
16785# Interferences not checked out yet, thus in makeat... only.
16786sub make_letter_sensitive_rx {
16787    $delimiter_rx = "([^$letters])";
16788    &make_sections_rx;
16789    &make_single_cmd_rx;
16790    &make_counters_rx;
16791}
16792
16793#JCL(jcl-del) - this could eat one optional newline, too.
16794# But this might result in large lines... anyway, it *should* be
16795# handled. A possible solution would be to convert adjacent newlines
16796# into \par's in preprocessing.
16797sub make_cmd_spc_rx {
16798    $cmd_spc = " \\t";
16799    $cmd_spc_rx = "[ \\t]*"; # zero or more
16800    $cmd_spcs_rx = "[ \\t]+"; # one or more
16801}
16802
16803sub make_single_cmd_rx {
16804    $single_cmd_rx = "\\\\([^$letters])|\\\\([$letters]+\\*?)(([^$letters$cmd_spc])|$cmd_spcs_rx|\n|\$)";
16805}
16806
16807sub make_counters_rx {
16808    # Matches counter commands - these are caught early and are appended to the
16809    # file that is passed to latex.
16810#JCL(jcl-del) - $delimiter_rx -> ^$letters
16811    $counters_rx = "()\\\\(newcounter|addtocounter|setcounter|refstepcounter|stepcounter|arabic|roman|Roman|alph|Alph|fnsymbol)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
16812}
16813
16814
16815# Creates an anchor for its argument and saves the information in
16816# the array %index;
16817# In the index the word will use the beginning of the title of
16818# the current section (instead of the usual pagenumber).
16819# The argument to the \index command is IGNORED (as in latex)
16820sub make_index_entry { &make_real_index_entry(@_) }
16821sub make_real_index_entry {
16822    local($br_id,$str) = @_;
16823    local($this_file) = $CURRENT_FILE;
16824    $TITLE = $saved_title if (($saved_title)&&(!($TITLE)||($TITLE eq $default_title)));
16825    # Save the reference
16826    $str = "$str###" . ++$global{'max_id'}; # Make unique
16827    $index{$str} .= &make_half_href($this_file."#$br_id");
16828    "<A NAME=\"$br_id\">$anchor_invisible_mark<\/A>";
16829}
16830
16831sub image_message { # clean
16832    print <<"EOF";
16833
16834To resolve the image conversion problems please consult
16835the "Troubleshooting" section of your local User Manual
16836or read it online at
16837   http://www-texdev.ics.mq.edu.au/l2h/docs/manual/
16838
16839EOF
16840}
16841
16842sub image_cache_message { # clean
16843   print <<"EOF";
16844
16845If you are having problems displaying the correct images with Mosaic,
16846try selecting "Flush Image Cache" from "Options" in the menu-bar
16847and then reload the HTML file.
16848EOF
16849}
16850
16851__DATA__
16852
16853# start of POD documentation
16854
16855=head1 NAME
16856
16857latex2html - Translate LaTeX files to HTML (HyperText Markup Language)
16858
16859=head1 SYNOPSIS
16860
16861B<latex2html> S<[ B<-help> | B<-h> ]> S<[ B<-version> | B<-V> ]>
16862
16863B<latex2html> S<[ B<-split> I<num> ]>
16864S<[ B<-link> I<num> ]>
16865S<[ B<-toc_depth> I<num> ]>
16866S<[ B<->(B<no>)B<toc_stars> ]>
16867S<[ B<->(B<no>)B<short_extn> ]>
16868S<[ B<-iso_language> I<lang> ]>
16869S<[ B<->(B<no>)B<validate> ]>
16870S<[ B<->(B<no>)B<latex> ]>
16871S<[ B<->(B<no>)B<djgpp> ]>
16872S<[ B<->(B<no>)B<fork> ]>
16873S<[ B<->(B<no>)B<external_images> ]>
16874S<[ B<->(B<no>)B<ascii_mode> ]>
16875S<[ B<->(B<no>)B<lcase_tags> ]>
16876S<[ B<->(B<no>)B<ps_images> ]>
16877S<[ B<-font_size> I<size> ]>
16878S<[ B<->(B<no>)B<tex_defs> ]>
16879S<[ B<->(B<no>)B<navigation> ]>
16880S<[ B<->(B<no>)B<top_navigation> ]>
16881S<[ B<->(B<no>)B<buttom_navigation> ]>
16882S<[ B<->(B<no>)B<auto_navigation> ]>
16883S<[ B<->(B<no>)B<index_in_navigation> ]>
16884S<[ B<->(B<no>)B<contents_in_navigation> ]>
16885S<[ B<->(B<no>)B<next_page_in_navigation> ]>
16886S<[ B<->(B<no>)B<previous_page_in_navigation> ]>
16887S<[ B<->(B<no>)B<footnode> ]>
16888S<[ B<->(B<no>)B<numbered_footnotes> ]>
16889S<[ B<-prefix> I<output_filename_prefix> ]>
16890S<[ B<->(B<no>)B<auto_prefix> ]>
16891S<[ B<-long_titles> I<num> ]>
16892S<[ B<->(B<no>)B<custom_titles> ]>
16893S<[ B<-title>|B<-t> I<top_page_title> ]>
16894S<[ B<->(B<no>)B<rooted> ]>
16895S<[ B<-rootdir> I<output_directory> ]>
16896S<[ B<-dir> I<output_directory> ]>
16897S<[ B<-mkdir> ]>
16898S<[ B<-address> I<author_address> | B<-noaddress> ]>
16899S<[ B<->(B<no>)B<subdir> ]>
16900S<[ B<-info> I<0> | I<1> | I<string> ]>
16901S<[ B<->(B<no>)B<auto_link> ]>
16902S<[ B<-reuse> I<num> | B<-noreuse> ]>
16903S<[ B<->(B<no>)B<antialias_text> ]>
16904S<[ B<->(B<no>)B<antialias> ]>
16905S<[ B<->(B<no>)B<transparent> ]>
16906S<[ B<->(B<no>)B<white> ]>
16907S<[ B<->(B<no>)B<discard> ]>
16908S<[ B<-image_type> I<type> ]>
16909S<[ B<->(B<no>)B<images> ]>
16910S<[ B<-accent_images> I<type> | B<-noaccent_images> ]>
16911S<[ B<-style> I<style> ]>
16912S<[ B<->(B<no>)B<parbox_images> ]>
16913S<[ B<->(B<no>)B<math> ]>
16914S<[ B<->(B<no>)B<math_parsing> ]>
16915S<[ B<->(B<no>)B<latin> ]>
16916S<[ B<->(B<no>)B<entities> ]>
16917S<[ B<->(B<no>)B<local_icons> ]>
16918S<[ B<->(B<no>)B<scalable_fonts> ]>
16919S<[ B<->(B<no>)B<images_only> ]>
16920S<[ B<->(B<no>)B<show_section_numbers> ]>
16921S<[ B<->(B<no>)B<show_init> ]>
16922S<[ B<-init_file> I<Perl_file> ]>
16923S<[ B<-up_url> I<up_URL> ]>
16924S<[ B<-up_title> I<up_title> ]>
16925S<[ B<-down_url> I<down_URL> ]>
16926S<[ B<-down_title> I<down_title> ]>
16927S<[ B<-prev_url> I<prev_URL> ]>
16928S<[ B<-prev_title> I<prev_title> ]>
16929S<[ B<-index> I<index_URL> ]>
16930S<[ B<-biblio> I<biblio_URL> ]>
16931S<[ B<-contents> I<toc_URL> ]>
16932S<[ B<-external_file> I<external_aux_file> ]>
16933S<[ B<->(B<no>)B<short_index> ]>
16934S<[ B<->(B<no>)B<unsegment> ]>
16935S<[ B<->(B<no>)B<debug> ]>
16936S<[ B<-tmp> I<path> ]>
16937S<[ B<->(B<no>)B<ldump> ]>
16938S<[ B<->(B<no>)B<timing> ]>
16939S<[ B<-verbosity> I<num> ]>
16940S<[ B<-html_version> I<num> ]>
16941S<[ B<->(B<no>)B<strict> ]>
16942I<file.tex> S<[ I<file2.tex> ... ]>
16943
16944=head1 DESCRIPTION
16945
16946I<LaTeX2HTML> is a Perl program that translates LaTeX source files into
16947HTML. For each source file given as an argument the translator will create
16948a directory containing the corresponding HTML files.
16949
16950=head1 OPTIONS
16951
16952Many options can be specified in a true/false manner. This is indicated by
16953I<(no)>, e.g. to enable passing unknown environments to LaTeX, say "-latex",
16954to disable the feature say "-nolatex" or "-no_latex" (portability mode).
16955
16956=over 4
16957
16958=item B<-help> | B<-h>
16959
16960Print this online manual and exit.
16961
16962=item B<-version> | B<-V>
16963
16964Print the LaTeX2HTML release and version information and exit.
16965
16966=item B<-split> I<num>
16967
16968Stop making separate files at this depth (say "-split 0" for one huge HTML
16969file).
16970
16971=item B<-link> I<num>
16972
16973Stop showing child nodes at this depth.
16974
16975=item B<-toc_depth> I<num>
16976
16977MISSING_DESCRIPTION
16978
16979=item B<->(B<no>)B<toc_stars>
16980
16981MISSING_DESCRIPTION
16982
16983=item B<->(B<no>)B<short_extn>
16984
16985If this is set all HTML file will have extension C<.htm> instead of
16986C<.html>. This is helpful when shipping the document to PC systems.
16987
16988=item B<-iso_language> I<lang>
16989
16990MISSING_DESCRIPTION
16991
16992=item B<->(B<no>)B<validate>
16993
16994When this is set true, the HTML validator specified in F<l2hconf.pm>
16995will run.
16996
16997=item B<->(B<no>)B<latex>
16998
16999Pass unknown environments to LaTeX. This is the default.
17000
17001=item B<->(B<no>)B<djgpp>
17002
17003Specify this switch if you are running DJGPP on DOS and need to avoid
17004running out of filehandles.
17005
17006=item B<->(B<no>)B<fork>
17007
17008Enable/disable forking. The default is reasonable for this platform.
17009
17010=item B<->(B<no>)B<external_images>
17011
17012If set, leave the images outside the document.
17013
17014=item B<->(B<no>)B<ascii_mode>
17015
17016This is different from B<-noimages>.
17017If this is set, B<LaTeX2HTML> will show textual tags rather than
17018images, both in navigation panel and text (Eg. C<[Up]> instead the up
17019icon).
17020You could use this feature to create simple text from your
17021document, eg. with 'Save as... Text' from B<Netscape> or with
17022B<lynx -dump>.
17023
17024=item B<->(B<no>)B<lcase_tags>
17025
17026writes out HTML tag names using lowercase letters, rather than uppercase.
17027
17028=item B<->(B<no>)B<ps_images>
17029
17030If set, use links to external postscript images rather than inlined bitmaps.
17031
17032=item B<-font_size> I<size>
17033
17034To set the point size of LaTeX-generated GIF files, specify the desired
17035value (i.e., C<10pt>, C<11pt>, C<12pt>, etc.).
17036The default is to use the point size of the original LaTeX document.
17037This value will be magnified by I<$FIGURE_SCALE_FACTOR> and
17038I<$MATH_SCALE_FACTOR> defined in F<l2hconf.pm>.
17039
17040=item B<->(B<no>)B<tex_defs>
17041
17042Enable interpretation of raw TeX commands (default).
17043Note: There are many variations of C<\def> that B<LaTeX2HTML> cannot process
17044correctly!
17045
17046=item B<->(B<no>)B<navigation>
17047
17048Put a navigation panel at the top of each page (default).
17049
17050=item B<->(B<no>)B<top_navigation>
17051
17052Enables navigation links at the top of each page (default).
17053
17054=item B<->(B<no>)B<buttom_navigation>
17055
17056Enables navigation links at the buttom of each page.
17057
17058=item B<->(B<no>)B<auto_navigation>
17059
17060Put navigation links at the top of each page. If the page exceeds
17061I<$WORDS_IN_PAGE> number of words then put one at the bottom of the page.
17062
17063=item B<->(B<no>)B<index_in_navigation>
17064
17065Put a link to the index page in the navigation panel.
17066
17067=item B<->(B<no>)B<contents_in_navigation>
17068
17069Put a link to the table of contents in the navigation panel.
17070
17071=item B<->(B<no>)B<next_page_in_navigation>
17072
17073Put a link to the next logical page in the navigation panel.
17074
17075=item B<->(B<no>)B<previous_page_in_navigation>
17076
17077Put a link to the previous logical page in the navigation panel.
17078
17079=item B<->(B<no>)B<footnode>
17080
17081Puts all footnotes onto a separate HTML page, called F<footnode.html>,
17082rather than at the bottom of the page where they are referenced.
17083
17084=item B<->(B<no>)B<numbered_footnotes>
17085
17086If true, you will get every footnote applied with a subsequent number, else
17087with a generic hyperlink icon.
17088
17089=item B<-prefix> I<output_filename_prefix>
17090
17091Set the output file prefix, prepended to all C<.html>, C<.gif> and C<.pl>
17092files. See also B<-auto_prefix>.
17093
17094=item B<->(B<no>)B<auto_prefix>
17095
17096Set this to automatically insert the equivalent of B<-prefix >C<basename->",
17097where "basename" is the base name of the file being translated.
17098
17099=item B<-long_titles> I<num>
17100
17101MISSING_DESCRIPTION
17102
17103=item B<->(B<no>)B<custom_titles>
17104
17105MISSING_DESCRIPTION
17106
17107=item B<-title>|B<-t> I<top_page_title>
17108
17109The title (displayed in the browser's title bar) the document shall get.
17110
17111=item B<->(B<no>)B<rooted>
17112
17113MISSING_DESCRIPTION
17114
17115=item B<-rootdir> I<output_directory>
17116
17117MISSING_DESCRIPTION
17118
17119=item B<-dir> I<output_directory>
17120
17121Put the result in this directory instead of parallel to the LaTeX file,
17122provided the directory exists, or B<-mkdir> is specified.
17123
17124=item B<-mkdir>
17125
17126Allow directory specified with B<-dir> to be created if necessary.
17127
17128=item B<-address> I<author_address> | B<-noaddress>
17129
17130Supply your own string if you don't like the default
17131"E<lt>NameE<gt> E<lt>DateE<gt>". B<-noaddress> suppresses the
17132generation of an address footer.
17133
17134=item B<->(B<no>)B<subdir>
17135
17136If set (default), B<LaTeX2HTML> creates (or reuses) another file directory.
17137When false, the generated HTML files will be placed in the current
17138directory.
17139
17140=item B<-info> I<0> | I<1> | I<string>
17141
17142=item B<-noinfo>
17143
17144If 0 is specified (or B<-noinfo> is used), do not generate an I<"About this
17145document..."> section. If 1 is specified (default), the standard info page is
17146generated. If a custom string is given, it is used as the info page.
17147
17148=item B<->(B<no>)B<auto_link>
17149
17150MISSING_DESCRIPTION
17151
17152=item B<-reuse> I<num> | B<-noreuse>
17153
17154If false, do not reuse or recycle identical images generated in previous
17155runs. If the html subdirectory already exists, start the interactive session.
17156If I<num> is nonzero, do recycle them and switch off the interactive session.
17157If 1, only recycle images generated from previous runs.
17158If 2, recycle images from the current and previous runs (default).
17159
17160=item B<->(B<no>)B<antialias_text>
17161
17162Use anti-aliasing in the generation of images of typeset material;
17163e.g. mathematics and text, e.g. in tables and {makeimage} environments.
17164
17165=item B<->(B<no>)B<antialias>
17166
17167Use anti-aliasing in the generation of images of figures. This usually
17168results in "sharper" bitmap images.
17169
17170=item B<->(B<no>)B<transparent>
17171
17172If this is set to false then any inlined images generated from "figure"
17173environments will NOT be transparent.
17174
17175=item B<->(B<no>)B<white>
17176
17177This sets the background of generated images to white for anti-aliasing.
17178
17179=item B<->(B<no>)B<discard>
17180
17181if true, the PostScript file created for each generated image
17182is discarded immediately after its image has been rendered and saved in the
17183required graphics format. This can lead to significant savings in disk-space,
17184when there are a lot of images, since otherwise these files are not discarded
17185until the end of all processing.
17186
17187=item B<-image_type> I<type>
17188
17189Specify the type of bitmap images to be generated. Depending on your setup,
17190B<LaTeX2HTML> can generate B<gif> or B<png> images. Note: Gif images have
17191certain legal restrictions, as their generation involves an algorithm
17192patented by Unisys.
17193
17194=item B<->(B<no>)B<images>
17195
17196If false, B<LaTeX2HTML> will not attempt to produce any inlined images.
17197The missing images can be generated "off-line" by restarting B<LaTeX2HTML>
17198with B<-images_only>.
17199
17200=item B<-accent_images> I<type> | B<-noaccent_images>
17201
17202MISSING_DESCRIPTION
17203
17204=item B<-style> I<style>
17205
17206MISSING_DESCRIPTION
17207
17208=item B<->(B<no>)B<parbox_images>
17209
17210MISSING_DESCRIPTION
17211
17212=item B<->(B<no>)B<math>
17213
17214By default the special MATH extensions are not used
17215since they do not conform with the HTML 3.2 standard.
17216
17217=item B<->(B<no>)B<math_parsing>
17218
17219MISSING_DESCRIPTION
17220
17221=item B<->(B<no>)B<latin>
17222
17223MISSING_DESCRIPTION
17224
17225=item B<->(B<no>)B<entities>
17226
17227MISSING_DESCRIPTION
17228
17229=item B<->(B<no>)B<local_icons>
17230
17231Set this if you want to copy the navigation icons to each document directory
17232so that the document directory is self-contained and can be dropped into
17233another server tree without further actions.
17234
17235=item B<->(B<no>)B<scalable_fonts>
17236
17237MISSING_DESCRIPTION
17238
17239=item B<->(B<no>)B<images_only>
17240
17241When true, B<LaTeX2HTML> will only try to convert the inlined images in the
17242file F<images.tex> which should have been generated automatically during
17243previous runs. This is very useful for correcting "bad LaTeX" in this file.
17244
17245=item B<->(B<no>)B<show_section_numbers>
17246
17247When this is set true, the section numbers are shown. The section numbers
17248should then match those that would have been produced by LaTeX.
17249The correct section numbers are obtained from the $FILE.aux file generated
17250by LaTeX.
17251Hiding the section numbers encourages use of particular sections
17252as standalone documents. In this case the cross reference to a section
17253is shown using the default symbol rather than the section number.
17254
17255=item B<->(B<no>)B<show_init>
17256
17257MISSING_DESCRIPTION
17258
17259=item B<-init_file> I<Perl_file>
17260
17261MISSING_DESCRIPTION
17262
17263=item B<-up_url> I<up_URL>, B<-up_title> I<up_title>
17264
17265=item B<-down_url> I<down_URL>, B<-down_title> I<down_title>
17266
17267=item B<-prev_url> I<prev_URL>, B<-prev_title> I<prev_title>
17268
17269=item B<-index> I<index_URL>,
17270
17271=item B<-contents> I<toc_URL>
17272
17273=item B<-biblio> I<biblio_URL>
17274
17275If both of the listed two options are set then the "Up" ("Previous" etc.)
17276button of the navigation panel in the first node/page of a converted
17277document will point to I<up_URL> etc. I<up_title> should be set
17278to some text which describes this external link.
17279Similarly you might use these options to link external documents
17280to your navigation panel.
17281
17282=item B<-external_file> I<external_aux_file>
17283
17284MISSING_DESCRIPTION
17285
17286=item B<->(B<no>)B<short_index>
17287
17288If this is set then B<makeidx.perl> will construct codified names
17289for the text of index references.
17290
17291=item B<->(B<no>)B<unsegment>
17292
17293Use this to translate a segmented document as if it were not
17294segmented.
17295
17296=item B<->(B<no>)B<debug>
17297
17298If this is set then intermediate files are left for later inspection and
17299a lot of diagnostic output is produced. This output may be useful when
17300searching for problems and/or submitting bug reports to the developers.
17301Temporary files include F<$$_images.tex> and F<$$_images.log> created during
17302image conversion. Caution: Intermediate files can be I<enormous>!
17303
17304=item B<-tmp> I<path>
17305
17306Path for temporary files. This should be a local, fast filesystem because it is heavily used during image generation. The default is set in F<l2hconf.pm>.
17307
17308=item B<->(B<no>)B<ldump>
17309
17310This will cause LaTeX2HTML to produce a LaTeX dump of images.tex which is read
17311in on subsequent runs and speeds up startup time of LaTeX on the images.tex
17312translation. This actually consumes additional time on the first run, but pays
17313off on subsequent runs. The dump file will need about 1 Meg of disk space.
17314
17315=item B<->(B<no>)B<timing>
17316
17317MISSING_DESCRIPTION
17318
17319=item B<-verbosity> I<num>
17320
17321The amount of message information printed to the screen during processing
17322by B<LaTeX2HTML> is controlled by this setting.
17323By increasing this value, more information is displayed.
17324Here is the type of extra information that is shown at each level:
17325
17326  0   no extra information
17327  1   section types and titles
17328  2   environment
17329  3   command names
17330  4   links, labels and internal sectioning codes
17331
17332=item B<-html_version> I<list>
17333
17334Which HTML version should be generated. Currently available are:
17335C<2.0>, C<3.0>, C<3.2>, C<4.0>. Some additional options that may be
17336added are: C<math> (parse mathematics), C<i18n> (?),
17337C<table> (generate tables), C<frame> (generate frames),
17338C<latin1>...C<latin9> (use ISO-Latin-x encoding),
17339C<unicode> (generate unicode characters). Separate the options with ',',
17340e.g. C<4.0,math,frame>.
17341
17342=item B<->(B<no>)B<strict>
17343
17344MISSING_DESCRIPTION
17345
17346=back
17347
17348=head1 FILES
17349
17350=over 4
17351
17352=item F<$LATEX2HTMLPLATDIR/l2hconf.pm>
17353
17354This file holds the global defaults and configuration settings for
17355B<LaTeX2HTML>.
17356
17357=item F<$HOME/.latex2html-init>
17358
17359=item F<./.latex2html-init>
17360
17361These files may contain settings that override the global defaults, just
17362like specifying command line switches.
17363
17364=back
17365
17366=head1 ENVIRONMENT
17367
17368=over 4
17369
17370=item LATEX2HTMLDIR
17371
17372Path where LaTeX2HTML library files are found. On this installation
17373LATEX2HTMLDIR is F<@LATEX2HTMLDIR@>
17374
17375=item PERL5LIB
17376
17377Set by the B<latex2html> program to find perl modules.
17378
17379=item L2HCONFIG
17380
17381An alternative configuration filename. The standard configuration file
17382is F<$LATEX2HTMLPLATDIR/l2hconf.pm>. You may specify a sole filename (searched
17383for in F<$LATEX2HTMLPLATDIR> (and F<$PERL5LIB>) or a complete path.
17384
17385=item L2HINIT_NAME
17386
17387The standard user-specific configuration filename is F<.latex2html-init>.
17388This environment variable will override this name.
17389
17390=item HOME
17391
17392Evaluated if the system does not know about "home" directories (like
17393DOS, WinXX, OS/2, ...) to determine the path to F<$L2HINIT_NAME>.
17394
17395=item TEXE_DONT_INCLUDE, TEXE_DO_INCLUDE
17396
17397Used internally for communication with B<texexpand>.
17398
17399=item TEXINPUTS
17400
17401Used to find TeX includes of all sorts.
17402
17403=back
17404
17405=head1 PROBLEMS
17406
17407For information on various problems and remedies see the WWW online
17408documentation or the documents available in the distribution.
17409An online bug reporting form and various archives are available at
17410F<http://www.latex2html.org/>
17411
17412There is a mailing list for discussing B<LaTeX2HTML>: C<latex2html@tug.org>
17413
17414=head1 AUTHOR
17415
17416Nikos Drakos,  Computer Based Learning Unit, University of Leeds
17417E<lt>nikos@cbl.leeds.ac.ukE<gt>. Several people have contributed
17418suggestions, ideas, solutions, support and encouragement.
17419
17420The B<pstoimg> script was written by Marek Rouchal
17421E<lt>marek@saftsack.fs.uni-bayreuth.deE<gt>
17422as a generalisation of the B<pstogif> utility to allow graphic formats
17423other than GIF to be created. Various options and enhancements have
17424been added by Ross Moore.
17425Some of the code is based upon the pstoppm.ps postscript program
17426originally written by Phillip Conrad (Perfect Byte, Inc.)
17427and modified by L. Peter Deutsch (Aladdin Enterprises).
17428
17429=head1 SEE ALSO
17430
17431See the WWW online documentation or the F<$LATEX2HTMLDIR/doc/manual.ps>
17432file for more detailed information and examples.
17433
17434L<pstoing>, L<texexpand>
17435
17436=cut
17437
17438