:
#!/bin/sh -- # -*- perl -*-
#
# html2ascii converter written by Christopher Creutzig for MuPAD.
# It can't do very much, but I think it handles everything that
# occurs in the help pages converted with mh2html.
eval 'exec perl -w -S $0 ${1+"$@"}'
    if 0;
# This is a NOP for perl, but shells parse line-oriented. Standard hack.

#
# This one only adds an additional search path
use lib '/wiwianka/user/cube/share/lib/perl';

require 5.004;
package HTML::ToText;
use HTML::Parser;
use HTML::Entities ();
use Text::WrapProp;
use Term::ANSIColor;
use Carp;

use vars qw($columns);

$columns = 75;

# HTML::Entities lacks definitions for a couple of entities:
my %add_entities = ('&thinsp;' => '',
		    '&bdquo;'  => '"',
		    '&rdquo;'  => '"',
                    '&sbquo;'  => '`',
                    '&rsquo;'  => '\'',
		    '&rarr;'   => '->',
		    '&lang;'   => '<',
		    '&rang;'   => '>',
                    '&isin;'   => ' in ',
                    '&partial;'=> 'd',
                    '&pi;'     => 'PI',
                    '&mu;'     => 'mu',
                    '&le;'     => '<=',
                    '&ge;'     => '>=',
                    '&infin;'  => 'oo',
                    '&alpha;'  => 'alpha',
                    '&beta;'   => 'beta',
                    '&epsilon;' => 'epsilon',
                    '&delta;'  => 'delta',
                    '&ldquo;'  => q{''},
                    '&radic;'  => 'sqrt ',
            '&#8211;'  => q{-},
		    '&#8220;'  => q{``},
		    '&#8221;'  => q{''},
		    '&#8222;'  => q{`},
		    '&#8223;'  => q{'},
		    '&#8217;'  => q{'},
		    );

# In case you don't know: This is inheritance in perl.
@ISA=qw(HTML::Parser);

sub doInit {
    my $self = shift;
    @{$self->{'text_acc'}} = ();  # Accumulator for text
    @{$self->{'table_acc'}} = (); # Accumulator for table text
    $self->{'table_col'} = -1;    # currently filling this coloumn 
    $self->{'table_row'} = -1;    #  (-1 outside of tables)
    $self->{'pre_seen'} = 0;      # Number of nested <pre> tags
    $self->{'indent'} = 0;        # current indentation
    $self->{'had_br'} = 0;        # Just made a <br> or <hr>,
                                  #  so an immediately following </p>
                                  #  causes one more empty line.
    $self->{'bullet'} = '';       # bullet for an <li>
    $self->{'newlines'} = 200;    # no. of \n just printed.  We do not start
                                  # pages with newlines.
    $self->{'ignore_a'} = 0;      # ignore </a>, because it ends
                                  # an <a name="..."> tag.
    $self->{'divs'} = ();         # stack of open <div>s
    $self->{'ignore'} = 0;        # ignore all text, do not output anything
}

# open tag
sub start {
    my ($self, $tag, $attr, $attrseq, $origtext) = @_;
    shift;
    $tag = lc $tag;
    $self->doInit()             if $tag eq 'html';
    $self->output(0,2)        if $tag =~ m/^h\d$/;
    $self->output(1,2) 	      if $tag =~ m/^(ol|ul|li|p|center)$/;
    $self->output(1,1) 	      if $tag =~ m/^pre$/;
    $self->{'pre_seen'}++     if $tag eq 'pre';
    $self->{'indent'} += 2    if $tag =~ m/^(ol|ul)$/;
    $self->save_table_entry   if $tag eq 'td'
	                         and @{$self->{'text_acc'}};
    if ($tag eq 'tr') {
	$self->save_table_entry() 
	    if @{$self->{'text_acc'}};
	$self->end_table_row();
	$self->{'table_col'} = 0;
    }
    if ($tag eq 'table') {
	$self->{'table_acc'} = () ;
	$self->{'table_row'} = 0;
	$self->output(0,0);
    };
    if ($tag eq 'hr') {
	$self->output(0,1);
	$self->text("-"x ($columns - 1));
	$self->output(0,1);
    };
    if ($tag eq 'br') {
	$self->output(1,1);
	$self->{'had_br'} = 1;
    };
    if ($tag eq 'img'
	and defined $attr->{'alt'}) {
	if ($attr->{'alt'} =~ /[\r\n]/) {
	    $self->output(1, 2);
	    $self->{'pre_seen'}++;
	    $attr->{'alt'} =~ s/\r\n?/\n/g;
	    $self->insert($attr->{'alt'});
	    $self->output(1, 2);
	    $self->{'pre_seen'}--;
	} else {
	    $self->insert($attr->{'alt'});
	}
    }
    $self->insert("\001") if $tag =~ m/^(code)$/;
    $self->insert("\002") if $tag =~ m/^(h\d)$/;
    $self->insert("^\010(") if $tag eq 'sup';
    $self->insert("[") if $tag eq 'sub';
    if ($tag eq 'a') {
	if (defined $attr->{'href'}) {
	    $self->insert("\002");
	} else {
	    $self->{'ignore_a'}=1;
	}
    }
    $self->insert("\001") 
	if $tag eq 'i'
	    and defined($attr->{'class'}) and $attr->{'class'} eq 'Option';
    $self->insert("\000") if $tag eq 'td';
    $self->{'bullet'} = 'o' if $tag eq 'li';
    if ($tag eq 'div') {
	$self->output(0,1);
	if (defined($attr->{'style'}) 
	    and $attr->{'style'} eq "margin-left:20pt") {
	    $self->{'indent'} += 3;
	    push @{$self->{'divs'}}, 1;
	} else {
	    push @{$self->{'divs'}}, 0;
	}
    }
#    $self->SUPER::start(@_);
}

# close tag
sub end {
    my ($self, $tag, $attr, $attrseq, $origtext) = @_;
    shift;
#    $self->SUPER::end(@_);
    $self->insert("\000") if $tag =~ m/^(code|h\d|i)$/;
    if ($tag eq 'a') {
	$self->insert("\000") unless $self->{'ignore_a'};
	$self->{'ignore_a'}=0;
    }
    $self->save_table_entry() if $tag eq 'td';
    $self->output_table()     if $tag eq 'table';
    $self->output(0,2)        if $tag =~ m/^h\d$/;
    $self->output(1,2) 	      if $tag =~ m/^(p|ol|ul|html|center)$/;
    $self->output(1,0) 	      if $tag eq 'pre';
    $self->{'indent'} -= 2    if $tag =~ m/^(ol|ul)$/;
    $self->{'pre_seen'}--     if $tag eq 'pre';
    if ($tag eq 'div') {
	$self->output(0,1);
	$self->{'indent'} -= 3
	    if pop @{$self->{'divs'}};
    }
    $self->insert(")") if $tag eq 'sup';
    $self->insert("]") if $tag eq 'sub';
}

sub comment {
    my ($self, $comment) = @_;
    $self->{'ignore'} = 1
	if $comment eq ' start menubuttons ';
    $self->{'ignore'} = 0
	if $comment eq ' end menubuttons ';
}

sub insert {
    my($self, $text)= @_;
    return if $self->{'ignore'};
    push(@{$self->{'text_acc'}}, $text);
}

# Text is simply accumulated until the next <p>, </p>, <pre> or </pre> occurs.
sub text {
    my($self, $text)= @_;
    return if $self->{'ignore'};
    my $decoded = HTML::Entities::decode($text);
    $decoded =~ s/(\&.*?;)/defined($add_entities{$1})?$add_entities{$1}:$1/eg;
    if($self->{'pre_seen'} < 1) {
	$decoded =~ tr/\n/ /;
    }
    push(@{$self->{'text_acc'}}, $decoded)
	if $decoded =~ /\S/ || @{$self->{'text_acc'}} > 0;
}

sub save_table_entry {
    my $self = shift;
# if this array position is undefined yet, insert an empty array.
#    $self->{'table_acc'}[$self->{'table_col'}] |= [];
    @{$self->{'table_acc'}[$self->{'table_col'}]}[$self->{'table_row'}] 
	= $self->get_text;
    @{$self->{'text_acc'}} = ();
    $self->{'table_col'}++;
}

sub end_table_row {
    my $self = shift;
    $self->{'table_row'}++
	if $self->{'table_acc'};
}

sub output_table {
    my $self = shift;
    my(@colwidth, $curwidth, $width, $max, $i, $linewidth);
# Figure out the width each coloumn would need to fit in a single row
    @colwidth = 
	map {
	    $width = 0;
	    my($line,@lines);
	    @lines = @{$_};
	    foreach $line (@lines) {
		$line ||= '';
		$line =~ s/^([\000-\010]*)\s*/$1/;
		$linewidth = length($line);
		$linewidth -= ($line =~ y/\000-\010//);
		$width = $linewidth
		    if $linewidth > $width;
	    };
	    $width + 1;
	} @{$self->{'table_acc'}};
    $curwidth = 2;
# +1 for space between cols
    foreach $i (@colwidth) { $curwidth += $i + 1; } 
    while ($curwidth > $columns) {
# Too wide.  Shorten the widest coloumn by 1.  (There are probably
# lots of better strategies. )
	$max=0;
	foreach $i (0 .. $#colwidth) {
	    $max = $i
		if ( $colwidth[$i] > $colwidth[$max] );
	}
	croak "Coloumn width must not go below 2"
	    if $colwidth[$max] < 3;
	$colwidth[$max]--;

	$curwidth--;
# If something else is done above, this might become necessary:
#	$curwidth = 2;
#	foreach $i (@colwidth) { $curwidth += $i + 1; } 
    }

# Now, wrapping each entry in {'table_acc'} to the coloumn width in
# @colwidth lets us combine the entries line by line and get a table line.
# those are then output.
    my(@currcols, $done, $done2, $entry);
    do {
	$done = 1;
	foreach $i ( 0 .. $#colwidth ) {
	    local $columns = $colwidth[$i];
	    $entry = shift @{$self->{'table_acc'}[$i]} || "";
	    $entry =~ s/^([\000-\010]*)\s*/$1/;
	    $done = 0
		unless $entry eq "";
	    $currcols[$i] = [ split('\n', wrap("", "", $entry)) ];
	};
	if(!$done) {
	    do {
		$done2 = 1;
		$line = " ";
		foreach $i ( 0 .. $#colwidth ) {
		    $entry = $prefix[$i] || '';
		    $prefix[$i] = '';
		    $entry .= shift(@{$currcols[$i]}) || '';
		    $done2 = 0
			unless $entry eq "";
		    if ($entry =~ m/([\001-\010])[^\000]*$/) {
			$entry .= "\000";
			$prefix[$i] = $1;
		    }						
		    $entry .= " " x $colwidth[$i];
		    my $countspec = ($entry =~ tr/\000-\010//);
		    $line  .= substr($entry, 0, $colwidth[$i]+$countspec)." ";
		}
		$line =~ s/([\001-\010]+)(\s+)/$2$1/g;
		$line =~ s/(\s+)(\000)+/$2$1/g;
		$line =~ s/\001/color 'bold'/eg;
		$line =~ s/\002/color 'underline'/eg;
		$line =~ s/\000/color 'reset'/eg;
		print $line.color('reset')."\n" unless $done2;
	    } while (!$done2);
	}
    } while(!$done);
    print (color 'reset', "\n");
}

sub get_text {
    my $self = shift;
    my $text = join('', @{$self->{'text_acc'}});
    $text =~ s/^[\s\001\002\000\n]*\n//g;
    $text =~ s/^[\s\001\002\000\n]*$//g;
    $text =~ s/\010\((.)\)/$1/g;
    $text =~ s/\010//g;
    $text =~ s/^\s*//;
    $text =~ s/\s+/ /g;
    return($text);
}

sub output {
    my ($self, $leftmarg, $nr_nl) = @_;
    if($self->get_text()) 
    {
	$leftmarg += $self->{'indent'};
	$self->{'newlines'} = 0;
	if($self->{'pre_seen'} > 0 ) {
	    # inside <pre>..</pre>
	    my $text=join '', @{$self->{'text_acc'}};
	    chomp $text;
	    $text =~ s/([\001-\010]+)(\s+)/$2$1/g;
	    $text =~ s/(\s+)(\000)+/$2$1/g;
	    $text =~ s/\001/color 'bold'/eg;
	    $text =~ s/\002/color 'underline'/eg;
	    $text =~ s/\000/color 'reset'/eg;
	    $text =~ s/\n/"\n"." " x $leftmarg/egm;
	    $text = " " x $leftmarg . $text;
	    print $text, "\n";
# count nr. of newlines + 1
	    $self->{'newlines'} = 1 + ($text =~ s/\s*\r?\n$//g);
#	    print "newlines found: $self->{'newlines'}";
	} else {
	    # normal text
	    my $parstart = " " x $leftmarg;
	    if ($self->{'bullet'}) {
		my $b = $self->{'bullet'};
		my $countspec = ($b =~ tr/\000-\010//);
		substr($parstart, -(length($b)-$countspec+1)) = $b.' ';
		$self->{'bullet'} = '';
	    }
	    my $line = wrap($parstart, " "x$leftmarg, $self->get_text);
# Have each line end with no properties set.
	    1 while ($line =~ s{([\001-\010])([^\000\n]*)\n}
	                       {$1$2\000\n$1}g);
	    $line =~ s/([\001-\010]+)(\s+)/$2$1/g;
	    $line =~ s/(\s+)(\000)+/$2$1/g;
	    $line =~ s/\001/color 'bold'/eg;
	    $line =~ s/\002/color 'underline'/eg;
	    $line =~ s/\000/color 'reset'/eg;
	    print $line.color 'reset';
	}
    }
    if($self->{'newlines'} < $nr_nl) {
	print "\n" x ($nr_nl - $self->{'newlines'});
	$self->{'newlines'} = $nr_nl;
    }
    @{$self->{'text_acc'}} = ();
    $self->{'had_br'} = 0;
}

sub wrap {
    my($pre_first, $pre, $text) = @_;
    length($text) or return $pre_first;
    my @width_table = ((0)x16,(1)x240);
    my $width = $columns - length($pre);
    my $prefix = $pre_first;
    if(length($pre_first) < length($pre)) {
# 5.005 can do that in one call...
	$prefix .= substr($text,0,length($pre)-length($pre_first));
	substr($text,0,length($pre)-length($pre_first))="";
    } else {
	if(length($pre_first) > length($pre)) {
	    $text = substr($prefix,length($prefix)-length($pre),-1).$text;
	    substr($prefix,length($prefix)-length($pre),-1)="";
	}
    }
    my $res = $prefix.join('',wrap_prop($text, $width, \@width_table));
    $res =~ s/\n/\n$pre/g;
    $res;
}

package main;

my $p = new HTML::ToText;
$p->doInit();
$p->parse_file($ARGV[0]);

# Local Variables:
# mode:perl
# End:
