#!/usr/local/bin/perl -w

use Text::ParseWords;
use Text::Tabs;
use vars qw($qq);
require "ctime.pl";

$progname = 'htm1-pp';
$sl  = '.htm1' ;
$inc = '.ht' ;

# See Documentation now!

# - the keys are case-sensitive (html isn't)
%Tags = ();
%UniTags = ();
%Funcs = (
          "raw" => \&unescape,
          "exec" => \&execute,
          "html-tag2" => \&add_html_tag2,
          "html-tag1" => \&add_html_tag1,
          "use" => \&useFile,
          "include" => \&includeFile,
          "html-def" => \&add_html_def,
          "html-esc" => \&html_add_esc,
          "html-undef" => \&undefineTag,
          "x" => \&html_direct,
         );

# shall we add all simple html-tags here? the default is to treat
# unknown tags as html-def2: implicitly. The bad-hashes here
# make the warning-message occur only once.
%badBiTag = ();
%badUniTag = ();

%Vars = ( );

%ArgReplace = (
       "top" => "valign=top",
       "middle" => "valign=middle",    # td
       "bottom" => "valign=bottom",
       "left"   => "align=left",       # textual
       "center" => "align=center",
       "right"  => "align=right",
       "justify" => "align=justify",
       "square" => "type=square",      # ul
       "circle" => "type=circle",
       "disc"   => "type=disc",
       "noborder" => "border=0",       # img table
       "nounder" => "style=text-decoration:none", # links
       "nocellspacing" => "cellspacing=0",
       "nocellpadding" => "cellpadding=0",
       "nospacing" => "cellspacing=0",
       "nopadding" => "cellpadding=0",
       "bgcolor2" => 'bgcolor=$(bgcolor2)',
       "fgcolor2" => 'color=$Vars{"fgcolor2"}',
       "bginvers" => 'bgcolor=$Vars{"fgcolor2"}',
       "fginvers" => 'color=$Vars{"bgcolor2"}',
       "100%" => 'width=100%',
       "80%"  => 'width=80%',
       "60%"  => 'width=60%',
       "50%"  => 'width=50%',
       "40%"  => 'width=40%',
       "20%"  => 'width=20%',
       );
%ArgShorthand = (
        "w" => "width",
        "h" => "height",
        "bg" => "bgcolor",
        "a" => "alt",
        "n" => "name",
        "s" => "src",
        "ls" => "lowsrc",
        "ce" => "center",
        "re" => "right",
        "le" => "left",
        "b"  => "border",
        "b0" => "noborder",
        "u0" => "nounder",
        "b2" => "bgcolor2",
        "f2" => "fgcolor2",
        "bx" => "bginvers",
        "fx" => "fginvers",
        "cs" => "cellspacing",
        "cp" => "cellpadding",
        );

@PostGeneration = ();


$NOW = `date`; chop $NOW;
$Text::Tabs::tabstop = 4;
$MaxDate = 0;
$Infile = "(unknown file)";

# Standard includes: try reading those config files
$dotfix = ".";                 # change for a FAT file system ...
for my $p ("/usr/etc/", "/usr/html/etc/",
     	"$ENV{HOME}/etc/",  "$ENV{HOME}/$dotfix", 
        "./etc/", "$dotfix") 
{
    for my $i ("",1,2,3,4,5,6,7,8,9) { 
       useFile ("$p$progname$inc$i") if -f "$p$progname$inc$i" ;
       my $file = "$p$progname.pl$i";
       if (-f "$file") {
          print STDERR "imports $file\n";
          unless ($return = do "$file") {
             warn "couldn't parse $file: $@" if $@;
             warn "couldn't do $file: $!"    unless defined $return;
             warn "couldn't run $file"       unless $return;
         }
       }
    }
    #old# useFile ("$p$progname$sl") if -f "$p$progname$sl" ; 
}


$parse_body = 0; # some functions will produce different output
                 # depending on context
$silent = 1;          # may suppress output in some functions
                      # usually all tags like "x-tag-like" will suppress
                      # warnings/errors about redefinition
$line_replace = 1;    # some functions differentiate on this
                      # 0 - block replace e.g. {note: this text}
                      # 1 - header line e.g. Content-Type: text\n..
                      # 2 - body line

foreach $File (@ARGV) {
    if ($File !~ /$sl$/i) {
        warn "Skipping $File (no $sl extension)";
        next;
    }

    if ($File =~ m#(.*)/#) {
        $Extras = $1;
    } else {
        $Extras = ".";
    }
    
    $Header = $Extras . "/.header";
    $Footer = $Extras . "/.footer";

    if (-f $Header)  {
        open(H, "$Header") or die;
        $HeaderText = join ("", <H>);
        close (H);
    } else {
        $HeaderText = "";
    }

    if (-f $Footer)  {
        open(H, "$Footer") or die;
        $FooterText = join ("", <H>);
        close (H);
    } else {
        $FooterText = "";
    }
    
    $Infile = $Title = $File;
    $Title ="";
    
    open (F, "$File") or die "Cannot open $File: $!";
    $MaxDate = (stat $Infile)[9];
    
    $File =~ s/$sl$/\.html/i;          # !!!!!!!!
    open (O, ">" . $File) or die "Cannot open output $File: $!";
    
    $_ = join("", expand(<F>));

    $currentFile = $Infile;
    
    m/^( (?:[^\n]*\n (?!\s*\n))* (?:[^\n]*\n) ) (\s*\n) (.*) $/sx;
    $head = $1; $body = $3;
    $head = parseHead($head);
    $body = parseBody($HeaderText . $body . $FooterText);

    # print O "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n";
    print O "<HTML><HEAD><TITLE>$Title</TITLE>\n" ;
    print O "<meta name=\"Generator\" content=\"$progname.pl\" date=\"$NOW\">\n";
    print O $head;
    print O "\n</HEAD>\n<BODY" ;
    if (exists $Vars{"bgcolor"}) { print O " bgcolor=", $Vars{"bgcolor"} ; }
    if (exists $Vars{"fgcolor"}) { print O " text=", $Vars{"fgcolor"} ; }
    if (exists $Vars{"link"})    { print O " link=", $Vars{"link"} ; }
    if (exists $Vars{"alink"})   { print O " alink=", $Vars{"alink"} ; }
    if (exists $Vars{"vlink"})   { print O " vlink=", $Vars{"vlink"} ; }
    if (exists $Vars{"bgimg"})   { print O " background=", $Vars{"bgimg"} ; }
    print O ">\n";
    print O $body;
    print O "\n</BODY></HTML>\n";
    
    close (O);
    close (F);
    undef $head;

    # Let's do a bit checking
    foreach (@PostGeneration) {
       &$_();
    }

    #    print STDERR "Date is $Date - ", scalar(localtime($Date)), "\n";
    utime $MaxDate,$MaxDate, $File or die "Oops: $!";
    
    printf STDERR ("%-40s => $File (%d bytes)\n", $Infile, -s $Infile);
}

sub parseText 
{
    $_ = $_[0];
    m/^( (?:[^\n]*\n (?!\s*\n))* (?:[^\n]*\n) ) (\s*\n) (.*) $/sx;
    $head = $1; $body = $3;
    $head = parseHead($head);
    $body = parseBody($body);
}

sub parseHead {
    $_ = $OriginalString = $_[0];
    
    $parse_body = 0;

    # Escaped characters turn into \ooo, then are turned back later
    # s/\\(.)/exists $SpecialEscapes{$1} ? $SpecialEscapes{$1} : sprintf("\\%03o", ord($1))/egs;
    s/\\([^\w])/sprintf("\\%03o", ord($1))/egs;
    
    unless ($Special) {
        # This must be escaped FIRST!
        s/&/&amp;/g;
        
        # Escape special characters
        foreach $c (sort keys %Escapes) {
            s/$c/$Escapes{$c}/g;
        }
    }

    # scan for implicit html-def -gud)
    s/^\[([\w\+\-\/\=\#]+)\:\](.*)$/&add_implicit_html_def($1,$2)/mge;
    
    # Non greedy match to the first paren OR
    # match until first leftparen, non-greedy match to matching right paren
    # and then non-greed match to the next paren
    
    # Note: this one does multiline matching
    # Note: We skip spaces, but note NEWLINES near {}
    
    foreach $qq (0..2) {
        # ... \ntag:(args) text\n - this is the version without '#' in names
        while (
               s/(^)([\w\+\-\/][\w\+\-\/\=]*): (\([^)]*\))? [ ]* (.*(?:\n\s.*)*) /&parseArgs($3); $1 . &x_line_replace_head($2,$4)/mgex
              ) {}

        # ..  {tag:(args) text} - expand those em'brace'd texts
        while(
              s/{([\w\+\-\/][\#\w\+\-\=]*): (\([^)]*\))? [ ]* ([^{}]*) [ ]* }/&parseArgs($2); &x_block_replace($1,$3)/sgex
             ) {}

        # ... \ntag:(args) text\n - this is the version *with* '#' in names
        while (
               s/(^)([\w\+\-\/][\#\w\+\-\/\=]*): (\([^)]*\))? [ ]* (.*(?:\n\s.*)*) /&parseArgs($3); $1 . &x_line_replace_head($2,$4)/mgex
              ) {}

        
        # Magic functions (e.g. make table of contents, but can embed any perl wanted)
        s/~~(.*?)~~/eval "$1" or die "Embedded evaulation failed: $@"/seg;
    }
    
    
    # Unescape escaped characters
    s/\\(\d{3})/chr(oct($1))/ge;
    
    # cut multiplied newlines in the header
    s(\n+)(\n)sg ;
    
    return $_;
}

sub parseBody {
    my $parse = 2;
    $_ = $OriginalString = $_[0];

    $parse_body = 1;
    # Escaped characters turn into \ooo, then are turned back later
    # s/\\(.)/exists $SpecialEscapes{$1} ? $SpecialEscapes{$1} : sprintf("\\%03o", ord($1))/egs;
    s/\\([^\w])/sprintf("\\%03o", ord($1))/egs;
    
    unless ($Special) {
        s/(^)(\<)(.*)$/$1x: $2$3/mg ;
        # This must be escaped FIRST!
        s/&/&amp;/sg;
        s/\</&lt;/sg ;
        s/\>/&gt;/sg ;
        s/\"/&quot;/sg ;
        
        # Escape special characters
        foreach $c (sort keys %Escapes) {
            s/$c/$Escapes{$c}/g;
        }
    }
    
    # Turn newlines into (soft) <P>
    s/^\s*$/<p >/mg;
    
    # scan for implicit html-def -gud)
    s/^\[([\w\+\-\/\=\#]+)\:\](.*)$/&add_implicit_html_def($1,$2)/mge;

    #   for the mails
    s/(^)(\&gt\;)(.*)/<br\t><i> $1 &gt; $3<\/i><br\t>/mg;
    
    # Non greedy match to the first paren OR
    # match until first leftparen, non-greedy match to matching right paren
    # and then non-greed match to the next paren
    
    # per document parse depth - choose with care
    if (exists $Vars{"parse"}) { $parse = $Vars{"parse"}; }
    
    while ($parse) {
        $parse -= 1;
    
        # ... \ntag: (args) text\n - this is the version without '#' in names
        ## print "run ^line-tags:^ (# $parse)\n";
        while (
               s/(^)([\*\w\+\-\/\=][\w\+\-\/\=]*): 
                  (\([^)]*\))? [ ]* (.*) /&parseArgs($3); $1 . &x_line_replace($2,$4)/mgex
              ) {}

        # .  {tag:(args) text} - expand em'brace'd texts
        ## print "run {line-tags} (# $parse)\n";
        while(
              s/{([\w\+\-\/][\#\w\+\-\/\=]*):(\([^)]*\))? [ ]* ([^{}]*) [ ]* }/&parseArgs($2); &x_block_replace($1,$3)/sgex
             ) {}

        # ... \ntag: (args) text\n - this is the version *with '#' in names
        ## print "run ^line-tags-#:^ (# $parse)\n";
        while (
               s/(^)([\*\w\+\-\/\=][\#\w\+\-\/\=]*): 
                  (\([^)]*\))? [ ]* (.*) /&parseArgs($3); $1 . &x_line_replace($2,$4)/mgex
              ) {}
              
        # scan for simple brace-pairs not being a {tag: ...}
        if ( 
           s/(\{(?![\w\+\-\/][\#\w\+\-\/\=]*:))([^\}]*)(\})/\&lb\;$2\&rb\;/sg 
        ){
           $parse += 1;
        }
        
        
        # Magic functions (e.g. make table of contents, but can embed any perl wanted)
        s/~~(.*?)~~/eval "$1" or die "Embedded evaulation failed: $@"/seg;
    }
    
    # multiple soft breaks to one real break
    s/(<p|<br)( ?>)([\n\s]*<br >*)*/\n$1$2/sg;
    
    # bracelets to normal
    s/\&lb\;/\{/sg ;
    s/\&rb\;/\}/sg ;
    s/\&lp\;/\(/sg ;
    s/\&rp\;/\)/sg ;
    
    # Unescape escaped characters
    s/\\(\d{3})/chr(oct($1))/ge;
    
    return $_;
}

sub includeFile {
    my ($fname, $old,$text,@lines,$prepend,$append) = @_;
    die "Cannot open $fname: $!" unless open(I, $fname);
    $prepend = $append = "";      print STDERR "include $fname\n";

    $old = $currentFile;
    $currentFile = $fname;

    @lines = <I>;
    if (exists $Args{prepend}) {
        $prepend = "$Args{prepend} ";
        $prepend =~ s/%/\@/g;
    }

    if (exists $Args{append}) {
        $append = " $Args{append} ";
        $append = ~s/%/\@/g;
    }

    $text = $prepend . join($prepend, @lines);
    $text =~ s/\n/\n$append/g;
    $text .= $append;

    # Ensure a file gets a modification date that depends on all the
    # files it has included
    $NewDate = (stat $fname)[9];
    $MaxDate = $NewDate if $NewDate > $MaxDate;
    
    $Infile = $fname;    
    $res = parseText($text);
    
    close (I);
    $currentFile = $old;

    return $res;
}

sub useFile {
    push @SpecialStack, $Special;
    $Special = 1;
    &includeFile($_[0]);
    $Special = pop @SpecialStack;
    
}


# (arg1,arg2)
sub parseArgs {
    my ($args, $arg, $keyword, $value) = @_;
    my $var;
    undef %Args;
    if (defined($args)) {
        $args =~ s/^\s*\((.*)\)\s*$/$1/; # spill enclosing whitespace
        foreach $arg (split(' ',$args)) {
            ($keyword, $value) = split("=" ,$arg);
            if ($keyword =~ m/^\s*$/) { next; }
            if ($keyword =~ m/^\w\w?$/) {
               if (exists $ArgShorthand{$keyword}) {
                  $keyword = $ArgShorthand{$keyword} ;
               }
            }
                  
            if (!defined($value)) {
                if (exists $ArgReplace{$keyword}) {
                   ($keyword, $value) = split("=", $ArgReplace{$keyword});
                   if (!defined($value)) {
                      $value = 1;
                   }
                } else {
                   $value = 1;
                }
            }

            foreach $var (keys %Args) {
                $value =~ s/\$\($var\)/$Args{$var}/g;
            }
            foreach $var (keys %Vars) {
                $value =~ s/\$\($var\)/$Vars{$var}/g;
            }

            $value =~ s/^ *\"(.*)\" *$/$1/sg; # cut out superfluous quotings
            
            $Args{$keyword} = $value;
        }
    }
}


# Return currently set args in a form of URL string, i.e. value="something"
sub getArgs {
    my ($arg, $s);
    $s = "";
    foreach $arg (keys %Args) {
        if ($Args{$arg} =~ /^\d+$/) {
            $s .= " $arg=$Args{$arg}";
        } else {
            $s .= " $arg=\"$Args{$arg}\"";
        }
    }
    #    print "Returning: $s\n" if length($s);
    return $s;
}

# *un* escape escaped characters
sub unescape {
    my ($text) = @_;

    foreach $c (keys %Escapes) {
        $text =~ s/$Escapes{$c}/$c/g;
    }

    $text =~ s/&amp;/&/g;
    return $text;
}

sub x_block_replace {
    ($tag, $text, $fun,$rep) = @_;

    $line_replace = 0;
    if ($tag =~ /^[xX]-(.*)/) {       # suppress warning
        $tag =~ s/^[xX]-// ;          
        $silent = 1;
    }else{
        $silent = 0;
    }

    if (exists $Funcs{$tag}) {
        $fun = $Funcs{$tag};
        $rep =&$fun($text);
    } elsif (exists $Tags{$tag}) {
        $rep = "<$Tags{$tag}" . &getArgs() . ">$text<$End{$tag}>";
    } elsif (exists $Defines{$tag}) {
        $rep = $Defines{$tag};
        # $(*) or $() -> $text and $(.) -> $args 
        $rep =~ s/\$\(\*?\)/$text/g ;
        $rep =~ s/\$\(\.\)/&getArgs()/ge;
        foreach $arg (keys %Args) {
            $rep =~ s/\$\($arg\)/$Args{$arg}/g;
        }
        foreach $arg (keys %Vars) {
            $rep =~ s/\$\($arg\)/$Vars{$arg}/g;
        }
        $rep =~ s/\$\((\w+)\)/ $1 /sg ;
        $rep =~ s/\$\((\w+)-(\w+)\)/ $1 $2 /sg ;
    } else {
        if ($tag =~ /^\/\w/) { # treat as implicit html-def2
           my ($_tag, $_end);
           $_tag = $_end = $tag;
           $_tag =~ s/^\///;  $_tag =~ s/_/ /g;
           $_end =~ s/^(\/[A-Za-z0-9]+).*/$1/;
           return "<$_tag".&getArgs().">$text<$_end>";
        }
        elsif ($tag =~ /^\w+\/$/) { # treat as implicit html-def1
           my $_tag = $tag;
           $_tag =~ s/\/$//;  $_tag =~ s/_/ /g;
           return "<$_tag".&getArgs()."$text>";
        }
        elsif (!exists $badBiTag{$tag}) {
            printf "unknown tag: {$tag: ...} in $Infile\n";
            $badBiTag{$tag} = $tag;
        }
        if ( $text eq "") {
           $rep = "<$tag>" ;
        } else {
           $rep = "<$tag> $text </$tag>" ;              # easy yet powerful
        }
    }

    # unless ($Special) { print STDERR "\n{$tag($text)} => $rep\n"; }

    return $rep;
}


sub x_line_replace_head {
    ($tag, $text, $rep) = @_;

    $line_replace = 1;
    if ($tag =~ /^[xX]-(.*)/) {       # will supress generation
        $tag =~ s/^[xX]-// ;          # of meta-tag if unknown
        $silent = 1;
    }else{
        $silent = 0;
    }
    
    $Vars{$tag} = $text;                             # overwrites if necessary

    if (exists $Funcs{$tag}) {
        $fun = $Funcs{$tag};
        $rep =&$fun($text);
    } elsif (exists $UniTags{$tag}) {
        $rep = "<$UniTags{$tag}" . &getArgs() . ">$text";
    } elsif (exists $Tags{$tag}) {
        $rep = "<$Tags{$tag}" . &getArgs() . ">$text<$End{$tag}>";
    } elsif (exists $Defines{$tag}) {
        $rep = $Defines{$tag};
        # $(*) or $() -> $text and $(.) -> $args 
        $rep =~ s/\$\(\*?\)/$text/g ;
        $rep =~ s/\$\(\.\)/&getArgs()/ge;
        foreach $arg (keys %Args) {
            $rep =~ s/\$\($arg\)/$Args{$arg}/g;
        }
        foreach $arg (keys %Vars) {
            $rep =~ s/\$\($arg\)/$Vars{$arg}/g;
        }
        $rep =~ s/\$\((\w+)\)/ $1 /sg ;
        $rep =~ s/\$\((\w+)-(\w+)\)/ $1 $2 /sg ;
    } else {
        if ($tag =~ /^\/\w/) { # treat as implicit html-def2
           my ($_tag, $_end);
           $_tag = $_end = $tag;
           $_tag =~ s/^\///;  $_tag =~ s/_/ /g;
           $_end =~ s/^(\/[A-Za-z0-9]+).*/$1/;
           return "<$_tag".&getArgs().">$text<$_end>";
        }
        elsif ($tag =~ /^\w+\/$/) { # treat as implicit html-def1
           my $_tag = $tag;
           $_tag =~ s/\/$//;  $_tag =~ s/_/ /g;
           return "<$_tag".&getArgs()."$text>";
        }
        elsif ($silent) { $rep = ""; }
        else {
            $tag  =~ s/^\s+//sg; $tag  =~ s/\s+$//sg; # chop surrounding
            $text =~ s/^\s+//sg; $text =~ s/\s+$//sg; # whitespaces
            if ( $text eq "") {
               $rep = "<meta name=\"$tag\">" ;    
            } else {
               $rep = "<meta name=\"$tag\" content=\"$text\">" ;
            }
        }
    }

    # unless ($Special) { print STDERR "\n^$tag($text) => $rep\n"; }
    return $rep;
}

sub x_line_replace {
    ($tag, $text, $rep) = @_;

    $line_replace = 2;
    if ($tag =~ /^[xX]-(.*)/) {       # will supress a warning if the tag
        $tag =~ s/^[xX]-// ;          # had been prepended with X-
        $silent = 1;
    }else{
        $silent = 0;
    }
    
    $Vars{$tag} = $text;                             # overwrites if necessary
    
    if (exists $Funcs{$tag}) {
        $fun = $Funcs{$tag};
        $rep =&$fun($text);
    } elsif (exists $UniTags{$tag}) {
        $rep = "<$UniTags{$tag}" . &getArgs() . ">$text";
    } elsif (exists $Tags{$tag}) {
        $rep = "<$Tags{$tag}" . &getArgs() . ">$text<$End{$tag}>";
    } elsif (exists $Defines{$tag}) {
        $rep = $Defines{$tag};
        # $(*) or $() -> $text and $(.) -> $args 
        $rep =~ s/\$\(\*?\)/$text/g ;
        $rep =~ s/\$\(\.\)/&getArgs()/ge;
        foreach $arg (keys %Args) {
            $rep =~ s/\$\($arg\)/$Args{$arg}/g;
        }
        foreach $arg (keys %Vars) {
            $rep =~ s/\$\($arg\)/$Vars{$arg}/g;
        }
        $rep =~ s/\$\((\w+)\)/ $1 /sg ;
        $rep =~ s/\$\((\w+)-(\w+)\)/ $1 $2 /sg ;
    } else {
        if ($tag =~ /^\/\w/) { # treat as implicit html-def2
           my ($_tag, $_end);
           $_tag = $_end = $tag;
           $_tag =~ s/^\///;  $_tag =~ s/_/ /g;
           $_end =~ s/^(\/[A-Za-z0-9]+).*/$1/;
           return "<$_tag".&getArgs().">$text<$_end>";
        }
        elsif ($tag =~ /^\w+\/$/) { # treat as implicit html-def1
           my $_tag = $tag;
           $_tag =~ s/\/$//;  $_tag =~ s/_/ /g;
           return "<$_tag".&getArgs()."$text>";
        }
        elsif (!exists $badUniTag{$tag}) {
            printf "unknown tag: ^$tag: ...^ in $Infile\n" if ! $silent;
            $badUniTag{$tag} = $tag;
        }
        $rep = "<br >$tag: $text";       # just keep it
    }

    #    print STDERR "\n$tag($text) \n=> \n$rep\n";
    return $rep;
}

sub execute {
    my ($program, $out, $ignoreError) = @_;
    $ignoreError = 1 if $program =~ s/^-//;
    
    $out = qx/$program/;
    die "Error executing $program: $?" if !$ignoreError and $?;

    chomp $out;
    return $out;
}

sub undefineTag {
    my $tag;
    foreach $tag (split(",", $_[0])) {
        delete $Tags{$tag} if exists $Tags{$tag};
        delete $End{$tag} if exists $End{$tag};
        delete $UniTags{$tag} if exists $UniTags{$tag};
        delete $Defines{$tag} if exists $Defines{$tag}
    }
}

sub add_html_tag2 {
    my ($tag, $repl, $end) = ($_[0] =~ /^(\S+)\s+"(.*?)"(?:\s*"(.*)")?/);
    if (!defined $repl) { 
       $tag = $_[0];  $tag =~ s/^(\S+)/$1/; $repl = $tag; 
    }

    die "Duplicate tag $tag ($currentFile:$.)" if exists $Tags{$tag} and !$silent;
    die "You must supply a replacement ($currentFile:$.)" unless length $repl;
    
    $Tags{$tag} = $repl;
    if (defined($end)) {
        $End{$tag} = $end;
    } else {
        $end = $repl;
        $end =~ s/^\s*(.*)$/$1/s ; # kill prefix-spaces
        $end =~ s/\s.*//sg; # chop off everything past the first whitespace
        $End{$tag} = "/$end";
    }

    return "";
}

sub add_html_tag1 {
    my ($tag, $repl) = ($_[0] =~ /^(\S+)\s+"(.*)"/);

    # die "Already define as bitag ($currentFile:$.)" if exists $Tags{$tag};
    die "Duplicate tag $tag ($currentFile:$.)" if exists $UniTags{$tag} and !$silent;
    die "You must supply a replacement ($currentFile:$.)" unless length $repl;

    $UniTags{$tag} = $repl;

    return "";
}

# Parse some well known escape codes
sub parseEscapeCodes {
    my ($s) = @_;
    $s =~ s/\\n/\n/;
    #    print STDERR "define = $s\n";
    return $s;
}

sub add_html_def {
    my ($tag, $repl) = ($_[0] =~ /^(\S+)\s+(.*)/) 
                or die "Invalid line: $_[0]\n";
                
    $repl =~ s/^\"(.*)\"/$1/g; # chop off surrounding quotes
    $repl =~ s/^\&quot\;(.*)\&quot\;/$1/g; # chop off surrounding quotes

    die "Already define as bitag ($currentFile:$.)" if exists $Tags{$tag} and !$silent;
    die "Already defined as unitag $tag ($currentFile:$.)" if exists $UniTags{$tag} and !$silent;
    die "You must supply a replacement ($currentFile:$.)" unless length $repl;

    $Defines{$tag} = &html_direct (&parseEscapeCodes($repl));

    return "";
}

sub add_implicit_html_def {
    my ($tag, $repl) = @_ ;
    
    $tag  =~ s/^\s+//sg; $tag  =~ s/\s+$//sg; # chop surrounding
    $repl =~ s/^\s+//sg; $text =~ s/\s+$//sg; # whitespaces
    $repl =~ s/^\"(.*)\"$/$1/g; # chop off surrounding quotes
    $repl =~ s/^\&quot\;(.*)\&quot\;$/$1/g; # chop off surrounding quotes

    die "Already defined as bitag ($currentFile:$.)" if exists $Tags{$tag} and !$silent;
    die "Already defined as unitag $tag ($currentFile:$.)" if exists $UniTags{$tag} and !$silent;
    die "You must supply a replacement ($currentFile:$.)" unless length $repl;

    $Defines{$tag} = &html_direct (&parseEscapeCodes($repl));

    return "";
}

sub html_add_esc {
    my ($tag, $repl) = ($_[0] =~ /^(\S+)\s+(.*)/) or die "Invalid escape: $_[0]";
    die "Already defined: $1"  if $Escapes{$1};
    $Escapes{$1} = "&$2;";
}

sub html_direct
{
   my $rep = $_[0];
   $rep =~ s/&lt;/\</sg;
   $rep =~  s/&gt;/\>/sg;
   $rep =~  s/&amp;/&/sg;
   $rep =~  s/&quot;/\"/sg ;
   return $rep;
}


