#!/usr/local/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='.';# 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 = "1-h";
$TVERSION = "96";
$RELDATE = "(September 30, 1996)";

$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";
				
push(@INC,$ENV{'HOME'});	

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

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

# Set $HOME to the environment variable just in case tries to use it!
$HOME = $ENV{'HOME'};		

# Author address
@address_data = &address_data;

###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($cwd);
    chop($cwd = `pwd`);
    $cwd;
}

# 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");

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

# Read .late2html-init file if one is found in current directory
require("./.latex2html-init") if ( (! (&getcwd eq $ENV{'HOME'} )) &&
				   (-f "./.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;

#JKR: The user may use `~` for his home.
$LATEX2HTMLSTYLES =~ s/~/$ENV{'HOME'}/g;

#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
}

# Process switches
$argv = join(' ',@ARGV);			# Save the command line arguments
while ($ARGV[0] =~ /^-/) {
    $_ = shift;
    if (/^-split$/) {
	$_ = shift;
	((($MAX_SPLIT_DEPTH) = /^(\d+)$/)
	 || print("Unrecognised value for -split: $_\n")
	 && &usage && die);
	
    }
    elsif (/^-link$/) {
	$_ = shift;
	((($MAX_LINK_DEPTH) = /^(\d+)$/)
	 || print("Unrecognised value for -link: $_\n")
	 && &usage && die);
    }
    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 (/^-prefix$/) {
	$PREFIX = shift;
    }
    elsif (/^-auto_prefix$/) {
	$AUTO_PREFIX = 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 (/^-reuse/) {
        $REUSE = shift;
    }
    elsif (/^-no_reuse/) {
        $REUSE = 0;
    }
    elsif (/^-no_images/) {
        $NO_IMAGES = 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);
    }
    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 ( /^-contents$/ ) {
	$EXTERNAL_CONTENTS = shift;
	$EXTERNAL_CONTENTS =~ s/~/&#126;/g; # protect `~'
    }
    elsif ( /^-external_file$/ ) {
	$EXTERNAL_FILE = shift;
    }
    elsif (/^-h(elp)?$/) {
	&usage;
    }
    elsif (/^-v/) {
	print "$TEX2HTMLV_SHORT\n";
	exit 0;
    }
    elsif (/^-short_index/) {
	$SHORT_INDEX = 1;
    }
    elsif (/^-debug/) {
	$DEBUG = 1;
    }
    ###MEH
    elsif (/^-html_version$/) {
	$_ = shift;
	if ( /^\d(\.\d)?$/ ) {
	    $HTML_VERSION = $_;
	} else {
	    print("Invalid HTML version, defaulting to $HTML_VERSION\n");
	};
    }
    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
$NO_FOOTNODE = 1 unless $MAX_SPLIT_DEPTH;
$NO_SPLIT = 1 unless $MAX_SPLIT_DEPTH;
$SEGMENT = $SEGMENTED = 0;

####################################################################
#
# 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 ($PK_GENERATION) {
      if ($MATH_SCALE_FACTOR <= 0) { $MATH_SCALE_FACTOR = 2; }
      if ($FIGURE_SCALE_FACTOR <= 0) { $FIGURE_SCALE_FACTOR = 2; }
      $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");
      }
      $DVIPSOPT = " -y $dvi_mag -D $METAFONT_DPI $mode_switch -e 5 ";
    }
    else
    {
      $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, $texfilepath, $orig_cwd, %unknown_commands, $bbl_cnt, $dbg);
    local(%styles_loaded);
    $orig_cwd = &getcwd;
    &initialise;		# Initialise some global variables
    &ascii_mode if $ASCII_MODE;	# Must come after initialization
    &titles_language($TITLES_LANGUAGE);
    $dbg = $DEBUG ? "-debug" : "";
    foreach $FILE (@ARGV) {
	local($bbl_nr) = 1;
	local($global_page_num) = (0); # The number of reused images and those in images.tex
	local($new_page_num) = (0); # The number of images in images.tex
	local($pid, $sections_rx, $sections_no_delim_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);
	## 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);
	if (-f "$texfilepath/$FILE") {
	    print "This is LaTeX2HTML Version $TEX2HTMLVERSION by Nikos Drakos, \n";
	    print "Computer Based Learning Unit, University of Leeds.\n\n";

            # Tell texexpand which files we *don't* want to look at.
            $ENV{'TEXE_DONT_INCLUDE'} = $DONT_INCLUDE;
	    $FILE =~ s/\.[^\.]*$//; ## AYS
	    $DESTDIR = $FILE unless $DESTDIR;
	    $PREFIX  = "$FILE-" if $AUTO_PREFIX;
	    $DESTDIR = "." if $NO_SUBDIR;
	    print "OPENING $texfilepath/$FILE.$EXT \n"; ## AYS
	    next unless &new_dir($DESTDIR);
	    &deal_with_texinputs($texfilepath, $DESTDIR);
	    # This needs $DESTDIR to have been created ...
	    &syswait("$TEXEXPAND $dbg -auto_exclude -save_styles " .
		     "$DESTDIR/TMP_styles $FILE.$EXT > $DESTDIR/TMP_$FILE")
		&& print "Error: $!\n";
	    chdir($DESTDIR) || die "$!\n";
	    $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;
		&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;
		&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.
	    local($from,$to) = (eval($LINKPOINT),eval($LINKNAME));
	    if (length($from) && length($to) && ($from ne $to)) {
		unlink($to);
		link($from,$to);
	    }

	    chdir($orig_cwd);# Go back to the source directory
	    }
	else {
	    print "Cannot read $texfilepath/$FILE \n";}
    }
    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";
    $_;
}

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_environment, "new_environment");
    $preamble = &read_mydb(*preamble, "preamble");
}

sub close_dbm_database {
    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);
}

#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 $_;
    close DB;
}

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

# 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;
}

# 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) = 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 ...";
	&slurp_input($file);
	&pre_process;
	&substitute_meta_cmds if (%new_command || %new_environment);
	&wrap_shorthand_environments;
	$_ = &translate_commands(&translate_environments($_));
    };
    $found;
}

sub deal_with_texinputs {
    local($source) = @_;
    $ENV{'TEXINPUTS'} = join(':',".","..",$source,$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($_, $file, $dir);
    print "\n";
    if ($TEXDEFS) {
	foreach $dir (split(/:/,$LATEX2HTMLSTYLES)) { 
	    if (-f ($_ = "$dir/texdefs.perl")) {
		print "Loading $_...\n";
		require ($_);
		$styles_loaded{'texdefs'} = 1;
		last;
	    }
	}
    }
    open(STYLES, "<TMP_styles");
    while(<STYLES>) {
	s/\s//g;
	$file = $_;
	if (-f ($_ = "$texfilepath/$file.perl"))  {
	    print "Loading $_...\n";
	    require ($_);
	}
	#JKR: use $LATEX2HTMLSTYLES as a pathlist
        #JCL: - load only first translation found in the pathlist
        #     - avoid multiple loading caused by equal entries in TMP_styles
	else {
	    foreach $dir (split(/:/,$LATEX2HTMLSTYLES)) { 
		if (-f ($_ = "$dir/$file.perl") && !( $styles_loaded{$file})) {
		    print "Loading $_...\n";
		    require($_);
		    $styles_loaded{$file} = 1;
		    last;
		}
	    }
	}
    }
    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(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(verb)
    while (/\\begin$verbatim_env_rx/o) {
 	local($before, $contents, $after, $env);
	($before, $after, $env) = ($`, $', $1);
 	if ($after =~ /\s*\\end{$env[*]?}/) { # Must NOT use the s///o option!!!
	    ($contents, $after) = ($`, $');
	    $contents =~ s/\\\\ /\\\\/go;
 	    $contents =~ s/^\n*//;
	    $contents =~ s/$comment_mark(\d+)/$comments{$1}/g;
	    $contents = &revert_to_raw_tex($contents)
		if ($env =~ /rawhtml/i);

	    &write_mydb("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(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);
    # must tag only one alternation per loop
    while (s/\\verb(\t*\*\t*)(\S)/"<verb$1".++$global{'verb_counter'}.">$2"/e ||
	   s/\\verb()([^a-zA-Z*\s])/"<verb$1".++$global{'verb_counter'}.">$2"/e ||
	   s/\\verb(\t\t*)([^*\s])/"<verb$1".++$global{'verb_counter'}.">$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>]*$global{'verb_counter'}>[$esc_del]([^$esc_del]*)[$esc_del]/
	    $contents=$1;
	$contents =~ s|\\\\ |\\\\|g;
	$contents =~ s|\n| |g;
	$verb{$global{'verb_counter'}}=$contents;
	$verb_delim{$global{'verb_counter'}}=$del;
	join('',$verb_mark,$global{'verb_counter'},$verb_mark)/e;
    }
    # 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;
#
    $* = 0;			# Multiline matching OFF
    &mark_string;
}

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);
	    # ... so that we can return to it
	    seek(SINPUT, $pos, 0);
	    print STDERR "\nDoing $1";
	    ++$count}
	else {
	    $string{'STRING'} .= $_}
    }
    &write_string_out($count);
    @files = sort file_sort (<TMP-part*>);
    foreach $file (@files) {
	$_ .= `cat $file`;}
}

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 ...
	&substitute_meta_cmds;
	open(OUTPUT, ">$ppid-part$count");
	print OUTPUT $_;
	close(OUTPUT);
	&write_mydb_simple("preamble", $preamble);
	&close_dbm_database;
	exit 0;
    };
    waitpid($pid,0);
    &open_dbm_database;
    delete $string{'STRING'};
}
    
# Reads the entire input file into a 
# single string. 
sub slurp_input  {
    local($file) = @_;
    local(%string);
    open(INPUT,"<$file");
    while (<INPUT>) {
	$string{'STRING'} .= $_};
    $_ = 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_closing_bracket/go;
    $* = 0;			# Multiline matching OFF
    for (;;) {			# Infinite loop
	last unless
	    s/{([^{}]*)}/join("",$O,++$global{'max_id'},$C,$1,$O,$global{'max_id'},$C)/geo;
    }
    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);
    s/([\\]begin\s*$O\d+$C\s*thebibliography)/$bbl_cnt++; $1/eg;
#print STDERR "\nthebibliography: $bbl_cnt\n";
    #if ($bbl_cnt == 1) {
	s/([\\]begin\s*$O\d+$C\s*thebibliography)/do { $id =  ++$global{'max_id'}; "\\bibliography$O$id$C$O$id$C $1"}/geo;
    #}
    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
sub convert_iso_latin_chars {
    local($_) = @_;
    local($next_language, $pattern, $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;
}
    
sub translate {
    &tokenize($sections_no_delim_rx); # Inserts space after a sectioning command
    &normalize_sections;	# Deal with the *-form of sectioning commands
    # Split the input into sections
    local(@sections) = split(/$sections_no_delim_rx /, $_);
    local($sections) = int(scalar(@sections) / 2);
    # 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, $changed);
    &process_aux_file  if (/\\ref/o || /\\caption/o || /\\cite/o || ($SHOW_SECTION_NUMBERS));
#RRM:
    local(%index_labels, %index_segment, $preindex);
    local($dir,$nosave) = ('','');
#
    require ("${PREFIX}internals.pl") if (-f "${PREFIX}internals.pl");
    $single_cmd_rx = $single_cmd_atother_rx;
    $tocfile = $EXTERNAL_CONTENTS;
    $idxfile = $EXTERNAL_INDEX;
    print "\nTranslating ...";
    while ($i <= @sections) {
	$_ = $sections[$i];
	s/^[\s]*//;		# Remove initial blank lines
	# The section command was removed when splitting ... 
	s/^/\\$curr_sec /  if ($i > 0); # ... so put it back
	if ($current_depth < $MAX_SPLIT_DEPTH)  {
	    $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;
	print $i/2 . "/$sections...";
	# Must do this early ... It also sets $TITLE
	&process_command($sections_rx, *_) if /$sections_rx/;
	# RRM(col) - next statement to support colors, look also color.perl & colordvi.perl
	if ((/\\latextohtmlditchpreceding/)||(/\\startdocument/)) {
	    $PREAMBLE = 1;
	    $NESTING_LEVEL=0;	#counter for TeX group nesting level
 	    local($after) = $';
 	    &translate_commands(&translate_environments("$`$&"));
 	    if (defined &set_section_color) { &set_section_color(); }
	    $PREAMBLE = 0;
 	    $NESTING_LEVEL=0;
 	    $_ = &translate_commands(&translate_environments("$after"));
 	} else {
 	    if (defined &set_section_color) { &set_section_color(); }
	    $PREAMBLE = 0;
 	    $NESTING_LEVEL=0;
 	    $_ = &translate_commands(&translate_environments($_));
 	}
	print OUTPUT $_;
	# Associate each id with the depth, the filename and the title
	$TITLE = $CURRENT_FILE unless ($TITLE);
	###MEH -- starred sections don't show up in TOC
	$toc_section_info{join(' ',@curr_sec_id)} =
	    "$current_depth$delim$CURRENT_FILE$delim$TITLE" .
		($curr_sec =~ /star$/ ? "$delim<tex2html_star_mark>" : "");
	$section_info{join(' ',@curr_sec_id)} =
	    "$current_depth$delim$CURRENT_FILE$delim$TITLE$delim$BODYTEXT" if 
		($current_depth < $MAX_SPLIT_DEPTH);
	++$i;
	# Get the depth of the current section;
	$curr_sec = $sections[$i];
	$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;
    }
    $_ = undef;
    $_ = &make_footnotes if $footnotes;
    print OUTPUT;
    close OUTPUT;
    &make_image_file;
    &make_images;
    # Link sections, add head/body/address do cross-refs etc
    &post_process;
    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);
}

############################ 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

    $* = 1;			# Multiline matching ON
    s/(^\\[(])|([^\\])(\\[(])/{$2.&make_wrapper(1).$1.$3}/geo;
    s/(^\\[)]|[^\\]\\[)])/{$1.&make_wrapper(0)}/geo;

    s/(^\\[[])|([^\\])(\\[[])/{$2.&make_any_wrapper(1,"displaymath")}/geo;
    s/(^\\[\]])|([^\\])(\\[\]])/{$2.&make_any_wrapper(0,"displaymath")}/geo;
    s/$enspair/{&make_any_wrapper(1,$wrapper).$&.&make_any_wrapper(0,$wrapper)}/geo;
    $* = 0;			# Multiline matching OFF		       

    $double_dol_rx = '(^|[^\\\\])\\$\\$';
    $single_dol_rx = '(^|[^\\\\])\\$';

    $_ = &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);
    local ($underscore_match_rx) = "^\\s*\\\\\\_\\s*\\\$";
    local ($wrapper);

    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;

        while (/$end_rx/) {
	    # Forget the $$ if we are going to replace it with "displaymath"
            $before = $` . (($wrapper eq "displaymath")? "$1" : $&);

	    $processed_text .= $before; 
            $_ = $';

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

            local ($losing, $br_rx) = (0, '');
            while ($before =~ /$begin_cmd_rx/) {
                $br_rx = &make_end_cmd_rx($1);  $before = $';

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

            last unless $losing;

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

            /$br_rx/;
            $processed_text .= $`.$&;
            $_ = $';
        }

        # Got to the end.  Whew!

        $processed_text .= &make_any_wrapper(0,$wrapper);
    }
    $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)) {
	    &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) &&
		    (! ($env =~ /latexonly/o)) &&
		    (! $raw_arg_cmds{$env}));
	    &process_environment($env, $br_id);
	    undef $_;
	    if ($capenv && $captions) {
		$after = "<BR>\n$captions<BR>\n$after";
		$captions = "";}
	    #JCL(env) - insert the $O$br_id$C stuff to handle environment grouping
	    $_ = join("", $before, $O,$br_id,$C, $contents, $O,$br_id,$C, $after)}
	### Evan Welsh <welsh@epcc.ed.ac.uk> added the next 24 lines ##
	elsif (&defined_env($env)) {
	    # 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, $contents);
	    $_ = join("", $before, $contents);
	    if ($capenv && $captions) {
		$_ .= "<BR>\n$captions<BR>\n";
		$captions = "";}
	} elsif ($ignored{$env}) {
	    # 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}) {
	    # 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, $contents);
	    $_ = join("", $before, $contents);
	    if ($capenv && $captions) {
		$_ .= "<BR>\n$captions<BR>\n";
		$captions = "";}
	    &write_warnings("\n\\end{$env} not found (truncated at next section boundary).\n");}
	else {
	    $pattern = &escape_rx_chars($pattern); 
	    s/$pattern//;
	    print "Cannot find \\end{$env}\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) = ("do_env_$env");
    if (&defined_env($env)) {
	print ".";
	$env_sub =~ s/\*$/star/;
	$contents = &$env_sub($contents);
    }
    elsif (&special_env) {	# &special_env modifies $contents
    }
    elsif ($ignore{$env}) {
	""    }
    else {			# Generate picture
	$contents = &process_undefined_environment($env, $id, $contents);
	$env_sub = "post_latex_$env_sub"; # i.e. post_latex_do_env_ENV
        ( defined &$env_sub ? $contents = &$env_sub($contents) :
	 do {$contents = join('',"<BR>",$contents,"<BR>") unless ($env =~ /^(tex2html_wrap|math)/o)});
    };
}

# 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 ($allow) = $HTML_VERSION ge '3.1' ?
	"[^#\$%&~\\\\{}]" : "[^^#\$%&~_\\\\{}]";
    #JKR: Use italics instead of bold #HWS: Generalize to include more symbols.
    $contents =~ s/^\$(\s*($html_specials_inv_rx|$allow)*\s*)\$/&simple_math_env($1)/ige;
}

# Translate simple math environments into italic.
# Only letters should become italic; symbols should stay non-italic.
sub simple_math_env {
    local($mathcontents) = @_;
    $mathcontents =~ s/\^$any_next_pair_rx/<SUP>$2<\/SUP>/go;
    $mathcontents =~ s/_$any_next_pair_rx/<SUB>$2<\/SUB>/go;
    $mathcontents =~ s/\^(.)/<SUP>$1<\/SUP>/g;
    $mathcontents =~ s/_(.)/<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;
}
 
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}));
}

sub process_undefined_environment {
    local($env, $id, $contents) = @_;
    local($name,$cached,$raw_contents,$uucontents) = ("$env$id");
    local($oldgif,$size);
    return if ($AUX_FILE);
#RRM - LaTeX commands wrapped with this environment go directly into images.tex.
    if ($env =~ /tex2html_nowrap/){ # leave off the wrapper, do not cache
	if (!($PREAMBLE)) {	# totally ignore if in preamble
	    $raw_contents = &revert_to_raw_tex($contents);
	    $latex_body .= "\n$raw_contents"."%\n\n";
	}
	return("");
    }
    $contents = "% latex2html id marker $id\n$contents" if
	$contents =~ /$order_sensitive_rx/;
    $contents = "\\begin{$env}$contents\\end{$env}";
    $latex_body{$name} = $contents;
    $uucontents = &encode($contents);
    $cached = $cached_env_img{$uucontents};
    if ($NOLATEX) {
	$id_map{$name} = "[$name]";
    }
    elsif (defined ($_ = $cached)) {	# Is it in our cache?
	if (($oldgif) = /SRC="$gif_rx\.gif"/o) {    # Have we already used it?
	    $size = &get_image_size("$oldgif.old"); # No, check its size
	    if ($size && / $size /) {	# Size is OK; recycle it!
		++$global_page_num;
		s/(${PREFIX}T?img)\d+\.(gif|html)/&rename_html($&,"$1$global_page_num.$2")/geo;
	    }
	    else {
	        $_ = "";		# The old GIF has wrong size!
	        undef($cached);	#  (or it doesn't exist)
	    }
        }
        s/$gif_rx\.new/$1.gif/go;	# Point to the actual GIF file(s)
        $id_map{$name} = $_;
        s/$gif_rx\.gif/$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);
	$img_params{$name} = join('#', &extract_parameters($contents));
	$raw_contents = &revert_to_raw_tex($contents);
        $raw_contents =~ s/\\pagebreak|\\newpage|\\clearpage/\\\\/go;
        if ($env =~ /inline|xy|diagram/) {
            $raw_contents = &make_hbox("$name", $raw_contents);
        }
	# 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(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\\samepage\n$_" .
	    "$raw_contents}\\hbox{}\\vfil\n\n";
    }
# Anchor the labels and put a marker in the text;
    &do_labels($contents,"$image_mark#$name#");
}

sub make_box {
    local($id,$contents) = @_;
    local($start) =  '\\setbox\\sizebox=\\hbox{';
    local($end) = "}\\lthtmltypeout{latex2htmlSize :$id: \\the\\ht\\sizebox";
    $end .= "::\\the\\dp\\sizebox.}\\box\\sizebox";
    $start . $contents . $end;
}

sub make_hbox {
    &make_box;
}

sub make_vbox {
    &make_box;
}

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 {
	&syswait("$LATEX ./${PREFIX}images.tex");
	print "\nGenerating postscript images using dvips ...\n";
	&process_log_file("./${PREFIX}images.log"); # Get eqn size info
	&syswait("$DVIPS $DVIPSOPT -S 1 -i -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$/};
    } if  ((!$NOLATEX) && (-f "./${PREFIX}images.tex"));
    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";
	&syswait("$LATEX ./${PREFIX}images.tex") 
	    unless ((-e "./${PREFIX}images.log") && ($NO_IMAGES));
	&process_log_file("./${PREFIX}images.log"); # Get eqn size info
    }
    if ($NO_IMAGES) {
	&syswait("cp $LATEX2HTMLDIR/icons/image.gif .")
	    if (-e "$LATEX2HTMLDIR/icons/image.gif") && !(-e "image.gif");
    }
    elsif ((!$NOLATEX) && ($latex_body =~ /newpage/)) {
   	print "\nGenerating postscript images using dvips ...\n";
	&syswait("$DVIPS $DVIPSOPT -S 1 -i -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$/};
    }
    while ( ($uucontents, $_) = each %cached_env_img) {
	delete $cached_env_img{$uucontents} if (/$gif_rx\.gif/o);
	$cached_env_img{$uucontents} = $_ if (s/$gif_rx\.new/$1.gif/go);
    }
    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});
		$uucontents = &encode($contents); # Arrggh
		if (! ($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
	}
    }
    &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"));
    &cleanup;
}

sub process_log_file {
    local($logfile) = @_;
    local($name);
    open(LOG, "$logfile") || die "Cannot find logfile $logfile";
    while (<LOG>) {
        if (/latex2htmlSize/) {
	    /:([^:]*):/;
	    $name = $1; $name =~ s/\*//g;
	    /: ([0-9.]*)pt/;
	    $height{$name} = $1;
	    /::([0-9.]*)pt/;
	    $depth{$name} = $1;
	}
    }
    close(LOG);
}

# Uses $img_params
sub extract_image {
    local($page_num,$name) = @_;  
    local($scale, $external, $thumbnail, $map, $psimage, $align, $usemap,
	  $flip, $global_num, $new_num, $lwidth);
    # $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/\*//;
    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.gif";
	($scale, $external, $thumbnail, $map, $psimage, $align, $usemap, $flip, $alt) = 
	    split('#', $img_params{$name});
	$lwidth = ($align =~ s/nojustify/middle/) ? 0 : $LINE_WIDTH;
	$alt = "ALT=\"$name\"" unless $alt;
	if ($NO_IMAGES) {
	    symlink("image.gif", "${PREFIX}$img");
	    if ($thumbnail) {
		symlink("image.gif", "${PREFIX}T$img");
		$thumbnail = "${PREFIX}T$img";
	    }
	}
	else {
	    if ( ($name =~ /figure/) || $psimage
		|| $scale || $thumbnail) {
		$scale = $FIGURE_SCALE_FACTOR unless ($scale);
		&convert_image("$psname.ps", "${PREFIX}$img", $scale, "", $flip);
		if ($thumbnail) { # $thumbnail contains the reduction factor
		    &convert_image("$psname.ps", "${PREFIX}T$img", $thumbnail, "", $flip);
		    $thumbnail = "${PREFIX}T$img";
		}
	    }
	    else {
		&convert_image("$psname.ps", "${PREFIX}$img", $MATH_SCALE_FACTOR , 1, "")}
	    if ($name =~ /(equation|eqnarray|displaymath)/) {
		&right_justify("${PREFIX}$basename", $name, $lwidth); }
	    if ($name =~ /inline/ && $depth{$name} != 0) {
	        &top_justify("${PREFIX}$basename", $name)}
	    if ($TRANSPARENT_FIGURES || !($env =~ /figure/o)) {
		&make_transparent("${PREFIX}$img"); 
		&make_transparent("${PREFIX}T$img") if $thumbnail; }
	    &write_warnings("\nFailed to convert image $psname.ps")
		if ! -r "${PREFIX}$img";
	}  
    }
    &embed_image("${PREFIX}$img", $name, $external, $alt, $thumbnail, $map,
		 $align, $usemap);
}

sub extract_parameters {
    local($contents) = @_;
    local($_, $scale, $external, $thumbnail, $map, $psimage, $align,
	  $usemap, $flip, $alt);
    $psimage++ if ($contents =~ /\.ps/);
    $contents =~ s/$htmlimage_rx/$_ = $2;''/ego;
    $contents =~ s/\s//g;	# Remove spaces
    ($scale) = /scale=([\.\d]*)/;
    $external = /(^|,\s*)external/;
    ($thumbnail) = /thumbnail=([\.\d]*)/;
    ($map) = /map=([^\s,]+)/;
    ($align) = /align=([^\s,]+)/;
    ($usemap) = /usemap=([^\s,]+)/;
    ($flip) = /flip=([^\s,]+)/;
    ($alt) = /alt=([^,]+)/;
    ($scale, $external, $thumbnail, $map, $psimage, $align, $usemap, $flip, $alt);
}
    
sub convert_image {
    local($in_img, $out_img, $scale, $depth, $flip) = @_;
    local($paperopt) = ( ($ENV{'PSTOPPM'} =~ /pstoppm\.ps/) ?
			" -papersize $PAPERSIZE" : undef);
    unlink $out_img;
    &syswait( "$PSTOGIF " .
	     do {" -depth $depth " if $depth} .
	     do {" -flip $flip" if $flip} .
	     # This will work not work with pstoppm3.ps
	     $paperopt .
	     do {" -scale $scale " if ($scale > 0)} .
	     " -out $out_img $in_img")
	&& print "Error while converting image: $!\n";
}

sub make_transparent {
    local($img) = @_;
    if ($GIFTRANS) {
	# Making the white color transparent - this may not
	# always be a good idea...
	# Make the background color gray anyway for broken browsers
	&syswait("$GIFTRANS -t '#ffffff' -B '#bfbfbf' $img > TMP_tmp.gif")
	    && do {print "Could not make $img transparent: $!\n";
		   return};
	rename("TMP_tmp.gif", $img);
    }
    elsif ($USENETPBM) {
	&syswait("$GIFTOPPM $img | $PPMTOGIF -trans '#ffffff' > TMP_tmp.gif")
	    && do {print "Could not make $img transparent: $!\n";
                   return};
	rename("TMP_tmp.gif", $img);
    }
}
	       
# This takes a filename (an GIF of an equation or equation array) and 
# prepends enough whitespace so that it is right justified.
# It uses the passed variable $width and the pbmplus routines
# giftoppm (to convert to ppm), pnmfile (to ask for its dimensions),
# pbmmake (to generate a blank PBM of (width)x1 pixel), pnmcat 
# (to prepend the replicated blank bitmap) and ppmtogif 
# (to convert the result back to GIF).
sub right_justify {
    local($basename, $env, $width) = @_;
    local($_, $img_width, $justification_width) = (0,0);
    &syswait("$GIFTOPPM $basename.gif > $basename.ppm") &&
	print "Error: $!\n";
    $_ = `$PNMFILE $basename.ppm`;
    s/([\d]+) by/$img_width = $1/eo;
    do {
	$justification_width = ($width -  $img_width);
	$justification_width = ($justification_width / 2) if
	    $env =~ /displaymath/;
	&syswait("$PBMMAKE -white $justification_width 1 |$PNMCAT -l - $basename.ppm|$PPMTOGIF - > $basename.gif") && print "Error: $!\n";
	unlink "$basename.ppm";
    } 
    if ($img_width < $width);
}

sub top_justify {
    local($basename, $name) = @_;  
    local($_, $img_height, $h, $d, $adjust) = (0,0);
    &syswait("$GIFTOPPM $basename.gif > $basename.ppm") &&
	print "Error: $!\n";
    $_ = `$PNMFILE $basename.ppm`;
    s/by ([\d]+)/$img_height = $1/eo;
    $h = $height{$name};
    $d = $depth{$name};
    $adjust = ($h - $d) * $img_height / ($h + $d);
    $adjust = int($adjust + 0.99);
    if ($adjust > 0 ) {		# Only if $adjust is positive!
	&syswait("$PBMMAKE -white 1 $adjust |$PNMCAT -tb $basename.ppm -|$PPMTOGIF - > $basename.gif") && print "Error: $!\n";
    }
    unlink "$basename.ppm";
}

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 =~ /[^\/]*$/;
        $dest .= '/' . $&;
        $dest =~ s|//||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*.gif>) {
	    $old_name = $_;
	    s/\.gif$/\.old/o;
	    rename($old_name, $_);
	    }
	}
    else {
	foreach (<img*.gif>) {
	    $old_name = $_;
	    s/\.gif$/\.old/o;
	    rename($old_name, $_);
	}
	foreach (<Timg*.gif>) {
	    $old_name = $_;
	    s/\.gif$/\.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) = ($`, $');
	    undef $_;
	    $contents = &translate_commands($contents)
                if ($contents =~ /$match_br_rx/o);
	    # Modifies $contents
	    &process_command($single_cmd_rx,*contents)
		if ($contents =~ /\\/o);
	    # 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);
    for (;;) {			# Do NOT use the o option
	last unless ($ref_contents =~ /$cmd_rx/ );
	($ref_before, $cmd, $after) = ($`, $1, "$2$'");
	print(".");
#	$after =~ s/^[ ]+/ /o;	 Collapse all spaces that follow a command
	if ($cmd =~ /[a-zA-Z]$/) { # Eat redundant spaces that follow a command
	    $after =~ s/^[ \t]+//o; }
	else {
	    $after =~ s/^[ \t]+/ /o; }
	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) {
		# $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);
		};
	    } 
	    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;
		} else {
		    $after = "<MATH>&$mathentity;</MATH>" . $after;
		    ++$commands_outside_math{$cmd};
		}; }
	    elsif ($ignore{$cmd}) { # Ignored command
		print "."}
	    elsif ($cmd =~ /^the(.+)$/) { # Counter
		$counter = $1;
		$after = &do_cmd_thecounter($after);}
	    else {
		# Do not add if reading an auxiliary file
		++$unknown_commands{$cmd} unless $AUX_FILE;
	    }
	}
	$ref_contents = join('', $ref_before, $after);
    }
    $ref_contents;
}
####################### 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, $argn, $body, $before, $after, $new_cmd_rx, $new_env_rx);
    &tokenize($meta_cmd_rx);	# Inserts a space after meta commands ...
    print "\nProcessing macros ..." if (%new_command || %new_environment);
    while (/$meta_cmd_rx /o) {	# ... and uses the space
	undef $_;
	($before, $cmd, $after) = ($`, $1, $');
	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, $new_env_rx) = (&make_new_cmd_rx, &make_new_env_rx('begin'));
    do {
	while (($cmd, $code) = each %new_command) {
	    if (! $expanded{"CMD$cmd"}) {
		$new_command{$cmd} = &expand_code($code);
		&write_mydb("new_command", $cmd, $new_command{$cmd});
	    }
#EI#
	    if (!defined($expanded{"CMD$cmd"})) {
		$expanded{"CMD$cmd"} = 1;
	    } else {
		$expanded{"CMD$cmd"}++;		
	    }
	}
	while (($cmd, $code) = each %new_environment) {
	    if (! $expanded{"ENV$cmd"}) {
		$new_environment{$cmd} = &expand_code($code);
		&write_mydb("new_environment", $cmd, $new_environment{$cmd});
	    }
	    if (!defined($expanded{"ENV$cmd"})) {
		$expanded{"ENV$cmd"} = 1;
	    } else {
		$expanded{"ENV$cmd"}++;		
	    }
#EI#
	}
	&tokenize($new_cmd_rx);	# Inserts a space after the new commands ...
	print "+";
	if ($new_cmd_rx) {
	    #JKR: There are two spaces after commands.
	    # As I don't understand the problem with &tokenize, this
	    # patch might break the expansion.
	    while (/$new_cmd_rx  /o && (($before, $cmd, $after) = ($`, $1, $'))) {
		print ".";
		$_ = join('',$before, &substitute_newcmd);
	    }
	}
	if ($new_env_rx) {
	    while (/$new_env_rx/o && (($before, $cmd, $after) = ($`, $2, $'))) {
		print ".";
		$_ = join('',$before, &substitute_newenv);
	    }
	}
#    } if (each %new_command || each %new_environment);
    };
}


sub expand_code {
    local($_) = @_;
    # Uses $new_cmd_rx and $new_env_rx set in the caller

    if ($new_cmd_rx eq "0") { $new_cmd_rx = "<<{this cant possibly match}>>"; }
    if ($new_env_rx eq "0") { $new_env_rx = "<<{this cant possibly match}>>"; }

    local($cmd, $before, $after);
    &tokenize($new_cmd_rx);	# Inserts a space after the new commands ...
    while ( ($new_cmd_rx && /$new_cmd_rx /) ||
	   ($new_env_rx && /$new_env_rx/)) {
	# $new_cmd_rx binds $1 and $new_env_rx binds $2 ...
	$cmd = ($2 ? $2 : $1);
	($before, $after) = ($`, $');
	if ($new_command{$cmd}) { # We have a command
	    $_ = join('',$before, &substitute_newcmd);
	}
	elsif ($new_environment{$cmd}) {
	    $_ = join('',$before, &substitute_newenv);
	}
	&tokenize($new_cmd_rx); # Must do it for any newly inserted code
    }
    $_;
}


# 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
    # (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(1);
    $tmp = "do_cmd_$cmd";
    $new_command{$cmd} = join(':!:',$argn,$body,$opt)
	unless (defined &$tmp);
    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)
    $opt = '}';			# Flag for no optional arg
    $opt = &get_next(0) if (/^\[/);
    $tmp = "do_env_$env";
    $begin = &get_next(1); $end = &get_next(1);
    $new_environment{$env} = join(':!:', $argn, $begin, $end, $opt)
	unless defined &$tmp;
    $_;
}
    
sub get_body_renewcommand {
    &get_body_newcommand($_[0]);
}
sub get_body_providecommand {
    &get_body_newcommand($_[0]);
}
sub get_body_renewenvironment {	
    &get_body_newenvironment($_[0]);
}

sub substitute_newcmd {
    # Modifies $cmd and $after in the caller
    # Get the body from the new_command array
    local($argn, $_, $opt) = split(/:!:/, $new_command{$cmd});
    local($arg);
    foreach $i (1..$argn) {
	if ($i == 1 && $opt ne '}') {
	    $after =~ s/$optional_arg_rx/$arg = $1;''/eo;
	    $arg = $opt unless $arg;
	}
	else {
	    $arg = $undef_mark;
	    $after =~ s/$next_pair_rx/$arg = $2;''/eo; # Get the next argument
	    # Next argument may not be in braces - get next character - ARGG!
	    $after =~ s/\s*(.)/$arg = $1;''/eo if ($arg eq $undef_mark);
	}
	s/\#$i/$arg/g;		# Substitute the arguments in the body
    }
    # Make the body unique (give unique id's to the brackets),
    # translate, and return it
    $_ = &revert_to_raw_tex($_);
    &pre_process;
    join('',$_,$after);
}

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);
    # Note that latex allows argument substitution only in the
    # \begin part of the new definition
    local($_) = $begdef;
    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;
	}
	s/\#$i/ $arg/g;		# Substitute the arguments in the body
    }
    # Make the body unique (Give unique id's to the brackets),
    # translate, and return it
    $_ = &revert_to_raw_tex($_);
    &pre_process;		# Make unique
    $begdef = $_;
    # Now substitute the \end part:
    $_ = &revert_to_raw_tex($enddef);
    &pre_process;		# Make unique
    $enddef = $_;
    $new_def_rx = &make_end_env_rx($cmd);
    $after =~ s/$new_def_rx/$enddef/;
    join('',$begdef,$after);
}

# 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(*_) = @_;
    # Just chop off the arguments and append to $next_def
    &get_next(1);
    &get_next(0);
    &get_next(1);
    &get_next(0);
    $_;
}     

# 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;}
    else {
	($next, $pat) =  &get_next_optional_argument;}
    $next_def .= &revert_to_raw_tex($pat) if $pat;
    $next =~ s/(^\s*)|(\s*$)//g;
    $next;
}

# The following get_next_<something> ARE ALL DESTRUCTIVE.
sub get_next_argument {
    local($next, $br_id, $pat);
    s/$next_pair_rx/$br_id=$1;$next=$2;$pat=$&;''/eo;
    ($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*\[\]//g unless $pat; # This is not picked by $optional_arg_rx
	    ($next, $pat);
}

sub get_next_tex_cmd {
    local($next, $pat);
    s/$single_cmd_rx/$next = $1; $pat=$&; ''/eo;
    ($next, $pat);
}

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 }

    # '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);
}

# 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;
    # Make the @ character a normal letter ...
    $preamble =~ s/(\\document(class|style)(\[[^\]]+\])?\{\w+\})/$1\n\\makeatletter/;
    # ... and make it special again after the preamble
    ($DEBUG ? "\\nonstopmode" : "\\batchmode") .
    "\n$preamble\n\\makeatother\n" .
    "\\newenvironment{tex2html_wrap}{}{}\n" .
    "\\newwrite\\lthtmlwrite\n" .
    "\\def\\lthtmltypeout#1{{\\let\\protect\\string\\immediate\\write\\lthtmlwrite{#1}}}%\n" .
    "\\newbox\\sizebox\n" .
# jcl(pag) - Allow very long pages to prevent page breaks with huge figures, tables, etc.
    "\\textheight 50cm\n" .
    "\\begin{document}\n" .
    "\\pagestyle{empty}\n" .
    "$contents\n".
    "\\clearpage\n" .
    "\\end{document}";
}
    
# 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($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;
    $title =~ s/<[^>]*>//g;		# Remove HTML tags
    "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML $HTML_VERSION//$isolanguage\">\n" .
    "<!--Converted with LaTeX2HTML $TEX2HTMLVERSION by Nikos Drakos (nikos\@cbl.leeds.ac.uk), CBLU, University of Leeds -->\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" : "" ) .
    "<LINK REL=STYLESHEET HREF=\"$FILE.css\">\n" .
    "</HEAD>\n<BODY LANG=\"$isolanguage\" $body>\n";
}

sub style_sheet {
    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);
}

sub make_address {
    local($_) = $ADDRESS;
    ($_ ? "<P><ADDRESS>\n$_\n</ADDRESS>" : "") . "\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;
    }
    #$_ = pack("u*", $_);	# uuencode
    s/\/|\\//g;			# remove funnies may cause problems in a hash key
    s/\s*|\n//g;		# Remove spaces  and newlines
    $_;
}

    
##################### 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);
    @tmp_keys = @keys = sort numerically keys %section_info;
    print "\nDoing section links ...";
    while (@tmp_keys) {
        $key = shift @tmp_keys;
	print ".";
	($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;
	@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);

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

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

	    @link = split(' ',$tmp_keys[0]);
	    ($NEXT, $NEXT_TITLE) = 
		&add_link($next_page_visible_mark, $file,@link);

	    $CONTENTS = &add_special_link($contents_visible_mark, $tocfile, $file)
		if $CONTENTS_IN_NAVIGATION;
	    $INDEX = &add_special_link($index_visible_mark, $idxfile, $file)
		if $INDEX_IN_NAVIGATION;
#
            $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;
	    $_ = &make_head_and_body($title, $body);
	    $header = join(' ', $_);
      	    $header = join(' ', $header, $top_navigation) if $TOP_NAVIGATION;
#
	    rename($file, "TMP.$file");
	    open(INPUT, "<TMP.$file") || die "Cannot open file TMP.$file $!";
	    open(OUTFILE, ">$file") || die "Cannot open file $file $!";
	    &slurp_input("TMP.$file");
	    if (($INDEX) && ($SHORT_INDEX) && ($SEGMENT eq 1)) { 
		&make_index_segment($title,$file); }
	    $child_links = &add_child_links(0,$depth, $key, @keys);
	    $flag = (($BOTTOM_NAVIGATION || &auto_navigation) &&
		     $bot_navigation);
	    $_ = join('', $_, $CHILDLINE)
		if $child_links;
	    $_ = join('', $header, $_, $child_links);
	    $_ = join('', $_, $bot_navigation) if ($flag);
#	    $flag = 0;
	    $_ = join('', $_, $CHILDLINE)
		unless $flag;
	    &remove_markers;
	    &post_post_process if (defined &post_post_process);
	    print OUTFILE $_;
	    print OUTFILE &make_address;	
	    close OUTFILE;
	    $done{$file}++;
	    &cleanup;
	}
    }
}

sub make_index_segment {
    local($title,$file)= @_ ;
    $title =~ s/<[^>]*>//go;
    $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)) {
	$title =~ s/<[^>]*>//g;	# Sanitize section headings;
	$title = &get_first_words($title, $WORDS_IN_NAVIGATION_PANEL_TITLES);
	return (&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 (&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 (&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 (&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)) ? &make_href($file, $icon) : undef)
}

sub remove_markers {
    &remove_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
    &remove_sensitive_markers;
}

sub remove_general_markers {
    s/$lof_mark/<UL>$figure_captions<\/UL>/o;
    s/$lot_mark/<UL>$table_captions<\/UL>/o;
    if (defined &replace_citations_hook) {&replace_citations_hook;}
    else {&replace_citations if /$bbl_mark/;}
    if (defined &add_toc_hook) {&add_toc_hook;}
    else {&add_toc if (/$toc_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_references if /$cross_ref_mark/;}
    if (defined &replace_external_references_hook) {&replace_external_references_hook;}
    else {&replace_external_references if /$external_ref_mark/;}
    if (defined &replace_cite_references_hook) {&replace_cite_references_hook;}
    else { &replace_cite_references if /$cite_mark/; }
    if (defined &replace_user_references) {
 	&replace_user_references if /$user_ref_mark/;
    }
}  

sub remove_sensitive_markers {
    if (defined &replace_images_hook) {&replace_images_hook;}
    else {&replace_images if /$image_mark/;}
    if (defined &replace_icons_hook) {&replace_icons_hook;}
    else {&replace_icons if /$icon_mark_rx/;}
    if (defined &replace_verbatim_hook) {&replace_verbatim_hook;}
    else {&replace_verbatim if /$verbatim_mark/;}
    if (defined &replace_verb_hook) {&replace_verb_hook;}
    else {&replace_verb if /$verb_mark/;}
    s/;SPM/\&/go;
    s/$percent_mark/%/go;
    #JKR: Turn encoded ~ back to normal
    s/&#126;/~/go;
}

# This code is extremely inefficient. At least the subtrees should be
# filtered according to $MAX_LINK_DEPTH before going into the 
# inner loops. 
sub add_child_links {
    local($exclude, $depth, $current_key, @keys) = @_;
    local($_, $child_rx, @subtree, $next, %open);
    $child_rx = $current_key;
    $child_rx =~ s/( 0)*$//;	# Remove trailing 0's
    local($first)="<A NAME=\"CHILD_LINKS\">$anchor_mark<\/A>"; #RRM:  add a NAME tag
    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);
	@subtree = sort numerically @subtree;
	foreach $next (@subtree) {
	    ($next_depth, $file, $title, $star) = split($delim,$toc_section_info{$next});
	    next if ($star);
	    $file = "" if (!$MAX_SPLIT_DEPTH); # Martin Wilck
	    next if ($exclude eq $title);
	    $file = join('', $file, "#SECTION", split(' ', $next));
	    if (($next_depth > $depth) &&
		((split('/',%open))[0] < $MAX_LINK_DEPTH) &&
		($next_depth < $MAX_SPLIT_DEPTH))
	    {
		$depth = $next_depth;
		$open{$depth}++; 
		$_ .= "<UL>$first\n";
		$_ .= "<LI> ". &make_href($file,$title) . "\n";
		$first = '';	#RRM:  include NAME tag first time only
	    }
            elsif (($next_depth <= $depth) && ((split('/',%open))[0]
					       <= $MAX_LINK_DEPTH)) {
		while (($next_depth < $depth) && %open ) { # Backing out
		    if ($open{$depth}) {
			if (! $open{$next_depth})  {
			    $open{$next_depth}++;
			}
			else {
			    $_ .= "</UL> \n";
			}
			delete $open{$depth};};
		    $depth--;}
		$depth = $next_depth;
		$_ .= "<LI> ". &make_href($file,$title) . "\n";
	    }
	}
	$_ .= "</UL>\n" x (split('/',%open))[0];
    }
    $_;
}

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;
    "<A NAME=\"tex2html$name\" HREF=\"$link\">$text</A>";
}

sub make_named_href {
    local($name, $link, $text) = @_;
    $text =~ s/<A .*><\/A>//go;
    if (!($name)) {"<A HREF=\"$link\">$text</A>";}
    else {"<A NAME=\"$name\" HREF=\"$link\">$text</A>";}
}

sub make_section_heading {
    local($text, $level) = @_;
    local($section_tag) = join('', @curr_sec_id);
    join('',"<$level>","<A NAME=\"SECTION$section_tag\">$text</A><\/$level>\n");
}

sub extract_captions {
    # Uses and modifies $contents 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, $number, @tmp, $_);
    # associate the br_id of the caption with the argument of the caption
    $contents =~ s/$caption_rx/do {
	$optional_captions{$8} = ($2 ? $2 : $9);
	$captions{$8} = $9; ''}/ego;
    $after = join("","<P>",$after);
    $before .= "<P>";
    #JKR: Replaced "Figure" and "Table" with variables (see latex2html.config too).
    if ($env eq 'figure') {
	$type = $fig_name;
	$list = "\$figure_captions";
    }
    elsif ($env eq 'table') {
	$type = $tab_name;
	$list = "\$table_captions";
    }

    $captions = "";
    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 .= "<LI>" .
	    &make_href("$CURRENT_FILE#$key", $optional_caption) . "\n";
	$before .= "<A NAME=\"$key\">$anchor_mark</A>";
	$_ = ($optional_caption || $caption);
	&text_cleanup;
	$_ = &encode_title($_);
	s/&nbsp;//g;            # HWS - LaTeX changes ~ in its .aux files
	@tmp = split(/$;/, eval ("\$encoded_$env" . "_number{\$_}"));
	$number = shift(@tmp);
	$number = "" if ($number eq "-1");
	&write_warnings(qq|No number for "$_"\n|) if (! $number);
	eval("\$encoded_$env" . "_number{\$_} = join(\$;, \@tmp)");
	undef $_;
	undef @tmp;

	$captions = join("", "<STRONG>$type",
			 ($number ? " $number:" : ":"),
			 "</STRONG> $caption",
			 ($captions ? "<BR>\n" : "" ), $captions);
    }
    eval "$list .= \$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);
    $context =~ s/$labels_rx/do {
	$label = &do_labels_helper($2);
	$new_context = &anchor_label($label,$CURRENT_FILE,$new_context);""}/geo;
    $new_context;
}

# This should be done inside the substitution but it doesn't work ...
sub do_labels_helper {
    local($_) = @_;
    s/\W//g;
    $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,$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\" HREF=\"$link\">";
}


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 .= "<DL COMPACT>\n";
	    $level++;
	} 
	while ($count < $level) {
	    $index .= "</DL>\n";
	    $level--;
	} 
	foreach $term (@current[$count .. $#current-1]) {
	    # need to "step in" a little
#	    $index .= "<DT>" . $term . "\n<DL COMPACT>\n";
	    $index .= "<DT><strong>" . $term . "</strong>\n<DD><DL COMPACT>\n";
	    $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 .= "</DL>\n";
	$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";
}

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//;
    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($_) = "<DL> $footnotes <\/DL>\n";
    print "\nDoing footnotes ...";
    &remove_markers;
    &text_cleanup;
    if ($footfile) {
	&make_file($footfile, "Footnotes"); # Modifies $_;
	$_ = ""
	}
    $_;
}

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

sub replace_verbatim {
    # Modifies $_
    s/$verbatim_mark(verbatim)(\d+)/<PRE>$verbatim{$2}<\/PRE>/go;
    s/$verbatim_mark(rawhtml)(\d+)/$verbatim{$2}/ego; # Raw HTML
}
sub replace_verb {
    # Modifies $_
    s/$verb_mark(\d+)$verb_mark/<code>$verb{$1}<\/code>/go;
}

# This is used by revert_to_raw_tex
sub revert_verbatim {
    # 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 {
    # Modifies $_
    s/$verb_mark(\d+)$verb_mark/\\verb*$verb_delim{$1}$verb{$1}$verb_delim{$1}/go;
}

sub replace_cross_references {
    # Modifies $_
    local($label,$id,$ref_label);
    s/$cross_ref_mark#(\w+)#(\w+)>$cross_ref_mark/
	do {($label,$id) = ($1,$2);
	    $ref_label = $external_labels{$label} unless
		($ref_label = $ref_files{$label});
	    '"' . "$ref_label#$label" . '">' .
		&get_ref_mark($label,$id)}/geo;
    # This is for pagerefs which cannot have symbolic labels
    s/$cross_ref_mark#(\w+)#\w+>/
	do {$label = $1; $ref_label = $external_labels{$label} unless
		($ref_label = $ref_files{$label});
	    '"' . "$ref_files{$label}#$label" . '">'}/geo;
}

sub replace_external_references {
    # Modifies $_
    local($label);
    s/$external_ref_mark#(\w+)#(\w+)>$external_ref_mark/
	do {($label,$id) = ($1,$2); '"'. "$external_labels{$label}#$label" .
		'">' . &get_ref_mark("userdefined$label",$id)}/geo;
}

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_citations {
    # Modifies $_
    s/$bbl_mark#([^#]+)#/$citations{$1}/go;
}

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

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

sub replace_cite_references {
    local($key,$label);
    # Modifies $_
    # Uses $citefile set by the thebibliography environment
    s/#([^#]+)#$cite_mark#([^#]+)#/&make_named_href('',
	"$citefile{$2}#$1","$cite_info{$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 text_cleanup {
    s/(\s*\n){2,}/\n<P>\n/go;	# Replace consecutive blank lines with a paragraph tag
    s/$O\d+$C//go;		# Get rid of bracket id's
    s/$OP\d+$CP//go;		# Get rid of processed bracket id's
    s/(<!)?--?(>)?/($mode)||$1||$2?"$1--$2":"-"/ge;
    s/\\( |$)/ /go;		# Spacing commands
    #JKR: There should be no more comments in the source now.
    #s/([^\\]?)%/$1/go;        # Remove the comment character
    s/\\,/ /go;	# Cannot treat \, as a command because , is a delimiter ...
    # Replace tilde's with non-breaking spaces
    s/~/&nbsp;/g;
}

# 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[^$delimiters]*//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]    
   [-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] 
   [-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]
   [-contents toc_url]
   [-external_file external.aux_file]
   [-info string] 
   [-reuse reuse_option]
   [-no_reuse]
   [-no_images]
   [-images_only]
   [-show_section_numbers] 
   [-init_file Perl_file] 
   [-html_version (2.0 | 2.1 | 2.2 | 3.0 | 3.1)]
   [-short_index]
   [-debug]
   [-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);
    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;});
    $MAX_SPLIT_DEPTH = $MAX_SPLIT_DEPTH + $level;
    %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("$_/${PREFIX}images.pl") if (-f "$_/${PREFIX}images.pl");
    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, $path);
    if (-d $file) {		# $file is a directory
	$path = &make_directory_absolute($file);
	$file = '';
    }
    elsif ($file =~ /\//) {
	@path = split(m;/;,$file);
	$file = pop(@path);
	$path = join('/',@path);
	chdir $path;
	$path = &make_directory_absolute($path);
    }
    elsif (-f $file) {		# $file is a plain file
	$path = &getcwd;
    }
    ($path, $file);
}


# 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/$file" unless ($file =~ /\//); # name begins with a /
    $file;
}

# This should not have been necessary if tokenization was done properly ...
# This is the OLD version of tokenize.   It suffers from the problems
# contained therein.  It will fail for constructs like
# \newcommand{\x}{X}
# \newcommand{\y}{Y}
# \newcommand{\z}{Z}
# foo \x\y\z bar
#

sub tokenize {
    local($rx) = @_;
    # Modifies $_;
    if ($rx) {
        # The repetition here is a massive hack to get around the problem
        # I can't get the substitution to work on a string like
        # "\me\myhost\me\myhost\me\myhost\me\myhost\me\myhost"
	# in one go (where both me and myhost are in $rx)
	s/$rx$delimiter_rx/\\$1 $2/g;
	s/$rx$delimiter_rx/\\$1 $2/g;
        s/$rx([ ]*)\$/\\$1 $2\$/g; # ALO
        s/$rx([ ]*)\$/\\$1 $2\$/g;
    }
}

#
#  This simpler version apparently overcomes this difficulty.
#  However, some users have reported that it introduces bugs in
#  the use of &, which I have not been able to reproduce.
#  It is thus commented out for the the Rev e release.  - HWS
#  (Note:  At this point, all &'s should be replaced by ;SPMamp;)
#
#sub tokenize {
#    local($rx) = @_;
#    if ($rx) {
#	while (/$rx$delimiter_rx/) {
#	    s/$rx/&$1  $2/;
#	    }
#	s/&/\\/g;
#	}
#    }

# 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/(\W)/\\$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($_) = @_;
    &do_AtBeginDocument;
    $ref_before = '';
    $_;
}

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

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

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

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

sub make_name {
    local($sec_name, $packed_curr_sec_id) = @_;
    # 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), ".html");
}

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/$&/\}/;
    }
    # The same for processed markers ...
    while (s/$OP\s*\d+\s*$CP/\{/o) {
	s/$&/\}/;
    }
    # Replace any verbatim markers ...
    &revert_verbatim;
    &revert_verb;
    s/$tex2html_wrap_rx//go;
    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);
    $_;
}

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_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);
    &extract_pure_text("strict");
    while (/\s(\S+)\s*$/g) {
	$word = $1;}
    $word;
}

sub get_first_words {
    # Returns the first word 
    local($_, $min) = @_;
    local($words,$i);
    &extract_pure_text("liberal");
    s/#tex2html_wrap\w*#//g;
    foreach (/[\S]*\s*/g) {
	$words .= $_; 
	last if (++$i >= $min);
    }
    $words =~ s/\s*[,]\s*//;
    $words;
}

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/geo;
}

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

    if (! ($NO_IMAGES | $PS_IMAGES)) {
	$image_size{$url} = &get_image_size($url);
	$url = &find_unique($url);
    }

    $urlgif = $url;
    $urlgif =~ s/\.gif$/.html/ if ($map);
    if ($altst =~ s/align\s*=\s*([^ ]+)//io) {
	$align = $1;
    }
    $aalign = "BOTTOM";
    $aalign = "MIDDLE" if ($name =~ /inline/ && $depth{$name} != 0);
    $aalign = "\U$align" if $align;
    $ausemp = "\UUSEMAP=$usemap" if $usemap;
    if ($thumbnail) {
	if ( $image_size{$thumbnail} = &get_image_size($thumbnail) ) {
	    $thumbnail = &find_unique($thumbnail);
	    $extern_image_mark = "<IMG " . $image_size{$thumbnail} .
		" ALIGN=$aalign $altst SRC=\"$thumbnail\">";
	}
    } else {
	$extern_image_mark = &extern_image_mark($type);
    }
    if ($external || $thumbnail || $EXTERNAL_IMAGES) {
	if ( $extern_image_mark ) {
	    $result = &make_href($urlgif, $extern_image_mark);
	    &save_image_map($url, $urlgif, $map, $name, $altst, $ausemp) if $map;
	}
    }
    else {
	if ($map) {
	    $anch1 = "<A HREF=\"$map\">\n";
	    $anch2 = "</A>";
	}
	if ($aalign eq "CENTER" && $NETSCAPE_HTML) {
	    $anch1 .= "<P ALIGN=CENTER>";
	    $anch2 .= "</P>";
	}

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

sub get_image_size {
    local($imagefile) = @_;
    local($width,$height,$magic) = (0,0,0);
    local($buffer,$size) = ('','');
    if ( open(IMAGE, $imagefile) ) {
	read(IMAGE,$buffer,10);
	close(IMAGE);
	($magic,$width,$height) = unpack('a6vv',$buffer);
	if ( ($magic eq "GIF87a" || $magic eq "GIF89a") 
	    && $width * $height > 0 ) {
	    $size = "WIDTH=$width HEIGHT=$height";
	};
    }
    $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 GIF to a temporary one.
#  Then try to link the new name to the old GIF.
#  If the link fails, restore the temporary GIF.
#
	    if ( $result ) {
		local($tmp) = 'temporary.gif';
		unlink($tmp);
		rename($image1, $tmp);
		if (link($image2, $image1)) {unlink($tmp);}
		else {rename($tmp, $image1)};
		return $image1;
	    }
	}
    }
    $image1;
}

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

#  Subroutine used mainly to rename an old GIF 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\.gif/$to_prefix.gif/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.gif");
    $to;
}

sub save_captions_in_file {
    local ($type, $_) = @_;
    if ($_) {
	&remove_markers;
	&add_dir_to_href if ($DESTDIR);
	open(CAPTIONS, ">${PREFIX}$type.pl");
	print CAPTIONS $_;
	close (CAPTIONS);
    }
}

sub add_dir_to_href {
    s/HREF=\"/HREF=\"..\/$DESTDIR\//o;
#    $_ =~ s/<LI><A NAME\=\"tex2html(\s*)\" HREF=\"/
#	\'<LI><A NAME\=\"tex2html$1\" HREF=\"\'.\$dir.\'/og;
#    $_ .= "\'";
}

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) {
	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 "\$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_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);
		    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) {
			    $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 =~ /(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);
    } 
}

# 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" ) {
	$character_map=$charset;
	$character_map =~ tr/-/_/;
	eval "\$${character_map}_character_map\{\"$char$kind\"\}";
    } else {
	$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};
	do {
	    # Replace the argument markers with appropriate patterns
	    grep(($_ = do { s/ //;
			    if (/{}/) {
				's/$next_pair_pr_rx//o || print "\nCannot find argument for $cmd!\n";';}
			    elsif (/[[][]]/) {
				'&get_next_optional_argument;';}
			    else { # We have arbitrary code - just add it
				"$_".';';} # 
			})
		 ,@args);
	    # Generate a new subroutine
	    eval "sub do_cmd_$cmd {" . 'local($_) = @_;'  . join('',@args) . '$_}';
	} if (@args);
    }
}

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;
}
  
sub process_in_latex_helper {
    local($cmd) = @_;
    ($ASCII_MODE ? "[$cmd]" : &process_in_latex("\\$cmd"))
}
	
# *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);
    while ( ($key, $val) = each %declarations) {
	if ($val) {
	    $val =~ m|</.*$|;
	    eval "sub do_env_$key {" 
		. 'local($_) = @_;' 
		    . "\"$` " . '$_'  . "$&\"};";
	}
    }
}

# *Generates* subroutines to handle each of the sectioning commands.
sub generate_sectioning_subs {
    local($key, $val);
    while ( ($key, $val) = each %section_headings) {
	eval "sub do_cmd_$key {" 
	    . 'local($after) = @_;'
		. '&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 . ');}';
    }
}

# Uses $after which is defined in the caller (the caller is a generated subroutine)
# Also uses @curr_sec_id
sub do_cmd_section_helper {
    local($H,$key) = @_;
    local($section_number, $anchor, @tmp, $optional);
    # if we have a $key the current section is not of the *-form, so we need
    # to update the counters.
    do {
	$latex_body .= "\\stepcounter{$key}\n";
    } if $key;
    local($_) = $after; 
    $optional = &get_next_optional_argument; $after = $_;
    $after =~ s/$next_pair_rx/do {$TITLE = $2; ''}/eo;
    $TITLE =~ s/\\(label|index)$any_next_pair_rx//;
    $TITLE = &translate_commands(&translate_environments($TITLE));
    $_ = $TITLE;  &extract_pure_text("liberal");
    $anchor = s/(.*)(<A.*><\/A>)(.*)/$1$3/ ? $2 : "";
    $TITLE = $_ ;
    # This is the LaTeX section number read from the $FILE.aux file
    @tmp = split(/$;/,$encoded_section_number{&encode_title($_)});
    $section_number = shift(@tmp);
    $section_number = "" if ($section_number eq "-1");
    $encoded_section_number{&encode_title($_)} = join($;, @tmp);
    #JKR: Don't prepend whitespace 
    $TITLE = "$section_number " . $_ if $section_number;
    $after = join('', $anchor, &make_section_heading($TITLE, $H), $after);
    $TITLE =~ s/<P>//g;		# Remove newlines
    $after;
}
    
sub do_cmd_usepackage {
    local($_) = @_;
    local($option);
    local ($options,$dum)=&get_next_optional_argument;
    s/$next_pair_pr_rx//o;
    local($package_name)=$2;
    local($rest) = $_;
    &do_package_options($package_name,$options);
    $rest;
}

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

    &do_package_options($file,$options)
	if ($options && $styles_loaded{$file});
    $_ = $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"})) {
		print STDERR "No implementation found for option ",
		"\`$option\' of \`${package}\' package\n";
	    }
	}
    }
    $rest;
}

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

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 ################################

# 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($_) = @_;
    #RRM - catch nested lists
    $_ = &translate_environments($_);
    if (/^\s*$item_description_rx/) { # Contains user defined optional labels
	&do_env_description($_, "COMPACT")}
    else {
	&list_helper($_,'OL');
    }
}

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/<DT>$1\n<DD>/g;
    } else {
	s/$item_description_rx/<DT><STRONG>$1<\/STRONG>\n<DD>/g;
    }
    # and just in case the description is empty ...
    s/\\item$delimiter_rx/<DT><DD>$1/g;
    $* = 0;			# Multiline matching OFF
    "<DL $compact>$_\n</DL>";
}

sub list_helper {
    local($_, $tag) = @_;
    $* = 1;			# Multiline matching ON
    # This deals with \item[xxx] ...
    s/$item_description_rx/<DT>$1\n<DD>/g;
    s/\s*\\item/\\item/g;
    s/\\item$delimiter_rx/\n<LI>$1/g;
    $* = 0;			# Multiline matching OFF
    "<$tag>$_</$tag>";
}

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;
    $_;
}

sub do_env_thebibliography {
    # Sets $citefile and $citations defined in translate
    local($_) = @_;
    $bibitem_counter = 0;
    $citefile = $CURRENT_FILE;
    $citefile{$bbl_nr} = $citefile;
    s/$next_pair_rx//o;
    $* = 1;			# Multiline matching ON
    s/^\s*$//g;			# Remove empty lines (otherwise will have paragraphs!)
    $* = 0;			# Multiline matching OFF
    $citations = join('',"<DL COMPACT>",
		      &translate_commands(&translate_environments($_)),"</DL>");
    $citations{$bbl_nr} = $citations;
    $_ = join('','<P>' , "<A NAME=\"SECTIONREF\"><H2>$bib_title</H2></A><P>\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_tabbing {
    local($_) = @_;
    &tabbing_helper($_);
}

sub tabbing_helper {
    local($_) = @_;
    $* = 1;			# Multiline matching ON
    s/^.*\\kill[ \t]*$//;
    s/\\\&gt;//go;
    s/([^\\])\\[>]/$1\t\t/go;
    s/\\>/\t\t/go;
    s/\\\\/\n/og;
    $* = 0;			# Multiline matching OFF
    "<PRE><TT> $_ \n</TT></PRE>";
}

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

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

# Do nothing (avoid the paragraph breaks)
sub post_latex_do_env_figure {
    $_[0];
}
sub post_latex_do_env_table {
    $_[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,$_);
    }
    $_;
}

sub do_cmd_title {
    local($_) = @_;
    &get_next_optional_argument;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    $_ = $&; &extract_pure_text("liberal");
    s/(.*)(<A.*><\/A>)(.*)/$1$3/; # HWS:  Remove embedded anchors
    ($t_title) = $_;
    $TITLE = $t_title if ($TITLE eq $default_title);
    $TITLE =~ s/<P>//g;		# Remove Newlines
    $TITLE =~ s/\s+/ /g;	# meh - remove empty lines 
    $rest;
}

sub do_cmd_author {
    local($_) = @_;
    s/$next_pair_pr_rx//o;
    ($t_author) = $&;
    $_;
}
sub do_cmd_date {
    local($_) = @_;
    s/$next_pair_pr_rx//o;
    ($t_date) = $&;
    $_;
}

sub do_cmd_maketitle {
    join('', "<H1 ALIGN=CENTER>$t_title</H1>\n",
	 "<P ALIGN=CENTER><STRONG>$t_author</STRONG></P><P>\n",
	 do {"<P ALIGN=CENTER><STRONG>$t_date</STRONG></P><P>\n" if $t_date;}
	 , $_[0]);
}

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

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

sub do_cmd_today {
    #JKR: Make it more similar to LaTeX
    ## AYS: moved french-case to styles/french.perl
    local($today) = (`date "+%m:%d, 20%y"`);
    $today =~ s/(\d{1,2}):0?/$Month[$1] /o;
    $today =~ s/20([7|8|9]\d{1})/19$1/o;
    join('',$today,$_[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 {
    join('',"<BR>", $_[0]);
}

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

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

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

sub do_cmd__at_ {
    $_[0];
    }

sub do_cmd_makeatletter {
    $single_cmd_rx = $single_cmd_atletter_rx;
    $_[0];
}
sub do_cmd_makeatother {
    $single_cmd_rx = $single_cmd_atother_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.
#
# 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($_) = @_;
    foreach (/.*\n?/g) {

	chop;
	# For each line

	local($cmd, @args) = split('#',$_);
	next unless $cmd;

	$cmd =~ s/ //g;

	# Build routine body ...

	local ($body, $code) = ("", "");
	foreach (@args) {
	    if (/\{\}/) {
		$body .= '$args .= "$`$&" if s/$next_pair_rx//o;' . "\n"
		} elsif (/\[\]/) {
		    $body .= '($dummy, $pat) = &get_next_optional_argument;' .
			"\n". '$args .= $pat;';
		} else {
		    $body .= $_ . "\n";
		}
	}

	# Generate a new subroutine
	$code = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";' . "\n"
	        .$body . "\n"
		    .'(&make_wrapper(1) . $cmd.$args . &make_wrapper(0), $_)}'
			."\n";
	eval $code;

	# And make sure the main loop will catch it ...

	$raw_arg_cmds{$cmd} = 1;
    }
}

sub process_commands_nowrap_in_tex {
    local($_) = @_;
    foreach (/.*\n?/g) {
	chop;
	local($cmd, @args) = split('#',$_);
	next unless $cmd;
	$cmd =~ s/ //g;
	# Build routine body ...
	local ($body, $code) = ("", "");
	foreach (@args) {
	    if (/\{\}/) {
		$body .= '$args .= "$`$&" if s/$next_pair_rx//o;' . "\n";
	    } elsif (/\[\]/) {
		$body .= '($dummy, $pat) = &get_next_optional_argument;' .
		    "\n". '$args .= $pat;';
	    } elsif (/<<\s*/) {
		$_ = $';
		if (/\s*>>/) {
		    $body .= '$args .= "$`$&" if (s/\\\\'.$`.'//o);' . "\n"
			. "\$_ = \$\';\n"
			} else { $body .= $_ . "\n"; }
	    } else { $body .= $_ . "\n"; }
	}
	# Generate a new subroutine
	$code = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args, $dummy, $pat) = "";'."\n"
		.$body . "\n"
		    .'(&make_nowrapper(1).$cmd.$args.&make_nowrapper(0),$_)}'
			."\n";
	eval $code;
	# 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) = ("", "");
	foreach (@args) {
	    if (/\{\}/) { 
		$body .= '$args .= "$`$&" if s/$any_next_pair_rx//o;' . "\n"
		    . "\$_ = \$\';\n";
	    } elsif (/\[\]/) {
		$body .= '($dummy, $pat) = &get_next_optional_argument;' .
		    "\n". '$args .= $pat;';
	    } elsif (/<<\s*/) {
		$_ = $';
		if (/\s*>>/) {
		    $body .= '$args .= "$`$&" if (s/\\\\'.$`.'//o);' . "\n"
			. "\$_ = \$\';\n"
			} else { $body .= $_ . "\n"; }
	    } else { $body .= $_ . "\n"; }
	}
	# Generate a new subroutine
	$code = "sub wrap_cmd_$cmd {" . "\n"
	    .'local($cmd, $_) = @_; local ($args) = "";' . "\n"
		.$body . "\n" 
		    .'(&make_inline_wrapper(1).$cmd.$args.&make_inline_wrapper(0),$_)}'
			."\n";
	eval $code;
	$raw_arg_cmds{$cmd} = 1;
    }
}

# Used in initialisation to build a regexp ...

sub get_raw_arg_cmds {
    local($_, $key);
    foreach $cmd (keys %raw_arg_cmds) {
	$_ .= $cmd . "|";
    }
    chop;			# Remove the last "|".
    $_;
}

# Invoked before actual translation; wraps these commands in
# tex2html_wrap environments, so that they are properly passed to
# TeX in &translate_environments ...

sub wrap_raw_arg_cmds {
    local ($processed_text, $delim, $cmd, $wrapper, $wrap, $star, $after);
    while (/$raw_arg_cmd_rx/) {
	$processed_text .= $`;
	$after = $';
	($cmd, $star, $delim) = ($1, $2, $3);
	$wrapper = "wrap_cmd_$cmd$star";
	$wrapper =~ s/\*/star/;
	($wrap, $_) = &$wrapper ("\\$cmd$star", "$delim$after");
	$processed_text .= $wrap;
    }
    $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('', '<P>', &make_section_heading($toc_title, "H2"), $toc_mark, $_);
}
sub do_cmd_listoffigures {
    local($_) = @_;
    $TITLE = $lof_title;
    $loffile = $CURRENT_FILE;
    join('', '<P>' , &make_section_heading($lof_title, "H2"), $lof_mark, $_);
}  
sub do_cmd_listoftables {
    local($_) = @_;
    $TITLE = $lot_title;
    $lotfile = $CURRENT_FILE;
    join('', '<P>' , &make_section_heading($lot_title, "H2"), $lot_mark, $_);
}

# $idx_mark will be replaced with the real index at the end
sub do_cmd_textohtmlindex {
    local($_) = @_;
    $TITLE = $idx_title;
    $idxfile = $CURRENT_FILE;
#RRM
    if (%index_labels) { &make_index_labels(); }
    if (($SHORT_INDEX) && (%index_segment)) { &make_preindex(); } 
    else { $preindex = ''; }
#
    join('','<P>' , &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);
    $preindex = "<HR><H4>Legend:</H4><DL COMPACT>";
    @keys = keys %index_segment;
    foreach $key (@keys) { 
	local($tmp) = "segment$key"; 
	$tmp = $ref_files{$tmp};
	$preindex .= "<DT>$key<DD>".&make_named_href('',
		$tmp."\#CHILD\_LINKS",$index_segment{$key})."\n";
    }
    $preindex .= "</DL>";
}

sub do_cmd_footnote {
    local($_) = @_;
    s/$next_pair_pr_rx//o;
    local($br_id, $footnote) = ($1, $2);
    &process_footnote($footnote);
    join('',&make_href("$footfile#$br_id",$footnote_mark),$_);
}

sub do_cmd_thanks {
    &do_cmd_footnote(@_);
}

sub do_cmd_footnotemark {
    local($_) = @_;
    &get_next_optional_argument;
    # Don't use ()'s for the optional argument!
    s/\\footnotetext\[?[^]]*\]?\s*$any_next_pair_pr_rx//o;
    local($br_id, $footnote) = ($1, $2);
    if ($footnote) {
	&process_footnote($footnote);
	$_ = join('',&make_href("$footfile#$br_id",$footnote_mark),$_);}
    else { print "\nCannot find \\footnotetext";};
    $_;
}

# 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($_) = @_;
    s/$next_pair_pr_rx//o;
    local($br_id, $footnote) = ($1, $2);
    &process_footnote($footnote) if $footnote;
    $_;
}
    
sub process_footnote {
    # Uses $before 
    # Sets $footfile defined in translate
    # Modifies $footnotes defined in translate
    local($footnote) = @_;
    local($last_word) = &get_last_word($ref_before);
    local($space) = "\n";
    if (! $NO_FOOTNODE) {	
        $footfile = "${PREFIX}footnode.html";
	$space = ".\n" x 30;
	## $space = "<P>\n" x 30;
    }
    $footnotes .= "<DT><A NAME=\"$br_id\">...$last_word</A><DD>" .
	&translate_commands($footnote) . "\n<PRE>" . $space . "</PRE>";
}

# 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) = @_;
    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/\W//g;	# Remove non alphanumeric characters
	$symbolic_labels{"$label$id"} = $use_label;
	# The quotes around the HREF are inserted later
	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($_) = @_;
    s/$next_pair_pr_rx//o;
    local($label) = $2;
    &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/\W//g;		# Remove 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; }
    join('',"<A NAME=\"$label\">$anchor_mark</A>",$context);
}

# This just creates a link from a label (yet to be determined) to the 
# cite_key in the citation file.
sub do_cmd_cite {
    local($_) = @_;
    local($cite_key, @cite_keys, $label);
    local($optional_text,$dummy) =  &get_next_optional_argument;
    $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) {
	@cite_keys = (split(/,/,$cite_key));
	# RRM:  if the URL and printable-key are known already, then use them...
	$label = "$cite_key"; $label =~ s/\W//g;
	if ( ($SEGMENT) && ($cite_info{$cite_key}) && ($ref_files{"cite_$cite_key"}) ) {
	    $_ = join('', "[",
		&make_named_href($label,$ref_files{'cite_'."$cite_key"},$cite_info{$cite_key})
		,"]", $_);
	} else {
	    #Replace the keys...
	    grep(do { $_ = "#$_#$cite_mark#$bbl_nr#";}, @cite_keys);
	    $optional_text = ", $optional_text" if $optional_text;
	    #JKR: Need space after "," at citations
	    $_ = join('', "[", join(', ',@cite_keys),  $optional_text, "]", $_);
	}
    }
    else {print "Cannot find citation argument\n";}
    $_;
}

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//o;
 	$label =~ s/\\citeauthoryear//o;
    }
    s/$next_pair_pr_rx//o;
    local($cite_key) = $2;
    $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('',"<DT><A NAME=\"$cite_key\"><STRONG>$label</STRONG></A><DD>", $_);
    } 
    else {
	print "Cannot find bibitem labels: $label\n";
	join('',"<DT><STRONG>$label</STRONG><DD>", $_);	# AFEB added this line
    }
}

# 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('',"<H2>No References!</H2>\n", $after);
	    }
	}
    } if $bibfile;
    $after;
}

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

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

sub do_cmd_caption {
    local($_) = @_;
    local($caption);
    s/$next_pair_pr_rx//o;
    $caption = $2;
    join('',"<BR>$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/\W//g;		# Remove non alphanumeric characters
    $latex_labels{$label} = $val;
    $_;
}
#
# Sets %encoded_(section|figure|table)_number, which maps encoded
# section titles to LaTeX numbers
#
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;
	$_ = $arg; &text_cleanup;
	$title = &encode_title($_);
	eval "\$encoded_${hash}_number{\$title} = \$number";
#	print "\$encoded_${hash}_number{$title} = \"" ,
#	    eval ("\$encoded_${hash}_number{\$title}") , "\"\n\n";
    }
    $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 #################################
sub do_cmd_newcounter {
    local($_) = @_;
    local($ctr,$dummy,$pat);
    s/$next_pair_rx/$ctr=$2;''/eo;
    ($dummy,$pat) = &get_next_optional_argument;
    $latex_body .= &revert_to_raw_tex("\\newcounter{$ctr}$pat\n")
	unless ($preamble =~ /\\newcounter{$ctr}/);
    $_;
}

sub do_cmd_addtocounter {
    local($_) = @_;
    local($ctr,$num);
    s/$next_pair_rx/$ctr = $2;''/eo;
    s/$next_pair_rx/$num = $2;''/eo;
    $latex_body .= &revert_to_raw_tex("\\addtocounter{$ctr}{$num}\n");
    $_;
}

sub do_cmd_setcounter {
    local($_) = @_;
    local($ctr,$num,$index);
    s/$next_pair_rx/$ctr = $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};
	$curr_sec_id[$index] = $num if $index;
	$global{'eqn_number'} = $num if ($ctr eq "equation");
    }
    $_;
}

sub do_cmd_stepcounter {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    if (! $AUX_FILE) {
	$latex_body .= &revert_to_raw_tex("\\stepcounter{$ctr}\n");
	$segment_sec_id[$index]  += 1 if ($index = $section_commands{$ctr});
	$global{'eqn_number'} += 1 if ($ctr eq "equation");
    }
    $_;
}

sub do_cmd_refstepcounter {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    $latex_body .= &revert_to_raw_tex("\\refstepcounter{$ctr}\n")
	if (! $AUX_FILE);
    $_;
}
   
sub do_cmd_arabic {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("arabic{$ctr}"),$_);
}
   
sub do_cmd_roman {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("roman{$ctr}"),$_);
}
   
sub do_cmd_Roman {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("Roman{$ctr}"),$_);
}
   
sub do_cmd_alph {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("alph{$ctr}"),$_);
}
   
sub do_cmd_Alph {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("Alph{$ctr}"),$_);
}
   
sub do_cmd_fnsymbol {
    local($_) = @_;
    local($ctr);
    s/$next_pair_rx/$ctr = $2;''/eo;
    join('',&process_in_latex_helper("fnsymbol{$ctr}"),$_);
}
   
sub do_cmd_thecounter {
    # Uses $counter bound by the caller
    join('',&process_in_latex_helper("the$counter"),@_);
}

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

sub do_cmd_LaTeX {
    join('','LaTeX', $_[0]);
}
sub do_cmd_LaTeXe {
    join('','LaTeX2e', $_[0]);
}
sub do_cmd_TeX {
    join('','TeX', $_[0]);
}

sub do_cmd_char {		#JKR: implementation of \char
    local($_) = @_;
    s/^\s*(\d*)\s*/&#\1;/;
    $_;
}

# 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}');" . '$_}';
    }
}

# 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) = @_;
    local($arg, $first_char);
    #print STDERR "\naccent_safe_for_ij: type: $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 "\n*** arg: |$arg|   first_char: |$first_char|\n";
    $_ = join('', (&iso_map($first_char, $type) || $first_char), $arg, $_);
}

# 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($_) = @_;
    s/\s*(\d+)\s*//o;
    local($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);
    $_;
}

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 address_data {
    local($user, $date, $_);
    # Get author email address and current date.
    ($user = (getpwuid ($<))[6]) =~ s/,.*//;
    $date = (`date`) || print "Can't get current date\n";
    chop($date);
    ($user, $date);
}

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

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

sub do_cmd_textbf {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<b>",$&,"<\/b>",$rest);
}

sub do_cmd_texttt {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<tt>",$&,"<\/tt>",$rest);
}

sub do_cmd_textit {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<i>",$&,"<\/i>",$rest);
}

sub do_cmd_textsl {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<i>",$&,"<\/i>",$rest);
}

sub do_cmd_textsc {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    local($match) = $&;
    $match =~ tr/a-z/A-Z/;
    join('',$match,$rest);
}

sub do_cmd_emph {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<em>",$&,"<\/em>",$rest);
}

sub do_cmd_textsf {
    local($_) = @_;
    local($rest) = $_;
    $rest =~ s/$next_pair_pr_rx//o;
    join('',"<i>",$&,"<\/i>",$rest);
}

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";
    $_;
}


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

sub initialise {
    ############################ Global variables ###############################
    $PREAMBLE = undef;		#1 if preamble translated, 0 if document body translated
    $NESTING_LEVEL = undef;	#counter for TeX group nesting level
    $OUT_NODE = 0;		# Used in making filenames of HTML nodes unique
    ($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

    ${AtBeginDocument_hook}  = "\$AtBeginDocument_hook\=\'\'; ";
    $cross_ref_mark = '<tex2html_cr_mark>';
    $external_ref_mark = '<tex2html_ext_cr_mark>';
    $cite_mark = '<tex2html_cite_mark>';
    $bbl_mark = '<tex2html_bbl_mark>';
    $toc_mark = '<tex2html_toc_mark>';
    $lof_mark = '<tex2html_lof_mark>';
    $lot_mark = '<tex2html_lot_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);

    # Require all necessary version specific files
    foreach ( sort <$LATEX2HTMLVERSIONS/html[1-9].[0-9].pl> ) {
	last if ( $_ gt "$LATEX2HTMLVERSIONS/html$HTML_VERSION.pl" );
	require $_;
    };

    %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
     );
    &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;');

    # 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');
		  
    # %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'
	 );
        
    # 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|cdm_<env|cmd> exists then it will be used.
    %ignore = ('sloppypar', 1,  'document', 1,  'mbox', 1,  'newblock', 1, 
	       ',', 1,  '@', 1, ' ', 1,  '-', 1,
               'sloppy', 1,
	       'hyphen', 1, 'titlepage', 1, 'htmlonly', 1,
	       'flushleft', 1, 'flushright', 1, 'slide', 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. mbox, center, etc)

&ignore_commands( <<_IGNORED_CMDS_);
addcontentsline # {} # {} # {}
addtocontents # {} # {}
addvspace # {} # &ignore_numeric_argument
and
and # \$_ = join(''," - ",\$_)
baselineskip # &ignore_numeric_argument
bibdata
bibliographystyle # {}
bibstyle # {}
center
citation 
citeauthoryear
clearpage
contentsline 
documentclass # [] # {}
documentstyle # [] # {}
end # {}
enlargethispage # {}
evensidemargin # &ignore_numeric_argument
fill
flushbottom 
footheight # &ignore_numeric_argument
footskip  # &ignore_numeric_argument
global
headheight # &ignore_numeric_argument 
headsep # &ignore_numeric_argument
hfill
hspace # {}# &ignore_numeric_argument 
hspacestar # {}# &ignore_numeric_argument
html
hyphenation # {}
ignorespaces 
indent
itemsep # &ignore_numeric_argument    
large
leftmargin # &ignore_numeric_argument
lower # &ignore_numeric_argument
makebox# []# []  
makeindex
marginpar # {}
marginparsep # &ignore_numeric_argument
marginparwidth # &ignore_numeric_argument
markboth # {} # {}
mbox
mdseries
newpage
nocite # {}
noindent
nolinebreak# []
nopagebreak #[]
normalmarginpar
numberline 
oddsidemargin # &ignore_numeric_argument
pagebreak# [] # \$_ = join('',"<P>",\$_)
pagenumbering #{}
pagestyle # {}
parindent # &ignore_numeric_argument
parskip # &ignore_numeric_argument
penalty # &ignore_numeric_argument
protect
raggedright
raise # &ignore_numeric_argument
relax 
reversemarginpar
rightmargin # &ignore_numeric_argument
rmfamily
rule # [] # {} # {}
samepage 
small
space 
startdocument # \$SEGMENT=1; \&do_AtBeginDocument; \$_
string 
suppressfloats # []
textheight # &ignore_numeric_argument
textwidth # &ignore_numeric_argument
thispagestyle # {}
tiny
topmargin # &ignore_numeric_argument
topskip # &ignore_numeric_argument
upshape
usebox # {}
vfil
vfill
vspace# {}# &ignore_numeric_argument
vspacestar # {}# &ignore_numeric_argument
_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 # [] # [] # {}
parbox # [] # {} # {}
dag
ddag
l
L
oe
OE
_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
iftrue
iffalse
psfigurepath # {}
pssilent
psdraft
psfull
thinlines
thicklines
linethickness # {}
DeclareMathAlphabet # {} # {} # {} # {} # {}
SetMathAlphabet # {} # {} # {} # {} # {} # {}
DeclareMathSizes # {} # {} # {} # {}
DeclareMathVersion # {}
DeclareSymbolFont # {} # {} # {} # {} # {}
DeclareSymbolFontAlphabet # {} # {}
DeclareMathSymbol # {} # {} # {} # {}
SetSymbolFont # {} # {} # {} # {} # {} # {}
DeclareFontShape # {} # {} # {} # {} # {} # {}
DeclareFontFamily # {} # {} # {}
DeclareFontEncoding # {} # {} # {}
DeclareFontSubstitution # {} # {} # {} # {}
mathversion # {}
newfont # {} # {}
normalfont
rmfamily
mdseries
newcounter # {}
setcounter # {}
addtocounter # {}
newlength # {}
setlength# {}# {}
addtolength# {}# {}
settowidth# {}# {}
settoheight# {}# {}
settodepth# {}# {}
newsavebox # {}
savebox # {} # [] # {} 
makebox # [] # [] # {} 
sbox # {} # {} 
setbox # {}
_RAW_ARG_NOWRAP_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;' , '\\.{}'
);
   
	
    ################### 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?
    $delimiters = '\'\\s[\\]\\\\<>(=).,#;:~\/!-';
    $delimiter_rx = "([$delimiters])";
    
    # $1 : br_id
    # $2 : <environment>
    $begin_env_rx = "[\\\\]begin\\s*$O(\\d+)$C\\s*([^$delimiters]+)\\s*$O\\1$C\\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";

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

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

    # $1 : image filename prefix
    $gif_rx = "(\\w*T?img\\d+)";
    
    # $1 : largest argument number
    $tex_def_arg_rx = "^[#0-9]*#([0-9])$O";

    # $1 : declaration or command or newline (\\)
    $cmd_delims = q|-#,.~/\'`^"=\$%&_{}@|; # Commands which are also delimiters!
    # The tex2html_dummy is an awful hack ....
    $single_cmd_atletter_rx = "\\\\([a-zA-Z\\\@]+\\*?|[$cmd_delims]|\\\\)"; 
    $single_cmd_atother_rx = "\\\\([a-zA-Z]+\\*?|[$cmd_delims]|\\\\)"; 
    $single_cmd_rx = $single_cmd_atletter_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";

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

    # 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";
    
    # 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 be revert_to_raw_tex
    $tex2html_wrap_rx = '[\\\\](begin|end)\s*{\s*tex2html_wrap[_a-z]*\s*}';

    $meta_cmd_rx = '[\\\\](providecommand|renewcommand|renewenvironment|newcommand|newenvironment|newtheorem)';

    # Matches counter commands - these are caught early and are appended to the 
    # file that is passed to latex.
    $counters_rx = "[\\\\](newcounter|addtocounter|setcounter|refstepcounter|stepcounter|arabic|roman|Roman|alph|Alph|fnsymbol)$delimiter_rx";

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

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

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

}

# 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";
}

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";
}
sub make_new_cmd_rx {
# RRM(nac) - allow new commands \W for non-alphanumerics, not already defined.
# old:    "[\\\\](". join("|", keys %new_command) . ")" 
    local($tmp) = join("|", keys %new_command);
    $tmp =~ s/(\W)/\\$1/g;
    $tmp =~ s/\\\|/\|/g;
    "[\\\\](". $tmp . ")" 
	if %new_command;
}

sub make_new_env_rx {
    local($where) = @_;
    $where = &escape_rx_chars($where);
    "[\\\\]$where\\s*$O(\\d+)$C\\s*(".
	join("|", keys %new_environment) . 
	    ")\\s*$O\\1$C\\s*"
		if %new_environment;
}

sub make_sections_rx {
    local($section_alts) = &get_current_sections;
    # $section_alts includes the *-forms of sectioning commands
    $sections_no_delim_rx = "\\\\($section_alts)";
    $sections_rx = "\\\\($section_alts)$delimiter_rx";
}

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";
}

sub make_raw_arg_cmd_rx {
    # $1 : commands to be processed in latex (with arguments untouched)
    $raw_arg_cmd_rx = "\\\\(" . &get_raw_arg_cmds . ")(\\*?)([$delimiters\@]+|\\\\|#|\$)";
    $raw_arg_cmd_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) = @_;
    # 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]    
   [-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] 
   [-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]
   [-contents toc_url]
   [-external_file external.aux_file]
   [-info string] 
   [-reuse reuse_option]
   [-no_reuse]
   [-no_images]
   [-images_only]
   [-show_section_numbers] 
   [-init_file Perl_file] 
   [-html_version (2.0 | 2.1 | 2.2 | 3.0 | 3.1)]
   [-short_index]
   [-debug]
   [-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 pstogif script uses the pstoppm.ps
postscript program originally written by Phillip Conrad (Perfect Byte, Inc.)
and modified by L.  Peter Deutsch (Aladdin Enterprises).  
cd
