#!/usr/bin/perl -w

#
# copyright notice: copyright 2006,2007,2008 David A Thompson
#

#
# statement of copying permission:
#
# this program is distributed under the terms of the GNU
# General Public License (or the Lesser GPL) and can be
# distributed under the GNU GPL version 2 or later

#
# otl uses a 'parameter' file to do a complex search/replace
# (see the README for additional documentation)
#

#
# outline of source code:
#
#	1. process command line
#	2. process parameter file
#	3. call PROCESS routine for each file specified on d
#		command line
#	4. PROCESS routine outline:
#		clean up file
#			strip whitespace, etc
#			deal w/problem characters
#		check for header
#		set title string
#	 	look for ## or ### lines
#		if HTML file, look for ampersands
#
#		tables
#
#		bracketed items
#		under items
#		if HTML file, process links (e.g., http://aaa.bbb.ccc.ddd)
#		triggered items (usu. list items)
#		process blank lines
#		add header
#		add footer
#		output file

#
# help debug...
use strict;

#
# modules to import

# external perl modules
use File::Find;			# for recursive directory descent
use List::Util;
# local modules
require DatPlUtils;

#
# declare local variables

my $version="0.51";	# program version

my $assumeyes=0;        # if 0, query user for everything; if 1, assume yes for all queries
my $avlength;		# argv array length
my $blank;
my $currentDirectory;

my $debug=0;		# debug mode (default off)
my $descend=0;		# descend mode on/off (default off)
my @dend=();
my @dirfiles=();	# holder for list of files in a dir
my $dlength;
my @dstart=();

my $endloop;

my $file;
my $filename;
my @filenames=();	# command-line filenames
my $fileout;
my @flag=();			# command-line flag arguments
my $flagl;
my @foot;
my $footpos;
my $fnl;
my @gend=();
my @gstart=();

my @headp;
my $headpos=();		# position in head array
my @help=();			# help message to print
my $i;			# count/index variable
my $j;			# count/index variable
my $k;			# count/index variable
my @look=();			# array with bracket items
my $looklength=0;		# length of @look

my $outputmode="";  # language in which data is output "latex" or "html"


my @newfilenames=();
my $nopretty=0;	# flag for calling otlsub (by default, call otlsub (nopretty = 0))

my $owheader;		# overwrite header?

my $paramdir;	# directory for .otl configuration files
my $paramfile;	# parameter file name
my $path;			# file system path
my $preequiv1;
my $preequiv2;
my @rend=();
my @rstart=();


my $smartmode=1;  # try and be smart regarding whether a problem character is part of output language or needs to be converted

my @stylearray;	# array holding CSS style from style file
my $stylefile=0;		# full pathname of CSS style file to use
my @stylefiles=();	# array holding names of CSS style files
my $stylefileslength=0;	# length of @stylefiles
my $stylefilenumber=0;
my $stylefilesuffix=".css";	# file type/suffix of $stylefile
my $stylequery=1;	# query for style (on by default)
# suffix is specified in the parameter file
# - string to be tacked on to end of output file
my $suffix;
my @t2h=();			# array containing parameter file
my $t2hlength;	# length of t2h

my @tlook=();			# array with triggered items items
my $tlooklength=0;	# length of @tlook
my @tstart=();
my @tend=();
my @ttend=();
my $ttlength;
my @ttstart=();
my @ttype=();

my $ulength;
my @ulook=();			# array with under items to look for
my @ustart=();
my @uend=();

my $x;			# dummy variables for counts, etc.

my $zz;			# dummy variable

########################
#
# process command line
#
########################
#
# use @flag to hold all flags
# use @fn to hold all non-flag arguments
# flagl = flag array length -1
# @filenames - used to hold all non-flag items in command line
# fnl = number of elements in @filenames -1

$avlength=@ARGV-1;

# scan ARGV:
# - put "--joebob" variables in @flag
# - put other ARGV elements in @filenames
$j=0;
$k=0;
for $i (0 .. $avlength)
{
	#print "argv-$i is ->$ARGV[$i]<-\n";
    if ( $ARGV[$i] =~ /--\w+?/ )
    {
		$flag[$j]=$ARGV[$i];
		$j++;
    }
    else
    {
		$filenames[$k]=$ARGV[$i];
		$k++;
    }
}
$flagl=$j;

#
# is --debug passed?
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--debug/)
    { 
	$debug=1;
    }
}
if ($debug) 
{
	print "flags are @flag\nfilenames are @filenames\n";
	print "debugging on\n";
}

#
# is --descend passed?
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--descend/)
    { 
	$descend=1;
    }
}


#
# is --help passed?
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--help/)
    { 
	@help = "\notl [--assume-yes] [--debug] [--descend] [--help ] [--nopretty] [--nt]  [--pfile </path/to/parameter-file>] [--no-style-query] [--style stylename ] [--version] [filename]\n\n\nsee documentation distributed with package for detailed instructions for use\n\n";
	print @help;
	exit;
    }
}


#
# check for flags passed from command-line
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--no-style-query/)
    { 
		$stylequery=0;
    }
    if ( $flag[$i] && $flag[$i] =~ m/--assume-yes/)
    { 
		$assumeyes=1;
    }    
    if ( $flag[$i] && $flag[$i] =~ m/--nopretty/)
    { 
	$nopretty=1;
    }
  }


#
# is --style passed? 
#
$path=$ENV{'HOME'};
$paramdir="$path/.otl";
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--style/)
    { 
		# at this point, this is the only flag
		# which accesses a non flag argument so
		# we can assume it sits at the top of @fn
		# - pop it off so it doesn't get reused as filename
		#   for otl
		$stylefile=shift(@filenames);
		$stylefile="$stylefile$stylefilesuffix";
		$stylefile="$paramdir/$stylefile";
		#print "Using style at stylefile $stylefile\n"; 
    }
}

#
# is --version passed? 
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--version/)
    { 
	print "otl version $version\n";
    }
}

######################################
#
# PROCESS PARAMETER FILE
#
######################################

# get parameter file name: is --pfile flag present?
#
for $i (0 .. $flagl)
{
    if ( $flag[$i] && $flag[$i] =~ m/--pfile/)
    { 
		# dig parameter file name out of ARGV and remove parameter file name from @fnl
		for $j (0 ..$avlength)
		{
			if ($ARGV[$j] =~ m/--pfile/)
			{ 
				$paramfile=$ARGV[$j+1];
				chomp $paramfile;

				$fnl=@filenames-1;

				if ($debug) { print "paramfile is $paramfile\nfnl is $fnl\n"; }
				# remove paramfile from @filenames
				$k=0;
				while ($k <= $fnl )
				{
					if ($filenames[$k] =~ m/$paramfile/)
					{
					splice @filenames,$k,1;
					$fnl--;	# you just shortened @filenames
					}
					$k++;
				}
			}
		}
    }
}
unless ($paramfile)
{
	# if no paramfile argument, define paramfile to look for
	$path=$ENV{'HOME'};
	$paramdir="$path/.otl";
	$paramfile="$paramdir/otl";
	if ($debug) { print "No parameter file given in command-line so using paramfile ->$paramfile<-\n"; }
}

#
# open parameter file if it exists and put it in @t2h array
#	- otherwise create default at ~/.otl
#
if ( open(INFO,$paramfile) )
{
	    if ($debug) { print "...using parameter file $paramfile \n"; }
	    open(INFO,$paramfile);
	    @t2h=<INFO>;
	    close(INFO);
}
else
{
	if ($debug) { print "no .otl file found...writing default to ~/.otl\n"; }
	# create @t2h array
	@t2h=<DATA>;

	#	hash %ENV contains current environment - use it to get home directory
	# - isn't $path already defined correctly? (is line below necessary?)
	$path=$ENV{'HOME'};
	$paramdir="$path/.otl";
	
	# make sure directory is present
	if (-e $paramdir && -d $paramdir)
	{
		# no worries
	}
	else
	{
		# directory isn't present or isn't a directory
		if (-e $paramdir)
		{
			# it's a file - delete it
			unlink( $paramdir );
		}
		mkdir($paramdir);
	}
	
	$paramfile="$paramdir/otl";
	open(FOUT, ">$paramfile");
	flock(FOUT,2);
	print FOUT @t2h;
	close (FOUT);
}

#
# figure out what parameter file looks like
#
$t2hlength=@t2h;
if ($debug) {
	print "\n\nParameter file is @t2h\n";
	print "\tNumber of lines in parameter file is $t2hlength\n";
}


######################################################################
# extract information from parameter file and put in useful arrays
#
# - look for file suffix
# - look for head or foot items
# - look for bracketed and triggered items

#
# check parameter array for file suffix
#
#	- otherwise use .otl as default output suffix
#
if ($debug) { print "Check parameter array for suffix items: line"; }
for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) { print " $i"; }

	if ($t2h[$i] =~ m/^(\S+)/ )							# check if line starts with a non-space character
	{
		if ($t2h[$i] =~ m/^suffix\s+(\w+)/ )
		{
		    if ($debug) { print "\t\tthis is the 'suffix' item $1\n"; }
		    $suffix=$1;
		    last;
		}
		elsif ( (not $suffix) && ( $0 =~ /\/([^\/]+)$/) )
		{ 
			$suffix=$1;
		 }
		if ( not $suffix )
		{
			print "we should never be here...\n";
			$suffix="otl";
		}
	}
}
if ($debug) { print "\tsuffix is $suffix\n"; }

# set output mode based on suffix
$outputmode=$suffix;



####
#
# style:
#
# once we know the file type via $suffix, we can define the appropriate
# default style file or offer the appropriate selection of style files
# if the style file isn't specified
#

# if --style wasn't passed, define default style
# if stylequery isn't 1
if ($debug)
{
	print "stylefile $stylefile\nstylequery $stylequery\n";
}
if ( $stylefile==0 && $stylequery==0 )
  {
    # define default style file
    $stylefile="$paramdir/default.css";
  }
else
  # list style files available and have user select one
  {
    opendir PARAMDIR, $paramdir;
    @dirfiles = readdir PARAMDIR;
    closedir PARAMDIR;
    for (@dirfiles)
      {
	if ($_ =~ m/.*?\.css$/)
	  {
	    push (@stylefiles,$_);
	  }
      }
    $stylefileslength=@stylefiles;
    if ($assumeyes==0)
      #
      # would be nice to have this offer appropriate selections
      # based on file type ($suffix = html, tex, ...)
      #
      {
	#
	# html style files
	#
	if ( $suffix eq "html" or $suffix eq "xhtml" )
	  {
	    print "\nChoose the style file to use (by number):\n\n";
	    
	    for ($x=0;$x<$stylefileslength;$x++)
	      {
		print "[$x] $stylefiles[$x]\n";
	      }
	
	    # should always have a 'nostyle' option
	    print "[$x] no style\n";
	    

	    print "\n  Selection: ";
	    # get choice
	    $stylefilenumber=<STDIN>;
	    chomp ($stylefilenumber);
	    # why not just convert string to number? possible?
	    if ( $stylefilenumber =~ m/[0-$stylefileslength]/ )
	      { 
		$stylefile="$paramdir/$stylefiles[$stylefilenumber]";
		print "\nUsing style file $stylefiles[$stylefilenumber]\n";
	      }
	    elsif ($stylefilenumber == $x)
	      # no style
	      {
		$stylefile="";
		}
	    else
	      {
		print "Using default style\n";
		$stylefilenumber = 0;
		$stylefile="$paramdir/default.css";
	      }
	  }
      }
    else
      {
	print "Using default style\n";
	$stylefilenumber = 0;
	$stylefile="$paramdir/default.css";
      }
    #die;
  }
if ($debug)
  { print "stylefile $stylefile\n"; }


#
# check parameter array for head or foot items...
# 	@headp - holds header lines from parameter file
if ($debug) {print "\nCheck for head items: parameter array line"; }
for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) {print " $i"; }
	# if line starts with a non-space character
	if ($t2h[$i] =~ m/^(\S+)/ )							
	{
		if ($t2h[$i] =~ m/^head/ )
		{
			# get head data
			# - meaning WHAT??
			$headpos=0;				# position in head array
			$endloop=$i+2;
			for ( $i=$i+1; $i < $endloop; $i++)
			{
				if ( $t2h[$i] !~ m/^head/)
				{ 
				$headp[$headpos]=$t2h[$i];
				$headpos++;
				$endloop++;
				}
			}
			#print "\nheadp: @headp\n\n";
			# now check and see if $stylefile defined

			#
			# ** could enhance by having predefined 'styles'
			#    for multiple formats (html, tex, etc.)
			#    (current implementation: style only for html)

			if ( $stylefile && $suffix eq "html" )
			{
				print "\nUsing stylefile: $stylefile\n";
				# insert file contents at
				# second-to-last line in head array
				if ( open (INFO,$stylefile) )
				{
					open(INFO,$stylefile);
					@stylearray=<INFO>;
					close(INFO);
					# stylearray contains a css file
					# - add stuff for insertion in html
					push(@stylearray,"</style>");
					unshift(@stylearray,"<style type=\"text/css\">");
					# insert stylearray stuff
					splice(@headp,-1,0,@stylearray);
				}
				else
				{
					print "\n\nProblem opening style file!\n";
					@stylearray=[""];
				}
			}
		}
	}
}

#
# check for foot items
#
if ($debug) {print "\nCheck for foot items: parameter array line"; }
for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) { print " $i"; }
	if ($t2h[$i] =~ m/^foot/ )
	{
		$footpos=0;											# position in foot array
		$endloop=$i+2;
		for ( $i=$i+1; $i < $endloop; $i++)
		{
			if ( $t2h[$i] !~ m/^foot/)
			{
				$foot[$footpos]=$t2h[$i];
				$footpos++;
				$endloop++;
			}
		}
	}
}
#
# process parameter file for preequivalent items
#
#	$preequiv1 is start tag
#	$preequiv2 is end tag
if ($debug) { print "\n\nCheck parameter array for preequiv items; checking line "; }
for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) { print " $i" }
	if ($t2h[$i] =~ m/^preequivalent\s+?\|(.+?)\|\s+?\|(.+?)\|/ )
	{
		$preequiv1 = $1;
		$preequiv2 = $2;
		if ($debug) { print "\n\tMatch: preequiv1 is $preequiv1 and preequiv2 is $preequiv2\n" }
	}
}

# processing parameter file for...
#
# 'bracketed' items...
#	@look 	- 'bracket' string to look for
#	@rstart - item to replace first bracket string with
#	@rend 	- item to replace second bracket string with
#
# for 'triggered' items...
#
#	format of line in parameter file:
#
#		tlook -item to look for
#			@tlook is array of these items
#			tlooklength is the # of items in this array
#		tstart - item to replace with
#		tend - item to replace with at end
#		gstart - if item is grouped, (e.g., a list), item to put at start of group
#		gend - if item is grouped, item to put at end of group
#		ttype - list item type (o=ordered,n=not a list item, u=unordered)
#
if ($debug) { print "\n\nCheck parameter array for BRACKETED AND TRIGGERED items\n\tchecking line "; }

for ( $i=0; $i < $t2hlength; $i++)
{
  if ($debug) { print " $i "; } # ->$t2h[$i]<-\n
	if ($t2h[$i] =~ m/^(\S+)/	) # check if it starts with a non-space character
	{
		if ($t2h[$i] =~ m/^start/ )
		{
			if ($debug) { print "this is a start item\n"; }		# 'triggered' items
# PUT TRIGGERED STUFF PROCESSING HERE		
			if ($t2h[$i] =~ m/^start\s+?(\S+)/ )		# get tlook
			{
				$tlook[$tlooklength] = $1;
				if ($debug) { print "tlook item $tlooklength is $1\n"; }
				if ($t2h[$i] =~ m/\|(.*?)\|\s+?\|(.*?)\|\s+?\|(.*?)\|\s+?\|(.*?)\|\s+?(.)/ )		# get tstart
				{ 
					$tstart[$tlooklength] = $1;
					$tend[$tlooklength] = $2;

					# assume other values present
					$gstart[$tlooklength] = $3;	# w/o checking syntax
					$gend[$tlooklength] = $4;
					$ttype[$tlooklength] = $5;
					if ($debug) { print "tstart: $1\ntend: $2\ngstart:$3\n"; }
				}
				else
				{ 
				#	if ($debug) { print "error in t2h format\n"; }
					$tlook[$tlooklength] = "";
					$tlooklength = $tlooklength-1;
				}	
				$tlooklength++;
			}
			else
			{ 
			#	if ($debug) { print "error in t2h format\n"; }
			}
		}
		elsif ($t2h[$i] =~ m/^bracket\s+?(.+?)\s/ )												# probably a 'bracketed' item
		{ 
			$look[$looklength] = $1;
			if ($t2h[$i] =~ m/\|(.*?)\|/ )
			{ 
				$rstart[$looklength] = $1;
				if ($t2h[$i] =~ m/([^\|]+)\|$/ )
				{ 
					$rend[$looklength] = $1; 
					if ($debug) 
					{ 
						print "IS a 'bracketed' item\n"; 
						print "\t\tlook is $look[$looklength]\n\t\t, rstart is $rstart[$looklength]\n\t\t, rend is $rend[$looklength]\n";
					}
					$looklength++;
				}
				else
				{
					$look[$looklength] = "";
					$rstart[$looklength]="";
				}
			}
			else
			{
				$look[$looklength] = "";
			}
		}
	}
}


#
# process parameter file for default line or lines
#
$dlength=0;			# initialize length of default array
if ($debug) { print "\nCheck parameter array for DEFAULT items\n\tchecking line "; }

for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) { print " $i "; }

	# check if the line in the parameter file is a default line
	# - allow default to be the null string
	if ( $t2h[$i] =~ m/^\|(.*?)\|\s+?\|(.*?)\|/	)
#	if ( $t2h[$i] =~ m/^\|(.+?)\|\s+?\|(.+?)\|/	)
	{
#			print "it's default with string 1 as $1 and string 2 as $2\n";
			$dstart[$dlength] = $1;
			$dend[$dlength] = $2;
			$dlength++;
	}
}

#
# process parameter file for 'underlying' items
#
#	parameter file format: under under-item |start tag| |finish tag|
#
#	variables:
#
#	ulook	array strings to look for
#	uend	array containing corresponding end tag to substitute in
#	ustart	array containing corresponding start tag to substitute in
if ($debug) { print "\n\tCheck parameter array for UNDER items\n"; 
	print "\t\tChecked parameter array line ";
	}
$ulength=0;
for ( $i=0; $i < $t2hlength; $i++)
{	
	if ($debug) { print " $i "; }
	if ( $t2h[$i] =~ m/^under\s+?(.+?)\s+?\|(.*?)\|\s+?\|(.*?)\|/ )		# under tag ? 
	{
		if ($debug) 
		{ 	
			print "it's under with string 1 as $1 and string 2 as $2 and string 3 as $3\n"; }
		$ulook[$ulength] = $1;
		$ustart[$ulength] = $2;
		$uend[$ulength] = $3;
		++$ulength;
	}
}


#
# process parameter file for blank item
#
#	parameter file format: blank |tag|
if ($debug) { print "\n\tCheck parameter array for BLANK items\n\tchecking line"; }
for ( $i=0; $i < $t2hlength; $i++)
{	
	if ($debug) { print "\t $i "; }
	if ( $t2h[$i] =~ m/^blank\s+?\|(.*?)\|\s+?/ )
	{
		if ($debug) 
		{ 	print "\tChecked parameter array t2h line $i: $t2h[$i]\n for BLANK item\n";
			print "it's blank with string 1 as $1\n"; 
		}
		$blank = $1;
	}
}

#
# process parameter file for trigger-tab line or lines
#	trigger-tab |<string 1>| |<string 2>|
#
# relevant variables:
#	ttlength - length (number of items in) ttstart and ttend arrays
#	@ttstart - array with start tags for each tab/nest mismatch level
#	@ttend - array with end tags for each tab/nest mismatch level
$ttlength=0;			# initialize length of default array
if ($debug) { print "\n\tCheck parameter array for TRIGGER-TAB items\n";
	print "\t\tchecking line "; }
for ( $i=0; $i < $t2hlength; $i++)
{
	if ($debug) { print " $i "; }
	if ( $t2h[$i] =~ m/trigger-tab\s+?\|(.+?)\|\s+?\|(.+?)\|/	)
	{
			if ($debug) { print "\t\ttrigger-tab with string1 $1 and string2 $2\n"; }
			$ttstart[$ttlength] = $1;
			$ttend[$ttlength] = $2;
			$ttlength++;
	}
}

###
#
# process files
#
###

# if --descend is on, we construct a new @filenames by recursing through
# the directories of interest
if ($descend)
{
	# imports function which lets us do recursive search through directory tree
	#	find ( \&subroutine, "directory name")
	#		- executes subroutine in each directory
	#		  in the subdirectories of "directory name"
	
	# is this variable necessary??
	$currentDirectory=0;	# need to evaluate first directory we hit
	
	@newfilenames=();	# initialize new filename holder
	
	print "globbed current directory: ",glob(".");
	
	find(\&matching_filename, glob("."));
	
	# debugging
	#die @newfilenames;
	@filenames=@newfilenames;
}

# process filenames
$fnl=@filenames;
unless ($filenames[0])
{
	print "Please at least supply a filename for otl to process\n";
	die;
}

for $zz (1 .. $fnl)
{
	$filename=$filenames[$zz-1];
	PROCESS($filename);
}

sub matching_filename
# names at $_ (from File::Find) correspond to names 
# being evaluated for
# a match against @filenames
{
	# debugging - where are we?
	#print "matching_filename: current directory (globbed): ", glob(".");

	# declare local variables
	my $currentFullName;	
	my $currentname=$_;	# current name File::Find has given us
	my (@filenamesFinal)=(); # array with final list of names to check against
	my (@globbed)=();
	# every time we enter a new directory,
	# see if @filenames needs
	# to be globbed
	if ($File::Find::dir ne $currentDirectory)
	{
		@filenamesFinal=();
		# we entered a new directory - check to see if @filenames needs globbing
		#print "filenames array: @filenames\n";
		foreach (@filenames)
		{
			if ($_ =~ /[\*\$]/ )
			{
				# $_ is current filename with wildcard
				@globbed=glob($_);
				#print "globbed array: @globbed\n";
				foreach (@globbed)
				{
					push(@filenamesFinal,$_);
				}
			}
			else
			{
				push(@filenamesFinal,$_);
			}
		}
	}
	# now that we have a list of filenames to check against, let's see if
	# any files in the current directory match
	$currentFullName=$File::Find::name;
	#print "filenamesFinal: @filenamesFinal\n";
	for (@filenamesFinal)
	{
		if ( $_ eq $currentname) 
		{
			push(@newfilenames,$currentFullName);
		}
	}
}

#########################################################
##                                                     ##
## start of routine which is called for each file      ##
##                                                     ##
#########################################################
sub PROCESS {

  use List::Util;

#
# variables passed from above which shouldn't be initialized
#
# $blank        - blank string specified in parameter file
#
# $dlength      - defaults array length
# @dstart       - holds default start items
# @dend         - holds default end items
#
# @foot         - footer from parameter file
#
# @gend         - if trigger item is grouped, item to put at start of group
# @gstart       - if trigger item is grouped, item to put at end of group
#
# @headp	- holds header text from parameter file
#		- should NOT be touched/altered by processing subroutine
# @look
#
# $nopretty - flag to not 'prettify' first using otlsub
#
# $preequiv1;   - preequivalent start tag
# $preequiv2;   - preeqiuvalent end tag
#
# @rstart
# @rend
#
# $suffix
#
# @tend
# @tlook
# @tstart
# @ttstart
# @ttend
# @ttype - corresponding list item type for tstart,tlook,tend,gstart,gend
#
# @uend
# $ulength
# @ulook
# @ustart
#

# declare lexically local variables
# - variables declared with my are 'hidden' from 
# "outside world"
my $a;			# dummy variable

my $b;			# dummy variable
my $cl;

my @def;		# line #'s to process as defaults
my $deflength;	# length of def array
my @dfin=();

my $filein;
my $fileout;
my $firstline;	# first line # in list

my @hd=();
my $headend;
my $hl;			# length of @headp
my $hprocess;	# process header

my $i;			# count/index variable
my $ii=0;
my @ignore=();
my $ilength;

my $j;			# count/index variable
my $k;			# count/index variable

my $lastline;
my $len;
my @lgendj=();
my $lineen;
my @lines=();
my $lineslength;	# length of @lines
my @linesout=();
my @list=();
my @llast=();
my $lline;
my @llines=();
my @lnest=();
my $lnum;
my @ltab=();
my @ltype=();

my $m;				# index/count variable
my $match;
my @match=();
  my $match1; # used with match2 for matching pairs
my $match2;

my $n;				# index/count variable
my $nest;
my $nextdline;
my $nextline;
my @nextlist=();
my $nlli;

my $nooverwrite=0;	# 0 = overwrite file

  my @NoProcess=();  # list of integers (a1,a2,b1,b2,...) representing line numbers with NoProcess tags

my $notitle;		# notitle command-line flag
my $number;

my $owheader;		# overwrite header?

my $q;				# dummy variable

my $pe;
my $plength;		# proclines length
my $pns;
my $preequivflag=0; # ?????
my @proclines=();
my $ps;

my @remove=();			# array containing individual line #'s to not process
my $removelen;		# remove length
my @returntable=();	# array of lines to treat as final table
my $rmv;

my $start;

my @tab=();
my @tablearray=();	# array of lines to treat as table
my @thislist=();
my $title;
my $tlli;
my $tmax;
my $tmin;
my $tmp;			# dummy variable

  my @underlines=();

my $v;			# dummy variable

  my @VerbatimPre=();  # list of integers (a1,a2,b1,b2,...) representing line numbers with VerbatimPre tags

my $x;			# dummy variable
  my $y; # dummy variable
my $z; # dummy variable

# name of file to process
$filein=shift(@_);

#
# first prettify file being processed (by default)
#
#print "nopretty: $debug\n";
if ($nopretty == 0)
{
	#print "executing: otlsub $filein\n";
	if ($assumeyes==0)
	  {
	    system("otlsub $filein");
	  }
	else
	  {
	    system("otlsub --assume-yes $filein");
	  }

	# change $filein to match new output file
	if ($filein =~ /(.+)\..*/)
	{
		$filein="$1.out";
	}
	#print "filein is now $filein";
}

#
# open file-to-process and put file content in array lines
#

# get info from @filein array now...
#if ($debug) { print "\nat sub is ->@_<-\n"; }
die "Can't open file name ->$filein<-" unless open(INFO,$filein);
#print "\nProcessing $filein\n";
open(INFO,$filein);
@lines=<INFO>;
close(INFO);

#
# figure out what filein looks like
#
$lineslength=@lines;

#
# create a duplicate array (linesout) that will be the output
#
for ( $i=0; $i < $lineslength; $i++)
{
	$linesout[$i]=$lines[$i];
}


#########################################
#
# PROCESS TEXT FILE-TO-PROCESS
#
#########################################
#
# order of processing:
#	- remove ^M-sh stuff
#	- generate array where each line is a separate array element
#	- strip out whitespace at line ends
#	- check if file already has something that looks like HTML header
#	- set range of lines to process
#       - set title string for customizing header later
#	- identify lines starting with ## or ### (don't process but include)
#       - look for special characters which should be converted
#            xhtml/html: ampersands
#            latex: ...
#	- check for bracketed items
#	- check for under items
#	- check for URLs and give them <a
#	- check for triggered items (usually list items)
#		- deal with items themselves
#		- deal with ends of lists
#		- deal with nesting of lists
#	- process blank lines
#	- establish array of "default" lines
#               - generate tentative array
#               - remove certain line types from default array
#	- apply defaults
#		- check for grouping (the "\" character)
#

#
# description of variables
# ------------------------
#
# @def	- contains line numbers which are being treated as defaults
#	- this array is constructed when looking for 'triggered' items
#	- contains start and end numbers for sets of lines which are
#		to be processed as defaults
#
# deflength	- # of items in def array
#
# dlength 	- number of items in dstart or in dend
# dstart[ ]	- array holding default start items
#
# 
#
# @hd	- elements 0 and 1 hold start and finish lines for HTML header, 
#	if present, in document (-1 if not present)
#
# hprocess	- process header? right now, this is used both to
#		1. decide to process lines in apparent header (hprocess=y)
#		2. decide to insert otl's own header element (hprocess=y)
#
# @ignore	- lines to not process
#	- populated by 
#		looking for lines with ##
#		looking for lines in header if ignoring header
# ii	- ignore index
#
# len	- number of tabs preceding trigger character (listing level)
# 	- should default to 0?
# 	- if len exceeds length of dstart, need to back up to last dstart value in array
#
# linesout	- array containing file to output
#
# linest	- first line in linesout to process (not used any more)
# lineen	- last line in linesout to process
#
# @proclines	
#	- list of line numbers corresponding to the lines in linesout to process
# plength	- length of proclines array
#
# @remove	- array of lines to remove from processing?
#
# @uend		- array holding "under" end items
# ulength		- number of items in ustart or in uend
# @ulook	- array
# @underlines   - array holding "under" line numbers (used in initial scan)
# @ustart	- array holding "under" start items

####
#
# strip out ^M and other CR-ish stuff not needed with linux
#
#
#if ($debug) { print "\tstripping..."; }
foreach (@linesout)
{
	$_ =~ s/[\cM\r]//g;	# remove all control-M's and ASCII carriage returns
}
####
#
# need to rewrite and reread file so that \n's are now processed correctly
# and each line ends up as a separate array element
# -- write it to temp file
open(FOUT, ">/tmp/tmp.otl");
flock(FOUT,2);
print FOUT @linesout;
close (FOUT);
# now read it
@linesout=();
open(INFO,"/tmp/tmp.otl");
@linesout=<INFO>;
close(INFO);

$lineslength=@linesout;
if ($debug) 
{	print "\nlinesout after rewrite is $lineslength lines long:\n";
	for ($i=0;$i<$lineslength;$i++) 
	{
		print " line $i ->$linesout[$i]<-\n"; 
	}
}
####
#
# strip out any whitespace at ends of lines
#
foreach (@linesout)
{
	chomp ($_);
	$_ = "$_\n";
}









######
#
# look for stuff that might precede a header
# (stuff which otl shouldn't process):
#
# 	<?xml
# 	<!DOCTYPE
#	<head
#
# no <?xml or <!DOCTYPE or <head or </head tags found yet
if ($debug) { print "\nCheck for <?xml or <!DOCTYPE on line: "; }
$hd[0]=-1;
$hd[1]=-1;
for ( $i=0; $i < $lineslength; $i++ )
{
	if ($debug) { print "$i "; }
	if ($linesout[$i] =~ m/\<\?xml/ || $linesout[$i] =~ m/\<\!DOCTYPE/)
	{
		if ($debug)
		{
			print "\t MATCHED line $i: $linesout[$i]";
		}
		# mark location in hd array
		$hd[0] = $i;
		# exit loop
		last;
	}
}

#
# look for header in file
# - if present, query whether to mark those lines to not be processed further
#
#	hprocess: variable for adding/processing header
if ($debug) { print "HEADER CHECK: checking line "; }
# look for <head tag if some sort of starting tag not yet found
if ($hd[0]==-1)
{
	for ( $i=0; $i < $lineslength; $i++ )
	{
		if ($debug) { print "$i "; }
		if ($linesout[$i] =~ m/\<head/ )
		{
			if ($debug)
			{
				print "\t MATCHED line $i: $linesout[$i]";
			}
		# mark location in hd array
		$hd[0] = $i;
		}
	}
}

if ($debug) { print "\tlooking for </head or <body: checking line "; }
unless ($hd[0] == -1)
{
	# look for </head or <body tag
	for ( $i=$hd[0]; $i < $lineslength; $i++)
	{
		if ($debug) { print "$i "; }
		if ($linesout[$i] =~ m/\<\/head/ || $linesout[$i] =~ m/\<body/)	
		{
			if ($debug)
			{
				print "MATCHED linesout line $linesout[$i] \n";
			}
			# mark location in head array
			$hd[1] = $i;
		}
	}
}

# default setting is to add the 'header' specified by the 'head' element in 
# the otl configuration file
$hprocess="y";



############
#
# identify pairs of NoProcess (##) or VerbatimPre (###)
#
# @VerbatimPre - pairs (line numbers) containing the VerbatimPre string (currently ###)
# @NoProcess - pairs (line numbers) containing the NoProcess string (currently ##)
#
# ** redundant w/some of code below?
#
if ($debug) {print "Populating NoProcess and VerbatimPre\n";}
for ( $i=0; $i <= $lineslength-1; $i++)
  {
    #$tmp=$linesout[$i];
    # look for ## or ###
    if ($linesout[$i] =~ m/(^##$|^##\s|^###$|^###\s)/)
      { 
	if ($debug) {print "MATCH: $1 at line $i \n";}
	$match1=$1;
	# look for second member of pair
	$endloop=0;
	for ( $j=$i+1; ($j <= $lineslength-1 && $endloop==0) ; $j++)
	  {
	    # get non-whitespace component of line
	    $linesout[$j] =~ m/(\S+?)\s/;
	    $match2=$1;
	    if ($debug) {print "line j: $j: looking... ->$match2<- and ->$match1<-\n";}
	    if ($match2 eq $match1)
	      # found match
	      { 
		$endloop=1;
		if ($debug) {print "ENDMATCH at line $j\n";}
		if ($match1="##")
		  # push to @NoProcess (## is match)
		  { push (@NoProcess,$i); push(@NoProcess,$j);
		  }
		# otherwise push to @VerbatimPre (### is match)
		else
		  { push (@VerbatimPre,$i); push(@VerbatimPre,$j);
		  }
		$i=$j+1;
	      }
	  }
      }
}

#
# decide what to do if we think we've found a header element
if ($hd[0] != -1 && $hd[1] != -1 && $suffix eq "html" )
{
  # check if $hd[0] and $hd[1] are inside a pair of ## or ###
  if (not
      (DatPlUtils::pairinsidepairs($hd[0],$hd[1],@NoProcess) ||
       DatPlUtils::pairinsidepairs($hd[0],$hd[1],@VerbatimPre)
       ))
    {
      # if not inside a pair of ## or ###... warn...
	print "\n\nFound <head> and </head>";
	print "(line $hd[0] to line $hd[1])\n";
	print "Should this header material be processed by otl as regular otl source text? [y (default=n)]? If you answer no (n), all lines in the header will be omitted from processing AND otl will not add the 'head' element indicated in the configuration file to the output file. (n/y[default]): ";
	$hprocess=<STDIN>;
	chomp ( $hprocess );		# remove \n at last line
	if ($hprocess eq "n")
	{
		print "Avoiding lines $hd[0] through $hd[1]\n";
	}
	else
	  {
	    $hprocess = "y";
	  }
      }
}
else
{
  if ($hd[0] != -1 || $hd[1] != -1)
    {
      if ( $suffix eq "html" )
	{ print "WARNING: found part of a <head> </head> combination at $hd[0] and $hd[1]\n"; }
    }
}

#
# add head lines to @remove
#
#	@remove - array w/individual line numbers to ignore
if ($hprocess =~ "n")
{
	# exclude header lines
	for ( $i=$hd[0]; $i <= $hd[1]; $i++)
	{
		push(@remove,$i);
	}
}

####
#
# set range of lines to process
#
#	@processline is array holding line numbers to process
#
# set up @proclines array as initially containing all lines
for ( $i=0; $i <= $lineslength-1; $i++)
{
	$proclines[$i]=$i;
}
$plength=@proclines;
$lineen=$proclines[$plength-1];
if ($debug) { print "\nfinal array of lines to process ->@proclines<-\n"; }





##########################
#
# set title string so that header can be customized later
# if desired (--nt to block)
#
#	** need to make this smarter if <head> or <xml> present @ start of document **
$title=$linesout[$proclines[0]];
chomp ($title);





####
#
# identify line ranges that correspond to material which isn't
# going to be processed:
#
# 1. stuff that will be left verbatim or in <pre>...</pre> or the equivalent thereof
# 2. stuff that will not be included at all
#
##########################
#
# check for lines that start with ## or ###
#	-- these bracket sets of lines to not process (but do include in 
#		final output)
#	-- for ###, also check
#             - if any "<" characters should be converted to &lt;
#
# approach:
#	- identify lines with ## and 
#		(1) put line numbers in array 
#		(2) pull ## from line
#	- mark those lines as 'off-limits' by adding to @ignore
#
# variables:
#	@ignore - array of line numbers with ##
#
#	$ii = index variable for ignore array
#	@remove - array containing individual line numbers to not process
#
if ($debug) { print "\nChecking for ##: checking line "; }
foreach(@proclines)
{
  $tmp=$_;
  if ($debug) { print "$_ " }
  # check for <maybeatag if we're between ###
  if ( $preequivflag == 1 && $linesout[$tmp] =~ m/(<)\S+?\s/ && $suffix eq "html" )
    {
      print "\n\nFocus is this line: --> $linesout[$tmp] <--\n";
      $x=0;
      while ($x ne "y" and $x ne "n" )
	{
	  print "\nIn file $filein...";
	  print "\n\tEncountered line with at least one potential tag - convert '<' to &lt;? ";
	  $x=<STDIN>;
	  chomp ( $x );		# remove \n at last line
	  if ($x == "y")
	    {
	      $linesout[$tmp] =~ s/<(\S+?)/&lt;$1/g;
	    }
	}
    }
  #if ($debug) { print "  Checking for special case"; }
  if ($linesout[$tmp] =~ m/^##$|^##\s|^###$|^###\s/  )
    {
      if ($debug) { print "Line with ## at start: ->$linesout[$tmp]<-"; }
      # check for special case -- if first line is "###" the this is 
      # "preequiv" line - insert preequiv tag here
      if ($linesout[$tmp]=~ m/^###$|^###\s/ and $preequiv1 and $preequiv2)
	{
	  # set toggle flag so we know when to stop inserting stuff
			if ($preequivflag==0)
			  {
			    if ($debug) { print "\tpreequivflag is zero: $preequivflag"; }
			    $preequivflag = 1;
			    $linesout[$tmp] =~ s/^###//;
			    $linesout[$tmp] = "$preequiv1$linesout[$tmp]";
			  }
			elsif ($preequivflag==1)
			  {
			    # then this is the ending one
			    if ($debug) { print "\tpreequivflag is one: $preequivflag" }
				$preequivflag =0;
			    $linesout[$tmp] =~ s/^###//;
			    $linesout[$tmp] = "$linesout[$tmp]$preequiv2";
			  }
		      }
      else
	# dealing with ##
	{
	  $linesout[$tmp] =~ s/(^##)//;
	}
      $ignore[$ii]=$tmp;
      $ii++;
    }
}

# put lines to ignore in @remove
$ilength=@ignore-1;
if ($debug) { print "\tilength is $ilength and ignore array is @ignore and remove array is @remove"; }
unless ( $ilength<=0 )
{
	for ( $i=0; $i <= $ilength; $i=$i+2)
	{
		if ($debug) { print "\t\tpushed line "; }
		for ($j=$ignore[$i];$j<=$ignore[$i+1];$j++)
		{
		       push(@remove, $j);	# put line # in remove array
		       if ($debug) { print " $j"; }
		}
		if ($debug) { print " onto remove array"; }
	}
	if ($debug) { print "done"; }
}

if ($debug) { print "\tFinal remove array: @remove"; }


###
#
# scan file to identify lines with under items
#
###

for ( $j=0; $j < $ulength; $j++ )
{
  if ($debug) { print "\nScan for under lines\n\tchecking for $ulook[$j]\n\t\tchecking line "; }
  foreach(@proclines)
    {
      $tmp=$_;
      # check if line $i is in remove array
      # - if it is jump to end of loop
      $rmv=0;
      foreach (@remove)
	{
	  if ($_==$tmp) {$rmv=1;}
	}
      unless ($rmv)
	{
	  if ($debug) { print " $tmp "; }
	  # look for [under-item] with space after it...
	  if ($linesout[$tmp] =~ m/^$ulook[$j]\s+?/ )	
	    {
	      if ($debug) { print "\n\t\t MATCHED line $i ->$linesout[$tmp]<-\n"; }
	      # add line number to array holding under lines
	      push (@underlines,$tmp);
	    }
	}
    }
}



###############################
#
# CHECK FOR SPECIAL CHARACTERS FOR
#   xhtml/html files
#   latex/tex files
#
###############################


if ($suffix eq "tex")
  {
    # latex files:
    # &     $     %     #     _    {     }    \ ^ ~
    if ($debug) { print "\n\nchecking on latex special chars..."; }    #
    # don't process lines in @remove, though...

    # keep track of line number with x
    $n=0;
    foreach(@proclines)
      {
	$tmp=$_;
	if ($debug) { print " $tmp"; }
	# test if current line number from @proclines is in @remove
	use List::Util 'first';
	$z = first { $_ == $tmp } @remove;
	# test if current line number is in @underlines
	# exclude lines which are 'under' lines from special char processing
	$y = first { $_ == $tmp } @underlines;
	if ( not $z and not $y )
	  {
	    if ( $linesout[$tmp] =~ m/[^\\]*?[&\$%_\{\}\^<>~]/ or
		 $linesout[$tmp] =~ m/\\/
		 or
		 # exclude lines with only '##' or '###' for # search...
		 ( $linesout[$tmp] =~ m/[^\\]*?#/
		   and
		   ( $linesout[$tmp] !~ m/^#{2}\s/ and  $linesout[$tmp] !~ m/^#{3}\s/ )
		 )
	       )
	      {
		if ($debug) { print "  smartmode: $smartmode\n  line: $linesout[$tmp]\n"; }
		# smartmode:
		#   things we don't want to waste user's time with if we're
		#   trying to be smart:
		#     1. match looks like a tex/latex command
		#        [implemented only for simple one-liner latex commands]
		#     2. special character is part of a typical phrase/structure
		#        [not implemented yet]
		if ((not
		     (
		      ($smartmode == 1) &&
		      (
		       # match \texcommand{more stuff}
		       # doesn't match \texcom[things]{stuff}
		       $linesout[$tmp] =~ m/\\\w+{[^}]+\}/)
		     )
		    )
		   )
		  {
		    # replace match in output file?
		    if ( $assumeyes == 0 ) 
		      {
			print "\n\nRegarding this line:\n--> $linesout[$tmp] <--\n"; 
		      }
		    $x=0;
		    while ($x ne "y" and $x ne "n")
		      {
			if ( $assumeyes == 0 ) {
			  print "\nEncountered line with one or more special tex character entities - should they be preceded with a reverse backslash? "; 
			  $x=<STDIN>;
			  chomp ( $x ); 		# remove \n at last line
			}
			else
			  {
			    $x = "y";
			  }
			if ( $x eq "y")
		      {
			# replace backslashes with ==01234BACKSLASH01234==
			# (don't use \$backslash$ since $ chars are problems)
			if ($linesout[$tmp] =~ m/\\/)
			  {
			    if ($debug) { print "backslash\n"; }
			    $linesout[$tmp] =~ s/\\/==01234BACKSLASH01234==/g;
			  }
			# then replace everything else, including $ chars
			# excluding ^ since it isn't escaped with backslash
			if ($linesout[$tmp] =~ m/[^\\]*?[&\$%#_\{\}]/)
			  {
			    if ($debug) { print "non-backslash\n"; }
			    $linesout[$tmp] =~ s/([^\\]*?)([\\&\$%#_\{\}]+?)/$1\\$2/g;
			  }
			# deal with ^
			# ** what if user want's to include tex math mode
			# stuff? **
			# - what if a under spec, or another spec, uses ^?
			$linesout[$tmp] =~ s/\^/\$\\mathchar"1356\$/g;
			
			
			# replace ==01234BACKSLASH01234== if present
			$linesout[$tmp] =~ s/==01234BACKSLASH01234==/\$\\backslash\$/g;
			# deal with >
			$linesout[$tmp] =~ s/>/\\textgreater /g;
			# deal with <
			$linesout[$tmp] =~ s/</\\textless /g;
			
			# deal with ~
			$linesout[$tmp] =~ s/~/\$\\sim\$/g;
			
			
		      }
		  }
	      }}
	  }
      }
  }


#
# for html, we let <joe> tags pass through except in special circumstances
#   - so... here, we only look for the ampersand character
#
if ($outputmode eq "html")
{
  # replace problem html/xhtml characters
  if ($debug) { print "proclines: @proclines\n"; }
  if ($debug) { print "\n\nreplacing problem xml characters... processing line number: "; }
  foreach(@proclines)
    {
      $tmp=$_;
      if ($debug) { print " $tmp"; }
      # match ampersands except when ampersands look like they are
      # part of a XML special character specification (&xyz;)
      if ($linesout[$tmp] =~ m/(&\S*?)\s|()&\s/ && $1 !~ /&.*?;/ 
	  and $suffix="html")
	{
	  # replace match in output file?
	  print "\n\nRegarding this line:\n--> $linesout[$tmp] <--\n";
	  $x=0;
	  while ($x ne "y" and $x ne "n")
	    {
	      print "\nEncountered line with potential ampersand (character entity) tag - convert to &amp;? ";
	      $x=<STDIN>;
	      chomp ( $x );		# remove \n at last line
	      if ($x eq "y")
		{
		  $linesout[$tmp] =~ s/&(.*?[^;])/&amp;$1/;
		}
	    }
	}
    }
}



######
#
# table of contents
#  
#  - implemented for: tex
#  - not implemented yet: html

if ($suffix eq "tex")
  {
    foreach(@proclines)
      {
	$tmp=$_;
	if ( $linesout[$tmp] =~ m/^\[\[TOC\]\]/ )
	  {
	    $linesout[$tmp] =~ s/^\[\[TOC\]\]/\\tableofcontents \\setcounter{tocdepth}{4}/;
	  }
      }
  }


###
#
# REPLACE 'bracketed' items IN FILE BEING processed
#
# e.g., find and replace -joe- with <span style="font-weight: bold;>joe</span>
#
#	i = line number in file
#  	j = line in look
#
# parameter file variables for 'bracketed items'
#	@look 	- 'bracket' string to look for
#	@rstart - item to replace first bracket string with
#	@rend 	- item to replace second bracket string with
#
if ($debug) { print "\n\nreplacing BRACKETED items\n\tlook array is @look"; }
# if look is empty, don't waste your time
unless ($looklength==0)
  {
for ( $j=0; $j < $looklength; $j++ )
{
	if ($debug) { print "\n\tchecking for look item $look[$j]"; 
		      print "\n\tchecking line(s)"; }
	foreach(@proclines)
	{
		$tmp=$_;
		# check if line $i is in remove array
		# - if it is jump to end of loop
		$rmv=0;
		foreach (@remove)
		{
			if ($_==$tmp) {$rmv=1;}
		}
		unless ($rmv)
		{
			if ($debug) { print " $tmp"; }
			if ($linesout[$tmp] =~ m/$look[$j](.+?)$look[$j]/ )										# look for -joe bob-
			{
			    # replace match in output file
				$linesout[$tmp] =~ s/$look[$j](.+?)$look[$j]/$rstart[$j]$1$rend[$j]/g;
				if ($debug)
				{
					print "\tMATCHED line $tmp -- match was ->$1<-\n";
					print "\tlinesout line now: $linesout[$tmp] \n";
				}
			}
		}
	}
}
}

###
#
# deal with tables, if present
#
# - table would be defined by two or more consecutive lines 
#   containing tabs at a position after the first
#   non-whitespace character in the line but this leads
#   to major ambiguity if aesthetics (visual appearance) 
#   are a concern since tab spacing is arbitrary
# - so...probably spaces give cleanest look; define a table
#   as two or more consecutive lines 
#   containing 2 or more space characters at a position 
#   after the first non-whitespace character where
#   the 2 or more space characters are also succeeded by
#   at least one non-whitespace character

# - in such a case intent is determined by assuming
#   a fixed-width font
#
# - procedure: identify lines of interest and put in
#              an array, send to PROCESSTABLE,
#              use the lines returned to replace the
#              lines sent

# let routine know we're not in the middle of a table
if ($debug) { print "\nTABLE processing...\n"; }
$lastline=0;
# move through lines to process, looking for a table
foreach(@proclines)
{
  $tmp=$_;
  if ($debug) { print " tmp is $tmp"; }
  
  ##### check for stuff in remove array ######
  
  # check if line $i is in remove array
  # - if it is jump to end of loop
  $rmv=0;
  foreach (@remove)
  {
    if ($_==$tmp) {$rmv=1;}
  }
  unless ($rmv)
  {
  ##### done checking #####
  
  
  
  
  # if lastline isn't defined, process lines
  # if lastline is defined and we haven't reached it yet
  # then we're in the middle of a table which has already
  # been processed
  unless ($lastline && $lastline > $tmp)
    {
      if ($linesout[$tmp] =~ m/\S  +?\S/ &&
	  $linesout[$tmp+1] =~ m/\S  +?\S/ )
	# matched a table
	{
	  $firstline=$tmp;
	  $x=$tmp+2;
	  # now find last line in table
	  while (($x < $lineslength) && ($linesout[$x] =~ m/\S  +?\S/)) 
	    {
	      $x=$x+1;
	    }
	  # lastline is used as a flag to let routine # know we found a table
	  # and to ignore future proclines values until
	  # we have passed $lastline
	  $lastline=$x-1;
	  if ($debug) {
	    print "matched table at lines $firstline through $lastline";
	  }
	  # submit table lines to process-table
	  # - set @tablearray to lines
	  for ($i=$firstline; $i<=$lastline; $i++) {
	    if ($debug) {
	      print "  processing table line $i";
	    }
	    $tablearray[$i-$firstline]=$linesout[$i];
	  }
	  if ($debug) {
	    print "\n\ttablearray is:\n@tablearray\n";
	  }

	  @returntable=TABLEPROCESS(@tablearray);

	  if ($debug) {
	    print "\n  returntable is: @returntable"; }
	  #  replace table lines with new table
	  # - range to replace is @linesout from $firstline to $lastline
	  $x=0;
	  for (@returntable) {
	    splice @linesout,$firstline+$x,1,$_;
	    $x++;
	  }
	  # put line #'s in @remove so no further processing occurs
	  for ($firstline .. $lastline) {
	    push (@remove,$_) ;
	  }
	}
  }  }
}

##########################
#
# CHECK FOR UNDER ITEMS In File Being Processed
#
# - should be done before processing lists since this changes length of file
# - should be done after processing bracketing
# - these items should be removed from default array
#	use $rmv to flag if there is a match or not
if ($debug) { print "\nreplacing UNDER items\n\tulength is $ulength\n\tulook array is @ulook"; }

for ( $j=0; $j < $ulength; $j++ )
{
	if ($debug) { print "\n\tchecking for $ulook[$j]\n"; }
	if ($debug) { print "\t\tchecking line "; }

	foreach(@proclines)
	{
		$tmp=$_;
		# check if line $i is in remove array
		# - if it is jump to end of loop
		$rmv=0;
		foreach (@remove)
		{
			if ($_==$tmp) {$rmv=1;}
		}
		unless ($rmv)
		{
			if ($debug) { print " $tmp "; }
# look for [under-item] with space after it...
			if ($linesout[$tmp] =~ m/^$ulook[$j]\s+?/ )	
			{
				if ($debug) { print "\n\t\t MATCHED line $i ->$linesout[$tmp]<-\n"; }
				# remove current line
				splice @linesout,$tmp,1;
				# modify previous line as appropriate
				if ($debug) { print "before chomp and substitution: under item $tmp-1 is ->$linesout[$tmp-1]<-\n"; }
				chomp ($linesout[$tmp-1]);
				if ($debug) { print "after chomp before substitution: under item $tmp-1 is ->$linesout[$tmp-1]<-\n"; }
				$linesout[$tmp-1] = "$ustart[$j]$linesout[$tmp-1]$uend[$j]\n";
				if ($debug) { print "under item $tmp-1 is ->$linesout[$tmp-1]<-\n"; }
			# since we have a match, we need to...
			#	- modify lineen to reflect loss of a line
			#	- modify @remove array to reflect loss of the line
			#	- add line number to @remove so it can be removed from
			#		def array later
				$lineen--;
				pop(@proclines);
# modify @remove to reflect loss of line $i
				$m=@remove-1;
				for $n (0 .. $m)
				{
					if ($remove[$n]>=$tmp)
					{
						if ($remove[$n]==$tmp)
						{
						# remove element from @remove
							splice @remove,$n,1;
						}
						else
						{
							$remove[$n]=$remove[$n]-1;
						}
					}
				}
				push (@remove, $tmp-1);
			}
		}
	}
}

#####
#
# replace URLS:
#
# example: replace http://aaa.bbb.ccc with 
# <a href="http://aaa.bbb.ccc">http://aaa.bbb.ccc</a>
#	1. if the file is a html file
#	2. if the link is not --already-- surrounded by <a and </a>
#
# * should expand to process ftp: gopher: news: , etc.
#
# relevant variables:
#	$suffix contains suffix from parameter file
#
#	$i is line number in file
#
if ($debug) { print "\nreplacing URLs\n\tsuffix variable is $suffix\n"; }

if ($suffix eq "html" || $suffix eq "xhtml")
{
	if ($debug) { print "\tchecking line"; }
	foreach (@proclines)
	{
		$tmp=$_;
		# check if line $i is in remove array
		# - if it is jump to end of loop
		$rmv=0;
		foreach (@remove)
		{
			#if ($debug) { print "\t\tremove array item is $_ and i is $tmp\n"; }
			if ($_==$tmp) { $rmv=1; last; }
		}
		unless ($rmv)
		{
			if ($debug) { print "  current line: $tmp"; }
			# be smart and do match but make sure it already isn't in a tag
			# ** not good code - if there are multiple URLS in line, one with <a href and one without, won't things get confused? ** need test case
			if ($linesout[$tmp] =~ m/https?:\/\/\S+\s/ && $linesout[$tmp] !~ m/<a href="https?:\/\/\S+/ )
			{
				if ($debug) { print "  matched @_"; }
				# replace match in output file
				$linesout[$tmp] =~ s/(https?:\/\/\S+)/<a href="$1">$1<\/a>/g;
				if ($debug) 
				{
				print "\tMATCHED line $tmp-- match was ->$1<-\n";
				print "\tlinesout line now: $linesout[$tmp] \n";
				}
			}
		}
	}
}

#if ($debug) { print "linesout is now @linesout\nENDEND\n"; }


##########################################################
#
# process TRIGGERED items (typically 'list' items)
#
##########################################################
#
# example: a pre-html file where
#		original line: "- listitem1"
#
#		is replaced with
#
#		<li>listitem1</li>"
#
# variables:
#
#	tmp = current line in linesout
#
#	@llines - an array of arrays - each array contains the lines in that list:
#		= ([lines in list 1]
#		[lines in list 2]
#		[lines in list 3]
#		... )
#		- note that list 1 is a nonsense array
#		- why? even when we encounter first list, we treat it as a new list and move an element forward in the array (from element 0 to element 1)
#
#
#	@ltype - replaces @list[0], array of list types (first array)
#		where list type corresponds to: o (ordered), u (unordered), n (not a list item)
#	@lnest - replaces $list[1], array of list nesting
#		where nest is defined as described below
#	@ltab - replaces $list[2], array of list tabs
#		where list tab level is 0 for nonlist
#			1 for 1 indent at start of line
#			2 for 2 indents at start of line, etc.
#	@llast - replaces $list[3], array of list last numbers
#		[list1 last number/letter (for ordered lists), list2 last number/letter (for ordered lists), ...]
#		"u" if it's an unordered list
#	@lgendj - replaces $list[4], array of j values for gend($j)
#
#
#	@list = ( 
#			[list 1 type, list 2 type, list 3 type, ...]
#			[list 1 nest level, list2 nest level, list3 nest level, ...] 
#			[list1 tab level, list2 tab level, list3 tab level, ...]
#			[list1 last number/letter (for ordered lists), list2 last number/letter (for ordered lists), ...]
#			[list1 j value for gend($j)
# 		)

#		where lines correspond to line numbers in that list
#		where last number/letter corresponds to "1" if last item matched in the ordered list was "1."
#
#		where j value is the number used to get the gend tag for this list
#
#	@match	- an array with 8 elements: 
#		first 4 elements set to 
#			the line number of the line matched
# 			the type of the line matched; 
#			the nesting level of the line matched
#			the number of space elements (tabs) preceding element
#		second row is set to the line number of the line last matched and type of line last matched
#
#			- initially set to -2 so if list item is first item in file, it still triggers
#			 start of list
#			- e.g., print @match yields "4 o 1 8" tells us last line matched was 4 and type of line was o and its nesting level was one and its tab level was 8
#
#
#
# nest = current level of nesting
#	-1 means not in a list
#	0 means in outermost list (i.e., not nested)
#	1 means a list inside of a list (the first nested list
#	etc...
#
#	how do we define a "list inside of a list"?
#		- tabbing level is greater than a previous list AND
#			- the list is in the middle of a numbered list
#
#	- why is nesting important??
#		- if there are tabbed list items but the list isn't nested, need to add CSS info
#
#	- PROBLEM: nesting won't work unless ends of lists are detected in this loop - right now ends are detected in a separate routine later since...
#
#			- problems: once ordered list started, could pick up anywhere in doc
#				and continue...
#
#				- only at end do we truly know ordered list is finished
#				(unless ended by another list beginning at same tab level, etc.)
#
#
#	CONCLUSION: any routine dependent on a correct nesting variable needs to run AFTER ends of lists have been ID'd
#
#		- new lists are triggered by:
#			- change in list type
#			- change in number of tabs preceding list item - e.g.,
#				- joe bob
#				- joe bob2
#					- joe bob2's stuff
#					- joe bob2's stuff2
#
# match =	flag used to make sure a line isn't checked twice
#		- set to 0 at start of things
#		- set to 1 if a line has been matched
#
# cl = current list number we're dealing with
#	tabs = current number of tabs
#
# @tlook	- array specifying item to look for
# tstart - item to replace with at start
# tend - item to replace with at end
# gstart - if item is grouped (item to pust at start of group)
# gend - if item is grouped (item to put at end)
# ttype - corresponding list item type for above parameters
if ($debug)
{
	print "\n\nidentifying TRIGGERED items and starting points\n";
}
@match = (-2,"n",0,-1,-2,"n",0,-1);
$nest=-1;
$cl=0;
# item at element 0 in each subarray is meaningless since it's not a list item (it corresponds to cl=0)
@ltype = ( "type" );
@lnest = ( "nest" );
@ltab = ( "tab" );
@llast = ( "last-number" );
@lgendj = ( "j value for gend-j" );
# first array is meaningless since it doesn't correspond to list items (it corresponds to cl=0)
@llines = ( [-1] );	

# run through linesout looking for matches
if ($debug) { print "\nevaluating line"; }
foreach (@proclines) # ( $i=$proclines[0]; $i <= $lineen; $i++)
{
	$tmp=$_;
	if ($debug) { print "\n\t$tmp"; }
	# check if line $tmp is in remove array
	# - if it is jump to end of loop
	$rmv=0;
	foreach (@remove)
	{
		if ($_==$tmp) {$rmv=1;}
	}
	unless ($rmv)
	{
		# check whether line $tmp matches any items in tlook
		# 	- and make sure line $tmp hasn't already been matched
		$match=0;	
		if ($debug) { print " for tlook item "; }
		for ( $j=0; (($j < $tlooklength) && ($match==0)); $j++ )
		{
			# print out current variables of interest
			if ($debug)
			{
				print " $j ($tlook[$j]) ";
			}
			# check whether line $tmp matches item $j in tlook
			#	i.e., look for equivalent of '- list item 1'
			if ($linesout[$tmp] =~ m/(^\s*?)($tlook[$j])\s+/ )		
			{
# set match so line doesn't get scanned again for other triggers
				$match=1;
				if ($debug) 
				{
					print "\n\tMATCHED line $tmp: ->$linesout[$tmp]<-";
				}
				# add line number to @remove
				push (@remove, $tmp);
				$match[4]=$match[0];
				$match[5]=$match[1];
				$match[6]=$match[2];
				$match[7]=$match[3];
	# enter current match stuff into match array
	# 	$match[0] is current line number
	#	$match[1] is current type
				$match[0]=$tmp;
				$match[1]=$ttype[$j];
	#	- can't assign nesting until we know more...
				$match[3]=length($1);	#	- tab level
	# now we have to figure out where we are in the list:
	#
	###
	#	- are we at the start of a list? if so,
	#		- is this an unordered list?
	#		- if not, then is this an ordered list?
	#		- if not, then is this not a list item (type 'n')?
	###
	# check to see if at start of an unordered list
	#	- only start of list if
	#		(1) this list item is an unordered list item
	#			AND one of...
	#		(2A) previous list item wasn't previous line
	#			OR
	#		(2B) previous list item wasn't unordered
	#			OR 
	#		(2C) previous list item was not at same nesting level

# assume we're not at the start of a list
				$start=0;
				if (
					($match[1] eq "u") && 
					( 	($tmp != $match[4]+1) || 
						("u" ne $match[5]) || 
						($match[7] != $match[3])
					)
				)
				{
				# this is the start of an unordered list - add modify line and add start tag
					$start=1;
					if ($debug)
					{
						print "\tStart of unordered list (j is $j and gstart-j is $gstart[$j]\n"; 
					}
					$nest++;
					$match[2]=$nest;
					# need to find out how many lists we have
					#	and bump up index for new list
					$cl=$#llines;
					if ($debug) { 
						print "\tcurrently have cl lists where cl is ->$cl\n"; 
						print "tlook[j] is: $tlook[$j]\n";  
						print "linesout[tmp] is: $linesout[$tmp]\n";}
					$cl++;
					# record new line info in @list
					$ltype[$cl] = $match[1];	# type
					$lnest[$cl] = $match[2];	# nest level
					$ltab[$cl] = $match[3];		# tab level
					$lgendj[$cl] = $j;			# gend index
					# add new row to @llines
					push @llines, [ $match[0] ];
					# replace match (tlook[j] is the 'trigger item') in output file
					# - no empty list items allowed:
					# $linesout[$tmp] =~ s/(^\s*?)$tlook[$j]\s+(.+)/\t$1$tstart[$j]$2$tend[$j]/;
					# - empty list items allowed:
					$linesout[$tmp] =~ s/(^\s*?)$tlook[$j]\s+(.*)/\t$1$tstart[$j]$2$tend[$j]/;
# add start of unordered list tag and make it pretty
# - here is where we also deal with situations such as
#	a list which is tabbed in 1 but it's the first list
# - however, this can't be done since nesting isn't known until
#	all list items processed
#
#	relevant variables:
#
#	tab level	$match[3]
#	nest level	$match[2]
					if ($debug) { 
						print "linesout[tmp] is: $linesout[$tmp]\n";}
					$linesout[$tmp] = "$gstart[$j]\n$linesout[$tmp]";
					for $k ( 1 .. $match[3] )
					{
						$linesout[$tmp]="\t$linesout[$tmp]";
					}
# llast is meaningless for ordered list but need to keep
# array length correct
					$llast[$cl]="u";
				}
				elsif ( $match[1] eq "u" )
	# then this is in the middle of an unordered list - set middle flag
				{
					if ($debug) { print "\t\tmiddle of unordered list\n"; }
# llast is meaningless for ordered list but need to keep
# array length correct
					$llast[$cl]="u";
				}
	###
	#
	# check to see if at ordered list
				elsif ( $match[1] eq "o" )
				{
	# this is an ordered list item
	#	- go through @list - are we in the middle of a list
	#		- find preceding ordered list item and then
	#			- compare number (sequential?)
	#			- compare tab level (same?)

	# $2 is the second match component (the first is the preceding spaces)
	#	- e.g., "1."
	#	- below extracts the number out
					$number = $2;	
					$number =~ m/(\d+)/;
					$number = $1;	
# $k defined as the number of elements in ltab array -1
					$k=@ltab-1;	
					if ($debug) { print "\t\tinitial k value is $k";}
# find last list that was 
#	- ordered (list-0-k is "o") 
# 	and
#	- at same tab level 
#	- if list found, then check if number is consecutive
	
	# k is number of lists - start evaluating with most recent list
					until ( ($k==0) ||
							(
							($ltype[$k] eq "o") && ($ltab[$k]==$match[3])
							)
						)
						{
							if ($debug) 
							{
								print "\tk is $k\n";
								print "\tmatch-3 (current tab lvl) is $match[3] and ltab[k] (tab lvl of last ordered list item) is $ltab[$k]\n";
								print "\tnumber of this list item is $number and llast[k] (list last number) is $llast[$k]\n";
							}
							$k--;
						}
	# check if we found the last list with matching tab level (if k isn't 0 we found it)
	# - if so, check if numbers are consecutive
	# - set k to 0 (indicates start of new ordered list) if they aren't
					if ( $k!=0 )
					{
						if ($debug)
						{
							print "\t\tfound last list $k at matching tab level\n"; 
						}
# are the numbers consecutive?
						if ( ($number-$llast[$k])==1) 
						{
							if ($debug) { print "\t\tthis is a consecutive item\n";}
						}
						else
						{
							if ($debug) {print "\t\tthis is start of a new list\n";}
							$k=0;
						}
					}
					if ($debug)
					{
						print "\tthis is an ordered list item\n";
						print "\tltype is @ltype\n";

					}
# if we're at k=0, then we're at the start of a list
# (not in middle of list if no preceding ordered item
#	at same tab level)
					if ( $k==0 )
					{
						$start=1;
						# need to find out how many lists we have
						# - bump up list index since at start of new list
						$cl=$#llines;
						$cl++;
			#			print "\tnow starting list cl where is $cl\n";
						# record new line info in @list
						# 	- at this point we can assign nesting
						$match[2]=$nest;
						$ltype[$cl] = $match[1];	# type
						$lnest[$cl] = $match[2];
						$ltab[$cl] = $match[3];	
						$lgendj[$cl] = $j;
						# add new row to @llines
						push @llines, [ $match[0] ];
						# replace match in output file
						if ($debug) { print "\t-replace match-\n"; }
						$linesout[$tmp] =~ s/(^\s*?)$tlook[$j]\s+(.*)/\t$1$tstart[$j]$2$tend[$j]/;

# add start of ordered list tag and make it pretty
# - here is where we would deal with situations such as
#	a list which is tabbed in 1 but it's the first list
# - however, we can't deal with these situations, because nesting isn't
#	established until all list items have been identified
#
#	relevant variables:
#	
#	tab level	$match[3]	$ltype[$cl]
#	nest level	$match[2]

	# add start of list tag and make it pretty
						$linesout[$tmp] = "$gstart[$j]\n$linesout[$tmp]";
						if ($debug)
						{
							print "\n\tat START of a list (k=0)\n";
							print "\t\tlinesout line $tmp is: $linesout[$tmp] \n";
							print "\t\tnest is $nest\n";
							print "\t\tllist is @llines\n";
							print "arrays:\n\tltype is @ltype\n";
							print "\tlnest is @lnest\n";
							print "\tltab is @ltab\n";
							print "\tllast is @llast\n";
							print "\tlgendj is @lgendj\n";
						}
					}
	# not at start of ordered list so
					else
					{
						if ($debug) { print "\tin middle or end of ordered list..."; }
	# so current list we're dealing with is $k
						$cl=$k;
					}
	# regardless of location in list need to update last number in list
					$llast[$cl] = $number;	# list item number (for ordered lists)
				}
	# at this point, check if item is type 'n'
				elsif ( $match[1] eq 'n' )
				{
					if ($debug) 
					{ 
						print "\tnot a list item\ntstart: ->$tstart[$j]<-\ntend:->$tend[$j]<-\ntlook:->$tlook[$j]<-";
					}
					# so just do substitution
					#
					# replace match in output file
					$linesout[$tmp] =~ s/(^\s*?)$tlook[$j]/$1$tstart[$j]/;
					$linesout[$tmp] =~ s/(.*?)\s*?$/$1$tend[$j]/;
					for $k ( 1 .. $match[3] )
					{
						$linesout[$tmp]="\t$linesout[$tmp]";
					}
				}
	# add new column item to @llines (do this whether in middle or at end of ordered or unordered list)
			#	print "\tstart is $start\n";
				if ( ($start != 1) && ($match[1] ne 'n'))
				{
					push @{ $llines[$cl] }, $tmp;	
					if ($debug)
					{
						print "\tadded line to existing row...";
						# printing a 'multidimensional' array is a hassle in perl
						for $q ( 0 .. $#llines ) 
						{
							print "\tllines element $q is [ @{$llines[$q]} ],\n";
						}
					}
					# replace match in output file
					if ($debug) { print "\t*replacing match*\n"; }
					$linesout[$tmp] =~ s/(^\s*?)$tlook[$j]\s+(.*)/\t$1$tstart[$j]$2$tend[$j]/;
				}
			}
		}
	}
}
###
#
# deal with where lists end 
#	- this routine does things --after-- the file has been totally scanned
#	- disadvantage: keeping track of nesting with $nest is meaningless
#	- advantage:
#		an outer numbered list can't be confirmed as ended until 
#		the whole file has been scanned anyways...
#
#	order of process:
#
#	- evaluate outermost lists first - identify where they end
#		- use tab levels to identify these lists
#
# 	variables:
#
#	lnum:	number of lists to consider
#
#	lline:	last line in whatever list we're dealing with
#		- defined using last element in llines array
#		- defined as last line in list
#		- this is the line to which end of list tag will be added
#
#	@llines:
#		array of arrays
#		- array 0 contains lines in list 0
#		- array 1 contains lines in list 1
#		etc.
#
#	tmax:	maximum tab level
#	tmin:	minimum tab level
#
#	@tab	contains all tab levels associated with last item in a list
#		- one item for each list item
#
#		e.g.,
#		- big list 1
#			- big list 2
#					- big list 3
#			- big list 4
#
#		would be described by @tab = [0,1,3,1]

# identify list ends using tab levels
#
# - what is range of tab levels?
#
#	@ltab is array with tab level of each list

if ($debug) { print "\nIdentifying list ends\n"; }

$lnum=$#llines;
$tmax=-1;	# set at obscenely low value and work up
$tmin=5000;	# set at obscenely high value and work down
for $i (0 .. $lnum)
{
	if ( $ltab[$i] =~ /\d/ )
	{
		if ( $ltab[$i] > $tmax )
		{ $tmax=$ltab[$i]; }
		if ( $ltab[$i] < $tmin )
		{ $tmin=$ltab[$i]; }
	}
}

if ($debug)
{
	print "\nllines array is:\n";
	for $q ( 0 .. $#llines ) 
	{
		print "\t llines element $q is [ @{$llines[$q]} ],\n";
	}
	print "\tNumber of lists is $lnum\n";
	print "\trange of tab values is min: $tmin to max: $tmax\n";
}

#
# find last line in list with given tab level and add end of list tag
#
for $i ( $tmin .. $tmax )
  {
    if ($debug) 
      {
	print "Evaluating tab level $i\n";
	print "--------------------\n";
      }
    # cycle through the lists
    for $j ( 0 .. $lnum )
      {
	# is this one of the lists with this tab level
	if ( $ltab[$j] =~ /\d/ && $ltab[$j] == $i )
	  {
	    # then find last line in list and 
	    #	- add end of list tag
	    $k=$#{$llines[$j]};
	    $lline=$llines[$j][$k];
	    if ($debug)
	      {
		print "\tEvaluating list $j (tab level $ltab[$j])\n";
		print "\t\tlist $j has MATCH tab level $i\n";
		print "\t\t\tlast line in this list: $lline\n";
	      }
	    ############
	    #
	    # at this point, need to see if a subsequent list is tagged on to last list item
	    #	- is next line item a list item? (i.e., no CR)
	    # problem with this criterion: list 1 ends after membrane (i.e. end is in wrong spot for)
	    #- types
	    #	1. membrane
	    #		signal
	    #	2. nuclear
	    #
	    # solution: rely on CSS more heavily and tab levels rather than nesting -- use nesting only for ORDERED lists
	    #
	    #	- is next item at a higher tab level?
	    #		@ltab has tab level of each list
	    #	- if so, move point at which end will be inserted
	    #
	    # is next item ($lline+1) in llines? (scan llines)
	    #	- do loop again if
	    #		(1) found a match AND
	    #		(2) not at end of file
	    #
	    #	i.e., until $lline isn't bumped up 1 (if it is $lline = y at end of day)
	    #
	    # lnum
	    #
	    # i	= current tab level being evaluated
	    # y	= next line #
	    # lnum 	= number of lists
	    # z	= number of lines in list x
	    #
	    # make sure we "matched" to start things off
	    #
	    # modify last line in this list and make it pretty
	    #
	    #	- first add a CR after list item tag since we need a line for the new gend
	    #	- then put the tabs in (we do highest level last)
	    #
	    if ($debug)
	      {
		print "\ttab level of top-most list is i $i\n";
		print "\tprettify the line ->$linesout[$lline]<-\n";
	      }
	    # special case: if i is 0 and we're at EOF, we still need
	    # to drop to next line for aesthetics unless line already
	    # ends with \n
	    #	- so add a line for gend right now
	    #	- q comes from
	    $q=$lgendj[$j];
	    # look for the last item tag (e.g., </li>)
	    # and put a \n after it
	    $v=$tend[$q];
	    # this is problematic if end tag isn't </li> but simply a space char
	    # $linesout[$lline] =~ s/$v[\t\r]*/$v\n/;
	    $linesout[$lline] =~ s/$v[\s]*?$/$v\n/;
	    if ($debug)
	      {	
		print "looking for v ->$v<-\n";	
		print "the line is now ->$linesout[$lline]<-";
	      }
	    # match <end-of-list-tag>\n
	    #	- list is x-1?
	    #	
	    #	$string =~ s/regex/replacement/g;
	    for $lline ( 1 .. $i )				
	      {
		if ( $linesout[$lline] =~ m/$v\n/ )
		  { 
		    # print "matched \\n\n";
		    $linesout[$lline] =~ s/$v\n/$v\n\t/; 
		  }
		else
		  { 
		    $linesout[$lline]="$linesout[$lline]\t"; 
		  }
	      }
	    #
	    #	*** $q\n won't match if there is extra space at
	    #	end of line to begin with ***
	    
	    # need to get gend index from @list
	    # - insert it after most recent run of tabs
	    $linesout[$lline] =~ s/($v\n\t*)/$1$gend[$q]/;
	    if ($debug) { print "\tmodified line is ->$linesout[$lline]<-\n"; }
	  }
      }
  }

###############################################
#
# list processing: identify if tab and nesting does't match
#	- if not, add trigger
#
#
# variables:
#
# $lnum - number of lists
#
# @llines:
#	array of arrays
#	- array 0 contains lines in list 0 (this is always just -1)
#	- array 1 contains lines in list 1 (the first real list)
#	- array 2 contains lines in list 2 (the second real list)
#	etc.
#
# @list replaced by:
#	@ltype
#	@lnest
#	@ltab
#	@llast
#	@lgendj
#
# @list: SEE ABOVE
#	array of arrays:
#	 
#	@list[0] is	[list 1 type, list 2 type, list 3 type, ...]
#	@list[1] is	[list 1 nest level, list2 nest level, list3 nest level, ...] 
#	@list[2] is	[list1 tab level, list2 tab level, list3 tab level, ...]
#	@list[3] is	[list1 last number/letter (for ordered lists), list2 last number/letter (for ordered lists), ...]
#	@list[4] is	[list1 j value for gend($j)
#
#	where nest level is:
#		-1 not in a list
#		0 outermost list
#		1 list inside of a 0 level list
#		...
#
# $nest - nest level of list
#
# $n - list number we are on
#
#

# flow of this segment of program:
#	- add correct nesting information to array with nesting data
#	- for each unnested list, check if tab matches nest
#		- add tt items at start/end of list if tab doesn't match
#	* note: simplest to do it with unnested lists since a different approach 
#	(using CSS info in <ol> rather than adding <div> in front of it) would
#	be required -- hard to know which approach is better -- need to decide
#	how otl is going to work...

if ($debug)
{
	print "\nllines array is:\n";
	for $q ( 0 .. $#llines ) 
	{
		print "\t llines element $q is [ @{$llines[$q]} ],\n";
	}
	print "\n\nIdentifying ends of lists and dealing with nesting\n\tNumber of lists: $lnum\n\trange of tab values is min: $tmin to max: $tmax\n";
}

$nest=0;	# first list has $nest defined as 0
$n=1;		# first list is at position 1 in @llines
# let's identify list nesting
#
# variables
#	tlli = last line in this list
#	nlfi = first line in next list
#	nflag = 0 unless nest list is nested
#
# flow of program
#	set all nesting values to 0
#	for each list n, check if subsequent list is nested relative to this list
#		- if it is, bump its nesting value up

# set all nesting values to 0
# - assume each list isn't nested
for ($i=1; $i <= $lnum; $i++)
{
	$lnest[$i]=0;
}
# check if next list nested relative to other lists
#
# - for each list, compare against all other lists
#	$i is current list number
#	@thislist has array of lines in this list
#
# e.g., 1. bob
#		1. jill
#	2. bob
#		1. sue
#	3. bob
for ($i = 1; $i < $lnum; $i++)
{
	@thislist=@{$llines[$i]};
	if ($debug) { 
		print "\nList $i lines are @thislist - comparing with "; }
	$tlli=pop(@thislist);

	for ($j=$i+1; $j<=$lnum; $j++)
	{
		# is next list inside this one?
		@nextlist=@{$llines[$j]};
		if ($debug) { print "\n\tlist $j - list $j lines are @nextlist\n";
			}
		$nlli=shift(@nextlist);
		if ($debug) { 
			print "\t\ttlli is $tlli and nlli is $nlli";
			}
		if ($nlli > $tlli)
		{
			# then the next list isn't nested relative to current list
			# so don't change nesting level
			if ($debug) { print "\n\tnext list $j not listed relative to list $i\n"; }
		}
		else
		{
			# the next list is nested relative to this one
			if ($debug) { print "\n\tlist $j is nested relative to $i\n"; }
			$lnest[$j]++;
		}
	}
}

# now modify the start and end of the list if tab and nest don't match...
#
#	use parameter file trigger-tab lines to determine start and end tags
#	to use when nest/tab levels aren't identical
#
#	- allows CSS margin-left tag to be added if list is indented
#		but not nested
#
#
# relevant variables:
#	ttlength - length of ttstart and ttend arrays
#	@ttstart - array with start tags for each tab/nest mismatch level
#	@ttend - array with end tags for each tab/nest mismatch level
#
# 	$lnum - number of lists
#
# 	@llines:
#		array of arrays
#		- array 0 contains lines in list 0 (this is always just -1)
#		- array 1 contains lines in list 1 (the first real list)
#		- array 2 contains lines in list 2 (the second real list)
#		etc.
#
#	@ltype
#	@lnest
#		where nest level is:
#			-1 not in a list
#			0 outermost list
#			1 list inside of a 0 level list
#			et cetera...
#	@ltab
#	@llast
#	@lgendj
#
#

# loop through lists looking for mismatches
if ($debug)
{
	print "\n\tChecking for tab/nest MISMATCHES\n";
	print "arrays:\n\tltype is @ltype\n";
	print "\tlnest is @lnest\n\tltab is @ltab\n\tllast is @llast\n\tlgendj is @lgendj\n";
}

for ($i = 1; $i <= $lnum; $i++)
{
  # unless tab level doesn't match nest level, move on...
  unless ($lnest[$i]==$ltab[$i])
    {
      # since there's a mismatch, insert ttstart and ttend
      
      # put tab level in easy-to-use variable
      $j=$ltab[$i];	
      # get last line # and first line # in list
      # ** careful! what if list is only one item long?
      #	-- make sure to replace item in list
      $lastline=pop( @{ $llines[$i] } );
      push (@{ $llines[$i] }, $lastline );
      $firstline=shift( @{ $llines[$i] });
      splice (@{ $llines[$i] },0,0,$firstline);
      if ($debug)
	{
	  print "\n\nTAB/NEST MISMATCH at list $i";
	  print "\n\tfirst line is $firstline and last line is $lastline";
	  print "\n\told lastline: $linesout[$lastline]"; 
	  print "\n\told firstline: $linesout[$firstline]"; 
	}
      if ($j > $ttlength)
	{
	  # just use last ttlength line for substitution
	  $k=$ttlength-1;
	  
	  $linesout[$firstline] = "$ttstart[$k]\n$linesout[$firstline]";
	  $linesout[$lastline] = "$linesout[$lastline]$ttend[$k]\n";
	  if ($debug) { 
	    print "\n\tused last ttend element: ttend-$k is $ttend[$k]";
	    print "\n\tnew lastline: $linesout[$lastline]";
	    print "\n\tnew firstline: $linesout[$firstline]"; 
	  }
	}
      else
	{
	  # use correct ttlength line
	  $k=$j-1;
	  $linesout[$lastline] = "$linesout[$lastline]$ttend[$k]\n";
	  $linesout[$firstline] = "$ttstart[$k]\n$linesout[$firstline]";
	  if ($debug) { 
	    print "\n\tused last ttend element: ttend-$k is $ttend[$k]";
	    print "\n\tnew lastline: $linesout[$lastline]";
	    print "\n\tnew firstline: $linesout[$firstline]"; 
	  }
	}
      # prettify: add tabs
      for (1 .. $j)
	{
	  $linesout[$lastline] =~ s/$ttend[$k]/\t$ttend[$k]/;
	  $linesout[$lastline] =~ s/([^\n]*\n[^\n]*\n)$/\t$1/;
	  $linesout[$firstline]="\t$linesout[$firstline]";
	  $linesout[$firstline] =~ s/(^[^\n]*)\n/$1\n\t/;
	}
    }
}

########
#
# substitute blank lines with relevant tag and then remove from further processing
#
#	relevant variables:
#
#	$blank - contains blank string specified in parameter file
if ($debug) { print "checking for BLANK lines\n"; }
foreach(@proclines)
{
	$tmp=$_;
	# check if line $i is in remove array
	# - if it is jump to end of loop
	$rmv=0;
	foreach (@remove)
	{
		if ($_==$tmp) {$rmv=1;}
	}
	unless ($rmv)
	{
		if ($debug) { print "\tchecking line $tmp ->$linesout[$tmp]<- for blank\n"; }
		if ($linesout[$tmp] !~ m/\S/ )	
		{
			# special cases where we want to ignore blank:
			# 1. if blank line is directly under <hN>...</hN> line

			if ($linesout[$tmp-1] !~ m/<\/h[0-9]>/ )
			{

			# comment line below out to just not process blank lines
			if ($debug) { print "\t\tmatch!\n"; }
			$linesout[$tmp] = "$blank\n\n";

			}
			push (@remove, $tmp);

		}
	}
}

#
# ESTABLISH ARRAY FOR NOTING LINES WHICH SHOULD BE PROCESSED AS DEFAULTS
#
#	use @def array to define lines which are default lines
#
#	- this array contains start and stop line numbers of sets of default lines: e.g.,
#		0 1 4 5 9 12 14 14
#
#		indicates lines 0 and 1 are a group 4 to 5 are a group and 9 to 12 are a group and line 14 is a group
#
#		** def array processing needs to be modified to exclude any line which begins with a tag
#			- problem: what if line begins with <bold>joe bob</bold>? - it could still be
#				a list item
#			- should note under items to avoid this

@def=();

# first set up default array to contain all possible default lines
#	- we'll weed out the "undesirables" later
$plength=@proclines;
if ($debug)
{
	print "proclines is @proclines\n";
	print "def array is @def\n";
	print "plength is $plength\n";
}
if ($plength > 0) { $def[0]=$proclines[0]; }
if ($plength > 1)
{
	$def[1]=$proclines[$plength-1];
}
# if there is only one default line then...
else
{
	$def[1]=$proclines[0];
}
$deflength=2;

if ($debug) { print "\n\nInitialized default array: @def\n"; }

###
#
# certain types of lines are removed from default array def:
#
#	list items
#	under items
#	lines that start with html-looking tags
#             ******** when/where are these added???
#	variables:
#
#	@remove contains lines to remove
#	@def contains default lines
#
#

$removelen=@remove-1;

if ($debug)
{
	print "\nREMOVING remove items from def array\n";
	print "\tremove is @remove\n";
	print "\tdef array is @def\n";
	print "\tprocessing remove from def array\n";
	print "\t remove length is $removelen\n"; 
}

for $i (0 .. $removelen)
{
	# def is looked at 2 at a time since formatted as a series of ranges
	for ( $k=0; $k < ($deflength/2); $k++)
	{
		$x=$k*2;	
		$a=$def[$x+1];
		# is remove value between the current array values?
		if ( ( $remove[$i] >= $def[$x]) && ( $remove[$i] <= $def[$x+1]) )
		{
			#
			# if current line matches both ends, need to just delete the line
			if ( ( $remove[$i] == $def[$x] ) && ( $remove[$i] == $def[$x+1]) )	
			{	
				# not at end so 
				splice @def,$x,2;
				$deflength=$deflength-2;
			}
			else
			{
				# if current line matches one of the ends
				#
				if ( ( $remove[$i] == $def[$x] ) || ( $remove[$i] == $def[$x+1]) )
				{
					if ( $remove[$i] == $def[$x] )
					{
						$def[$x]=$def[$x]+1;
					}
					else
					{
						$def[$x+1]=$def[$x+1]-1;
					}
				}
				else
				{
					# 'remove' element by adding elements "on both sides"
					splice @def,$x+1,0,$remove[$i]-1;
					# def array just got one longer so next insert has to go after 1st insert
					#	- insert $i+1 unless $i+1 is past end of lines
					if ($remove[$i]+1 > $lineen ) 
					{
					    splice @def,$x+2,0,$remove[$i];
					}
					else
					{
					    splice @def,$x+2,0,$remove[$i]+1; 
					}
					$deflength=$deflength+2;
				}
			}
		}
	}
}

# if ($debug) { $x=$linesout[0]; print "linesout 0: ->$x<-"; }

###########################################################
#
# apply defaults last
#
#	- first figure out if there was even a default value given in array
#
# 	- then determine groupings by analyzing def array
#		and storing first and last lines of groups in
#			dfin array
#
#	variables:
#
#	dlength 	- number of items in dstart or in dend
#	dstart[ ]	- array holding default start items
#
#	@def - array containing line ranges to be treated as defaults
#	@dfin - first and last lines of ?
#
#	pns - flag to put default start tag in at start of --next-- line (if not 1, then don't put tag in)
#	ps - flag to put default start tag in at start of --current--line
#	pe - flag to put default end tag in at end of --current-- line (if not 1, then don't put tag in)
#
#	ln - line number in linesout that we're dealing with from @def array
#	len = number ot tabs in line

if ($debug)
{
	print "\napplying DEFAULTS\n\tdlength is $dlength\n\tdeflength is $deflength\n\n";
}

# don't bother applying defaults if there aren't any
if ($dlength != 0)
{
	#
	# apply standard tags to lines designated in def array
	#
	for ($i=0; $i < $deflength; $i=$i+2)
	{
		if ($debug) {
			$a=$def[$i];
			$b=$def[$i+1];
			print "\tprocessing line range $a to $b for default tags\n"; }
		$pns=1;
		$pe=1;
		$ps=1;
		for ($j=$def[$i]; $j<=$def[$i+1]; $j++)
		{
			unless ($pns) { $ps=0; }
			if ($debug) { print "applying default start to line ln = $j of linesout -->$linesout[$j]<-- \n"; print "linesout is ->@linesout<-"; }

			# figure out tabbing level to determine which default tag to use
			# match all whitespace characters at the start of the line
			$linesout[$j] =~ m/(^\s*?)\S/;

			# put # of tabs present in $len
			$len=length($1);

			# ensure that if len exceeds length of dstart array that we use last value
			# in array - otherwise we get uninitialized value errors all over the place
			#
			$x=$len;
			if ( $x > $dlength-1 )
			{ $x=$dlength-1; }

			# check for grouping character
			# - if there's a "@" (formerly \ ) at end of line, omit dend for this line and dstart for next line
			# - unless next line isn't a default line
			# v: use as $linesout[$j] and then replace at end
			$v=$linesout[$j];
			$nextdline=$def[$i+1];
			$nextline=$j+1;
			if ($debug)
			{
				print "\t\tprocessing linesout line $j: ->$v<-\n";
				print "\t\tnextdline is $nextdline and nextline is $nextline\n";
			}
			if ($v =~ m/@\s+?$/ && ($nextdline==$nextline))
			{
				if ($debug)
				{
					print "\t\t\tcontinue character matched at line $j:\n\t->$v<-\n";
				}
				# delete \ character from end of line
				$v =~ s/@\s+?$/ /;
				if ($debug) { print"linesout now ->$v<-\n";}
				$pns=0;	# don't put default start tag on next line
				$pe=0;	# don't put default end tag on this line
			}
			elsif ($v =~ m/@\s+?$/)
			{
				if ($debug)
				{ print "\t\t\tcont char matched but last line\n";}
				$v =~ s/@\s+?$/ /;
				$pe=1;
			}
			else
			{
			  if ($debug) { print "\t\t\tcont char not matched\n"; }
				chomp ($v);
				$pns=1;
				$pe=1;
			}
			if ($debug)
			{ print "\t\t\tps is $ps and pe is $pe\n"; }
			# reassign v to linesout w/suitable modifications
			if ($ps)
			{
				$linesout[$j]="$dstart[$x]$v"; 
			}
			else
			{ 
				$linesout[$j] = $v; 
			}
			$v=$linesout[$j];
			if ($pe) 
			{
				$linesout[$j]="$v$dend[$x]\n"; 
			}
			if ($debug) { print"linesout now ->$linesout[$j]<-\n";}		}
	}
}
elsif($debug)
{
	print "no default lines to process\n";
}

#
# ADD HEADER IF WE HAVE ONE
#	@headp - holds head text from parameter file
#
if ($hprocess =~ "n")
{
	if ($debug) { print "No header added\n"; }
}
else
{
	# insert @head at front of @linesout
	unshift(@linesout,@headp);
}

#
# customize title attribute <title>XXX</title> in header if present
#	hl=length of header array
#	notitle=flag set by command line option --nt (not yet implemented)
#	title=content of first line of document

# if using a preexisting <head> element, don't add title
$notitle=0;
if ($hd[0]!=-1 && $hd[1]!=-1)
{
	$notitle=1;
}
unless ($notitle == 1)
{
	$hl=@headp;
	for ($i=0; $i < $hl; $i=$i+1)
	{
		if ($linesout[$i] =~ m/<title>(.*?)<\/title>/ )
		{
			# replace match in output file
			if ($debug) { print "Adding title to header line $i ->$linesout[$i]<-\n"; }
			$linesout[$i] =~ s/(^.*?<title>).*?(<\/title>.*?)/$1$title$2/;
			if ($debug) { print "\tmodified line: ->$linesout[$i]<-\n"; }
		}
	}
}

# regardless of whether we added <head></head> or it was already present, need to add <body> element below it
# look for <head tag
$lineslength=@linesout;			# reset lineslength
$headend=0;
if ($debug) { print "looking for </head>: checking line "; }
for ( $i=0; $i < $lineslength; $i++)
{
	if ($linesout[$i] =~ m/\<\/head/)	
	{
		if ($debug)
		{
			print "MATCHED linesout line $linesout[$i] \n";
		}
		# mark location
		$headend = $i;
		last;
	}
	$headend=0;
}

# ** this kludge shouldn't be here... if the parameter file is written
# correctly, it should have <body> present, shouldn't it?... **
# check to see if <body> present at end of <head> </head> section
# add <body> unless already present
unless ($linesout[$headend+1] =~ m/\<body/) # || $linesout[$headend] =~ m/\<body/)	
{
  # this is nonsensical unless it's html
  if ( $suffix eq "html" )
    {
      splice @linesout,$headend+1,0,"<body>\n";
    }
}

#
# ADD FOOTER IF WE HAVE ONE
#
push(@linesout,@foot);

################################
#
# OUTPUT THE OUTPUT FILE
#
################################

# match the name of the file and preserve any directory structure
#	- should process "./index.html" properly
#	- should also process "./index" properly
#
#	--> need to pull off suffix only if there is one present
if ($debug) { print "suffix is : $suffix"; }
if ($suffix)
{
	# there any reason for suffix test below
	# when there is a suffix test above?
	if ( $filein =~ /(.+)\..*/ && $suffix)
	{
		$fileout="$1.$suffix";
	}
	else
	{
		$fileout="$filein.$suffix";
	}
}
if ($debug)
{
	print "filein is ->$filein<-\n";
	print "new filename is $fileout\n"; 
}

# check if filein has .out suffix and, if so, delete it
# since it is a temporary file
if ($suffix)
{ 
		if ( $filein =~ /(.+)\.out/ )
		{
			unlink($filein);
		}
}


# check if fileout is already present
#	ow - variable indicating to overwrite header
$owheader = "n";
if (-e "$fileout")
{
  if ( $assumeyes == 0 )
    { 
      print "\nOverwrite file $fileout [default: n]? ";
      $owheader=<STDIN>;
      chomp ( $owheader );
    }
  else
    {
      $owheader="y"
    }
  # remove \n at last line
  unless ($owheader =~ "y")
    {
      print "File not overwritten\n";
      $nooverwrite=1;
    }
}


if ($debug) { print "fileout: $fileout\n"; }

if ($nooverwrite==0)
{
	open(FOUT, ">$fileout");
	flock(FOUT,2);
	# DO NOT COMMENT LINE BELOW OUT :)
	print FOUT @linesout;
	if ($debug) { print "Output file is ->$fileout<-\n"; }
	close (FOUT);
}



############### END PROCESS SUBROUTINE #############
}


####################################################
#
# PROCESSTABLE SUBROUTINE
# - expects @tablearray to be defined as an array
#   containing one or more consecutive lines containing
#   two or more spaces after the first non-whitespace character in the
#   line
#   and where the spaces are succeeded by another non-whitespace character
# - processes @tablearray assuming a fixed-width font
#   reveals intent with respect to spacing
#   
sub TABLEPROCESS
{
  # variables to pass on from main:
  #   debug

  # initialize private variables
  my $dummy;
  my $firstitem;
  my $matchnumber;
  my $numberofcolumns=0;
  my $numberofrows;
  my $num_spacings;
  my @returntable=();
  my @rowitems=();		#  hold td items for the row
  my $thisrow;
  my @table = @_;
  my @tablebak = ();
  
if ($debug) {
    print "\nTABLEPROCESS subroutine with table array:\n@table\n";
  }
  # determine number of rows
  $numberofrows = @table;

  # make a backup copy since we use \s on original table to get count data
  @tablebak=@table;
  # determine number of columns (** not necessary at this point... **)
  for (@table) {
    # copy to dummy variable since count needs substitution form of things ( s/ / /)
    if ($debug) { print "\n\tanalyzing row: -->$_<--"; }
    $num_spacings = $_ =~ s/\S  +?\S//g; # number of columns is this # + 1
    if ($debug) {
      print "\tnum_spacings is $num_spacings\n";
    }
    if (($num_spacings+1) > $numberofcolumns) {
      $numberofcolumns=$num_spacings+1;
    }
  }
  #
  # now build table
  # - keep # of array elements equal to # of elements being replaced
  $returntable[0] = "<table><tbody>\n";
  # construct each row
  $i=0;
  if ($debug) { print "\n\tnow constructing rows:";
	      print "\n\ttablebak array is: @tablebak"; }
  # use original copy since other copy modified by s/ / /
  for (@tablebak) {
    # clear things out for a new row
    # get the new row
    if ($debug) { print "\t\tcurrent row: $_\n"; }
    # get first item on line (first non-whitespace string)
    $_ =~ m/\s*([\S ]+?)  +/;
    $firstitem = $1;
    # put it in table
    $returntable[$i]="$returntable[$i]\t<tr><td>$firstitem</td>";
    # get remaining items (items preceded by at least two spaces)
    # - succeeded by two spaces
    while ($_ =~   m/  +(\S[\S ]*?)  +/ )
      {
	# should just put it in row:
	$returntable[$i]="$returntable[$i]<td>$1</td>";
	# get rid of first match so we can move to next match
	$_ =~ s/(  +\S[\S ]*?)  +//;
      }
    # get last item in row
    if ($_ =~ m/  +(\S[\S ]*?)$/)
      {
	# should just put it in row:
	$returntable[$i]="$returntable[$i]<td>$1</td>";
      }
    # add end of row html
    $returntable[$i]="$returntable[$i]</tr>\n";
    if ($debug) { print "\trow: $returntable[$i]\n"; }
    $i++;
  }
  $i--;
  $returntable[$i]="$returntable[$i]</tbody></table>\n";
# return the newly constructed table
  return @returntable;
    ##############end process-table subroutine##############
}

  # default format is xhtml
  # - utf8 is used since perl tries to use utf8 - this eliminates many 'problem character' issues
  # - a stylesheet link is included but otl doesn't look for stylesheet yet and correct path appropriately
  #

__DATA__
##SUB##
[hH]\+	H<sup>+</sup>
[hH]2[Oo]	H<sub>2</sub>O
[nN][Aa][Dd]\+	NAD<sup>+</sup>
[mM]g2\+	Mg<sup>2+</sup>
[mM][Gg]\+\+	Mg<sup>++</sup>
[fF]e2\+	Fe<sup>2+</sup>
[fF]e3\+	Fe<sup>3+</sup>
[cC]a2\+	Ca<sup>2+</sup>
[cC]a\+\+	Ca<sup>++</sup>
[cC][oO]2	CO<sub>2</sub>oO]2
[Oo]2	O<sub>2</sub>
([-+]*[0123456789]+?)oC	$1<sup>o</sup>C
##ENDSUB##

suffix html

under	====+	|<h1>| |</h1>|
under	-=-=	|<h2>| |</h2>|
under	----+	|<h2>| |</h2>|
under	~~~~+	|<h3>| |</h3>|
under \^\^\^\^+ |<h4>| |</h4>|

head
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN" "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<title>TITLE</title>
	<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>
head

foot
</body>
</html>
foot

|<p>| |</p>|
|<p style="margin-left: 2.5em;">| |</p>|
|<p style="margin-left: 4em;">| |</p>|
|<p style="margin-left: 5.5em;">| |</p>|
|<p style="margin-left: 7.5em;">| |</p>|

bracket	\*\*	|<span style="font-weight: bold;">|	|</span>|
bracket	;;	|<span style="font-style: italic;">|	|</span>|
bracket	__	|<span style="text-decoration: underline;">|	|</span>|
bracket \[\[   |<span style="font-family: monospace;">| |</span>|

start - |<li>| |</li>|	|<ul style="list-style-type: disc">| |</ul>| u
start [0-9]+\. |<li>| |</li>| |<ol>| |</ol>|	o
start = |<li>| |</li>| |<ul style="list-style-type: none">| |</ul>| u

trigger-tab |<div style="margin-left: 2.5em;">| |</div>|
trigger-tab |<div style="margin-left: 4em;">| |</div>|

blank |<div style="margin-top: 0.35em; margin-bottom: 0.35em; padding: 0em;"></div>|

preequivalent |<pre>| |</pre>|
