#! /usr/local/bin/perl
##---------------------------------------------------------------------------##
##  File:
##      dtd2html
##  Author:
##      Earl Hood       ehood@convex.com
##  Description:
##	dtd2html is a Perl program to generate HTML documents to allow
##	people to navigate thru an SGML dtd.  This program requires the
##	use of the "dtd" package.
##---------------------------------------------------------------------------##
##  Copyright (C) 1994	Earl Hood, ehood@convex.com
##
##  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., 675 Mass Ave, Cambridge, MA 02139, USA.
##---------------------------------------------------------------------------##

package main;

##---------------------------------------------------------------------------##

## Store name of program ##
($PROG = $0) =~ s/.*\///;

%ElemDesc = ();		# Associative array of element descriptions

## Require libraries ##
unshift(@INC, '.');
require "dtd/dtd.pl" || die "Unable to require dtd.pl\n";
require "newgetopt.pl" || die "Unable to require newgetopt.pl\n";

## Import dtd variables ##
$rni = $dtd'rni;
$PCDATA = $dtd'PCDATA;
$RCDATA = $dtd'RCDATA;
$SDATA = $dtd'SDATA;
$CDATA = $dtd'CDATA;
$EMPTY = $dtd'EMPTY;

## Replace dtd'print_elem routine to spit out HTML ##
$sub_print_elem =<<'EndOfSub';
package dtd;
sub print_elem {
    local($elem, $level, *open) = @_;
    local($i, $indent, $base);
    if ($level == 1) {
	print($TREEFILE $elem, "\n"); }
    else {
	for ($i=2; $i < $level; $i++) {
	    $indent .= ($open{$i} ? " | " : "   "); }
	if ($elem ne "") {
	    if ($elem =~ /\(/) {
		$indent .= " | ";
		print($TREEFILE $indent, $elem, "\n");
	    }
	    elsif ($elem =~ /\|/) {
		print($TREEFILE $indent, $elem, "\n");
	    } else {
		$indent .= " |_";
		if (&'DTDis_keyword($elem)) {
		    print($TREEFILE $indent, $elem, "\n");
		} else {
		    $elem  =~ /^(\S+)(.*)$/;
		    print $TREEFILE $indent,
			    "<A HREF=\"$1.html\"><STRONG>$1</STRONG></A>$2\n";
		}
	    }
	}
    }
}
EndOfSub
eval $sub_print_elem;

##---------------------------------------------------------------------------##
			    ##------------##
			    ## MAIN BLOCK ##
			    ##------------##
&get_cli_opts();

&DTDread_mapfile($MAPFILE);
&DTDread_dtd($DTD);

&print_elemdesc() if $ELEMLIST;
&read_descfile();
&write_home_page();
&write_topelem_page();
&write_allelem_page();
&write_tree_page() if $TREE;
foreach (&DTDget_elements()) {
    &write_elem_page($_);
}

exit 0;
			    ##----------##
			    ## END MAIN ##
			    ##----------##

##---------------------------------------------------------------------------##
##				SubRoutines				     ##
##---------------------------------------------------------------------------##
sub get_cli_opts {
    local($tmp);
    &usage() unless
    &NGetOpt(
	"mapfile=s",	# Entity map file
	"descfile=s",	# Element description file
	"outdir=s",	# Destination directory for files
	"dtdname=s",	# Name of DTD
	"homename=s",	# Name of home page
	"topname=s",	# Name of top element list page
	"allname=s",	# Name of all element list page
	"treename=s",	# Name of tree page
	"tree",		# Create tree file
	"level=i",	# Cutoff level for tree
	"treetop=s",	# Comma separated list of top elements for tree
	"elemlist",	# Flag to generate empty element list
	"help"		# Print usage
    );
    &usage() if defined($opt_help);

    $DTDFILE  = ($ARGV[0] ? $ARGV[0] : "");
    $MAPFILE  = ($opt_mapfile ? $opt_mapfile : "map.txt");
    $DESCFILE = ($opt_descfile ? $opt_descfile : "");
    $OUTDIR   = ($opt_outdir ? $opt_outdir : ".");
    $DTDNAME  = ($opt_dtdname ? $opt_dtdname : "");
    $HOMEFILE = ($opt_homename ? $opt_homename : "DTD-HOME.html");
    $TOPFILE  = ($opt_topname ? $opt_topname : "TOP-ELEM.html");
    $ALLFILE  = ($opt_allname ? $opt_allname : "ALL-ELEM.html");
    $TREEFILE = ($opt_treename ? $opt_treename : "DTD-TREE.html");
    $TREE     = (defined($opt_tree) ? 1 : 0);
    $ELEMLIST = (defined($opt_elemlist) ? 1 : 0);
    $LEVEL    = ($opt_level ? $opt_level : 15);
    $TREETOP  = ($opt_treetop ? $opt_treetop : "");
 
    if ($DTDFILE) {
	open(DTD_FILE, "< $DTDFILE") || die "Unable to open $DTDFILE\n";
	$DTD = "main'DTD_FILE";
    } else {
	$DTD = 'STDIN';
    }
    if (! $DTDNAME) {
	if ($DTDFILE) {
	    $DTDNAME = $DTDFILE;
	    $DTDNAME =~ s/.*\///;  $DTDNAME =~ s/^(.*)\..*$/\1/;
	} else {
	    $DTDNAME = 'Unknown'; }
    }
    $DTDNAME .= ' DTD';
}
##---------------------------------------------------------------------------##
sub read_descfile {
    return unless $DESCFILE;
    open(FILE, "< $DESCFILE") ||
	(warn "Unable to open $DESCFILE\n" && return);
    
    local($elem, $txt);
    while(<FILE>) {
	if (/^\s*<\?\s*DTD2HTML\s+(\S+)\s*>\s*$/) {
	    $ElemDesc{$elem} = $txt if $elem;
	    $elem = $1;
	    $elem =~ tr/A-Z/a-z/;  $txt = "";
	} else {
	    $txt .= $_;
	}
    }
    $ElemDesc{$elem} = $txt if $elem;

    close(FILE);
}
##---------------------------------------------------------------------------##
sub write_home_page {
    open(PGFILE, "> $OUTDIR/$HOMEFILE") ||
	die "Unable to create $OUTDIR/$HOMEFILE\n";
    &print_head(PGFILE, $DTDNAME);
    print PGFILE "<HR>\n";
    &print_info(PGFILE);
    &print_goto_topall(PGFILE);
    &print_end(PGFILE);
    close(PGFILE);
}
##---------------------------------------------------------------------------##
sub write_tree_page {
    local(@array);
    open(PGFILE, "> $OUTDIR/$TREEFILE") ||
	die "Unable to create $OUTDIR/$TREEFILE\n";
    &print_head(PGFILE, "$DTDNAME: <BR>Top element trees");
    if ($TREETOP) { @array = split(/,/, $TREETOP);
    } else { @array = &DTDget_top_elements(); }
    foreach (@array) {
	tr/a-z/A-Z/;
	print PGFILE "<HR>\n",
		     "<H2>$_</H2>\n",
		     "<HR>\n",
		     "<PRE>\n";
	&DTDprint_tree($_, $LEVEL, "main'PGFILE");
	print PGFILE "</PRE>\n";
    }
    &print_goto_topall(PGFILE);
    &print_end(PGFILE);
    close(PGFILE);
}
##---------------------------------------------------------------------------##
sub write_topelem_page {
    local(@array);
    open(PGFILE, "> $OUTDIR/$TOPFILE") ||
	die "Unable to create $OUTDIR/$TOPFILE\n";
    &print_head(PGFILE, "Top Elements in $DTDNAME");
    @array = &DTDget_top_elements();
    &print_elem_list(PGFILE, *array);
    &print_goto_topall(PGFILE);
    &print_end(PGFILE);
    close(PGFILE);
}
##---------------------------------------------------------------------------##
sub write_allelem_page {
    local(@array);
    open(PGFILE, "> $OUTDIR/$ALLFILE") ||
	die "Unable to create $OUTDIR/$ALLFILE\n";
    &print_head(PGFILE, "All elements in $DTDNAME");
    @array = &DTDget_elements();
    &print_elem_list(PGFILE, *array);
    &print_goto_topall(PGFILE);
    &print_end(PGFILE);
    close(PGFILE);
}
##---------------------------------------------------------------------------##
sub write_elem_page {
    local($elem) = shift @_;
    local($Elem) = $elem;
    local(@array);
    open(FILE, "> $OUTDIR/$elem.html") ||
	die "Unable to create $OUTDIR/$elem.html\n";
    $Elem =~ tr/a-z/A-Z/;
    &print_head(FILE, $Elem);
    &print_elem_desc(FILE, $elem);
    print FILE "<HR>\n";

    ## Content ##
    print FILE "<H2>Content</H2>\n";
    @array = sort &dtd'extract_elem_names($dtd'ElemCont{$elem});
    &remove_dups(*array);
    &print_elem_list(FILE, *array);

    ## Inclusions ##
    @array = sort &dtd'extract_elem_names($dtd'ElemInc{$elem});
    if ($#array >= 0) {
	print FILE "<H3>Inclusions</H3>\n";
	&remove_dups(*array);
	&print_elem_list(FILE, *array);
    }

    ## Exclusions ##
    @array = sort &dtd'extract_elem_names($dtd'ElemExc{$elem});
    if ($#array >= 0) {
	print FILE "<H3>Exclusions</H3>\n";
	&remove_dups(*array);
	&print_elem_list(FILE, *array);
    }

    ## Tag Minimization ##
    print FILE "<HR>\n";
    @array = split(' ', $dtd'ElemTag{$elem});
    if ($#array > 0) {
	print FILE "<DL>\n";
	print FILE "<DT>Tag Minimization<DD>\n";
	print FILE "Open Tag: ";
	if ($array[0] eq '-') {
	    print FILE "<EM>REQUIRED</EM><BR>\n";
	} else {
	    print FILE "<EM>OPTIONAL</EM><BR>\n";
	}
	print FILE "Close Tag: ";
	if ($array[1] eq '-') {
	    print FILE "<EM>REQUIRED</EM><BR>\n";
	} else {
	    print FILE "<EM>OPTIONAL</EM><BR>\n";
	}
	print FILE "</DL>\n";
    }

    print FILE "See <A HREF=\"$elem.attr.html\">",
	       "<STRONG>ATTRIBUTES</STRONG></A><BR>\n",
	       "See <A HREF=\"$elem.cont.html\">",
	       "<STRONG>CONTENT DECLARATION</STRONG></A>\n",
	       "<P>\n";

    print FILE "<HR>\n";

    &print_parent_list(FILE, $elem);

    &print_goto_topall(FILE);
    &print_end(FILE);
    close(FILE);

    &write_elem_attr($elem);
    &write_elem_syntax($elem);
}
##---------------------------------------------------------------------------##
sub write_elem_attr {
    local($elem) = shift @_;
    local($Elem) = $elem;
    local(%attr, @array, @vals, $def, $tmp);

    open(FILE, "> $OUTDIR/$elem.attr.html") ||
	die "Unable to create $OUTDIR/$elem.attr.html\n";

    $Elem =~ tr/a-z/A-Z/;
    &print_head(FILE, "$Elem: <BR>Attributes");
    &print_attr_desc(FILE, $elem);
    print FILE "<HR>\n";

    %attr = &DTDget_elem_attr($elem);
    @array = sort keys %attr;
    if ($#array >= 0) {
	foreach (@array) {
	    ($tmp = $_) =~ tr/a-z/A-Z/;
	    print FILE "<H2>$tmp</H2>\n", "<P>\n";
	    @vals = split(/$;/o, $attr{$_});
	    $def = shift @vals;
	    &print_attr_list(FILE, *vals);
	    print FILE "<P>\n", "Default = <STRONG>$def</STRONG><P>\n";
	}
    } else {
	print FILE "No attributes<P>\n";
    }
    print FILE "<HR>\n",
	   "Go Back to <A HREF=\"$elem.html\">",
	   "<STRONG>$Elem</STRONG></A><P>\n";
    &print_end(FILE);
    close(FILE);
}
##---------------------------------------------------------------------------##
sub write_elem_syntax {
    local($elem) = shift @_;
    local($Elem) = $elem;

    open(FILE, "> $OUTDIR/$elem.cont.html") ||
	die "Unable to create $OUTDIR/$elem.cont.html\n";

    $Elem =~ tr/a-z/A-Z/;
    &print_head(FILE, "$Elem: <BR>Content Model Declaration");

    ## Content Rule ##
    print FILE "<H2>Content Rule</H2>\n";
    print FILE "<CODE>", $dtd'ElemCont{$elem}, "</CODE><P>\n";
    ## Inclusions ##
    if ($dtd'ElemInc{$elem}) {
	print FILE "<H3>Inclusions</H3>\n";
	print FILE "<CODE>", $dtd'ElemInc{$elem}, "</CODE><P>\n";
    }
    ## Exclusions ##
    if ($dtd'ElemExc{$elem}) {
	print FILE "<H3>Exclusions</H3>\n";
	print FILE "<CODE>", $dtd'ElemExc{$elem}, "</CODE><P>\n";
    }
    print FILE "<HR>\n",
	   "Go Back to <A HREF=\"$elem.html\">",
	   "<STRONG>$Elem</STRONG></A><P>\n";
    &print_end(FILE);
    close(FILE);
}
##---------------------------------------------------------------------------##
sub print_elem_desc {
    local($handle, $elem) = @_;
    $elem =~ tr/A-Z/a-z/;
    if ($elem && $ElemDesc{$elem}) {
	print $handle $ElemDesc{$elem};
    }
}
##---------------------------------------------------------------------------##
sub print_attr_desc {
    local($handle, $elem) = @_;
    $elem =~ tr/A-Z/a-z/;
    $attr = $elem . '*';
    if ($elem && $ElemDesc{$attr}) {
	print $handle $ElemDesc{$attr};
    }
}
##---------------------------------------------------------------------------##
sub print_goto_topall {
    local($old) = select(shift);
    print <<End;
<HR>
Goto <A HREF="$TOPFILE"><STRONG>top element list</STRONG></A>.
<BR>
Goto <A HREF="$ALLFILE"><STRONG>all element list</STRONG></A>.
<BR>
End
    print "Goto <A HREF=\"$TREEFILE\"><STRONG>tree</STRONG></A>.\n" if $TREE;
    print "<P>\n";
    print <<End;
Goto <A HREF="$HOMEFILE"><STRONG>$DTDNAME</STRONG></A> main page.
<P>
End
    select($old);
}
##---------------------------------------------------------------------------##
sub print_attr_list {
    local($handle, *array) = @_;
    print $handle "<UL COMPACT>\n";
    foreach (@array) {
	if (&DTDis_attr_keyword($_)) { tr/a-z/A-Z/; }
	print $handle qq|<LI>$_</LI>\n|;
    }
    print $handle "</UL>\n";
}
##---------------------------------------------------------------------------##
sub print_elem_list {
    local($handle, *array) = @_;
    print $handle "<UL COMPACT>\n";
    foreach (@array) {
	if (&DTDis_elem_keyword($_)) {
	    tr/a-z/A-Z/;
	    print $handle qq|<LI>$_</LI>\n|;
	} else {
	    print $handle qq|<LI><A HREF="$_.html">|,
			  qq|<STRONG>$_</STRONG></A></LI>\n|;
	}
    }
    print $handle "</UL>\n";
}
##---------------------------------------------------------------------------##
sub print_head {
    local($handle, $title) = @_;
    local($old) = select($handle);
    print <<End;
<HTML>
<HEAD>
<TITLE>$title</TITLE>
</HEAD>
<BODY>
<H1>$title</H1>
End
    select($old);
}
##---------------------------------------------------------------------------##
sub print_end {
    local($old) = select(shift);
    print <<End;
<HR>
<ADDRESS>$DTDNAME</ADDRESS><P>
</BODY>
</HTML>
End
    select($old);
}
##---------------------------------------------------------------------------##
sub print_info {
    local($old) = select(shift);
    print <<End;
This document was automatically generated by the Perl program
<A HREF="dtd2html.doc.html"><CODE>dtd2html</CODE></A>.
The information of this document conveys the structural information
defined by the $DTDNAME.
<P>
End
    select($old);
}
##---------------------------------------------------------------------------##
sub remove_dups {
    local(*array) = shift;
    local(%dup);
    @array = grep($dup{$_}++ < 1, @array);
}
##---------------------------------------------------------------------------##
sub print_elemdesc {
    foreach (&DTDget_elements()) {
	print STDOUT "<?DTD2HTML $_ >\n<P>\n";
    }
    exit 0;
}
##---------------------------------------------------------------------------##
sub print_parent_list {
    local($handle, $elem) = @_;
    local(@array);

    print $handle "<H2>Parent Elements</H2>\n";
    @array = &DTDget_parents($elem);
    if ($#array >= 0) {
	&remove_dups(*array);
	print $handle "<UL COMPACT>\n";
	foreach (@array) {
	    tr/A-Z/a-z/;
	    print $handle qq|<LI><A HREF="$_.html">|,
			  qq|<STRONG>$_</STRONG></A></LI>\n|;
	}
	print $handle "</UL>\n";
    } else {
	print $handle "None<P>\n";
    }
}
##---------------------------------------------------------------------------##
sub usage {
    print STDOUT <<EndOfUsage;
Usage: $PROG [<options>] file 
Options:
    allname <filename>	: Filename of all element list page.
    descfile <filename> : Element description file.
    dtdname <string>	: String name of DTD for HTML output.
    elemlist		: Flag to generate empty element list.
    help		: This message.
    homename <filename>	: Filename of home page.
    level <#>		: Cutoff level for tree.
    mapfile <filename>	: Entity map file.
    outdir <path>	: Destination directory for HTML files.
    topname <filename>	: Filename of top element list page.
    tree		: Create tree file.
    treename <filename>	: Filename of tree page.
    treetop <string>	: Comma separated list of top elements for tree.

EndOfUsage
    exit 0;
}
##---------------------------------------------------------------------------##
