#!/usr/bin/perl
#
# Copyright 1994-2002 Werner Almesberger.
# All rights reserved.
#
# This program is distributed under version 2 of the General Public
# License. See http://www.gnu.org/copyleft/ for details.
#
#
$VERSION = "26-MAY-2002";
#
#-----------------------------------------------------------------------------
#
# Known bugs:
#
#   Usually doesn't check for prepended backslashes, e.g. things like
#   \\begin{verbatim} would be processed incorrectly.
#
#   Tokenization should be done once at the beginning, not on the fly
#   with cleanup and check procedures at the end of each step.
#
#-----------------------------------------------------------------------------
#
# Style parameters:
#
#  $WIDTH		line width (characters)
#  $LENGTH		page length, without header and footer (lines)
#  $numbered_sections	number sections (0/1)
#  $indent_sections	indent sections according to nesting depth (0/1)
#  $paged		fill to page size, add header and footer (0/1)
#  $center_figures	center figures (from .asc files)

# default settings (plain)
$WIDTH = 75;

if ($ARGV[0] eq "-id") {
    pop(@ARGV);
    $WIDTH = 72;
    $LENGTH = 58;
    $numbered_sections = 1;
    $indent_sections = 1;
    $paged = 1;
}
elsif ($ARGV[0] eq "-plain") {
    pop(@ARGV);
    # use default
}
elsif ($ARGV[0] eq "-V") {
    print STDERR "version $VERSION\n";
    exit(0);
}
elsif ($ARGV[0] =~ /^-/) {
    die "unrecognized option \"$ARGV[0]\"";
}
#
# default macros
#
$m{"\\\\ldots"} = "...";
#
# read the file
#
print STDERR "[".length($t)."] Reading the file\n";
$/ = "\000";
$t = "\n".<>."\n";
$t =~ s/%%end\b.*//s;
if ($t =~ /^\n%%format\s+([^\n]*)\n/) {
    $t = "\n".$';
    for (split(/\s+/,$1)) {
	if ($_ =~ /^width=(\d+)$/) {
	    $WIDTH = $1;
	}
	elsif ($_ =~ /^length=(\d+)$/) {
	    $LENGTH = $1;
	}
	elsif ($_ =~ /^([-+])numbered_sections$/) {
	    $numbered_sections = $1 eq "+";
	}
	elsif ($_ =~ /^([-+])indent_sections$/) {
	    $indent_sections = $1 eq "+";
	}
	elsif ($_ =~ /^([-+])paged$/) {
	    $paged = $1 eq "+";
	}
	else {
	    die "unrecognized %%format option \"$_\"";
	}
    }
}
#
# universal markers
#
$N = "\000";	# non-character
$X = "\007";	# generic marker
$Y = "\010";	# another generic marker
$Z = "\011";	# yet another generic marker
$B = "\001";	# begin
$E = "\002";	# end
$BS = "\013";	# second begin
$ES = "\014";	# second end
$CO = "\003";	# curly open
$CC = "\004";	# curly close
#
# begin/endfigure marker
#
$BF = "\015";	# %%beginfigure
$EF = "\016";	# %%endfigure
#
# commands to the output formatter
#
$SI = "\020";	# increase indentation by one
$SO = "\021";	# decrease indentation by one
$B1 = "\022";	# one blank line
$B2 = "\023";	# two blank lines
$TB = "\024";	# begin transaction
$TE = "\025";	# end transaction
$VL = "\026";	# verbatim line

sub cleanse
{
    local ($s) = @_;

    $s =~ tr/$B$E$X$Y/BEXY/;
    $s =~ tr/\000-\037\177-\377//d;
    return $s;
}

sub die
{
    local ($msg,$before,$ctx,$after) = @_;

    print STDERR "ERROR: $msg\n";
    print STDERR "----- context ".("-" x 63)."\n";
    print STDERR "| ".substr(&cleanse($before),-75)."\n";
    print STDERR "> ".substr(&cleanse($ctx),0,75)."\n";
    print STDERR "| ".substr(&cleanse($after),0,75)."\n";
    print STDERR ("-" x 77)."\n";
    exit(1);
}

sub xlat
{
    local ($l) = @_;

    $l =~ s/\\backslash */$X/g;		# \backslash -> \
    $l =~ s/(?<!\\)\\~/$Y/g;	# \~ -> ~
    $l =~ tr/~/ /;
    $l =~ s/\\([_~&%^\$\#\[\]|\-])/\1/g;# unescape special characters
    $l =~ s/\\,//g;			# remove small spaces
    if ($l =~ /\\([A-Za-z]+|.)/) {
	warn "unrecognized command $& ($l)";
	$l = $`."\n!!! UNRECOGNIZED COMMAND: $&\n$'";
    }
    $l =~ s/$X/\\/g;
    $l =~ s/$Y/~/g;
    $l =~ tr/{}//d;			# delete stray curly braces
    $l =~ s/$CO/{/g;			# put escaped braces back
    $l =~ s/$CC/}/g;
    return $l;
}


sub compose
{
    local ($s,$left,$n,$spc);
    local (@tmp) = @_;

    for (@tmp) {
	s/PAGE/$page/;
    }
    for (@tmp) { $s += length($_); }
    $left = $WIDTH-$s;
    $n = $#_;
    for (@tmp) {
	print $_;
	$spc = $n ? int($left/$n) : 0;
	print " " x $spc;
	$left -= $spc;
	$n--;
    }
    print "\n";
}


sub print
{
    local ($l) = @_;

    if (!$paged) {
	print $l;
	return;
    }
    if ($transaction) {
	push(@queue,$l);
	return;
    }
    if (!$line) {
	if ($page) {
	    &compose(@head);
	}
	else {
	    print "\n";
	    $page++;
	}
	print "\n\n";
	$line = 3;
    }
    return if $line == 3 && $l =~ /^\s*\n/;
    if ($line++ < $LENGTH-3) {
	$l =~ s/[ \t]*\n/\n/;
	print $l;
	return;
    }
    print "\n\n";
    &compose(@foot);
    print "\014\n";
    $line = 0;
    return if $terminating;
    $page++;
    &print($l);
}


sub flush
{
    $transaction = 0;
    if ($line+$#queue+1 > $LENGTH-3) {
	while ($line != 3) { &print("\n"); }
    }
    for (@queue) { &print($_); }
    undef @queue;
}


sub verbatim
{
    local ($b) = @_;

    $b =~ s/~/$X/g;
    $b =~ s/ /~/g;
    $b =~ s/\\/\\backslash /g;
    $b =~ s/$X/\\~/g;
    $b =~ s/[_^%#&{}\$\-]/\\$&/g;
    $b =~ s/[`']/\\$&~/g;
    $b =~ s/\n\n\n/$B2/g;
    $b =~ s/\n\n/$B1/g;
    $b =~ s/\n/\\\\/g;
    return $b;
}

#
#  handle \input
#
print STDERR "[".length($t)."] Loading \\input files (if any)\n";
while ($t =~ /\\input\s+(\S+)\s*\n/) {
    $t = $`;
    open(FILE,$1) || die "open $1: $!";
    while ($l = <FILE>) { $t .= $l; }
    close FILE;
    $t .= "\n".$';
}
#
# load macros
#
print STDERR "[".length($t)."] Loading macros\n";
while ($t =~ /\n%%(def|cmd)([^\n]*)\n/) {
    $t = $`."\n".$';
    $a = $1;
    $2 =~ /([^\\])=/ || die "= missing in $2";
    if ($a eq "def") {
	$m{$`.$1} = $';
	$c{$`.$1} = "";
    }
    else {
	$m{$`.$1} = "";
	$c{$`.$1} = $';
    }
}
#
# remove %%beginskip ... %%endskip pairs
#
print STDERR "[".length($t)."] Removing %%beginskip ... %%endskip pairs\n";
while ($t =~ /\n%%beginskip\s*\n/) { $t = $`.$B.$'; }
while ($t =~ /\n%%endskip\s*\n/) { $t = $`.$E.$'; }
while ($t =~ /$B[^$B$E]*$E/) { $t = $`."\n".$'; }
$t !~ /[$B$E]/ || die "%%beginskip/%%endskip mismatch";
#
# handle %%head and %%foot
#
while ($t =~ /\n%%head\s+(\S.*\S)\s*\n/) {
    $t = $`."\n".$';
    push(@head,$1);
}
while ($t =~ /\n%%foot\s+(\S.*\S)\s*\n/) {
    $t = $`."\n".$';
    push(@foot,$1);
}
#
# Handle begin/endfigure
#
while ($t =~ /\n%%beginfigure\s*(\S+)\s*\n/) { $t = $`.$BF.$1.$BF."\n".$'; }
while ($t =~ /\n%%endfigure\s*([^\n]*\S)\s*\n/) { $t = $`.$EF.$1.$EF."\n".$'; }
#
#  process macros
#
print STDERR "[".length($t)."] Processing macros (may take a while)\n";
while (1) {
    $none = 1;
    for (keys %m) {
	while ($t =~ /$_/) {
	    $none = 0;
	    if ($c{$_} eq "") {
		eval "\$t = \$`.\"$m{$_}\".\$';";
	    }
	    else {
		eval "\$t = \$`.$c{$_}.\$';";
	    }
	    die "syntax error: $@" if $@;
	}
    }
    last if $none;
    print STDERR "[".length($t)."] "."  next pass\n";
# perfectionist's approach:
#    $l = 0;
#    for (keys %m) {
#	if ($t =~ /$_/) {
#	    if (length($&) > $l) {
#		$i = $_;
#		$l = length($&);
#	    }
#	}
#    }
#    last if !$l;
#    $t =~ /$i/ || die "internal error";
#    eval "\$t = \$`.\"$m{$i}\".\$'";
#    die "syntax error: $@" if $@;
#    print STDERR "[".length($t)."] "."$i\n";
}
#
# handle verbatim sections (we're not trying to be perfect here)
#
print STDERR "[".length($t)."] Handling verbatim sections\n";
while ($t =~ /\\begin{verbatim}([ \t]*\n)?/) { $t = $`."\n\n".$B.$'; }
while ($t =~ /\\end{verbatim}([ \t]*\n)?/) { $t = $`.$E."\n\n".$'; }
while ($t =~ /\\verb([^a-zA-Z \t\n])/) {
    local ($tmp) = quotemeta($1);
    last unless $t =~ /\\verb$tmp([^$tmp]*)$tmp/;
    $t = $`.$B.$1.$E.$';
}
while ($t =~ /$B([^$B$E]*)$E/) {
    ($a,$b,$c) = ($`,$1,$');
    &die("no support for \\t in verbatim sections yet, sorry",$a,$b,$c)
      if $b =~ /\t/;
    $b = &verbatim($b);
    $t = $a.$b.$c;
}
if ($t =~ /[$B$E]/) {
    if ($t =~ /..........[$B$E]........../) { print STDERR "$&\n"; }
    die "verbatim conflict";
}
#
# hide escaped curly braces
#
print STDERR "[".length($t)."] Hiding escaped curly braces\n";
$t =~ s/\\{/$CO/g;
$t =~ s/\\}/$CC/g;
#
# discard comments and italic corrections
#
print STDERR "[".length($t)."] Discarding comments and italic corrections\n";
while ($t =~ s/(?<!\\)%[^\n]*\n//g) {};
$t =~ s#(?<!\\)\\/##g;
#
# no math mode
#
print STDERR "[".length($t)."] No math mode\n";
while ($t =~ s/(?<!\\)\$//g) {};

#
# Remove tabs and massage blanks
#
print STDERR "[".length($t)."] Removing tabs and massaging blanks\n";
$t =~ s/\\ / /g;	# \cmd\ blah
$t =~ tr/ \t/ /s;

#
# Various minor issues
#
print STDERR "[".length($t)."] Dealing with various minor issues\n";
$t =~ s/(?<!\\)\\leftarrow\s*/<-/g;
$t =~ s/(?<!\\)\\longleftarrow\s*/<--/g;
$t =~ s/(?<!\\)\\Leftarrow\s*/<=/g;
$t =~ s/(?<!\\)\\Longleftarrow\s*/<==/g;
$t =~ s/(?<!\\)\\rightarrow\s*/->/g;
$t =~ s/(?<!\\)\\Rightarrow\s*/=>/g;
$t =~ s/(?<!\\)\\longrightarrow\s*/-->/g;
$t =~ s/(?<!\\)\\Longrightarrow\s*/==>/g;
$t =~ s/(?<!\\)\\leftrightarrow\s*/<->/g;
$t =~ s/(?<!\\)\\Leftrightarrow\s*/<=>/g;
$t =~ s/(?<!\\)\\longleftrightarrow\s*/<-->/g;
$t =~ s/(?<!\\)\\Longleftrightarrow\s*/<==>/g;
$t =~ s/(?<!\\)\\quad\s*/~/g;
$t =~ s/(?<!\\)\\qquad\s*/~~/g;
$t =~ s/(?<!\\)\\vert/|/g;
$t =~ s/(?<!\\)\\TeX/TeX/g;
$t =~ s/(?<!\\)\\LaTeX/LaTeX/g;
$t =~ s/(?<!\\)\\rm\s*//g;
$t =~ s/(?<!\\)\\hbox{/\{/g;
$t =~ s/(?<!\\)\\protect//g;
$t =~ s/(?<!\\)\\newpage\s*//g;
$t =~ tr/-/-/s;
$t =~ s/\n\n+/$B1/g;
$t =~ s/(?<!\\)\\documentclass(\[[^]]*\])?{[^}]*}//;
$t =~ s/(?<!\\)\\begin{document}//;
$t =~ s/(?<!\\)\\end{document}(\n|.)*//;
$t =~ s/(?<!\\)\\begin{center}//g;
$t =~ s/(?<!\\)\\end{center}//g;
#
# process figures
#
print STDERR "[".length($t)."] Removing figures\n";
while ($t =~ /\\begin{figure\*?}\s*/) { $t = $`.$B.$'; }
while ($t =~ /\\end{figure\*?}\s*/) { $t = $`.$E.$'; }
while ($t =~ /$BF([^$BF]*)$BF([^$BF$EF]*)$EF([^$EF]*)$EF|$B[^$B$E]*$E/) {
    ($a,$b,$c) = ($`,$&,$');
    if (substr($b,0,1) eq $BF) {
	$l{$1} = ++$figref;
	$t = $a."\n".$B2.$TB.$2.$B1."Figure $figref: $3".$TE.$B2.$c;
    }
    else {
	if ($b =~ /\\epsfig{file=([^,}]+)\.eps/) {
	    $fig = "";
	    open(FILE,"$1.asc") || die "open $1: $!";
	    while ($l = <FILE>) { $fig .= "$VL$l"; }
	    close FILE;
	    $fig =~ s/\n/\n$VL/g;
	    if ($center_figures) {
		$max = 0;
		for (split("\n",$fig)) {
		    $max = length($_)-1 if length($_) > $max;
		}
		if ($max < $WIDTH) {
		    local ($spc);
		    $spc = " " x (($WIDTH-$max)/2);
		    $fig =~ s/$VL/$VL$spc/g;
		}
	    }
	    $t = $a.&verbatim($fig)."\n$B1\nFigure ";
	}
	else {
	    $t = $a."[ Figure ";
	    $c = " ]".$c;
	}
	if ($b =~ /\\label{([^}]*)}/) {
	    $l{$1} = ++$figref;
	    $t .= " $figref";
	}
	if ($b =~ /\\caption{([^}]*)}/) {
	    $t .= ": $1";
	}
	$t .= $c;
    }
}
$t !~ /[$B$E]/ || die "\\begin/\\end{figure} mismatch";
$t !~ /[$BF$EF]/ || die "%%begin/endfigure problem";

#
# Handle accents, umlauts, and double quotes
#
print STDERR "[".length($t)."] Handling accents, umlauts, double quotes ".
  "and hyphens\n";
$t =~ s/\\[`']([AEOUaeou])/$1/g;
$t =~ s/\\([`'])~/$1/g;
$t =~ s/\\"([AOUaou])/$1e/g;
$t =~ s/``/"/g;
$t =~ s/''/"/g;

#
# Citations
#
print STDERR "[".length($t)."] Processing citations\n";
while ($t =~ /\\cite{([^}]+)}/) {
    $t = $`."[";
    $after = $';
    for (split(",",$1)) {
	if (defined $cite{$_}) { $t .= "$cite{$_},"; }
	else {
	    $cite{$_} = ++$citation;
	    $bibref[$citation] = $_;
	    $t .= "$citation,";
	    die "unmatched ref $_" unless $after =~ /\\bibitem{$_}/;
	    $after = $`."\\item[\[$citation\]] ".$';
	}
    }
    $t =~ s/,$//;
    $t .= "]$after";
}
$t =~
  s/\\begin{thebibliography}{[^}]*}/\\section{References}\\begin{description}/;
$t =~ s/\\end{thebibliography/\\end{description}/;

#
# Flag unused bibitems
#
while ($t =~ /\\bibitem{([^}]+)}/) {
    print STDERR "Unused: \\bibitem{$1}\n";
    $t = $`."\\item[\[$1\]] ".$';
}

#
# Handle footnotes
#
print STDERR "[".length($t)."] Handling footnotes\n";
$t =~ s/\\footnote{/\\footnotemark\\footnotetext{/g;
$t =~ s/\\footnotemark/$X/g;
$t =~ s/\\footnotetext{/$Y/g;
while ($t =~ /$X([^$Y]*)$Y/) {
    ($a,$b,$c) = ($`,$',$1);
    $t =~ /^[^$Y]*$Y$B1/;
    $d = $';
    for ($s = "*"; $d =~ /$Z/; $d = $`.$Y.$') { $s .= "*"; }
    $a = $a.$s.$c;
    while ($b =~ /^([^}]*){([^{}]*)}/) { $b = $`.$1.$B.$2.$E.$'; }
    $b =~ /^([^{}]*)}/ || die "{ } confusion";
    ($b,$t) = ($1,$');
    $b =~ s/$B/{/g;
    $b =~ s/$E/}/g;
    $d = "$B1$Z\\begin{description}\\item[$s] $b\\end{description}$B1";
    if ($t =~ /$B1([^$Z][^$N]*)$/) { $t = $`.$d.$1; }
    else { $t = $t.$d; }
    $t = $a.$t;
}
$t =~ s/$Z//g;
if ($t =~ /[$X$Y$Z$B$E]/) {
    if ($t =~ /..............[$X$Y$Z$B$E]/) { print STDERR "HEY $&\n"; }
    die "footnote confusion";
}
#
# process simple tables ...
#
print STDERR "[".length($t)."] Processing simple tables\n";

sub draw_line # helper function
{
    local ($i,$has_work);

    $has_work = 0;
    for (@l) {
	next unless $_;
	$has_work = 1;
	last;
    }
    return unless $has_work;
    $i = 0;
    for (@d) {
	if ($_ =~ /^\|/) { $a .= $l[$i] ? "--" : $bar[$i] ? "| " : "~ "; }
	$a .= ($l[$i] ? "-" : "~") x ($w[$i]+1);
	if ($_ =~ /\|$/) {
	    $a .= $l[$i+1] ? "--" : $l[$i] ? "- ": $bar[$i+1] ? "| " : "~ ";
	}
	$i++;
    }
    $a .= "\\\\";
    @l = ();
}

while ($t =~ /\\begin{tabular}/) { $t = $`.$B.$'; }
while ($t =~ /\\end{tabular}/) { $t = $`.$E.$'; }
while ($t =~ /$B\{([rlc|]+)\}([^$B$E]*)$E/) {
    ($a,$b,$c,$d) = ($`,$',$2,$1);
    $c =~ s/\\\\/&/g;
    $c =~ s/[\s\n]*\\hline[\s\n]*/$X &/g;
    $c =~ s/[\s\n]*\\cline{([^}]*)}[\s\n]*/$X$1 &/g;
    $c =~
      s/[\s\n]*\\multicolumn{(\d+)}{([^}]+)}({[^}]*})[\s\n]*&?/$Y$1 $2 $3&/g;
    ($e = $d) =~ tr/|//cd;
    @d = ();
    while ($d =~ /^(\|*)[a-z](\|*)/) {
	push(@d,$&);
	$d = $';
    }
    @f = ();
    while ($c =~ /(?<!\\)&/) {
	push(@f,$`.$1);
	$c = $';
    }
    @w = ();
    $d =~ tr/|//d;
    $i = 0;
    for (@f) {
	next if $_ =~ /^$X/;
	if ($_ =~ /^$Y(\d+)/) {
	    $i += $1;
	    next;
	}
	$f = $i % @d;
	$_ = &xlat($_);
	$_ =~ s/^[\s\n]*//g;
	$_ =~ s/[\s\n]*$//g;
	if ($w[$f] < length($_)) { $w[$f] = length($_); }
	$i++;
    }
    $i = 0;
    for (@f) {
	next if $_ =~ /^$X/;
	if ($_ !~ /^$Y(\d+) (\S+) {([^}]*)}/) {
	    $i++;
	    next;
	}
	local ($a,$b,$c) = ($1,$2,$3);
	$f = $i % @d;
	$c = &xlat($c);
	$c =~ s/^[\s\n]*//g;
	$c =~ s/[\s\n]*$//g;
	$s = $a-1-($b =~ s/\|/\|/g)*2;
#	$s++ if $b =~ /^\|/;
$s++ if $s < 0;
#die if $s < 0;
	for ($j = $i; $j < $i+$a; $j++) {
	    $s += $w[$j % @d]+($d[$j % @d] =~ s/\|/\|/g)*2;
	}
	$_ = "$Y$a $s $b {$c}";	# NB: format change to include computed length !
	$s = length($c)-$s;
	for ($j = 0; $j < $a; $j++) {
	    if ($s <= 0) {
		$i++;
		next;
	    }
	    $tmp = int($s/($a-$j));
	    $w[$i++ % @d] += $tmp;
	    $s -= $tmp;
	}
    }
    $l = @d+2*length($e)-1;
    for (@w) { $l += $_; }
    $a .= "$B1";
    $i = 0;
    @l = ();
    @bar = ();
    for (@f) {
	if (/^$X\s/) {
	    $a .= ("-" x $l)."\\\\";
	    next;
        }
	if (/^$X(\d+)-(\d+)/) {
	    for ($1..$2) { $l[$_-1] = 1; }
	    next;
	}
	if (!($i % @d)) {
	    &draw_line;
	    @bar = ();
	}
	if (/^$Y(\d+) (\d+) (\S+) {([^}]*)}/) {
	    ($w,$d,$f) = ($2,$3,$4);
	    $i += $1;
	}
	else {
	    $f = $_;
	    $d = $d[$i % @d];
	    $w = $w[$i % @d];
	    $i++;
	}
	if ($d =~ /^\|/) {
	    $a .= "| ";
	    $bar[0] = 1;
	}
	$g = $w-length($f);
	if ($d =~ /l/) { $a .= $f.("~" x $g); }
	if ($d =~ /c/) {
	    $a .= ("~" x int($g/2)).$f.("~" x ($g-int($g/2)));
	}
	if ($d =~ /r/) { $a .= ("~" x $g).$f; }
	$a .= " ";
	if ($d =~ /\|$/) {
	    $a .= "| ";
	    $bar[$i % @d ? $i % @d : @d] = 1;
	}
	if (!($i % @d)) { $a .= "\\\\"; }
    }
    &draw_line;
    $t = $a.$b.$B1;
}
if ($t =~ /[$B$E$X$Y]/) {
    &die("\\begin/end{tabular} mismatch",
      $`,$& eq $B ? "\\begin{tabular}" : $& eq $E ? "\\end{tabular}" :
      $& eq $X ? "\\?line" : "\\multicolumn",$');
#    if ($t =~ /(.|\n)(.|\n)(.|\n)(.|\n)(.|\n)(.|\n)[$B$E$X$Y](.|\n)(.|\n)(.|\n)(.|\n)(.|\n)(.|\n)/) { print STDERR "$&\n"; }
#    die "\\begin/end{tabular} mismatch";
}
#
# process lists
#
print STDERR "[".length($t)."] Formatting lists\n";
while ($t =~ /\\begin{itemize}\s*/) { $t = $`.$B.$'; }
while ($t =~ /\\end{itemize}\s*/) { $t = $`.$E.$'; }
while ($t =~ /$B[^$B$E]*$E/) {
    ($a,$b,$c) = ($`,$&,$');
    while ($b =~ /\\item\s*/) { $b = $`.$X.$'; }
    while ($b =~ /$X([^$X]*)([$X$E])/) {
	$b = $`."- ".$SI.$SI.$1.$SO.$SO."\\\\"."$2".$';
    }
    $b =~ /$B([^$B$E]*)$E/;
    $t = $a.$SI.$SI.$B1.$1.$SO.$SO."$B1".$c;
}
$t !~ /[$B$E]/ || die "\\begin/\\end{itemize} mismatch";
while ($t =~ /\\begin{enumerate}\s*/) { $t = $`.$B.$'; }
while ($t =~ /\\end{enumerate}\s*/) { $t = $`.$E.$'; }
$num = 0;
while ($t =~ /$B[^$B$E]*$E/) {
    ($a,$b,$c) = ($`,$&,$');
    while ($b =~ /\\item\s*/) { $b = $`.$X.$'; }
    while ($b =~ /$X([^$X]*)([$X$E])/) {
	$num++;
	$b = $`."$num. ".$SI.$SI.$1.$SO.$SO."\\\\"."$2".$';
    }
    $b =~ /$B([^$B$E]*)$E/;
    $t = $a.$SI.$SI.$B1.$1.$SO.$SO."$B1".$c;
}
$t !~ /[$B$E]/ || die "\\begin/\\end{enumerate} mismatch";
while ($t =~ /\\begin{description}\s*/) { $t = $`.$B.$'; }
while ($t =~ /\\end{description}\s*/) { $t = $`.$E.$'; }
while ($t =~ /$B[^$B$E]*$E/) {
    ($a,$b,$c) = ($`,$&,$');
    while ($b =~ /\\item\[/) { $b = $`.$X."[".$'; }
    while ($b =~ /$X\[/) {
	($d,$e) = ($`,$');
	while ($e =~ s/\[([^\[\]]*)\]/$BS$1$ES/g) {};
	$e =~ /^([^\[\]]*)]\s*([^$X]*)([$X$E])/ || die "\item problem (1)";
	$b = $d.$1."~~".$SI.$SI.$2.$SO.$SO."\\\\".$3.$';
	$b =~ s/$BS/[/g;
	$b =~ s/$ES/]/g;
    }
    $b =~ /$B([^$B$E]*)$E/;
    $t = $a.$SI.$SI.$B1.$1.$SO.$SO.$B1.$c;
}
$t !~ /[$X]/ || die "\item problem (2)";
$t !~ /[$B$E]/ || die "\\begin/\\end{description} mismatch";

#
# process floating tables
#
print STDERR "[".length($t)."] Removing tables\n";
while ($t =~ /\\begin{table\*?}(\[[^]]*\])?\s*/) { $t = $`.$B.$'; }
while ($t =~ /\\end{table\*?}\s*/) { $t = $`.$E.$'; }
while ($t =~ /$B([^$B$E]*)$E/) {
    ($a,$b,$c) = ($`,$1,$');
    $tag = "Table ";
    if ($b =~ /\\label{([^}]*)}/) {
	$l{$1} = ++$tabref;
	$tag .= " $tabref";
	$b = $`.$';
    }
    if ($b =~ /\\caption{([^}]*)}/) {
	$tag .= ": $1";
	$b = $`.$';
    }
    $t = $a.$b.$tag.$c;
}
$t !~ /[$B$E]/ || die "\\begin/\\end{figure} mismatch";
$t !~ /[$BF$EF]/ || die "%%begin/endfigure problem";

#
# process sections and labels
#
print STDERR "[".length($t)."] Processing sections and labels\n";
$t =~ s/\\begin{abstract}/\\section{Abstract}/g;
$t =~ s/\\end{abstract}//g;
$LB = "\005";	# they don't necessarily have to be unique
$SC = "\006";
while ($t =~ /\\label{/) { $t = $`.$LB."{".$'; }
$fake_push = "";
if ($t =~ /\\chapter/) {
    $fake_push = "sub";
    while ($t =~ /\\chapter(\*)?{/) {
	$t = $`.$SC.$3."{".$';
    }
}
while ($t =~ /\\((sub)*)section(\*)?{/) {
    $t = $`.$SC.$3.$1.$fake_push."{".$';
}
$l = "";
while (1) {
    if ($t =~ /^([^$LB$SC]*)$LB\{([^{}]*)\}/) {
	$l{$2} = '"'.$l.'"';
	$t = $1.$';
    }
    if ($t =~ /$SC(\*?)((sub)*){/) {
	($a,$b,$c) = ($`,$',$2);
	$numbered = $1 ne "*" && $numbered_sections;
	while ($b =~ /^([^}]*){([^{}]*)}/) { $b = $`.$1.$B.$2.$E.$'; }
	$b =~ /^([^{}]*)}\s*/ || die "{ } confusion";
	($b,$d) = ($1,$');
	$b =~ s/$B/{/g;
	$b =~ s/$E/}/g;
	$l = $b;
	$so = defined($depth) && $indent_sections ? $SO x (3*($depth+1)) : "";
	$depth = length($c)/3;
	$pfx = "";
	if ($numbered) {
	    $snum[$depth]++;
	    for ($i = 0; $i <= $depth; $i++) {
		$pfx .= sprintf("%d%s",$snum[$i],$i == $depth ? $i ? " " :
		  ". " : ".");
	    }
	}
	for ($i = $depth+1; $i < 10; $i++) { $snum[$i] = 0; }
	$b = $pfx.$b;
	# fix this
	$b = &xlat($b);
	if ($indent_sections) {
	    $u = $SI x (3*($depth+1));
	}
        else {
	    if (($u = ("=","-","- ","")[$depth]) eq "") {
		$u = "";
	    }
	    else {
	        $u = "\\\\".substr($u x length($b),0,length($b));
	    }
        }
        $t = $a.$so.$B2.$b.$u.$B1.$d;
    }
    else {
	last;
    }
}
#
# handle references
#
print STDERR "[".length($t)."] Handling references\n";
$t =~ s/[Pp]age \\pageref({[^{}]*})/\\ref$1/g;
$t =~ s/\\pageref{[^{}]*}/???/g;
while ($t =~ /\\ref{([^{}]*)}/) {
    $t = $`.(defined($l{$1}) ? $l{$1} : "???").$';
}
#
# collapse whitespace
#
print STDERR "[".length($t)."] Collapsing whitespace\n";
$t =~ s/\\par\s*/\n\n/g;
$t =~ s/ *(\n+) */$1/g;
$t =~ tr/\n/ /;
$t =~ tr/ \t/ /s;	# again
#
# handle line breaks
#
print STDERR "[".length($t)."] Handling line breaks\n";
$t =~ tr/\n//d;
$t =~ s/\\\\/\n/g;
$t =~ s/\\par\s*/$B1/g;
#
# apply ultimate set of fixes to newlines
#
print STDERR "[".length($t)."] Applying ultimate set of fixes to newlines\n";
while ($t =~ s/([\n$B1$B2]+)([$SI$SO])/$2$1/g) {};
$t =~ s/([\n$B1$B2]*)\s+([\n$B1$B2]+)/$1$2/g;
$t =~ s/\n+/\n/g;
$t =~ s/\n?($B1)[\n$B1]*/\n\n/g;
$t =~ s/\n*($B2)[\n$B2]*/\n\n\n/g;
#
# translate what's left
#
print STDERR "[".length($t)."] Final translation\n";
$t = &xlat($t);
$t =~ s/^\s*//;
$t =~ s/\s*$//;
$t .= "\n";
#
# okay, now format and print it
#
print STDERR "[".length($t)."] "."Formatting (may take a while)\n";
$l = "";
$m = 0;
while ($t =~ /([$SI$SO$TB$TE$VL\n]| +)/) {
    if ($` ne "" || substr($1,0,1) eq " ") {
	if (length($l)+length($`) > $WIDTH && $l ne "") {
	    &print($l."\n");
	    $l = "";
	}
	if ($l eq "") { $l = " " x $m; }
	$l = $l.$`.(substr($1,0,1) eq " " ? $1 : "");
    }
    $t = $';
    if ($1 eq $SI) { $m++; }
    if ($1 eq $SO) { $m--; }
    if ($1 eq $TB) { $transaction = 1; }
    if ($1 eq $TE) { &flush; }
    if ($1 eq $VL) {
	$t =~ /\n/;
	&print($l."\n") unless $l eq "";
	$l = "";
	&print($`."\n");
#print STDERR "XXX $`\n";
	$t = $';
    }
    if ($1 eq "\n") {
	&print($l."\n");
	$l = "";
#	$t = s/^ *(\S.*)/\1/;
    }
}
&print("$l\n") if $l ne "";
if ($paged) {
    $terminating = 1;
    while ($line) { &print("\n"); }
}
print STDERR "Done\n";
