#!/usr/bin/perl -w
# $Id: setext2html.txt,v 1.8 2003/01/04 07:36:18 mcm Exp $
# setext -> HTML converter
#
# (C) 2002 Erik Oliver
#
# Modified by Matthew C. Miller <mcm@matthewcmiller.net>, 20040508
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
# USA.
#
# Status of typotags:
#     header-tt		Subject:, From:, and Date: added as a comment 
#                   to the top of the file, separate from the text body.
#     title-tt		<H1>...</H1> (and <TITLE> if needed)
#     subhead-tt	<H2>...</H2> (and <TITLE> if needed)
#     indent-tt		reflows paragraphs
#
#     bold-tt		<strong>...</strong>
#     italic-tt		<em>...</em>
#     underline-tt	<em>...</em>
#     hot-tt		<A HREF="href-tt">...</A>		(see also href-tt)
#
#     bullet-tt		<UL>...</UL>
#     include-tt    <PRE>...</PRE>
#     
#     quote-tt      TODO
#
#     twobuck-tt	<HR>
#     suppress-tt	suppressed in output
#     twodot-tt		ignored
#
# Additional typotags supported for HTML:
#     href-tt		.. _text HREF
#     note-tt       TODO
#

# TODO:
# Allow HTML markup in the text while at the same time suppresing 
#  "unintentional" markup.
# Ensure quoted text within bulletted and included lists is
#   displayed unrendered


use strict;
use Carp;

# Global variables
my $numargs = @ARGV;
my $infile;
my $outfile;
my %href;
my %notes;
my @toc;
my $tocindex = -1;

sub style_encode($) {
	use HTML::Entities;
	my $string = shift;
	encode_entities($string,"<>&");
	# bold-tt
		$string =~ s#\*\*([^\*]*)\*\*#<strong>$1</strong>#g;
	# italic-tt
		$string =~ s#~([^~]*)~#<em>$1</em>#g;
	# underline-tt
		$string =~ s#\b_(\S*)_\b#
			($a = $1) =~ s,_, ,g; "<em>$a</em>"; #eg;	
	# hot-tt
		my $h;
 		$string =~ s#\b(\S+)_\b#
			$h = $href{$1}; 
			($a = $1) =~ s,_, ,g;
			$h ? qq'<a href="$h">$a</a>' : qq'$a'; #eg;
	return($string);
} #end style_encode
	
sub encode_only($) {
	use HTML::Entities;
	my $string = shift;
	encode_entities($string,"<>&");
	return($string);
} #end encode_only

if ($numargs == 1) {
	if($ARGV[0] =~ s/\.etx$// ) {
		print STDERR "warning: one argument form used but called with $ARGV[0].etx, argument shortened to $ARGV[0].\n";
	}
	$infile = "$ARGV[0].etx";
	$outfile = "$ARGV[0].html";
} elsif ($numargs == 2) {
	$infile = $ARGV[0];
	$outfile = $ARGV[1];
} else {
    print STDERR "usage: $0 infile.etx outfile.html\n";
    print STDERR "usage: $0 inoutfile\n";
    exit -1;
}

if (! -e $infile) {
	print STDERR "error: Input, $infile, does not exist\n";
    exit -1;
}

if (! -r $infile) {
    print STDERR "error: Input, $infile, not readable\n";
    exit -1;
}

open INFILE, "<$infile" ||
        die "Could not open $infile for reading, $!";
my @data = <INFILE>;    # slurp input
chomp @data;            # strip newlines
close INFILE;

open OUTFILE,">$outfile" ||
        die "Could not open $outfile for writing, $!";

## Loop 1: Find any href-tt tags and hash the URL against a key
## [Also escape a literal "`" with +++.
for(my $loop=0; $loop <= @data; $loop++) 
{
    if(!$data[$loop]) { next; } # skip blank lines

    # href-tt finder: ^.. _href URL
    if($data[$loop] =~ /^\.\.\s+_(\S*)\s+(.*\/\/.*)\s*/ ) {
    	my $key = $1;
		my $pos = $loop + 1;
		my $value = $2;
		$data[$loop] = "";
		while ($pos <= $#data) {
			# lookahead
			if($data[$pos] =~ m/^\.\. ([^_])/) {
				$value .= "$1";
				$data[$pos] = "";
				$pos++;
				next;
			} else {
				last;
			}
		}
		$href{$key} = $value;
    # note-tt finder: ^.. _href text
    } elsif($data[$loop] =~ /^\.\.\s+_(\S*)\s+(.*)\s*/ ) {
    	my $key = $1;
		my $pos = $loop + 1;
		my $value = $2;
		$data[$loop] = "";
		while ($pos <= $#data) {
			# lookahead
			if($data[$pos] =~ m/^\.\. ([^_])/) {
				$value .= "$1";
				$data[$pos] = "";
				$pos++;
				next;
			} else {
				last;
			}
		}
		$notes{$key} = $value;
	}
    $data[$loop] =~ s/``/+++/g;
}

my ($htmltitle, $htmlauthor, $htmldate) = ("","","");


## Loop 2: Find headers and flow paragraphs, etc. together
for(my $loop = 0; $loop <= @data; $loop++) 
{
	if(!$data[$loop]) {next;} # skip blank lines
	$_ = $data[$loop];

	if(/^\.\.\s*$/ ) {carp "Found twodot-tt\n";}
	# stop when the twodot-tt is encountered
	
	if(/^\.\. / ) {
		# supress-tt
		$data[$loop] = "";
	} elsif (/^Subject: (.*$)/) {
		if($htmltitle eq "") {
			$htmltitle = style_encode($1);
		}
		$data[$loop] = "";
	} elsif (/^From: (.*$)/) {
		if($htmlauthor eq "") {
			$htmlauthor = $1;
		}
		$data[$loop] = "";
	} elsif (/^Date: (.*$)/) {
		if($htmldate eq "") {
			$htmldate = $1;
		}
		$data[$loop] = "";
	} elsif (/^===/) {
		if($htmltitle eq "") {
			$htmltitle = style_encode($data[$loop-1]);
		}
		$tocindex++;
		my $headline = style_encode($data[$loop-1]);
		my $id = "sh$tocindex";
		$toc[$tocindex] = "1:$headline";


		$data[$loop-1] = "<h1 id=\"$id\">$headline</h1>\n";
		$data[$loop] = "";
	} elsif (/^---/) {
		if($htmltitle eq "") {
			$htmltitle = style_encode($data[$loop-1]);
		}
		$tocindex++;
		my $headline = style_encode($data[$loop-1]);
		my $id = "sh$tocindex";
		$toc[$tocindex] = "2:$headline";

		$data[$loop-1] = "<h2 id=\"$id\">$headline</h2>\n";
		$data[$loop] = "";
	} elsif (/^\s{0,2}\* /) {
		# this is a bulleted list
		$data[$loop] =~ s/^\s*\* //;
		$data[$loop] = "<ul><li>" . style_encode($data[$loop]);
		my $pos = $loop+1;
		while($pos <= $#data) {
			if($data[$pos] =~ s/^\s{0,2}\* //) {
				$data[$loop] .= "</li>\n<li>" . style_encode($data[$pos]);
				$data[$pos] = "";
				$pos++;
			} elsif ($data[$pos] =~ s/^  ([^ *])/$1/) {
				$data[$loop] .= " " . style_encode($data[$pos]);
				$data[$pos] = "";
				$pos++;
			} else {
				last;
			}
		}
		$data[$loop] .= "</li></ul>\n";
	} elsif (/^\s{0,2}> \s*/) {
		# this is included text with a ">"
		$data[$loop] =~ s/^\s{0,2}> \s*//;
		$data[$loop] = "<pre>" . style_encode($data[$loop]) . "<br>";
		my $pos = $loop+1;
		while($pos <= $#data) {
			if($data[$pos] =~ s/^\s{0,2}> \s*//) {
					$data[$loop] .= style_encode($data[$pos]) . "<br>";
					$data[$pos] = "";
					$pos++;
			} else {
				last;
			}
		}
		$data[$loop] = $data[$loop] . "</pre>\n";
	} elsif (/^  [^ ]/) {
		# this is body text to wrap up
		$data[$loop] =~ s/^  //;
		$data[$loop] = style_encode($data[$loop]);
		my $pos = $loop+1;
		while($pos <= $#data) {
			if($data[$pos] =~ s/^  ([^ >])/$1/) {
				$data[$loop] .= " " . style_encode($data[$pos]);
				$data[$pos] ="";
				$pos++;
			} else {
				last;
			}
		}
		$data[$loop] = "<p>" . $data[$loop] . "</p>\n";
	} elsif (/`/) {
		# if nothing else and there is a ` assume multiline
		# verbatim environment
		$data[$loop] =~ s/`//;
		$data[$loop] = encode_only($data[$loop]);
		my $pos = $loop+1;
		while($pos<= $#data) {
			if($data[$pos] =~ s/`//) {
				if($data[$pos] ne '') {
					$data[$loop] .= "\n". encode_only($data[$pos]);
					$data[$pos] = "";
				}
				last;	
			} else {
				$data[$loop] .= "\n" . encode_only($data[$pos]);
				$data[$pos] = "";
				$pos++;
			}
		}
	} elsif (/\$\$/) {
#		$data[$loop] = "<span class=\"twobuck-tt\">" . $data[$loop] . "</span>";
		$data[$loop] = "<hr>"
	} elsif (/^\s*$/) {
		$data[$loop] = ""; # effectively a blank line
	} else {
		# should only be here if at end or next line is the === or ---
		next if ($loop == $#data);
		next if ($data[$loop+1] =~ /^===/);
		next if ($data[$loop+1] =~ /^---/);
		
		carp "Unhandled typotag, line = $loop, \"$_\"\n";
	}
}

print OUTFILE qq|
<html>
<head><title>$htmltitle</title>
<style type="text/css">
body { margin: 0; padding: 0; font-family: Verdana, sans-serif; font-size: .75em color: #000; background: #ffd; }
.content { padding: 10px 20px; margin: 5% 10%; border: 1px dotted #333; background: #fff; } 
p { margin-top: 0; padding-top: 0; font-family: Georgia, serif; line-height: 1.5em, margin-bottom: 1em; }
h1 {font-size: 2em; }
h2 {font-size: 1.25em; }
h1, h2 { margin: 1.25em 0 0 0; font-family: Verdana, sans-serif; }
pre { font: "Andale Mono", Courier, "Courier New", monospace; }
a {text-decoration:none;font-weight:600; }
a:hover {background-color:#eee;}
a.note{position:relative;z-index:24;background-color:#eee;color:#3F3;text-decoration:none;font-weight:600;}
a.note:hover{z-index:25;background-color:#ff0}
a.note span{display: none}
a.note:hover span{display:block;position:absolute;top:2em;left:2em;
                  width:15em;border:1px dotted #333; background-color:#ffd;
                  color:#000;text-alignment:center}

\@media print{
	body{font-size 10pt}
	div#main{margin: 0 0.5cm}
	a.note{background-color: #fff; color:000}
	a.note span{display:inline;font-style:italic}
	a.note span:before{content: " ["}
	a.note span:after{conten: "] "}
}
</style>
</head>
<body>|;

# header information
print OUTFILE "<p><em>$htmltitle &nbsp;&nbsp;</em>" if ($htmltitle);
print OUTFILE "by ",encode_only($htmlauthor) if ($htmlauthor);
print OUTFILE " on $htmldate" if ($htmldate);

print OUTFILE qq|
<div class="content">
|;

# table of contents?
print OUTFILE "<p>Table of Contents</p>\n";
print OUTFILE "<ul>\n";
my $lastlevel = 0;
for(my $loop = 0; $loop <= $tocindex; $loop++) {
	$_ = $toc[$loop];
	m#([^:]*):(.*)#;
	my $level = $1;
	my $content = $2; 

	if($lastlevel == 0) {
		# first entry
		print OUTFILE qq|<li><a href="#sh$loop">$content</a>|;
		$lastlevel = $level;
	} elsif ($lastlevel == $level) {
		# stayed same, end prior entry start new one
		print OUTFILE qq|</li>\n<li><a href="#sh$loop">$content</a>|;
	} elsif ($level == 2 && $lastlevel == 1) {
		# starting new level
		print OUTFILE qq|\n<ul>\n<li><a href="#sh$loop">$content</a>|;
		$lastlevel = $level;
	} elsif ($level == 1 && $lastlevel == 2) {
		# finishing a level 
		print OUTFILE qq|</li>\n</ul>\n</li>\n<li><a href="#sh$loop">$content</a>|;
		$lastlevel = $level;
	} else {
		print STDERR "toc error lastlevel vs level mismatch\n";
		die;
	}
}
print OUTFILE "</li></ul>\n";
if ($lastlevel == 2) { print OUTFILE "</li></ul>\n"; }

for(my $loop = 0; $loop <= @data; $loop++) {
	if(!$data[$loop]) {next;} # skip blank lines
	$data[$loop] =~ s/\+\+\+/`/g;
	print OUTFILE "$data[$loop]\n";
}

print OUTFILE qq|
</div>
</body></html>
|;

close OUTFILE;

