#!/usr/bin/perl 

'di';
'ig00';

# This variable can be set manually or by the installation script
# to point to the DIRECTORY where the latex2html files can be found.
$LATEX2HTMLDIR="$ENV{'L2HMODULE'}/user";# Inserted by installation script

# LaTeX2HTML by Nikos Drakos <nikos@cbl.leeds.ac.uk>

#
# Comprises patches by various authors: See Changes, the log file of LaTeX2HTML.
#
# ****************************************************************
# LaTeX To HTML Translation **************************************
# ****************************************************************
# LaTeX2HTML is a Perl program that translates LaTeX source
# files into HTML (HyperText Markup Language). For each source
# file given as an argument the translator will create a
# directory containing the corresponding HTML files.
#
# The man page for this program is included at the end of this file
# and can be viewed using
# %nroff -man latex2html
#
# For more information on this program and some examples of its
# capabilities see the accompanying documentation in the docs/
# directory, or
#
# http://www-dsed.llnl.gov/files/programs/unix/latex2html/manual/
#
# or
#
# http://www.cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/
#
# Written by Nikos Drakos, July 1993.
#
# Address: Computer Based Learning Unit
#          University of Leeds
#          Leeds,  LS2 9JT
#
# Copyright (c) 1993. All rights reserved.
#
# See general license below.
#
# ****************************************************************
# General License Agreement and Lack of Warranty *****************
# ****************************************************************
#
# This software is distributed in the hope that it will be useful
# but WITHOUT ANY WARRANTY. The author(s) do not accept responsibility
# to anyone for the consequences of using it or for whether it serves
# any particular purpose or works at all. No warranty is made about
# the software or its performance.
#
# Use and copying of this software and the preparation of derivative
# works based on this software are permitted, so long as the following
# conditions are met:
# 	o  The copyright notice and this entire notice are included intact
# 	   and prominently carried on all copies and supporting documentation.
# 	o  No fees or compensation are charged for use, copies, or
# 	   access to this software. You may charge a nominal
# 	   distribution fee for the physical act of transferring a
# 	   copy, but you may not charge for the program itself.
# 	o  If you modify this software, you must cause the modified
# 	   file(s) to carry prominent notices (a Change Log)
# 	   describing the changes, who made the changes, and the date
# 	   of those changes.
# 	o  Any work distributed or published that in whole or in part
# 	   contains or is a derivative of this software or any part
# 	   thereof is subject to the terms of this agreement. The
# 	   aggregation of another unrelated program with this software
# 	   or its derivative on a volume of storage or distribution
# 	   medium does not bring the other program under the scope
# 	   of these terms.
#
# This software is made available AS IS, and is distributed without
# warranty of any kind, either expressed or implied.
#
# In no event will the author(s) or their institutions be liable to you
# for damages, including lost profits, lost monies, or other special,
# incidental or consequential damages arising out of or in connection
# with the use or inability to use (including but not limited to loss of
# data or data being rendered inaccurate or losses sustained by third
# parties or a failure of the program to operate as documented) the
# program, even if you have been advised of the possibility of such
# damages, or for any claim by any other party, whether in an action of
# contract, negligence, or other tortious action.
#
# Please send bug reports, comments, questions and suggestions to
# nikos@cbl.leeds.ac.uk. We would also appreciate receiving any changes
# or improvements you may make.
#
############################# System Parameters ##########################
#
# Uncomment the following statement if your Linux system requires it.
# use GDBM_File;

# change these whenever you do a patch to this program and then
# name the resulting patch file accordingly
$TPATCHLEVEL = "";
$TVERSION = "97.1 (release)";
$RELDATE = "(July 13th, 1997)";

$TEX2HTMLV_SHORT = $TVERSION . $TPATCHLEVEL;
$TEX2HTMLVERSION = $TEX2HTMLV_SHORT . ' ' . $RELDATE;
$TEX2HTMLADDRESS = "http://www-dsed.llnl.gov/files/programs/unix/latex2html/manual/";
$AUTHORADDRESS = "http://cbl.leeds.ac.uk/nikos/personal.html";

# Set $HOME to the environment variable.
$HOME = $ENV{'HOME'} || (getpwuid($<))[7];

push(@INC,$HOME);

# flush stdout with every print -- gives better feedback during
# long computations
$| = 1;

# set Perl's subscript separator to LaTeX's illegal character.
# (quite defensive but why not)
$; = "\000";

# No arguments!!
(&usage && die "No files to process!\n") unless @ARGV;

die ("LaTeX2HTML must be installed first before usage:\n".
    "Please run installation script.\n")
    unless $LATEX2HTMLDIR;


# Author address
@address_data = &address_data;

# Read latex2html.config
require("$LATEX2HTMLDIR/latex2html.config") if
    ((-f "$LATEX2HTMLDIR/latex2html.config") ||
     die "LaTeX2HTML has not been installed correctly:".
     "\nCould not find file $LATEX2HTMLDIR/latex2html.config\n");

#Set $dd to the directory delimiter character
$dd = '/' unless ($dd);

# Read .latex2html-init file if one is found
if (-f "$HOME$dd.latex2html-init") {
    print "\nloading $HOME$dd.latex2html-init";
    require("$HOME$dd.latex2html-init");
    die "You have an out-of-date " . $HOME .
	"$dd.latex2html-init file.\nPlease update or delete it.\n"
	if ($DESTDIR eq '.');
    }

# Read .latex2html-init file if one is found in current directory
if ( (! (&getcwd eq $HOME )) && (-f ".$dd.latex2html-init")) {
    print "\nloading .$dd.latex2html-init";
    require(".$dd.latex2html-init");
    }
die "'.' is an incorrect setting for DESTDIR.\n" .
    "Please check your .latex2html-init file.\n"
    if ($DESTDIR eq '.');

$ADDRESS = "$address_data[0]\n$address_data[1]" unless $ADDRESS;

# User home substitutions
$LATEX2HTMLSTYLES =~ s/~([$dd:]|$)/$HOME$1/g;
$LATEX2HTMLSTYLES =~ s/~([^$dd:]+)/(getpwnam($1))[7]/ge;


#HWS:  That was the last reference to HOME.  Now set HOME to $LATEX2HTMLDIR,
#	to enable dvips to see that version of .dvipsrc!  But only if we
#	have DVIPS_MODE not set - yes - this is a horrible nasty kludge

if ($PK_GENERATION && ! $DVIPS_MODE) {
	$ENV{HOME} =  "$LATEX2HTMLDIR";
	delete $ENV{PRINTER};		# Overrides .dvipsrc
}

$MAX_SPLIT_DEPTH = 4 unless ($MAX_SPLIT_DEPTH);

# Process switches
$argv = join(' ',@ARGV);			# Save the command line arguments
while ($ARGV[0] =~ /^-/) {
    $_ = shift;
    if (/^-split$/) {
	$_ = shift;
	if (/^(\+?)(\d+)$/) {
	    $MAX_SPLIT_DEPTH = $2;
	    if ($1) { $MAX_SPLIT_DEPTH *= -1; $REL_DEPTH = 1 }
	} else { 
	    print("Unrecognised value for -split: $_\n") && &usage && die 
        };
    }
    elsif (/^-link$/) {
	$_ = shift;
	((($MAX_LINK_DEPTH) = /^(\d+)$/)
	 || print("Unrecognised value for -link: $_\n")
	 && &usage && die);
    }
    elsif (/^-toc_depth$/) {
	$_ = shift;
	((($TOC_DEPTH) = /^(\d+)$/)
	 || print("Unrecognised value for -toc_depth: $_\n")
	 && &usage && die);
    }
    elsif (/^-toc_stars$/) {
	$TOC_STARS = 1;
    }
    elsif (/^-short_extn$/) {
      $SHORTEXTN = 1;
    }
    elsif (/^-nolatex$/) {
	$NOLATEX = 1;
    }
    elsif (/^-external_images$/) {
	$EXTERNAL_IMAGES = 1;
    }
    elsif (/^-ascii_mode$/) {
	$ASCII_MODE = 1; $EXTERNAL_IMAGES = 1;
    }
    elsif (/^-ps_images$/) {
      $PS_IMAGES = 1; $EXTERNAL_IMAGES = 1;
    }
    elsif (/^-font_size$/) {
      $FONT_SIZE = shift;
      &usage && die "Font size must end with \"pt\":  $FONT_SIZE"
	  unless ($FONT_SIZE =~ /^\d*pt$/);
    }
    elsif (/^-no_tex_defs$/) {
        $TEXDEFS = 0;
    }
    elsif (/^-no_navigation$/) {
	$NO_NAVIGATION = 1;
    }
    elsif (/^-top_navigation$/) {
	$TOP_NAVIGATION = 1;
    }
    elsif (/^-bottom_navigation$/) {
	$BOTTOM_NAVIGATION = 1;
    }
    elsif (/^-auto_navigation$/) {
	$AUTO_NAVIGATION = 1;
    }
    elsif (/^-index_in_navigation$/) {
	$INDEX_IN_NAVIGATION = 1;
    }
    elsif (/^-contents_in_navigation$/) {
	$CONTENTS_IN_NAVIGATION = 1;
    }
    elsif (/^-next_page_in_navigation$/) {
	$NEXT_PAGE_IN_NAVIGATION = 1;
    }
    elsif (/^-previous_page_in_navigation$/) {
	$PREVIOUS_PAGE_IN_NAVIGATION = 1;
    }
    elsif (/^-no_footnode$/) {
	$NO_FOOTNODE = 1;
    }
    elsif (/^-prefix$/) {
	$PREFIX = shift;
    }
    elsif (/^-auto_prefix$/) {
	$AUTO_PREFIX = 1;
    }
    elsif (/^-long_titles$/) {
	$_ = shift;
	$LONG_TITLES = $_;
	((($LONG_TITLES) = /^(\d+)$/)
	 || print("max words for -long_titles must be integer: $_\n")
	 && &usage && die);
    }
    elsif (/^-custom_titles$/) {
	$CUSTOM_TITLES = 1;
    }
    elsif (/^-t$/) {
	$_ = shift;
        ((($TITLE) = /^(.+)$/)
	 || print("No title for -t? $_\n")
	 && &usage && die);
    }
    elsif (/^-dir$/) {
	$_ = shift;
	$DESTDIR = $_;
	&usage && die unless ($_);
    }
    elsif (/^-address$/) {
	$ADDRESS = shift;
    }
    elsif (/^-no_subdir$/) {
 	$NO_SUBDIR = 1;
     }
    elsif (/^-info$/) {
	$_ = shift;
	((($INFO) = /^(.+)$/)
	 || print("No string for -info: Will not generate information page.\n")
	 );
    }
    elsif (/^-no_auto_link/) {
        $NO_AUTO_LINK = 1;
    }
    elsif (/^-reuse/) {
        $REUSE = shift;
    }
    elsif (/^-no_reuse/) {
        $REUSE = 0;
    }
    elsif (/^-antialias_text/) {
        $ANTI_ALIAS_TEXT = 1;
    }
    elsif (/^-no_antialias_text/) {
        $ANTI_ALIAS_TEXT = 0;
    }
    elsif (/^-antialias/) {
        $ANTI_ALIAS = 1;
    }
    elsif (/^-no_antialias/) {
        $ANTI_ALIAS = 0;
    }
    elsif (/^-discard/) {
        $DISCARD_PS = 1;
    }
    elsif (/^-no_images/) {
        $NO_IMAGES = 1;
    }
    elsif (/^-no_accent_images/) {
        $NO_ACCENT_IMAGES = 1;
    }
    elsif (/^-no_math$/) {
 	$NO_SIMPLE_MATH = 1;
     }
    elsif (/^-no_latin$/) {
 	$NO_ISOLATIN = 1;
     }
    elsif (/^-local_icons/) {
        $LOCAL_ICONS = 1;
    }
    elsif (/^-scalable_fonts/) {
        $SCALABLE_FONTS = 1;
    }
    elsif (/^-images_only/) {
        $IMAGES_ONLY = 1;
    }
    elsif (/^-show_section_numbers/) {
        $SHOW_SECTION_NUMBERS = 1;
    }
    elsif (/^-init_file/) {
        $init_file = shift;
#	require($init_file) if (-f $init_file);
	if (-f $init_file) {
	    print "\ninitialising with file: $init_file"
		if (($DEBUG)||($VERBOSITY));
	    require($init_file);
	} else { print "\nCould not find file: $init_file"; }
    }
    elsif ( /^-up_url$/ ) {
	$EXTERNAL_UP_LINK = shift;
	$EXTERNAL_UP_LINK =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-down_title$/ ) {
	$EXTERNAL_DOWN_TITLE = shift;
    }
    elsif ( /^-down_url$/ ) {
	$EXTERNAL_DOWN_LINK = shift;
	$EXTERNAL_DOWN_LINK =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-up_title$/ ) {
	$EXTERNAL_UP_TITLE = shift;
    }
    elsif ( /^-prev_url$/ ) {
	$EXTERNAL_PREV_LINK = shift;
	$EXTERNAL_PREV_LINK =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-prev_title$/ ) {
	$EXTERNAL_PREV_TITLE = shift;
    }
    elsif ( /^-index$/ ) {
	$EXTERNAL_INDEX = shift;
	$EXTERNAL_INDEX =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-biblio$/ ) {
	$EXTERNAL_BIBLIO = shift;
	$EXTERNAL_BIBLIO =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-contents$/ ) {
	$EXTERNAL_CONTENTS = shift;
	$EXTERNAL_CONTENTS =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-external_file$/ ) {
	$EXTERNAL_FILE = shift;
    }
    elsif (/^-short_index/) {
	$SHORT_INDEX = 1;
    }
    elsif (/^-debug/) {
	$DEBUG = 1;
    }
    elsif (/^-timing/) {
	$TIMING = 1;
    }
    elsif (/^-verbosity/) {
        $_ = shift;
        ((($VERBOSITY) = /^(\d+)$/)
         || print("invalid verbosity level: $_, must be a number\n")
         && &usage && die);
    }
    elsif (/^-h(elp)?$/) {
	&usage;
    }
    elsif (/^-v/) {
	print "$TEX2HTMLV_SHORT\n";
	exit 0;
    }
    ###MEH
    elsif (/^-html_version$/) {
	$_ = shift;
	$HTML_VERSION = $_;
#	if ( /^\d(\.\d)?$/ ) {
#	    $HTML_VERSION = $_;
#	} else {
#	    print("Invalid HTML version, defaulting to $HTML_VERSION\n");
#	};
    }
    elsif (/^-strict$/) {
	$STRICT_HTML = 1;
    }
    elsif (/^-no_strict$/) {
	$STRICT_HTML = 0;
    }
    else {
	&usage;
	die "Unrecognised switch: $_\n";
    }
}

if ( $EXTERNAL_UP_TITLE || $EXTERNAL_UP_LINK ) {
    if ( ! $EXTERNAL_UP_TITLE || !$EXTERNAL_UP_LINK ) {
	print STDERR "Need to specify both a parent URL and a parent title!\n";
	$EXTERNAL_UP_TITLE = $EXTERNAL_UP_LINK = "";
    };
};

if ( $EXTERNAL_DOWN_TITLE || $EXTERNAL_DOWN_LINK ) {
    if ( ! $EXTERNAL_DOWN_TITLE || !$EXTERNAL_DOWN_LINK ) {
	print STDERR "Need to specify both a parent URL and a parent title!\n";
	$EXTERNAL_DOWN_TITLE = $EXTERNAL_DOWN_LINK = "";
    };
};

# $NO_NAVIGATION = 1 unless $MAX_SPLIT_DEPTH;	#  Martin Wilck

if (($MAX_SPLIT_DEPTH) && ($MAX_SPLIT_DEPTH < 0 )) {
    $MAX_SPLIT_DEPTH *= -1; $REL_DEPTH = 1 }

$NO_FOOTNODE = 1 unless ($MAX_SPLIT_DEPTH || $NO_FOOTNODE);
$NO_SPLIT = 1 unless $MAX_SPLIT_DEPTH;
$SEGMENT = $SEGMENTED = 0;
$NO_MATH_MARKUP = 1;
if ($SHORTEXTN) { $EXTN = ".htm"; }
else { $EXTN = ".html"; }
$CHARSET = $charset unless ($CHARSET);
$CHARSET = "iso_8859_1" unless ($CHARSET);

####################################################################
#
# Figure out what options we need to pass to DVIPS and store that in
# the $DVIPSOPT variable.  Also, scaling is taken care of at the
# dvips level if PK_GENERATION is set to 1, so adjust SCALE_FACTORs
# accordingly.
#
if ($SCALABLE_FONTS) {
    $PK_GENERATION = 0;
    $DVIPS_MODE = '';
}


if ($PK_GENERATION) {
    if ($MATH_SCALE_FACTOR <= 0) { $MATH_SCALE_FACTOR = 2; }
    if ($FIGURE_SCALE_FACTOR <= 0) { $FIGURE_SCALE_FACTOR = 2; }
    $saveMSF = $MATH_SCALE_FACTOR;
    $saveFSF = $FIGURE_SCALE_FACTOR;
    $desired_dpi = int($MATH_SCALE_FACTOR*75);
    $FIGURE_SCALE_FACTOR = ($METAFONT_DPI / 72) *
	($FIGURE_SCALE_FACTOR / $MATH_SCALE_FACTOR) ;
    $MATH_SCALE_FACTOR = $METAFONT_DPI / 72;
    $dvi_mag = int(1000 * $desired_dpi / $METAFONT_DPI);
    $mode_switch = "-mode $DVIPS_MODE" if $DVIPS_MODE;
    if ($dvi_mag > 1000) {
	&write_warnings(
	    "WARNING: Your SCALE FACTOR is too large for PK_GENERATION.\n" .
	    "         See latex2html.config for more information.\n");
    }

    # RRM: over-sized scaling, using dvi-magnification
    if ($EXTRA_IMAGE_SCALE) {
	print "\n *** Images at $EXTRA_IMAGE_SCALE times resolution of displayed size ***\n";
	$desired_dpi = int($EXTRA_IMAGE_SCALE * $desired_dpi+.5);
	print "    desired_dpi = $desired_dpi  METAFONT_DPI = $METAFONT_DPI\n" if $DEBUG;
	$dvi_mag = int(1000 * $desired_dpi / $METAFONT_DPI);
	$MATH_SCALE_FACTOR = $saveMSF;
	$FIGURE_SCALE_FACTOR = $saveFSF;
    }
    $DVIPSOPT = " -y $dvi_mag -D $METAFONT_DPI $mode_switch -e 5 ";
} else {
#    if ($EXTRA_IMAGE_SCALE) {
#	&write_warnings(
#	   "the \$EXTRA_IMAGE_SCALE feature requires either \$PK_GENERATION=1"
#			. " or the '-scalable_fonts' option");
#	print "\n*** the \$EXTRA_IMAGE_SCALE feature requires \$PK_GENERATION=1"
#	    . " or the '-scalable_fonts' option ***\n";
#	$EXTRA_IMAGE_SCALE = '';
#    }
    $DVIPSOPT = ' -M ';
}

# The mapping from numbers to accents.
# These are required to process the \accent command, which is found in
# tables of contents whenever there is an accented character in a
# caption or section title.  Processing the \accent command makes
# $encoded_*_number work properly (see &extract_captions) with
# captions that contain accented characters.
# I got the numbers from the plain.tex file, version 3.141.

# Missing entries should be looked up by a native speaker.
# Have a look at generate_accent_commands and $iso_8859_1_character_map.

# MEH: added more accent types
%accent_type = (
 '18', 'grave',			# \`
 '19', 'acute',			# `'
 '20', 'caron',			# \v
 '21', 'breve',			# \u
 '22', 'macr',			# \=
 '23', 'ring',			#
 '24', 'cedil',			# \c
 '94', 'circ',			# \^
 '95', 'dot',			# \.
 '7D', 'dblac',			# \H
 '7d', 'dblac',			# \H
 '7E', 'tilde',			# \~
 '7e', 'tilde',			# \~
 '7F', 'uml',			# \"
 '7f', 'uml',			# \"
);

&driver;

# Process each file ...
sub driver {
    local($FILE, $orig_cwd, %unknown_commands, %dependent, $bbl_cnt, $dbg);
    # MRO: $texfilepath has to be global!
    local(%styles_loaded);
    $orig_cwd = &getcwd;

    print "\n *** initialise *** " if ($VERBOSITY > 1);
    &initialise;		# Initialise some global variables

    print "\n *** check modes *** " if ($VERBOSITY > 1);
    &ascii_mode if $ASCII_MODE;	# Must come after initialization
    &titles_language($TITLES_LANGUAGE);
    $dbg = $DEBUG ? "-debug" : "";
    $dbg .= ($VERBOSITY>2) ? " -verbose" : "";

    print "\n *** files: ".join(',',@ARGV)." *** " if ($VERBOSITY > 1);
    foreach $FILE (@ARGV) {
	local($bbl_nr) = 1;
	# The number of reused images and those in images.tex
	local($global_page_num) = (0);
	# The number of images in images.tex
	local($new_page_num) = (0);
	local($pid, $sections_rx,
	      $outermost_level, %cached_env_img, %id_map, %latex_body,
	      $latex_body, %symbolic_labels, %latex_labels,
	      %encoded_section_number,
	      %verbatim, %new_command, %new_environment,
	      $preamble, $preamble_aux);
## AYS: Allow extension other than .tex and make it optional
	($EXT = $FILE) =~ s/.*\.([^\.]*)$/$1/;
	if ( $EXT eq $FILE ) {
	    $EXT = "tex";
	    $FILE =~ s/$/.tex/;
	}
	($texfilepath, $FILE) = &get_full_path($FILE);

	die "Cannot read $texfilepath$dd$FILE \n"
	    unless (-f "$texfilepath$dd$FILE");


# Tell texexpand which files we *don't* want to look at.
	$ENV{'TEXE_DONT_INCLUDE'} = $DONT_INCLUDE if $DONT_INCLUDE;
# Tell texexpand which files we *do* want to look at, e.g.
# home-brew style files
	$ENV{'TEXE_DO_INCLUDE'} = $DO_INCLUDE if $DO_INCLUDE;

	$FILE =~ s/\.[^\.]*$//; ## AYS
	$DESTDIR = $FILE unless $DESTDIR;
	$PREFIX  = "$FILE-" if $AUTO_PREFIX;
	$DESTDIR = "." if $NO_SUBDIR;

	print "OPENING $texfilepath$dd$FILE.$EXT \n"; ## AYS

	next unless &new_dir($DESTDIR);

# Need to clean up a bit in case there's garbage left
# from former runs.
	chdir($DESTDIR) || die "$!\n";
	&cleanup(1);
	chdir($orig_cwd);

# JCL(jcl-dir)
# We need absolute paths for TEXINPUTS here, because
# we change the directory
	&deal_with_texinputs($orig_cwd, $texfilepath);

# This needs $DESTDIR to have been created ...
	print " *** calling  `texexpand' ***" if ($VERBOSITY > 1);
	&syswait("$TEXEXPAND $dbg -auto_exclude -save_styles " .
		 "$DESTDIR${dd}TMP_styles $texfilepath$dd$FILE.$EXT > $DESTDIR${dd}TMP_$FILE")
	    && print "Error: $!\n";

	chdir($DESTDIR);
	$SIG{'INT'} = 'handler';

	&open_dbm_database;

	if ($IMAGES_ONLY) {
	    &make_off_line_images}
	else {
	    &rename_image_files;
	    &load_style_file_translations;
	    &make_language_rx;
	    &make_raw_arg_cmd_rx;
#	    &make_isolatin1_rx unless ($NO_ISOLATIN);
	    &translate_titles;
	    print "\nReading ...";
	    &slurp_input_and_partition_and_pre_process("TMP_$FILE");
	    &add_preamble_head;
	    # Create a regular expressions
	    &set_depth_levels;
	    &make_sections_rx;
	    &make_order_sensitive_rx;
	    &add_document_info_page if (($INFO) && !(/\\htmlinfo/));
	    &add_bbl_and_idx_dummy_commands;
	    &translate;	# Destructive!
	}
	&cleanup;
	&style_sheet;

#JCL: read warnings from file to $warnings
	local($warnings) = &get_warnings;
	print "\n\n*********** WARNINGS ***********  \n$warnings"
	    if ($warnings || $NO_IMAGES || $IMAGES_ONLY);
	&image_cache_message if ($NO_IMAGES || $IMAGES_ONLY);
	&image_message if ($warnings =~ /Failed to convert/io);
	undef $warnings;

# JCL - generate directory index entry.
# Yet, a hard link, cause Perl lacks symlink() on some systems.
	do {
	    local($from,$to) = (eval($LINKPOINT),eval($LINKNAME));
	    if (length($from) && length($to) && ($from ne $to)) {
		unlink($to);
		link($from,$to);
	    }
	} unless ($NO_AUTO_LINK || !($LINKPOINT) || !($LINKNAME));

# Go back to the source directory
	chdir($orig_cwd);
    }
    print "\nUnknown commands: ". join(" ",keys %unknown_commands)
	if %unknown_commands;
###MEH -- math support
    print "\nMath commands outside math: " .
	join(" ",keys %commands_outside_math) .
	    "\n  Output may look weird or may be faulty!\n"
		if %commands_outside_math;
    print "\nDone.\n";
    $end_time = time; 
    $total_time = $end_time - $start_time;
    print join(' ',"Timing:",$total_time,"seconds")
	if ($TIMING||$DEBUG||($VERBOSITY > 2));
    $_;
}

sub open_dbm_database {
    # These are DBM (unix DataBase Management) arrays which are actually
    # stored in external files. They are used for communication between
    # the main process and forked child processes;
    dbmopen(%verb, "TMP_verb",0755);
    dbmopen(%verb_delim, "TMP_verb_delim",0755);
    dbmopen(%expanded,"TMP_expanded",0755);
# Holds max_id, verb_counter, verbatim_counter, eqn_number
    dbmopen(%global, "TMP_global",0755);

# Theses next two are used during off-line image conversion
# %new_id_map maps image id's to page_numbers of the images in images.tex
# %image_params maps image_ids to conversion parameters for that image
    dbmopen(%new_id_map, ".ID_MAP",0755);
    dbmopen(%img_params, ".IMG_PARAMS",0755);
    dbmopen(%orig_name_map, ".ORIG_MAP",0755);

    $global{'max_id'} = ($global{'max_id'} | 0);
    &read_mydb(*verbatim, "verbatim");
    $global{'verb_counter'} = ($global{'verb_counter'} | 0);
    $global{'verbatim_counter'} = ($global{'verbatim_counter'} | 0);
    &read_mydb(*new_command, "new_command");
    &read_mydb(*new_theorem, "new_theorem");
    &read_mydb(*new_environment, "new_environment");
    &read_mydb(*dependent, "dependent");
    $preamble = &read_mydb(*preamble, "preamble");
    $preamble_aux = &read_mydb(*preamble_aux, "preamble_aux");
    &restore_critical_variables;
}

sub close_dbm_database {
    &save_critical_variables;
    dbmclose(%verb); undef %verb;
    dbmclose(%verb_delim); undef %verb_delim;
    dbmclose(%expanded); undef %expanded;
    dbmclose(%global); undef %global;
    dbmclose(%new_id_map); undef %new_id_map;
    dbmclose(%img_params); undef %img_params;
    dbmclose(%orig_name_map); undef %orig_name_map;
}

sub clear_images_dbm_database {
    # <Added calls to dbmclose dprhws>
    # %new_id_map will be used by the off-line image conversion process
    #
    dbmclose(%new_id_map);
    dbmclose(%img_params);
    dbmclose(%orig_name_map);
    undef %new_id_map;
    undef %img_params;
    undef %orig_name_map;
    dbmopen(%new_id_map, ".ID_MAP",0755);
    dbmopen(%img_params, ".IMG_PARAMS",0755);
    dbmopen(%orig_name_map, ".ORIG_MAP",0755);
}

sub save_critical_variables{
    $global{'math_markup'} = $NO_MATH_MARKUP;
    $global{'isolatin'} = $ISOLATIN_CHARS;
    $global{'unicode'} = $UNICODE_CHARS;
    if ($UNFINISHED_ENV) {
	$global{'unfinished_env'} = $UNFINISHED_ENV;
	$global{'replace_end_env'} = $REPLACE_END_ENV;
    }
    if (@UNMATCHED_OPENING) {
	$global{'unmatched'} = join(',',@UNMATCHED_OPENING);
    }
}

sub restore_critical_variables{
    $NO_MATH_MARKUP = ($global{'math_markup'}|
	(defined $NO_MATH_MARKUP ? $NO_MATH_MARKUP:1));
    $ISOLATIN_CHARS = ($global{'isolatin'}|
	(defined $ISOLATIN_CHARS ? $ISOLATIN_CHARS:0));
    $UNICODE_CHARS = ($global{'unicode'}|
	(defined $UNICODE_CHARS ? $UNICODE_CHARS:0));
    if ($global{'unfinished_env'}) {
	$UNFINISHED_ENV = $global{'unfinished_env'};
	$REPLACE_END_ENV = $global{'replace_end_env'};
    }
    if ($global{'unmatched'}) {
	@UNMATCHED_OPENING = split(',',$global{'unmatched'});
    }
}


#JCL: The warnings should have been handled within the DBM database.
# Unfortunately if the contents of an array are more than ~900 (system
# dependent) chars long then dbm cannot handle it and gives error messages.
sub write_warnings {
    local($_) = @_;
    open(DB,">>WARNINGS");	#file name mustn't start with TMP_
    print DB $_;
    print STDERR "\n *** Warning: $_\n" if ($VERBOSITY > 1);
    close DB;
}

sub get_warnings {
    local($_);
    return unless (-f "WARNINGS");
    $_ = `cat WARNINGS`;
    unlink("WARNINGS");
    $_;
}

sub lost_argument {
    local($cmd) = @_;
    &write_warnings("\nincomplete argument to command: \\$cmd");
}

# These three subroutines should have been handled within the DBM database.
# Unfortunately if the contents of an array are more than ~900 (system
# dependent) chars long then dbm cannot handle it and gives error messages.
# So here we save and then read the contents explicitly.
sub write_mydb {
    local($db, $key, $_) = @_;
    open(DB,">>TMP_$db");
    print DB join('', "\n$mydb_mark","#", $key, "#", $_);
    close DB;
}

sub write_mydb_simple {
    local($db, $_) = @_;
    open(DB,">TMP_$db");
    print DB $_;
    close DB;
}

sub clear_mydb {
    local($db) = @_;
    open(DB,">TMP_$db");
    close DB;
}

# Assumes the existence of a file TMP_verbatim which contains
# sequences of verbatim counters and verbatim contents.
sub read_mydb {
    local(*db,$name) = @_;
    local($_,@tmp,$i,$tmp1,$tmp2);
    return unless (-f "TMP_$name");
    $_ = `cat TMP_$name`;
    $| = 1;
    @tmp = split(/\n$mydb_mark#([^#]*)#/);
    $i = 1;	# Ignore the first element at 0
    while ($i < scalar(@tmp)) {
	$tmp1 = $tmp[$i]; $tmp2 = $tmp[++$i];
	$db{$tmp1} = $tmp2;
	++$i;
    };
    undef @tmp;
    $_;
}


# Reads in a latex generated file (e.g. .bbl or .aux)
# It returns success or failure
# ****** and binds $_ in the caller as a side-effect ******
sub process_ext_file {
    local($ext) = @_;
    local($found, $extfile,$dum) = 0;
    $extfile = $FILE;
    $extfile = $EXTERNAL_FILE if $EXTERNAL_FILE;
    local($file) = &fulltexpath("$extfile.$ext");
    &write_warnings(
	    "\n$extfile.$EXT is newer than $extfile.$ext: Please rerun latex" . ## AYS
	    (($ext =~ /bbl/) ? " and bibtex.\n" : ".\n"))
	if ( ($found = (-f $file)) &&
	    &newer(&fulltexpath("$extfile.$EXT"), $file)); ## AYS
    if ( $found ) {
	print "\nReading $extfile.$ext ...";
	# must allow @ within control-sequence names
	$dum = &do_cmd_makeatletter();
	&slurp_input($file);
	&pre_process;
	&substitute_meta_cmds if (%new_command || %new_environment);
	&wrap_shorthand_environments;
	$_ = &translate_commands(&translate_environments($_));
	$dum = &do_cmd_makeatother();
	if ($ext eq "aux") {
	    $preamble_aux .= "\\AtBeginDocument{\\input $file }\n";
	}
    } else { 
	print "\n*** Could not find file: $file ***\n" if ($DEBUG)
    };
    $found;
}

sub deal_with_texinputs {
# The dot precedes all, this let's local files override always.
# The dirs we want are given as parameter list.
    die("You must have TEXINPUTS set at least to the TeX library to" .
	" work with LaTeX2HTML")
	unless $ENV{'TEXINPUTS'};
    $ENV{'TEXINPUTS'} = join(':',".",@_,$ENV{'TEXINPUTS'});
}

sub add_document_info_page {
    # Uses $outermost_level
    # Nasty race conditions if the next two are done in parallel
    local($X) = ++$global{'max_id'};
    local($Y) = ++$global{'max_id'};
    ###MEH -- changed for math support: no underscores in commandnames
    $_ = join('', $_, "\\$outermost_level", "*",
	      "$O$X$C $O$Y$C $info_title$O$Y$C $O$X$C \n",
	      " \\textohtmlinfopage");
}

# For each style file name in TMP_styles (generated by texexpand) look for a
# perl file in $LATEX2HTMLDIR/styles and load it.
sub load_style_file_translations {
    local($_, $style, $options, $dir);
    print "\n";
    if ($TEXDEFS) {
	foreach $dir (split(/:/,$LATEX2HTMLSTYLES)) {
	    if (-f ($_ = "$dir${dd}texdefs.perl")) {
		print "Loading $_...\n";
		require ($_);
		$styles_loaded{'texdefs'} = 1;
		last;
	    }
	}
    }
    open(STYLES, "<TMP_styles");
    while(<STYLES>) {
	    if(s/^\s*(\S+)\s*(.*)$/$style = $1; $options = $2;/eo) {
            &do_require_package($style);
	    $_ = $DONT_INCLUDE;
	    s/:/|/g;
	    &write_warnings("No implementation found for style \`$style\'\n")
		unless $styles_loaded{$style} || $style =~ /^($_)$/;

            # MRO: Process options for packages
            &do_package_options($style,$options) if($options);
        }
    }
    close(STYLES);
}

################## Weird Special case ##################

# The new texexpand can be told to leave in \input and \include
# commands which contain code that the translator should simply pass
# to latex, such as the psfig stuff.  These should still be seen by
# TeX, so we add them to the preamble ...

sub do_include_lines {
    while (s/$include_line_rx//o) {
	local($include_line) = &revert_to_raw_tex($&);
	&add_to_preamble ('include', $include_line);
    }
}

########################## Preprocessing ############################

# JCL(jcl-verb)
# The \verb declaration and the verbatim environment contain simulated
# typed text and should not be processed. Characters such as $,\,{,and }
# loose their special meanings and should not be considered when marking
# brackets etc. To achieve this \verb declarations and the contents of
# verbatim environments are replaced by markers. At the end the original
# text is put back into the document.
# The markers for verb and verbatim are different so that these commands
# can be restored to what the raw input was just in case they need to
# be passed to latex.

sub pre_process {
    # Modifies $_;
    #JKR: We need support for some special environments.
    # This has to be here, because  they might contain
    # structuring commands like \section etc.
    local(%comments);
    local($comment_counter) = 0;

    $* = 1;			# Multiline matching ON
    &pre_pre_process if (defined &pre_pre_process);
    $* = 0;			# Multiline matching OFF
    &replace_html_special_chars;
    $* = 1;			# Multiline matching ON
    # Remove fake environment which should be invisible to LaTeX2HTML.
    s/%end{latexonly}/\001/go;
    s/%begin{latexonly}([^\001]*)\001/%/go;
    # Move all LaTeX comments into a local list
    s/^([ \t]*%.*)\n/$comments{++$comment_counter} = "$1";
	"$comment_mark"."$comment_counter\n"/ge;

    # Remove the htmlonly-environment
    s/\\begin{htmlonly}\s*\n?//go;
    s/\\end{htmlonly}\s*\n?//go;
    # Remove enviroments which should be invisible to LaTeX2HTML.
    s/\n[^%\n]*\\end{latexonly}\s*\n?/\001/go;
    s/((^|\n)[^%\n]*)\\begin{latexonly}([^\001]*)\001/$1/go;
    s/\\end{comment}\s*\n?/\001/go;
    s/\\begin{comment}([^\001]*)\001//go;
    $* = 0;			# Multiline matching OFF

    s/\\\\/\\\\ /go;		# Makes it unnecessary to look for escaped cmds
    local($next, $esc_del);
    &normalize_language_changes;
    # Patches by #JKR, #EI#, #JCL(jcl-verb)
    while (/\\begin\{($verbatim_env_rx|$keepcomments_rx)\}/o) {
	local($before, $contents, $after, $env);
	($before, $after, $env) = ($`, $', $1) unless ($env);
 	if ($after =~ /\s*\\end{$env[*]?}/) { # Must NOT use the s///o option!!!
	    ($contents, $after) = ($`, $');
 	    $contents =~ s/^\n+//;
	
	    # re-insert comments
	    $contents =~ s/$comment_mark(\d+)/$comments{$1}/g;

	    # revert '\\ ' -> '\\' only once 
	    if ($env =~ /rawhtml|$keepcomments_rx/i) {
		$contents = &revert_to_raw_tex($contents);
	    } else {
		$contents =~ s/\\\\ /\\\\/go;
	    }
	    if ($env =~/$keepcomments_rx/) {
		++$global{'verbatim_counter'};
		$verbatim{$global{'verbatim_counter'}} = "$contents";
	    } else {
		&write_mydb("verbatim", ++$global{'verbatim_counter'}, $contents);
	    }
#	    $verbatim{$global{'verbatim_counter'}} = "$contents" if ($env =~/$keepcomments_rx/);
#	    $verbatim{$global{'verbatim_counter'}} = "$contents";

 	    $after = join("",$verbatim_mark,$env,$global{'verbatim_counter'},$after);}
     	else {
	    print "Cannot find \\end{$env}\n";
	}
	$_ = join("",$before,$after);
    }
    # Now do the \verb declarations
    # Patches by: #JKR, #EI#, #JCL(jcl-verb)
    # Tag \verb command and legal opening delimiter with unique number.
    # Replace tagged ones and its contents with $verb_mark & id number if the
    # closing delimiter can be found. After no more \verb's are to tag, revert
    # tagged one's to the original pattern.
    local($del,$contents);
    local($id) = $global{'verb_counter'};
    # must tag only one alternation per loop
    while (s/\\verb(\t*\*\t*)(\S)/"<verb$1".++$id.">$2"/e ||
	   s/\\verb()([^a-zA-Z*\s])/"<verb$1".++$id.">$2"/e ||
	   s/\\verb(\t\t*)([^*\s])/"<verb$1".++$id.">$2"/e) {

	$del = $2;
	$esc_del = &escape_rx_chars($del);

	# try to find closing delimiter and substitute the complete
	# statement with $verb_mark
	s/<verb[^\d>]*$id>[$esc_del]([^$esc_del]*)[$esc_del]/
	    $contents=$1;
	$contents =~ s|\\\\ |\\\\|g;
	$contents =~ s|\n| |g;
	$verb{$id}=$contents;
	$verb_delim{$id}=$del;
	join('',$verb_mark,$id,$verb_mark)/e;
    }
    $global{'verb_counter'} = $id;
    # revert changes to fake verb statements
    s/<verb([^\d>]*)\d+>/\\verb$1/g;

    $* = 1;			# Multiline matching ON
    &preprocess_alltt if defined(&preprocess_alltt);
    #JKR: the comments include the linebreak and the following whitespace
#   s/([^\\]|^)(%.*\n[ \t]*)+/$1/go; # Remove Comments but not % which may be meaningful
    s/$comment_mark(\d+)\n//go; # Remove comment markers
#  HWS:  Correctly remove multiple %%'s.
#
    s/\\%/\002/g;
    s/(%.*\n[ \t]*)//g;
    s/\002/\\%/g;
    s/$verbatim_mark$keepcomments_rx(\d+)/
	"\\begin{$1}\n".$verbatim{$2}."\n\\end{$1}"/ego; # Raw TeX
#
    $* = 0;			# Multiline matching OFF
    &mark_string;
#    &make_unique;
}

sub warn_if_too_long {
    local(*str,*type) = @_;
    if (length($str) > 900) {
	local($tmp) = &get_first_words($str, 7);
	&write_warnings(
		"A $type environment is too long and may have disappeared\n" .
		"(causing \"dbm\" errors). Try separating it into smaller pieces.\n" .
		"Potential DBM error:\n$tmp\n\n");
	print "\nPotential DBM error >>>: \n$tmp\n<<<\n";

    }
}



#################### Marking Matching Brackets ######################

# Reads the entire input file and performs pre_processing operations
# on it before returning it as a single string. The pre_processing is
# done on separate chunks of the input file by separate Unix processes
# as determined by LaTeX \input commands, in order to reduce the memory
# requirements of LaTeX2HTML.
sub slurp_input_and_partition_and_pre_process {
    local($file) = @_;
    local(%string, @files, $pos);
    local ($count) =  1;

    open(SINPUT,"<$file");
    while (<SINPUT>) {
	if (/TEXEXPAND: INCLUDED FILE MARKER (\S*)/) {
	    # Forking seems to screw up the rest of the input stream
	    # We save the current position ...
	    $pos = tell SINPUT;
	    &write_string_out($count);
	    delete $string{'STRING'};
	    # ... so that we can return to it
	    seek(SINPUT, $pos, 0);
	    print STDERR "\nDoing $1";
	    ++$count}
	else {
	    $string{'STRING'} .= $_}
    }
    &write_string_out($count);
    delete $string{'STRING'};
    close SINPUT;
    @files = sort file_sort (<TMP-part*>);
    die "\nFailed to read in document parts.\n".
	"Look up section Globbing in the troubleshooting manual.\n"
	    if $#files < 0;

    foreach $file (@files) {
	$_ .= `cat $file`;
    }
    die "\nFailed to read in document parts (out of memory?).\n"
	unless length($_);
}

sub write_string_out {
    local($count) = @_;
    local($ppid) = "TMP";
    local($pid);
    # All open unflushed streams are inherited by the child. If this is
    # not set then the parent will *not* wait
    $| = 1;
    # fork returns 0 to the child and PID to the parent
    &close_dbm_database;
    unless ($pid = fork) {
    	local($_);
	&open_dbm_database;
	$_ = delete $string{'STRING'};
	# Replace verbatim environments etc.
	&pre_process;
	# Handle newcommand, newenvironment, newcounter ...
	&substitute_meta_cmds;
	&wrap_shorthand_environments;
	print STDERR "\n *** End-of-partition ***" if ($VERBOSITY > 4);
	open(OUTPUT, ">$ppid-part$count");
	print OUTPUT $_;
	close(OUTPUT);
	print STDERR $_ if ($VERBOSITY > 9);
	&write_mydb_simple("preamble", $preamble);
	&write_mydb_simple("preamble_aux", $preamble_aux);
	&close_dbm_database;
	exit 0;
    };
    waitpid($pid,0);
    &open_dbm_database;
}

# Reads the entire input file into a
# single string.
sub slurp_input  {
    local($file) = @_;
    local(%string);
    open(INPUT,"<$file");
    while (<INPUT>) {
	$string{'STRING'} .= $_};
    close INPUT;
    $_ = delete $string{'STRING'}; # Blow it away and return the result
}

sub special { ($x) = @_; $y= $html_specials{$x}; ($y ? $y : $x)}
sub special_inv { ($x) = @_; $y= $html_specials_inv{$x}; ($y ? $y : $x)}


# Mark each matching opening and closing bracket with a unique id.
sub mark_string {
    # Modifies $_ in the caller;
    $* = 1;			# Multiline matching ON
    s/^\\{|([^\\])\\{/$1tex2html_escaped_opening_bracket/go;
    s/^\\{|([^\\])\\{/$1tex2html_escaped_opening_bracket/go; # repeat this
    s/^\\}|([^\\])\\}/$1tex2html_escaped_closing_bracket/go;
    s/^\\}|([^\\])\\}/$1tex2html_escaped_closing_bracket/go; # repeat this
    $* = 0;			# Multiline matching OFF
    local($id) = $global{'max_id'};
    for (;;) {			# Infinite loop
	last unless
	    s/{([^{}]*)}/join("",$O,++$id,$C,$1,$O,$id,$C)/geo;
    }
    local($before,$after) = ('','');
    while (/\{/) { 
	local($before) = $`;
	local($after) = $';
	if ((@UNMATCHED_OPENING) && ($before =~ /\}/)){
	    while ($before =~ /\}/) {
	        local ($this) = pop(@UNMATCHED_OPENING);
	        print "\n *** matching brace \#$this found ***\n";
	        $before =~ s/\}/join("",$O,$this,$C)/geo;
	    }
	    $_= join('',$before,"\{",$after);
	}
	s/\{/join("",$O,++$id,$C)/geo;
	$before = $`;
	$after = $';
        if ($after =~ /\}/) { 
	    $after =~ s/\}/join("",$O,$id,$C)/geo;
	    $_ = join('',$before,$O,$id,$C,$after);
	} else {
	    print "\n *** opening brace \#$id  is unmatched ***\n";
	    $after =~ /^([^\n]+\n)/;
	    print " preceding: $& \n\n";
	    push (@UNMATCHED_OPENING,$id);
	}
    }
    while (/\}/) {
	local($afterclose);
	if (@UNMATCHED_OPENING) {
	    local ($this) = pop(@UNMATCHED_OPENING);
	    print "\n *** matching brace \#$this found ***\n";
	    s/\}/join("",$O,$this,$C)/geo;
        } else {
	    print "\n *** there was an unmatched closing \} ";
	    local($beforeline,$prevline,$afterline) = ($`, $`.$& , $');
	    $* = 1; $prevline =~ /\n([^\n]+)\}$/; 
	    if ($1) {
		print "at the end of:\n" . $1 . "\}\n\n";
	    } else {
		$afterline =~ /^([^\n]+)\n/;
		if ($1) {
		    print "at the start of:\n\}" . $1 ."\n\n";
		} else {
		    $prevline =~ /\n([^\n]+)\n\}$/;
		    print "on a line by itself after:\n" . $1 . "\n\}\n\n";
		}
	    }
	    $* = 0;
	    $_ =  $beforeline . $afterline;
        }
    }
    $global{'max_id'} = $id;

    s/tex2html_escaped_opening_bracket/\\{/go;
    s/tex2html_escaped_closing_bracket/\\}/go;
}

sub replace_html_special_chars {
    # Replaces html special characters with markers unless preceded by "\"
    $* = 1;			# Multiline matching ON
    s/([^\\])(<|>|&|\")/&special($1).&special($2)/geo;
    # MUST DO IT AGAIN JUST IN CASE THERE ARE CONSECUTIVE HTML SPECIALS
    s/([^\\])(<|>|&|\")/&special($1).&special($2)/geo;
    s/^(<|>|&|\")/&special($1)/geo;
    $* = 0;			# Multiline matching OFF
}

# The bibliography and the index should be treated as separate sections
# in their own HTML files. The \bibliography{} command acts as a sectioning command
# that has the desired effect. But when the bibliography is constructed
# manually using the thebibliography environment, or when using the
# theindex environment it is not possible to use the normal sectioning
# mechanism. This subroutine inserts a \bibliography{} or a dummy
# \textohtmlindex command just before the appropriate environments
# to force sectioning.
sub add_bbl_and_idx_dummy_commands {
    local($id) = $global{'max_id'};

    s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
    #if ($bbl_cnt == 1) {
	s/([\\]begin\s*$O\d+$C\s*thebibliography)/$id++; "\\bibliography$O$id$C$O$id$C $1"/geo;
    #}
    $global{'max_id'} = $id;
    s/([\\]begin\s*$O\d+$C\s*theindex)/\\textohtmlindex $1/o;
    s/[\\]printindex/\\textohtmlindex /o;
    &lib_add_bbl_and_idx_dummy_commands() if defined(&lib_add_bbl_and_idx_dummy_commands);
}


# Uses and modifies $default_language
# This would be straight-forward except when there are
#  \MakeUppercase, \MakeLowercase  or \uppercase , \lowercase commands
# present in the source. The cases have to be adjusted before the
# ISO-character code is set; e.g. with "z --> "Z  in  german.perl
#
sub convert_iso_latin_chars {
    local($_) = @_;
    local($next_language, $pattern, $before, $after, $funct, $level, $delim);
    while (/$case_change_rx/) {
	$before .= $`;
	$funct = $&;
	$after = '';
	$_ = $';
	s/^[\s%]*(.)/$delim=$1;''/eo;

	if ($delim =~ /{/ ) {
            # brackets not yet numbered...
	    $before .= $funct . $delim;
	    $level = 1;
	    $after = $delim;
	    while (($level)&&($_)&&(/[\{\}]/)) {
		$after .= $` . $&;
		$_ = $';
		if ( "$&" eq "\{" ) {$level++}
		elsif ( "$&" eq "\}" ) { $level-- }
		else { print $_ }
		print "$level";
	    } 
	    $before .= $after;
	} elsif ($delim eq "<") {
            # brackets numbered, but maybe not processed...
	    s/((<|#)(\d+)(>|#)>).*\1//;
	    $after .= $delim . $&;
	    $_ = $';
	    print "<$2$funct$4>" if ($VERBOSITY > 2);
	    $funct =~ s/^\\//o;
	    local($cmd) = "do_cmd_$funct";
	    $after = &$cmd($after);
	    $before .= $after;
	} else {
            # this should not happen, but just in case...
	    $funct =~ s/^\\//o;
	    local($cmd) = "do_cmd_$funct";
	    print "<$delim$funct>" if ($VERBOSITY > 2);
	    $_ = join('', $delim , $_ );
	    if (defined &$cmd) { $_ = &$cmd($_) }
	}
    }
    $_ = join('', $before, $_) if ($before);

    # ...now do the conversions
    ($before, $after, $funct) = ('','','');
    if (/$language_rx/o) {
	($next_language, $pattern, $before, $after) = (($1||$2), $&, $`, $');
	$before = &convert_iso_latin_chars($before);
	$default_language = $next_language;
	$_ = join($pattern, $before,
		  &convert_iso_latin_chars($after));
    } else {
	$funct = $language_translations{$default_language};
	(defined(&$funct) ? $_ = &$funct($_) :
	 do {   &write_warnings(
		"\nCould not find translation function for $default_language.\n\n")
	    }
	)
    }
    $_;
}

# May need to add something here later
sub english_translation {
    $_[0];
}

# This replaces \setlanguage{\language} with \languageTeX
# This makes the identification of language chunks easier.
sub normalize_language_changes {
    s/$setlanguage_rx/\\$1TeX/go;
}


# General translation mechanism:
#
#
# The main program latex2html calls texexpand with the document name
# in order to expand some of its \input and \include statements, here
# also called 'merging', and to write a list of sensitized style, class,
# input, or include file names.
# When texexpand has finished, all is contained in one file, TMP_foo.
# (assumed foo.tex is the name of the document to translate).
#
# In this version, texexpand cares for following environments
# that may span include files / section boundaries:
# (For a more technical description, see texexpand.)
#  a) \begin{comment}
#  b) %begin{comment}
#  c) \begin{any}  introduced with \excludecomment
#  d) %begin{any}
#  e) \begin{verbatim}
#  f) \begin{latexonly}
#  g) %begin{latexonly}
# 
# a)-d) cause texexpand to drop its contents, it will not show up in the
# output file. You can use this to 'comment out' a bunch of files, say.
# 
# e)-g) prevent texexpand from expanding input files, but the environment
# content goes fully into the output file.
# 
# Together with each merging of \input etc. there are so-called %%%texexpand
# markers accompanying the boundary.
#
# When latex2html reads in the output file, it uses these markers to write
# each part to a separate file, and process them further.
#
#
# If you have, for example:
#
# a) preample
# b) \begin{document}
# c) text
# d) \input{chapter}
# e) more text
# f) \end{document}
#
# you end up in two parts, part 1 is a)-c), part 2 is the rest.
# Regardless of environments spanning input files or sections.
#
#
# What now starts is meta command substitution:
# Therefore, latex2html forks a child process on the first part and waits
# until it finished, then forks another on the next part and so forth
# (see also &slurp_input_and_partition_and_preprocess).
#
# Here's what each child is doing:
# Each child process reads the new commands translated so far by the previous
# child from the TMP_global DBM database.
# After &pre_processing, it substitutes the meta commands (\newcommand, \def,
# and the like) it finds, and adds the freshly retrieved new commands to the
# list so far.
# This is done *only on its part* of the document; this saves upwards of memory.
# Finally, it writes its list of new commands (synopsis and bodies) to the
# DBM database, and exits.
# After the last child finished, latex2html reads in all parts and
# concatenates them.
#
#
# So, at this point in time (start of &translate), it again has the complete
# document, but now preprocessed and with new commands substituted.
# This has several disadvantages: an amount of commands is substituted (in
# TeX lingo, expanded) earlier than the rest.
# This causes trouble if commands really must get expanded at the point
# in time they show up.
#
#
# Then, still in &translate, latex2html uses the list of section commands to
# split the complete document into chunks.
# The chunks are not written to files or so, they are retained in the @sections
# list, but each chunk is handled separately.
# latex2html puts the current chunk to $_ and processes it with
# &translate_environments etc., then fetches the next chunk, and so on.
# This prevents environments that span section boundaries from getting
# translated, because \begin and \end cannot find one another, to say it this
# way.
#
#
# After the chunk is translated to HTML, it is written to a file.
# When all chunks are done, latex2html rereads each file to get cross
# references right, replace image markers with the image file names, and
# writes index and bibliography.
#
#
sub translate {
    &normalize_sections;	# Deal with the *-form of sectioning commands

    # Split the input into sections.
    # Due to the regular expression, each split will create 5 more entries.
    # Entry 1 and 2: non-letter/letter sectioning command,
    # entry 4: the delimiter (may be empty)
    # entry 5: the text.
    local(@sections) = split(/$sections_rx/, $_);
    local($sections) = int(scalar(@sections) / 5);

    # Initialises $curr_sec_id to a list of 0's equal to
    # the number of sectioning commands.
    local(@curr_sec_id) = split(' ', &make_first_key);
    local(@segment_sec_id) = @curr_sec_id;
    local($i, $j, $current_depth) = (0,0,0);
    local($curr_sec) = $FILE;
    local(%section_info, %toc_section_info, $CURRENT_FILE, %cite_info, %ref_files);
    # These filenames may be set when translating the corresponding commands.
    local($tocfile, $loffile, $lotfile, $footfile, $citefile, $idxfile,
	  $figure_captions, $table_captions, $footnotes, $citations, %index,
	  %done, $t_title, $t_author, $t_date, $t_address, $t_affil, $changed);
    local(%index_labels, %index_segment, $preindex, %footnotes, %citefiles);
    local($segment_table_captions, $segment_figure_captions);
    local($dir,$nosave) = ('','');
    local($del);

    &process_aux_file  if $SHOW_SECTION_NUMBERS || /\\(caption|(html|hyper)?(ref|cite))/;

    require ("${PREFIX}internals.pl") if (-f "${PREFIX}internals.pl");
#JCL(jcl-del)
    &make_single_cmd_rx;
#
    $tocfile = $EXTERNAL_CONTENTS;
    $idxfile = $EXTERNAL_INDEX;
    $citefile = $EXTERNAL_BIBLIO;
    $citefiles{1} = $citefile if ($citefile);
    print "\nTranslating ...";

    while ($i <= @sections) {
        undef $_;
	$_ = $sections[$i];
	s/^[\s]*//;		# Remove initial blank lines
	# The section command was removed when splitting ...
	s/^/\\$curr_sec$del/  if ($i > 0); # ... so put it back
	if ($current_depth < $MAX_SPLIT_DEPTH)  {

	if (($footnotes)&&($NO_FOOTNODE)&&( $current_depth < $MAX_SPLIT_DEPTH)) {
	    local($thesenotes) = &make_footnotes ;
	    print OUTPUT $thesenotes;
	}
	    $CURRENT_FILE = &make_name($curr_sec, join('_',@curr_sec_id));
	    open(OUTPUT, ">$CURRENT_FILE")
		|| die "Cannot open $DESTDIR/$FILE $!";
	};
	&remove_document_env;
#        &wrap_shorthand_environments;    #RRM  Is this needed ?
	print STDERR "\n" if ($VERBOSITY);
	print STDERR "\n" if ($VERBOSITY > 2);
	print $i/5;
        print "/$sections:$curr_sec:" if ($VERBOSITY);

	# Must do this early ... It also sets $TITLE
	&process_command($sections_rx, *_) if (/^$sections_rx/);
	if ((! $TITLE) || ($TITLE eq $default_title)) {
	    local($curr_sec_tex) = &revert_to_raw_tex($curr_sec);
	    print STDERR "\"$curr_sec_tex\" for $CURRENT_FILE\n" if ($VERBOSITY);
	} else { 
	    local($tmp) = &purify($TITLE,1);
	    $tmp = &revert_to_raw_tex($tmp);
	    print STDERR "\"$tmp\" for $CURRENT_FILE\n" if ($VERBOSITY); 
	}

	if ((/\\latextohtmlditchpreceding/)||(/\\startdocument/)) {
 	    local($after) = $';
	    print STDERR "\n *** translating preamble ***\n" if ($VERBOSITY);
	    $_ = &translate_preamble("$`$&");
	    s/\n\n//g; s/<BR>//g;	# remove redundant blank lines and breaks
#
#	    &process_aux_file  if$AUX_FILE_NEEDED;
#
	    print STDERR "\n *** preamble done ***\n" if ($VERBOSITY);
	    $PREAMBLE = 0;
 	    $NESTING_LEVEL=0;
	    $* = 1;
	    $after =~ s/^\s*//;
	    $* = 0;
	    print (($VERBOSITY >2)? "\n*** Translating environments ***" : ";");
            $after = &translate_environments($after);
	    print (($VERBOSITY >2)? "\n*** Translating commands ***" : ";");
            $_ .= &translate_commands($after);
#            $_ = &translate_commands($after);
 	} else {
	    &do_AtBeginDocument;
	    $PREAMBLE = 0;
 	    $NESTING_LEVEL=0;
	    print (($VERBOSITY >2)? "\n*** Translating environments ***" : ";");
 	    $_ = &translate_environments($_);
	    print (($VERBOSITY >2)? "\n*** Translating commands ***" : ";");
 	    $_ = &translate_commands($_);
 	}

	print (($VERBOSITY >2)? "\n*** Translations done ***" : "\n");
#	if (($footnotes)&&($NO_FOOTNODE)&&( $current_depth < $MAX_SPLIT_DEPTH)) {
#	    $_ .= &make_footnotes
#	}
	print OUTPUT $_;

	# Associate each id with the depth, the filename and the title
        ###MEH -- starred sections don't show up in TOC ...
	# RRM:  ...unless $TOC_STARS is set
	if ($TOC_STARS) {
	    $toc_section_info{join(' ',@curr_sec_id)} =
		"$current_depth$delim$CURRENT_FILE$delim$TITLE"
		    if ($current_depth <= $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
	} else {
	    $toc_section_info{join(' ',@curr_sec_id)} =
		"$current_depth$delim$CURRENT_FILE$delim$TITLE"
		. ($curr_sec =~ /star$/ ? "$delim<tex2html_star_mark>" : "")
		    if ($current_depth <= $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
	}

	# include $BODYTEXT in the section_info, when starting a new page
	$section_info{join(' ',@curr_sec_id)} =
	    "$current_depth$delim$CURRENT_FILE$delim$TITLE$delim"
		. (($current_depth < $MAX_SPLIT_DEPTH)? $BODYTEXT: "");

	# Get type of section (see also the split above)
	$curr_sec = $sections[$i+1].$sections[$i+2];
	$del = $sections[$i+4];

	# Get the depth of the current section;
	$current_depth = $section_commands{$curr_sec};
	for($j=0; $j <= $#curr_sec_id; $j++) {
	    $curr_sec_id[$j] += $segment_sec_id[$j];
	    $segment_sec_id[$j] = 0;
	}
	@curr_sec_id = &new_level($current_depth, @curr_sec_id);
	$TITLE = '';
	$i+=5; #skip to next text section
    }
    $_ = undef;
    $_ = &make_footnotes if ($footnotes);
    print OUTPUT;
    close OUTPUT;
    print STDERR "\n *** making images.tex ***" if ($VERBOSITY > 1);
    &make_image_file;
    print STDERR "\n *** making images ***" if ($VERBOSITY > 1);
    &make_images;
    # Link sections, add head/body/address do cross-refs etc
    print STDERR "\n *** post-process ***" if ($VERBOSITY > 1);
    &post_process;
    print STDERR "\n *** post-processed ***" if ($VERBOSITY > 1);
    &copy_icons if $LOCAL_ICONS;
    if ($SEGMENT || $DEBUG || $SEGMENTED) {
	&save_captions_in_file("figure",  $figure_captions) if $figure_captions;
	&save_captions_in_file("table",  $table_captions) if $table_captions;
#	&save_array_in_file ("captions", "figure_captions", %figure_captions) if %figure_captions;
#	&save_array_in_file ("captions", "table_captions", %table_captions) if %table_captions;
	&save_array_in_file ("index", "index", %index);
	&save_array_in_file ("sections", "section_info", %section_info);
	&save_array_in_file ("contents", "toc_section_info", %toc_section_info);
	&save_array_in_file ("index", "sub_index", %sub_index) if %sub_index;
	&save_array_in_file ("index", "index_labels", %index_labels) if %index_labels;
	&save_array_in_file ("index", "index_segment", %index_segment) if %index_segment;
	&save_array_in_file ("index", "printable_key", %printable_key) 
	    if (%printable_key || %index_segment);
    }
    &save_array_in_file ("internals", "ref_files", %ref_files) if $changed;
    &save_array_in_file ("labels", "external_labels", %ref_files);
    &save_array_in_file ("images", "cached_env_img", %cached_env_img);
}

# RRM:
sub translate_preamble {
    local($_) = @_;
    $PREAMBLE = 1;
    $NESTING_LEVEL=0;   #counter for TeX group nesting level
    if (/\\htmlhead/) {
        print STDERR "\nPREAMBLE: discarding...\n$`" if ($VERBOSITY > 4);
        local($after) = $&.$';
	# translate segment preamble preceding  \htmlhead
	&translate_commands(&translate_environments($`));
	# translate \htmlhead  and rest of preamble
	$_=&translate_commands(&translate_environments($after));
        print STDERR "\nPREAMBLE: retaining...\n$_" if ($VERBOSITY > 4);
    } else {
	# translate only preamble here (metacommands etc.)
	# there should be no textual results, if so, discard them
	&translate_commands(&translate_environments($`));
        print STDERR "\nPREAMBLE: discarding...\n$_" if ($VERBOSITY > 4);
	$_="";
    };
    $_ = &do_AtBeginDocument($_);
    if (! $SEGMENT) { $_ = ''} # segmented documents have a heading already
    print STDERR "\nPREAMBLE: $_" if (($_)&&($VERBOSITY > 1));
    $_;
}

############################ Processing Environments ##########################

sub wrap_shorthand_environments {
    # This wraps a dummy environment around environments that do not use
    # the begin-end convention. The wrapper will force them to be
    # evaluated by Latex rather than them being translated.
    # Wrap a dummy environment around matching TMPs.
    # s/^\$\$|([^\\])\$\$/{$1.&next_wrapper('tex2html_double_dollar')}/ge;
    # Wrap a dummy environment around matching $s.
    # s/^\$|([^\\])\$/{$1.&next_wrapper('$')}/ge;
    # s/tex2html_double_dollar/\$\$/go;
    # Do \(s and \[s
    #
    local($wrapper) = "tex2html_wrap_inline";	# \ensuremath wrapper
    print STDERR "\n *** wrapping environments ***\n" if ($VERBOSITY > 3);

    $* = 1;			# Multiline matching ON
    print STDERR "\\(" if ($VERBOSITY > 3);
    s/(^\\[(])|([^\\])(\\[(])/{$2.&make_any_wrapper(1,$wrapper).$1.$3}/geo;
    print STDERR "\\)" if ($VERBOSITY > 3);
    s/(^\\[)]|[^\\]\\[)])/{$1.&make_any_wrapper(0,$wrapper)}/geo;

    print STDERR "\\[" if ($VERBOSITY > 3);
    s/(^\\[[])|([^\\])(\\[[])/{$2.&make_any_wrapper(1,"displaymath")}/geo;
    print STDERR "\\]" if ($VERBOSITY > 3);
    s/(^\\[\]])|([^\\])(\\[\]])/{$2.&make_any_wrapper(0,"displaymath")}/geo;

    print STDERR "\$" if ($VERBOSITY > 3);
    s/$enspair/print "\$";
       {&make_any_wrapper(1,$wrapper).$&.&make_any_wrapper(0,$wrapper)}/geo;
    $* = 0;			# Multiline matching OFF

    $double_dol_rx = '(^|[^\\\\])\\$\\$';
    $single_dol_rx = '(^|[^\\\\])\\$';
    print STDERR "\$" if ($VERBOSITY > 3);

    local($dollars_remain) = 0;
    $_ = &wrap_math_environment;
    $_ = &wrap_raw_arg_cmds;
}

sub wrap_math_environment {

    # This wraps math-type environments
    # The trick here is that the opening brace is the same as the close,
    # but they *can* still nest, in cases like this:
    #
    # $ outer stuff ... \hbox{ ... $ inner stuff $ ... } ... $
    #
    # Note that the inner pair of $'s is nested within a group.  So, to
    # handle these cases correctly, we need to make sure that the outer
    # brace-level is the same as the inner. --- rst
    #tex2html_wrap
    # And yet another problem:  there is a scungy local idiom to do
    # this:  $\_$ for a boldfaced underscore.  xmosaic can't display the
    # resulting itty-bitty bitmap, for some reason; even if it could, it
    # would probably come out as an overbar because of the floating-
    # baseline problem.  So, we have to special case this.  --- rst again.

    local ($processed_text, $before, $end_rx, $delim, $ifclosed);
    local ($underscore_match_rx) = "^\\s*\\\\\\_\\s*\\\$";
    local ($wrapper);
    print STDERR "\nwrap math:" if ($VERBOSITY > 3);

    $dollars_remain = 0;
    while (/$single_dol_rx/) {
        $processed_text .= $`.$1;
        $_ = $';
	$wrapper = "tex2html_wrap_inline";

	$end_rx = $single_dol_rx; # Default, unless we begin with $$.
        $delim = "\$";

        if (/^\$/ && (! $`)) {
	    s/^\$//;
	    $end_rx = $double_dol_rx;
	    $delim = "";	# Cannot say "\$\$" inside displaymath
	    $wrapper = "displaymath";

        } elsif (/$underscore_match_rx/ && (! $`)) {

            # Special case for $\_$ ...

            s/$underscore_match_rx//;
            $processed_text .= '\\_';
            next;
        }

        # Have an opening $ or $$.  Find matching close, at same bracket level
#	$processed_text .= &make_any_wrapper(1,$wrapper).$delim;

	print STDERR "\$" if ($VERBOSITY > 3);
	$ifclosed = 0;
	local($thismath);
        while (/$end_rx/) {
	    # Forget the $$ if we are going to replace it with "displaymath"
            $before = $` . (($wrapper eq "displaymath")? "$1" : $&);
	    last if ($before =~ /\\(sub)*(item|section|chapter|part|paragraph)(star)?\b/);
	    $thismath .= $before;
            $_ = $';
	    s/^( [^\n])/\\space$1/;  #make sure a trailing space doesn't get lost.

            # Found dollar sign inside open subgroup ... now see if it's
            # at the same brace-level ...

            local ($losing, $br_rx) = (0, '');
	    print STDERR "\$" if ($VERBOSITY > 3);
            while ($before =~ /$begin_cmd_rx/) {
                $br_rx = &make_end_cmd_rx($1);  $before = $';

                if ($before =~ /$br_rx/) { $before = $'; }
                else { $losing = 1; last; }
            }
            do { $ifclosed = 1; last } unless $losing;

            # It wasn't ... find the matching close brace farther on; then
            # keep going.

            /$br_rx/;

            $thismath .= $`.$&;

	    #RRM: may now contain unprocessed $s e.g. $\mbox{...$...$...}$
	    # the &do_cmd_mbox uses this specially to force an image
	    # ...but there may be other situations; e.g. \hbox
	    # so set a flag:
	    $dollars_remain = 1;

            $_ = $';
        }

        # Got to the end.  Whew!
	if ($ifclosed) {
	    # also process any nested math
	    while (($dollars_remain)&&($delim eq "\$")) {
		local($saved) = $_;
                $thismath =~ s/\$$//;
                $_ = $thismath;
		$thismath =  &wrap_math_environment;
		$thismath .= "\$";
		$_ = $saved;
	    }
	    $processed_text .= &make_any_wrapper(1,$wrapper) . $delim 
		. $thismath . &make_any_wrapper(0,$wrapper);
	} else {
	    print STDERR "\n\n *** Error: unclosed math or extra `\$', before:\n$thismath\n\n";
	    # remove a $ to try to recover as much as possible.
	    $thismath =~ s/([^\\]\\\\|[^\\])\$/$1\%\%/;
	    $_ = $thismath . $_; $thismath = "";
	}
    }
    $processed_text . $_;
}

sub translate_environments {
    local ($_) = @_;
    local($tmp, $capenv);
#    print "\nTranslating environments ...";
    for (;;) {
	last unless (/$begin_env_rx/o);
	local ($contents, $before, $br_id, $env, $after, $pattern);
	# $1 : br_id (at the beginning)
        # $2 : environment
       	($before, $br_id, $env, $after, $pattern) = ($`, $1, $2, $', $&);
	$contents = undef;
	$capenv = $env =~ /.*(figure|table)/ ? $1 : "";
	# Sets $contents and modifies $after
	if (&find_end_env($env,*contents,*after)) {
	    print STDERR "\nIN {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    &process_command($counters_rx, *before)
		if ($before =~ /$counters_rx/);
	    # This may modify $before and $after
	    &extract_captions($capenv) if $capenv;
	    # Modifies $contents
	    $contents = &translate_environments($contents)
		if (&defined_env($env) && (! $raw_arg_cmds{$env})
		   && (!($env =~ /latexonly|enumerate|figure|table|makeimage|wrap_inline/))
#		   && ((! $NO_SIMPLE_MATH)||(!($env =~ /(math|wrap|equation|eqnarray)/)) ));
		   && ((! $NO_SIMPLE_MATH)||(!($env =~ /wrap/)))
		   && (!($env =~ /(math|wrap|equation|eqnarray|makeimage|minipage|deferred)/) ));
	    &process_environment($env, $br_id); # alters $contents
	    undef $_;
	    print STDERR "\nOUT {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
#	    if ($capenv && $captions) {
#		$after = "<BR>\n$captions<BR>\n$after";
#		$captions = "";
#	    }
	    #JCL(jcl-env) - insert the $O$br_id$C stuff to handle environment grouping
	    if ($contents) {
		$_ = join("", $before, $O,$br_id,$C, $contents, $O,$br_id,$C, $after);
	    } else { $_ = join("", $before, $after) }
	### Evan Welsh <welsh@epcc.ed.ac.uk> added the next 24 lines ##
	} elsif (&defined_env($env)) {
	    print STDERR "\nIN {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    # If I specify a function for the environment then it
	    # calls it with the contents truncated at the next section.
	    # It assumes I know what I'm doing and doesn't give a
	    # deferred warning.
	    &extract_captions($capenv) if $capenv;
	    $contents = $after;
	    $contents = &process_environment($env, $br_id);
	    print STDERR "\nOUT {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    $_ = join("", $before, $contents);
#	    if ($capenv && $captions) {
#		$_ .= "<BR>\n$captions<BR>\n";
#		$captions = "";}
	} elsif ($ignored{$env}) {
	    print STDERR "\nIGNORED {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    # If I specify that the environment should be ignored then
	    # it is but I get a deferred warning.
	    $_ = join("", $before, $contents, $after);
	    &write_warnings("\n\\end{$env} not found (ignored).\n");
	} elsif ($raw_arg_cmds{$env}) {
	    print "\nIN {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    # If I specify that the environment should be passed to tex
	    # then it is with the environment truncated at the next
	    # section and I get a deferred warning.
	    &extract_captions($capenv) if $capenv;
	    $contents = $after;
	    $contents = &process_environment($env, $br_id);
	    print STDERR "\nOUT {$env $br_id}\n$contents\n" if ($VERBOSITY > 4);
	    $_ = join("", $before, $contents);
#	    if ($capenv && $captions) {
#		$_ .= "<BR>\n$captions<BR>\n";
#		$captions = "";
#	    }
	    &write_warnings("\n\\end{$env $br_id} not found (truncated at next section boundary).\n");
	} else {
	    $pattern = &escape_rx_chars($pattern);
	    s/$pattern//;
	    print "\nCannot find \\end{$env $br_id}\n";
	}
    }
    $tmp = $_; undef $_;
    &process_command($counters_rx, *tmp) if ($tmp =~ /$counters_rx/);
    $_ = $tmp; undef $tmp;
    $_
}

sub find_end_env {
    local ($env, *ref_contents, *rest) = @_;
    local ($be_rx) = &make_begin_end_env_rx ($env);
    local ($count) = 1;

    while ($rest =~ /$be_rx/) {
	$ref_contents .= $`;

	if ($1 eq "begin") { ++$count }
	else { --$count };

	$rest = $';
	last if $count == 0;

	$ref_contents .= $&;
    }

    if ($count != 0) {
	$rest = join('', $ref_contents, $rest);
	$ref_contents = "";
	return(0)}
    else {
	return(1)}
}

# MODIFIES $contents
sub process_environment {
    local($env, $id) = @_;
    local($env_sub,$border,$attribs) = ("do_env_$env",'','');
    local($original) = $contents;
    if (&defined_env($env)) {
	print STDERR ",";
	print STDERR "{$env $id}" if ($VERBOSITY > 1);
	$env_sub =~ s/\*$/star/;
	$contents = &$env_sub($contents);
    } elsif ($env =~ /tex2html_nowrap/) {
	#pass it on directly for LaTeX, via images.tex
	$contents = &process_undefined_environment($env, $id, $contents);
	return ($contents);

#    elsif (&special_env) {	# &special_env modifies $contents
    } else { 
	local($no_special_chars) = 0;
	local($failed) = 0;
	local($has_special_chars) = 0;
	&special_env; #  modifies $contents
	print STDERR "\n<MATH $env$id $contents>" if ($VERBOSITY > 3);
	if ($failed || $has_special_chars) {
	    $contents = $original;
	    $failed = 1;
	    print STDERR " !failed!\n" if ($VERBOSITY > 3);
        }
    }
    if (($contents) && ($contents eq $original)) {
        if ($ignore{$env}) {  return(''); }
        # Generate picture
	if ($contents =~ s/$htmlborder_rx//o)
	    { $attribs = $2; $border = (($4)? "$4" : 1) }
	$contents = &process_undefined_environment($env, $id, $contents);
	$env_sub = "post_latex_$env_sub"; # i.e. post_latex_do_env_ENV
        if ( defined &$env_sub) { $contents = &$env_sub($contents) }
	elsif (($border||($attributes))&&($HTML_VERSION > 2.1)) {
	    $contents = &make_table( $border, $attribs, '', '', '', $contents );
	} else {
	    $contents = join('',"<BR>\n",$contents,"\n<BR>")
	        unless (!($contents)||($env =~
	              /^(tex2html_wrap|tex2html_nowrap|\w*math|eq\w*n)/o ));
	}
    }
    $contents;
}

# The $<$, $>$, $|$ and $=>$, etc strings are replaced with their textual
# equivalents instead of passing them on to latex for processing in math-mode.
# This will not be necessary when the mechanism for passing environments
# to Latex is improved.
# RETURNS SUCCESS OR FAILURE
sub special_env {
    # Modifies $contents in its caller
    local($next)='';
    local ($allow) = $HTML_VERSION ge '3.0' ?
	 "[^#\$%&~\\\\{}]|\\limits" : "[^^#\$%&~_\\\\{}]";
    #JKR: Use italics instead of bold #HWS: Generalize to include more symbols.
#    $contents =~ s/^\$(\s*($html_specials_inv_rx|$allow)*\s*)\$(.)?/
#	$next=$3;&simple_math_env($1).(($next =~ m|\w|)? " ":'').$next/ige;
    $contents =~ s/^\$(\s*($html_specials_inv_rx|$allow)*\s*)\$$/
	&simple_math_env($1)." "/ige;
    if ($contents =~ /\&\w*;/) { $has_math_chars=1 }
    if ($contents =~ /;SPM([a-zA-Z]+);/) { $has_special_chars=1 };
}

# Translate simple math environments into italic.
# Only letters should become italic; symbols should stay non-italic.
sub simple_math_env {
    local($mathcontents) = @_;
    if (!($mathcontents)) {
	return();
    } elsif ($NO_SIMPLE_MATH) { 
	$failed = 1;
	return($mathcontents);

    # any macro kills "simple-math"
    } elsif ($mathcontents =~ /\\/) {
	$failed = 1;
	return($mathcontents);
    }
    # Is there a problem here, with nested super/subscripts ?
    $mathcontents =~ s/\^$any_next_pair_rx/<SUP>$2<\/SUP>/go;
    $mathcontents =~ s/_$any_next_pair_rx/<SUB>$2<\/SUB>/go;
    $mathcontents =~ s/\^(\\[a-zA-Z]+|.)/<SUP>$1<\/SUP>/g;
    $mathcontents =~ s/_(\\[a-zA-Z]+|.)/<SUB>$1<\/SUB>/g;
    $mathcontents =~ s/([a-z]([a-z ]*[a-z])?)/<I>$1<\/I>/igo;
    $mathcontents =~ s/;<I>SPM([a-zA-Z]+)<\/I>;/;SPM$1;/go;
    $mathcontents =~ s/<(\/?)<I>(SUB|SUP)<\/I>>/<$1$2>/g;
    $mathcontents =~ s/<\/I><SU(B|P)>([^<]*)<\/SU(B|P)>/<SU$1>$2<\/SU$3><\/I>/g;
    $mathcontents =~ s/<\/I><I>//g;
    $mathcontents;
}


sub process_math_in_latex {
    local($mode,$style,$level,$math) = @_;
    if ($level) { $style = (($level > 1) ? "script" : "") . "script" }
    elsif (! $style) { 
	$style = (($mode =~/display|equation/)? "display" : "") }
    $style = "\\${style}style" if ($style);

    #  &process_undefined_environment  changes $_ , so save it.
    local($after) = $_;
    $mode = "tex2html_wrap_" .
	(($mode =~/display|equation|eqnarray/) ? 'indisplay' : 'inline');
    $global{'max_id'}++;
    $math = &process_undefined_environment( $mode
	,$global{'max_id'}, join('', "\$$style", $math, "\$"));
    $_ = $after;
    # the delimiter \001 inhibits an unwanted \n at image-replacement
    $math . "\001";
}
     


sub defined_env {
    local($env) = @_;
    $env =~ s/\*$/star/;
    local($env_sub) = ("do_env_$env");
    # The test using declarations should not be necessary but 'defined'
    # doesn't seem to recognise subroutines generated dynamically using 'eval'.
    # Remember that each entry in $declarations generates a dynamic prodedure ...
    ((defined &$env_sub) || ($declarations{$env}));
}

# RRM: utility to add style information to stored image-parameters
#      currently only (math) scaling info is included;
#      current color, etc.  could also be added here.
sub addto_encoding {
    local($env, $contents) = @_;
    $contents =~ s/(\\(begin|end))?<<\d*>>|\n//g;	# RRM: remove env delimiters
    # append scaling information for environments using it
    if (($MATH_SCALE_FACTOR)
	&&(($contents =~ /inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/)
	   ||($env =~ /inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/))
        ) { $contents .= "MSF=$MATH_SCALE_FACTOR;" }

    if (($EXTRA_IMAGE_SCALE)
	&&(($contents =~ /inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/)
	   ||($env =~ /inline|indisplay|entity|displaymath|eqnarray|equation|xy|diagram/))
	) { $contents .= "EIS=$EXTRA_IMAGE_SCALE;"; }

    if (($DISP_SCALE_FACTOR)
	&&(($contents =~ /indisplay|displaymath|eqnarray|equation/)
	   ||($env =~ /indisplay|displaymath|eqnarray|equation/))
	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
        ) { $contents .= "DSF=$DISP_SCALE_FACTOR;" }

    if (($EQN_TAGS)
	&&(($env =~ /eqnarray($|[^_\*])|equation/)
	   ||($contents =~ /eqnarray($|[^_\*])|equation/))
	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
	) { $contents .= "TAGS=$EQN_TAGS;" }

    if (($FIGURE_SCALE_FACTOR)
	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
	&&(($contents =~ /figure/)||($env =~ /figure/))
        ) { $contents .= "FSF=$FIGURE_SCALE_FACTOR;"}

    if (($ANTI_ALIAS)
	&&(($contents =~ /figure/)||($env =~ /figure/))
	&&!(($contents =~ /makeimage/)||($env =~ /makeimage/))
	) { $contents .= "AAF;" }
    elsif ($ANTI_ALIAS_TEXT) { $contents .= "AAT;" }

    $contents;
}

sub process_undefined_environment {
    local($env, $id, $contents) = @_;
    local($name,$cached,$raw_contents,$uucontents) = ("$env$id");
    $name =~ s/\*/star/;
    local($oldimg,$size,$fullcontents);
    return if ($AUX_FILE);
    print STDERR "\nIN {$env $id}:\n$contents\n" if ($VERBOSITY > 4);
#RRM - LaTeX commands wrapped with this environment go directly into images.tex.
    if ($env =~ /tex2html_nowrap|^lrbox$/){ # leave off the wrapper, do not cache
	# totally ignore if in preamble...
	# ...since it will be put into  images.tex  anyway!!
	if (!($PREAMBLE)) {
	    local($lcontents) = join('', "\\begin{$env}", $contents , "\\end{$env}" );
	    print STDERR "pre-LATEX {$env}:\n$lcontents\n" if ($VERBOSITY > 3);
	    $raw_contents = &revert_to_raw_tex($lcontents);
	    print STDERR "LATEX {$env}:\n$raw_contents\n" if ($VERBOSITY > 3);
	    $latex_body .= "\n$raw_contents"."%\n\n";
	}
	return("") if ($env =~ /^lrbox/);
	# ignore enclosed environments; e.g. in  \settolength  commands
#	$contents = &translate_environments($contents); # ignore environments
#	$contents = &translate_commands($contents);
	# ...but apply any Perl settings that may be defined
	$contents = &process_command($single_cmd_rx,*contents);
	print STDERR "\nOUT {$env $id}:\n$contents\n" if ($VERBOSITY > 4);
	return("");
    }
    $fullcontents =  $contents; # save for later \label search.
    $* = 1;			# Multiline matching ON
    $contents =~ s/\n?$labels_rx(\%$EOL)?/\n/g;
    $* = 0;			# Multiline matching OFF
    $contents = "% latex2html id marker $id\n$contents" if
	(($contents =~ /$order_sensitive_rx/)&&(!($env =~ /makeimage/)));
    $contents = "\\begin{$env}$contents\\end{$env}";
    if (!($latex_body{$name} = $contents)) {
	print "\n *** code for $name is too long ***\n"}
    if ($contents =~ /$htmlimage_rx/) {
	$uucontents = &special_encoding($env,$2,$contents);
    } else {
	$uucontents = &encode(&addto_encoding($env,$contents));
    }
    $cached = $cached_env_img{$uucontents};
    print STDERR "\nCACHED $uucontents:\n$cached\n" if ($VERBOSITY > 4);
    if ($NOLATEX) { $id_map{$name} = "[$name]"; }
    elsif (defined ($_ = $cached)) {		# Is it in our cache?
	if (($oldimg) = /SRC="$PREFIX$img_rx\.$IMAGE_TYPE"/o) {# Have we already used it?
	    $size = &get_image_size("$PREFIX$oldimg.old");	# No, check its size
	    if ($uucontents =~ /EIS=(.*);/) {	# if this has extra scaling ?
		local($eis) = $1; local($w,$h);
		s/(WIDTH=\")(\d*)(\".*HEIGHT=\")(\d*)\"/
		    $w = int($2 * $eis); $h=int($4 * $eis);
		    "$1$w$3$h\""/e ;		# insert the actual size
	    }
	    if ($size && /\s$size\s/) {			# Size is OK; recycle it!
		++$global_page_num;
		$_ = $cached ;    # ...perhaps restoring the desired size.
		s/(${PREFIX}T?img)\d+\.($IMAGE_TYPE|html)/
			&rename_html($&,"$1$global_page_num.$2")/geo;
	    } else {
		if ($env =~ /equation/) { &extract_eqno($name,$cached) }
	        $_ = "";				# The old Image has wrong size!
	        undef($cached);				#  (or it doesn't exist)
	    }
        }
        s/$PREFIX$img_rx\.new/$PREFIX$1.$IMAGE_TYPE/go; # Point to the actual image file(s)
        $id_map{$name} = $_;
        s/$PREFIX$img_rx\.$IMAGE_TYPE/$PREFIX$1.new/go;	# But remember them as used.
        $cached_env_img{$uucontents} = $_;
    }

    if (! defined($cached)) {				# Must generate it anew.
	&clear_images_dbm_database unless $new_page_num;
	$new_id_map{$name} = $id_map{$name} = ++$global_page_num . "#" .
	    ++$new_page_num;
	$orig_name_map{$id_map{$name}} = $name;
	$cached_env_img{$uucontents} = $id_map{$name} if ($REUSE == 2);

	#RRM: this (old) code frequently crashes NDBM, so do it in 2 steps
#	$img_params{$name} = join('#', &extract_parameters($contents));
	local(@params) = &extract_parameters($contents);
	$img_params{$name} = join('#',@params); undef $params;
	print "\nIMAGE_PARAMS $name: ".$img_params{$name} if ($VERBOSITY > 3);

	print STDERR "\nLATEX {$env}:\n$contents" if ($VERBOSITY > 3);
	$raw_contents = &revert_to_raw_tex($contents) unless ($contents =~ /^\s*$/) ;
        $raw_contents =~ s/\\pagebreak|\\newpage|\\clearpage/\\\\/go;
	print STDERR "\nLATEX {$env}:\n$raw_contents\n" if ($VERBOSITY > 3);
	local($box_type) = '';
	if ($raw_contents =~ /\\special\s*\{/) { 
	    $tex_specials{$name} = "1";
            &write_warnings("\nenvironment $name contains \\special commands");
            print STDERR "\n *** environment $name contains \\special commands ***\n"
		if ($VERBOSITY);
	} elsif ($env =~ /inline|indisplay|entity|xy|diagram/) {
	    $crop{$name} = "bls";
	    $box_type = "h";
	} elsif ($env =~ /eqnarray(\*|star)/) {
	    $crop{$name} = "blrl";
	    $box_type = "v";
	} elsif ($env =~ /displaymath|eqnarray|equation|makeimage/) {
	    $crop{$name} = "blrl" ;
#	    if (($env =~ /equation/)&&($eqno{$name})) {
	    if ($env =~ /equation|eqnarray\d/) {
		$raw_contents = join(''
#		    , &set_equation_counter($eqno{$name})
		    , &set_equation_counter , $raw_contents);
	        $crop{$name} = "bl" ;
	    }
	    $box_type = "v";
	}
	
	#RRM: include the TeX-code for the appropriate type of box.
        eval "\$raw_contents = \&make_$box_type"."box($name, \$raw_contents);";

	# JCL(jcl-pag) - remember html text if debug is set.
	local($_);
	if ($DEBUG) {
	    $_ = $contents;
	    s/\n/ /g;
	    $_ = join('',substr($_,0,200),"...") if (length($_) > 200);
	    $_ = join('',"% contents=",$_,"\n");
	}
# JCL(jcl-pag) - build the page entries for images.tex:  Each page is embraced to
# let most statements have only local effect. Each page must compile into a
# single dvi page to get proper image translation. Hence the invisible glue to
# get *at least* one page (raw_contents alone might not wield glue), and
# sufficing page length to get *exactly* one page.
#
	$latex_body .= "{\\newpage\\clearpage\n$_" .
#	    "$raw_contents\\hfill\\vglue1pt\\vfill}\n\n";
#	    "$raw_contents\\hfill\\vss}\n\n" if ($raw_contents);
	    "$raw_contents\\hfill\\lthtmlcheckvsize\\clearpage}\n\n" if ($raw_contents);
    }

    print STDERR "\nIMAGE_CODE:{$env $id}:\n$raw_contents\n" if ($VERBOSITY > 4);
    print STDERR "\nOUT {$env $id}:\n$contents\n" if ($VERBOSITY > 4);

# Anchor the labels and put a marker in the text;
    &do_labels($fullcontents,"$image_mark#$name#");
}

sub special_encoding { # locally sets $EXTRA_IMAGE_SCALE
    local($env,$_,$contents) = @_; 
    local($exscale) = /extrascale=([\.\d]*)/;
    local($EXTRA_IMAGE_SCALE) = $exscale if ($exscale);
    &encode(&addto_encoding($env,$contents));
}


sub extract_eqno{
    local($name,$contents) = @_;
    if ($contents =~ /<P ALIGN="\w+">\(([^<>])\)<\/P>$/) {
	if (($eqno{$name})&&!($eqno{$name} eq $1)) {
	    &write_warnings("\nequation number for $name may be wrong.")};
	$eqno{$name}="$1";
    }
}
sub set_equation_counter{
    if ( $global{'eqn_number'}) {
	"\\setcounter{equation}{". $global{'eqn_number'} ."}\n"
    } else { "\\setcounter{equation}{0}\n" }
}

# RRM: 3 different types of boxing, for image environments.

#	general environments --- crops to width & height
sub make_box {
    local($id,$contents) = @_;
    "\\lthtmlfigureA{". $id ."}%\n". $contents ."%\n\\lthtmlfigureZ\n";
}

#	inline math --- horizontal mode, captures height/depth + \mathsurround
sub make_hbox {
    local($id,$contents) = @_;
    "\\lthtmlinlinemathA{". $id ."}%\n". $contents ."%\n\\lthtmlinlinemathZ\n";
}

#	displayed math --- vertical mode, captures height/depth + page-width
sub make_vbox {
    local($id,$contents) = @_;
    if (($HTML_VERSION >=3.2)&&($id =~/equation|eqnarray\d/)) {
	if ($contents =~ s/^\\setcounter\{equation\}\{\d+\}/$&%\n\\lthtmldisplayB\{$id\}%/)
	    { $contents ."%\n\\lthtmldisplayZ\n" }
	else { "\\lthtmldisplayB{$id}%\n". $contents ."%\n\\lthtmldisplayZ\n" }
    } else { "\\lthtmldisplayA{$id}%\n". $contents ."%\n\\lthtmldisplayZ\n"}
}

sub make_image_file {
    do {
	print "\nWriting image file ...\n";
	open(ENV,">./${PREFIX}images.tex") || die "Cannot open ${PREFIX}images.tex $!\n";
	print ENV &make_latex($latex_body);
	print ENV "\n";
	close ENV;
	&copy_file($FILE, "bbl");
	&copy_file($FILE, "aux");
    } if ((%latex_body) && ($latex_body =~ /newpage/));
}

sub make_off_line_images {
    local($name, $page_num);
    do {
	print "$LATEX ./${PREFIX}images.tex\n" if (($DEBUG)||($VERBOSITY > 1));
	&syswait("$LATEX ./${PREFIX}images.tex");
	print "\nGenerating postscript images using dvips ...\n";
	&process_log_file("./${PREFIX}images.log"); # Get eqn size info
	do {
	    print "$DVIPS -S 1 -i $DVIPSOPT -o $$\_image ./${PREFIX}images.dvi\n"
		if (($DEBUG)||($VERBOSITY > 1));
	    &syswait("$DVIPS -S 1 -i $DVIPSOPT -o $$\_image ./${PREFIX}images.dvi") &&
	        print "Error: $!\n";
	    open(IMAGE, "echo $$\_image* | tr -s ' \t\r\f' '\\012\\012\\012\\012'|");
	    while (<IMAGE>) {chop; rename($_, "$_.ps") if /\d\d\d$/};
	} unless ($LaTeXERROR);
    } if  ((!$NOLATEX) && (-f "./${PREFIX}images.tex"));
    do {print "\n\n*** LaTeXERROR"; return()} if ($LaTeXERROR);

    while ( ($name, $page_num) = each %new_id_map) {
	# Extract the page, convert and save it
	&extract_image($page_num,$orig_name_map{$page_num});
    }
}

# Generate images for unknown environments, equations etc, and replace
# the markers in the main text with them.
# - $cached_env_img maps encoded contents to image URL's
# - $id_map maps $env$id to page numbers in the generated latex file and after
# the images are generated, maps page numbers to image URL's
# - $page_map maps page_numbers to image URL's (temporary map);
# Uses global variables $id_map and $cached_env_img,
# $new_page_num and $latex_body

sub make_images {
    local($name, $contents, $raw_contents, $uucontents, $page_num,
	  $uucontents, %page_map, $img);
    # It is necessary to run LaTeX this early because we need the log file
    # which contains information used to determine equation alignment
    if ( $latex_body =~ /newpage/) {
	print "\n";
	do {
	    print "$LATEX ./${PREFIX}images.tex\n" if (($DEBUG)||($VERBOSITY > 1));
	    &syswait("$LATEX ./${PREFIX}images.tex");
	}; # unless ((-e "./${PREFIX}images.log") && ($NO_IMAGES));
	$LaTeXERROR = 0;
	&process_log_file("./${PREFIX}images.log"); # Get image size info
    }
    if ($NO_IMAGES) {
	&syswait("cp $LATEX2HTMLDIR/icons.$IMAGE_TYPE/image.$IMAGE_TYPE .")
	    if (-e "$LATEX2HTMLDIR/icons.$IMAGE_TYPE/image.$IMAGE_TYPE")
		&& !(-e "image.$IMAGE_TYPE");
    }
    elsif ((!$NOLATEX) && ($latex_body =~ /newpage/) && !($LaTeXERROR)) {
   	print "\nGenerating postscript images using dvips ...\n";
	print "$DVIPS -S 1 -i $DVIPSOPT -o $$\_image ./${PREFIX}images.dvi\n"
	    if (($DEBUG)||($VERBOSITY > 1));
	&syswait("$DVIPS -S 1 -i $DVIPSOPT -o $$\_image ./${PREFIX}images.dvi")
	    && print "Error: $!\n";
	open(IMAGE, "echo $$\_image* | tr -s ' \t\r\f' '\\012\\012\\012\\012'|");
	while (<IMAGE>) {chop; rename($_, "$_.ps") if /\d\d\d$/};
    }
    do {print "\n\n*** LaTeXERROR"; return()} if ($LaTeXERROR);
    return() if ($LaTeXERROR); # empty .dvi file

    print "\n *** updating image cache\n" if ($VERBOSITY > 1);
    while ( ($uucontents, $_) = each %cached_env_img) {
	delete $cached_env_img{$uucontents} if (/$PREFIX$img_rx\.$IMAGE_TYPE/o);
	$cached_env_img{$uucontents} = $_ if (s/$PREFIX$img_rx\.new/$PREFIX$1.$IMAGE_TYPE/go);
    }
    print "\n *** removing unnecessary images ***\n" if ($VERBOSITY > 1);
    while ( ($name, $page_num) = each %id_map) {
	$contents = $latex_body{$name};
	if ($page_num =~ /^\d+\#\d+$/) { # If it is a page number
	    do {		# Extract the page, convert and save it
		$img = &extract_image($page_num,$orig_name_map{$page_num});
		if ($contents =~ /$htmlimage_rx/) {
		    $uucontents = &special_encoding($env,$2,$contents);
		} else {
		    $uucontents = &encode(&addto_encoding($contents,$contents));
		}
		if (($HTML_VERSION >=3.2)||!($contents=~/$order_sensitive_rx/)){
		    $cached_env_img{$uucontents} = $img;
		} else {
                    # Blow it away so it is not saved for next time
		    delete $cached_env_img{$uucontents};
		}
		$page_map{$page_num} = $img;
	    } unless ($img = $page_map{$page_num}); # unless we've just done it
	    $id_map{$name} = $img;
	}
	else {
	    $img = $page_num;	# it is already available from previous runs
	}
#	print " *** image done ***\n" if ($VERBOSITY > 2);
    }
    &write_warnings(
		    "\nOne of the images is more than one page long.\n".
		    "This may cause the rest of the images to get out of sync.\n\n")
	if (-f sprintf("%s%.3d%s", "$$\_image", ++$new_page_num, ".ps"));
    print "\n *** no more images ***\n" if ($VERBOSITY > 1);
    &cleanup;
}

# MRO: This copies the navigation icons from the distribution directory
# to the document directory.

sub copy_icons {
    local($icon,$_);
    print "\nCopying navigation icons ...";
    foreach (keys %used_icons) {
	# each entry ends in gif or png
        if (/(gif|png)$/) {
            &syswait("cp $LATEX2HTMLDIR/icons.$1/$_ .")
	            if (-e "$LATEX2HTMLDIR/icons.$1/$_") && !(-e "$_");
            }
        }
    }

sub process_log_file {
    local($logfile) = @_;
    local($name,$before,$lengthsfound);
    local($TeXpt)= 72/72.27;
    open(LOG, "$logfile") || die "Cannot find logfile $logfile";
    while (<LOG>) {
        if (/Overfull/) { $before .= $_ }
        elsif (/latex2htmlLength ([a-zA-Z]+)=(\-?[\d\.]+)pt/) {
	    ${$1} = 0.0+$2; $lengthsfound = 1;
	} elsif (/latex2htmlSize|l2hSize/) {
	    /:([^:]*):/;
	    $name = $1; $name =~ s/\*//g;
	    s/:([0-9.]*)pt/$height{$name} = $1*$TeXpt;''/e;
	    s/::([0-9.]*)pt/$depth{$name} = $1*$TeXpt;''/e;
	    s/::([0-9.]*)pt/$width{$name} = $1*$TeXpt;''/e;
	    s/\((.*)\)/$eqno{$name} = 1+$1;''/e;
	    if ($before) {
		local($tmp);
		if ($before =~ /hbox\s*\((\d+\.?\d*)pt/) {
		    $width{$name} = $width{$name}+$1*$TeXpt;
		}
		if ($before =~ /vbox\s*\((\d+\.?\d*)pt/) {
		    $height{$name} = $height{$name}+$1*$TeXpt;
		}
	        $before = '';
	    }
	}
    $LaTeXERROR = 1 if (/^No pages of output./);
    }

    if ($LaTeXERROR) {
	print STDERR "\n\n *** LaTeX produced no output ***\n"
	    . " *** no new images can be created\n"
	    . " *** Examine the  images.log  file.\n\n";
	return;
    }
    print STDERR "\n *** LATEX LOG OK. ***\n" if ($VERBOSITY > 1);

    if ($lengthsfound) {
	$ODD_HMARGIN  = $hoffset + $oddsidemargin;
	$EVEN_HMARGIN = $hoffset + $evensidemargin;
	$VMARGIN = $voffset + $topmargin + $headheight + $headsep;
        if ($dvi_mag >0 && $dvi_mag != 1000) {
	    $ODD_HMARGIN = int($dvi_mag /1000 * $ODD_HMARGIN);
	    $EVEN_HMARGIN = int($dvi_mag /1000 * $EVEN_HMARGIN);
	    $VMARGIN = int($dvi_mag /1000 * $VMARGIN);
	}
    } else {
	$ODD_HMARGIN = 0;
	$EVEN_HMARGIN = 0;
	$VMARGIN = 0;
    }
    $ODD_HMARGIN  = int($ODD_HMARGIN*$TeXpt  + 72.5);
    $EVEN_HMARGIN = int($EVEN_HMARGIN*$TeXpt + 72.5);
    $VMARGIN = int($VMARGIN*$TeXpt + 72.5);
    close(LOG);
}

# Uses $img_params
sub extract_image {
    local($page_num,$name) = @_;
    local($scale, $external, $thumbnail, $map, $psimage, $align, $usemap,
	  $flip, $aalias, $trans, $alt, $global_num, $new_num, $lwidth, $val);
    local ($custom_size,$color_depth,$height,$width,$exscale,$exstr);

    print "\nextracting $name as $page_num" if ($VERBOSITY > 2);
    # $global_num identifies this image in the original source file
    # $new_num identifies this image in images.tex
    ($global_num, $new_num) = split("#", $page_num);
    $name =~ s/\*/star/;
    local($env,$basename,$img) = ($name,"img$global_num");
    $env =~ s/\d+$//;
    $psname = sprintf("%s%.3d", "$$\_image", $new_num);
    if ( $EXTERNAL_IMAGES && $PS_IMAGES ) {
	$img =  "$basename.ps";
	&syswait("cp $psname.ps ${PREFIX}$img")
    } else {
	$img =  "$basename.$IMAGE_TYPE";
	($scale, $external, $thumbnail, $map, $psimage, $align, $usemap, 
	 $flip, $aalias, $trans, $exscale, $alt, $exstr) = split('#', $img_params{$name});
	$lwidth = ($align =~ s/nojustify/middle/) ? 0 : $LINE_WIDTH;
	$alt = "ALT=\"$name\"" unless $alt;
	local($EXTRA_IMAGE_SCALE) = $EXTRA_IMAGE_SCALE;
	$EXTRA_IMAGE_SCALE = $exscale if ($exscale);
	if ($NO_IMAGES) {
	    symlink("image.$IMAGE_TYPE", "${PREFIX}$img");
	    if ($thumbnail) {
		symlink("image.$IMAGE_TYPE", "${PREFIX}T$img");
		$thumbnail = "${PREFIX}T$img";
	    }
	} else {
           # RRM: deal with size data
 	    if ($width{$name} < 0) {
		if (($EXTRA_IMAGE_SCALE)&&($PK_GENERATION)) {
	    	    $height = int(				
			$EXTRA_IMAGE_SCALE*$height{$name}+	
			$EXTRA_IMAGE_SCALE*$depth{$name} +.5);
		    $width = int($EXTRA_IMAGE_SCALE*$width{$name}-.5);
		} else {
	    	    $height = int($height{$name}+$depth{$name}+.5);
		    $width = int($width{$name}-.5);
		}
		$custom_size = "${width}x$height";
		$custom_size .= " -crop $crop{$name}"
		    if ($crop{$name});
	    } elsif ($width{$name}) {
                if (($EXTRA_IMAGE_SCALE)&&($PK_GENERATION)) {
                    $height = int( $height{$name} * $EXTRA_IMAGE_SCALE +
			$depth{$name} * $EXTRA_IMAGE_SCALE +.5);
                    $width = int($width{$name} * $EXTRA_IMAGE_SCALE +.5);
                } else {
                    $height = int($height{$name}+$depth{$name}+.5);
		    $width = int($width{$name}+.5);
                }
		$custom_size = "${width}x$height";
		$custom_size .= " -crop $crop{$name}"
		    if ($crop{$name});
	    } else { $custom_size = '' }
	    $page_num  =~ s/^\d+#//o;
	    $custom_size .= " -margins " .
	        (($page_num % 2) ? $ODD_HMARGIN:$EVEN_HMARGIN) .
	            ",$VMARGIN" if ($custom_size);

	    #RRM: \special commands may place ink outside the expected bounds:
	    $custom_size = '' if ($tex_specials{$name});

            # MRO: Patches for image conversion with pstoimg
            # RRM: ...with modifications and fixes
            unlink "${PREFIX}$img";
            if ( ($name =~ /figure/) || $psimage || $scale || $thumbnail) {
                $scale = $FIGURE_SCALE_FACTOR unless ($scale);
	        print STDERR "\n\nFIGURE: $name scaled $scale\n" if ($VERBOSITY);
                &syswait( "$PSTOIMG -$IMAGE_TYPE "
                 . (($DEBUG) ? "-debug " : '' )
                 . (($DISCARD_PS && !$thumbnail && !$psimage)? "-discard " :'')
                 . (($INTERLACE) ? "-interlace " : '' )
                 . (((($ANTI_ALIAS)||($aalias))&&(!($aalias =~ /no/)))? "-antialias ":'')
		 . (($custom_size) ? "-geometry $custom_size ": '' )
		 . $color_depth
                 . (($flip) ? "-flip $flip " : '' )
                 . (($scale > 0) ? "-scale $scale " : '' )
                 . (((($TRANSPARENT_FIGURES && ($env =~ /figure/o))||($trans))
		     &&(!($trans =~ /no/))) ? "-transparent " : '')
                 . "-out ${PREFIX}$img $psname.ps"
                ) && print "Error while converting image: $!\n";

                if ($thumbnail) { # $thumbnail contains the reduction factor
                    unlink "${PREFIX}T$img";
		    print STDERR "\n\nIMAGE thumbnail: $name\n" if ($VERBOSITY);
                    &syswait( "$PSTOIMG -$IMAGE_TYPE "
                     . (($DEBUG) ? "-debug " : '' )
		     . (($DISCARD_PS && !$psimage) ? "-discard " : '' )
                     . (($INTERLACE) ? "-interlace " : '' )
		     . ((($ANTI_ALIAS||($aalias))&&(!($aalias =~/no/)))? "-antialias " :'')
		     . (($custom_size) ? "-geometry $custom_size " : '' )
		     . $color_depth
                     . (($flip) ? "-flip $flip " : '' )
                     . (($thumbnail > 0) ? "-scale $thumbnail " : '' )
		     . ((($trans)&&(!($trans =~ /no/))) ? "-transparent " : '')
                     . "-out ${PREFIX}T$img $psname.ps"
                    ) && print "Error while converting thumbnail: $!\n";
                    $thumbnail = "${PREFIX}T$img";
                }
            } elsif ((($EXTRA_IMAGE_SCALE)&&(!$PK_GENERATION))&&($width{$name})) {
		local($under)='';
		local($mathscale) = (($MATH_SCALE_FACTOR > 0) ? $MATH_SCALE_FACTOR : 1);
                if (($DISP_SCALE_FACTOR > 0) &&
		    ( $name =~ /equation|eqnarray|display/))
		        { $mathscale *= $DISP_SCALE_FACTOR; };
		if ($scale) {
		    $scale *= $EXTRA_IMAGE_SCALE if ($name =~ /makeimage|tab/);
		} else {
		    $scale = $mathscale*$EXTRA_IMAGE_SCALE;
		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
		}
		print STDERR "\n\nIMAGE: $name  scaled by $scale \n" if ($VERBOSITY);
                &syswait( "$PSTOIMG -$IMAGE_TYPE "
                 . (($DEBUG)? "-debug " : '' )
                 . (($DISCARD_PS)? "-discard " : '' )
                 . (($INTERLACE)? "-interlace " : '' )
                 . ((($ANTI_ALIAS_TEXT||($aalias))&&(!($aalias =~/no/)))? 
		    "-antialias -depth 1 " :'')
		 . (($custom_size)? "-geometry $custom_size " : '' )
                 . (($scale != 1)? "-scale $scale " : '' )
                 . (($EXTRA_IMAGE_SCALE != 1)? 
		    "-shoreup $EXTRA_IMAGE_SCALE$under " :'')
                 . ((($TRANSPARENT_FIGURES ||($trans))
		     &&(!($trans =~ /no/)))? "-transparent " : '')
                 . "-out ${PREFIX}$img $psname.ps"
		) && print "Error while converting image: $!\n";
            } else {
		print STDERR "\nIMAGE: $name\n" if ($VERBOSITY > 2);
		local($under)='';
		local($mathscale) = (($MATH_SCALE_FACTOR > 0) ? $MATH_SCALE_FACTOR : 1);
                if (($DISP_SCALE_FACTOR > 0) &&
		    ( $name =~ /equation|eqnarray|display/))
		        { $mathscale *= $DISP_SCALE_FACTOR; };
		if (($scale)&&($EXTRA_IMAGE_SCALE)) {
		    $scale *= $EXTRA_IMAGE_SCALE if ($name =~ /makeimage|tab/);
		} elsif ($scale) {
		} elsif (($mathscale)&&($EXTRA_IMAGE_SCALE)) {
		    $scale = $mathscale*$EXTRA_IMAGE_SCALE;
		    $under = "d" if (($name =~/inline|indisplay/)&&($depth{$name}));
		} elsif ($mathscale) { $scale = $mathscale; }

                &syswait( "$PSTOIMG -$IMAGE_TYPE "
                 . (($DEBUG) ? "-debug " : '' )
                 . (($DISCARD_PS) ? "-discard " : '' )
                 . (($INTERLACE) ? "-interlace " : '' )
                 . ((($ANTI_ALIAS_TEXT||($aalias))&&(!($aalias =~ /no/)))?
		    "-antialias -depth 1 " :'')
                 . ((($EXTRA_IMAGE_SCALE)&&($EXTRA_IMAGE_SCALE!= 1))? 
		        "-shoreup $EXTRA_IMAGE_SCALE " :'')
                 . (($scale ne 1) ? "-scale $scale " : '' )
		 . (($custom_size) ? "-geometry $custom_size " : '' )
#                .  (($name =~ /(equation|eqnarray)/) ? "-rightjustify $lwidth " : '')
#                .  (($name =~ /displaymath/) ? "-center $lwidth " : '')
                 . (($name =~ /inline|indisplay/ && (!($custom_size))&&$depth{$name}!= 0) ?
                   do {$val=($height{$name}-$depth{$name})/($height{$name}+$depth{$name});
                   "-topjustify x$val "} : '')
#                 . (((($TRANSPARENT_FIGURES && !($env =~ /makeimage/))||($trans))
                 . ((($TRANSPARENT_FIGURES||($trans))
		     &&(!($trans =~ /no/))) ? "-transparent " : '')
                 . "-out ${PREFIX}$img $psname.ps")
                 && print "Error while converting image: $!\n";
	    }
	    &write_warnings("\nFailed to convert image $psname.ps")
		if ! -r "${PREFIX}$img"
        }
    }
    &embed_image("${PREFIX}$img", $name, $external, $alt, $thumbnail, $map,
        $align, $usemap, $exscale, $exstr);
}

sub extract_parameters {
    local($contents) = @_;
    local($_, $scale, $external, $thumbnail, $map, $psimage, $align,
	  $usemap, $flip, $aalias, $trans, $pagecolor, $alt, $exscale,
	  $htmlparams);

    #remove the \htmlimage commands and arguments before...
    $contents =~ s/$htmlimage_rx/$_ = $2;''/ego;

    #...catching all the code for the ALT text.
    $alt = &flatten_math($contents);
    #RRM: too long strings upset the DBM. Truncate to <= 165 chars.
    if ( length($alt) > 163 ) {
	local($start,$end);
	$start = substr($alt,0,80);
	$end = substr($alt,length($alt)-80,80);
	$alt = join('',$start,"...\n ...",$end);
    }
    s/ALT\s*=\"([\w\W]*)\"/$alt=$1;''/ie;
    if ($alt) {
	if ($alt =~ /\#/) { $alt = $` . " ... " };
	$alt = "ALT=\"$alt\"" if ($alt);
    }
    $psimage++ if ($contents =~ /\.ps/);
#    $contents =~ s/\s//g;	# Remove spaces   Why ?
    s/extrascale=([\.\d]*)/$exscale=$1;''/ie;
    s/\bscale=([\.\d]*)/$scale=$1;''/ie;
    s/(^|,\s*)external/$external=$1;''/ie;
    s/(^|,\s*)((no)?_?anti)alias/$aalias = $2;''/ie;
    s/(^|,\s*)((no)?_?trans)parent/$trans = $2;''/ie;
    s/thumbnail=([\.\d]*)/$thumbnail=$1;''/ie;
    s/usemap=([^\s,]+)/$usemap=$1;''/ie;
    s/map=([^\s,]+)/;$map=$1;''/ie;
    s/align=([^\s,]+)/$align=$1;''/ie;
    s/flip=([^\s,]+)/$flip=$1;''/ie;
    ($scale,$external,$thumbnail,$map,$psimage,$align
     ,$usemap,$flip,$aalias,$trans,$exscale,$alt,$_);
}


# RRM: Put the raw \TeX code into the ALT tag
# replacing artificial environments and awkward characters
sub flatten_math {
    local ($_) = @_;
    $_ = &revert_to_raw_tex($_);
    s/[ \t]+/ /g;
    $* = 1;			# Multiline matching ON  
    s/$tex2html_wrap_rx//g;
    s/(\\begin\{[^\}]*\})(\s*(\[[^]]*\]))?[ \t]*(\n)?/$1$3\n/g;
    s/\n?(\\end\{[^\}]*\})\n?/$1/g;
    $* = 0;			# Multiline matching OFF
    s/>(\w)?/($1)?"\\gt $1":"\\gt"/eg;		# replace > by \gt
    s/\\\|(\w)?/($1)?"\\Vert $1":"\\Vert"/eg; 	# replace \| by \Vert
    s/\|(\w)?/($1)?"\\vert $1":"\\vert"/eg; 	# replace | by \vert
    s/\\\\/\\\\ /g; 	# insert space after \\ 
    s/\\"/\\uml /g;	# screen umlaut accents...
    s/"/\'\'/g;		# replace " by ''
    s/\\\#/\\char93 /g;	# replace \# by \char93 else caching fails
#    s/"(\w)?/($1)?"\\rq\\rq $1":"\'\'"/eg;	# replace " by \rq\rq
#    s/\&\\uml /\\\"/g;	# ...reinstate umlauts
    $_;
}

sub scaled_image_size {
    local($exscale,$_) = @_;
    local($width,$height) = ('','');
    /WIDTH=\"?(\d*)\"?\s*HEIGHT=\"?(\d*)\"?$/o;
    $width=$1/$exscale;
    $height=$2/$exscale;
    "WIDTH=\"$width\" HEIGHT=\"$height\""
}

sub process_in_latex {
    # This is just a wrapper for process_undefined_environment.
    # @[0] = contents
    $global{'max_id'}++;
    &process_undefined_environment('tex2html_wrap',$global{'max_id'},$_[0]);
}

sub cp {			# Marcus Hennecke  6/3/96
    local($src, $dest) = @_;
    return unless (-f $src);
    if ( -d $dest ) {
        $src =~ /[^$dd]*$/;
        $dest .= $dd . $&;
        $dest =~ s|$dd$dd||g;
    }
    open(IN, $src) || return;
    open(OUT, ">$dest") || return;
    local($/) = undef;
    print OUT <IN>;
    close(OUT);
    close(IN);
}

sub copy_file {			# Marcus Hennecke  6/3/96
    local($file, $ext) = @_;
    $file =  &fulltexpath("$FILE.$ext");
    &cp($file, "./${PREFIX}images.$ext");
}

sub rename_image_files {
    local($_, $old_name, $prefix);
    if ($PREFIX) {
	foreach (<${PREFIX}*img*.$IMAGE_TYPE>) {
	    $old_name = $_;
	    s/\.$IMAGE_TYPE$/\.old/o;
	    rename($old_name, $_);
	    }
	}
    else {
	foreach (<img*.$IMAGE_TYPE>) {
	    $old_name = $_;
	    s/\.$IMAGE_TYPE$/\.old/o;
	    rename($old_name, $_);
	}
	foreach (<Timg*.$IMAGE_TYPE>) {
	    $old_name = $_;
	    s/\.$IMAGE_TYPE$/\.old/o;
	    rename($old_name, $_);
	}
    }
}


############################ Processing Commands ##########################

sub translate_commands {
    local ($_) = @_;
    #print "\nTranslating commands ...";

    &replace_strange_accents;
    for (;;) {			# For each opening bracket ...
	last unless (/$begin_cmd_rx/o);
        local($before, $contents, $br_id, $after, $pattern);
        ($before, $br_id, $after, $pattern) = ($`, $1, $', $&);
	local($end_cmd_rx) = &make_end_cmd_rx($br_id);
        if ($after =~ /$end_cmd_rx/) { # ... find the the matching closing one
	    $NESTING_LEVEL++;
            ($contents, $after) = ($`, $');
	    print STDERR "\nIN:{$br_id}" if ($VERBOSITY > 5);
	    print STDERR "\n:$contents\n" if ($VERBOSITY > 7);
	    undef $_;
	    $contents = &translate_commands($contents)
                if ($contents =~ /$match_br_rx/o);
	    # Modifies $contents
	    &process_command($single_cmd_rx,*contents)
		if ($contents =~ /\\/o);
	    print STDERR "\nOUT: {$br_id}" if ($VERBOSITY > 5);
	    print STDERR "\n:$contents\n" if ($VERBOSITY > 7);
	    # THIS MARKS THE OPEN-CLOSE DELIMITERS AS PROCESSED
	    $_ = join("", $before,"$OP$br_id$CP", $contents,"$OP$br_id$CP", $after);
	    $NESTING_LEVEL--;
	}
	else {
	    $pattern = &escape_rx_chars($pattern);
	    s/$pattern//;
	    print "\nCannot find matching bracket for $br_id";
	}
    }
    # Now do any top level commands that are not inside any brackets
    # MODIFIES $_
    &process_command($single_cmd_rx,*_);
}

# Modifies $contents
sub process_command {
    local ($cmd_rx, *ref_contents) = @_;
    local($ref_before, $cmd, $after);
    local($cmd_sub, $cmd_msub, $cmd_trans, $mathentity);
    local (@open_font_tags,@open_size_tags);
    $ref_contents = &convert_iso_latin_chars($ref_contents)
	unless ($cmd =~ /(Make)?([Uu]pp|[Ll]ow)ercase/);
    for (;;) {			# Do NOT use the o option
	last unless ($ref_contents =~ /$cmd_rx/ );
	print ".";
	#JCL(jcl-del) - use new regexp form which handles white space
	($ref_before, $cmd, $after) = ($`, $1.$2, $4.$');
#print $4 if ($cmd_rx eq $single_cmd_rx);
	print STDERR "$cmd" if ($VERBOSITY > 2);
	print STDERR "\nIN: $ref_contents\n" if ($VERBOSITY > 6);
	#
	if ( $cmd = &normalize($cmd) ) {
	    ($cmd_sub, $cmd_msub, $cmd_trans, $mathentity) =
		("do_cmd_$cmd", "do_math_cmd_$cmd",
		 $declarations{$cmd}, $mathentities{$cmd});
	    if (defined &$cmd_sub) {
#print "CMD:$cmd :  ";
		# $ref_before may also be modified ...
		if ($cmd =~ /$sizechange_rx/o) {
		    $after = &$cmd_sub($after, @open_size_tags);
		} else {
		    $after = &$cmd_sub($after, @open_font_tags);
		};
#print "CMD:$cmd :  ";
	    } elsif (defined &$cmd_msub) {
		# $ref_before may also be modified ...
		$after = &$cmd_msub($after, @open_font_tags);
		if ( !$math_mode ) {
		    $after = "<MATH>" . $after . "</MATH>";
		    ++$commands_outside_math{$cmd};
		};
	    } elsif ($cmd_trans) { # One to one transform
		$cmd_trans =~ m|</.*$|;
		$after = $` . $after . $&;
		push(@open_font_tags, $cmd) if ($cmd =~ /$fontchange_rx/o);
		push(@open_size_tags, $cmd) if ($cmd =~ /$sizechange_rx/o);
	    } elsif ($mathentity) {
		if ( $math_mode ) {
#		    $after = "&$mathentity;" . $after;
		    $after = "&$mathentity#$cmd;" . $after;
		} else {
		    $after = "<MATH>&$mathentity#$cmd;</MATH>" . $after;
		    ++$commands_outside_math{$cmd};
		}
	    } elsif ($ignore{$cmd}) { # Ignored command
		$after = join('', " ", $after) if ($cmd eq " "); # catches `\ '
	    } elsif ($new_command{$cmd}) { 
                # e.g. some \the$counter 
		local($argn, $body, $opt) = split(/:!:/, $new_command{$cmd});
		do { local($_) = $body;
		     $body = &make_unique } if ($body =~ /$O/);
		if ($argn) {
		    local($before) = '';
		    local($_) = "\\$cmd ".$after;
		    $after = '';
		    $after = &substitute_newcmd;   # may change $after
		} else {
		    $after = $body . $after;
		}
	    } elsif ($cmd =~ /^the(.+)$/){
		$counter = $1;
	        local($tmp)="do_cmd_$cmd";
	        if (defined &$tmp) { # Counter
		    $after = &do_cmd_thecounter($after);
		} else {
		    if (defined $failed) {
                        $failed = 1;
		        $ref_before .= "$cmd";
		    } else { 
			$tmp = "wrap_cmd_$cmd";
			++$unknown_commands{$cmd} unless ($AUX_FILE||(defined &$tmp));
		        if ($VERBOSITY > 2) {
			    print STDERR "\n*** Unknown command[1]: \\$cmd *** \n" 
                                unless ($AUX_FILE||(defined &$tmp));
		        }
	            }
#		    $ref_before .= "$cmd" if ($failed);
		}
	    } elsif ($cmd eq "\n") { $ref_before .= " "; 
	    } else {
		# Do not add if reading an auxiliary file
		if (defined $failed) { 
                    $failed = 1;
		} else { 
	            local($tmp)="wrap_cmd_$cmd";
                    ++$unknown_commands{$cmd} unless ($AUX_FILE||(defined &$tmp));
		    if ($VERBOSITY > 2) {
                        print STDERR "\n*** Unknown command: \\$cmd [2]*** \n" 
                            unless ($AUX_FILE||(defined &$tmp));
                    }
	        }
	    }
	}
	$ref_contents = join('', $ref_before, $after);
	print STDERR "\n-> $ref_before\n" if ($VERBOSITY > 6);
    }
    $ref_contents;
}

# This makes images from the code for math-entities,
# when $NO_SIMPLE_MATH is set and the  math  extension is loaded.
#
sub replace_math_constructions {
    local($math_mode) = @_;
    &make_math_box_images($math_mode) if (/<BOX>/);
    &make_math_entity_images($math_mode) if (/\&\w+#\w+;/);
}

sub make_math_box_images {
    local($math_mode) = @_;
    local($pre,$this,$post,$tmp) = ('','','');
    local($slevel,$blevel) = 0;

    while (/<BOX>/) {
	$pre .= $`; $tmp = $`; $this = ''; $post = $';	
        # compute the super/sub-scripting level for each entity
        $tmp =~ s/<(\/?)SU[BP]>/
            if ($1) { $slevel--} else { $slevel++};''/eog;

	$tmp = $post;
	if ($tmp =~ /<(\/?)BOX>/o ) {
	    if ($1) { $this = $`; $post = $' }
	    else { $failed = 1 } # nested box, too complicated !
	} else {
	    &write_warnings("\nLost end of a <BOX> ?");
	    $failed = 1;
	}
        last if ($failed);

	($this,$_) = &process_box_in_latex(
		    $math_mode, $slevel, $this, $post);
	$_ =~ s/^\s*//; # remove any leading spaces
	$pre .= $this ."\001"; 
    }
    return  if ($failed);
    $_ = $pre . $_;
}

sub make_math_entity_images {
    local($math_mode) = @_;
    local($pre,$this,$post,$tmp) = ('','','');
    local($slevel) = 0;
    # compute the super/sub-scripting level for each entity
    while (/\&\w+#(\w+);/) {
	$pre .= $`; $tmp = $`; $this = $1; $post = $';
        $tmp =~ s/<(\/?)SU[BP]>/
            if ($1) { $slevel--} else { $slevel++};''/eog; 
	($this,$_) = &process_entity_in_latex(
		    $math_mode, $slevel, $this, $post);
	$_ =~ s/^\s*//; # remove any leading spaces
	$pre .= $this ."\001"; 
    }
    $_ = $pre . $_;
}


#RRM:  Revert a math-entity to create image using LaTeX, together with
# any super/sub-scripts (possibly nested or with \limits ).
# Must also get the correct  \display/text/(script)script  style.
#
sub process_entity_in_latex {
    local($mode,$level,$entity,$after) = @_;
    local($math_style,$supsub,$rest) = ('','','');
    $level++ if ($mode =~/box/); # for top/bottom of inline fractions, etc.

    if ($level) {
	$math_style = "\\". (($level > 1) ? "script" : "")."scriptstyle"
    } else {
	$math_style = "\\displaystyle" unless ($mode =~ /inline/);
    }
    while ($after =~ s/^\s*((\\limits)?\s*<SU(P|B)>)\s*/$supsub .= $1;''/eo) {
	local($slevel) = 1;
	local($aftersupb) = '';
	while ($slevel) {
	    $after =~ s/(<(\/)SU(B|P)>)/($2)? $slevel-- : $slevel++;''/oe;
	    $supsub .= $`.$&;
	    $aftersupb = $';
	}
	$after = $aftersupb;
    }

    local($latex_code) = "\$$math_style\\$entity$supsub\$";

    $global{'max_id'}++;
    ( &process_undefined_environment('tex2html_wrap_inline'
	     ,$global{'max_id'}, $latex_code ) , $after);
}

sub process_box_in_latex {
    local($mode,$level,$inside,$after) = @_;
    local($math_style,$which,$pre,$post,$tmp) = ('','',"\{","\}");
    
    if ($level) {
	$math_style = "\\". (($level > 1) ? "script" : "")."scriptstyle"
    } else {
	$math_style = "\\displaystyle" unless ($mode =~ /inline/);
    }

    if ($inside =~ /<((LEFT)|(RIGHT))>/ ) {
	$pre = "\\left"; $post = "\\right";
	if ($2) { 
	    $tmp = $`; $inside = $';
	    $pre .= (($tmp) ? $tmp : ".") . "\{";
	    if ( $inside =~ /<RIGHT>/ ) {
		$tmp = $';
		$inside = $`;
		$post = "\}". (($tmp) ? $tmp : ".");
	    }
	} else {
	    $pre .= ".\{"; $tmp = $'; $inside = $`;
	    $post = "\}". (($tmp) ? $tmp : ".");
	}
    }
    if ( $inside =~ /<((OVER)|(ATOP)|(CHOOSE))>/ ) {
	$pre .= $`;
	$post = $' . $post ;
	if ($2) { $which = "over " }
	elsif ($3) { $which = "atop " }
	elsif ($4) { $which = "atopwithdelims\(\)" }
    }

    local($latex_code) = join('', "\$" , $math_style , $pre 
	  , (($which)? "\\$which" : "") , $post , "\$" );

    if ($after =~ s/<SUP ALIGN=\"CENTER\">([^<]*)<\/SUP>/
	$tmp =$1;''/eo ) {
	$latex_code = join('', "\\stackrel" , $latex_code
			   , "\{" , $tmp , "\}" );
    }
    
    $global{'max_id'}++;
    ( &process_undefined_environment('tex2html_wrap_inline'
	     ,$global{'max_id'}, $latex_code ) , $after);
}

####################### Processing Meta Commands ############################
# This is a specialised version of process_command above.
# The special commands (newcommand, newenvironment etc.)
# must be processed before translating their arguments,
# and before we cut up the document into sections
# (there might be sectioning commands in the new definitions etc.).
# \newtheorem commands are treated during normal processing by
# generating code for the environments they define.

sub substitute_meta_cmds {
    local ($next_def);
    local ($cmd, $arg, $argn, $opt, $body, $before, $after);
    local ($new_cmd_no_delim_rx, $new_cmd_rx, $new_env_rx, $new_cmd_or_env_rx);
    local ($new_end_env_rx);
    &tokenize($meta_cmd_rx);	#JCL(jcl-del) - put delimiter after meta command
    print "\nProcessing macros ..." if (%new_command || %new_environment);
    # First complete any replacement left-over from the previous part.
    if ($UNFINISHED_ENV) { 
	s/$UNFINISHED_ENV/$REPLACE_END_ENV/;
	$UNFINISHED_ENV = '';
	$REPLACE_END_ENV = '';
    }

    while (/$meta_cmd_rx/o) {	# ... and uses the delimiter
#	undef $_;
	($before, $cmd, $after) = ($`, $1.$2, $');
	print ".";
	$next_def = "\n\\$cmd";
	local($cmd_sub) = "get_body_$cmd";
	if (defined &$cmd_sub) {
	    $_ = join('',$before, &$cmd_sub(*after));
	    &add_to_preamble($cmd, $next_def);
	}
	else {
	    $_ = join('', $before, $cmd, $after);
	}
    }
    # All the definitions have now moved to the $preamble and their bodies
    # are stored in %new_command and %new_environment
    #
    # Now substitute the new commands and environments:
    # (must do them all together because of cross definitions)
    $new_cmd_rx = &make_new_cmd_rx(keys %new_command);
    $new_cmd_no_delim_rx = &make_new_cmd_no_delim_rx(keys %new_command);
    $new_env_rx = &make_new_env_rx;
    $new_end_env_rx = &make_new_end_env_rx;
#    $new_cnt_rx = &make_new_cnt_rx(keys %new_counter);
    $new_cmd_or_env_rx = join("|", $new_cmd_no_delim_rx." ", $new_env_rx);
#    $new_cmd_or_env_rx = join("|", $new_cmd_no_delim_rx." ", $new_env_rx, " ".$new_cnt_rx);
    $new_cmd_or_env_rx =~ s/^ \||\|$//;

    print STDERR "\nnew commands:\n" if ($VERBOSITY > 2);
    while (($cmd, $body) = each %new_command) {
	unless ($expanded{"CMD$cmd"}++) {
	    print STDERR ".$cmd" if ($VERBOSITY > 2);
	    $new_command{$cmd} = &expand_body;
	    &write_mydb("new_command", $cmd, $new_command{$cmd});
	}
    }

    print STDERR "\nnew environments:\n" if ($VERBOSITY > 2);
    while (($cmd, $body) = each %new_environment) {
	unless ($expanded{"ENV$cmd"}++) {
	    print STDERR ".$cmd" if ($VERBOSITY > 2);
	    $new_environment{$cmd} = &expand_body;
	    &write_mydb("new_environment", $cmd, $new_environment{$cmd});
	}
    }

    print STDERR "\nnew counters and dependencies:\n" if ($VERBOSITY > 2);
    &clear_mydb("dependent") if ($DEBUG);     #avoids appending to a previous version
    while (($cmd, $body) = each %dependent) {
	print STDERR ".($cmd,$body)" if ($VERBOSITY > 2);
        &write_mydb("dependent", $cmd, $dependent{$cmd});
    }

    print STDERR "\ntheorem counters:\n" if ($VERBOSITY > 2);
    &clear_mydb("new_theorem") if ($DEBUG);     #avoids appending to a previous version
    while (($cmd, $body) = each %new_theorem) {
	print STDERR ".($cmd,$body)" if ($VERBOSITY > 2);
        &write_mydb("new_theorem", $cmd, $new_theorem{$cmd});
    }

    print "+";
    if (length($new_cmd_rx)) {
        print STDERR "\ntokenizing: $new_cmd_rx\n" if ($VERBOSITY > 2);
	&tokenize($new_cmd_rx); # Put delimiter after the new commands

	# and use the delimiter.
        # This strategy will save much time, because
	# of the simple search pattern. It could be much more efficient
	# inside a s/// command.
        print STDERR "\nsubstituting new commands: $new_cmd_rx\n" if ($VERBOSITY > 2);
	while (/$new_cmd_no_delim_rx /o) {
	    ($before,$cmd,$after) = ($`,$1,$');
	    print ".";
	    print STDERR "$cmd" if ($VERBOSITY > 2);
	    &substitute_newcmd # sets $_
		if ($new_command{$cmd});
	}
    }
    if (length($new_env_rx)) {
        print STDERR "\nsubstituting new environments: $new_env_rx\n" if ($VERBOSITY > 3);
	while (/\n?$new_env_rx/ && (($before, $cmd, $after) = ($`, $2, $'))) {
	    print STDERR ",";
	    print STDERR "{$cmd}" if ($VERBOSITY > 1);
	    $_ = join('',$before, &substitute_newenv);
	}
    }
    print STDERR "\n *** substituting metacommands done ***\n" if ($VERBOSITY > 3);
}


sub expand_body {
    return unless length($new_cmd_or_env_rx);
    local($_) = $body;
    local($cmd);
    # Uses $before, $body, $arg, etc. of the caller, but not $cmd.
    # Uses $new_cmd_rx (resp. $new_cmd_no_delim_rx) and $new_env_rx
    # set in the caller, of which one might be empty.

    # Puts delimiter after the new commands ...
    &tokenize($new_cmd_rx) if length($new_cmd_rx);

    while (/$new_cmd_or_env_rx/o) {
	# $new_cmd_rx binds $1, and $new_env_rx binds $3.
	($before,$cmd,$after) = ($`,$1.$3,$');

	if (length($new_command{$cmd})) { # We have a command
	    # this tokenizes again
	    &substitute_newcmd;# sets $_
	}
	elsif (length($new_environment{$cmd})) {
	    $_ = join('',$before, &substitute_newenv);
	}
    }
    $_;
}


sub substitute_newcmd {
    # Modifies $arg,$argn,$_,$opt, $before and $after in the caller
    # Get the body from the new_command array
    local($tmp,$cnt) = ('',0);
    ($argn, $_, $opt) = split(/:!:/, $new_command{$cmd});
    &tokenize($new_cmd_rx); # must do it again for newly inserted cmd bodies
    print STDERR "\nNEW:$cmd:$_" if ($VERBOSITY > 5);
    foreach $i (1..$argn) {
	if ($i == 1 && $opt ne '}') {
	    $after =~ s/$optional_arg_rx//eo;
	    length($1) ? $arg = $1 : $arg = $opt;
	}
	else {
	    # Get the next argument, if not in braces, get next character
	    $after =~ s/$next_pair_rx/$arg = $2;''/eo ||
		$after =~ s/\s*(\\[a-zA-Z]+|.)/$arg = $1;''/eo;
	}
	$arg =~ s/(^|[^\\])\\\#/$1$hash_mark/g;
	$arg =~ s/\#/$param_mark/g;

	#RRM: multiple instances can fail later in  &make_unique
#	s/\#$i/$arg/g;		# Substitute the arguments in the body
	#RRM: ...so do one at a time and  &make_unique
	$tmp = $_;
	$cnt = $tmp =~ s/\#$i//g ;
	if ($cnt > 1 ) {
	    $tmp = $_;
	    while ($cnt > 1) { 
		if ( s/\#$i/$arg/) { 
		    &make_unique if ($arg =~ /$O/ ); 
		    &make_unique_p if ($arg =~ /$OP/ );
		}
		$cnt--;
	    }
	    $tmp = $_;
	}
	s/\#$i/$arg/ ;
	print "\n *** substitution: $arg \nfor \#$i in \\$cmd did not take ***\n"
	   if (/\#$i/);
	&warnings("incomplete substitution in a \\$cmd command:\n$_") if (/\#$i/);
    }
    s/$param_mark/\#/g;
    s/$hash_mark/\\\#/g;

    # Make the body unique (give unique id's to the brackets),
    # translate, and return it
    &make_unique;
    print STDERR "\nOUT:$cmd:$_" if ($VERBOSITY > 5);

# Insert a space to prevent letters from clashing together with a
# letter command. Consider this:
# New command substitution is restricted to commands introduced by
# \newcommand etc. (so-called meta commands), but it is not done
# for already defined commands, eg. \large.
# But new command, as well as new environment, substitution is done
# prior to any other substitution.
# So \newcommand{\this}{...} {\large\this b} will get expanded the
# following way:
# 1. \newcommand{\this}{...}
#    is handled by &substitute_meta_cmds, it gets the definition
#    of \this and stores it within a table, %new_command.
#    After all new commands are recognized, &expand_body is called
#    to expand one command body from each other. That's O(n*n)!
# 2. A regular expression $new_cmd_rx is built containing a pattern
#    that matches all occurrences of a properly delimited \this
#    macro. When matching, ensuing white space gets lost.
#    (But only for letter commands, see also &make_new_cmd_rx.)
#    Another regular expression called $new_cmd_no_delim_rx is built
#    which matches exact the \this, and would also match the prefix
#    of \thisx.
# 3. The *whole* text is tokenized using $new_cmd_rx, with separates
#    \this from the ensuing text by one white space.
# 4. Then $new_cmd_no_delim_rx together with the delimiting space
#    is used to substitute \this with its body.
# 5. The following situations may occur:
#  a) ... is some text (no macros) => {\large<text>yyy}
#     Then we must prevent that the text clashes into \large.
#     This is only dangerous when <text> begins with a letter.
#  b) ... contains another, not expanded new command.
#     This happens during &expand_body.
#     In this case, make sure to &tokenize the body before giving
#     the result to the caller. Also take care that leading letters
#     of the body cannot clash into \large.
#  e) ... contains a macro not known as new command:
#     Make sure that the macro cannot clash with the ensuing yyy.
#  f) ... is empty:
#     Make sure that \large cannot clash with yyy.
# 6. We prevent clashing by inserting a delimiting blank.
#    Out of the scetched situation, there are three conditions to
#    take care of:
#  a) empty body, left a letter command, right a letter => blank
#  b) body starts with letter, left a letter command    => blank
#  c) body ends with letter command, right a letter     => blank
#  d) else => no blank, clash all together, it will work.
# 7. With this rules, the expansion should work quite well,
#    concerning letter/non-letter commands and white space
#    handling.
# 8. Deficiencies:
# 8.1 Consider \this<CR>that. It's handled this way:
#  a) The \this swallows the <CR> in LaTeX, but what LaTeX2HTML does
#     is to &tokenize the expression into \this <CR>that.
#  b) If ... is some text, it results in <text><CR>that.
#  c) If ... is a macro (or command, or control sequence, these
#     terms are often mixed up, but effectively mean the same),
#     then if the macro later takes at least one argument, the <CR>
#     might get swallowed, this depends on the grace of $next_pair_rx
#     resp. $next_pair_pr_rx.
#     If the macro takes no arguments, the <CR> remains in the text.
#  d) If ... ends in another new command, the problem repeats.
# 8.2 The new commands are substituted in a very insensitive way.
#     If \this occurs within an environment which sees \this
#     totally different, there's no chance to substitute \this in
#     a different way.
# 8.3 In relation to 8.2 a similar problem arises when the meta
#     command, or several meta commands redefining \this, occur
#     amongst several \this macros.
# 8.4 In raw TeX like environments it is not possible to revert the
#     expansion of \this, but \this probably *must* occur in its
#     raw form.

# Handle the cases as depicted in the description of new command
# substitution.
    local($befdel,$aftdel);
    $befdel = ' '
	if ($before=~/(^|[^\\])\\[a-zA-Z]+$/ && /^$/ && $after=~/^[a-zA-Z]/) ||
	    ($before=~/(^|[^\\])\\[a-zA-Z]+$/ && /^[a-zA-Z]/);
    $aftdel = ' '
	if /(^|[^\\])\\[a-zA-Z]+$/ && $after=~/^[a-zA-Z]/;
    $_ = join('', $before, $befdel, $_, $aftdel, $after);
}

#RRM:  use this to test whether a specific command is substituting correctly
sub trace_cmd {
    local($this) = @_;
    if ($cmd eq $this) { print "\n$1=>$id:$2::"}
}

# Make the text unique (give unique id's to the brackets).
# The text shouldn't contain processed brackets.
sub make_unique {
    local($id) = $global{'max_id'};
    $* = 1;    # Multiline matching ON
    for (;;) {
	# this looks quite funny but is optimized
	last unless s/$O(\d+)$C([\w\W]*)$O\1$C/$id++;"\000$id $2\000$id "/geo;
    }
    s/\000(\d+) /$O$1$C/go;
    $* = 0;    # Multiline matching OFF
    $global{'max_id'} = $id;
}

#RRM: this shouldn't be needed, but just in case...
sub make_unique_p {
    local($id) = $global{'max_id'};
    $* = 1;    # Multiline matching ON
    for (;;) {
        # this looks quite funny but is optimized
        last unless s/$OP(\d+)$CP([\w\W]*)$OP\1$CP/$id++;"\000$id $2\000$id "/geo;
    }
    s/\000(\d+) /$OP$1$CP/go;
    $* = 0;    # Multiline matching OFF
    $global{'max_id'} = $id;
}


sub substitute_newenv {
    # Modifies $cmd and $after in the caller
    # Get the body from the new_environment array
    local($argn, $begdef, $enddef, $opt) = split(/:!:/, $new_environment{$cmd});
    local($arg,$new_def_rx,$tmp,$cnt);
    # Note that latex allows argument substitution only in the
    # \begin part of the new definition
    local($_) = $begdef;
    print STDERR "\nNEW:{$cmd}:$_" if ($VERBOSITY > 4);
    foreach $i (1..$argn) {	# Process the arguments
	if ($1 == 1 && $opt ne '}') {
	    $after =~ s/$optional_arg_rx/$arg = $1;''/eo;
	    $arg = $opt unless $arg;
	}
	else {
	    $after =~ s/$next_pair_rx/$arg = $2;''/eo;
	}
	$arg =~ s/(^|[^\\])\\\#/$1$hash_mark/g;
	$arg =~ s/\#/$param_mark/g;

        #RRM: multiple instances can fail later in  &make_unique
#       s/\#$i/$arg/g;          # Substitute the arguments in the body
        #RRM: ...so do one at a time and  &make_unique_p
        $tmp = $_;
        $cnt = $tmp =~ s/\#$i//g ;
        if ($cnt > 1) {
            $tmp = $_;
            while ($cnt > 1) {
		if ( s/\#$i/$arg/) { 
		    &make_unique if ($arg =~ /$O/ ); 
		    &make_unique_p if ($arg =~ /$OP/ );
		}
                $cnt--;
            }
            $tmp = $_;
        }
        s/\#$i/$arg/ ;
        print "\n *** substitution: $arg \nfor \#$i in {$cmd} did not take ***\n"
           if (/\#$i/);
	&warnings("incomplete substitution in a {$cmd} environment:\n$_") if (/\#$i/);
    }
    s/$param_mark/\#/g;
    s/$hash_mark/\\\#/g;

    # Make the body unique (Give unique id's to the brackets),
    # translate, and return it
    $_ = &revert_to_raw_tex($_);
    &pre_process;
    &make_unique;		# Make bracket IDs unique
    print STDERR "\nBEGIN:$cmd:$_" if ($VERBOSITY > 4);
    $begdef = $_;
    # Now substitute the \end part:
    $_ = &revert_to_raw_tex($enddef);
    &pre_process;
    &make_unique;		# Make bracket IDs unique
    $enddef = $_;
    local($new_end_def_rx) = &make_end_env_rx($cmd);
    if (($enddef)&&!($after =~ s/\n?$new_end_def_rx/$enddef/ )) {
        $UNFINISHED_ENV = $new_end_def_rx;
        $REPLACE_END_ENV = $enddef;
    };
    join('',$begdef,$after);
}

sub do_cmd_end { #RRM:  catches the end of any unclosed environments
    local($_) = @_;
    local($cmd);
    s/$next_pair_pr_rx//o;
    s/^\n//;
    $_;
}

# Removes the definition from the input string, adds to the preamble
# and stores the body in %new_command;
sub get_body_newcommand {
    local(*_) = @_;
    local($argn,$cmd,$body,$tmp,$tmp1,$opt);
    $cmd = &get_next(1);	# Get command name
    $cmd =~ s/^\s*\\//;
    $argn = &get_next(0);	# Get optional no. of args
    $argn = 0 unless $argn;
    # Get the body of the code and store it with the name and number of args
    # UNLESS THE COMMAND IS ALREADY DEFINED
    # ...in which case $ALLOW_REDEFINE must also have been set.  # RRM
    # (This is the mechanism with which raw html can be ignored in a Latex document
    # but be recognised as such by the translator).
    $opt = '}';			# Flag for no optional arg
    $opt = &get_next(0) if (/^\[/);
    $body = &get_next(4);#get the body
#    if ($body =~ /\\/) {
#	$body = join('',&make_deferred_wrapper(1), $body, &make_deferred_wrapper(0));
#    }
    $tmp = "do_cmd_$cmd";
    if ((defined &$tmp)&&($ALLOW_REDEFINE)) {
	print "\n*** redefining \\$cmd ***\n";
	&write_warnings("\nredefined command \\$cmd ");
    } elsif ($ALLOW_REDEFINE) {
	print "\n*** redefining \\$cmd ***\n";
	&write_warnings("\ncommand \\$cmd had no previous definition");
    }
    $new_command{$cmd} = join(':!:',$argn,$body,$opt)
	unless ((defined &$tmp)&&(! $ALLOW_REDEFINE));
    undef $body;
    $_;
}

# Removes the definition from the input string, adds to the preamble
# and stores the body in %new_environment;
sub get_body_newenvironment {
    local(*_) = @_;
    local($argn,$env,$begin,$end,$tmp,$opt);
    $env = &get_next(1);	# Get the environment name
    $env =~ s/^\s*\\//;
    $argn = &get_next(0);	# Get optional no. of args
    $argn = 0 unless $argn;
    # Get the body of the code and store it with the name and number of args
    # UNLESS THE COMMAND IS ALREADY DEFINED (see get_body_newcommand)
    # ...in which case $ALLOW_REDEFINE must also have been set.  # RRM
    $opt = '}';			# Flag for no optional arg
    $opt = &get_next(0) if (/^\[/);
    $tmp = "do_env_$env";
    $begin = &get_next(1); $end = &get_next(1);
    if ((defined &$tmp)&&($ALLOW_REDEFINE)) {
	print STDERR "\n*** redefining environment {$env} ***\n";
	&write_warnings("\nredefined environment {$env} ");
    }
    $new_environment{$env} = join(':!:', $argn, $begin, $end, $opt)
	unless ((defined &$tmp)&&(! $ALLOW_REDEFINE));
    $_;
}

sub get_body_renewcommand {
    local($ALLOW_REDEFINE) = 1;
    &get_body_newcommand($_[0]);
}

sub get_body_providecommand {
    &get_body_newcommand($_[0]);
}

sub get_body_renewenvironment {
    local($ALLOW_REDEFINE) = 1;
    &get_body_newenvironment($_[0]);
}

# Instead of substituting as with newcommand and newenvironment,
# or generating code to handle each new theorem environment,
# it now does nothing. This forces theorem environments to be passed
# to latex. Although it would be possible to handle theorem
# formatting in HTML as it was done previously it is impossible
# to keep the theorem counters in step with other counters (e.g. equations)
# to which only latex has access to. Sad...
sub get_body_newtheorem {
    local(*_) = @_;
    local($title, $env, $ctr, $within, $cmd, $tmp, $body, $begin, $end);
    # Just chop off the arguments and append to $next_def
    $env = &get_next(1);
    $ctr = &get_next(0);
    $title = &get_next(1);
    $within = &get_next(0);
    if (!($ctr)) {
	# define the new counter
	$ctr = $env;
	do {
	    local($_) = "\\arabic<<1>>$ctr<<1>>";
	    $_ = join('',"\\the$within", "." , $_) if ($within);
	    &make_unique; $body = $_;
	    $cmd = "the$ctr";
	    $tmp = "do_cmd_$cmd";
	    do {
                $new_command{$cmd} = join(':!:',0,$body,'}') 
	    } unless (defined &$tmp);
	    &write_mydb("new_command", $cmd, $new_command{$cmd});
	    eval "sub do_cmd_$cmd {\n"
		. '&translate_commands(' . "\"$body\"" . ");\n}\n";
	    $raw_arg_cmds{$cmd} = 1;
	    undef $body;
	};
	&do_body_newcounter($ctr);
    }

    # record the counter dependency
    &addto_dependents($within,$ctr) if ($within);

    # save the text-label in the %new_theorem hash
    $new_theorem{$env} = $title;

    # define a new environment
    $begin = "\\begin{theorem_type}[$env][$ctr][$within]";
    $end = "\\end{theorem_type}";
    $tmp = "do_env_$env";
    if ((defined &$tmp)&&($ALLOW_REDEFINE)) {
	print STDERR "\n*** redefining theorem environment {$env} ***\n";
    }
    $new_environment{$env} = join(':!:', '', $begin, $end, '')
	unless ((defined &$tmp)&&(! $ALLOW_REDEFINE));
    $_;
}

sub do_env_theorem_type {
    local($_) = @_;
    local($dum,$env,$ctr,$within, $label, $name, $title, $text);
    ($env, $dum) = &get_next_optional_argument;
    ($ctr, $dum) = &get_next_optional_argument;
    ($within, $dum) = &get_next_optional_argument;
    ($name, $dum) = &get_next_optional_argument;
    $name = &translate_commands($name);

    print STDERR "\nSTP:$ctr:+1" if ($VERBOSITY > 3);
    $global{$ctr}++;
    print STDERR "=".$global{$ctr}." " if ($VERBOSITY > 3);
    &reset_dependents($ctr) if ($dependent{$ctr});

    # construct the full title from the counter values
    $title = join( '', "<B>", $new_theorem{$env}, " "
	      , &translate_commands("\\the$ctr"), "</B>" );

    $_ = &translate_environments($_);
    local($attribs, $border);
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    $_ = &translate_commands($_);

    # extract any name or label that may occur at the start
    s/^\s*(\\label<<\d*>>([^<]*)<<\d*>>)?\s*(\([^\)]*\))?/
	$label=$1; $text=$2; $name=$3 if ($3); ''/eo;
    if ($label) {
	$label = &anchor_label($text,$CURRENT_FILE,'');
	$label =~ s/$anchor_mark/$title/;
	$title = $label;
    }
    if ($name) {
	$name =~ s/^\s*|\s*$//g; 
	$name = join('', " <B>(", $name, ") </B>");
    }
    $_ = join('',"<P>\n", $title, "$name", "</P>\n<P>", $_,"</P>\n<P>");

    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) { 
	&make_table( $border, $attribs, '', '', '', $_ ) 
    } else { $_ }
}

# Modifies $_ in the caller and as a side-effect it modifies $next_def
# which is local to substitute_meta_cmds
sub get_next {
    local($what) = @_;
    local($next, $pat, $tmp);
    if ($what == 1) {
	($next, $tmp, $pat) = &get_next_argument;
    }
    elsif ($what == 2) {
	($next, $pat) = &get_next_tex_cmd;
    }
    elsif ($what == 3) {
	($next, $pat) = &get_next_def_arg;
    }
    elsif ($what == 4) {
	($next, $tmp, $pat) = &get_next_argument;
    }
    else {
	($next, $pat) =  &get_next_optional_argument;
    }
    $next_def .= &revert_to_raw_tex($pat) if $pat;
    $next =~ s/(^\s*)|(\s*$)//g unless ($what == 4);#don't loose white space on body
    $next;
}

# The following get_next_<something> ARE ALL DESTRUCTIVE.
sub get_next_argument {
    local($next, $br_id, $pat);
    if (!(s/$next_pair_rx/$br_id=$1;$next=$2;$pat=$&;''/eo)) {
	print " *** Could not find argument for command ***\n";
	print "$_\n";
    };
    ($next, $br_id, $pat);
}

sub get_next_pair_or_char_pr {
    local($next, $br_id, $pat, $epat);
    if ( (/^\s*([^\s\\<])/o && (! $`))) {
	($next, $pat) = ($1, $&) }
    elsif ( /$next_pair_pr_rx/o && (! $`)) {
	($next, $br_id, $pat) = ($2, $1, $&) };
    $epat = &escape_rx_chars($pat);
    s/$epat// if $pat;
    ($next, $br_id, $pat);
}

#sub get_next_optional_argument {
#    local($next, $pat);
#    s/$optional_arg_rx/$next=$1;$pat=$&;''/eo
#	if (/\s*[[]/ && (! $`)); # if the first character is a [
#        # (/^[]/ does not work because it may match the beginning of ANY line
#    s/^\s*\[\]/$pat=$&;''/eg unless $pat; # This is not picked by $optional_arg_rx
#    ($next, $pat);
#}
sub get_next_optional_argument {
    local($next, $pat);
    s/$optional_arg_rx/$next=$1;$pat=$&;''/eo
	if (/\s*[[]/ && (! $`)); # if the first character is a [
    # if  nested inside {}s  we need to get more tokens  
    if ($pat) {
	# check for \item, indicating something has gone wrong
	if ($pat =~ /\\item/ ) {
	    print "\n*** optional argument badly formed:\n" . $pat . "\n\n";
	    $_ = $pat . $_;
	    return('','');
	}
        # check for being nested inside {}s
	local($found) = $pat;
        while ($found =~ s/$O(\d+)$C[\s\S]*$O\1$C//g) {
	    if ($found =~ /$O(\d+)$C/) {
		local($br_id) = $1;
		if (s/$O$br_id$C//) {
		    $found .= $`.$&;
		    $pat .= "]".$`.$&;
		    $next .= "]".$`.$&;
		    $_ = $';
		    s/^([^]]*)\]/$next.=$1;$pat.=$&;''/e;
		    $found .= $&;
	        } else { last } # give up if no closing brace
	    }
        }
    } else {
	s/^\s*\[\]/$pat=$&;''/e; # This is not picked by $optional_arg_rx
    }
    ($next, $pat);
}

#JCL(jcl-del) - use new form of $single_cmd_rx.
sub get_next_tex_cmd {
    local($next, $pat);
    s/$single_cmd_rx/$4/;
    ($next, $pat) = ($1.$2,"\\".$1.$2);
}

sub get_next_def_arg {
    local($next, $pat);

    # Sets is_simple_def for caller.  Start by turning it off, then
    # turn it on if we find one of the "simple" patterns.

    # This has got to be hit-or-miss to an extent, given the
    # thoroughly incestuous relationship between the TeX macroprocessor
    # ('mouth') and typesetting back-end ('stomach').  Anything which
    # even does catcode hacking is going to lose BAD.

    s/^\s*//o;			# Remove whitespace

    $is_simple_def = 0;

    # no arguments

#    if (/^$O/ && (! $`)) { $next=0; $pat=''; $is_simple_def=1; $O }
    if (/^$O/ && (! $`)) { $next=0; $pat=''; $is_simple_def=1;}

    # 'simple' arguments

    if (! $is_simple_def && /$tex_def_arg_rx/o && (! $`)) {
	s/$tex_def_arg_rx/$next=$1; $pat=$&; $is_simple_def=1; $O/eo; }

    # MESSY arguments

    if (! $is_simple_def) {
 	print "Arguments to $cmd are too complex ...\n";
	print "It will not be processed unless used in another environment\n";
	print "which is passed to LaTeX whole for processing.\n";

	s/^[^<]*(<[^<]+)*<</$next=''; $pat=$&; $O/eo;
    }

    $pat =~ s/$O$//o;

    ($next, $pat);
}

#### Key-value parsing added by RRM
#
#   This cleans-up the key-value pairs for a given tag, 
#   by removing unnecessary spaces and commas, inserting quotes
#   around the value and puts a preceding space.
#   The key becomes upper-case, while the value becomes lower-case.
#   If specific `tags' are provided, then checking is done to verify 
#   that the keys and values are valid for these tags, eliminating
#   any that are not; unmatched keys or values are handled as well.
#   If no tags are provided, then just a list of pairs is returned.
#
sub parse_keyvalues {
    local($_,@tags) = @_;
    local($key,$KEY,$attribs,$atts,%attributes)=('','','','');

    local($saved) = &revert_to_raw_tex(&translate_commands($_));
    $saved =~ s/$percent_mark/%/g;
    if (@tags) {
	foreach $tag (@tags) {
	    $_ = $saved;
	    local($name)= $tag."_attribs";
	    $taglist = $$name;
	    $name .= "_rx_list";
	    $taglist .= $$name;
	    $taglist =~ s/,,/,/;
	    s/(^|,)\s*([a-zA-Z]+)\s*\=\s*"?(\w+)"?\s*/$attributes{$2}="$3";''/eg;
	    foreach $key (keys %attributes){ 
		$KEY = $key;
		$KEY =~ tr/a-z/A-Z/;
		if ($taglist =~ /,$KEY,/i) {	        
		    local($keyname) = $tag."__".$KEY; 
		    local($keyvalues) = '';
		    if ($$keyname) {
			$keyvalues = $$keyname;
			$atts = $attributes{$key};
			if ($keyvalues =~ /\,$atts\,/i ) {
			    $atts =~ tr/A-Z/a-z/;
			    $attribs .= " $KEY=\"$atts\"";
			    print "$KEY=$atts " if ($VERBOSITY > 4);
			} else { &invalid_tag($tag,$KEY,$atts); }
		    } else {	# test for a regular expression
		        $keyname = $keyname."_rx";
			if ($$keyname) {
			    $keyvalues = $$keyname;
			    $atts = $attributes{$key};
			    if ($atts =~ /$keyvalues/) {
				$atts =~ tr/A-Z/a-z/;
				$attribs .= " $KEY=\"$atts\"";				
				print "$KEY=$atts " if ($VERBOSITY > 4);
			    } else { &invalid_tag($tag,$KEY,$atts) }
			} else {
			    $atts = $attributes{$key};
			    $atts =~ tr/A-Z/a-z/;
			    $attribs .= " $KEY=\"$atts\"";
			    print "$KEY=$atts " if ($VERBOSITY > 4);
			}
		    }
		} else {
		    print "$key not in $taglist for $tag" if ($VERBOSITY > 4);
		}
	    }
	}
	$attribs .= &parse_valuesonly($_,@tags);
    } else {
	# with no tags provided, just list the key-value pairs
	$_ = $saved;
	s/\s*(\w+)\s*=\s*\"?(\w+)\"?\s*,?/$attributes{$1}=$2;''/eg;
	foreach $key (keys %attributes){ 
	    $KEY = $key;
	    $KEY =~ tr/a-z/A-Z/;
	    $atts = $attributes{$key};
	    $atts =~ tr/A-Z/a-z/;
	    $attribs .= " $KEY=\"$atts\"";
	}
    }
    $attribs;
}

sub invalid_tag {
    local($tag,$key,$value) = @_;
    &write_warnings("$key=$value is an invalid value in the <$tag> tag\n");
}

# RRM
#   This creates key-value pairs from values only, 
#   by checking whether the data matches any key to the provided tags.
#   Only the first match found is retained.
#   Attributes with no values are also recognised here.
#
sub parse_valuesonly {
    local($values,@tags) = @_;
    local($i,$tag,$key,$KEY,$attribs,$atts)=(0,'','','','','');
    local($saved) = &revert_to_raw_tex(&translate_commands($values));
    $saved =~ s/$percent_mark/%/g;
    foreach $tag (@tags) {
	local($name)= $tag."_attribs";
	$taglist = $$name;
	$values = $saved;
#        $values =~ s/\s*\"?([^,\s\"]+)\"?\s*,?/$i++;$attributes{$i}=$1};''/eg;
        $values =~ s/\s*\"?([^,\s\"]+)\"?\s*,?/$i++;$attributes{$i}=$1;''/eg;
        local($j) = 0;
	while ($j < $i) {
	    $j++;
	    $key = $attributes{$j};
	    if ($taglist =~ /,$key,/i) {
		$KEY = $key;
		$KEY =~ tr/a-z/A-Z/;
		$attribs .= " $KEY";
		print " $KEY" if ($VERBOSITY > 4);
	    } else {
		$atts = $attributes{$j};
		$key = &find_attribute($key,$tag);
	        if ($key) {
		    $KEY = $key;
		    $KEY =~ tr/a-z/A-Z/;
		    $atts =~ tr/A-Z/a-z/;
	            $attribs .= " $KEY=\"$atts\"";
		    print " $KEY = $atts" if ($VERBOSITY > 4);
		} else { }
	    }
	}
    }
    $attribs;
}

# RRM
#   Extracts key-value pairs using a supplied (comma-separated) list.
#   When no list is given, it checks for a pre-defined list for the tag.
#   
sub extract_attributes {
    local($tag,$taglist,$_) = @_;
    local($key,$attribs,$unused,%attributes);
    if (! ($taglist)) {
	local($name) = "$tag"."_attribs";
	if ($$name) { $taglist = $$name }
    }
    s/\s*(\w+)\s*=\s*\"?(\w+)\"?\s*,?/$attributes{$1}=$2;''/eg;
    foreach $key (keys %attributes){ 
	if ($taglist =~ /\,$key\,/) {
	    $attribs .= " $key=\"$attributes{$key}\"";
	    &write_warnings("valid attribute $key for $tag\n");
	} else {
	    &write_warnings("unknown attribute $key for $tag\n");
	    $unused .= " $key=\"$attributes{$key}\"";
	}
    }
    ($attribs,$unused);
}

# RRM
#   Finds the attribute of a given tag, for which a given value is valid.
#   Requires variables: <tag>_<key> to be a comma-separated list of keys.
#   So far it cannot recognise data-types, only names.
#
sub find_attribute {
    local($key,$attrib,$tag) = ('',@_);
    local($name) = $tag."_attribs";
    local($attrib_list)=$$name;
    if (!($attrib_list)) { return(0); }
    $attrib_list =~ s/^\,//o;
    $attrib_list =~ s/\,$//o;
    local(@keys) = split(',',$attrib_list);
    local($attrib_vals) = '';
    foreach $key (@keys) {
	$name = $tag."__".$key;
	$attrib_vals = $$name;
	if ($attrib_vals =~ /\,$attrib\,/i ) { 
	    return ($key);
	}
    }
    $name = $tag."_attribs_rx_list";
    $attrib_list=$$name;
    if (!($attrib_list)) { return(0); }
    $attrib_list =~ s/^\,//o;
    $attrib_list =~ s/\,$//o;
    @keys = split(',',$attrib_list);
    foreach $key (@keys) {
	next if ($attribs =~ / $key=/);
	$name = $tag."__".$key."_rx";
	$attrib_vals = $$name;
	if ( $attrib =~ /^$attrib_vals$/ ) { 
	    return ($key);
	}
    }
    0;
}


sub do_cmd_HTML {
    local($_) = @_;
    local($tag,$value,$attribs,$dum);
    local($attribs, $dum) = &get_next_optional_argument;
    s/$next_pair_pr_rx/$tag = $2;''/eo;
    $tag = &translate_commands($tag) if ($tag =~ /\\/);
    if (! $tag) {
	print "*** no tag given with \\HTML command, ignoring it";
	return($_);
    }
    local($after) = $_;
    local($value,$TAGattribs,$etag);
    if (defined $unclosed_tags_list{$tag}) {
    } elsif (defined $closed_tags_list{$tag}) {
	s/$next_pair_pr_rx/$value = $2;''/eo;
	$etag = "</$tag>";
	$after = $_;
    } else {
	print "\n*** <$tag> is not a valid tag for HTML $HTML_VERSION";
	print "\n rejecting: \\HTML".(($attribs)? "[$attribs]" : '')."{$tag}";
	return $_ ;
    }
    if ($dum) {
	$attribs = &translate_commands($attribs) if ($attribs=~/\\/);
        if ($attribs) {
            if (!($attribs =~ /=/)) {
                $TAGattribs = &parse_valuesonly($attribs,$tag);
            } else {
                $TAGattribs = &parse_keyvalues($attribs,$tag);
            }
        }
    } else { }  # default if no [...]
    local($needed) = join(','
	    , $closed_tags_list{$tag},$unclosed_tags_list{$tag});
    $needed =~ s/,,/,/g; $needed =~ s/^,|,$//g;
    if ($TAGattribs) {
	if ($needed) {
	    $needed =~ s/,,/,/g;
	    local($this, @needed);
	    (@needed) = split(',',$needed);
	    foreach $this (@needed) {
		next unless ($this);
		next if ($TAGattribs =~ /\b$this\b/);
		print "\n*** attribute $this required for <$tag> ***";
		print "\n rejecting: \\HTML".(($attribs)? "[$attribs]" : '')."{$tag}";
		return($value.$after);
	    }
	}
	$value = &translate_environments($value);
	$value = &translate_commands($value) if ($value =~ /\\/);
	join('', "<$tag", $TAGattribs, ">", $value, $etag, $after);
   } elsif ($needed) {
	print "\n*** attributes $needed are required for <$tag> ***";
	return($value.$after);
    } elsif ($value) {
	$value = &translate_environments($value);
	$value = &translate_commands($value) if ($value =~ /\\/);
	join('', "<$tag>", $value, $etag, $after);
    } else {
	join('', "<$tag>", $etag, $after);
    }
}



####


# Appends $next_def to the preamble if it is not already there.
sub add_to_preamble {
    local($type, $next_def) = @_;

    local($name);
    if ($type =~ /(renew)|(def)|(include)|(special)|(graphicspath)/) {
        local($pat) = &escape_rx_chars ($next_def);
        $preamble .= $next_def . "\n" unless ($preamble =~ /$pat/);
    }
    else {
	($name) = $next_def =~ /$marker\s*({[^}]+})/; # matches type{name}
	$name = &escape_rx_chars($name);
	$preamble .= $next_def . "\n" unless ($preamble =~ /$marker\s*$name/);
    }
}

sub make_latex{
# This is the environment in which to process constructs that cannot be
# translated to HTML.
# The environment tex2html_wrap will be wrapped around any shorthand
# environments (e.g. $, \(, \[).
# The tex2html_wrap environment will be treated as an unrecognised
# evironment by the translator and its contents (i.e. the 'shorthand'
# environment) will be passed to latex for processing as usual.
    local($contents) = @_;
    local($preamble) = $preamble;
    local($preamble_aux) = $preamble_aux;
    # Make the @ character a normal letter ...
    $preamble =~ s/(\\document(class|style)(\[[^\]]+\])?\{\w+\})/$1\n\\makeatletter/;
    # ... and make it special again after the preamble
    # remove the  \begin/\end  for  tex2html_nowrap and tex2html_deferred environments
    $preamble =~s/\\(begin|end)\s*\{(tex2html_(nowrap|deferred|nomath)[_a-z]*|imagesonly)\}//g;
    $preamble = "\\documentclass\{article\}%\\usepackage{html}%\n\\makeatletter"
	unless ($preamble);
    $preamble_aux = '' unless (($preamble_aux)&&($contents =~ /\\(hyper)?(ref|cite)/));
    
    local($paperwidth) = '';
    if ($PAPERSIZE) { $paperwidth = &adjust_textwidth($PAPERSIZE); }
    else { $paperwidth = &adjust_textwidth("a5"); }
    local($kern) = ($EXTRA_IMAGE_SCALE ? $EXTRA_IMAGE_SCALE/2 : ".5" );
    $kern = $kern * $MATH_SCALE_FACTOR;
    ($DEBUG ? "\\nonstopmode" : "\\batchmode") .
    "\n$preamble\n$preamble_aux\\makeatother\n" .
    "\\ifx\\AtBeginDocument\\undefined \\newcommand{\\AtBeginDocument}[1]{}\\fi\n" .
    "\\newenvironment{tex2html_wrap}{}{}\n" .
    "\\newbox\\sizebox\n" . "$paperwidth" .
    "\\newwrite\\lthtmlwrite\n" . "\\makeatletter\n" .
    "\\let\\realnormalsize=\\normalsize\n\\topskip=0pt\n\\def\\preveqno{}" .
    "\\let\\real\@float=\\\@float \\let\\realend\@float=\\end\@float\n" .
    "\\def\\\@float{\\let\\\@savefreelist\\\@freelist\\real\@float}\n" .
#    "\\def\\\@float{\\\@dbflt}\n" .
    "\\def\\end\@float{\\realend\@float\\global\\let\\\@freelist\\\@savefreelist}\n" . 
    "\\let\\real\@dbflt=\\\@dbflt \\let\\end\@dblfloat=\\end\@float\n" .
    "\\let\\\@largefloatcheck=\\relax\n" .
    "\\def\\\@dbflt{\\let\\\@savefreelist\\\@freelist\\real\@dbflt}\n" .
    "\\def\\adjustnormalsize{\\def\\normalsize{\\mathsurround=0pt \\realnormalsize" .
    "\\parindent=0pt\\abovedisplayskip=0pt\\belowdisplayskip=0pt}\\normalsize}\n" .
    "\\def\\lthtmltypeout#1{{\\let\\protect\\string\\immediate\\write\\lthtmlwrite{#1}}}%\n" .
    "\\newcommand\\lthtmlhboxmathA{\\adjustnormalsize\\setbox\\sizebox=\\hbox\\bgroup}%\n" .
    "\\newcommand\\lthtmlvboxmathA{\\adjustnormalsize\\setbox\\sizebox=\\vbox\\bgroup%\n".
    " \\let\\ifinner=\\iffalse }%\n" .
    "\\newcommand\\lthtmlboxmathZ{\\\@next\\next\\\@currlist{}{\\def\\next{\\voidb\@x}}%\n" .
#    " \\expandafter\\box\\next\\edef\\next{\\egroup\\def\\noexpand\\thiseqn{\\theequation}}\\next}%\n" .
    " \\expandafter\\box\\next\\egroup}%\n" .
    "\\newcommand\\lthtmlmathtype[1]{\\def\\lthtmlmathenv{#1}}%\n" .
#    "\\newcommand\\lthtmllogmath{\\lthtmltypeout{latex2htmlSize%\n" .
    "\\newcommand\\lthtmllogmath{\\lthtmltypeout{l2hSize %\n" .
    ":\\lthtmlmathenv:\\the\\ht\\sizebox::\\the\\dp\\sizebox::\\the\\wd\\sizebox.\\preveqno}}%\n" .
    "\\newcommand\\lthtmlfigureA[1]{\\let\\\@savefreelist\\\@freelist
       \\lthtmlmathtype{#1}\\lthtmlvboxmathA}%\n" .
    "\\newcommand\\lthtmlfigureZ{\\lthtmlboxmathZ\\lthtmllogmath\\copy\\sizebox
       \\global\\let\\\@freelist\\\@savefreelist}%\n" .
    "\\newcommand\\lthtmldisplayA[1]{\\lthtmlmathtype{#1}\\lthtmlvboxmathA}%\n" .
    "\\newcommand\\lthtmldisplayB[1]{\\edef\\preveqno{(\\theequation)}%\n" .
    "  \\lthtmldisplayA{#1}\\let\\\@eqnnum\\relax}%\n" .
    "\\newcommand\\lthtmldisplayZ{\\lthtmlboxmathZ\\lthtmllogmath\\lthtmlsetmath}%\n" .
    "\\newcommand\\lthtmlinlinemathA[1]{\\lthtmlmathtype{#1}\\lthtmlhboxmathA" .
    "  \\vrule height1.5ex width0pt }%\n" .
    "\\newcommand\\lthtmlinlinemathZ{\\egroup\\expandafter\\ifdim\\dp\\sizebox>0pt %\n" .
    "  \\expandafter\\centerinlinemath\\fi\\lthtmllogmath\\lthtmlsetmath}\n" .
    "\\def\\lthtmlsetmath{\\hbox{\\vrule width.5pt\\vtop{\\vbox{%\n" .
    "  \\kern.5pt\\kern$kern pt\\hbox{\\hglue.5pt\\copy\\sizebox\\hglue$kern pt}\\kern.5pt%\n" .
    "  \\ifdim\\dp\\sizebox>0pt\\kern$kern pt\\fi}%\n" .
    "  \\ifdim\\hsize>\\wd\\sizebox \\hrule depth1pt\\fi}}}\n" .
    "\\def\\centerinlinemath{\\dimen1=\\ht\\sizebox\n" . 
    "  \\ifdim\\dimen1<\\dp\\sizebox \\ht\\sizebox=\\dp\\sizebox\n" .
    "  \\else \\dp\\sizebox=\\ht\\sizebox \\fi}\n\n" .
    "\\def\\lthtmlcheckvsize{\\ifdim\\ht\\sizebox<\\vsize\\expandafter\\vfill\n" .
    "  \\else\\expandafter\\vss\\fi}%\n" .
#    "\\def\\\@enddocumenthook{\\ifnum\\count0>1 \\ifvoid\\\@cclv\\penalty-\\\@MM\\fi\\fi}\n" .
    "\\makeatletter\n" .
    $LaTeXmacros . "\n" .  # macros defined in extension files
#    "\\usepackage{lthimages}\n" .
    "\n\\begin{document}\n" .
    "\\pagestyle{empty}\\thispagestyle{empty}%\n" .
    "\\lthtmltypeout{latex2htmlLength hsize=\\the\\hsize}%\n" .
    "\\lthtmltypeout{latex2htmlLength vsize=\\the\\vsize}%\n" .
    "\\lthtmltypeout{latex2htmlLength hoffset=\\the\\hoffset}%\n" .
    "\\lthtmltypeout{latex2htmlLength voffset=\\the\\voffset}%\n" .
    "\\lthtmltypeout{latex2htmlLength topmargin=\\the\\topmargin}%\n" .
    "\\lthtmltypeout{latex2htmlLength topskip=\\the\\topskip}%\n" .
    "\\lthtmltypeout{latex2htmlLength headheight=\\the\\headheight}%\n" .
    "\\lthtmltypeout{latex2htmlLength headsep=\\the\\headsep}%\n" .
    "\\lthtmltypeout{latex2htmlLength parskip=\\the\\parskip}%\n" .
    "\\lthtmltypeout{latex2htmlLength oddsidemargin=\\the\\oddsidemargin}%\n" .
    "\\makeatletter\n" .
    "\\if\@twoside\\lthtmltypeout{latex2htmlLength evensidemargin=\\the\\evensidemargin}%\n" .
    "\\else\\lthtmltypeout{latex2htmlLength evensidemargin=\\the\\oddsidemargin}\\fi%\n" .
    "\\makeatother\n" .
    "$contents\n".
#    "\\clearpage\n" .
    "\\end{document}";
}

sub adjust_textwidth {
    local($_) = @_;
    local($width,$length) = ('','');
    if (/a4/) {$width = 595; $length= 842; }
    elsif (/letter/) {$width = 612; $length= 792; }
    elsif (/legal/) {$width = 612; $length= 1008; }
    elsif (/note/) {$width = 540; $length= 720; }
    elsif (/b5/) {$width = 501; $length= 709; }
    elsif (/a5/) {$width = 421; $length= 595; }
    elsif (/a6/) {$width = 297; $length= 421; }
    elsif (/a7/) {$width = 210; $length= 297; }
    elsif (/a8/) {$width = 148; $length= 210; }
    elsif (/a9/) {$width = 105; $length= 148; }
    elsif (/a10/) {$width = 74; $length= 105; }
    elsif (/b4/) {$width = 709; $length= 1002; }
    elsif (/a3/) {$width = 842; $length= 1190; }
    elsif (/b3/) {$width = 1002; $length= 1418; }
    elsif (/a2/) {$width = 1190; $length= 1684; }
    elsif (/b2/) {$width = 1418; $length= 2004; }
    elsif (/a1/) {$width = 1684; $length= 2380; }
    elsif (/b1/) {$width = 2004; $length= 2836; }
    elsif (/a0/) {$width = 2380; $length= 3368; }
    elsif (/b0/) {$width = 2836; $length= 4013; }
    else {
	&write_warnings("\nPAPERSIZE: $_ unknown, using LaTeX's size.");
	return();
     }
    if ($width > 500) { $width = $width - 144; $length = $length - 288; }
    elsif ($width > 250) { $width = $width - 72; $length = $length - 144; }
    elsif ($width > 125) { $width = $width - 36; $length = $length - 72; }
#    "\\setlength{\\oddsidemargin}{0pt}\n" .
#    "\\setlength{\\evensidemargin}{0pt}\n" .
#    "\\setlength{\\parskip}{0pt}\\setlength{\\topskip}{0pt}\n" .
    "\\setlength{\\hoffset}{0pt}\\setlength{\\voffset}{0pt}\n" .
    "\\addtolength{\\textheight}{\\footskip}\\setlength{\\footskip}{0pt}\n" .
    "\\addtolength{\\textheight}{\\topmargin}\\setlength{\\topmargin}{0pt}\n" .
    "\\addtolength{\\textheight}{\\headheight}\\setlength{\\headheight}{0pt}\n" .
    "\\addtolength{\\textheight}{\\headsep}\\setlength{\\headsep}{0pt}\n" .
    "\\setlength{\\textwidth}{${width}pt}\n"
}

# Given the depth of the current sectioning declaration and the current
# section numbers it returns the new section numbers.
# It increments the $depth-ieth element of the @curr_sec_id list and
# 0's the elements after the $depth-ieth element.
sub new_level {
    local($depth, @curr_sec_id) = @_;
    local($i) = 0;
    grep( do { if ($i == $depth) {$_++ ;}
	       elsif ($i > $depth) {$_ = 0 ;};
	       $i++;
	       0;
	   },
	 @curr_sec_id);
    @curr_sec_id;
}

sub make_head_and_body {
    local($title,$body) = @_;
    local($DTDcomment) = '';
    local($version,$isolanguage) = ($HTML_VERSION, 'EN');
    local(%isolanguages) = ('english',	'EN',	'USenglish', 'EN.US',
			    'original',	'EN',	'german',    'DE',
			    'austrian',	'DE.AT','french',    'FR');
    $isolanguage = $isolanguages{$default_language};
    $isolanguage = 'EN' unless $isolanguage;
#JCL(jcl-tcl)
# clean title as necessary
# the first words ... is a kludge, but reasonable (or not?) 
#RRM: why bother? --- as long as it is pure text.
    $title = &purify($title,1);
#    $title = &get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
    $DTDcomment = "<!DOCTYPE HTML PUBLIC \"$DOCTYPE//$isolanguage\">\n";

    join('', ($DOCTYPE ? $DTDcomment : '' )
	,"<!--Converted with LaTeX2HTML $TEX2HTMLVERSION\n by "
	,"Nikos Drakos (nikos\@cbl.leeds.ac.uk), CBLU, University of Leeds"
	, "\n* revised and updated by:  Marcus Hennecke, Ross Moore, Herb Swan"
        , "\n* with significant contributions from:"
        , "\n  Jens Lippman, Marek Rouchal, Martin Wilck and others"
	, " -->\n<HTML>\n<HEAD>\n<TITLE>", $title, "</TITLE>\n"
	, &meta_information($title)
	,  ($CHARSET && $HTML_VERSION ge "2.1" ? 
	      "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=$CHARSET\">\n" 
	      : "" )
	, ($BASE ? "<BASE HREF=\"$BASE\">\n" : "" )
#	, "<LINK REL=\"STYLESHEET\" TYPE=\"text/css\" HREF=\"$FILE.css\">", $more_links_mark
	, "<LINK REL=\"STYLESHEET\" HREF=\"$FILE.css\">", $more_links_mark
	, "\n</HEAD>\n<BODY $body>\n");
}


sub style_sheet {
    do{
	open(STYLESHEET, ">$FILE.css");
	print STYLESHEET "
SMALL.TINY		{ font-size : xx-small }
SMALL.SCRIPTSIZE	{ font-size : xx-small }
SMALL.FOOTNOTESIZE	{ font-size : x-small  }
SMALL.SMALL		{ font-size : small    }
BIG.LARGE		{ font-size : large    }
BIG.XLARGE		{ font-size : x-large  }
BIG.XXLARGE		{ font-size : xx-large }
BIG.HUGE		{ font-size : xx-large }
BIG.XHUGE		{ font-size : xx-large }
";
	close(STYLESHEET);
    #AXR:  don't overwrite existing .css
    }  unless -f "$FILE.css";
}

sub make_address {
    local($_) = $ADDRESS;
    if ((defined &custom_address)&&($_)) {
	join('', &custom_address($_) , "\n</BODY>\n</HTML>\n");
    } elsif($_) {
	"<ADDRESS>\n$_\n</ADDRESS>" . "\n</BODY>\n</HTML>\n";
    } else {
	"\n</BODY>\n</HTML>\n"
    }
}


sub encode_title {
    local($_) = @_;
    $_ = &encode($_);
    while (/(<[^<>]*>)/o) {s/$1//g}; # Remove HTML tags
    s/#[^#]*#//g;               # Remove #-delimited markers
    $_;
}

# Encodes the contents of enviroments that are passed to latex. The code
# is then used as key to a hash table pointing to the URL of the resulting
# picture.
sub encode {
    local($_) = @_;
    for (;;) {
	# Remove invocation-specific stuff
        last unless s/begin|end|<<\d+>>|tex2html_|wrap//go;
    }
    $_ = &revert_to_raw_tex($_);
    #$_ = pack("u*", $_);	# uuencode
    s/\$|\/|\\//g;		# remove funnies may cause problems in a hash key
    s/\s*|\n//g;		# Remove spaces  and newlines
    s/^(.{80}).*(.{80})$/$1$2/;		# truncate to avoid DBM problems
    $_;
}


##################### Hypertext Section Links ########################
sub post_process {
    # Put hyperlinks between sections, add HTML headers and addresses,
    # do cross references and citations.
    # Uses the %section_info array created in sub translate.
    # Binds the global variables
    # $PREVIOUS, $PREVIOUS_TITLE
    # $NEXT, $NEXT_TITLE
    # $UP, $UP_TITLE
    # $CONTENTS
    # $INDEX
    # $NEXT_GROUP, $NEXT_GROUP_TITLE
    # $PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE
    # Converting to and from lists and strings is very inefficient.
    # Maybe proper lists of lists should be used (or wait for Perl5?)
    # JKR:  Now using top_navigation and bot_navigation instead of navigation
    local($_, $key, $depth, $file, $title, $header, @link, @old_link,
	  $top_navigation, $bot_navigation, @keys,
	  @tmp_keys, $flag, $child_links, $body, $more_links);
    @tmp_keys = @keys = sort numerically keys %section_info;
    print "\nDoing section links ...";
    while (@tmp_keys) {
        $key = shift @tmp_keys;
	print ".";
	print STDERR "\n$key" if ($VERBOSITY > 3);
	$more_links = "";
	($depth, $file, $title, $body) = split($delim,$section_info{$key});
	$PREVIOUS = $PREVIOUS_TITLE = $NEXT = $NEXT_TITLE = $UP = $UP_TITLE =
	    $CONTENTS = $INDEX = $NEXT_GROUP = $NEXT_GROUP_TITLE =
		$PREVIOUS_GROUP = $PREVIOUS_GROUP_TITLE =
		    $_ = $top_navigation = $bot_navigation = undef;
	&add_link_tag($file,'previous');
	@link =  split(' ',$key);
        ($PREVIOUS, $PREVIOUS_TITLE) =
	    &add_link($previous_page_visible_mark,$file,@old_link);
	@old_link = @link;
	unless ($done{$file}) {
	    $link[$depth]++;
	    ($NEXT_GROUP, $NEXT_GROUP_TITLE) =
		&add_link($next_visible_mark, $file, @link);
	    &add_link_tag('next', $file, @link);

	    $link[$depth]--;$link[$depth]--;
	    ($PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE) =
		&add_link($previous_visible_mark, $file,@link);
	    &add_link_tag('previous', $file,@link);

	    $link[$depth] = 0;
	    ($UP, $UP_TITLE) =
		&add_link($up_visible_mark, $file, @link);
	    &add_link_tag('up', $file, @link);

	    if ($CONTENTS_IN_NAVIGATION) {
	        $CONTENTS = &add_special_link($contents_visible_mark, $tocfile, $file);
		&add_link_tag('contents', $file, $delim.$tocfile);
	    }

	    if ($INDEX_IN_NAVIGATION) {
	        $INDEX = &add_special_link($index_visible_mark, $idxfile, $file);
		&add_link_tag('index', $file, $delim.$idxfile,);
	    }

	    @link = split(' ',$tmp_keys[0]);
	    # the required `next' link may be several sub-sections along
	    local($nextdepth,$nextfile,$nextkey)=($depth,$file,$key);
	    $nextkey = shift @tmp_keys;
	    ($nextdepth, $nextfile) = split($delim,$section_info{$nextkey});
	    if ($nextdepth<$MAX_SPLIT_DEPTH) {
		($NEXT, $NEXT_TITLE) =
		    &add_link($next_page_visible_mark, $file, @link);
		&add_link_tag('next', $file, @link);
	    } else {
		# the required `next' link may be several sub-sections along
		while ((@tmp_keys)&&($MAX_SPLIT_DEPTH < $nextdepth+1 )) {
		    $nextkey = shift @tmp_keys;
		    ($nextdepth, $nextfile) = split($delim,$section_info{$nextkey});
		    print ",";
		    print STDERR "\n$nextkey" if ($VERBOSITY > 3);
		}
		@link = split(' ',$nextkey);
		if (($nextkey)&&($nextdepth<$MAX_SPLIT_DEPTH)) {
		    ($NEXT, $NEXT_TITLE) =
			&add_link($next_page_visible_mark, $file, @link);
		    &add_link_tag('next', $file, @link);
		} else {
		    ($NEXT, $NEXT_TITLE) = ($NEXT_GROUP, $NEXT_GROUP_TITLE);
		    $NEXT =~ s/next_(inactive_)?visible_mark/next_page_$1visible_mark/;
		    ($PREVIOUS, $PREVIOUS_TITLE) = ($PREVIOUS_GROUP, $PREVIOUS_GROUP_TITLE);
		    $PREVIOUS =~ s/previous_(inactive_)?visible_mark/previous_page_$1visible_mark/;
		}
	    }
	    unshift (@tmp_keys,$nextkey) if ($nextkey);
#
            $top_navigation = (defined(&top_navigation_panel) ?
			       &top_navigation_panel : &navigation_panel)
		unless $NO_NAVIGATION;
            $bot_navigation = (defined(&bot_navigation_panel) ?
			       &bot_navigation_panel : &navigation_panel)
		unless $NO_NAVIGATION;
	    local($end_navigation)="\n<!--End of Navigation Panel-->\n";

	    $header = &make_head_and_body($title, $body);
      	    $header = join('', $header, $top_navigation, $end_navigation) if ($top_navigation);
	    &slurp_input($file);
	    open(OUTFILE, ">$file") || die "Cannot open file $file $!";
	    if (($INDEX) && ($SHORT_INDEX) && ($SEGMENT eq 1)) {
		&make_index_segment($title,$file); }

	    local($child_star);
	    if (/$childlinks_mark\#(\d)\#/) { $child_star = $1 }
	    $child_links = &add_child_links('',$depth, $child_star,$key, @keys);    
	    if (($child_links)&&(!/$childlinks_mark/)&&($MAX_SPLIT_DEPTH > 1)) {
		if ($depth < $MAX_SPLIT_DEPTH -1) {
	            $_ = join('', $header, $_, &child_line(), $childlinks_mark, "\#0\#" );
		} else {
	            $_ = join('', $header, "\n$childlinks_mark\#0\#", &upper_child_line(), $_ );
		}
	    } else {
	        $_ = join('', $header, $_ );
	    }
	    $flag = (($BOTTOM_NAVIGATION || &auto_navigation) && $bot_navigation);
	    $_ = join('', $_, $bot_navigation, $end_navigation) if ($flag &&($bot_navigation));
	    $_ = join('', $_, &child_line()) unless $flag;
	    print STDERR "\n *** replace markers *** " if ($VERBOSITY > 1);
	    &replace_markers;
	    print STDERR "\n *** post-post-process *** " if ($VERBOSITY > 1);
	    &post_post_process if (defined &post_post_process);
	    print OUTFILE $_;
	    print OUTFILE &make_address;
	    close OUTFILE;
	    $done{$file}++;
	}
    }
    &post_process_footnotes if ($footfile);
}

sub post_replace_markers {
    $* = 1;			# Multiline matching ON  
    # clean up starts and ends of  P, BR and DIV tags
    s/(<\/?(P|BR|DIV)>)\s*(\w)/$1\n$3/go unless ($file eq $citefile);
    s/([^\s])(<(BR|DIV))/$1\n$2/go unless ($file eq $citefile);
    local($keep,$after);

    # anchor images when otherwise there is an invisible-anchor
    s/(<A[^>]*>)\&\#160;<\/A>\s?(<(P|DIV)[^>]*>)\s*(<IMG[^>]*>)\s*(<\/(P|DIV)>)/
	do{ $keep="$2$1$4<\/A>"; 
	    $after = &after_punct_break($5);
	    $keep.$after;
	    } /ego;

    # absorb named anchor (e.g. from index-entry) into preceding or following anchor
#    s/(<A NAME=\"[^\"]+\")>\&#160;<\/A>\s*\b?<A( HREF=\"[^\"]+\">)/$1$2/go;
#    s/(<A HREF=\"[^\"]+\")(>\s*\b?([^<]+|<([^>\/]+|\/[^>A]+)>\s*)*<\/A>)\s*\b?<A( NAME=\"[^\"]+\")>\&#160;<\/A>/$1$5$2/go;

    # clean up empty table cells
    s/(<TD[^>]*>)\s*(<\/TD>)/<TD>$2/go;

    # clean up list items (only desirable in the bibliography ?)
    # s/\n<P>(<DT[^>]*>)/\n<P><\/P>\n$1/go;

    # remove blank lines
#    s/\n\n/\n/g;  # no, cause this kills intended ones in verbatims

    # italic \LaTeX looks bad
    s/<(I|EM)>(($Laname|$AmSname)?$TeXname)<\/\1>/$2/g;

    $* = 0;			# Multiline matching OFF
}

sub after_punct_break {
    local($_) = @_;
    s/^\s*([,;\.\)\!\"\'\?][ \t]*)?\n?/$1/o;
    $_;
}

sub make_index_segment {
    local($title,$file)= @_ ;
#JCL(jcl-tcl)
#    s/<[^>]*>//g;
#
    $index_segment{$PREFIX} = "$title";
    if (!($ref_files{"segment"."$PREFIX"} eq "$file")) {
	$ref_files{"segment"."$PREFIX"} = "$file";
	$changed = 1
    }
    $SEGMENT = 2;
}


sub add_link {
    # Returns a pair (iconic link, textual link)
    local($icon, $current_file, @link) = @_;
    local($dummy, $file, $title) = split($delim,$toc_section_info{join(' ',@link)});
#    if ($title && ($file ne $current_file || $icon ne $up_visible_mark)) {
    if ($title && ($file ne $current_file)) {

#JCL(jcl-tcl)
#    s/<[^>]*>//g;
#
        $title = &purify($title);
	$title = &get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
	return ("\n".&make_href($file, $icon), &make_href($file, "$title"))
	}
#    elsif ($icon eq $up_visible_mark && $file eq $current_file && $EXTERNAL_UP_LINK) {
    elsif ($icon eq $up_visible_mark && $EXTERNAL_UP_LINK) {
 	return ("\n".&make_href($EXTERNAL_UP_LINK, $icon),
		&make_href($EXTERNAL_UP_LINK, "$EXTERNAL_UP_TITLE"))
	}
    elsif (($icon eq $previous_visible_mark || $icon eq $previous_page_visible_mark) &&
	   $EXTERNAL_PREV_LINK && $EXTERNAL_PREV_TITLE) {
	return ("\n".&make_href($EXTERNAL_PREV_LINK, $icon),
		&make_href($EXTERNAL_PREV_LINK, "$EXTERNAL_PREV_TITLE"))
	}
    elsif (($icon eq $next_visible_mark ||  $icon eq $next_page_visible_mark) &&
	   $EXTERNAL_DOWN_LINK && $EXTERNAL_DOWN_TITLE) {
	return ("\n".&make_href($EXTERNAL_DOWN_LINK, $icon),
		&make_href($EXTERNAL_DOWN_LINK, "$EXTERNAL_DOWN_TITLE"))
	}
    (&inactive_img($icon), "");
}

sub add_special_link {
    local($icon, $file, $current_file) = @_;
    (($file && ($file ne $current_file)) ? "\n".&make_href($file, $icon) : undef)
}

#RRM: add <LINK ...> tag to the HTML head.
#     suggested by Marcus Hennecke
#
sub add_link_tag {
    local($rel, $currentfile, @link ) = @_;
    local($dummy, $file, $title) = split($delim,$toc_section_info{join(' ',@link)});
    if ($file && !($file eq $currentfile) && (!$NO_NAVIGATION)) {
        $more_links .= "\n<LINK REL=\"$rel\" HREF=\"$file\">";
    }
}

sub remove_markers {
# modifies $_
    s/$lof_mark//go;
    s/$lot_mark//go;
    &remove_bbl_marks;
    s/$toc_mark//go;
    s/$idx_mark//go;
    &remove_cross_ref_marks;
    &remove_external_ref_marks;
    &remove_cite_marks;
# sensitive markers
    &remove_image_marks;
    &remove_icon_marks;
    &remove_verbatim_marks;
    &remove_verb_marks;
# uncatched markers
    s/$percent_mark/%/go;
    s/<tex2html[^>]*>//g;
}

sub replace_markers {
    &replace_general_markers;
    &text_cleanup;
    # Must NOT clean the ~'s out of the navigation icons (in panel or text),
    # and must not interfere with verbatim-like environments
    &replace_sensitive_markers;
    &post_replace_markers;
}

sub replace_general_markers {
    if ($segment_figure_captions) {
	s/$lof_mark/<UL>$segment_figure_captions<\/UL>/o
    } else { s/$lof_mark/<UL>$figure_captions<\/UL>/o }
    if ($segment_table_captions) {
	s/$lot_mark/<UL>$segment_table_captions<\/UL>/o
    } else { s/$lot_mark/<UL>$table_captions<\/UL>/o }
    &replace_morelinks();
    if (defined &replace_citations_hook) {&replace_citations_hook;}
    else {&replace_bbl_marks if /$bbl_mark/;}
    if (defined &add_toc_hook) {&add_toc_hook;}
    else {&add_toc if (/$toc_mark/);}
    if (defined &add_childs_hook) {&add_childs_hook;}
    else {&add_childlinks if (/$childlinks_mark/);}
    if (defined &add_idx_hook) {&add_idx_hook;}
    else {&add_idx if (/$idx_mark/);}
    if (defined &replace_cross_references_hook) {&replace_cross_references_hook;}
    else {&replace_cross_ref_marks if /$cross_ref_mark||$cross_ref_visible_mark/;}
    if (defined &replace_external_references_hook) {&replace_external_references_hook;}
    else {&replace_external_ref_marks if /$external_ref_mark/;}
    if (defined &replace_cite_references_hook) {&replace_cite_references_hook;}
    else { &replace_cite_marks if /$cite_mark/; }
    if (defined &replace_user_references) {
 	&replace_user_references if /$user_ref_mark/; }
    if (defined &replace_infopage_hook) {&replace_infopage_hook;}
    else { &replace_infopage if (/$info_page_mark/); }
}

sub replace_sensitive_markers {
    if (defined &replace_images_hook) {&replace_images_hook;}
    else {&replace_image_marks if /$image_mark/;}
    if (defined &replace_icons_hook) {&replace_icons_hook;}
    else {&replace_icon_marks if /$icon_mark_rx/;}
    if (defined &replace_verbatim_hook) {&replace_verbatim_hook;}
    else {&replace_verbatim_marks if /$verbatim_mark/;}
    if (defined &replace_verb_hook) {&replace_verb_hook;}
    else {&replace_verb_marks if /$verb_mark/;}
    s/;SPM/\&/go;
    s/$percent_mark/%/go;
    #JKR: Turn encoded ~ back to normal
    s/&#126;/~/go;
}

sub add_childlinks {
    local($before, $after, $star);
    while (/$childlinks_mark\#(\d)\#/) {
	$star = $1;
	$before = $`;
	$after = $';
	$before =~ s/\n\s*$//;
	$_ = join('', $before, "\n", $child_links, $after);
    }
}

sub replace_infopage {
    local($INFO)=1 if !($INFO);
    $_ =~ s/$info_title_mark/"\n<H2>$info_title<\/H2>"/eog if ($INFO==1);
    while (/$info_page_mark/o) {
	$_ = join('', $`, &do_cmd_textohtmlinfopage, $');
    }
}

sub replace_morelinks {
    $_ =~ s/$more_links_mark/$more_links/e;
}

# This code is extremely inefficient. At least the subtrees should be
# filtered according to $MAX_LINK_DEPTH before going into the
# inner loops.
# RRM: revamped parts, for $TOC_STARS, fixing some errors.
#
sub add_child_links {
    local($exclude, $depth, $star, $current_key, @keys) = @_;
    if ((!$depth)&&($outermost_level)) {
	$depth = $section_commands{$outermost_level} - 1;
    }
    local($_, $child_rx, @subtree, $next, %open);
    local($first, $what, $pre);
    $childlinks_start = "<!--Table of Child-Links-->";
    $childlinks_end = "<!--End of Table of Child-Links-->\n";
    $child_rx = $current_key;
    $child_rx =~ s/( 0)*$//;	# Remove trailing 0's
    if ((! $exclude)&&($depth < $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH -1 )
	 &&($depth >= $MAX_SPLIT_DEPTH-1)) {
	$what = "<strong>Subsections</strong>";
	$first = "$childlinks_start\n<A NAME=\"CHILD_LINKS\">$what<\/A>\n";
    } elsif ($exclude) { # Table-of-Contents
	$childlinks_start = "\n<!--Table of Contents-->\n";
	$childlinks_end = "<!--End of Table of Contents-->";
	$first = "$childlinks_start";
#    } elsif ($CHILD_STAR) {
    } elsif ($star) {
	$first = "$childlinks_start\n";
    } else {
	$first = "$childlinks_start\n<A NAME=\"CHILD_LINKS\">$anchor_mark<\/A>\n";
    }

    # collect the keys which start as $current_key
    foreach $next (@keys) {
	if (($next =~ /^$child_rx /) && ($next ne $current_key)) {
	    push(@subtree,$next);
	}
    }
    # @subtree now contains the subtree rooted at the current node
    if (@subtree) {
	local($next_depth, $file, $title, $star, $ldepth);
	$ldepth = $depth;
	@subtree = sort numerically @subtree;
	foreach $next (@subtree) {
	    ($next_depth, $file, $title, $star) = split($delim,$toc_section_info{$next});
#	    ($next_depth, $file, $title, $star) = split($delim,$section_info{$next})
#	        unless ($title);
	    $title = "\n".$title if !($title =~/^\n/);
	    next if (($exclude)&&(				# doing Table-of-Contents
		(($TOC_DEPTH)&&($next_depth > $TOC_DEPTH))	# and  too deep
		||(($star)&&(!$TOC_STARS)) ));			# or no starred sections 
	    $file = "" if (!$MAX_SPLIT_DEPTH); # Martin Wilck
	    next if ($exclude eq $title);
	    $file = join('', $file, "#SECTION", split(' ', $next));
            if (($next_depth > $ldepth) && (
		(split('/',%open))[0] < $MAX_LINK_DEPTH + $depth)) {
	    # start a new <UL> list
		$ldepth = $next_depth;
		if ($first) {
		    $_ .= "$first<UL>\n";
		    local $i = 1;
		    while ($i < $ldepth) { $open{$i}=0; $i++ }
		    $first = '';	# include NAME tag first time only
		} else {
		    $_ .= "<UL>\n";
		}
		$open{$ldepth}++; 
	    # append item to this list
		$_ .= "<LI>". &make_href($file,$title) . "\n";
	    }
            elsif (($next_depth)&&($next_depth <= $ldepth) && (
		(split('/',%open))[0] <= $MAX_LINK_DEPTH + $depth )) {
	    # append item to existing <UL> list
		while (($next_depth < $ldepth) && %open ) {
		# ...closing-off any nested <UL> lists
		    if ($open{$ldepth}) {
			if (! $open{$next_depth})  {
			    $open{$next_depth}++;
			} else {
			    $_ .= "</UL>\n";
			}
			delete $open{$ldepth};
		    };
		    $ldepth--;
		}
		$ldepth = $next_depth;
		$_ .= "<LI>". &make_href($file,$title) . "\n"
	    } else {
	    # ignore items that are deeper than $MAX_LINK_DEPTH
	    }
	}

	if (%open) {
	# close-off any remaining <UL> lists
	    local $cnt = (split('/',%open))[0];
	    local $i = $cnt;
		while ($i > $depth) { 
		    if ($open{$i}) {
			$_ .= "</UL>";
			delete $open{$i};
		    }
		$i-- }
	}
    }
    $_ = join('', $_, "\n$childlinks_end") if ($_);
    $_;
}

sub child_line {($CHILDLINE) ? "$CHILDLINE" : "<BR>\n<HR>";}
sub upper_child_line { "<HR>\n"; }

sub top_page {
    local($file, @navigation_panel) = @_;
    # It is the top page if there is a link to itself
    join('', @navigation_panel) =~ /$file/;
}

# Sets global variable $AUX_FILE
sub process_aux_file {
    local($_, $status);		# To protect caller from &process_ext_file
    $AUX_FILE = 1;
    foreach $auxfile ("aux", "lof", "lot") {
	$status = &process_ext_file($auxfile);
	if ($auxfile eq "aux" && ! $status) {
	    print "\nCannot open $FILE.aux $!\n";
	    &write_warnings("\nThe $FILE.aux file was not found," .
			    " so sections will not be numbered \nand cross-references "
			    . "will be shown as icons.\n");
	}
    }
    $AUX_FILE = 0;
}

sub make_href {
    local($link, $text) = @_;
    $name++;
    #HWS: Nested anchors not allowed.
    $text =~ s/<A .*><\/A>//go;
    #JKR: ~ is handled different - &#126; is turned to ~ later.
    #$link =~ s/&#126;/$percent_mark . "7E"/geo;
    if ($text eq $link) { $text =~ s/~/&#126;/g; }
    $link =~ s/~/&#126;/g;
    "<A NAME=\"tex2html$name\"\n HREF=\"$link\">$text</A>";
}

sub make_named_href {
    local($name, $link, $text) = @_;
    $text =~ s/<A .*><\/A>//go;
    $text = &simplify(&translate_commands($text)) if ($text =~ /\\/);
    if ($text eq $link) { $text =~ s/~/&#126;/g; }
    $link =~ s/~/&#126;/g;
    $text = &simplify($text);
    if (!($name)) {"<A\n HREF=\"$link\">$text</A>";}
    elsif ($text =~ /^\w/) {"<A NAME=\"$name\"\n HREF=\"$link\">$text</A>";}
    else {"<A NAME=\"$name\"\n HREF=\"$link\">$text</A>";}
}

sub make_section_heading {
    local($text, $level, $anchors) = @_;
    local($section_tag) = join('', @curr_sec_id);
    local($align,$pre_anchors);
    # separate any invisible anchors or alignment, if this has not already been done
    if (!($anchors)){ ($anchors,$text) = &extract_anchors($text) }
    else { $anchors =~ s/(ALIGN=\"\w*\")/$align = " $1";''/e }
    if (!($text)) {
	# anchor to a single `.' only
	$text = "<A NAME=\"SECTION$section_tag\">.</A>$anchors\n";
    } elsif ($anchors) {
	# put anchors immediately after, except if title is too long
	if ((length($text)<60 )&&(!($align)||($align =~/left/))) {
	    $text = "<A NAME=\"SECTION$section_tag\">$text</A>\n" . $anchors;
	# ...put anchors preceding the title, on a separate when left-aligned
	} else {
	    $text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A>$anchors"
		. (!($align)||($align =~ /left/i ) ? "<BR>" : "") . "\n". $text;
	}
    } elsif (!($text =~ /<A[^\w]/io)) {
	# no embedded anchors, so anchor it all
	$text = "<A NAME=\"SECTION$section_tag\">\n" . $text . "</A>";
    } else {
	# there are embedded anchors; these cannot be nested
	local ($tmp) = $text;
	$tmp =~ s/<//o ;	# find 1st <
	if ($`) {		# anchor text before the first < 
#	    $text = "<A NAME=\"SECTION$section_tag\">\n" . $` . "</A>\n<" . $';
	    $text = "<A NAME=\"SECTION$section_tag\">\n" . $` . "</A>";
	    $pre_anchors = "<" . $';
            if ($pre_anchors =~ /^(<A NAME=\"[^\"]+>${anchor_invisible_mark}<\/A>\s*)+$/) {
                $pre_anchors .= "\n"
            } else { $text .= $pre_anchor; $pre_anchor = '' }
	} else {
	    # $text starts with a tag
	    local($after,$tmp) = ($','');
	    if ( $after =~ /^A[^\w]/i ) {	
		# it is an anchor already, so need a separate line
		$text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A><BR>\n$text";
	    } else {
		# Is it a tag enclosing the anchor ?
		$after =~ s/^(\w)*[\s|>]/$tmp = $1;''/eo;
		if ($after =~ /<A.*<\/$tmp>/) {
		    # it encloses an anchor, so use anchor_mark + break
		    $text = "<A NAME=\"SECTION$section_tag\">$anchor_invisible_mark</A><BR>\n$text";
		} else {
		    # take up to the anchor
		    $text =~ s/^(.*)<A([^\w])/"<A NAME=\"SECTION$section_tag\">$1<A$2"/oe;
		}
	    }
	}
    }
    "$pre_anchors<$level$align>$text\n<\/$level>";
}

sub extract_captions {
    # Uses and modifies $contents and $cap_anchors, defined in translate_environments
    # and modifies $figure_captions, $table_captions, $before and $after
    local($env) = @_;
    local(%captions, %optional_captions, $key, $caption, $optional_caption,
	  $item, $type, $list, $extra_list, $number, @tmp, $_);
    # associate the br_id of the caption with the argument of the caption
    $contents =~ s/$caption_rx(\n)?/do {
	$optional_captions{$8} = ($2 ? $2 : $9);
	$captions{$8} = $9; ''}/ego;
#    $after = join("","<P>",$after) if ($&);
#    $before .= "</P>" if ($&);
    #JKR: Replaced "Figure" and "Table" with variables (see latex2html.config too).
    if ($env eq 'figure') {
	$type = $fig_name;
	$list = "\$figure_captions";
	$extra_list = "\$segment_figure_captions" if ($figure_table_captions);
    }
    elsif ($env eq 'table') {
	$type = $tab_name;
	$list = "\$table_captions";
	$extra_list = "\$segment_table_captions" if ($segment_table_captions);
    }

    $captions = "";
    $cap_anchors = "";
    foreach $key (sort {$a <=> $b;} keys %captions){ # Sort numerically
	$caption =
	    &translate_commands(&translate_environments($captions{$key}));
	$optional_caption =
	    &translate_commands(&translate_environments($optional_captions{$key}));
#	$item .= "\n<LI>" .
#	    &make_href("$CURRENT_FILE#$key", $optional_caption);
#	$before .= "<A NAME=\"$key\">$anchor_mark</A>";
	$cap_anchors .= "<A NAME=\"$key\">$anchor_mark</A>";
	$_ = ($optional_caption || $caption);
	local($pre_anchor,$post_anchor) = ('','');
	if (/</){
	    $pre_anchor = "$`";
	    $post_anchor = "$&$'";
            $pre_anchor = $anchor_invisible_mark unless ($pre_anchor);
	} else {
            $pre_anchor = "$_";
        }
	$item = &make_href("$CURRENT_FILE#$key", $pre_anchor) . $post_anchor;
#JCL(jcl-tcl)
##	&text_cleanup;
##	$_ = &encode_title($_);
##	s/&nbsp;//g;            # HWS - LaTeX changes ~ in its .aux files
#	$_ = &sanitize($_);
##
#RRM: resolve any embedded cross-references first
	$_ = &simplify($_);
	$_ = &sanitize($_);

	@tmp = split(/$;/, eval ("\$encoded_$env" . "_number{\$_}"));
	$number = shift(@tmp);
	$number = "" if ($number eq "-1");
	&write_warnings(qq|\nNo number for "$_"|) if (! $number);
	eval("\$encoded_$env" . "_number{\$_} = join(\$;, \@tmp)");
	undef $_;
	undef @tmp;

	$captions = join("", "<STRONG>$type",
		($number ? " $number:" : ":"),
		"</STRONG>\n$caption",
		(($captions) ? "\n" : "" ), $captions);
    }
    eval "$extra_list .= \"\n<LI>\" .\$item" if ($extra_list);
    eval "$list .= \"\n<LI>\" .\$item";
}


# This processes \label commands found in environments that will
# be handed over to Latex. Sets the table %symbolic_labels
sub do_labels {
    local($context,$new_context) = @_;
    local($label);
    $* = 1;			# Multiline matching ON
    $context =~ s/\s*$labels_rx/do {
	$label = &do_labels_helper($2);
	$new_context = &anchor_label($label,$CURRENT_FILE,$new_context);""}/geo;
    $* = 0;			# Multiline matching OFF
    $new_context;
}

sub extract_labels {
    local($_) = @_;
    local($label,$anchors);
    $* = 1;                     # Multiline matching ON
    while (s/\s*$labels_rx//o) {
        $label = &do_labels_helper($2);
        $anchors .= &anchor_label($label,$CURRENT_FILE,'');
    };
    $* = 0;                     # Multiline matching OFF
    ($_, $anchors);
}

# This should be done inside the substitution but it doesn't work ...
sub do_labels_helper {
    local($_) = @_;
    s/$label_rx/_/g;  # replace non-alphanumeric characters
    $symbolic_labels{$_} = $latex_labels{$_}; # May be empty;
    $_;
}

sub add_toc {
    local($temp1, $temp2);
    print "\nDoing table of contents ...";
    local(@keys) = keys %toc_section_info;
    @keys = sort numerically @keys;
    $temp1 = $MAX_LINK_DEPTH; $temp2 = $MAX_SPLIT_DEPTH;
    $MAX_SPLIT_DEPTH = $MAX_LINK_DEPTH = 1000;
    #JKR: Here was a "Contents" - replaced it with $toc_title
    s/$toc_mark/&add_child_links($toc_title,0,1,$keys[0],@keys)/eo;
    $MAX_LINK_DEPTH = $temp1; $MAX_SPLIT_DEPTH = $temp2;
}

# Assign ref value, but postpone naming the label
sub make_half_href {
    local($link) = $_[0];
    $name++;
    "<A NAME=\"tex2html$name\"\n HREF=\"$link\">";
}


# Redefined in makeidx.perl
sub add_idx {
    print "\nDoing the index ...";
    local($key, $str, @keys, $index, $level, $count,
	  @previous, @current);
    @keys = keys %index;
    @keys = sort keysort  @keys;
    $level = 0;
    foreach $key (@keys) {
	@current = split(/!/, $key);
	$count = 0;
	while ($current[$count] eq $previous[$count]) {
	    $count++;
	}
	while ($count > $level) {
	    $index .= "\n<DL COMPACT>";
	    $level++;
	}
	while ($count < $level) {
	    $index .= "\n</DL>";
	    $level--;
	}
	foreach $term (@current[$count .. $#current-1]) {
	    # need to "step in" a little
#	    $index .= "<DT>" . $term . "\n<DL COMPACT>";
	    $index .= "\n<DT><strong>" . $term . "</strong>\n<DD><DL COMPACT>";
	    $level++;
	}
	$str = $current[$#current];
	$str =~ s/\#\#\#\d+$//o; # Remove the unique id's
	$index .= $index{$key} .
	    # If it's the same string don't start a new line
	    (&index_key_eq(join('',@current), join('',@previous)) ?
	     ", <strong>" . $cross_ref_visible_mark . "</strong></A>\n" :
	     "<DT><strong>" . $str . "</strong></A>\n");
	@previous = @current;
    }
    while ($count < $level) {
	$index .= "\n</DL>";
	$level--;
    }
    s/$idx_mark/<DL COMPACT>$index<\/DL>/o;
#    s/$idx_mark/$preindex<DL COMPACT>$index<\/DL>/o;
}

sub keysort {
    local($x, $y) = ($a,$b);
    $x = &clean_key($x);
    $y = &clean_key($y);
#    "\L$x" cmp "\L$y";  # changed sort-rules, by M Ernst.
    # Put alphabetic characters after symbols; already downcased
#    $x =~ s/^([a-z])/~~~\1/;
#    $y =~ s/^([a-z])/~~~\1/;
    $x =~ s/^([a-z])/~~~$1/;
    $y =~ s/^([a-z])/~~~$1/;
    $x cmp $y;
}

sub index_key_eq {
    local($a,$b) = @_;
    $a = &clean_key($a);
    $b = &clean_key($b);
    $a eq $b;
}

sub clean_key {
    local ($_) = @_;
    tr/A-Z/a-z/;
    s/\s+/ /g;		# squeeze white space and newlines into space
    s/ (\W)/$1/g;	# make foo( ), foo () and foo(), or <TT>foo</TT>
    ;			# and <TT>foo </TT> to be equal
    s/$O\d+$C//go;	# Get rid of bracket id's
    s/$OP\d+$CP//go;	# Get rid of processed bracket id's
    s/\#\#\#\d+$//o;	# Remove the unique id
    $_;
}


sub make_footnotes {
    # Uses $footnotes defined in translate and set in do_cmd_footnote
    # Also uses $footfile
    local($_) = "\n<DL>$footnotes\n<\/DL>";
    $footnotes = ""; # else they get used
    print "\nDoing footnotes ...";
#JCL(jcl-tcl)
# If the footnotes go into a separate file: see &make_file.
# Else we don't need to &text_cleanup, it's done by &post_process.
#    &replace_markers;
#    &text_cleanup;
    if ($footfile) {
	&make_file($footfile, "Footnotes"); # Modifies $_;
	$_ = "";
    } else {
	$footnotes = ""; # else they get re-used
	$_ = join ('', '<BR><HR><H4>Footnotes</H4>', $_ );
    }
    $_;
}

sub post_process_footnotes {
    &slurp_input($footfile);
    open(OUTFILE, ">$footfile") || die "Cannot open file $footfile $!";
    &replace_markers;
    &post_post_process if (defined &post_post_process);
    print OUTFILE $_;
    close OUTFILE;
}

sub make_file {
    # Uses and modifies $_ defined in the caller
    local($filename, $title) = @_;
    $_ = join('',&make_head_and_body($title,""),$_,&make_address);
#JCL(jcl-tcl)
    &replace_markers unless ($filename eq $footfile); 
#    &text_cleanup;
#
    open(FILE,">$filename") || print "Cannot open $filename $!\n";
    print FILE $_;
    close(FILE);
}

sub replace_verbatim_marks {
    # Modifies $_
    s/$verbatim_mark(verbatim\*?)(\d+)/<PRE>\n$verbatim{$2}\n<\/PRE>/go;
    s/$verbatim_mark(rawhtml)(\d+)/$verbatim{$2}/ego; # Raw HTML
    s/$verbatim_mark#math(\d+)#/"\n<!-- MATH: ".$verbatim{$1}." -->\n"/ego; # math-comments
    s/$verbatim_mark$keepcomments(\d+)/$verbatim{$2}/ego; # Raw TeX
}

sub remove_verbatim_marks {
    # Modifies $_
    s/$verbatim_mark(verbatim\*?)(\d+)//go;
    s/$verbatim_mark(rawhtml)(\d+)//go;
#    s/$verbatim_mark#math\d+#//go;
    s/$verbatim_mark$keepcomments(\d+)//go;
}

sub replace_verb_marks {
    # Modifies $_
    s/$verb_mark(\d+)$verb_mark/<code>$verb{$1}<\/code>/go;
}

sub remove_verb_marks {
    # Modifies $_
    s/$verb_mark(\d+)$verb_mark//go;
}

# This is used by revert_to_raw_tex
sub revert_verbatim_marks {
    # Modifies $_
    s/$verbatim_mark(verbatim)(\d+)/\\begin{verbatim}$verbatim{$2}\\end{verbatim}/go;
    s/$verbatim_mark(rawhtml)(\d+)/\\begin{rawhtml}$verbatim{$2}\\end{rawhtml}/go;
}

sub revert_verb_marks {
    # Modifies $_
    s/$verb_mark(\d+)$verb_mark/\\verb*$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
}

sub replace_cross_ref_marks {
    # Modifies $_
    local($label,$id,$ref_label,$ref_mark,$after,$name);
    local($invis) = "<tex2html_anchor_invisible_mark></A>";
#    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/
    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark<\/A>(\s*<A( NAME=\"\d+)\">$invis)?/
	do {($label,$id) = ($1,$2); $name = $4;
	    $ref_label = $external_labels{$label} unless
		($ref_label = $ref_files{$label});
	    $ref_mark = &get_ref_mark($label,$id);
	    &extend_ref if ($name); $name = '';
	    print "\nLINK: $label : $ref_label : $ref_mark " if ($VERBOSITY > 3);
	    '"' . "$ref_label#$label" . "\">" . $ref_mark . "<\/A>"
	}/geo;

    # This is for pagerefs which cannot have symbolic labels ??? 
#    s/$cross_ref_mark#(\w+)#\w+>/
    s/$cross_ref_mark#([^#]+)#[^>]+>/
	do {$label = $1;
	    $ref_label = $external_labels{$label} unless
		($ref_label = $ref_files{$label});
	    print "\nLINK: $label : $ref_label" if ($VERBOSITY > 3);
	    '"' . "$ref_files{$label}#$label" . "\">"
	}/geo;
}

#RRM: this simply absorbs the name from the invisible anchor following, 
#     when the anchor itself is not already named.
sub extend_ref {
    if ($ref_label !=~ /NAME=/) { $label .= "\"\n".$name  }
}

sub remove_cross_ref_marks {
    # Modifies $_
#    s/$cross_ref_mark#(\w+)#(\w+)>$cross_ref_mark/
    s/$cross_ref_mark#([^#]+)#([^>]+)>$cross_ref_mark/
	print "\nLOST XREF: $1 : $2" if ($VERBOSITY > 3);''/ego;
#    s/$cross_ref_mark#(\w+)#\w+>//go;
    s/$cross_ref_mark#([^#]+)#[^#>]+>//go;
}

sub replace_external_ref_marks {
    # Modifies $_
    local($label, $link);
#    s/$external_ref_mark#(\w+)#(\w+)>$external_ref_mark/
    s/$external_ref_mark#([^#]+)#([^>]+)>$external_ref_mark/
	do {($label,$id) = ($1,$2); 
	    $link = $external_labels{$label};
	    print "\nLINK: $label : $link" if ($VERBOSITY > 3);
	    '"'. "$link#$label" . "\">\n"
	       . &get_ref_mark("userdefined$label",$id)}/geo;
}

sub remove_external_ref_marks {
    # Modifies $_
#    s/$external_ref_mark#(\w+)#(\w+)>$external_ref_mark/
    s/$external_ref_mark#([^#]+)#([^>]+)>$external_ref_mark/
	print "\nLOST LINK: $1 : $2" if ($VERBOSITY > 3);''/ego;
}

sub get_ref_mark {
    local($label,$id) = @_;
    ( ($SHOW_SECTION_NUMBERS && $symbolic_labels{"$label$id"}) ||
     $latex_labels{"userdefined$label$id"} ||
     $symbolic_labels{"$label$id"} ||
     $latex_labels{$label} ||
     $cross_ref_visible_mark );
}

sub replace_bbl_marks {
    # Modifies $_
    s/$bbl_mark#([^#]+)#/$citations{$1}/go;
}

sub remove_bbl_marks {
    # Modifies $_
    s/$bbl_mark#([^#]+)#//go;
}

sub replace_image_marks {
    # Modifies $_
    s/$image_mark#([^#]+)#([\.,;:\)\]])?\n?(\001)?/
	"$id_map{$1}$2"/ego;
}

sub remove_image_marks {
    # Modifies $_
    s/$image_mark#([^#]+)#//go;
}

sub replace_icon_marks {
    # Modifies $_
    s/$icon_mark_rx/&img_tag($icons{$1})/ego;
}

sub remove_icon_marks {
    # Modifies $_
    s/$icon_mark_rx//go;
}

sub replace_cite_marks {
    local($key,$label,$text);
    # Modifies $_
    # Uses $citefile set by the thebibliography environment
    s/#([^#]+)#$cite_mark#([^#]+)#([^#]*)#$cite_mark#/
	$text = $3;
        $text = $cite_info{$1} unless ($text);
	if ($citefiles{$2}){
            &make_named_href('', "$citefiles{$2}#$1","$text"); }
	elsif ($PREAMBLE) { $text }
	else {
	     &write_warnings("\nno reference for citation: $1");
	     "\#!$1!\#"
	 }/ge ;
    #
    #RRM: Associate the cite_key with  $citefile , for use by other segments.
    if ($citefile) {
	local($cite_key, $cite_ref);
	while (($cite_key, $cite_ref) = each %cite_info) {
	    if ($ref_files{'cite_'."$cite_key"} ne $citefile) {
		$ref_files{'cite_'."$cite_key"} = $citefile;
		$changed = 1; }
	}
    }
}

sub remove_cite_marks {
    # Modifies $_
    s/#([^#]+)#$cite_mark#([^#]+)#([^#]*)#$cite_mark#//go;
}

sub remove_anchors {
# modifies $_
    s/<A[^>]*>//g;
    s/<\/A>//g;
}


# We need two matching keys to determine section/figure/etc. numbers.
# The "keys" are the name of the section/figure/etc. and its
# equivalent in the .aux file (also carrying the number we desire).
# But both keys might have been translated slightly different,
# depending on the usage of math, labels, special characters such
# as umlauts, or simply spacing!
#
# This routine tries to squeeze the HTML translated keys such
# that they match (hopefully very often). -- JCL
#
sub sanitize {
    local($_,$mode) = @_;
    &remove_markers;
    &remove_anchors;
    &text_cleanup;
    s/&nbsp;//g;            # HWS - LaTeX changes ~ in its .aux files
    #strip unwanted HTML constructs
    s/<\/?(P|BR|H)[^>]*>//g;
    s/\s+//g; #collapse white space
    $_;
}

# This one removes any HTML markup, so that pure
# plain text remains. (perhaps with <SUP>/<SUB> tags)
# As the result will be part of the HTML file, it will be
# &text_cleanup'd later together with its context.
#
sub purify {
    local($_,$strict) = @_;
    &remove_markers;
    #strip unwanted HTML constructs
#    s/<[^>]*>/ /g;
    s/<(\/?SU[BP])>/>$1>/g unless ($strict);  # keep sup/subscripts ...
    s/<[^>]*>//g;                             # remove all other tags
    s/>(\/?SU[BP])>/<$1>/g unless ($strict);  # ...reinsert them
    s/^\s+//; s/\s\s+/ /g;                    #collapse white space
    $_;
}

# This one is not as strict as &sanitize.
# It is chosen to strip section names etc. a bit from
# constructs so that it better fits a table of contents,
# label files, etc.
# As the result will be part of the HTML file, it will be
# &text_cleanup'd later together with its context.
#
sub simplify {
    local($_) = @_;
    &replace_external_ref_marks if /$external_ref_mark/;
    &replace_cross_ref_marks if /$cross_ref_mark||$cross_ref_visible_mark/;
    &replace_cite_marks if /$cite_mark/;
    # strip unwanted HTML constructs
#    s/<\/?H[^>]*>/ /g;
    s/<\/?(H)[^>]*>//g;
    s/<\#\d+\#>//g;
    s/^\s+//;
    $_;
}
sub extract_anchors {
    local($_) = @_; 
    local($anchors) = '';
    while (s/<A[^>]*>($anchor_mark|$anchor_invisible_mark)<\/A>//o) {
	$anchors .= $&;
    }
    ($anchors,$_); 
}

# This routine must be called once on the text only,
# else it will "eat up" sensitive constructs.
sub text_cleanup {
    $* = 1;
    s/(\s*\n){2,}/\n<P>/go;	# Replace consecutive blank lines with a paragraph tag
    s/<(\/?)P>\s*(\w)/<$1P>\n$2/go;      # clean up paragraph starts and ends
    $* = 0;
    s/$O\d+$C//go;		# Get rid of bracket id's
    s/$OP\d+$CP//go;		# Get rid of processed bracket id's
    s/(<!)?--?(>)?/(length($1) || length($2)) ? "$1--$2" : "-"/ge;
    # Spacing commands
    s/\\( |$)/ /go;
    #JKR: There should be no more comments in the source now.
    #s/([^\\]?)%/$1/go;        # Remove the comment character
    # Cannot treat \, as a command because , is a delimiter ...
    s/\\,/ /go;
    # Replace tilde's with non-breaking spaces
    s/~/&nbsp;/g;
#JCL(jcl-hex)
# Replace ^^ special chars (according to p.47 of the TeX book)
# Useful when coming from the .aux file (german umlauts, etc.)
    s/\^\^([^0-9a-f])/chr((64+ord($1))&127)/ge;
    s/\^\^([0-9a-f][0-9a-f])/chr(hex($1))/ge;
}

# This is useful for getting words from a title which are not cluttered
# with tex2html markers or HTML constructs
sub extract_pure_text {
    local($mode) = @_;
    &text_cleanup;		# Remove marking brackets
#
# HWS <hswan@perc.Arco.com>:  Conditionally doing the following
#     permits equations in section headings.
#
    if ($mode eq "strict") {
	s/$image_mark#[^#]*#//g;	# Remove image marker
	s/$bbl_mark#[^#]*#//g;		# Remove citations marker
        s/<tex2html_percent_mark>/%/g;  # BMcM: Retain % signs...
	s/tex2html[\w\d]*//g; 	# Remove other markers
	}

#
# HWS <hswan@perc.Arco.com>:  Replace next statement with the following two
#    to permit symbolic links and images to appear in section headings.

#   s/<[^>]*>//go;			# Remove HTML constructs
    s/$OP[^#]*$CP//go;			# Remove <# * #> constructs
    s/<\s*>//go;			# Remove embedded whitespace
}

############################ Misc ####################################
sub usage {
# Any changes must go in sync with the nroff information at file end.
    print <<_EOM_
This is LaTeX2HTML Version $TEX2HTMLVERSION by Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Usage:
latex2html
   [-split num]
   [-link num]
   [-toc_depth num]
   [-short_extn]
   [-nolatex]
   [-external_images]
   [-ps_images]
   [-font_size (10pt | 11pt | 12pt | ...)]
   [-no_tex_defs]
   [-ascii_mode]
   [-t top_page_title]
   [-dir output_directory]
   [-no_subdir]
   [-address author_address]
   [-long_titles num ]
   [-custom_titles]
   [-no_navigation]
   [-top_navigation]
   [-bottom_navigation]
   [-auto_navigation]
   [-index_in_navigation]
   [-contents_in_navigation]
   [-next_page_in_navigation]
   [-previous_page_in_navigation]
   [-prefix output_filename_prefix]
   [-auto_prefix]
   [-up_url up_url]
   [-up_title up_title]
   [-down_url down_url]
   [-down_title down_title]
   [-prev_url prev_url]
   [-prev_title prev_title]
   [-index index_url]
   [-biblio biblio_url]
   [-contents toc_url]
   [-external_file external.aux_file]
   [-info string]
   [-no_auto_link]
   [-discard]
   [-reuse reuse_option]
   [-no_reuse]
   [-no_images]
   [-no_math]
   [-images_only]
   [-antialias]
   [-no_antialias]
   [-antialias_text]
   [-no_antialias_text]
   [-local_icons]
   [-scalable_fonts]
   [-show_section_numbers]
   [-init_file Perl_file]
   [-html_version (2.0|3.0|3.2)[,(math|i18n|table)]*]
   [-short_index]
   [-debug]
   [-timing]
   [-verbosity num]
   [-h(elp)]
   [-v]
   file(s)
_EOM_
}

# The bibliographic references, the appendices, the lists of figures and tables
# etc. must appear in the contents table at the same level as the outermost
# sectioning command. This subroutine finds what is the outermost level and
# sets the above to the same level;
sub set_depth_levels {
    # Sets $outermost_level
    local($level);
    #RRM:  do not alter user-set value for  $MAX_SPLIT_DEPTH
    foreach $level ("part", "chapter", "section", "subsection",
		    "subsubsection", "paragraph") {
	last if (($outermost_level) = /\\($level)$delimiter_rx/);
    }
    $level = ($outermost_level ? $section_commands{$outermost_level} :
	      do {$outermost_level = 'section'; 3;});

    #RRM:  but calculate value for $MAX_SPLIT_DEPTH when a $REL_DEPTH was given
    if ($REL_DEPTH && $MAX_SPLIT_DEPTH) { 
	$MAX_SPLIT_DEPTH = $level + $MAX_SPLIT_DEPTH;
    } elsif (!($MAX_SPLIT_DEPTH)) { $MAX_SPLIT_DEPTH = 1 };

    %section_commands = ('tableofcontents', $level, 'listoffigures', $level,
			 'listoftables', $level, 'bibliography', $level,
			 'textohtmlindex', $level, %section_commands);
}

# Now ignores accents which cannot be translated to ISO-LATIN-1 characters
# Also replaces ?' and !' ....
sub replace_strange_accents {
    # Modifies $_;
    s/\?`/&iso_map("questacute", "")/geo;
    s/!`/&iso_map("exclamacute", "")/geo;
    s/\\\^\\i /&iso_map("icirc", "")/geo;
};

# Creates a new directory
sub new_dir {
    local($_) = @_;
    local($answer);
    mkdir($_, oct(755)) ||
	do {print "Cannot create directory $_: $!\n";
	    if ($REUSE) {
		&reuse;}
	    else {
		while (! ($answer =~ /^[dqr]$/)) {
		    print "(r) Reuse the images in the old directory OR\n".
			"(d) *** DELETE *** $_ AND ITS CONTENTS OR\n".
			    "(q) Quit ?\n:";
		    $answer = scalar(<STDIN>);
		    if ($answer =~ /^d$/) {
			`rm -r $_`; # ******
			&new_dir($_);
			return(1);}
		    elsif ($answer =~ /^q$/) {
			die "Bye!\n";}
		    elsif ($answer =~ /^r$/) {
			&reuse;
			return(1);}
		    else {print "Please answer r d or q!\n";};} };
	};
}

sub reuse {
    print "Reusing directory $_:\n";
    local($key);
    require("$_$dd${PREFIX}images.pl") if (-f "$_$dd${PREFIX}images.pl");
    1;
}


# JCL(jcl-del) - use $CD rather than a space as delimiter.
# The commands might take white space, or not, depending on
# their definition. Eg. \relax takes white space, because it's a
# letter command, but \/ won't.
# TeX seems to have an internal separator: If \x is " x",
# and \y is "y", then \expandafter\y \x expands to "y x", TeX
# hasn't gobbled the space, meaning that spaces are gobbled once
# when the \y token is consumed, but then never again after \y.
#
# The actions below ensure to insert exactly one space after
# the command name.	# what happens to  `\ '  ?
# The substition is done twice to handle \one\delimits\another
# cases.
# The internal shortcut $CD is then turned into the single
# space we desire.
#
sub tokenize {
    # Modifies $_;
    local($rx) = @_;
    # $rx must be specially constructed, see &make_new_cmd_rx.
    if (length($rx)) {
	# $1: non-letter cmd, or $2: letter cmd
	s/$rx/\\$1$2$CD$4/g;
	s/$rx/\\$1$2$CD$4/g;
	s/$CD+/ /g;	# puts space after each command name
    }
}

# When part of the input text contains special perl characters and the text
# is to be used as a pattern then these specials must be escaped.
sub escape_rx_chars {
    local($_) = @_;
    s:([\\(){}[\]\^\$*+?.|]):\\$1:g;
    $_;
}

# Does not do much but may need it later ...
# The document environment has to be removed because it spans
# more than one sections (the translator can only deal with
# environments wholly contained with sections).

# (Does a little more now ... the end of the preamble is now marked
# with an internally-generated command which causes all output
# erroneously generated from unrecognized commands in the preamble
# to vanish --- rst).

sub remove_document_env {
    s/\\begin$match_br_rx[d]ocument$match_br_rx/\\latextohtmlditchpreceding /o;
    s/\\end$match_br_rx[d]ocument$match_br_rx(.|\n)*//o;
}

# And here's the code to handle the marker ...

sub do_cmd_latextohtmlditchpreceding {
    local($_) = @_;
    $ref_before = '';
    $_;
}

print "\n"; # flushes a cache? This helps, for some unknown reason!!

sub do_AtBeginDocument{
    local($_) = @_;
    eval ${AtBeginDocument_hook};
    $_;
}

sub cleanup {
    local($explicit) = @_;

    if ($explicit || !$DEBUG) {
	if (opendir(DIR, '.')) {
	    while ($_ = readdir(DIR)) {
		unlink ($_) if (/\.(pbm|dvi)$/ || /^(TMP|$$)/);
	    }
	}
	closedir (DIR);
    }
}

sub handler {
    print "\nLaTeX2HTML shutting down.\n";
    kill ('INT', $child_pid) if ($child_pid);
    &cleanup();
    exit(-1);
}

# Given a filename or a directory it returns the file and the full pathname
# relative to the current directory.
sub get_full_path {
    local($file) = @_;
    local($path);
    if (-d $file) {		# $file is a directory
	$path = &make_directory_absolute($file);
	$file = '';
    }
# JCL(jcl-dir)
    elsif ($file =~ s|$dd([^$dd]*)$|| ) {
	$path = $file;
	$file = $1;
	$path = &make_directory_absolute($path);
    }
#
    else {
	$path = &getcwd;
    }
    ($path, $file);
}


# Given a directory name in either relative or absolute form, returns
# the absolute form.
# Note: The argument *must* be a directory name.
sub make_directory_absolute {
    local($path) = @_;
    local($orig_cwd);
    if (! ($path =~ /^$dd/)) {   # if $path doesn't start with '/'
        $orig_cwd = &getcwd;
        chdir $path;
        $path = &getcwd;
        chdir $orig_cwd;
    }
    $path;
}


# Given a relative filename from the directory in which the original
# latex document lives, it tries to expand it to the full pathname.
sub fulltexpath {
    # Uses $texfilepath defined in sub driver
    local($file) = @_;
    $file =~ s/\s//g;
    $file = "$texfilepath$dd$file" unless ($file =~ /$dd/); # name begins with a /
    $file;
}


###KZ -- fixes `Cannot read /file.tex' problem
# Trap errors if getcwd.pl is not found in the library
# eval "require 'getcwd.pl'";

# Define GETCWD if we can't find it in the Perl library
# eval {sub getcwd { local($pwd); chop($pwd = `pwd`); $pwd }}
# unless (defined &getcwd);

sub getcwd {
    local($_) = `pwd`;

    die "'pwd' failed (out of memory?)\n"
	unless length;
    chop;
    $_;
}

sub syswait {
    local($_) = @_;
    local($status);
    print "$_\n" if ($DEBUG);
    if ($child_pid = fork) {
	$status = waitpid($child_pid, 0);
	print "\n *** finished child process: \#$child_pid\n" if ($DEBUG);
	$child_pid = 0;
	return($?);
    }
    else {
	exec($_);
	print "$_[0]:  $!\n";
	exit($!);
    }
}

#RRM  Extended to allow customised filenames, set $CUSTOM_TITLES
#     or long title from the section-name, set $LONG_TITLES
#
sub make_name {
    local($sec_name, $packed_curr_sec_id) = @_;
    local($title) = '';
    if ($LONG_TITLES) {
	&process_command($sections_rx, *_) if /^$sections_rx/;
	$title = &make_long_title($TITLE)
	    unless ((! $TITLE) || ($TITLE eq $default_title));
    } elsif ($CUSTOM_TITLES) {
	&process_command($sections_rx, *_) if /^$sections_rx/;
	$title = &custom_title_hook($TITLE)
	    if ((! $TITLE) || ($TITLE eq $default_title));
    }
    if ($title) {
    	++$OUT_NODE;
	join("", ${PREFIX}, $title, $EXTN);
    }else {
    # Remove 0's from the end of $packed_curr_sec_id
	$packed_curr_sec_id =~ s/(_0)*$//;
	$packed_curr_sec_id =~ s/^0$//o; # Top level file
	join("",($packed_curr_sec_id ? 
	    "${PREFIX}node". ++$OUT_NODE : $sec_name), $EXTN);
    }
}

#RRM: redefine this subroutine, to create customised file-names
#     based upon the actual section title.
#     The default is empty, so reverts to:  node1, node2, ...
#
sub custom_title_hook {
    local($_)= @_;
    "";
}

sub make_long_title {
    local($_)= @_;
    s/\s+(and|the|of|for|by|a|an|to)$//;
    $_ = &get_first_words($_, $LONG_TITLES) if ($LONG_TITLES);
    #cleanup spaces and punctuation
    s/\s/_/g; s/\W/_/g; s/__+/_/g;
    $_;
}

sub make_first_key {
    local($_);
    $_ = ('0 ' x keys %section_commands);
    chop;
    $_;
}

# This copies the preamble into the variable $preamble.
# It also sets the LaTeX font size, if $FONT_SIZE is set.
sub add_preamble_head {
    $preamble = join ('', &revert_to_raw_tex(/$preamble_rx/o),
				$preamble);
    if ($FONT_SIZE) {
	local (@pre) = $preamble =~
	    /((.|\n)*)(\\document)(style|class)(\s*)(\[.*\])?(\s*\{)((.|\n)*)/;
	local ($_) = $pre[5];
	s/1\dpt//;
	s/(\[\s*),+/$1/;
	s/,+(\s*\])/$1/;
	s/\[/[$FONT_SIZE,/ if ($FONT_SIZE ne "10pt");
	s/\[\s*\]//;
	$pre[5] = $_;
	$preamble = join("", @pre);
    }
}

# It is necessary to filter some parts of the document back to raw
# tex before passing them to latex for processing.
sub revert_to_raw_tex {
    local($_) = @_;
    local($character_map) = "";
    if ( $CHARSET && $HTML_VERSION ge "2.1" ) {
	$character_map = $CHARSET;
	$character_map =~ tr/-/_/; }
    while (s/$O\s*\d+\s*$C/\{/o) { s/$&/\}/;}
    while (s/$O\s*\d+\s*$C/\{/o) { s/$&/\}/;} #repeat this.
    # The same for processed markers ...
    while ( s/$OP\s*\d+\s*$CP/\{/o ) { s/$&/\}/; }
    while ( s/$OP\s*\d+\s*$CP/\{/o ) { s/$&/\}/;} #repeat this.

    s/<BR>/\\\\/g; # restores the \\ from \parbox's

    # revert any math-entities
    s/\&\w+#(\w+);/\\$1/g;
    s/\&limits;/\\limits/g;
    s/\\underscore/\\_/g;
    s/\\circflex/\\^/g;
    s/;SPMthinsp;/\\,/g;
    s/;SPMnegsp;/\\!/g;
    s/;SPMsp;/\\:/g;
    s/;SPMthicksp;/\\;/g;

    # revert any super/sub-scripts
    s/<SUP>/\^\{/g;
    s/<SUB>/\_\{/g;
    s/<\/SU(B|P)>/\}/g;

#    #revert common character entities  ??
#    s/&#92;/\\/g;

#    # revert special marks
#    s/$percent_mark/\\%/go;

    # From &pre_process.
    $* = 1;
    s/\\\\ (\n)?/\\\\/g;
    $* = 0;
    # Replace any verbatim markers ...
    &revert_verbatim_marks;
    &revert_verb_marks;
    $* = 1;
    s/$tex2html_wrap_rx//go; # remove artificial environments
    s/\n{3,}/\n\n/g; # remove multiple (3+) new-lines 
    s/^\n+$//g; # ...especially if that is all there is!
    $* = 0;
    s/^\s*$//g if ($PREAMBLE); #remove blank lines in the preamble

    s/($html_specials_inv_rx)/$html_specials_inv{$1}/geo;
    s/$character_entity_rx/( $character_map ?
	eval "\$${character_map}_character_map_inv\{\"$1\"\}" :
	$iso_8859_1_character_map_inv{$1} ||
	    $iso_10646_character_map_inv{$1})/geo;
    # Need an entity inverse mapping here ...
    &write_warnings("\nA character entity ($1) has crept in the source text.")
	if (/$character_entity_rx/o);
    &revert_to_raw_tex_hook if (defined &revert_to_raw_tex_hook);
    $_;
}

sub next_wrapper {
    local($dollar) = @_;
    local($_,$id);
    $wrap_toggle = (($wrap_toggle eq 'end') ? 'begin' : 'end');
    $id = ++$global{'max_id'};
    $_ = "\\$wrap_toggle$O$id$C"."tex2html_wrap$O$id$C";
    $_ = (($wrap_toggle eq 'end') ? $dollar.$_ : $_.$dollar);
    $_;
}

sub make_wrapper {
    &make_any_wrapper($_[0], "tex2html_wrap");
}

sub make_nowrapper {
    &make_any_wrapper($_[0], "tex2html_nowrap");
}

sub make_inline_wrapper {
    &make_any_wrapper($_[0], "tex2html_wrap_inline");
}

sub make_deferred_wrapper {
    &make_any_wrapper($_[0], "tex2html_deferred");
}

sub make_nomath_wrapper {
    &make_any_wrapper($_[0], "tex2html_nomath_inline");
}

sub make_any_wrapper {
    local($toggle,$kind) = @_;
    local($max_id) = ++$global{'max_id'};
    '\\'. (($toggle) ? 'begin' : 'end') . "$O$max_id$C"."$kind$O$max_id$C";
}

sub get_last_word {
    # Returns the last word in multi-line strings
    local($_) = @_;
    local ($word);
#JCL(jcl-tcl)
# also remove anchors and other awkward HTML markup
#    &extract_pure_text("strict");
##    $_ = &purify($_);  ## No. what if it is a verbatim string ?
#
    while (/\s(\S+)\s*$/g) { $word = $1;}
    $word;
}

#JCL(jcl-tcl)
# changed completely
#
# We take the first real words specified by $min from the string.
# Allow for simple HTML constructs like <I>...</I> (but not <H*>
# or <P*> and the like), math, or images to remain in the result,
# not counting as words.
# Take care that eg. <I>...</I> grouping tags are not broken.
# This is achieved by lifting the markup, removing superfluous
# words, re-inserting the markup, and throw empty markup away.
# In later versions images could be modified such that they become
# thumbnail sized.
#
# rawhtml or verbatim environments might introduce lots of awkward
# stuff, but yet we leave the according tex2html markers in.
#
sub get_first_words {
    local($_, $min) = @_;
    local($words,$i);
    local($id,%markup);

    &remove_anchors;
    #strip unwanted HTML constructs
    s/<\/?(P|BR|H)[^>]*>/ /g;
    #remove leading white space
    s/^\s+//;
    #lift html markup
    s/(<[^>]*>(#[^#]*#)?)/$markup{++$id}=$1; "\000$id"/ge;

    foreach (split /\s+|\-{3,}/) {
#        # count words, but not HTML markup
#        ++$i unless /\000/;
        # count words (incl. HTML markup as part of the word)
        ++$i ;  # unless /^\000\d+$/; an <IMG> counts as a word, why not?
        # markup falls through
        $words .= $_ . " " if (/\000/ || ($i <= $min));
    }
    $_ = $words;
    chop;

    #re-insert markup
    s/\000(\d+)/$markup{$1}/g;
    # remove empty markup
    # it's normalized, because generated by LaTeX2HTML only
    s/<([A-Z]+)[^>]*>\s*<\/\1>\s*//g;
#was etc.:
#    &extract_pure_text("liberal");
     $_;
}

sub replace_word {
    # Replaces the LAST occurrence of $old with $new in $str;
    local($str, $old, $new) = @_;
    substr($str,rindex($str,$old),length($old)) = $new;
    $str;
}

# Returns the recognised sectioning commands as a string of alternatives
# for use in regular expressions;
sub get_current_sections {
    local($_, $key);
    foreach $key (keys %section_commands) {
	if ($key =~ /star/) {
	    $_ = $key . "|" . $_}
	else {
	    $_ .= "$key" . '[*]?|';
	}
    }
    chop;			# Remove the last "|".
    $_;
}

sub numerically {
    local(@x) = split(' ',$a);
    local(@y) = split(' ',$b);
    local($i, $result);
    for($i=0;$i<$#x;$i++) {
       last if ($result = ($x[$i] <=> $y[$i]));
    }
    $result
}

# Assumes that the files to be sorted are of the form
# <NAME><NUMBER>
sub file_sort {
    local($i,$j) = ($a,$b);
    $i =~ s/^[^\d]*(\d+)$/$1/;
    $j =~ s/^[^\d]*(\d+)$/$1/;
    $i <=> $j
}

# If a normalized command name exists, return it.
sub normalize {
    local($cmd) = @_;
    local($ncmd);
    # Escaped special LaTeX characters
    if ($cmd =~ /^($latex_specials_rx)/) {
	$cmd =~ s/&(.*)$/&amp;$1/o;
        $cmd =~ s/%/$percent_mark/o;
	$after = join('', $cmd, $after);
	$cmd = ""}
    elsif ($ncmd = $normalize{$cmd}) {
	$ncmd;
    }
    else {
 	$cmd =~ s/[*]$/star/;
 	$cmd =~ s/\@/_at_/g;
	$cmd;
    }
}

sub normalize_sections {
    s/$sections_rx/'\\' . &normalize($1.$2) . $4/ge;
}

sub embed_image {
    local($url,$name,$external,$altst,$thumbnail,$map,$align
	,$usemap,$exscale,$exstr) = @_;
    local($extern_image_mark,$result,$size,$aalign,$after);
    local($anch1,$anch2,$ausemp,$ismap)=("","","");
    local($urlimg) =  $url;

    $ismap = " ISMAP" if $map;
    print "\nembedding $url for  $name" if ($VERBOSITY > 1);

    if (! ($NO_IMAGES || $PS_IMAGES)) {
	$size = &get_image_size($url);
	# for over-scaled GIFs with pre-determined sizes	# RRM 11-9-96
	if (($width{$name})&&(($exscale)||($EXTRA_IMAGE_SCALE))) {
	    $exscale = $EXTRA_IMAGE_SCALE unless ($exscale);
	    $size = &scaled_image_size($exscale, $size) if
		($name =~ /inline|indisplay|entity|equation|math|eqn|makeimage/);
	}
	$image_size{$url} = $size 
	    unless ((! $size) || ($size eq "WIDTH=\"0\" HEIGHT=\"0\""));
	$url = &find_unique($url);
    }

    $urlimg = $url;
    $urlimg =~ s/\.$IMAGE_TYPE$/.html/ if ($map);
    if ($exstr =~ s/align\s*=\s*(\"?)(\w+)\1($|\s|,)//io) { $align = $2; }
    local($usersize)='';
    if ($exstr =~ s/width\s*=\s*(\"?)([^\s,]+)\1($|\s|,)//io) {
	local($pxs,$len) = &convert_length($2);
	$usersize = " WIDTH=\"$pxs\"";
    }
    if ($exstr =~ s/height\s*=\s*(\"?)([^\s,]+)\1($|\s|,)//io) { 
	local($pxs,$len) = &convert_length($2);
	$usersize .= " HEIGHT=\"$pxs\"";
    }

    if (($name =~ /figure|table|displaymath|eqnarraystar/)&&(!$align)) {
    } elsif (($name =~ /equation|eqnarray\d/)&&(!$align)) {
	if ($HTML_VERSION >= 3.2) {
	    $aalign =  ($EQN_TAGS eq "L") ? "right" : "left" ;
	    $aalign .= "\" ALIGN=\"middle"; } 
	elsif ($EQN_TAGS eq "L") { $aalign = "left" }
	elsif ($EQN_TAGS eq "R") { $aalign = "right" }
    } elsif ($name =~ /inline|indisplay|entity|xy|diagram/ && $depth{$name} != 0) {
	$aalign = "MIDDLE\" BORDER=\"0" 
    } else {  $aalign = "BOTTOM\" BORDER=\"0" }

    $aalign = "\U$align" if $align;
    $ausemp = "\UUSEMAP=$usemap" if $usemap;

    #append any extra valid options 
    $ismap .= parse_keyvalues ($exstr, ("IMG")) if ($exstr);

    if ($thumbnail) {
	print "\nmaking thumbnail" if ($VERBOSITY > 1);
	if ( $image_size{$thumbnail} = &get_image_size($thumbnail) ) {
	    $thumbnail = &find_unique($thumbnail);
	    $extern_image_mark = join('',"<IMG\n " , $image_size{$thumbnail}
		, (($aalign) ? " ALIGN=\"$aalign\"" : '')
		, " SRC=\"$thumbnail\" \n $altst>");
	}
    } else { $extern_image_mark = &extern_image_mark($type); }

    if ($external || $thumbnail || $EXTERNAL_IMAGES) {
	if ( $extern_image_mark ) {
	    $result = &make_href($urlimg, $extern_image_mark);
	    &save_image_map($url, $urlimg, $map, $name, $altst, $ausemp) if $map;
	}
    } else {
	if ($map) {
	    $anch1 = "<A HREF=\"$map\">";
	    $anch2 = "</A>";
	}
#	if ($aalign eq "CENTER") {
#	    if ($HTML_VERSION eq "2.0") {
#	        $anch1 .= "\n<P ALIGN=\"CENTER\">";
#	        $anch2 .= "</P>";
#	    } else {
#	        $anch1 .= "\n<DIV ALIGN=\"CENTER\">";
#	        $anch2 .= "</DIV>";
#	    }
#	}

	local($imagesize) = $image_size{$url};
	$imagesize = $usersize if ($usersize);
	### MEH Add width and height to IMG
	### Patched by <hswan@perc.Arco.com>:  Fixed \htmladdimg 
	if ( $imagesize || $name eq "external" || $NO_IMAGES || $PS_IMAGES) {
	    $result = join('', "<IMG ", $imagesize 
			   , (($aalign)? " ALIGN=\"$aalign\"" : ''), $ismap );
	    if ($ausemp) { $result .= " $ausemp" }
	    $result .= "\n SRC=\"$url\"";
	    if ($altst) { $result .= "\n $altst" }
	    $result .= ">";
	}
    }
    join('',$anch1, $result, $anch2);
}

# MRO: added PNG support
sub get_image_size {
    local($imagefile) = @_;
    local($width,$height,$magic) = (0,0,0);
    local($buffer,$size,$dummy) = ('','','');
    if ( open(IMAGE, $imagefile) ) {
        if($IMAGE_TYPE =~ /gif/) {
	    read(IMAGE,$buffer,10);
	    ($magic,$width,$height) = unpack('a6vv',$buffer);
	    if ( ($magic eq "GIF87a" || $magic eq "GIF89a")
	        && $width * $height > 0 ) {
	        $size = "WIDTH=\"$width\" HEIGHT=\"$height\"";
	    };
        }
        elsif($IMAGE_TYPE =~ /png/) {
            read(IMAGE,$buffer,24);
	    ($magic,$dummy,$width,$height) = unpack('a4a12NN',$buffer);
	    if ( ($magic eq "\x89PNG") && $width * $height > 0 ) {
	        $size = "WIDTH=\"$width\" HEIGHT=\"$height\"";
            };
	close(IMAGE);
	};
    }
    $size;
}

sub find_unique {
    local($image1) = @_;
    local($image2,$result,$key);
    local($/) = undef;

    open(IMG1,$image1);
    local($imagedata) = <IMG1>;
    close(IMG1);

    foreach $image2 (keys(%image_size)) {
	if ( $image1 ne $image2 &&
	    $image_size{$image1} eq $image_size{$image2} ) {
	    open(IMG2,$image2);
	    $result = ($imagedata eq <IMG2>);
	    close(IMG2);
#
#  If we've found a match, rename the new image to a temporary one.
#  Then try to link the new name to the old image.
#  If the link fails, restore the temporary image.
#
	    if ( $result ) {
		local($tmp) = 'temporary.$IMAGE_TYPE';
		unlink($tmp);
		rename($image1, $tmp);
		if (link($image2, $image1)) {unlink($tmp);}
		else {rename($tmp, $image1)};
		return $image1;
	    }
	}
    }
    $image1;
}

sub save_image_map {
    local($url, $urlimg, $map, $name, $altst, $ausemp) = @_;
    open(IMAGE_MAP, ">$urlimg");
    ### HWS  Pass server map unchanged from user
    print IMAGE_MAP "<HTML>\n<BODY>\n<A HREF=\"$map\">\n";
    print IMAGE_MAP "<IMG\n SRC=\"$url\" ISMAP $ausemp $altst> </A>";
    print IMAGE_MAP "</BODY>\n</HTML>\n";
    close IMAGE_MAP;
}

#  Subroutine used mainly to rename an old image file about to recycled.
#  But for active image maps, we must edit the auxiliary HTML file to point
#     to the newly renames image.
sub rename_html {
    local ($from, $to) = @_;
    local ($from_prefix, $to_prefix, $suffix);
    ($from_prefix, $suffix) = split(/\./, $from);
    ($to_prefix, $suffix) = split(/\./, $to);
    if ($suffix eq "html") {
	if (open(FROM, "<$from") && open(HTMP, ">HTML_tmp")) {
	    while (<FROM>) {
		s/$from_prefix\.$IMAGE_TYPE/$to_prefix.$IMAGE_TYPE/g;
		print HTMP;
	    }
	    close (FROM);
	    close (HTMP);
	    rename ("HTML_tmp", $to);
	    unlink("$from") unless ($from eq $to);
	}
	else {
	    &write_warnings("File $from is missing!\n");
	}
    }
    rename("$from_prefix.old", "$to_prefix.$IMAGE_TYPE");
    $to;
}

sub save_captions_in_file {
    local ($type, $_) = @_;
    if ($_) {
	$* = 1; s/^\n//o; $* = 0;
	&replace_markers;
	&add_dir_to_href if ($DESTDIR);
	open(CAPTIONS, ">${PREFIX}$type.pl");
	print CAPTIONS $_;
	close (CAPTIONS);
    }
}

sub add_dir_to_href {
    $_ =~ s/(<LI><A )(NAME\=\"tex2html\d+\")?\s*(HREF=\")/$1$3\'.\$dir.\'/og;
    $_ = join('', "\'", $_, "\'\n");
}

sub save_array_in_file {
    local ($type, $array_name, %array) = @_;
    local ($uutxt,$file,$prefix,$suffix,$done_file,$depth,$title);
    $prefix = $suffix = "";
    $prefix = "\"\$URL/\" . " if ($type eq "labels");
    $suffix = " unless (\$$array_name\{\$key\})"
	if (($type =~ /(sections|contents)/)||($array_name eq "printable\_key"));
    if (%array) {
	print "\nSAVE_ARRAY:$array_name in FILE: ${PREFIX}$type.pl"
	    if ($VERBOSITY > 1);
	if (($array_name eq "sub\_index") || ($array_name eq "printable\_key")) {
	    open(FILE,">>${PREFIX}$type.pl");
	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
	    print FILE "# Printable index-keys from $array_name array.\n\n";
	} elsif ($array_name eq "index\_labels") {
	    open(FILE,">>${PREFIX}$type.pl");
	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
	    print FILE "# labels from $array_name array.\n\n";
	} elsif ($array_name eq "index\_segment") {
	    open(FILE,">>${PREFIX}$type.pl");
	    print FILE "\n# LaTeX2HTML $TEX2HTMLVERSION\n";
	    print FILE "# segment identifier from $array_name array.\n\n";
	} else {
	    open(FILE,">${PREFIX}$type.pl");
	    print FILE "# LaTeX2HTML $TEX2HTMLVERSION\n";
	    print FILE "# Associate $type original text with physical files.\n\n";
	}
	while (($uutxt,$file) = each %array) {
	    $uutxt =~ s|/|\\/|g;
	    $uutxt =~ s|\\\\/|\\/|g;
#
	    local ($nosave) = $noresave{$uutxt}; #RRM: suppress info from other segments
	    if (($nosave ne 1) && ($file ne ''))  {
		print FILE "\n\$key = q/$uutxt/;\n";

		$file =~ s/\|/\\\|/g; # RRM:  escape any occurrences of |
		$file =~ s/\\\\\|/\\\|/g; # unless already escaped as \|
#
# added code for  $dir  with segmented docs;  RRM  15/3/96
#
		if ($type eq "contents") {
		    ($depth, $done_file) = split($delim, $file, 2 );
		    next if ($depth > $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
		    print FILE "\$$array_name\{\$key\} = '$depth$delim'.\"\$dir\".q|$done_file|$suffix; \n";
		} elsif ($type eq "sections") {
		    ($depth, $done_file) = split($delim, $file, 2 );
		    next if ($depth > $MAX_SPLIT_DEPTH + $MAX_LINK_DEPTH);
		    print FILE "\$$array_name\{\$key\} = '$depth$delim'.\"\$dir\".q|$done_file|$suffix; \n";
		} elsif ($type eq "internals") {
		    print FILE "\$$array_name\{\$key\} = \"\$dir\".q|$file|$suffix; \n";
		} elsif ($array_name eq "sub_index") {
		    print FILE "\$$array_name\{\$key\} .= q|$file|$suffix; \n";
		} elsif ($array_name eq "index") {
		    local($tmp_file) = '';
		    ($depth, $done_file) = split('HREF=\"', $file, 2 );
		    if ($done_file) {
			while ($done_file) {
			    $depth =~ s/\s*$/ / if ($depth);
			    $tmp_file .= "q|${depth}HREF=\"|.\"\$dir\".";
			    ($depth, $done_file) = split('HREF=\"', $done_file, 2 );
			}
			print FILE "\$$array_name\{\$key\} .= ${tmp_file}q|$depth|$suffix; \n";
		    } else {
			print FILE "\$$array_name\{\$key\} .= q|$file|$suffix; \n";
		    }
		} elsif ($array_name eq "printable_key") {
		    print FILE "\$$array_name\{\$key\} = q|$file|$suffix; \n";
		} else {
		    print FILE "\$$array_name\{\$key\} = ${prefix}q|$file|$suffix; \n";
		}
#		if ($type =~ /images/) {
#		    print FILE "\$eqno\{\$key\} = ". $eqno{$key} . "; \n" if ($eqno{$key});
#		}
		if ($type =~ /(figure|table|images)/) {} else {
		    print FILE "\$noresave\{\$key\} = \"\$nosave\";\n";
		}
		if ($type eq "sections") {
		    ($depth, $done_file, $title) = split($delim, $file);
		    print FILE "\$done\{\"\$\{dir\}$done_file\"\} = 1;\n";
		}
	    }
	}
	print FILE "\n1;\n\n"  unless  ( $array_name =~ /index/ );
	close (FILE);
    } else {
	print "\nSAVE_FILE:$array_name: ${PREFIX}$type.pl  EMPTY " if ($VERBOSITY > 1);
    }
}

# returns true if $AUTO_NAVIGATION is on and there are more words in $_
# than $WORDS_IN_PAGE
sub auto_navigation {
    # Uses $_;
    local(@tmp) = split(/\W*\s+\W*/, $_);
    ($AUTO_NAVIGATION && ( (scalar @tmp) > $WORDS_IN_PAGE));
}

# Returns true if $f1 is newer than $f2
sub newer {
    ($f1,$f2) = @_;
    local(@f1s) = stat($f1);
    local(@f2s) = stat($f2);
    ($f1s[9] > $f2s[9]);
};

sub iso_map {
    local($char, $kind) = @_;
    local($character_map);
    if ( $CHARSET && $HTML_VERSION ge "2.1" ) {
	$ISOLATIN_CHARS = 1;
	$character_map=$CHARSET;
	$character_map =~ tr/-/_/;
	eval "\$${character_map}_character_map\{\"$char$kind\"\}";
#    } elsif (!$NO_ISOLATIN) {
#	eval "\$this = \$${character_map}_character_map\{\"$char$kind\"\}";
#	eval "\$${character_map}_character_map_inv\{\"\{\$\mbox\{$this\}\$\}\"\}";
    } else {
	$ISOLATIN_CHARS = 1;
	$iso_8859_1_character_map{"$char$kind"} ||
	    $iso_10646_character_map{"$char$kind"}
    }
}


sub titles_language {
    local($_) = @_;
    local($lang) = $_ . "_titles";
    if (defined(&$lang)) { &$lang }
    else {
	&english_titles;
	&write_warnings(
	    "\nThere is currently no support for the $tmp language." .
	    "\nSee the file latex2html.config for examples on how to add it\n\n");
    }
}

sub translate_titles {
    $toc_title = &translate_commands($toc_title);
    $lof_title = &translate_commands($lof_title);
    $lot_title = &translate_commands($lot_title);
    $idx_title = &translate_commands($idx_title);
    $bib_title = &translate_commands($bib_title);
    $info_title = &translate_commands($info_title);
}
####################### Code Generation Subroutines ############################
# This takes a string of commands followed by optional or compulsory
# argument markers and generates a subroutine for each command that will
# ignore the command and its arguments.
# The commands are separated by newlines and have the format:
##      <cmd_name>#{}# []# {}# [] etc.
# {} marks a compulsory argument and [] an  optional one.
sub ignore_commands {
    local($_) = @_;
    foreach (/.*\n?/g) {
	s/\n//g;
	# For each line
	local($cmd, @args) = split('\s*#\s*',$_);
	next unless $cmd;
	$cmd =~ s/ //;
	++$ignore{$cmd};
	local ($body, $code, $thisone) = ("", "");
	
	# alter the pattern here to debug particular commands
#	$thisone = 1 if ($cmd =~ /span/);

	if (@args) {
	    print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
	    # Replace the argument markers with appropriate patterns
	    foreach $arg (@args) {
		print "\nARG: $arg" if ($thisone);
		if ($arg =~ /\{\}/) {
		    $body .= 'local($cmd) = '."\"$cmd\"".";\n";
		    $body .= '$args .= &missing_braces'."\n ".'unless (';
                    $body .= '(s/$next_pair_pr_rx/$args .= $2;\'\'/eo)'."\n";
                    $body .= '  ||(s/$next_pair_rx/$args .= $2;\'\'/eo));'."\n";
                    print "\nAFTER:$'" if (($thisone)&&($'));
		    $body .= $' if ($');
		} elsif ($arg =~ /\[\]/) {
		    $body .= '($dummy, $pat) = &get_next_optional_argument;'
			. '$args .= $pat;'."\n";
                    print "\nAFTER:$'" if (($thisone)&&($'));
		    $body .= $' if ($');
		} elsif ($arg =~ /<<\s*([^>]*)[\b\s]*>>/) {
		    local($endcmd, $after) = ($1,$');
		    $after =~ s/(^\s*|\s*$)//g;
		    $endcmd =~ s:([\\(){}[\]\^\$*+?.|]):\\$1:g;
		    $body .= 'if (/'.$endcmd.'/o) { $args .= $`; $_ = $\' };'."\n";
                    print "\nAFTER:$after" if (($thisone)&&($after));
		    $body .= "$after" if ($after);
		} else {
                    print "\nAFTER:$'" if (($thisone)&&($arg));
		    $body .= $arg ;
		}
	    }
	    # Generate a new subroutine
#	    $code = "sub do_cmd_$cmd {\n".'local($_) = @_;'. join('',@args) .'$_}';
	    $code = "sub do_cmd_$cmd {\n".'local($_)=@_; local($args);'
		    . "\n" . $body . (($body)? ";\n" : '')
                    . (($thisone)? "print \"\\n$cmd:\".\$args.\"\\n\";\n" : '')
                    . (($arg)? $arg : '$_') . "}";
            print "\n$code\n" if ($thisone); # for error-checking
	    eval ($code);# unless ($thisone);
            if ($@) {print "\n\n*** sub do_cmd_$cmd failed: $@\n"}
	} else {
	    $code = "sub do_cmd_$cmd {\n".'local($_) = @_; $_}';
            print "\n$code\n" if ($thisone); # for error-checking
	    eval ($code);# unless ($thisone);
            if ($@) {print "\n\n*** sub do_cmd_$cmd failed: $@\n"}
        }
    }
}


sub ignore_numeric_argument {
    # Chop this off
    local($num) = '(width|height|plus|minus)*\s*[+-]?[\d\.]+(cm|em|ex|in|pc|pt|mm)?\s*';
    s/^\s*=?\s*($num)*//o unless (/^(\s*\<\<\d+\>\>|$)/);
}

sub process_in_latex_helper {
    local($ctr,$val,$cmd) = @_;
    ($ASCII_MODE ? "[$cmd]" : 
	&process_in_latex("\\setcounter{$ctr}{$val}\\$cmd"))
}

sub do_cmd_catcode {
    local($_) = @_;
    s/^\s*[^=]+(=?\s*\d+\s|\\active)\s?//;
    $_;
}

sub do_cmd_string {
    local($_) = @_;
    local($tok);
    s/^\s*(\\([a-zA-Z]+|.)|[&;]\w+;(#\w+;)?|.)/$tok=$1;''/e;
    if ($2) {$tok = "\&#92;$2"}
    "$tok".$_
}

sub do_cmd_boldmath {
    local($_) = @_;
    $BOLD_MATH = 1;
    $_;
}

sub do_cmd_unboldmath {
    local($_) = @_;
    $BOLD_MATH = 0;
    $_;
}

sub do_cmd_parbox {
    local($_) = @_;
    local($args, $contents, $dum, $pat);
    $* = 1;			# Multiline matching ON
    ($dum,$pat) = &get_next_optional_argument; # discard this
    ($dum,$pat) = &get_next_optional_argument; # discard this
    ($dum,$pat) = &get_next_optional_argument; # discard this
    $args .= $pat if ($pat);
    $pat = &missing_braces
	unless ((s/$next_pair_pr_rx/$pat=$2;''/eo)
	    ||(s/$next_pair_rx/$pat=$2;''/eo));
    $args .= "{".$`.$pat."}";
    $contents = &missing_braces
	unless ((s/$next_pair_pr_rx/$contents=$2;''/eo)
	    ||(s/$next_pair_rx/$contents=$2;''/eo));
    $args .= "{".$`.$contents."}";
    $* = 0;			# Multiline matching OFF
    $contents = &process_math_in_latex('','text',0,"\\parbox$args") if ($contents);
    $contents . $_;
}


sub do_cmd_mbox {
    local($_) = @_;
    local($text,$after)=('','');
    $text = &missing_braces
        unless ((s/$next_pair_pr_rx/$text = $2;''/eo)
		||(s/$next_pair_rx/$text = $2;''/eo));
    $after = $_;

    if ($text =~ /(tex2html_wrap_inline|\$$OP(\d+)$CP$OP\2$CP\$|\$$O(\d+)$C$O\2$C\$)/) {
#	join ('', &process_in_latex("\\hbox{$text}"), $after )
	join ('', &process_math_in_latex('','','',"\\hbox{$text}"), $after )
    } else {
	$text = &translate_environments($text);
	$text = &translate_commands($text);
	join('', $text, $after);
    }
}



# *Generates* subroutines to handle each of the declarations
# like \em, \quote etc., in case they appear with the begin-end
# syntax.
sub generate_declaration_subs {
    local($key, $val, $pre, $post, $code );
    print "\n *** processing declarations ***\n";
    while ( ($key, $val) = each %declarations) {
	if ($val) {
	    $val =~ m|</.*$|;
	    $pre = $`; $post = $&;
	    $pre =~ s/"/\\"/g; $post =~ s/"/\\"/g;
	    $code = "sub do_env_$key {"
		. 'local($_) = @_;' . "\n"
		. '$_ = &translate_environments($_);'. "\n"
		. '$_ = &translate_commands($_);'. "\n"
		. "join('',\"$pre\",\"\\n\"," .'$_' .",\"$post\");\n};";
	    eval $code;
	    if ($@) {print "\n *** $key ".  $@ };
	}
    }
}

# *Generates* subroutines to handle each of the sectioning commands.
sub generate_sectioning_subs {
    local($key, $val, $cmd, $body);
    while ( ($key, $val) = each %section_headings) {
	eval "sub do_cmd_$key {"
	    . 'local($after) = @_;'
		. '&reset_dependents('. $key . ');'
		. '&do_cmd_section_helper('.$val.','.$key.');}';
	# Now define the *-form of the same commands. The difference is that the
	# $key is not passed as an argument.
	eval "sub do_cmd_$key" . "star {"
	    . 'local($after) = @_;'
		. '&do_cmd_section_helper(' . $val . ');}';
	# Now define the macro  \the$key  
	&process_commands_wrap_deferred("the$key \# {}\n");
	local($_) = "<<1>>$key<<1>>";
	&make_unique; $body = $_;
	$cmd = "the$key";
	eval "sub do_cmd_$cmd {"
	    . "local(\$after) = \@_;"
		. '&do_cmd_arabic(' . "\"$body\"" . ").\$after;};";
	$raw_arg_cmds{$cmd} = 1;
    }
    &addto_dependents('chapter','section');
    &addto_dependents('section','subsection');
    &addto_dependents('subsection','subsubsection');
    &addto_dependents('subsubsection','paragraph');
    &addto_dependents('paragraph','subparagraph');
}

sub addto_dependents {
    local($ctr, $dep) = @_;
    local($tmp);
    $tmp = $dependent{$ctr};
    if ($tmp) { 
	$dependent{$ctr} = join($delim, $tmp, $dep);
    } else {
	$dependent{$ctr} = $dep;
    }
}

# Uses $after which is defined in the caller (the caller is a generated subroutine)
# Also uses @curr_sec_id
#
#JCL(jcl-tcl) (changed almost everything)
#
sub do_cmd_section_helper {
    local($H,$key) = @_;
    local($section_number, $titletext, $hash, @tmp, $align, $dummy);
    local($anchors,$pre,$post,$_) = ('', "\n", "\n", $after);

    # if we have a $key the current section is not of the *-form, so we need
    # to update the counters.
    $latex_body .= "\\stepcounter{$key}\n" if $key;
    local ($align, $dummy)=&get_next_optional_argument;
    if ($align =~/(left|right|center)/i) { $align = "ALIGN=\"$1\""; }
    s/$next_pair_rx//eo;
    $titletext = &translate_environments($2);
    $titletext = &translate_commands($titletext);
    # RRM: collect all anchors from \label and \index commands
    ($anchors,$titletext) = &extract_anchors($titletext);
    $hash = &sanitize($titletext);
    # This is the LaTeX section number read from the $FILE.aux file
    @tmp = split(/$;/,$encoded_section_number{$hash});
    $section_number = shift(@tmp);
    $section_number = "" if ($section_number eq "-1");
    $encoded_section_number{$hash} = join($;, @tmp);
    #JKR: Don't prepend whitespace
    $titletext = "$section_number " . $titletext if $section_number;
    $titletext = &simplify($titletext);
#    $TITLE = &purify($titletext);
    local($after) = $_; 
    $_ = $titletext; &remove_anchors; 
    if ($_) { $TITLE = $_ } else { $TITLE = '.' };
    #RRM: no preceding \n when this is the first section-head on the page.
    if (! $key || $key < $MAX_SPLIT_DEPTH) { $pre = '' };
    #RRM: no trailing \n when $after already has one, else a <P> results.
    if ($after =~/^( |\t)*\n/) { $post = '' };

    join('', $pre, &make_section_heading($titletext, $H, $align.$anchors), $post, $after);
}

sub do_cmd_usepackage {
    local($_) = @_;
    # RRM:  allow lists of packages and options
    local ($package, $packages)=('','');
    local ($options,$dum)=&get_next_optional_argument;
    s/$next_pair_pr_rx/$packages = $2;''/eo;
    local($rest) = $_;
    # MRO: The files should have already been loaded by
    #      TMP_styles, but we better make it sure.
    foreach $package (split (',',$packages)) {	# allow multiple packages
	&do_require_package($package);
	if (! $styles_loaded{$package}) {
	    &no_implementation("package",$package);
	} else {
	    if($options =~ /\S+/) { # are there any options?
		&do_package_options($package,$options);
	    }
	}
    }
    $rest;
}

sub no_implementation {
    local($what,$which)= @_;
    print STDERR "\nWarning: No implementation found for $what: $which";
}

sub do_cmd_RequirePackage {
    local($_)= @_;
    local($options,$dum)=&get_next_optional_argument;
    s/$next_pair_pr_rx//o;
    local($file) = $2;
    local($rest) = $_;
    $file =~ s/^[\s\t\n]*//o;
    $file =~ s/[\s\t\n]*$//o;
    # load the package, unless that has already been done
    &do_require_package($file) unless ($styles_loaded{$file});
    # process any options
    if (! $styles_loaded{$file}) {
	    &no_implementation("style",$file);
    } else {
	# process any options
	&do_package_options($file,$options) if ($options);
    }
    $_ = $rest;
    # ignore trailing optional argument
    local($date,$dum)=&get_next_optional_argument;
    $_;
}

sub do_package_options {
    local($package,$options)=@_;
    foreach $option (split (',',$options)) {
        $option =~ s/^[\s\t\n]*//o;
        $option =~ s/[\s\t\n]*$//o;
        if (!($styles_loaded{$package."_$option"})) {
            &do_require_packageoption($package."_$option");
            if (!($styles_loaded{$package."_$option"})) {
		&no_implementation("option","\`$option\' for \`$package\' package\n");
	    }
	}
    }
    $rest;
}

sub do_require_package {
    local($file)= @_;
    local($dir);
    if (! $styles_loaded{$file}) {
	# look for a file named ${file}.perl
	# MRO: use $texfilepath instead of `..'
	if ((-f "$texfilepath$dd${file}.perl") && ! $styles_loaded{$file}){
	    require("$texfilepath$dd${file}.perl");
	    print STDERR "\nLoading $texfilepath$dd${file}.perl";
	    $styles_loaded{$file} = 1;
	} else {
	    foreach $dir (split(/:/,$LATEX2HTMLSTYLES)) {
		if ((-f "$dir$dd${file}.perl") && ! $styles_loaded{$file}){
		    require("$dir$dd${file}.perl");
		    print STDERR "\nLoading $dir$dd${file}.perl";
	    	    $styles_loaded{$file} = 1;
                    last;
		}
	    }
	}
    }
}

sub do_require_packageoption {
    local($option)= @_;
    local($do_option);
    # first look for a file named ${option}.perl
    &do_require_package($option) unless ($styles_loaded{$option});
    # next look for a subroutine named  do_$option
    $do_option = "do_$option";
    if (!($styles_loaded{$option}) && defined(&$do_option)) {
	&$do_option();
	$styles_loaded{$option} = 1;
    }
}

############################ Environments ################################

# This is a dummy environment used to synchronise the expansion
# of order-sensitive macros.
sub do_env_tex2html_deferred {
    local($_) = @_;
    &process_command($single_cmd_rx,*_);
}


# The following list environment subroutines still do not handle
# correctly the case where the list counters are modified (e.g. \alph{enumi})
# and the cases where user defined bullets are mixed with the default ones.
# e.g. \begin{enumerate} \item[(1)] one \item two \end{enumerate} will
# not produce the same bullets as in the dvi output.
sub do_env_itemize {
    local($_) = @_;
    #RRM - catch nested lists
    $_ = &translate_environments($_);
    if (/^\s*$item_description_rx/) { # Contains user defined optional labels
	&do_env_description($_, " COMPACT")}
    else {
	&list_helper($_,'UL');
    }
}

sub do_env_enumerate {
    local($_) = @_;
# Reiner Miericke provided the main code; integrated by RRM: 14/1/97
# works currently only with 'enumerate' and derived environments
# explicit styled labels are computed for each \item
# ultimately the environment is done as:  &do_env_description($_, " COMPACT")
    ++$enum_level;
    local(%enum) = %enum;		# to allow local changes
    $_ = &translate_environments($_);	#catch nested lists

# Reiner: \begin{enumerate}[<standard_label>]
    local($standard_label) = "";
    local(@label_fields);
    local($label_func);
    local($rlevel) = &froman($enum_level); # e.g. 3 => iii

    # \begin{enumerate}[$standard_label]
    if (/^$standard_label_rx/) {		# multiline on/off ?
	# standard label should be used later to modify
	# entries in %enum
	$standard_label = $1;		# save the standard label
	s/^$standard_label_rx//;	# and cut it off

	# Search for [aAiI1] which is not between a pair of { }
	# Other cases like "\theenumi" are not handled
	@label_fields = $standard_label =~ /$enum_label_rx/;
#	$label_func = $enum_label_funcs{@label_fields[$#label_fields-1]} . 
#		"(\'enum" . $rlevel . "\')";
	$label_func = $enum_label_funcs{@label_fields[$#label_fields-1]} . 
		"(\'enum" . $rlevel . "\')";
	$enum{'theenum' . $rlevel} = "\&$label_func";
#	$standard_label = 
#		"\"@label_fields[0]\" . eval(\$enum{\"theenum$rlevel\"}) .
#		\"@label_fields[$#label_fields]\"";
	$standard_label = 
		"\"$label_fields[0]\" . eval(\$enum{\"theenum$rlevel\"}) .
		\"$label_fields[$#label_fields]\"";
	$enum{'labelenum' . $rlevel} = "$standard_label";

    }
    # split it into items
    @items = split(/\\item/,$_);
    # save anything (non-blank) before the items actually start
    local($preitems) = shift(@items);
    $preitems =~ s/^\s*$//;
    local($enum_label);
    # prepend each item with an item label: \item => \item[<label>]
    foreach $item (@items) {
#	unless ( $item =~ /^\s*$/ ) { # first line may be empty
	    $enum{"enum" . $rlevel}++;	# increase enumi
	    $enum_label = eval("$enum{'labelenum' . $rlevel}");
	    # insert a label, removing preceding space, BUT...
	    # do NOT handle items with existing labels
	    $item =~ s/^\s*//;
	    if ($item =~ /^\s*\[([^]]*)\]/) {
		$enum{"enum" . $rlevel}--;
		$enum_label = $1;
	    } else { 
		$item = "[$enum_label\]$item";
		$enum_label =~ s/\.$//;
	    }
	    $item =~ s/\\labelitem$rlevel/$enum_label/g ;
#	}
    }
    $_ = join("\\item", $preitems, @items);

# Original, but $result
    local($result) = &do_env_description($_, " COMPACT");
    $enum{"enum" . $rlevel} = 0;
    $enum{"enum" . &froman($enum_level)} = 0;
    --$enum_level;
    $result;
}

sub do_env_list {
    local ($_) = @_;
    #RRM - catch nested lists
    $_ = &translate_environments($_);
    local ($list_type) = 'UL';

    s/$next_pair_rx//;		# Ditch the label specifier
    s/$next_pair_rx//;		# Ditto the length declarations ...
    # but we may want to switch to enumerated style
    # if they include a \usecounter.

    $list_type = 'OL' if $1 =~ /\\usecounter/;

    &list_helper($_, $list_type);
}

sub do_env_description {
    local($_, $compact) = @_;
    #RRM - catch nested lists
    $_ = &translate_environments($_);
    $compact = "" unless $compact;
    $* = 1;			# Multiline matching ON
    if ($compact) {		# itemize/enumerate with optional labels
	s/$item_description_rx\s*($labels_rx8)?\s*/"<DT>". 
	    (($9)? "<A NAME=\"$9\">$1<\/A>" : $1 ) ."\n<DD>"/eg;
    } else {
	s/$item_description_rx\s*($labels_rx8)?\s*/"<DT>". 
	    (($9)? "<A NAME=\"$9\"><STRONG>$1<\/STRONG><\/A>" 
	     : "<STRONG>$1<\/STRONG>") ."\n<DD>"/eg;
    }
    # and just in case the description is empty ...
#JCL(jcl-del) - $delimiter_rx -> ^$letters
    s/\\item([^$letters])\s*/<DT><DD>$1/g;
    s/^\s+//;
    $* = 0;			# Multiline matching OFF
    "<DL$compact>\n$_</DL>";
}

sub list_helper {
    local($_, $tag) = @_;
    local($item_sep) = "<LI>";
    $* = 1;			# Multiline matching ON
    # This deals with \item[xxx] ...
    if ($tag =~ /DL/) {
	$item_sep = "<DD>";
	s/$item_description_rx\s*($labels_rx8)?\s*/"<DT>" .
	    (($9)? "<A NAME=\"$9\">$1<\/A>" : $1 ) ."\n<DD>"/eg;
    }
#    s/$item_description_rx\s*/<DT>$1\n<DD>/g;
    s/\s*\\item/\\item/g;
#JCL(jcl-del) - $delimiter_rx -> ^$letters
    s/\\item([^$letters])\s*/\n$item_sep$1/g;
    $* = 0;			# Multiline matching OFF
    "<$tag>$_</$tag>";
}

# RRM:  A figure environment generates a picture UNLESS it contains a 
# {makeimage} sub-environment; in which case it creates a <DIV>
# inside which the contents are interpreted as much as is possible.
# When there are captions, this modifies $before .
sub do_env_figure {
    local($_) = @_;
    local($halign, $anchors) = ('CENTER','');
    local ( $border, $attribs );
    &get_next_optional_argument;

    ($_,$anchors) = &extract_labels($_); # extract labels
    # Try to establish the alignment
    if (/^(\[[^\]]*])?\s*\\begin<<\d*>>(\w*)<<\d*>>|\\(\w*)line/) {
        $halign = $2.$3;
        if ($halign =~ /right/i)  { $halign = 'RIGHT' }
        elsif ($halign =~ /left/i) { $halign = 'LEFT' }
        elsif ($halign =~ /center/i) { $halign = 'CENTER' }
        else { $halign = '' }
    }

    if (/\\begin<<\d+>>\s*makeimage\s*<<\d+>>/) {
	$_ = &translate_environments($_);
	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
	$_ = &translate_commands($_);
	while ($_ =~ s/(^\s*<BR>\s*|\s*<BR>\s*$)//g){}; # remove unneeded breaks
    } else {
	# Generate picture of the whole environment
	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
	$_ = &process_undefined_environment($env, $id, $_);
	$_ = &post_latex_do_env_figure($_);
	$_ =~ s/\s*<BR>\s*$//g;
    }

    if ($captions) { $* = 1; $captions =~ s/^\n//; $captions =~ s/\n$//; $* = 0; }

    # place all the pieces inside a TABLE, if available
    if ($HTML_VERSION > 2.1) {
	if ($captions) {
	    $halign = 'CENTER' unless $halign;
	    local($table) = '<TABLE';
	    if ($border) { $table .= " BORDER=\"$border\"" } # no checking !!
	    $table .= ">";
	    s/^\s*|\s*$//g;
	    join ('',"<BR>\n<DIV ALIGN=\"$halign\">$anchors$cap_anchors\n$table\n<CAPTION>"
		, $captions , "</CAPTION>\n<TR><TD>" 
		, $_ , "</TD></TR>\n</TABLE>\n</DIV><BR>" )
        } elsif ($halign) {
	    if ($border||($attributes)) {
		&make_table( $border, $attribs, $anchors, '', $halign, $_ );
	    } else { join ('', "<BR>\n$anchors", $_ , "\n<BR>" ) }
	} else {
	    if ($border||($attributes)) {
		join ('', "<BR>\n<DIV ALIGN=\"CENTER\">"
		      , &make_table( $border, $attribs, $anchors, '', $halign, $_ )
		      , "\n</DIV><BR>");
	    } else {  
		join ('', "<BR>\n<DIV ALIGN=\"CENTER\">$anchors\n" , $_ , "\n</DIV><BR>" );
	    }
	}
    } else {
	$*=1; s/^\n//; s/\n$//; $*=0;
	if ($captions) {
	    $halign = 'CENTER' unless $halign;
	    join('', "<BR>\n", (($anchors) ? "$anchors" : ''), "$cap_anchors\n$captions\n<BR>" 
		, "\n<P ALIGN=\"$halign\">", $_, "\n</P><BR>");
        } elsif ($halign) {
            join ('', "<BR>\n$anchors", $_ , "\n<BR>" )
	} else {
	    join('', "<BR>\n<P ALIGN=\"CENTER\">$anchors\n", $_, "\n</P><BR>");
	}
    }
}

sub do_env_figurestar {
    &do_env_figure(@_);
}

sub do_env_table {
    local($_) = @_;
    local($halign, $anchors) = ('','');
    local ( $border, $attribs );
    &get_next_optional_argument;

    # Try to establish the alignment 
    if (/^(\[[^\]]*])?\s*\\begin<<\d*>>(\w*)<<\d*>>|\\(\w*)line/) {
	$halign = $2.$3;
	if ($halign =~ /right/i)  { $halign = 'RIGHT' }
	elsif ($halign =~ /left/i) { $halign = 'LEFT' }
	elsif ($halign =~ /center/i) { $halign = 'CENTER' }
	else { $halign = '' }
    }

    if ((/\\begin<<\d+>>\s*makeimage\s*<<\d+>>/)||
	    ($HTML_VERSION > 2.0 && (/\\begin<<\d+>>\s*tabular\s*<<\d+>>/))) {
	$_ = &translate_environments($_);
        ($_,$anchors) = &extract_labels($_); # extract labels
	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
	$_ = &translate_commands($_);
	while ($_ =~ s/(^\s*<BR>\s*|\s*<BR>\s*$)//g){};
    } else {
	# Make an image of the whole environment.
        ($_,$anchors) = &extract_labels($_); # extract labels
	if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
	$_ = &process_undefined_environment($env, $id, $_);
	$_ = &post_latex_do_env_table($_);
	$_ =~ s/\s*<BR>\s*$//g;
    }

    if ($captions) { $* = 1; $captions =~ s/^\n//; $captions =~ s/\n$//; $* = 0; }

    #  when $captions remain place all the pieces inside a TABLE, if available
    if ($HTML_VERSION > 2.1) {
        if ($captions) {
	    if (!($halign)) { $halign = 'CENTER' };
	    local($table) = '<TABLE';
	    if ($border) { $table .= " BORDER=\"$border\"" } # no checking !!
	    $table .= ">";
            join ('', "<BR>\n<DIV ALIGN=\"$halign\">$anchors$cap_anchors\n$table\n<CAPTION>"
                , $captions , "</CAPTION>\n<TR><TD>" 
		, $_ , "</TD></TR>\n</TABLE>\n</DIV><BR>" )
        } elsif ($halign) {
	    if ($halign) {
                $* = 1;
		s/^\s*(<(P|DIV) ALIGN=\"\w+[^>]+>)/$1$anchors/ if ($anchors);
                $* = 0;
		join('', "<BR>", $_, "\n<BR>" )
            } else {
		join ('', "<BR>\n$anchors", $_ , "\n<BR>" )
	    }
        } else {
            join ('', "<BR>\n<DIV ALIGN=\"CENTER\">$anchors\n", $_ , "\n</DIV><BR>" )
        }
    } else {
        $*=1; s/^\n//; s/\n$//; $*=0;
        if ($captions) {
            join('', "<BR>\n", (($anchors) ? "$anchors" : ''), "$cap_anchors\n$captions\n<BR>"
                , "\n<P ALIGN=\"$halign\">", $_, "\n</P><BR>");
        } elsif ($halign) {
            join ('', "<BR>\n$anchors", $_ , "\n<BR>" )
        } else {
            join('', "<BR>\n<P ALIGN=\"CENTER\">$anchors\n", $_, "\n</P><BR>");
        }
    }
}

sub do_env_tablestar {
    &do_env_table(@_);
}

# RRM:  A makeimage environment generates a picture of its entire contents, 
#  UNLESS it is empty.
#
sub do_env_makeimage {
    local($_) = @_;
    local($attribs, $border);
    s/^\s*//;
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    if (!($_)) { return() }
    $_ = &process_undefined_environment($env, $id, $_);
    if (($border||($attributes))&&($HTML_VERSION > 2.1 )) { 
	$_ = &make_table( $border, $attribs, '', '', '', $_ ) }
    $_ . "\n<BR>\n";
}

sub do_env_verse {
    local($_) = @_;
    "<P> \n$_  <P>";
}

sub do_env_abstract {
    local($_) = @_;
    &make_abstract($_);
}

sub do_env_minipage {
    local($_) = @_;
    &get_next_optional_argument;
    s/$next_pair_rx//o;
    local ( %mpfootnotes, $mpfootnotes ) unless ($MINIPAGE);
    local ( $border, $attribs );
    $global{'mpfootnote'} = 0 unless ($MINIPAGE);
    $MINIPAGE++;
    print "\n *** doing minipage *** " if ($VERBOSITY > 1);
    $_ = &translate_environments($_);
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    $_ = &translate_commands($_);
    $MINIPAGE--;
    if (($border||($attributes))&&($MINIPAGE)&&($HTML_VERSION > 2.1 )) { 
	&make_table( $border, $attribs, '', '', '', $_ );
    } elsif ($MINIPAGE) { 
	join ('', '<BR><HR>', $_ , '<BR><HR><BR>' );
    } elsif (($border||($attributes))&&($HTML_VERSION > 2.1 )) {
	&make_table( $border, $attribs, '', $mpfootnotes, '', $_ );
    } else {
	$global{'mpfootnote'} = 0;
	if ($mpfootnotes) {
	    join('','<BR><HR>', $_ , '<BR><HR'
		 , (($HTML_VERSION > 3.0)? ' WIDTH="200" ALIGN="left"' : '')
		 , '><DL>', $mpfootnotes , '</DL><HR><BR'
		 , (($HTML_VERSION > 3.0)? ' CLEAR="all"' : '')
		 , '>' );
	} else {
	    join ('', '<BR><HR>', $_ , '<BR><HR><BR>' );
	}
    }
}

if ($HTML_VERSION > 2.1) {
    $TABLE_attribs = ",ALIGN,";
    $TABLE__ALIGN = ",left,right,center,";
    $TABLE_attribs_rx_list = ",CELLPADDING,BORDER,WIDTH,CELLSPACING,";
    $TABLE__WIDTH_rx = "\^\\d+%?";
    $TABLE__BORDER_rx = $TABLE__CELLSPACING_rx = $TABLE__CELLPADDING_rx = "\^\\d+";
}

sub make_table {
    local($border, $attribs, $anchors, $extra_cell, $halign, $_) = @_;
    local($table,$caption,$div,$end,$Tattribs);
    $end = "</TD></TR>\n</TABLE>";
    $table = "<TABLE" . (($border) ? " BORDER=\"$border\"" : '');
    if ($attribs) {
	if (!($attribs =~ /=/)) {
	    $Tattribs = &parse_valuesonly($attribs,"TABLE");
	} else {
	    $Tattribs = &parse_keyvalues($attribs,"TABLE");
	}
	$table .= " $Tattribs" if ($Tattribs);
    }
    print "\nTABLE: $table>" if ($VERBOSITY >2 );
    $table .= ">\n<TR><TD>";
    if ($extra_cell) {
	local($sep) = "</TD></TR>\n<TR ALIGN=\"LEFT\">\n<TD>";
	join ('', $div, $anchors, $table, $_ , $sep, $extra_cell, $end );
    } else {
	join ('', $div, $anchors, $table, $_ , $end );
    }
}

sub do_env_thebibliography {
    # Sets $citefile and $citations defined in translate
    local($_) = @_;
    $bibitem_counter = 0;
    $citefile = $CURRENT_FILE;
    $citefiles{$bbl_nr} = $citefile;
    s/$next_pair_rx//o;
    $* = 1;	                # Multiline matching ON
    s/^\s*$//g;	      # Remove empty lines (otherwise will have paragraphs!)
    s/^\s*//;
    $* = 0;			# Multiline matching OFF

    # Replace non-breaking spaces, particularly in author names.
    s/([^\\])~/$1 /g; # Replace non-breaking spaces.

    $citations = join('',"<DL COMPACT>",
	 &translate_commands(&translate_environments($_)),"</DL>");
    $citations{$bbl_nr} = $citations;
    $_ = join('', "<H2><A NAME=\"SECTIONREF\">"
	      , "$bib_title</A>\n</H2>\n$bbl_mark#$bbl_nr#");
    $bbl_nr++ if $bbl_cnt > 1;
    $_;
}

# IGNORE the contents of this environment - We construct our own index
sub do_env_theindex {
    "";
}


# This is defined in html.sty
sub do_env_comment {
    "";
}

sub do_env_equation{
    local($_)=@_;  
    local($attribs, $border);
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    if (/\\nonumber/) {
	$_ = &process_undefined_environment($env,$id,$_);
    } else {
	$_ = &process_undefined_environment($env,$id,$_);
	local($save) = $_;
	$_ = join('', $save, &post_latex_do_env_equation($eqno_prefix));
    }
    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) { 
	join('',"<BR>\n<DIV ALIGN=\"CENTER\">\n"
            , &make_table( $border, $attribs, '', '', '', $_ )
	    , "\n<BR CLEAR=\"ALL\">");
    } else { $_ }
}

sub do_env_eqnarray{
    local($_)=@_;
    local($attribs, $border);
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    local($contents) = $_;
    $_ = &process_undefined_environment($env,$id,$_);
    $_ .= &post_latex_do_env_eqnarray($eqno_prefix,$contents);
    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) { 
	join('',"<BR>\n<DIV ALIGN=\"CENTER\">\n"
            , &make_table( $border, $attribs, '', '', '', $_ )
	    , "\n<BR CLEAR=\"ALL\">");
    } else { $_ }
}


sub do_env_tabbing {
    local($_) = @_;
    local($attribs, $border);
    if (s/$htmlborder_rx//o) { $attribs = $2; $border = (($4)? "$4" : 1) }
    $_ = &tabbing_helper($_);
    if (($border||($attribs))&&($HTML_VERSION > 2.1 )) { 
	join('',"<BR>\n<DIV ALIGN=\"CENTER\">\n"
            , &make_table( $border, $attribs, '', '', '', $_ )
	    , "\n<BR CLEAR=\"ALL\">");
    } else { $_ }
}

sub tabbing_helper {
    local($_) = @_;
    s/\\=\s*//go;  # cannot alter the tab-stops
    s/\t/ /g;      # convert any tabs to spaces
    $* = 1;			# Multiline matching ON
    s/(^|\n)[^\n]*\\kill *\n/\n/g;
    s/( )? *\n/$1/g; # retain at most 1 space for a \n
    # replace \\ by \n ... , ignoring any trailing space
    s/\\\\ */\n/g;
    # ...but make sure successive \\ do not generate a <P> tag
    s/\n( *)?\n/\n&nbsp;\n/g;
    $* = 0;			# Multiline matching OFF
    s/\\\&gt;//go;
    s/(^| *([^\\]))\\[>]/$2\t\t/go;
    s/([^\\])\\>/$1\t\t/go;
    s/\n$//; s/^\n//;           # strip off leading/trailing \n
    "<PRE><TT>\n$_\n</TT></PRE>";
}

################# Post Processing Latex Generated Images ################

# A subroutine of the form post_latex_do_env_<ENV> can be used to
# format images that have come back from latex

# Do nothing (avoid the paragraph breaks)
sub post_latex_do_env_figure {
    $_[0];
}
sub post_latex_do_env_figurestar {
    &post_latex_do_env_figure(@_);
}

sub post_latex_do_env_table {
    $_[0];
}
sub post_latex_do_env_tablestar {
    &post_latex_do_env_table(@_);
}

sub post_latex_do_env_equation {
    local($prefix) = @_;
    $global{'eqn_number'}+=1;
    # include equation number at the side of the image -- HTML 3.2
    if ($HTML_VERSION >= 3.2){
	"<P ALIGN=\"" . (($EQN_TAGS eq "L") ? "left" : "right")
		. "\">$EQNO_START" . $prefix . $global{'eqn_number'}
		. "$EQNO_END</P>\n<BR CLEAR=\"all\">"
    # </P> creates unwanted space in some browsers, but others need it.
    } else { "" }
}

sub do_cmd_theequation {
#    join('',$EQNO_START, &get_counter_value('eqn_number'), $EQNO_END, @_[1]);
    join('',$EQNO_START, &get_counter_value('eqn_number'), $EQNO_END, $_[1]);
}

sub post_latex_do_env_eqnarray {
    local($prefix,$body) = @_;
    local($num_string,$line,@lines) = '';
    local($side) = (($EQN_TAGS eq "L") ? "\"left\"" : "\"right\"" );
    $*=1;
    @lines = split('\\\\\\\\',$body);
    $*=0;
    $line = pop(@lines);
    if (!($line=~/^\s*$/)&&!($line =~/\\nonumber/)) {
	$global{'eqn_number'}+=1;
	$num_string .= "<BR><BR>\n".$EQNO_START . $prefix . $global{'eqn_number'} . $EQNO_END;
    }
    foreach $line (@lines) {
	next if ($line=~/^\s*$/);
	$num_string .= "\n<BR>". (($MATH_SCALE_FACTOR > 1.3)? '<BR>' : '')
	                . "<BR CLEAR=$side>";
	if (!($line =~/\\nonumber/)) {
	    $global{'eqn_number'}+=1;
	    $num_string .= $EQNO_START.$prefix. $global{'eqn_number'}.$EQNO_END;
	 }
    }
    # include equation numbers at the side of the image -- HTML 3.2
    if ($HTML_VERSION >= 3.2){
        "<P ALIGN=\"" . (($EQN_TAGS eq "L") ? "left" : "right")
                . "\">" . (($DISP_SCALE_FACTOR >= 1.2 ) ? '<BIG>' : '')
		. ${num_string}
                . (($DISP_SCALE_FACTOR >= 1.2 ) ? '</BIG>' : '')
		. "</P>\n<BR CLEAR=\"all\">"
    # </P> creates unwanted space in some browsers, but others need it.
    } else { "" };
}
sub post_latex_do_env_eqnarraystar {
    $_[0];
}

############################ Commands ###################################

# Capitalizes what follows the \sc declaration
# *** POTENTIAL ERROR ****
# (This is NOT the correct meaning of \sc in the cases when it
# is followed by another declaration (e.g. \em).
# The scope of \sc should be limited to the next occurence of a
# declaration.
sub do_cmd_sc {
    local($_) = @_;
    local(@words) = split(" ");
    # Capitalize the words which are not commands and do not contain any markers
#   grep (do {tr/a-z/A-Z/ unless /(^\\)|(tex2html)/}, @words);
    grep (do {s/([a-z]+)/<font size=-1><small>\U$1\E<\/small><\/font>/g unless /(^\\)|(tex2html)/}, @words);
    join(" ", @words);
}

# This is supposed to put the font back into roman.
# Since there is no HTML equivalent for reverting
# to roman we keep track of the open font tags in
# the current context and close them.
# *** POTENTIAL ERROR ****#
# This will produce incorrect results in the exceptional
# case where \rm is followed by another context
# containing font tags of the type we are trying to close
# e.g. {a \bf b \rm c {\bf d} e} will produce
#       a <b> b </b> c   <b> d   e</b>
# i.e. it should move closing tags from the end
sub do_cmd_rm {
    local($_, @open_font_tags, @open_size_tags) = @_;
    local($next,$open,$close);
    for $next (@open_font_tags) {
	$declarations{$next} =~ m|</.*$|;
	$open = $';
	$close = $&;
	s/$close//;
	$_ = join('',$close,$_);
    }
    $_;
}

# This is supposed to put the font back into normalsize.
# Since there is no HTML equivalent for reverting
# to normalsize we keep track of the open size tags in
# the current context and close them.
# *** POTENTIAL ERROR ****#
# This will produce incorrect results in the exceptional
# case where \normalsize is followed by another context
# containing font tags of the type we are trying to close
# e.g. {a \small b \normalsize c {\small d} e} will produce
#       a <SMALL> b </SMALL> c   <SMALL> d   e</SMALL>
# i.e. it should move closing tags from the end
sub do_cmd_normalsize {
    local($_, @open_size_tags) = @_;
    local($next,$open,$close);
#    print "\n";
    for $next (@open_size_tags) {
	$declarations{$next} =~ m|</.*$|;
	$open = $';
	$close = $&;
	s/$close//;
	$_ = join('',$close,$_);
    }
    $_;
}

#JCL(jcl-tcl)
# changed everything
#
sub do_cmd_title {
    local($_) = @_;
    &get_next_optional_argument;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $2));
    $t_title = &simplify(&translate_commands($next));
    $TITLE = (($t_title)? $t_title : $default_title);
    $_;
}

sub do_cmd_author {
    local($_) = @_;
    &get_next_optional_argument;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $&));
    ($t_author) = &simplify(&translate_commands($next));
    $_;
}

sub do_cmd_address {
    local($_) = @_;
    &get_next_optional_argument;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $&));
    ($t_address) = &simplify(&translate_commands($next));
    $_;
}

sub do_cmd_dedicatory {
    local($_) = @_;
    &get_next_optional_argument;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $&));
#    local($rest) = $_;
#    $rest =~ s/$next_pair_pr_rx//o;
#    ($t_affil) = &simplify(&translate_commands($&));
#    $rest;
    ($t_affil) = &simplify(&translate_commands($next));
    $_;
}

sub do_cmd_email {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $2));
    local($mail) = &translate_commands($next);
    ($t_email) = &make_href("mailto:$mail","$mail");
    $_;
}

sub do_cmd_authorURL {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $2));
    ($t_authorURL) =  &translate_commands($next);
    $_;
}

sub do_cmd_date {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($next = $&));
    ($t_date) = &translate_commands($next);
    $_;
}

sub do_cmd_maketitle {
    local($_) = @_;
    local($the_title) = '';
    if ($t_title) {
	$the_title .= "<H1 ALIGN=\"CENTER\">$t_title</H1>";
    } else { &write_warnings("This document has no title."); }
    if ($t_author) {
	$the_title .= "\n<P ALIGN=\"CENTER\"><STRONG>$t_author</STRONG></P>";
    } else { &write_warnings("There is no author for this document."); }
    if ($t_affil) {
	$the_title .= "\n<P ALIGN=\"CENTER\"><I>$t_affil</I></P>";}
    if ($t_date) {
	$the_title .= "\n<P ALIGN=\"CENTER\"><STRONG>$t_date</STRONG></P>";}
    if ($t_address) {
	$the_title .= "<BR>\n<P ALIGN=\"LEFT\"><SMALL>$t_address</SMALL></P>";
    } else { $the_title .= "\n<P ALIGN=\"LEFT\">"}
    if ($t_email) {
	$the_title .= "\n<P ALIGN=\"LEFT\"><SMALL>$t_email</SMALL></P>";
    } else { $the_title .= "</P>" }
    $the_title . $_ ;
}

sub do_cmd_abstract {
    local($_) = @_;
    local($abstract);
    $abstract = &missing_braces
        unless ((s/$next_pair_pr_rx//o)&&($abstract = $&));
    join('',&make_abstract($abstract), $_);
}

sub make_abstract {
    local($_) = @_;
# HWS  Removed emphasis (hard to read)	   # RRM: which DTD is this markup ???
    join('',"\n<H3 CLASS=\"ABSTRACT\">$abs_title:</H3>\n<P CLASS=\"ABSTRACT\">$_</P>\n<P>"); ## AYS
}

sub do_cmd_today {
    #JKR: Make it more similar to LaTeX
    ## AYS: moved french-case to styles/french.perl
    local($today) = &get_date;

    $today =~ s|(\d+)/0?(\d+)/|$Month[$1] $2, |;
    join('',$today,$_[0]);
}

sub do_cmd_textbackslash {
   join('','&#92;', $_[0]);
}

sub do_cmd_ldots {
    join('',($math_mode ? "\&ldots;" : "..."),$_[0]);
}

#sub do_cmd_dots {
#    join('',($math_mode ? "\&ldots;" : "..."),$_[0]);
#}

sub do_cmd_hrule {
    local($_) = @_;
    &ignore_numeric_argument;
    #JKR: No need for <BR>
    join('',"<HR>", $_);
}

sub do_cmd_linebreak {
    local($num,$dum) = &get_next_optional_argument;
    if (($num)&&($num<4)) { return $_[0] }
    join('',"<BR>", $_[0]);
}

sub do_cmd_pagebreak {
    local($_) = @_;
    local($num,$dum) = &get_next_optional_argument;
    if (($num)&&($num<4)) { return($_) }
    elsif (/^ *\n *\n/) { join('',"<BR>\n<P>", $') }
    else { $_ }
}


sub do_cmd_newline {
    join('',"<BR>", $_[0]);
}

# this allows for forced newlines in tables, etc.
sub do_cmd_endgraf {
    join('',"<BR>", $_[0]);
}

sub do_cmd_dots {
    join('',"...",$_[0]);
}

sub do_cmd_space {
    join(''," ",$_[0]);
}

sub do_cmd_enspace {
    join('',"\&nbsp;",$_[0]);
}

sub do_cmd_quad {
    join('',"\&nbsp;\&nbsp;",$_[0]);
}

sub do_cmd_qquad {
    join('',"\&nbsp;\&nbsp;\&nbsp;\&nbsp;",$_[0]);
}

sub do_cmd_par {
    join('',"\n<P>",$_[0]);
}

sub do_cmd_medskip {
    join('',"<P>\n<BR>",$_[0]);
}

sub do_cmd_smallskip {
    join('',"<P></P>",$_[0]);
}

sub do_cmd_bigskip {
    join('',"<P>\n<P>\n<BR>",$_[0]);
}

# MEH: Where does the slash command come from?
# sub do_cmd_slash {
#    join('',"/",$_[0]);
#}
sub do_cmd_esc_slash {
    $_[0];
}

sub do_cmd_esc_hash {
    "\#". $_[0];
}
sub do_cmd_esc_dollar {
    "\$". $_[0];
}
sub do_cmd__at_ {
    $_[0];
}

sub do_cmd_lbrace {
    "\{". $_[0];
}
sub do_cmd_rbrace {
    "\}". $_[0];
}
sub do_cmd_Vert {
    "||". $_[0];
}
sub do_cmd_backslash {
    "\\". $_[0];
}


#JCL(jcl-del) - the next two ones must only have local effect.
# Yet, we don't have a mechanism to revert such changes after
# a group has closed.
#
sub do_cmd_makeatletter {
    $letters =~ s/@//;
    $letters .= '@';
    &make_letter_sensitive_rx;
    $_[0];
}

sub do_cmd_makeatother {
    $letters =~ s/@//;
    &make_letter_sensitive_rx;
    $_[0];
}


################## Commands to be processed by Latex #################
#
# The following commands are passed to Latex for processing.
# They cannot be processed at the same time as normal commands
# because their arguments must be left untouched by the translator.
# (Normally the arguments of a command are translated before the
# command itself).
#
# In fact, it's worse:  it is not correct to process these
# commands after we process environments, because some of them
# (for instance, \parbox) may contain unknown or wrapped
# environments.  If math mode occurs in a parbox, the
# translate_environments routine should *not* process it, lest
# we encounter the lossage outlined above.
#
# On the other hand, it is not correct to process these commands
# *before* we process environments, or figures containing
# parboxes, etc., will be mishandled.
#
# RRM: (added for V97.1) 
#  \parbox now uses the  _wrap_deferred  mechanism, and has a  do_cmd_parbox
#  subroutine defined. This means that environments where parboxes are
#  common (.g. within table cells), can detect the \parbox command and
#  adjust the processing accordingly.
#
# So, the only way to handle these commands is to wrap them up
# in null environments, as for math mode, and let translate_environments
# (which can handle nesting) figure out which is the outermost.
#
# Incidentally, we might as well make these things easier to configure...

sub process_commands_in_tex {
    local($_) = @_;
    local($arg);
    foreach (/.*\n?/g) {
	chop;
	# For each line
	local($cmd, @args) = split('#',$_);
	next unless $cmd;
	$cmd =~ s/ //g;

	# Build routine body ...
	local ($body, $code, $thisone) = ("", "");

	# alter the pattern here to debug particular commands
#	$thisone = 1 if ($cmd =~ /span/);

	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
	foreach $arg (@args) {
	    print "\nARG: $arg" if ($thisone);
	    if ($arg =~ /\{\}/) {
		$body .= '$args .= "$`$&" if s/$next_pair_rx//o;'."\n"; 
		print "\nAFTER:$'" if (($thisone)&&($'));
		$body .= $' if ($');
	    } elsif ($arg =~ /\[\]/) {
		$body .= '($dummy, $pat) = &get_next_optional_argument;'
		    . '$args .= $pat;'."\n";
		print "\nAFTER:$'" if (($thisone)&&($'));
		$body .= $' if ($');
	    } elsif ($arg =~ /<<\s*/) {
		$arg = $';
		if ($arg =~ /\s*>>/) {
		    $body .= '$args .= "$`$&" if (/\\\\'.$`.'/);' . "\n"
			. "\$_ = \$\';\n";
		    print "\nAFTER:$'" if (($thisone)&&($'));
		    $body .= $' if ($');
		} else { $body .= $arg ; }
	    } else {
	        print "\nAFTER:$'" if (($thisone)&&($arg));
		$body .= $arg ;
	    }
	}

	# Generate a new subroutine
	$code = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";' . "\n"
	    .$body . "\n"
            . (($thisone)? "print \"\\n$cmd:\".\$args.\"\\n\";\n" : '')
	    .'(&make_wrapper(1)."\n".$cmd." ".$args."\n".&make_wrapper(0), $_)}'
	    ."\n";
	print "\nWRAP_CMD: $code " if ($thisone); # for debugging
	eval $code;# unless ($thisone);
	print STDERR "\n*** sub wrap_cmd_$cmd  failed: $@" if ($@);

	# And make sure the main loop will catch it ...
	$raw_arg_cmds{$cmd} = 1;
    }
}

sub process_commands_nowrap_in_tex {
    local($_) = @_;
    local($arg);
    foreach (/.*\n?/g) {
	chop;
	local($cmd, @args) = split('#',$_);
	next unless $cmd;
	$cmd =~ s/ //g;
	# Build routine body ...
	local ($bodyA, $codeA, $bodyB, $codeB, $thisone) = ("", "", "", "");

	# alter the pattern here to debug particular commands
#	$thisone = 1 if ($cmd =~ /color/);

	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
	foreach $arg (@args) {
	    print "\nARG: $arg" if ($thisone);
	    if ($arg =~ /\{\}/) {
		$bodyA .= '$args .= "$`"."$&" if (s/$next_pair_rx//);'."\n";
		$bodyB .= '$args .= &missing_braces'."\n unless (";
		$bodyB .= '(s/$any_next_pair_pr_rx/$args.=$`.$1;\'\'/eo)'."\n";
		$bodyB .= '  ||(s/$any_next_pair_rx/$args.=$`.$1;\'\'/eo));'."\n";
		print "\nAFTER:$'" if (($thisone)&&($'));
#		$bodyA .= $' if ($');
		$bodyB .= $' if ($');
	    } elsif ($arg =~ /\[\]/) {
		$bodyA .= '($dummy, $pat) = &get_next_optional_argument;'
		    . '$args .= $pat;'."\n";
		print "\nAFTER:$'" if (($thisone)&&($'));
#		$bodyA .= $' if ($');
		$bodyB .= $' if ($');
	    } elsif ($arg =~ /<<\s*/) {
		$arg = $';
		if ($arg =~ /\s*>>/) {
		    $bodyA .= '$args .= $`.$& if (/\\\\'.$`.'/);' . "\n"
			. "\$_ = \$\';\n";
		    print "\nAFTER:$'" if (($thisone)&&($'));
#		    $bodyA .= $' if ($');
		    $bodyB .= $' if ($');
		} else { 
		    print "\nAFTER:$arg" if (($thisone)&&($arg));
#		    $bodyA .= $arg if ($arg);
		    $bodyB .= $arg if ($arg);
		}
	    } else { 
		print "\nAFTER:$arg" if (($thisone)&&($arg));
#		$bodyA .= $arg if ($');
		$bodyB .= $arg if ($'); 
	    }
	}
	# Generate 2 new subroutines
	$codeA = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local($args, $dummy, $pat) = "";'."\n"
	    . $bodyA
            . (($thisone)? ";print \"\\nwrap $cmd:\".\$args.\"\\n\$_\\n\";\n" : '')
	    . '(&make_nowrapper(1)."\n".$cmd." ".$args."\n".&make_nowrapper(0)," ". $_)}'
	    ."\n";
	print "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
	eval $codeA;
	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
	$codeB = "do_cmd_$cmd";
	do {
	    $bodyB = '"";' if !($bodyB);
	    $codeB = "sub do_cmd_$cmd {" . "\n"
		. 'local($_) = @_;'
		. 'local($cmd,$args,$dummy,$pat)=("'.$cmd.'","","","");'."\n"
	        . $bodyB . "\n"
                . (($thisone)? ";print \"\\ndo $cmd:\".\$args.\"\\n\";\n" : '')
	        ."\$_;}\n";
	    print "\nDO_CMD: $codeB " if ($thisone); # for debugging
	    eval $codeB;
	    print STDERR "\n\n*** sub do_cmd_$cmd  failed: $@\n" if ($@);
	} unless (defined &$codeB );

	# And make sure the main loop will catch it ...
	$raw_arg_cmds{$cmd} = 1;
    }
}

sub process_commands_wrap_deferred {
    local($_) = @_;
    local($arg,$thisone);
    foreach (/.*\n?/g) {
	chop;
	local($cmd, @args) = split('#',$_);
	next unless $cmd;
	$cmd =~ s/ //g;
	# Build routine body ...
	local ($bodyA, $codeA, $bodyB, $codeB, $after, $thisone);

	# alter the pattern here to debug particular commands
#	$thisone = 1 if ($cmd =~ //);

	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
	foreach $arg (@args) {
	    print "\nARG: $arg" if ($thisone);
	    if ($arg =~ /\{\}/) {
		$bodyA .= '$args .= "$`$&" if (s/$next_pair_rx//o);';
		$after = $';
		print "\nAFTER:$'" if (($thisone)&&($'));
	    } elsif ($arg =~ /\[\]/) {
		$bodyA .= '($dummy, $pat) = &get_next_optional_argument;' .
		    "\n". '$args .= $pat;';
		$after = $';
		print "\nAFTER:$'" if (($thisone)&&($'));
	    } elsif (/<<\s*([^>]*)[\b\s]*>>/) {
		local($endcmd, $afterthis) = ($1,$');
		$afterthis =~ s/(^\s*|\s*$)//g;
		$endcmd =~ s/\\/\\\\/g;
		$bodyA .= "\n". 'if (/'.$endcmd.'/) { $args .= $`.$& ; $_ = $\' };';
		$after .= $afterthis if ($afterthis);
		print "\nAFTER:$'" if (($thisone)&&($'));
	    } else { 
		print "\nAFTER:$arg" if (($thisone)&&($arg));
                $bodyB .= $arg ; $after = ''
            }
	    $after =~ s/(^\s*|\s*$)//g if ($after);
	    $bodyA .= $after . ";" if ($after);
	}
	# Generate 2 new subroutines
	$codeA = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'
	    . $bodyA . "\n" 
            . (($thisone)? ";print \"\\nwrap $cmd:\".\$args.\"\\n\";\n" : '')
	    .'(&make_deferred_wrapper(1).$cmd.$args.&make_deferred_wrapper(0),$_)}'
	    ."\n";
	print "\nWRAP_CMD: $codeA " if ($thisone); # for debugging
	eval $codeA;
	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);

	#RRM: currently these commands only go to LaTeX or access counters.
	#   They could be implemented more generally, as below with  do_dcmd_$cmd
	#   requiring replacement to be performed before evaluation.
	$codeB = "sub do_dcmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'."\n"
	    . $bodyA . "\n" 
            . (($thisone)? ";print \"\\ndo_def $cmd:\".\$args.\"\\n\";\n" : '')
            . $bodyB . "}" . "\n";
	print "\nDEF_CMD: $codeB " if ($thisone); # for debugging
	local($tmp) = "do_cmd_$cmd";
	eval $codeB unless (defined &$tmp);
	print STDERR "\n\n*** sub do_dcmd_$cmd  failed: $@\n" if ($@);

	# And make sure the main loop will catch it ...
	$raw_arg_cmds{$cmd} = 1;
    }
}

sub process_commands_inline_in_tex {
    local($_) = @_;
    foreach (/.*\n?/g) {
	chop;
	local($cmd, @args) = split('#',$_);
	next unless $cmd;
	$cmd =~ s/ //g;
	# Build routine body ...
	local ($body, $code, $thisone) = ("", "");

	# alter the pattern here to debug particular commands
#	$thisone = 1 if ($cmd =~ /span/);

	print "\n$cmd: ".scalar(@args)." arguments" if ($thisone);
	foreach (@args) {
	    print "\nARG: $_" if ($thisone);
	    if (/\{\}/) {
		$body .= '$args .= "$`$&" if (/$any_next_pair_rx/);' . "\n"
		    . "\$_ = \$\';\n";
	    } elsif (/\[\]/) {
		$body .= '($dummy, $pat) = &get_next_optional_argument;' .
		    "\n". '$args .= $pat;';
	    } elsif (/<<\s*/) {
		$_ = $';
		if (/\s*>>/) {
		    $body .= '$args .= "$`$&" if (/\\\\'.$`.'/);' . "\n"
			. "\$_ = \$\';"
		} else { $body .= $_ ; }
	    } else { $body .= $_ ; }
	}
	# Generate a new subroutine
	$code = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args) = "";' . "\n"
	    . $body . "\n"
            . (($thisone)? ";print \"\\ndo $cmd:\".\$args.\"\\n\";\n" : '')
	    .'(&make_nomath_wrapper(1).$cmd.$args.&make_nomath_wrapper(0),$_)}'
	    ."\n";
	print "\nWRAP_CMD: $code " if ($thisone); # for debugging
	eval $code;
	print STDERR "\n\n*** sub wrap_cmd_$cmd  failed: $@\n" if ($@);
	# And make sure the main loop will catch it ...
	$raw_arg_cmds{$cmd} = 1;
    }
}


# Invoked before actual translation; wraps these commands in
# tex2html_wrap environments, so that they are properly passed to
# TeX in &translate_environments ...
# JCL(jcl-del) - new usage of $raw_arg_cmd_rx
sub wrap_raw_arg_cmds {
    local ($processed_text, $cmd, $wrapper, $wrap, $star, $after);
    print "\nwrapping raw arg commands\n" if ($DEBUG);
    while (/$raw_arg_cmd_rx/) {
	$processed_text .= $`;
	$after = $4.$';
	#JCL(jcl-del) - status of starred raw arg cmds yet unclear
	($cmd, $star) = ($1.$2,"");
	print "\nWRAPPED: $cmd" if ($VERBOSITY > 5);
	$wrapper = "wrap_cmd_$cmd";
	$wrapper .= "star" if $star;
	($wrap, $_) = &$wrapper("\\$cmd$star", $after);
	$processed_text .= $wrap;
        last unless ($after =~ /\\/);
    }
    $processed_text . $_;
}

#########################################################################

# To make a table of contents, list of figures and list of tables commands
# create a link to corresponding files which do not yet exist.
# The binding of the file variable in each case acts as a flag
# for creating the actual file at the end, after all the information
# has been gathered.

sub do_cmd_tableofcontents {
    local($_) = @_;
    $tocfile = $CURRENT_FILE;
    $TITLE = $toc_title;
    join('', "<BR>\n", &make_section_heading($toc_title, "H2"), $toc_mark, $_);
}
sub do_cmd_listoffigures {
    local($_) = @_;
    $TITLE = $lof_title;
    $loffile = $CURRENT_FILE;
    join('', "<BR>\n" , &make_section_heading($lof_title, "H2"), $lof_mark, $_);
}
sub do_cmd_listoftables {
    local($_) = @_;
    $TITLE = $lot_title;
    $lotfile = $CURRENT_FILE;
    join('', "<BR>\n" , &make_section_heading($lot_title, "H2"), $lot_mark, $_);
}

# Indicator for where to put the CHILD_LINKS table.
sub do_cmd_tableofchildlinks {
    local($_) = @_;
    join('', "<BR>", $childlinks_mark, "\#0\#", $_);
}

# leave out the preceding <BR>
sub do_cmd_tableofchildlinksstar {
    local($_) = @_;
    join('', $childlinks_mark, "\#1\#", $_);
}

sub do_cmd_htmlinfo {
    local($_) = @_;
    join('', '<BR>', $info_title_mark, $info_page_mark, $_);
}
sub do_cmd_htmlinfostar {
    local($_) = @_;
    join('', $info_page_mark, $_);
}

# $idx_mark will be replaced with the real index at the end
sub do_cmd_textohtmlindex {
    local($_) = @_;
    $TITLE = $idx_title;
    $idxfile = $CURRENT_FILE;
    if (%index_labels) { &make_index_labels(); }
    if (($SHORT_INDEX) && (%index_segment)) { &make_preindex(); }
    else { $preindex = ''; }
    join('',"<BR>\n" , &make_section_heading($idx_title, "H2"), $idx_mark, $_);
}

#RRM: added 17 May 1996
# allows labels within the printable key of index-entries,
# when using  makeidx.perl
sub make_index_labels {
    local($key, @keys);
    @keys = keys %index_labels;
    foreach $key (@keys) {
	if (($ref_files{$key}) && !($ref_files{$key} eq "$idxfile")) {
	    local($tmp) = $ref_files{$key};
	    &write_warnings("\nmultiple label $key , target in $idxfile masks $tmp ");
	}
	$ref_files{$key} .= "$idxfile";
    }
}
#RRM: added 17 May 1996
# constructs a legend for the SHORT_INDEX, with segments
# when using  makeidx.perl
sub make_preindex {
    local($key, @keys, $head, $body);
    $head = "<HR>\n<H4>Legend:</H4>\n<DL COMPACT>";
    @keys = keys %index_segment;
    foreach $key (@keys) {
	local($tmp) = "segment$key";
	$tmp = $ref_files{$tmp};
	$body .= "\n<DT>$key<DD>".&make_named_href('',$tmp,$index_segment{$key});
#	$body .= "\n<DT>$key<DD>".&make_named_href('',
#		$tmp."\#CHILD\_LINKS",$index_segment{$key})
#	             unless ($CHILD_STAR);
    }
    $preindex = join('', $head, $body, "\n</DL>") if ($body);
}


# FOOTNOTES , also within Mini-page environments

sub do_cmd_footnote {
    local($_) = @_;
    local($cnt,$marker,$smark,$emark)=('', $footnote_mark);
    local($mark,$dum) = &get_next_optional_argument;
    local($file);

    do {
	 $footfile = "${PREFIX}footnode$EXTN" unless ($footfile);
	 $file = $footfile;
        } unless (($MINIPAGE)||($NO_FOOTNODE));

    if ($mark) { 
	$cnt = $mark;
	if ($MINIPAGE) { $global{'mpfootnote'} = $cnt }
	else { $global{'footnote'} = $cnt }
    } else {
	$cnt = (($MINIPAGE)? $global{'mpfootnote'}++ : $global{'footnote'}++);
    }
    s/$next_pair_pr_rx//o;
    local($br_id, $footnote) = ($1, $2);
    $br_id = "mp".$br_id if ($MINIPAGE);
    $marker = &get_footnote_mark($MINIPAGE);
    local($last_word) = &get_last_word($ref_before);
    $last_word .= $marker unless ($marker =~ /$footnote_mark/ );
    &process_footnote($footnote,$cnt,$br_id,$last_word,$mark
		      ,(($MINIPAGE)?$marker:''));
    # this may not work if there is a <BASE> tag and !($file) !!! #
    join('',&make_href("$file#$br_id",$marker),$_);
}

sub do_cmd_thanks { &do_cmd_footnote(@_); }

sub get_footnote_mark {
    local($mini) = @_;
    return($footnote_mark) if ($HTML_VERSION < 3.0 );
    local($cmd,$tmp,@tmp,$marker);
    $cmd = "the". (($mini)? 'mp' : '') . "footnote";
    if ($new_command{$cmd}) {
	$tmp = "do_cmd_$cmd";
        @tmp = split (':!:', $new_command{$cmd});
        pop @tmp; $tmp = pop @tmp;
	if ($tmp =~ /$O/) {
	    local($_) = &translate_commands($tmp);
	    &make_unique;
	    $marker = $_;
	} else { $marker = &translate_commands($tmp); }
    } elsif ($mini) {  $marker = &do_cmd_thempfootnote; }
    else { $marker = $footnote_mark; }
    join('','<SUP>',$marker,'</SUP>');
}

# default numbering style for minipage notes
sub do_cmd_thempfootnote { &arabic('<<0>>mpfootnote<<0>>'); }

sub do_cmd_footnotemark {
    local($_) = @_;
    local($br_id, $footnote,$marker,$mpnote,$tmp,$smark,$emark);
    # Don't use ()'s for the optional argument!
    local($mark,$dum) = &get_next_optional_argument;
    local ($cnt);
    if ($mark) {
	$cnt = $mark;
	if (($MINIPAGE)&&($mpfootnotes{$cnt})) {
	    $mpnote = 1;
	    $br_id  = $mpfootnotes{$cnt};
	} else {
	    $global{'footnote'} = $cnt;
	    local($tmp) = $footnotes{$cnt};
	    if ($tmp) {$br_id  = $tmp }
	    else { $footnotes{$cnt} = $br_id }
	}
    } else { 
	$cnt = $global{'footnote'}++ ;
    }

    local($last_word) = &get_last_word($ref_before) unless ($mpnote);

    # Try to find a  \footnotetext  further on.
    do {
	if (s/\\footnotetext\s*\[\s*$cnt\s*]*\]\s*$any_next_pair_pr_rx//o) {
            ($br_id, $footnote) = ($2, $3);  
	} else { 
	    $br_id = "fnm$mark";
	    $footnotes{$cnt} = $br_id;
	}
    } unless ($br_id);

    $marker = &get_footnote_mark($mpnote);
    $last_word .= $marker unless ($marker =~ /$footnote_mark/ );
    if ($footnote) {
	# found a  \footnotetext  further on
	&process_footnote($footnote,$cnt,$br_id,$last_word,$mark);
        $_ = join('',&make_href("$footfile#$br_id",$marker),$_);
    } elsif ($br_id =~ /fnm/) {
	# no  \footnotetext  yet, so make the entry in $footnotes
	&process_footnote('',$cnt,$br_id,$last_word,$mark);
        # this may not work if there is a <BASE> tag and !($footfile) !!! #
	$_ = join('',&make_href("$footfile#$br_id",$marker),$_)
    } elsif ($br_id) {
	# \footnotetext  already processed
	if ($mpnote) {
	    $mpfootnotes =~ s/(=\"$br_id\">...)(<\/A>)/$1$last_word$3/
		if ($last_word);
	    # this may not work if there is a <BASE> tag !!! #
	    $_ = join('',&make_href("#$br_id",$marker),$_);
	} else {
	    $footnotes =~ s/(=\"$br_id\">...)(<\/A>)/$1$last_word$3/;
	    # this may not work if there is a <BASE> tag and !($footfile) !!! #
	    $_ = join('',&make_href("$footfile#$br_id",$marker),$_);
	}
    } else { 
        print "\nCannot find \\footnotetext for \\footnotemark $cnt";
        # this may not work if there is a <BASE> tag and !($footfile) !!! #
	$_ = join('',&make_href("$footfile",$marker),$_)
    }
    $_;
}

# Under normal circumstances this is never executed. Any commands \footnotetext
# should have been processed when the corresponding \footnotemark was
# encountered. It is possible however that when processing pieces of text
# out of context (e.g. \footnotemarks in figure and table captions)
# the pair of commands gets separated. Until this is fixed properly,
# this command just puts the footnote in the footnote file in the hope
# that its context will be obvious ....
sub do_cmd_footnotetext {
    local($_) = @_;
    local($mark,$dum) = &get_next_optional_argument;
    local($br_id, $footnote,$prev);
    $footnote = &missing_braces 
	unless ((s/$next_pair_pr_rx/($br_id,$footnote)=($1,$2);''/eo)
		||(s/$next_pair_rx/($br_id,$footnote)=($1,$2);''/eo));
    if ($mark) { $prev = $footnotes{$mark} }
    if ($prev) {
	$footnotes =~ s/(=\"$prev\">...[^<]*<\/A>\n<DD>)\n/
	    join('',$1, &translate_commands($footnote),"\n")/e;
    } else {
	&process_footnote($footnote,$mark,$br_id,'','') if $footnote;
    }
    $_;
}


sub process_footnote {
    # Uses $before
    # Sets $footfile defined in translate
    # Modifies $footnotes defined in translate
    local($footnote, $cnt, $br_id, $last_word, $mark, $mini) = @_;
    local($space) = ("\n");
    if ((! $NO_FOOTNODE)&&(!$mini)) {
        $footfile = "${PREFIX}footnode$EXTN";
	$space = ".\n" x 30;
	$space = "<PRE>$space</PRE>";
	## $space = "<P>\n" x 30;
    }
    if ($mark) {
	if ($mini) { $cnt = $mpfootnotes{$mark}}
	else { $cnt = $footnotes{$mark} }
	if ($cnt) { 
	    &write_warnings("\nredefined target for footnote $mark" )
                 unless ( $cnt eq $br_id ) }
	if ($mini) { $mpfootnotes{$mark} = "$br_id" }
	elsif ($br_id =~ /fnm\d+/) {
	    $mark = "$footnotes{$cnt}";
	    $footnotes{$cnt} = "$br_id";
	    $footnotes .= "\n<DT><A NAME=\"$br_id\">..."
		. $last_word . "</A>\n<DD>\n" . $space ;
	    return;
	} else { $footnotes{$mark} = "$br_id" }
    } else {
	if ($mini) { $mpfootnotes{$cnt} = "$br_id" }
	else { $footnotes{$cnt} = "$br_id" }
    }

    # catch a \footnotemark *after* the \footnotetext
    if ((!$footnote)&&($last_word)&&(!$mini)) {
	$footnotes .= "\n<DT><A NAME=\"$br_id\">..."
	    . $last_word . "</A>\n<DD>\n" . $space ;

    } elsif ($mini) {
	if ($HTML_VERSION < 3.0) { $mini .= "." }
	$mpfootnotes .= "\n<DD><A NAME=\"$br_id\">$mini</A> " .
	    &translate_commands($footnote) . "\n";
    } else {
	$footnotes .= "\n<DT><A NAME=\"$br_id\">...$last_word</A>\n<DD>" .
	    &translate_commands($footnote) . "\n" . $space ;
    }
}


# This just changes the depth of section so that an appendix is at the
# outermost level.
sub do_cmd_appendix {
    $latex_body .= "\\appendix\n";
    #$section_commands{'section'} = $section_commands{$outermost_level};
    $_[0];
}

sub do_cmd_ref {
    local($_) = @_;
    &process_ref($cross_ref_mark,$cross_ref_mark);
}

sub do_cmd_pageref {
    local($_) = @_;
    &process_ref($cross_ref_mark,$cross_ref_visible_mark);
}

# This is used by external style files ...
sub process_ref {
    local($ref_mark, $visible_mark, $use_label) = @_;
    $use_label = &simplify(&translate_commands($use_label))
	if ($use_label =~ /\\/ );
    local($label,$id);
    s/$next_pair_pr_rx/($id, $label) = ($1, $2);''/eo;
    if ($label) {
     	# if $use_label is 1 then $label is used as the cross_ref_mark
	# elseif $use_label is a string then $use_label is used
        # else the usual mark will be used
	$use_label = ( (($use_label == 1) && $label) || $use_label);
	$label =~ s/<[^>]*>//go ; #RRM: Remove any HTML tags
	$label =~ s/$label_rx/_/g;	# replace non alphanumeric characters
	$symbolic_labels{"$label$id"} = $use_label;
	print "\nLINK: $ref_mark\#$label$id  $use_label" if ($VERBOSITY > 3);
	# The quotes around the HREF are inserted later
#	s/^\n//o; # avoid \n\n which would become a break
	join('',"<A HREF=$ref_mark#$label#$id>$visible_mark<\/A>",$_);
    }
    else {
	print "Cannot find label argument after <$last_word>\n" if $last_word;
	$_;
    }
}

# Uses $CURRENT_FILE defined in translate
sub do_cmd_label {
    local($_) = @_;
    local($label);
    $label = &missing_braces
        unless ((s/$next_pair_pr_rx\n?/$label = $2;''/eo)
		||(s/$next_pair_rx\n?/$label = $2;''/eo));
    &anchor_label($label,$CURRENT_FILE,$_);
}

# This subroutine is also used to process labels in undefined environments
sub anchor_label {
    # Modifies entries in %ref_files defined in translate
    local($label,$filename,$context) = @_;
    $label =~ s/<[^>]*>//go;	#RRM: Remove any HTML tags
    $label =~ s/$label_rx/_/g;	# replace non alphanumeric characters
    # Associate the label with the current file
    if ($ref_files{$label} ne $filename) {
	$ref_files{$label} = $filename;
	$noresave{$label} = 0; $changed = 1; }
	print "<LABEL: $label>" if ($VERBOSITY > 3);
    join('',"<A NAME=\"$label\">$anchor_mark</A>",$context);
}

sub do_cmd_cite {
    local($_) = @_;
    &process_cite('','');
}

# This just creates a link from a label (yet to be determined) to the
# cite_key in the citation file.
sub process_cite {
    local($mode,$text) = @_;
    local($has_text) = (($text)? 1 : 0);
    # process the text from \htmlcite or \hypercite
    $text = &simplify(translate_commands($text)) if ($has_text);

    local($label, $cite_key, @cite_keys);
    local($optional_text,$dummy) =  &get_next_optional_argument;
    $optional_text = ", $optional_text" if $optional_text;
    s/^\s*\\space//o;		# Hack - \space is inserted in .aux
    s/$next_pair_pr_rx//o;
    if (!($cite_key = $2)) {
        print "\n *** Cannot find citation argument\n";
	return ($_);
    }
    @cite_keys = (split(/,/,$cite_key));
    local($citations, $join) = ('',',');
    $join  = '' if ($text);
    foreach $cite_key (@cite_keys) {
	$cite_key =~ s/(^\s+|\s+$)//g;
	$cite_key =~ s/(^\s+|\s+$)//g;
    # RRM:  if the URL and printable-key are known already, then use them...
	$label = "$cite_key";
	$label =~ s/$label_rx/_/g;
	if ( ($SEGMENT) && ($cite_info{$cite_key}) && ($ref_files{"cite_$cite_key"}) ) {
	    $join  = "," unless ($text);
	    $text = $cite_info{$cite_key} unless ($text);
	    $citations .= join('', $join
		, &make_named_href($label,$ref_files{'cite_'."$cite_key"},$text));
	} elsif (($mode eq "external") && ($external_labels{"cite_$cite_key"})) {
	    $join  = "," unless ($text);
	    $text = $cross_ref_visible_mark unless ($text);
	    $citations .= join('', $join
	        , &make_named_href($label,$external_labels{'cite_'."$cite_key"}."\#$label"
		    ,$text));
	} elsif ($mode eq 'external') {
	    $join  = "," unless ($text);
	    &write_warnings("\nExternal reference missing for citation: $cite_key");
	    $citations .= "$text$join#!$cite_key!#";
        } else {
	    $join  = "," unless ($text);
	    #Replace the key...
	    $citations .= "$join#$cite_key#$cite_mark#$bbl_nr#$text#$cite_mark#";
        }
	$text = '';
    }
    $citations =~ s/^\s*,\s*//;
    if ($has_text) { join('', $citations,  $optional_text, $_) }
    else { join('', "[", $citations,  $optional_text, "]", $_) }
}

sub do_cmd_index {
    local($_) = @_;
    s/$next_pair_pr_rx//o;
    local($br_id, $str) = ($1, $2);
    join('',&make_index_entry($br_id,$str),$_);
}

# RRM: \bibcite supplies info via the .aux file; necessary with segmented docs.
sub do_cmd_bibcite {
    local($_) = @_;
    s/$next_pair_pr_rx//o;
    local($br_id, $cite_key) = ($1, $2);
    s/$next_pair_pr_rx//o;
    local($br_id, $print_key) = ($1, $2);
    $cite_info{"$cite_key"} = "$print_key";
    $_;
}

# This command will only be encountered inside a thebibliography environment.
sub do_cmd_bibitem {
    local($_) = @_;
    # The square brackets may contain the label to be printed
    local($label, $dummy) = &get_next_optional_argument;
    # Support for the "named" bibliography style
    if ($label) {
 	$label =~ s/\\protect//g;
 	$label =~ &translate_commands($label);
    }
    s/$next_pair_pr_rx//o;
    local($cite_key) = $2;
    $cite_key =~ s/$label_rx/_/;
    $label = $cite_info{$cite_key} unless $label; # read from .aux file
    $label = ++$bibitem_counter unless $label; # Numerical labels
    if ($cite_key) {
	# Associate the cite_key with the printed label.
	# The printed label will be substituted back into the document later.
	$cite_info{$cite_key} = &translate_commands($label);
	if (!($ref_files{'cite_'."$cite_key"} eq $CURRENT_FILE)) { # RRM
	    $ref_files{'cite_'."$cite_key"} = $CURRENT_FILE;
	    $changed = 1; }	# /RRM
	# Create an anchor around the citation
	join('',"<P></P><DT><A NAME=\"$cite_key\"><STRONG>$label</STRONG></A>\n<DD>", $_);
    }
    else {
	print "Cannot find bibitem labels: $label\n";
	join('',"<P></P><DT><STRONG>$label</STRONG>\n<DD>", $_); # AFEB added this line
    }
}

sub do_cmd_newblock {
#    "<BR>".@_[0]
    "<BR>".$_[0]
}

# This just reads in the $FILE.bbl file if it is available and appends
# it to the items that are still to be processed.
# The $FILE.bbl should contain a thebibliography environment which will
# cause its contents to be processed later in the appropriate way.
# (Note that it might be possible for both the \bibliography command and
# the thebibliography environment to be present as the former may have been
# added by the translator as a sectioning command. In this case (both present)
# the $citefile would have already been set by the thebibliography environment)
sub do_cmd_bibliography {
    local($after) = @_;
    $after =~ s/$next_pair_rx//o;
    local($bibfile) = $2;
    $TITLE = $bib_title;
    do {
	unless ($citefile) {
	    $citefile = $CURRENT_FILE;
	    if (&process_ext_file("bbl")) { # *** BINDS $_ as a side effect ***
		$after = join('',$_,$after);}
	    else {
		print "\nCannot open $FILE.bbl $!\n";
		&write_warnings("\nThe bibliography file was not found.");
		$after = join('',"\n<H2>No References!</H2>", $after);
	    }
	}
    print "\n";
    } if $bibfile;
    $after;
}

sub do_cmd_textohtmlinfopage {
    local($_) = @_;
    ( ($INFO == 1)
     ? join('', "<STRONG>$t_title</STRONG><P>\nThis document was generated using the\n"
       , "<A HREF=\"$TEX2HTMLADDRESS\"><STRONG>LaTeX</STRONG>2<tt>HTML</tt></A> translator Version $TEX2HTMLVERSION\n"
       , "<P>Copyright &#169; 1993, 1994, 1995, 1996, 1997,\n<A HREF=\"$AUTHORADDRESS\">Nikos Drakos</A>, \n"
       , "Computer Based Learning Unit, University of Leeds.\n"
       , "<P>The command line arguments were: <BR>\n "
       , "<STRONG>latex2html</STRONG> <tt>$argv</tt>.\n"
       , "<P>The translation was initiated by $address_data[0] on $address_data[1]", $_)
     : join('',$INFO,"\n",$_))
}


# Try to translate LaTeX vertical space in a number of <BR>'s.
# Eg. 1cm results in one + two extra <BR>'s.
# To help the browser rendering is quite ugly, but why not.
#
sub get_vspace {
    local($_) = @_;
    local($vh) = 0;

    return("<BR>") if /-/;

    $vh = int($1 * $vspace_12pt{$2} + 0.5)
	if (/([0-9.]+)\s*([a-z]+)/);
    join('',"<BR>","\n<BR>" x $vh);
}

sub do_cmd_vskip {
    local($_) = @_;
    &ignore_numeric_argument;
    join('',&get_vspace($1),$_);
}

sub do_cmd_break {
    local($_) = @_;
    join('',"<BR>",$_);
}

sub do_cmd_vspace {
    local($_) = @_;
    s/$next_pair_pr_rx//;
    join('',&get_vspace($2),$_);
}

sub do_cmd_vspacestar {
    &do_cmd_vspace;
}

sub do_cmd_d_backslash {
    local($_) = @_;

    # Eat space from &pre_process.
    # We could also modifiy $single_cmd_rx and %normalize, but why not here.
    s/^ \*?//;
    local($spc,$dum)=&get_next_optional_argument;
    # If the [...] occurs on the next line, then it is *not* an argument to \\ .
    $* = 1; if ($dum =~ /\n/) { 
	$spc = $`; $spc =~ s/\s//g;  $_ = $'.$_ 
    }; $*=0;
    join('',(($spc)? &get_vspace($spc): "<BR>"),$_);
}

sub do_cmd_caption {
    local($_) = @_;
    local($caption);
    s/$next_pair_pr_rx//o;
    $caption = $2;
    join('',"<BR>\n$caption<P>",$_);
}

################## Commands used in the $FILE.aux file #######################

# This is used in $FILE.aux
sub do_cmd_newlabel {
    local($_) = @_;
    local($label,$val,$tmp);
    s/$next_pair_pr_rx/$label = $2;''/eo;
    s/$next_pair_pr_rx/$tmp=$2;''/eo;
    $tmp =~ s/$next_pair_pr_rx/$val=$2/eo;
    $label =~ s/$label_rx/_/g;	# Replace non alphanumeric characters
    $latex_labels{"$label"} = $val;
    $_;
}
#
# Sets %encoded_(section|figure|table)_number, which maps encoded
# section titles to LaTeX numbers
# .= \$number . \"$;\"";
sub do_cmd_contentsline {
    local($_) = @_;
    local($arg,$after,$title,$number,$hash,$stype,$page);
    # The form of the expression is:
    # \contentsline{SECTION} {... {SECTION_NUMBER} TITLE}{PAGE}
    s/$any_next_pair_pr_rx/$stype = $2;''/eo; # Chop off {SECTION}
    s/$any_next_pair_pr_rx/$arg   = $2;''/eo; # Get {... {SECTION_NUMBER} TITLE}
    s/$any_next_pair_pr_rx/$page  = $2;''/eo; # Get page number
    $hash = $stype if ($stype eq "figure" || $stype eq "table" || $SHOW_SECTION_NUMBERS);
    $hash =~ s/(sub)*(section|chapter|part)/section/;
    $after = $_;
    if ($hash) {
	$arg =~ s/$next_pair_pr_rx/$number = $2; ''/eo;
	if ($stype eq "part") {
 	    while ($arg =~ s/$next_pair_pr_rx//o) {};
  	    $number =~ tr/a-z/A-Z/;
   	    $number = "Part $number:"}
        # This cause problem when picking figure numbers...
	# while ($tmp =~ s/$next_pair_pr_rx//o) {};
	$number = -1 unless $number;
#JCL(jcl-tcl)
##	$_ = $arg;
#	$title = &sanitize($arg);
##	&text_cleanup;
##	$title = &encode_title($_);
##
#RRM: resolve any embedded cross-references first
	$title = &simplify($arg);
	$title = &sanitize($title);

	eval "\$encoded_${hash}_number{\$title} .= \$number . \"$;\"";
    }
    $after;
}

#
#  Before normalizing this was \@input.  Used in .aux files.
#
sub do_cmd__at_input {
    local ($_) = @_;
    local ($file, $extfile, $after);
    $extfile = $EXTERNAL_FILE;
    s/$next_pair_pr_rx/$file=$2;''/eo;
    ($prefix, $suffix) = split(/\./, $file);
    $after = $_;
    $EXTERNAL_FILE = $prefix;
    &process_ext_file($suffix);
    $EXTERNAL_FILE = $extfile;
    $after;
}

########################### Counter Commands #################################
# Removes the definition from the input string, adds to the preamble
# and stores the body in %new_counter;
sub get_body_newcounter {
    local(*_) = @_;
    local($within,$ctr,$cmd,$tmp,$body);
    $ctr = &get_next(1);	# Get counter name
    $ctr =~ s/^\s*\\//;
    $within = &get_next(0);	# Get optional within, currently ignored
    &addto_dependents($within,$ctr);
    do {
	local($_) = "\\arabic<<1>>$ctr<<1>>";
	&make_unique; $body = $_;
	$cmd = "the$ctr";
	$tmp = "do_cmd_$cmd";
	$new_command{$cmd} = join(':!:',0,$body,'}') unless (defined &$tmp);
	    &write_mydb("new_command", $cmd, $new_command{$cmd});
	undef $body;
    };
    &do_body_newcounter($ctr);
    $_;
}

sub do_body_newcounter {
    local($ctr) = @_;
    $latex_body .= &revert_to_raw_tex("\\newcounter{$ctr}\n")
	unless ($preamble =~ /\\new(counter|theorem){$ctr}/);
    $global{$ctr} = 0;
    &process_commands_wrap_deferred("the$ctr ");
    $_;
}


#RRM: This doesn't work properly yet.
#     The new booleans need to be stored for use in all partitions.
#     \if... \else  \fi  is not yet implemented.
sub get_body_newif {
    local(*_) = @_;
    local($bool, $body, $ifbool,$cmd,$tmp);
    if (!(s/^\s*\\if([a-zA-Z]+)//)) {
	return($_);
    }
    $bool = $1;
#    $bool = &get_next(1);	# Get boolean name
    $ifbool = "if".$bool;
    $global{$ifbool} = 0;

    do {
	$body = "\$global{$ifbool} = 1;";
	$cmd = $bool."true";
	$code = "sub do_cmd_$cmd {\n".$body."\$_[0]\n}";
	eval $code;
	$raw_arg_cmds{$cmd} = 1;

	$body = "\$global{$ifbool} = 0;";
	$cmd = $bool."false";
	$code = "sub do_cmd_$cmd {\n".$body."\$_[0]\n}";
	eval $code;
	$raw_arg_cmds{$cmd} = 1;

	undef $body;
    };
    &process_commands_wrap_deferred("${bool}true\n${bool}false\nif$bool\n");

    $latex_body .= &revert_to_raw_tex("\\newif\\$ifbool\n")
	unless ($preamble =~ /\\newif\s*\\$ifbool/);
    $_;
}

sub do_cmd_value {
    local($_) = @_;
    local($ctr,$val);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    $val = &get_counter_value($ctr);
    if ($val) { $val.$_ }
    else { join(''," 0",$_) }
}

sub do_cmd_boolean {
    local($_) = @_;
    local($bool,$val);
    $bool = &missing_braces
	unless ((s/$next_pair_pr_rx/$bool = $2;''/eo)
	      ||(s/$next_pair_rx/$bool = $2;''/eo));
    $val = &get_boolean_value($bool);
    if ($val) { $val.$_ }
    else { "0".$_ }
}

sub get_counter_value {
    local($ctr) = @_;
    local($val,$index);
    $ctr = 'eqn_number' if ($ctr eq "equation");
    $index = $section_commands{$ctr};
    if ($index) { 
	if ($SEGMENT) { $val = $segment_sec_id[$index] }
	else { $val = $curr_sec_id[$index] }
    } elsif (defined $global{$ctr}) { 
	$val= $global{$ctr};
    } else {
	&write_warnings ("counter $ctr not defined\n");
	$val= 0;
    }
    print "\nVAL:$ctr: $val " if ($VERBOSITY > 3);
    $val;
}

sub get_boolean_value {
    local($bool) = @_;
    local($val,$index);
    if (defined $global{$bool}) { $val= $global{$bool} }
    else {
	&write_warnings ("boolean $bool not defined\n");
	$val="0";
    }
    print "\nBOOL:$bool: $val " if ($VERBOSITY > 3);
    $val;
}

sub do_cmd_addtocounter {
    local($_) = @_;
    local($ctr,$num,$index);
    $ctr = &missing_braces
	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
    $num = &missing_braces
	unless ((s/$next_pair_rx/$num = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$num = $2;''/eo));
    $latex_body .= &revert_to_raw_tex("\\addtocounter{$ctr}{$num}\n");
    $index = $section_commands{$ctr};
    if ($index) { 
	if ($SEGMENT) { $segment_sec_id[$index] += $num }
	else { $curr_sec_id[$index] += $num }
    } elsif ($ctr eq "equation") {
	$global{'eqn_number'} += $num
    } else {$global{$ctr} += $num };
    print "\nADD:$ctr:+$num= ". $global{$ctr}." " if ($VERBOSITY > 3);
    $_;
}

sub do_cmd_setcounter {
    local($_) = @_;
    local($ctr,$num,$index);
    $ctr = &missing_braces
	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
    $num = &missing_braces
	unless ((s/$next_pair_rx/$num = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$num = $2;''/eo));
#    s/$next_pair_rx/$num = $2;''/eo;
    if (! $AUX_FILE) {
	$latex_body .= &revert_to_raw_tex("\\setcounter{$ctr}{$num}\n");
	$index = $section_commands{$ctr};
	if ($index) { $curr_sec_id[$index] = $num }
	elsif ($ctr eq "equation") {$global{'eqn_number'} = $num }
	else { $global{$ctr} = $num };
    }
    print "\nSET:$ctr: = $num" if ($VERBOSITY > 3);
    $_;
}

sub do_cmd_setboolean {
    local($_) = @_;
    local($bool,$val);
    $bool = &missing_braces
	unless ((s/$next_pair_rx/$bool = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$bool = $2;''/eo));
    $val = &missing_braces
	unless ((s/$next_pair_rx/$val = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$val = $2;''/eo));
    if (! $AUX_FILE) {
	$latex_body .= &revert_to_raw_tex("\\setboolean{$bool}{$val}\n");
	$global{"if$bool"} = (($val = ~/true/) ? 1 : 0);
	print "\nSETBOOL:$bool = $val" if ($VERBOSITY > 3);
    }
    $_;
}

sub do_cmd_stepcounter {
    local($_) = @_;
    local($ctr,$index);
    $ctr = &missing_braces
	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
    if (! $AUX_FILE) {
	$latex_body .= &revert_to_raw_tex("\\stepcounter{$ctr}\n");
	$index = $section_commands{$ctr};
	if ($index) {
	    if ($SEGMENT) { $segment_sec_id[$index] += 1 }
	    else { $curr_sec_id[$index] += 1 }
	} elsif ($ctr eq "equation") { $global{'eqn_number'} += 1 }
	else { $global{$ctr} += 1 };
    }
    print "\nSTP:$ctr:+1" if ($VERBOSITY > 3);
    &reset_dependents($ctr) if ($dependent{$ctr});
    $_;
}

#RRM:   dependent counters are stored as a comma-separated list
#       in the %dependent hash.
sub reset_dependents {
    local($ctr) = @_;
    local($dep,$subdep,%dependents);
    @dependents = (split($delim, $dependent{$ctr}));
    print "\n" if (($VERBOSITY > 3)&&(@dependents));
    while (@dependents) {
	$dep = pop(@dependents);
	print "RESET $dep to 0\n" if ($VERBOSITY > 3);
	$global{$dep} = 0 if ($global{$dep});
	if ($dependent{$dep}) {
	    push(@dependents,split($delim,$dependent{$dep}));
	}
    }
}

sub do_cmd_refstepcounter {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_pr_rx/$ctr = $2;''/eo));
    if (! $AUX_FILE) {
	$latex_body .= &revert_to_raw_tex("\\refstepcounter{$ctr}\n");
	$index = $section_commands{$ctr};
	if ($index) {
	    if ($SEGMENT) { $segment_sec_id[$index] += 1 }
	    else { $curr_sec_id[$index] += 1 }
	} elsif ($ctr eq "equation") { $global{'eqn_number'} += 1 }
	else { $global{$ctr} += 1 };
    }
    print "\nSTP: $ctr : +1" if ($VERBOSITY > 3);
    &reset_dependents($ctr) if ($dependent{$ctr});
    $_;
}

sub do_cmd_arabic {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    if ($val) { join('',&farabic($val),$_) }
    else { join(''," 0",$_) }
}

sub do_cmd_roman {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    if ($val < 0 ) { join('',"-",&froman(-$val),$_) }
    elsif ($val) { join('',&froman($val),$_) }
    else { join(''," 0",$_) }
}

sub do_cmd_Roman {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    if ($val < 0 ) { join('',"-",&fRoman(-$val),$_) }
    elsif ($val) { join('',&fRoman($val),$_) }
    else { join(''," 0",$_) }
}

sub do_cmd_alph {
    local($_) = @_;
    local($ctr,$char);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    if ($val) {
	$char = ($val >0) ? &falph($val) : &falph(-$val);
	join('', ($val < 0 ) ? "-" : '', $char , $_);
    } else { join(''," 0",$_) }
}

sub do_cmd_Alph {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    if ($val) {
	$char = ($val >0) ? &fAlph($val) : &fAlph(-$val);
	join('', ($val < 0 ) ? "-" : '', $char , $_);
    } else { join(''," 0",$_) }
}

sub do_cmd_fnsymbol {
    local($_) = @_;
    local($ctr);
    $ctr = &missing_braces
	unless ((s/$next_pair_pr_rx/$ctr = $2;''/eo)
	      ||(s/$next_pair_rx/$ctr = $2;''/eo));
    local($val) = &get_counter_value($ctr);
    join('',&process_in_latex_helper($ctr, $val, "fnsymbol{$ctr}"),$_);
}


# This is a general command for getting counter values;
# e.g. for section-numbers.

sub do_cmd_thecounter {
    # Uses $counter bound by the caller
    local($val) = &get_counter_value($counter);
    join('',&process_in_latex_helper($counter,$val,"the$counter"),@_);
}


################# Special Naming Macros ##################################

#sub do_cmd_LaTeX {
#    local($_) = @_;
#    join('',$Laname, $TeXname, $_);
#}

sub do_cmd_LaTeX {
    local($_) = @_;
    join('',$Laname, $TeXname, $_);
}

sub do_cmd_LaTeXe {
    local($_) = @_;
    join('',$Laname,$TeXname,(($HTML_VERSION >= 3.0)? '2<SUB>e</SUB>':'2e'),$_);
}

sub do_cmd_latextohtml {
    local($_) = @_;
    join('',$Laname,$TeXname,"2<TT>HTML</TT>",$_);
}

sub do_cmd_TeX {
    local($_) = @_;
    join('',$TeXname, $_);
}

sub do_cmd_Xy {
    local($_) = @_;
    join('',$Xyname, $_);
}

sub do_cmd_AmS {
    local($_) = @_;
    join('',$AmSname, $_);
}

sub do_cmd_AmSTeX {
    local($_) = @_;
    join('',$AmSname, "-", $TeXname, $_);
}

sub do_cmd_char {
    local($_) = @_;
# some special characters are already turned into l2h internal
# representation.
# Get its represention from the table and use it like as regexp form.
    local($spmquot) = &escape_rx_chars($html_specials{'"'});
# Get all internal special char representations as implied during
# preprocessing.
    local($spmrx) = join("\000",values %html_specials);
# escape regexp special chars (not really necessary yet, but why not)
    $spmrx =~ s:([\\(){}[\]\^\$*+?.|]):\\$1:g;
    $spmrx =~ s/\000/|/g;
    $spmrx = "(.)" unless $spmrx =~ s/(.+)/($1|.)/;

    s/^[ \t]*(\d{1,3})[ \t]*/&#$1;/ &&
	return($_);

    s/^[ \t]*\'(\d{1,3})[ \t]*/"&#".oct($1).";"/e &&
	return($_);

    s/^[ \t]*$spmquot(\d{1,2})[ \t]*/"&#".hex($1).";"/e &&
	return($_);

# This is a kludge to work together with german.perl. Brrr.
    s/^[ \t]*\'\'(\d{1,2})[ \t]*/"&#".hex($1).";"/e &&
	return($_);
# If l2h's special char marker represents more than one character,
# it's already in the &#xxx; form. Else convert the single character
# into &#xxx; with the ord() command.
    s/^[ \t]*\`\\?$spmrx[ \t]*/
	(length($html_specials_inv{$1}) > 1 ?
	 $html_specials_inv{$1} : "&#".ord($html_specials_inv{$1}||$1).";")/e &&
	     return($_);
    &write_warnings(join('',
			 "Could not find character number in \\char",
			 (/\n/ ? $` : $_), " etc.\n"));
    $_;
}


sub do_cmd_symbol {
    local($_) = @_;
    local($char);
    $char = &missing_braces
	unless ((s/$next_pair_pr_rx/$char = $2;''/eo)
		||(s/$next_pair_rx/$char = $2;''/eo));
    join('',&do_cmd_char($char),$_);
}

################# Accent and Special Symbols ##################################

# Generate code for the accents handling commands that are never
# applied to i or j.
# MEH: Now all accents are safe for dotless i or j
# MEH: Math accents supported as well
sub generate_accent_commands {
    local($accent);
    local(%accents) = ("c", "cedil", "pc", "cedil", "d", "bdot", "b", "b",
		       "tilde", "tilde", "dot", "dot", "bar", "macr",
		       "hat", "circ", "u", "breve", "v", "caron",
		       "H", "dblac", "t", "t", "grave", "grave",
		       "acute", "acute", "ddot", "uml", "check", "caron",
		       "breve", "breve", "vec", "vec",
		       "k", "ogon", "r", "ring");
    foreach $accent (keys(%accents))  {
	eval "sub do_cmd_$accent {" . 'local($_) = @_;'  .
	    "&accent_safe_for_ij('$accents{$accent}','$accent');" . '$_}';
    }
}

# These handle accents, taking care of the dotless i's and j's that
# may follow (even though accented j's are not part of any alphabet
# that I know).
#
# Note that many forms of accents over dotless i's and j's are
# handled:
#   "\^\i rest"
#   "\^\i
#    rest"
#   "\^{\i}rest"
#   "\^\i{}rest"
# They all produce "&#238;rest".
# MEH: now also handles
#   "\^{}rest"
#   "\^,rest"
# and many more

sub accent_safe_for_ij {
    local($type,$acc_cmd) = @_;
    local($arg, $first_char);
    #print STDERR "\nACCENT: $type <$_>\n" ;
    s/^[ \t]*\n?[ \t]*(\S)/$1/;	# Remove whitespace
    if (s/^\\([ij])([^a-zA-Z]|$)/$2/) {
	# Accent of this form: "\^\i rest" or "\^\i{}rest"
	($arg) =  $1;
	s/^[ \t]+//o;		# Get rid of whitespaces after \i
	if (substr($_, 0, 2) =~ /[\n\r][^\n\r]/) {
	    $_ = substr($_, 1); # Get rid of 1 newline after \i
	}
    } else {
	# Accent of this form: "\^{\i}rest" or not an accent on i nor j
	($arg) =  &get_next_pair_or_char_pr;
    }
    $arg =~ s/([^\s\\<])/$first_char = $1; ''/eo;
#    print STDERR "\nACCENT type: $type   arg: |$arg|   first_char: |$first_char|\n";
    if (!($ACCENT_IMAGES)) {
	$_ = join('', (&iso_map($first_char, $type) || $first_char), $arg, $_);
    } else {
	$_ = join('', (&iso_map($first_char, $type) || &mbox_accent($acc_cmd, $first_char))
	      , $arg, $_);
    }
}

sub mbox_accent {
    local($type, $char) = @_;
    if (length($type) > 1 ) {
	if ($text_accent{$type}) {
	    $type = $text_accent{$type};
	} elsif ($type =~ /^(math)?accent/) {
	} else {
	    print "\n unrecognised accent $type for `$char' ";
	    return $char;
	}
    }
    local(@styles);
    local($cmd,$style,$bstyle,$estyle) = ('','');
    local(@styles) = split(',',$ACCENT_IMAGES);
    foreach $style (@styles) {
	$style =~ s/(^\s*\\|\s*)//g; 
	$cmd = "do_cmd_$style";
	if (defined &$cmd) { 
	    $bstyle .= "\\$style\{" ;
	    $estyle .= "\}";
	} else {
	    &write_warnings("\nunrecognized style \\$cmd for accented characters");
	}
    }
    if (!$bstyle) {
	    $style = "\\large\{";
	    $estyle = "\}";
    }
    print "\nACCENT: $type, $char" if ($VERBOSITY > 2);
    join('',&process_math_in_latex('','text',0
	   , join('',"\\mbox{",$bstyle,"\\$type $char", $estyle, "}")));
}

# MEH: Actually tries to find a dotless i or j
sub do_cmd_i {
    join('',&iso_map('i', 'nodot') || 'i',$_[0]);
}
sub do_cmd_j {
    join('',&iso_map('j', 'nodot') || 'j',$_[0]);
}

sub do_cmd_accent {
    local($_) = @_;
    local($number);
    if (s/\s*(\d+)\s*//o) {$number = $1}
    elsif (s/\s*&SMPquot;(\d)(\d)\s*//o) { $number = $1*16 + $2 }
    elsif (s/\s*\'(\d)(\d)(\d)\s*//o) { $number = $1*64 + $2*8 + $3 }
    else { s/\s*(\W\w+)([\s\W])/$2/o;  $number = $1 }
    local($type) = $accent_type{$number};
    #print STDERR "\ndo_cmd_accent: $number ($type) |$_|\n";
    if (! $type) {
	&write_warnings("Accent number $number is unknown.\n");
	return $_;
    }
    &accent_safe_for_ij($type , 'accent$number' );
    $_;
}

sub do_cmd_ae {
    join('', &iso_map("ae", "lig"), $_[0]);}
sub do_cmd_AE {
    join('', &iso_map("AE", "lig"), $_[0]);}
sub do_cmd_aa {
    join('', &iso_map("a", "ring"), $_[0]);}
sub do_cmd_AA {
    join('', &iso_map("A", "ring"), $_[0]);}
sub do_cmd_o {
    join('', &iso_map("o", "slash"), $_[0]);}
sub do_cmd_O {
    join('', &iso_map("O", "slash"), $_[0]);}
sub do_cmd_ss {
    join('', &iso_map("sz", "lig"), $_[0]);}
sub do_cmd_DH {
    join('', &iso_map("ETH", ""), $_[0]);}
sub do_cmd_dh {
    join('', &iso_map("eth", ""), $_[0]);}
sub do_cmd_TH {
    join('', &iso_map("THORN", ""), $_[0]);}
sub do_cmd_th {
    join('', &iso_map("thorn", ""), $_[0]);}

sub do_cmd_pounds {
    join('', &iso_map("pounds", ""), $_[0]);}
sub do_cmd_S {
    join('', &iso_map("S", ""), $_[0]);}
sub do_cmd_copyright {
    join('', &iso_map("copyright", ""), $_[0]);}
sub do_cmd_P {
    join('', &iso_map("P", ""), $_[0]);}

sub brackets {
    ($OP, $CP);
}

sub get_date {
    local(@lt) = localtime;
    local($d,$m,$y) = @lt[3,4,5];
    sprintf("%d/%d/%d", $m+1, $d, 1900+$y);
}

sub address_data {
    local($user, $date, $_);
    # Get author email address and current date.
    ($user = (getpwuid ($<))[6]) =~ s/,.*//;
    ($user, &get_date);
}


#################################### LaTeX2e ##################################

sub missing_braces {
#    local($cmd) = @_;
    local($next, $thisline);
    &write_warnings("\n? brace missing for \\$cmd");
    if (/^[\s%]*([^\n]*)\n/ ) {
	$thisline = &revert_to_raw_tex($1)
    } else { $thisline = $_ }
    print "\n\n*** no brace for \\$cmd , before:\n$thisline";
    $next = $& if (s/$next_token_rx//o);
    $next = &translate_commands($next) if ($next =~ /^\\/);
    $next = &revert_to_raw_tex($next) if ($next);
    print "\n*** using \"$next\" as the argument instead; is this correct?  ***\n\n";
    $next;
}

sub do_cmd_textbf {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<B>",$next,"<\/B>",$_);
}

sub do_cmd_texttt {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<TT>",$next,"<\/TT>",$_);
}

sub do_cmd_textit {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<I>",$next,"<\/I>",$_);
}

sub do_cmd_textsl {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<I>",$next,"<\/I>",$_);
}

sub do_cmd_textsc {
    local($_) = @_;
    local($text, $next, $scstr, $before, $special);
    $text = &missing_braces
        unless ((s/$next_pair_pr_rx/$text = $2;''/eo)
	    || (s/$next_pair_rx/$text = $2;''/eo));
    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;|<+[^>]>+)/ ) {
	$before = $`; $special = $&; $text = $';
	while ( $before =~/[a-z]+/) {
	    $scstr .= $`; $before = $';
	    $next = $&;  $next =~ tr/a-z/A-Z/;
	    $scstr .= "<SMALL>" . $next ."<\/SMALL>";
	}
	$scstr .= $before . $special;
    }
    if ($text) {
	while ( $text =~/[a-z]+/) {
	    $scstr .= $`; $text = $';
	    $next = $&;  $next =~ tr/a-z/A-Z/;
	    $scstr .= "<SMALL>" . $next ."<\/SMALL>";
	}
	$scstr .= $text;
    }
    join('', $scstr, $_);
}

sub do_cmd_emph {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<EM>",$next,"<\/EM> ",$_);
}

sub do_cmd_underline {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    || (s/$next_pair_rx/$next = $2;''/eo));
    join('',"<U>",$next,"<\/U>",$_);
}

sub do_cmd_uppercase {
    local($_) = @_;
    local($text,$next,$done,$special,$after);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$text = $2;''/eo)
	    || (s/$next_pair_rx/$text = $2;''/eo));
    $after = $_;
    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;)/ ) {
	$next = $`;
	$special = $&;
	$text = $';
	$next =~ tr /a-z/A-Z/ if ($next);
	$done .= $next . $special;
    }
    $text =~ tr /a-z/A-Z/ if ($text);
    $done .= $text;
    $done = &convert_iso_latin_chars($done) if ($done);
    join('',$done,$after);
}

sub do_cmd_lowercase {
    local($_) = @_;
    local($text,$next,$done,$special,$after);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$text = $2;''/eo)
	    || (s/$next_pair_rx/$text = $2;''/eo));
    $after = $_;
    while ($text =~ /(\\[a-zA-Z]+|[&;]SPM\w+;)/ ) {
	$next = $`;
	$special = $&;
	$text = $';
	$next =~ tr /A-Z/a-z/ if ($next);
	$done .= $next . $special;
    }
    $text =~ tr /A-Z/a-z/ if ($text);
    $done .= $text;
    $done = &convert_iso_latin_chars($done) if ($done);
    join('',$done,$after);
}

sub do_cmd_MakeUppercase { &do_cmd_uppercase }
sub do_cmd_MakeLowercase { &do_cmd_lowercase }

sub do_cmd_strikeout {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    ||(s/$next_pair_rx/$next = $2;''/eo));
    join('',"<STRIKE>",$next,"<\/STRIKE>",$_);
}

sub do_cmd_textsf {
    local($_) = @_;
    local($next);
    $next = &missing_braces
        unless ((s/$next_pair_pr_rx/$next = $2;''/eo)
	    ||(s/$next_pair_rx/$next = $2;''/eo));
    join('',"<i>",$next,"<\/i>",$_);
}

sub do_env_small {
    join('',"@_","");
}

sub do_cmd_ensuremath {
    local($_) = @_;
    local ($id, $value) = /$any_next_pair_pr_rx/o;
    s/$any_next_pair_pr_rx//o;
    join('', &simple_math_env($value), $');
}

#
#  This is mainly for \special{header=PostScript_Prologue},
#	and \graphicspath{path} which occur OUTSIDE and an environment
#	passed to TeX.  \special's INSIDE such environments are, of
#	course, left alone.

sub do_cmd_special {
    local($_) = @_;
    local ($id, $value) = /$any_next_pair_pr_rx/o;
    local ($special_cmd);
    s/$any_next_pair_pr_rx//o;
    $special_cmd = &revert_to_raw_tex($value);
    &add_to_preamble($cmd,"\\$cmd\{$special_cmd\}");
    $_;
}


########################## Input and Include commands #########################

sub do_cmd_input {
    local($_) = @_;
    local($file);
    s/$next_pair_pr_rx/$file=$2;''/eo;
    $file = &revert_to_raw_tex("\\input{$file}\n") if $file;
    &add_to_preamble('include',$file);
    $_;
}

sub do_cmd_include {
    local($_) = @_;
    local($file);
    s/$next_pair_pr_rx/$file=$2;''/eo;
    $file = &revert_to_raw_tex("\\include{$file}\n") if $file;
    &add_to_preamble('include',$file);
    $_;
}

########################## Messages #########################

sub do_cmd_message {
    local($_) = @_;
    local($message);
    s/$next_pair_pr_rx/$message=$2;''/eo;
    local($after) = $_;
    $message = &translate_commands($message);
    print STDERR "$message";
    $after;
}

sub do_cmd_typeout {
    print STDERR "\n";
    local($_) = &do_cmd_message(@_);
    print STDERR "\n";
    $_;
}

sub do_cmd_tracingall {print "\nTRACING:\n$ref_contents\n$after\n";$VERBOSITY = 8; "";}

sub do_cmd_htmltracenv { &do_cmd_htmltracing }

sub do_cmd_htmltracing {
    local($_) = @_;
    local($value);
    $value = &missing_braces
        unless ((s/$next_pair_rx/$value = $2;''/eo)
	    ||(s/$next_pair_pr_rx/$value = $2;''/eo));
    if ($value =~ /^\s*(\d+)\s*$/) { 
	$VERBOSITY = $1;
	if ($VERBOSITY) { 
	    print "\n\n *** setting trace-level to $VERBOSITY ***\n";
	} else {
	    print "\n\n *** cancelling all tracing ***\n\n";
	}
    } else {
	&write_warnings("argument to \\htmltracing must be a number");
     }
    $_ ;
}


############################ Initialization ####################################

sub initialise {
    ############################ Global variables ###############################
    $PREAMBLE = 2;		# 1 while translating preamble, 0 while translating body 
    $NESTING_LEVEL = undef;	#counter for TeX group nesting level
    $OUT_NODE = 0;		# Used in making filenames of HTML nodes unique
    $eqno_prefix = '';		# default prefix on equation numbers
    ($O , $C, $OP, $CP) = ('<<' , '>>', '<#', '#>'); # Open/Close Markers
    $name = 0;			# Used in the HREF NAME= field
    $wrap_toggle = 'end';
    $delim = '%:%';		# Delimits items of sectioning information
				# stored in a string

#    $TeXname = (($HTML_VERSION ge "3.0")? "T<SUB><BIG>E</BIG></SUB>X" : "TeX");
    $TeXname = (($HTML_VERSION ge "3.0")? "T<SMALL>E</SMALL>X" : "TeX");
#    $TeXname = "TeX";
    $Laname = (($HTML_VERSION ge "3.0")? "L<SUP><SMALL>A</SMALL></SUP>" : "La");
    $Xyname = (($HTML_VERSION ge "3.0")? "X<SUB><BIG>Y</BIG></SUB>" : "Xy");
    $AmSname = (($HTML_VERSION ge "3.0")? "A<SUB><BIG>M</BIG></SUB>S" : "AmS");

    $EQN_TAGS = "R" unless ($EQN_TAGS);
    $EQNO_START = "(";
    $EQNO_END   = ")";

    ${AtBeginDocument_hook}  = "\$AtBeginDocument_hook\=\'\'; ";
    $cross_ref_mark = '<tex2html_cr_mark>';
    $external_ref_mark = '<tex2html_ext_cr_mark>';
    $cite_mark = '<tex2html_cite_mark>';
    $hash_mark = '<tex2html_hash_mark>';
    $param_mark = '<tex2html_param_mark>';
    $bbl_mark = '<tex2html_bbl_mark>';
    $toc_mark = '<tex2html_toc_mark>';
    $lof_mark = '<tex2html_lof_mark>';
    $lot_mark = '<tex2html_lot_mark>';
    $info_page_mark = '<tex2html_info_page_mark>';
    $info_title_mark = '<tex2html_info_title_mark>';
    $childlinks_mark = '<tex2html_childlinks_mark>';
    $more_links_mark = '<tex2html_morelinks_mark>';
    $idx_mark = '<tex2html_idx_mark>';
    $verbatim_mark = '<tex2html_verbatim_mark>';
    $verb_mark = '<tex2html_verb_mark>';
    $image_mark = '<tex2html_image_mark>';
    $mydb_mark =  '<tex2html_mydb_mark>';
    $percent_mark = '<tex2html_percent_mark>';
    $comment_mark = '<tex2html_comment_mark>';

    $bibitem_counter = 0;
    $undef_mark = '<tex2html_undef_mark>';

    # This defines textual markers for all the icons
    # e.g. $up_visible_mark = '<tex2html_up_visible_mark>';
    # They will be replaced with the real icons at the very end.
    foreach $icon (keys %icons) {eval "\$$icon = '<tex2html_$icon>'"};

    # Make sure $HTML_VERSION is in the right range and in the right format.
#    $HTML_VERSION =~ /[\d.]*/;
#    $HTML_VERSION = 0.0 + $&;
#    $HTML_VERSION = 2 if ( $HTML_VERSION < 2 );
#    $HTML_VERSION = 9 if ( $HTML_VERSION > 9 );
#    $HTML_VERSION = sprintf("%3.1f",$HTML_VERSION);

    print "\nThis is LaTeX2HTML Version $TEX2HTMLVERSION by Nikos Drakos, \n";
    print "Computer Based Learning Unit, University of Leeds.\n\n";

    print "Revised and extended by: Marcus Hennecke, Ross Moore, Herb Swan and others\n";

    # Collect HTML options and figure out HTML version
    $HTML_OPTIONS = '' unless ($HTML_OPTIONS);
    $HTML_VERSION =~ s/\s+//g;
    local(@HTML_VERSION) = split(/,/, $HTML_VERSION);
    foreach ( @HTML_VERSION ) {
	if (/^[\d.]+$/) {
	    # Make sure $HTML_VERSION is in the right range and in the right format.
	    $HTML_VERSION = 0.0 + $_;
	    $HTML_VERSION = 2 if ( $HTML_VERSION < 2 );
	    $HTML_VERSION = 9 if ( $HTML_VERSION > 9 );
	    $HTML_VERSION = sprintf("%3.1f",$HTML_VERSION);
	} else {
	    $HTML_OPTIONS .= "$_,";
	}
    }
    chop $HTML_OPTIONS;

    print "...producing markup for HTML version $HTML_VERSION  ";
    print ($HTML_OPTIONS ? "with $HTML_OPTIONS extensions\n\n\n" : "\n\n\n");

    if ($HTML_VERSION =~ /(2.0|3.0|3.2)/) {
        # Require the version specific file 
	$_ = $HTML_VERSION;
#    s/\./_/g; # not needed under Unix
	$_ = "$LATEX2HTMLVERSIONS${dd}html$_.pl";
        print "Loading $_\n";
	require $_ if (-f $_);
	$DOCTYPE = "-//".(($HTML_VERSION eq "2.0")? "IETF" : "W3C")
	    . "//DTD HTML $HTML_VERSION";

	if ($HTML_OPTIONS) {
            # Require the option specific files 
	    @HTML_VERSION = split(/,/, $HTML_OPTIONS);
	    foreach ( @HTML_VERSION ) {
		require "$LATEX2HTMLVERSIONS$dd$_.pl"
		    if (-f "$LATEX2HTMLVERSIONS$dd$_.pl");
		print "Loading $LATEX2HTMLVERSIONS$dd$_.pl\n";
	    }
	}
    } 
    else {
	print "\n You specified an invalid version: $HTML_VERSION\n"
	    . "In future please request extensions by name:\n"
	    . "  i18n  table  math  \n";

    # Require all necessary version specific files
	foreach ( sort <$LATEX2HTMLVERSIONS${dd}html[1-9].[0-9].pl> ) {
	    last if ( $_ gt "$LATEX2HTMLVERSIONS${dd}html$HTML_VERSION.pl" );
	    do { print "\nloading $_" if ($DEBUG); require $_; }
	    unless (($NO_SIMPLE_MATH)&&($_ eq "$LATEX2HTMLVERSIONS${dd}html3.1.pl"));
	};
        $STRICT_HTML = 0;
    }



    %declarations =
    ('em' , '<EM></EM>',
     'it' , '<I></I>',
     'bf' , '<B></B>',
     'tt' , '<TT></TT>',
     'sl' , '<I></I>',		# Oops!
     'sf' , '<I></I>',		# Oops!
     'itshape' ,  '<EM></EM>',
     'bfseries' , '<B></B>',
     'ttfamily' , '<TT></TT>',
     'slshape' ,  '<I></I>',	# Oops!
     'sffamily' , '<I></I>',	# Oops!
     'scshape' ,  '<I></I>',	# Oops!
#     'boldmath' , '<B></B>',
     'quote', '<BLOCKQUOTE></BLOCKQUOTE>',
     'quotation', '<BLOCKQUOTE></BLOCKQUOTE>',
     %declarations	# Just in case someone extends it in the init file
     );


%declarations = (
     'tiny', '<FONT SIZE="-2"></FONT>',
     'Tiny', '<FONT SIZE="-2"></FONT>',
     'scriptsize', '<FONT SIZE="-2"></FONT>',
     'small', '<SMALL></SMALL>',
     'Small', '<SMALL></SMALL>',
     'SMALL', '<SMALL></SMALL>',
     'footnotesize', '<SMALL></SMALL>',
     'large', '<BIG></BIG>',
     'Large', '<BIG></BIG>',
     'LARGE', '<FONT SIZE="+2"></FONT>',
     'huge', '<FONT SIZE="+3"></FONT>',
     'Huge', '<FONT SIZE="+4"></FONT>',
     'centering', '<DIV ALIGN="CENTER"></DIV>',
     'center', '<DIV ALIGN="CENTER"></DIV>',
     'flushleft', '<DIV ALIGN="LEFT"></DIV>',
     'raggedright', '<DIV ALIGN="LEFT"></DIV>',
     'flushright', '<DIV ALIGN="RIGHT"></DIV>',
     'raggedleft', '<DIV ALIGN="RIGHT"></DIV>',
     %declarations
    ) if ($HTML_VERSION > 2.0 );

    &generate_declaration_subs;	# Generate code to handle declarations

    %section_commands =
	('partstar' , '1' , 'chapterstar', '2', 'sectionstar', '3',
	 'subsectionstar', '4', 'subsubsectionstar', '5', 'paragraphstar',
	 '6', 'subparagraphstar', '7',
	 'part' , '1' , 'chapter', '2', 'section', '3','subsection', '4',
	 'subsubsection', '5', 'paragraph', '6', 'subparagraph', '7' ,
	 'slidehead', '3');
    # The tableofcontents, listoffigures, listoftables, bibliography and
    # textohtmlindex are set after determining what is the outermost level
    # in sub set_depth_levels. Appendix is implemented as a command.

    %section_headings =
	('part' , 'H1' , 'chapter' , 'H1', 'section', 'H1', 'subsection', 'H2',
	 'subsubsection', 'H3', 'paragraph', 'H4', 'subparagraph', 'H5');
    &generate_sectioning_subs;	# Generates code to handle sectioning commands
    %section_headings =
	('partstar' , 'H1' , 'chapterstar' , 'H1', 'sectionstar', 'subsectionstar',
	 'H1', 'H2', 'subsubsectionstar', 'H3', 'paragraphstar',
	 'H4', 'subparagraphstar', 'H5');

    # These need their own custom code but are treated as sectioning commands
    %section_headings =
	('tableofcontents', 'H1', 'listoffigures', 'H1', 'listoftables', 'H1',
	 'bibliography', 'H1', 'textohtmlindex', 'H1', %section_headings);

    &generate_accent_commands;	# Code to handle accent commands

    # These are replaced as soon as the text is read in.
    %html_specials = ('<', ';SPMlt;' ,
		      '>', ';SPMgt;',
		      '&', ';SPMamp;',
		      '"', ';SPMquot;');

    # This mapping is needed in sub revert_to_raw_tex
    # before passing stuff to latex for processing.
    %html_specials_inv = ( ';SPMlt;' ,'<',
			  ';SPMgt;','>',
			  ';SPMamp;','&',
			  ';SPMquot;','"',
			  ';SPMdollar;', '$',	# for alltt
			  ';SPMpct;', '%',
			  ';SPMtilde;', '&#126;');

    # normalsize vertical dimension factors for 12pt (1.0 <=> <BR>)
    %vspace_12pt = ('ex', 1.0, 'em', 1.0, 'pt', 0.1, 'pc', 1.0,
	'in', 6.0, 'bp', 0.1, 'cm', 2.3, 'mm', 0.2, 'dd', 0.1,
	'cc', 1.0, 'sp', 0.0);

    # For some commands such as \\, \, etc it is not possible to define
    # perl subroutines because perl does not allow some non-ascii characters
    # in subroutine names. So we define a table and a subroutine to relate
    # such commands to ascii names.
    %normalize = ('\\', 'd_backslash'
		  , '/', 'esc_slash', "`", 'grave'
		  , "'", 'acute', "^", 'hat', '"', 'ddot'
		  , '~', 'tilde', '.', 'dot', '=', 'bar'
		  , '{', 'lbrace' , '}', 'rbrace', '|', 'Vert'
		  , '#', 'esc_hash', '$', 'esc_dollar'
                 );

    %text_accent = ( 'hat' , '^', 'check' , 'v' , 'tilde' , '~'
                    , 'acute' , '\'' , 'grave' , '`' , 'dot' , '.'
                    , 'ddot' , '"' , 'breve' , 'u' , 'bar' , '=' 
                  );

    # %languages_translations holds for each known language the
    # appropriate translation function. The function is called in
    # slurp_input.
    # The translation functions subtitute LaTeX macros
    # with ISO-LATIN-1 character references
    %language_translations =
	('english',	'english_translation',
	 'USenglish',   'english_translation',
	 'original',	'english_translation',
	 'german',	'german_translation',
	 'austrian',	'german_translation',
	 'french',      'french_translation'
	 );

# Reiner: 
#    $standard_label_rx = 
#	"\\s*[[]\\s*(((\$any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";
#    $enum_label_rx = "^((({[^{}]*})|([^{}]))*)([aAiI1])(.*)";
#    $enum_level = 0;	# level for enumerate (1-4, i-iv)
    %enum = ( 
		'enumi',	0,			# counter for level 1
		'enumii',	0,			# counter for level 2
		'enumiii',	0,
		'enumiv',	0,
		'theenumi',	"&arabic('enumi')",	# eval($enum{"theenumi"})
		'theenumii',	"&alph('enumii')",
		'theenumiii',	"&roman('enumiii')",
		'theenumiv',	"&Alph('enumiv')",
			# e.g. eval("$enum{'labelenumi'}")
		'labelenumi',	'eval($enum{"theenumi"}) . "."', 
		'labelenumii',	'"(" . eval($enum{"theenumii"}) . ")"',	
		'labelenumiii',	'eval($enum{"theenumiii"}) . "."',
		'labelenumiv',	'eval($enum{"theenumiv"}) . "."'
		);

    %RomanI = ( '1',"I",'2',"II",'3',"III",'4',"IV"
		    ,'5',"V",'6',"VI",'7',"VII", '8',"VIII",'9',"IX");
    %RomanX = ( '1',"X",'2',"XX",'3',"XXX",'4',"XL"
		    ,'5',"L",'6',"LX",'7',"LXX", '8',"LXXX",'9',"XC");
    %RomanC = ( '1',"C",'2',"CC",'3',"CCC",'4',"CD"
		    ,'5',"D",'6',"DC",'7',"DCC", '8',"DCCC",'9',"CM");
    %RomanM = ( '1',"M",'2',"MM",'3',"MMM",'4',"MH"
		    ,'5',"H",'6',"HM",'7',"HMM",'8',"HMMM");

    %enum_label_funcs = ( 
	"a", "alph", "A", "Alph", "i", "roman", "I", "Roman", "1", "arabic" );

sub farabic{
    local($_)=@_;
    $_;
}
sub arabic{
    local($_)=@_;
    eval($enum{$_});
}

sub falph{
    local($num)=@_;
#    chr($num+64);
    substr(" abcdefghijklmnopqrstuvwxyz",$num,1)
}
sub alph{
    local($num)=@_;
    &falph(eval($enum{$num}));
}
sub fAlph{
    local($num)=@_;
#    chr($num+32);
    substr(" ABCDEFGHIJKLMNOPQRSTUVWXYZ",$num,1)
}
sub Alph{
    local($num)=@_;
    &falph(eval($enum{$num}));
}

sub Roman{
    local($num)=@_;
    &fRoman(eval($enum{$num}));
}
sub fRoman{
    local($num)=@_;
    local($RmI)= $num%10; ($RmI) = (($RmI) ? $RomanI{"$RmI"} : '' );
    $num = $num/10; local($RmX)= $num%10; ($RmX) = (($RmX) ? $RomanX{"$RmX"} : '' );
    $num = $num/10; local($RmC)= $num%10; ($RmC) = (($RmC) ? $RomanC{"$RmC"} : '' );
    $num = $num/10; local($RmM)= $num%10; ($RmM) = (($RmM) ? $RomanM{"$RmM"} : '' );
    "$RmM" . "$RmC" . "$RmX" . "$RmI";
}
sub froman{
    local($_)=@_;
    $_ = &fRoman($_);
    $_ =~ tr/A-Z/a-z/;
    $_;
}
sub roman{
    local($num)=@_;
    &froman(eval($enum{$num}));
}


    %unitscale = ("in",72,"pt",72.27/72,"pc",12,"mm",72/25.4,"cm",72/2.54
		  ,"\\hsize",100,"\\vsize",100,"\\textwidth",100,"\\textheight",100);
    %units = ("in","in","pt","pt","pc","pi","mm","mm","cm","cm"
	      ,"\\hsize","%","\\vsize","%","\\textwidth","%","\\textheight","%");

sub convert_length {
    local($this) = @_;
    local($pxs,$len);
    if ( $this =~ /([0-9.]+)(in|pt|pc|mm|cm|\\hsize)/ ) {
        $pxs = int($1 * $unitscale{$2} + 0.5);
        $len = $1 . $units{$2};
        if ( $2 =~ /\\([hv]size|text(width|height))/) { $pxs .= '\%';};
    };
    ($pxs,$len);
}
 



    # Inclusion in this list will cause a command or an environment to be ignored.
    # This is suitable for commands without arguments and for environments.
    # If however a do_env|cmd_<env|cmd> exists then it will be used.
    %ignore = ('sloppypar', 1,  'document', 1, 'newblock', 1,
	       ',', 1,  '@', 1, ' ', 1,  '-', 1,
               'sloppy', 1,
	       'hyphen', 1, 'titlepage', 1, 'htmlonly', 1,
	       'flushleft', 1, 'flushright', 1, 'slide', 1,
	       'tiny', 1, 'Tiny', 1, 'scriptsize', 1, 'footnotesize', 1,
	       'small', 1, 'normalsize', 1, 'large', 1, 'Large', 1,
	       'LARGE', 1, 'huge', 1, 'Huge', 1,
	       %ignore);

    # Specify commands with arguments that should be ignored.
    # Arbitrary code can be placed between the arguments
    # to be executed while processing the command.
    #
# Note that some commands MAY HAVE ARGUMENTS WHICH SHOULD BE LEFT AS TEXT
    # EVEN THOUGH THE COMMAND IS IGNORED (e.g. hbox, center, etc)

&ignore_commands( <<_IGNORED_CMDS_);
addcontentsline # {} # {} # {}
addtocontents # {} # {}
addvspace # {} # &ignore_numeric_argument
and
and # \$_ = join(''," - ",\$_)
backmatter
baselineskip # &ignore_numeric_argument
bibdata
bibliographystyle # {}
bibstyle # {}
center
citation # {}
citeauthoryear
clearpage
cline # {}
documentclass # [] # {}
documentstyle # [] # {}
#end # {}
enlargethispage # {}
evensidemargin # &ignore_numeric_argument
filbreak
fill
flushbottom
footheight # &ignore_numeric_argument
footskip  # &ignore_numeric_argument
frontmatter
global
goodbreak
headheight # &ignore_numeric_argument
headsep # &ignore_numeric_argument
hfill
hline
hspace # {} # \$_ = join(''," ",\$_)
hspacestar # {} # \$_ = join(''," ",\$_)
html
hyphenation # {}
ignorespaces
indent
itemsep # &ignore_numeric_argument
leavevmode
leftmargin # &ignore_numeric_argument
lower # &ignore_numeric_argument
mainmatter
makebox # [] # []
makeindex
marginpar # {}
marginparsep # &ignore_numeric_argument
marginparwidth # &ignore_numeric_argument
markboth # {} # {}
mathord
mathbin
mathrel
mathop
mathrm
mathtt
mdseries
newpage
nobreak
nocite # {}
noindent
nolinebreak# []
nopagebreak #[]
normalmarginpar
numberline
oddsidemargin # &ignore_numeric_argument
#pagebreak# [] # \$_ = join('',"<P>",\$_)
pagenumbering #{}
pagestyle # {}
parindent # &ignore_numeric_argument
parsep # &ignore_numeric_argument
parskip # &ignore_numeric_argument
penalty # &ignore_numeric_argument
phantom # {}
protect
raggedright
raggedbottom
raise # &ignore_numeric_argument
raisebox # {}
relax
reversemarginpar
rightmargin # &ignore_numeric_argument
rmfamily
rule # [] # {} # {}
samepage
startdocument # \$SEGMENT=1; \$_
suppressfloats # []
textheight # &ignore_numeric_argument
textwidth # &ignore_numeric_argument
textnormal
textrm
textup
thispagestyle # {}
topmargin # &ignore_numeric_argument
topskip # &ignore_numeric_argument
upshape
vfil
vfill
vline
_IGNORED_CMDS_

    # Commands which need to be passed, ALONG WITH THEIR ARGUMENTS, to TeX.
    # Note that this means that the arguments should *not* be translated,
    # This is handled by wrapping the commands in the dummy tex2html_wrap
    # environment before translation begins ...

    # Also it can be used to specify environments which may be defined
    # using do_env_* but whose contents will be passed to LaTeX and
    # therefore should not be translated.

    # Note that this code squeezes spaces out of the args of psfig;
    # that's what the last round did ...

&process_commands_in_tex (<<_RAW_ARG_CMDS_);
psfig # {} # \$args =~ s/ //g;
fbox # {}
etalchar # \$args =~ s/(.*)/\$\^\{\$1\}\\\$/o; 
framebox # [] # [] # {}
dag
ddag
l
L
oe
OE
usebox # {}
_RAW_ARG_CMDS_



# These are handled by wrapping the commands in the dummy tex2html_nowrap
# environment before translation begins. This environment will be
# stripped off later, when the commands are put into  images.tex  ...

&process_commands_nowrap_in_tex (<<_RAW_ARG_NOWRAP_CMDS_);
begingroup
endgroup
bgroup
egroup
errorstopmode
nonstopmode
scrollmode
batchmode
psfigurepath # {}
pssilent
psdraft
psfull
thinlines
thicklines
linethickness # {}
DeclareMathAlphabet # {} # {} # {} # {} # {}
SetMathAlphabet # {} # {} # {} # {} # {} # {}
DeclareMathSizes # {} # {} # {} # {}
DeclareMathVersion # {}
DeclareSymbolFont # {} # {} # {} # {} # {}
DeclareSymbolFontAlphabet # {} # {}
DeclareMathSymbol # {} # {} # {} # {}
SetSymbolFont # {} # {} # {} # {} # {} # {}
DeclareFontShape # {} # {} # {} # {} # {} # {}
DeclareFontFamily # {} # {} # {}
DeclareFontEncoding # {} # {} # {}
DeclareFontSubstitution # {} # {} # {} # {}
mathversion # {}
newfont # {} # {}
normalfont
rmfamily
mdseries
newlength # {}
setlength # {} # {}
addtolength # {} # {}
settowidth # {}# {}
settoheight # {} # {}
settodepth # {} # {}
newsavebox # {}
savebox # {} # [] # {}
sbox # {} # {}
setbox # {}
TagsOnLeft  # \$EQN_TAGS = \"L\" if \$PREAMBLE;
TagsOnRight # \$EQN_TAGS = \"R\" if \$PREAMBLE;
_RAW_ARG_NOWRAP_CMDS_


&process_commands_wrap_deferred (<<_RAW_ARG_DEFERRED_CMDS_);
alph # {}
Alph # {}
arabic # {}
boldmath
unboldmath
fnsymbol # {}
footnotetext # [] # {}
roman # {}
Roman # {}
#mbox # {}
parbox # [] # [] # [] # {} # {}
setcounter # {} # {}
addtocounter # {} # {}
stepcounter # {}
refstepcounter # {}
value # {} 
thepart
thepage
thechapter
thesection
thesubsection
thesubsubsection
theparagraph
thesubparagraph
theequation
htmltracenv # {}
HTMLsetenv # [] # {} # {}
_RAW_ARG_DEFERRED_CMDS_

# This maps the HTML mnemonic names for the ISO-LATIN-1 character references
# to their numeric values. When converting latex specials characters to
# ISO-LATIN-1 equivalents I use the numeric values because this makes any
# conversion back to latex (using revert_raw_tex) more reliable (in case
# the text contains "&mnemonic_name"). Errors may occur if an environment
# passed to latex (e.g. a table) contains the numeric values of character
# references.

%iso_8859_1_character_map
    = (
       'AElig', '&#198;', 	# capital AE diphthong (ligature)
       'Aacute', '&#193;', 	# capital A, acute accent
       'Acirc', '&#194;', 	# capital A, circumflex accent
       'Agrave', '&#192;', 	# capital A, grave accent
       'Aring', '&#197;', 	# capital A, ring
       'Atilde', '&#195;', 	# capital A, tilde
       'Auml', '&#196;', 	# capital A, dieresis or umlaut mark
       'Ccedil', '&#199;', 	# capital C, cedilla
       'ETH', '&#208;', 	# capital Eth, Icelandic
       'Eacute', '&#201;', 	# capital E, acute accent
       'Ecirc', '&#202;', 	# capital E, circumflex accent
       'Egrave', '&#200;', 	# capital E, grave accent
       'Euml', '&#203;', 	# capital E, dieresis or umlaut mark
       'Iacute', '&#205;', 	# capital I, acute accent
       'Icirc', '&#206;', 	# capital I, circumflex accent
       'Igrave', '&#204;', 	# capital I, grave accent
       'Iuml', '&#207;', 	# capital I, dieresis or umlaut mark
       'Ntilde', '&#209;', 	# capital N, tilde
       'Oacute', '&#211;', 	# capital O, acute accent
       'Ocirc', '&#212;', 	# capital O, circumflex accent
       'Ograve', '&#210;', 	# capital O, grave accent
       'Oslash', '&#216;', 	# capital O, slash
       'Otilde', '&#213;', 	# capital O, tilde
       'Ouml', '&#214;', 	# capital O, dieresis or umlaut mark
       'THORN', '&#222;', 	# capital THORN, Icelandic
       'Uacute', '&#218;', 	# capital U, acute accent
       'Ucirc', '&#219;', 	# capital U, circumflex accent
       'Ugrave', '&#217;', 	# capital U, grave accent
       'Uuml', '&#220;', 	# capital U, dieresis or umlaut mark
       'Yacute', '&#221;', 	# capital Y, acute accent
       'aacute', '&#225;', 	# small a, acute accent
       'acirc', '&#226;', 	# small a, circumflex accent
       'aelig', '&#230;', 	# small ae diphthong (ligature)
       'agrave', '&#224;', 	# small a, grave accent
       'amp', '&amp;', 	# ampersand
       'aring', '&#229;', 	# small a, ring
       'atilde', '&#227;', 	# small a, tilde
       'auml', '&#228;', 	# small a, dieresis or umlaut mark
       'ccedil', '&#231;', 	# small c, cedilla
       'eacute', '&#233;', 	# small e, acute accent
       'ecirc', '&#234;', 	# small e, circumflex accent
       'egrave', '&#232;', 	# small e, grave accent
       'eth', '&#240;', 	# small eth, Icelandic
       'euml', '&#235;', 	# small e, dieresis or umlaut mark
       'gt', '&#62;', 	# greater than
       'iacute', '&#237;', 	# small i, acute accent
       'icirc', '&#238;', 	# small i, circumflex accent
       'igrave', '&#236;', 	# small i, grave accent
       'iuml', '&#239;', 	# small i, dieresis or umlaut mark
       'lt', '&lt;', 	# less than
       'ntilde', '&#241;', 	# small n, tilde
       'oacute', '&#243;', 	# small o, acute accent
       'ocirc', '&#244;', 	# small o, circumflex accent
       'ograve', '&#242;', 	# small o, grave accent
       'oslash', '&#248;', 	# small o, slash
       'otilde', '&#245;', 	# small o, tilde
       'ouml', '&#246;', 	# small o, dieresis or umlaut mark
       'szlig', '&#223;', 	# small sharp s, German (sz ligature)
       'thorn', '&#254;', 	# small thorn, Icelandic
       'uacute', '&#250;', 	# small u, acute accent
       'ucirc', '&#251;', 	# small u, circumflex accent
       'ugrave', '&#249;', 	# small u, grave accent
       'uuml', '&#252;', 	# small u, dieresis or umlaut mark
       'yacute', '&#253;', 	# small y, acute accent
       'yuml', '&#255;',	# small y, dieresis or umlaut mark
       'quot', '&quot;',	# double quote

# These do not have HTML mnemonic names ...
       'pounds', '&#163;',	# pound sign
       'S', '&#167;',		# section mark
       'copyright', '&#169;',	# copyright mark
       'P', '&#182;',		# paragraph mark
       'questacute', '&#191;',	# question mark - upside down
       'exclamacute', '&#161;',	# exclamation mark - upside down

# These are character types without arguments ...
       'grave' , "`",
       'acute' , "&#180;",
       'circ', '^',
       'tilde', '&#126;',
       'ring', '&#176;',
       'middot', '&#183;',
       'dot', '.',
       'uml', '&#168;',
       'macr' , '&#175;',
       'dblac', "&#180;&#180;",
       'cedil', "&#184;"
       );

# Global variable $iso_8859_1_character_map_inv

%iso_8859_1_character_map_inv =
    (
     '&#198;' , '\\AE{}',
     '&#193;' , '\\\'{A}',
     '&#194;' , '\\^{A}',
     '&#192;' , '\\`{A}',
     '&#197;' , '\\AA{}',
     '&#195;' , '\\~{A}',
     '&#196;' , '\\"{A}',
     '&#199;' , '\\c{C}',
     '&#208;' , '\\DH{}',
     '&#201;' , '\\\'{E}',
     '&#202;' , '\\^{E}',
     '&#200;' , '\\`{E}',
     '&#203;' , '\\"{E}',
     '&#205;' , '\\\'{I}',
     '&#206;' , '\\^{I}',
     '&#204;' , '\\`{I}',
     '&#207;' , '\\"{I}',
     '&#209;' , '\\~{N}',
     '&#211;' , '\\\'{O}',
     '&#212;' , '\\^{O}',
     '&#210;' , '\\`{O}',
     '&#216;' , '\\O{}',
     '&#213;' , '\\~{O}',
     '&#214;' , '\\"{O}',
     '&#222;' , '\\TH{}',
     '&#182;' , '\\P{}',
     '&#167;' , '\\S{}',
     '&#218;' , '\\\'{U}',
     '&#219;' , '\\^{U}',
     '&#217;' , '\\`{U}',
     '&#220;' , '\\"{U}',
     '&#221;' , '\\\'{Y}',
     '&#225;' , '\\\'{a}',
     '&#226;' , '\\^{a}',
     '&#230;' , '\\ae{}',
     '&#224;' , '\\`{a}',
     '&amp;'  , '\\&',
     '&#229;' , '\\aa{}',
     '&#227;' , '\\~{a}',
     '&#228;' , '\\"{a}',
     '&#231;' , '\\c{c}',
     '&#184;' , '\\c{}',
     '^'      , '\\^{}',
     '&#168;' , '\\"{}',
     '&#169;' , '\\copyright{}',
     '&#233;' , '\\\'{e}',
     '&#234;' , '\\^{e}',
     '&#232;' , '\\`{e}',
     '&#240;' , '\\dh{}',
     '&#235;' , '\\"{e}',
     '&#161;' , '!`',
     '&#62;'  , '$>$',
     '&#237;' , '\\\'{i}',
     '&#238;' , '\\^{i}',
     '&#236;' , '\\`{i}',
     '&#239;' , '\\"{i}',
     '&lt;'   , '$<$',
     '&#241;' , '\\~{n}',
     '&#243;' , '\\\'{o}',
     '&#244;' , '\\^{o}',
     '&#242;' , '\\`{o}',
     '&#248;' , '\\o{}',
     '&#245;' , '\\~{o}',
     '&#246;' , '\\"{o}',
     '&#223;' , '\\ss{}',
     '&#254;' , '\\th{}',
     '&#250;' , '\\\'{u}',
     '&#251;' , '\\^{u}',
     '&#249;' , '\\`{u}',
     '&#252;' , '\\"{u}',
     '&#253;' , '\\\'{y}',
     '&#255;' , '\\"{y}',
     '&#163;' , '\\pounds{}',
     '&#180;' , '\\\'{}',
     '&#126;' , '\\~{}',
     '&#176;' , '\\r{}',
     '&#175;' , '\\={}',
     '&#191;' , '?`',
     '&#183;' , '\\.{}'
);

sub make_isolatin1_rx {
    local($list) = &escape_rx_chars(join($CD,(values %iso_8859_1_character_map_inv)));
    $list =~ s/$CD/|/g;
    $isolatin1_rx = "($list)";
}


    ################### Frequently used regular expressions ###################
    # $1 : preamble

    $preamble_rx = "(^[\\s\\S]*)(\\\\begin\\s*$O\\d+$C\\s*document\\s*$O\\d+$C|\\\\startdocument)";

    # \d (number) should sometimes also be a delimiter but this causes
    # problems with command names  that are allowed to contain numbers (eg tex2html)
    # \d is a delimiter with commands which take numeric arguments?
    # JCL: I can't see that. \tex2html is also no valid LaTeX (or TeX).
    # It is parsed \tex 2html, and \tex may take 2html as argument, but this
    # is invalid LaTeX. \d must be treated as delimiter.

# JCL(jcl-del) - Characters to be treated as letters, everything else
# is a delimiter.
    # internal LaTeX command separator, shouldn't be equal to $;
    $CD = "\001";
    &make_cmd_spc_rx; # determines space to follow a letter command
#old    $delimiters = '\'\\s[\\]\\\\<>(=).,#;:~\/!-';
    $letters = 'a-zA-Z';
    $delimiter_rx = "([^$letters])";
#

    # liberalized environment names (white space, interpunctuation signs etc.)
    # $1 : br_id, $2 : <environment>
    $begin_env_rx = "\\\\begin\\s*$O(\\d+)$C\\s*([^'[\\]\\\\#~]+)\\s*$O\\1$C\\s*";
    $mbox_rx = "\\\\mbox\\s*";

    $match_br_rx = "\\s*$O\\d+$C\\s*";

    $optional_arg_rx = "^\\s*\\[([^]]*)\\]";	# Cannot handle nested []s!

    # Matches a pair of matching brackets
    # $1 : br_id
    # $2 : contents
    $next_pair_rx = "^[\\s%]*$O(\\d+)$C([\\s\\S]*)$O\\1$C";
    $any_next_pair_rx = "$O(\\d+)$C([\\s\\S]*)$O\\1$C";
    $any_next_pair_rx4 = "$O(\\d+)$C([\\s\\S]*)$O\\4$C";
    $any_next_pair_rx5 = "$O(\\d+)$C([\\s\\S]*)$O\\5$C";


    # used for labels in {enumerate} environments
    $standard_label_rx = 
	"\\s*[[]\\s*((($any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";
    $enum_label_rx = "^((({[^{}]*})|([^{}]))*)([aAiI1])(.*)";
    $enum_level = 0;	# level for enumerate (1-4, i-iv)


    # Matches the \ensuremath command
    $enspair = "\\\\ensuremath\\s*" . $any_next_pair_rx;
#    $enspair = "\\\\ensuremath\\s*$O(\\d+)$C([\\s\\S]*[\\\\\$&]+[\\s\\S]*)$O\\1$C";

    # Matches math comments, from  math.pl
    $math_verbatim_rx = "\$verbatim_mark#math(\\d+)#";

    # Matches to end-of-line and subsequent spaces
    $EOL = "[ \\t]*\\n?";

    # $1 : br_id
    $begin_cmd_rx = "$O(\\d+)$C";

    # $1 : image filename prefix
    $img_rx = "(\\w*T?img\\d+)";

    # $1 : largest argument number
    $tex_def_arg_rx = "^[#0-9]*#([0-9])$O";

    #   only some non-alphanumerics are allowed in labels,  Why?
    $label_rx = "[^\\w\.\\\-\\\+\\\:]";

#JCL(jcl-del) - new face, see also &do_cmd_makeatletter et.al.
#    $cmd_delims = q|-#,.~/\'`^"=\$%&_{}@|; # Commands which are also delimiters!
#    $single_cmd_atletter_rx = "\\\\([a-zA-Z\\\@]+\\*?|[$cmd_delims]|\\\\)";
#    $single_cmd_atother_rx = "\\\\([a-zA-Z]+\\*?|[$cmd_delims]|\\\\)";
    # $1 : declaration or command or newline (\\)
    &make_single_cmd_rx;
#

    # $1 : description in a list environment
    $item_description_rx =
	"\\\\item\\s*[[]\\s*((($any_next_pair_rx4)|([[][^]]*[]])|[^]])*)[]]";

    $fontchange_rx = 'rm|em|bf|it|sl|sf|tt';
    $sizechange_rx = 'tiny|scriptsize|footnotesize|small|normalsize' .
	'|large|Large|LARGE|huge|Huge';

    # Matches the \caption command
    # $1 : br_id
    # $2 : contents
    $caption_rx = "\\\\caption\\s*([[]\\s*((($any_next_pair_rx5)|([[][^]]*[]])|[^]])*)[]])?$O(\\d+)$C([\\s\\S]*)$O\\8$C$EOL";

    # Matches the \htmlimage command
    # $1 : br_id
    # $2 : contents
    $htmlimage_rx = "\\\\htmlimage\\s*$O(\\d+)$C([\\s\\S]*)$O\\1$C$EOL";

    # Matches the \htmlborder command
    # $1 : optional argument...
    # $2 : ...contents  i.e. extra attributes
    # $3 : br_id
    # $4 : contents i.e. width
    $htmlborder_rx = "\\\\htmlborder\\s*(\\[([^]]*)\\])?\\s*$O(\\d+)$C(\\d*)$O\\3$C$EOL";

    # Matches a pair of matching brackets
    # USING PROCESSED DELIMITERS;
    # (the delimiters are processed during command translation)
    # $1 : br_id
    # $2 : contents
    $next_pair_pr_rx = "^[\\s%]*$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP";
    $any_next_pair_pr_rx = "$OP(\\d+)$CP([\\s\\S]*)$OP\\1$CP";
#    $next_token_rx = "^[\\s%]*(\\\\\w+|\\\\[^a-zA-Z]|.)";
    $next_token_rx = "^[\\s%]*(\\\\[a-zA-Z]+|\\\\[^a-zA-Z]|.)";

    # This will be used to recognise escaped special characters as such
    # and not as commands
    $latex_specials_rx = '[\$]|&|%|#|{|}|_';

    # This is used in sub revert_to_raw_tex before handing text to be processed
    # by latex.
    $html_specials_inv_rx = join("|", keys %html_specials_inv);

    # This is also used in sub revert_to_raw_tex
    $character_entity_rx = '(&#\d+;)';

    # Matches a \begin or \end {tex2html_wrap}. Also used by revert_to_raw_tex
    $tex2html_wrap_rx = '\\\\(begin|end)\s*\{\s*(tex2html_(wrap|nowrap|deferred|nomath)[_a-z]*|makeimage)\s*\}';

    # The first empty parenthese pair is for non-letter commands.
    # $2: meta command, $4: delimiter (may be empty)
    $meta_cmd_rx = "()\\\\(providecommand|renewcommand|renewenvironment|newcommand|newenvironment|newtheorem|newcounter|newboolean|newif)(([^$letters$cmd_spc])|$cmd_spcs_rx)";

    &make_counters_rx;

    # Matches a label command and its argument
    $labels_rx = "$EOL\\\\label\\s*$O(\\d+)$C([\\s\\S]*)$O\\1$C$EOL";
    $labels_rx8 = "$EOL\\\\label\\s*$O(\\d+)$C([\\s\\S]*)$O\\8$C$EOL";

    # Matches environments that should not be touched during the translation
#   $verbatim_env_rx = "\\s*{(verbatim|rawhtml|LVerbatim)[*]?}";
    $verbatim_env_rx = "\\s*(verbatim|rawhtml|LVerbatim)[*]?";
    $keepcomments_rx = "\\s*(picture|makeimage|xy|diagram)[*]?";

    # Matches icon markers
    $icon_mark_rx = "<tex2html_(" . join("|", keys %icons) . ")>";

    $start_time = time;
    print join(" ", "Starting at", $start_time, "seconds\n")
        if ($TIMING||$DEBUG||($VERBOSITY>2));
}

# Frequently used regular expressions with arguments
sub make_end_env_rx {
    local($env) = @_;
    $env = &escape_rx_chars($env);
    "\\\\end\\s*$O(\\d+)$C\\s*$env\\s*$O\\1$C".$EOL;
}

sub make_begin_end_env_rx {
    local($env) = @_;
    $env = &escape_rx_chars($env);
    "\\\\(begin|end)\\s*$O(\\d+)$C\\s*$env\\s*$O\\2$C(\\s*\$)?";
}

sub make_end_cmd_rx {
    local($br_id) = @_;
    "$O$br_id$C";
}

#JCL(jcl-del) - see also &tokenize.
# Arrange commands into a regexp for tokenisation.
# Any letter command will gobble spaces, but avoids to match
# on ensuing letters (\foo won't match on \foox).
# Any non-letter command retains spaces and matches always
# by itself (\| matches \|... regardless of ...).
#
# This all is a huge kludge. The commands names should stay fix,
# regardless of changing catcodes. If we have \makeatletter,
# and LaTeX2HTML marks \@foo, then \@foo will be expanded
# properly before \makeatother, but does weird things on \@foo
# after \makeatother (\@foo in LaTeX is then \@ and foo, which
# isn't recognized as such).
# The reason is that the text to match the command \@foo
# in LaTeX mustn't be \@foo at all, because any text in LaTeX
# is also attributed with the category codes.
#
# But at least we have proper parsing of letter and non-letter
# commands as long as catcoding won't upset LaTeX2HTML too much.
#
sub make_new_cmd_rx {
    return("") if $#_ < 0; # empty regexp if list is empty!

    # We have a subtle treatment of ambivalent commands like
    # \@foo in situations depicted above!
    # Get every command that contains no letters ...
    local($nonlettercmds) =
	&escape_rx_chars(join($CD, grep(!/[$letters]/,@_)));
    # and every command that contains a letter
    local($lettercmds) =
	&escape_rx_chars(join($CD, grep(/[$letters]/,@_)));

    # replace the temporary $CD delimiter (this enables eg. \| command)
    $nonlettercmds =~ s/$CD/|/g;
    $lettercmds =~ s/$CD/|/g;

    # In case we have no non-letter commands, insert empty parentheses
    # to align match strings.
    #
    local($rx) = (length($nonlettercmds) ? "\\\\($nonlettercmds)" : "");
    if (length($lettercmds)) {
	$rx .= ( length($rx) ? "|" : "()" );
	$rx .= "\\\\($lettercmds)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
    }
    # $1: non-letter cmd, $2: letter cmd, $4: delimiter
    # Eg. \\(\@|...|\+)|\\(abc|...|xyz)(([^a-zA-Z \t])|[ \t]+)
    # $1 and $2 are guaranteed to alternate, $4 may be empty.
    $rx;
}

# Build a simple regexp to use after tokenisation for
# faster translation.
sub make_new_cmd_no_delim_rx {
    return("") if $#_ < 0; # empty regexp if list is empty!

    # Get every command that contains no letters ...
    local($_) = &escape_rx_chars(join($CD, @_));
    s/$CD/|/g;

    join('',"\\\\(",$_,")");
}


#JCL(jcl-del) - new face: w/o arg (was 'begin' only), escapes env names
sub make_new_env_rx {
    local($envs) = &escape_rx_chars(join($CD, keys %new_environment));
    $envs =~ s/$CD/|/g;
    length($envs) ? "\\\\begin\\s*$O(\\d+)$C\\s*($envs)\\s*$O\\1$C\\s*" : "";
}

sub make_new_end_env_rx {
    local($envs) = &escape_rx_chars(join($CD, keys %new_environment));
    $envs =~ s/$CD/|/g;
    length($envs) ? "\\\\end\\s*$O(\\d+)$C\\s*($envs)\\s*$O\\1$C\\s*" : "";
}

#JCL(jcl-del) - $delimiter_rx -> ^$letters
# don't care for $cmd_spc_rx; space after sectioning commands
# is unlikely and I don't want to try too much new things
#
sub make_sections_rx {
    local($section_alts) = &get_current_sections;
    # $section_alts includes the *-forms of sectioning commands
    $sections_rx = "()\\\\($section_alts)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
#    $sections_rx = "()\\\\($section_alts)([^$letters])";
}

sub make_order_sensitive_rx {
    local(@theorem_alts, $theorem_alts);
    @theorem_alts = ($preamble =~ /\\newtheorem\s*{([^\s}]+)}/og);
    $theorem_alts = join('|',@theorem_alts);
#
#  HWS: Added kludge to require counters to be more than 2 characters long
#	in order to be flagged as order-sensitive.  This will permit equations
#	with \theta to remain order-insensitive.  Also permit \alpha and
#	the eqnarray* environment to remain order-insensitive.
#
    $order_sensitive_rx =
        "(equation|eqnarray[^*]|\\\\caption|\\\\ref|\\\\the[a-z]{2,2}[a-z]|\\\\stepcounter" .
        "|\\\\arabic|\\\\roman|\\\\Roman|\\\\alph[^a]|\\\\Alph|\\\\fnsymbol)";
    $order_sensitive_rx =~ s/\)/|$theorem_alts)/ if $theorem_alts;
}

sub make_language_rx {
    local($language_alts) = join("|", keys %language_translations);
    $setlanguage_rx = "\\\\setlanguage{\\\\($language_alts)}";
    $language_rx = "\\\\($language_alts)TeX";
#    $case_change_rx = "\\\\((Make)?([Uu]pp|[Ll]ow)ercase)\s*";
    $case_change_rx = "\\\\((Make)?([Uu]pp|[Ll]ow)ercase)\\s*";
}

# JCL(jcl-del) - new rexexp type
sub make_raw_arg_cmd_rx {
    # $1 or $2 : commands to be processed in latex (with arguments untouched)
    # $4 : delimiter
    $raw_arg_cmd_rx = &make_new_cmd_rx(keys %raw_arg_cmds);
}

# There are probably more.
# Interferences not checked out yet, thus in makeat... only.
sub make_letter_sensitive_rx {
    $delimiter_rx = "([^$letters])";
    &make_sections_rx;
    &make_single_cmd_rx;
    &make_counters_rx;
}

#JCL(jcl-del) - this could eat one optional newline, too.
# But this might result in large lines... anyway, it *should* be
# handled. A possible solution would be to convert adjacent newlines
# into \par's in preprocessing.
sub make_cmd_spc_rx {
    $cmd_spc = " \\t";
    $cmd_spc_rx = "[ \\t]*";# zero or more
    $cmd_spcs_rx = "[ \\t]+";# one or more
}

sub make_single_cmd_rx {
    $single_cmd_rx = "\\\\([^$letters])|\\\\([$letters]+\\*?)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
}

sub make_counters_rx {
    # Matches counter commands - these are caught early and are appended to the
    # file that is passed to latex.
#JCL(jcl-del) - $delimiter_rx -> ^$letters
    $counters_rx = "()\\\\(newcounter|addtocounter|setcounter|refstepcounter|stepcounter|arabic|roman|Roman|alph|Alph|fnsymbol)(([^$letters$cmd_spc])|$cmd_spcs_rx|\$)";
}


# Creates an anchor for its argument and saves the information in
# the array %index;
# In the index the word will use the beginning of the title of
# the current section (instead of the usual pagenumber).
# The argument to the \index command is IGNORED (as in latex)
sub make_index_entry {
    local($br_id,$str) = @_;
#JCL(jcl-tcl)
#    # If TITLE is not yet available (i.e the \index command is in the title of the
#    # current section), use $ref_before.
#    $TITLE = $ref_before unless $TITLE;
#
    # Save the reference
    $str = "$str###" . ++$global{'max_id'}; # Make unique
    $index{$str} .= &make_half_href("$CURRENT_FILE#$br_id");
    "<A NAME=\"$br_id\">$anchor_invisible_mark<\/A>";
}

sub image_message {
    print <<_EOM_

To resolve the image conversion problems please consult
the "Troubleshooting" section of your local User Manual
or follow the links to it at
http://cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/latex2html.html

_EOM_
}

sub image_cache_message {
   print <<_EOM_

If you are having problems displaying the correct images with Mosaic,
try selecting "Flush Image Cache" from "Options" in the menu-bar
and then reload the HTML file.
_EOM_

}

###############################################################

# These next few lines are legal in both Perl and nroff.
# Any changes to the synopsis must be kept in sync with &usage().

.00;                       # finish .ig

'di           \" finish diversion--previous line must be blank
.nr nl 0-1    \" fake up transition to first page again
.nr % 0         \" start at page 1
'; __END__ ##### From here on it's a standard manual page - VERSION #####
.TH LaTeX2HTML 1
.AT 3
.SH NAME
latex2html \- translate LaTeX files to HTML (HyperText Markup Language)
.SH SYNOPSIS
.B

latex2html
   [-split num]
   [-link num]
   [-toc_depth num]
   [-short_extn]
   [-nolatex]
   [-external_images]
   [-ps_images]
   [-font_size (10pt | 11pt | 12pt | ...)]
   [-no_tex_defs]
   [-ascii_mode]
   [-t top_page_title]
   [-dir output_directory]
   [-no_subdir]
   [-address author_address]
   [-long_titles num ]
   [-custom_titles]
   [-no_navigation]
   [-top_navigation]
   [-bottom_navigation]
   [-auto_navigation]
   [-index_in_navigation]
   [-contents_in_navigation]
   [-next_page_in_navigation]
   [-previous_page_in_navigation]
   [-prefix output_filename_prefix]
   [-auto_prefix]
   [-up_url up_url]
   [-up_title up_title]
   [-down_url down_url]
   [-down_title down_title]
   [-prev_url prev_url]
   [-prev_title prev_title]
   [-index index_url]
   [-biblio biblio_url]
   [-contents toc_url]
   [-external_file external.aux_file]
   [-info string]
   [-no_auto_link]
   [-discard]
   [-reuse reuse_option]
   [-no_reuse]
   [-no_images]
   [-no_math]
   [-images_only]
   [-antialias]
   [-no_antialias]
   [-antialias_text]
   [-no_antialias_text]
   [-local_icons]
   [-scalable_fonts]
   [-show_section_numbers]
   [-init_file Perl_file]
   [-html_version (2.0|3.0|3.2)[,(math|i18n|table)]* ]
   [-short_index]
   [-debug]
   [-timing]
   [-verbosity num]
   [-h(elp)]
   [-v]
   file(s)


.SH DESCRIPTION
.I LaTeX2HTML
is a Perl program that translates LaTeX source files into HTML. For each source
file given as an argument the translator will create a directory containing the
corresponding HTML files.
See the WWW online documentation or the <latex2htmldir>/doc/manual.ps
file for more detailed information and examples.

.SH PROBLEMS
For information on various problems and remedies see the WWW online documentation
or the documents available in the distribution. An online bug reporting
form and various archives are available at
http://cbl.leeds.ac.uk/nikos/tex2html/doc/latex2html/latex2html.html

.SH AUTHOR
Nikos Drakos,  Computer Based Learning Unit, University of Leeds
<nikos@cbl.leeds.ac.uk>. Several people have contributed suggestions,
ideas, solutions, support and encouragement.

The pstoimg script was written by Marek Rouchal,
as a generalisation of the  pstogif  utility to allow graphic formats
other than GIF to be created. Various options and enhancements have
been added by Ross Moore.
Some of the code is based upon the pstoppm.ps postscript program 
originally written by Phillip Conrad (Perfect Byte, Inc.)
and modified by L.  Peter Deutsch (Aladdin Enterprises).
cd
