# dtdformat module for RefEntrys

# $Id: refentry.pl,v 1.3 2001/08/01 14:52:12 nwalsh Exp $

$fileext = ".xml";

$config{'expanded-element-index'} = "elements";
$config{'unexpanded-element-index'} = "dtdelem";
$config{'expanded-entity-index'} = "entities";
$config{'unexpanded-entity-index'} = "dtdent";
$config{'notation-index'} = 'notations';

# ======================================================================

my $dtdparseHomepage = "http://sourceforge.net/projects/dtdparse/";

# ======================================================================

sub elementRefpurpose {
    my $count   = shift;
    my $name    = $elements[$count];

    return "&$baseid.purp.elem.$name;";
}

sub entityRefpurpose {
    my $count  = shift;
    my $name   = $entities[$count];
    my $entity = $entities{$name};

    return "&$baseid.purp." . $entity->getAttribute('type') . ".$name;";
}

sub notationRefpurpose {
    my $count   = shift;
    my $name    = $notations[$count];

    return "&$baseid.purp.notn.$name;";
}

sub elementDescription {
    my $count = shift;

    return "<para>desc</para>\n";
}

sub entityDescription {
    my $count = shift;

    return "<para>desc</para>\n";
}

sub notationDescription {
    my $count = shift;

    return "<para>desc</para>\n";
}

# ======================================================================

sub basenames {
    my @names = @_;
    my %basename = ();
    my %usedname = ();

    foreach my $name (@names) {
	my $count = 2;
	my $bname = lc($name);

	if ($usedname{$bname}) {
	    $bname = lc($name) . $count;
	    while ($usedname{$bname}) {
		$bname++;
	    }
	}

	$basename{$name} = $bname;
	$usedname{$name} = 1;
    }

    return %basename;
}

# ======================================================================

sub formatElement {
    my $count   = shift;
    my $html    = "";

    my $name    = $elements[$count];
    my $element = $elements{$name};

    my $cmex    = undef;
    my $cmunx   = undef;
    my $incl    = undef;
    my $excl    = undef;

    my $node = $element->getFirstChild();
    while ($node) {
	if ($node->getNodeType() == XML::DOM::ELEMENT_NODE) {
	    $cmex = $node if $node->getTagName() eq 'content-model-expanded';
	    $cmunx = $node if $node->getTagName() eq 'content-model';
	    $incl = $node if $node->getTagName() eq 'inclusions';
	    $excl = $node if $node->getTagName() eq 'exclusions';
	}
	$node = $node->getNextSibling();
    }

    $html .= &formatElementHeader($count);

    $html .= &formatElementTitle($count);

    if ($option{'synopsis'}) {
	if ($expanded eq 'expanded') {
	    $html .= &formatElementSynopsis($count, $cmex, $cmex);
	} else {
	    $html .= &formatElementSynopsis($count, $cmunx, $cmex);
	}
    }

    $html .= &formatElementDescription($count, $count)
        if $option{'description'};

    $html .= &formatElementExamples($count, $count) if $option{'examples'};

    $html .= &formatElementFooter($count);
}

sub formatElementHeader {
    my $count     = shift;
    my $html      = "";
    my $name      = $elements[$count];
    my $element   = $elements{$name};

    $html .= "<refentry id=\"$baseid.elem.$name\">\n";
    $html .= "<!-- Generated by DTDParse version $main::VERSION -->\n";
    $html .= "<!-- see $dtdparseHomepage -->\n\n";

    return $html;
}

sub formatElementTitle {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html = "";

    $html .= "<refmeta>\n";
    $html .= "<refentrytitle>";
    $html .= $element->getAttribute('name');
    $html .= "</refentrytitle>\n";
    $html .= "<refmiscinfo>Element</refmiscinfo>\n";
    $html .= "</refmeta>\n\n";

    $html .= "<refnamediv>\n";
    $html .= "<refname>" . $element->getAttribute('name') . "</refname>\n";
    $html .= "<refpurpose>";
    $html .= &elementRefpurpose($count);
    $html .= "</refpurpose>\n";
    $html .= "</refnamediv>\n\n";
}

sub formatElementSynopsis {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $cm      = shift;
    my $cmex    = shift;
    my $html = "";

    # What are the possibilities: mixed content, element content, or
    # declared content...
    my $mixed    = $element->getAttribute('content-type') eq 'mixed';
    my $declared = (!$mixed &&
		    $element->getAttribute('content-type') ne 'element');

    $html .= "<refsynopsisdiv>\n";
    $html .= "<informaltable frame='all' role='elemsynop'>\n";
    $html .= "<tgroup cols='3'>\n";
    $html .= "<colspec colnum='1' colname='c1'/>\n";
    $html .= "<colspec colnum='2' colname='c2'/>\n";
    $html .= "<colspec colnum='3' colname='c3'/>\n";
    $html .= "<tbody>\n";

    $html .= "<row rowsep='0' role='cmtitle'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><emphasis role='bold'>";

    if ($mixed) {
	$html .= "Mixed Content Model";
    } elsif ($declared) {
	$html .= "Declared Content";
    } else {
	$html .= "Content Model";
    }

    $html .= "</emphasis></entry>\n";
    $html .= "</row>\n";

    $html .= "<row rowsep='1' role='cmsynop'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><synopsis>";
    $html .= $element->getAttribute('name') . " ::=\n";
    $html .= &formatContentModel($count, $cm);

    $html .= "</synopsis></entry>\n";
    $html .= "</row>\n";

    $html .= &formatInclusions($count, $incl)
        if $incl && $option{'inclusions'};

    $html .= &formatExclusions($count, $excl)
        if $excl && $option{'exclusions'};

    $html .= &formatAttributeList($count) if $option{'attributes'};

    $html .= &formatTagMinimization($count) if $option{'tag-minimization'};

    $html .= &formatElementAppearsIn($count) if $option{'appears-in'};

    $html .= "</tbody>\n";
    $html .= "</tgroup>\n";
    $html .= "</informaltable>\n";
    $html .= "</refsynopsisdiv>\n\n";

    return $html;
}

sub formatInclusions {
    my $count   = shift;
    my $cm      = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html    = "";

    $html .= "<row rowsep='0' role='incltitle'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><emphasis role='bold'>Inclusions</emphasis></entry>\n";
    $html .= "</row>\n";

    $html .= "<row rowsep='1' role='inclsynop'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><synopsis>";
    $html .= &formatContentModel($count, $cm);
    $html .= "</synopsis></entry>\n";
    $html .= "</row>\n";

    return $html;
}

sub formatExclusions {
    my $count   = shift;
    my $cm      = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html    = "";

    $html .= "<row rowsep='0' role='excltitle'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><emphasis role='bold'>Exclusions</emphasis></entry>\n";
    $html .= "</row>\n";

    $html .= "<row rowsep='1' role='exclsynop'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><synopsis>";
    $html .= &formatContentModel($count, $cm);
    $html .= "</synopsis></entry>\n";
    $html .= "</row>\n";

    return $html;
}

sub formatAttributeList {
    my $count   = shift;
    my $html    = "";
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $attlist = $attlists{$name};

    $html .= "<row rowsep='0' role='attrtitle'>\n";
    $html .= "<entry colsep='0'\n";
    $html .= "><emphasis role='bold'>Attributes</emphasis></entry>\n";
    $html .= "<entry namest='c2' nameend='c3'></entry>\n";
    $html .= "</row>\n";

    if (defined($attlist)) {
	$html .= "<row rowsep='1' role='attrheader'>\n";
	$html .= "<entry role='th'><para\n";
	$html .= "><emphasis role='bold'>Name</emphasis></para></entry>\n";
	$html .= "<entry role='th'><para\n";
	$html .= "><emphasis role='bold'>Type</emphasis></para></entry>\n";
	$html .= "<entry role='th'><para\n";
	$html .= "><emphasis role='bold'>Default</emphasis></para></entry>\n";
	$html .= "</row>\n";
	$html .= &formatAttributes($attlist);
    } else {
	$html .= "<row>\n";
	$html .= "<entry namest='c1' nameend='c3'>None</entry>\n";
	$html .= "</row>\n";
    }

    return $html;
}

sub formatAttributes {
    my $attlist = shift;
    my $html    = "";
    my $attrs   = $attlist->getElementsByTagName("attribute");

    for (my $count = 0; $count < $attrs->getLength(); $count++) {
	my $attr = $attrs->item($count);

	my $name     = $attr->getAttribute('name');
	my $type     = $attr->getAttribute('value');
	my $decltype = $attr->getAttribute('type');
	my $default = "";

	if ($decltype eq '#IMPLIED') {
	    $default = "<emphasis>None</emphasis>";
	} elsif ($decltype eq '#REQUIRED') {
	    $default = "<emphasis>Required</emphasis>";
	} elsif ($decltype eq '#CONREF') {
	    $default = "<emphasis>Content reference</emphasis>";
	} else {
	    $default = $attr->getAttribute('default');
	    if ($default =~ /\"/) {
		$default = "'" . $default . "'";
	    } else {
		$default = "\"" . $default . "\"";
	    }
	}

	if ($decltype eq '#FIXED') {
	    $default = $default . " <emphasis>(fixed)</emphasis>";
	}

	$html .= "<row>\n";
	$html .= &formatCell($name);
	$html .= &formatValues($type, $attr);
	$html .= &formatCell($default);
	$html .= "</row>\n";
    }

    return $html;
}

sub formatCell {
    my $value = shift;

    return "<entry align='left' valign='top'>$value</entry>\n";
}

sub formatValues {
    my $values = shift;
    my $attr = shift;
    my $enum = $attr->getAttribute('enumeration');
    my $html = "";

    if ($enum eq 'no' || $enum eq '') {
	return &formatCell($values);
    }

    $html .= "<entry align='left' valign='top'>";
    if ($enum eq 'notation') {
	$html .= "<para><emphasis>Enumerated notation:</emphasis>\n</para>";
    } else {
	$html .= "<para><emphasis>Enumeration:</emphasis>\n</para>";
    }

    $html .= "<simplelist>\n";
    foreach my $val (sort { uc($a) cmp uc($b) }
		     split(/\s+/, $attr->getAttribute('value'))) {
	$html .= "<member>$val</member>\n";
    }
    $html .= "</simplelist></entry>\n";

    return $html;
}

sub formatTagMinimization {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html    = "";
    my $stagm   = $element->getAttribute('stagm') || "-";
    my $etagm   = $element->getAttribute('etagm') || "-";

    if ($element->getAttribute('stagm')
	|| $element->getAttribute('etagm')) {
	my (%min) = ('--' => "Both the start- and end-tags are required for this element.",
		     'OO' => "Both the start- and end-tags are optional for this element, if your SGML declaration allows tag minimization.",
		     'O-' => "The start-tag is optional for this element, if your SGML declaration allows tag minimization.  The end-tag is required.",
		     '-O' => "The start-tag is required for this element.  The end-tag is optional, if your SGML declaration allows minimization."
		    );

	$html .= "<row rowsep='0' role='tmtitle'>\n";
	$html .= "<entry namest='c1' nameend='c3' align='left'\n";
	$html .= "><emphasis role='bold'>Tag Minimization</emphasis>";
	$html .= "</entry>\n";
	$html .= "</row>\n";

	$html .= "<row rowsep='1' role='tmsynop'>\n";
	$html .= "<entry namest='c1' nameend='c3' align='left'\n";
	$html .= "><para>";
	$html .= $min{$stagm . $etagm};
	$html .= "</para></entry>\n";
	$html .= "</row>\n";
    }

    return $html;
}

sub formatElementAppearsIn {
    my $count = shift;
    my $html = "";
    my $elementname = $elements[$count];
    my $element = $elements{$elementname};
    my %appears = ();

    %appears = %{$APPEARSIN{$elementname}} if exists $APPEARSIN{$elementname};

    if (%appears) {
	my @ents = sort { uc($a) cmp uc($b) } keys %appears;
	my $href = $config{$expanded . "-entity-dir"};


	$html .= "<row rowsep='0' role='petitle'>\n";
	$html .= "<entry namest='c1' nameend='c3' align='left'\n";
	$html .= "><emphasis role='bold'>Parameter Entities</emphasis>";
	$html .= "</entry>\n";
	$html .= "</row>\n";

	while (@ents) {
	    $html .= "<row rowsep='0' role='pe'>\n";

	    for (my $count = 0; $count < 3; $count++) {
		my $name = shift @ents;
		if ($name) {
		    my $entity = $entities{$name};
		    $html .= "<entry colsep='0'>";
		    $html .= $entity->getAttribute('name');
		    $html .= "</entry>\n";
		} else {
		    $html .= "<entry colsep='0'></entry>\n";
		}
	    }

	    $html .= "</row>\n";
	}
    }

    return $html;
}

sub formatElementDescription {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $desc    = &elementDescription($count);
    my $html    = "";

    return "" if !defined($desc);

    $html .= "<refsect1><title>Description</title>\n";
    $html .= $desc;
    $html .= "\n\n";

    $html .= &formatParents($count) if $option{'parents'};

    $html .= &formatChildren($count) if $option{'children'};

    $html .= "</refsect1>\n\n";

    return $html;
}

sub formatParents {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html    = "";

    if (exists $PARENTS{$name}) {
	$html .= "<refsect2><title>Parents</title>\n";
	$html .= "<para>These elements contain ";
	$html .= $element->getAttribute('name') . ":\n";
	$html .= "<simplelist type='inline'>";

	my $pname;
	foreach $pname (sort { uc($a) cmp uc($b) } keys %{$PARENTS{$name}}) {
	    my $child = $elements{$pname};
	    $html .= "<member>";
	    $html .= "<link linkend=\"$baseid.elem.$pname\">";
	    $html .= "<sgmltag>" . $child->getAttribute('name') . "</sgmltag>";
	    $html .= "</link>";
	    $html .= "</member>\n";
	}

	$html .= "</simplelist>.</para>\n";
	$html .= "</refsect2>\n\n";
    }

    return $html;
}

sub formatChildren {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};
    my $html    = "";
    my $mixed    = $element->getAttribute('content-type') eq 'mixed';
    my $declared = (!$mixed &&
		    $element->getAttribute('content-type') ne 'element');

    return "" if $declared; # can't be any children...

    if (exists $CHILDREN{$name}
	|| exists $POSSINCL{$name}
	|| exists $POSSEXCL{$name}) {
	$html .= "<refsect2><title>Children</title>\n";
    }

    if (exists $CHILDREN{$name}) {
	$html .= "<para>The following elements occur in ";
	$html .= $element->getAttribute('name') . ":\n";
	$html .= "<simplelist type='inline'>";

	my $cname;
	foreach $cname (sort { uc($a) cmp uc($b) } keys %{$CHILDREN{$name}}) {
	    my $child = $elements{$cname};

	    die "Unexpected error (1): can't find element \"$cname\".\n"
		if !$child;

	    $html .= "<member>";
	    $html .= "<link linkend=\"$baseid.elem.$cname\">";
	    $html .= "<sgmltag>";
	    $html .= $child->getAttribute('name');
	    $html .= "</sgmltag>";
	    $html .= "</link>";
	    $html .= "</member>\n";
	}

	$html .= "</simplelist>.</para>\n";
    }

    if (exists $POSSINCL{$name}) {
	$html .= "<para>In some contexts, the following elements are\n";
	$html .= "allowed anywhere:\n";
	$html .= "<simplelist type='inline'>\n";

	my $cname;
	foreach $cname (sort { uc($a) cmp uc($b) } keys %{$POSSINCL{$name}}) {
	    my $child = $elements{$cname};

	    die "Unexpected error (2): can't find element \"$cname\".\n"
		if !$child;

	    $html .= "<member>";
	    $html .= "<link linkend=\"$baseid.elem.$cname\">";
	    $html .= "<sgmltag>";
	    $html .= $child->getAttribute('name');
	    $html .= "</sgmltag>";
	    $html .= "</link>";
	    $html .= "</member>\n";
	}

	$html .= "</simplelist>.</para>\n\n";
    }

    if (exists $POSSEXCL{$name}) {
	$html .= "<para>In some contexts, the following elements are\n";
	$html .= "excluded:\n";
	$html .= "<simplelist type='inline'>\n";

	my $cname;
	foreach $cname (sort { uc($a) cmp uc($b) } keys %{$POSSEXCL{$name}}) {
	    my $element = $elements{$cname};
	    $html .= "<member>";
	    $html .= "<link linkend=\"$baseid.elem.$cname\">";
	    $html .= "<sgmltag>";
	    $html .= $element->getAttribute('name');
	    $html .= "</sgmltag>";
	    $html .= "</link>";
	    $html .= "</member>\n";
	}

	$html .= "</simplelist>.</para>\n\n";
    }

    if (exists $CHILDREN{$name}
	|| exists $POSSINCL{$name}
	|| exists $POSSEXCL{$name}) {
	$html .= "</refsect2>\n\n";
    }

    return $html;
}

sub formatElementExamples {
    my $count   = shift;
    my $name    = $elements[$count];
    my $element = $elements{$name};

    return "";
}

sub formatElementFooter {
    my $count = shift;
    my $html = "";

    $html .= "</refentry>\n";

    return $html;
}

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

my $state = 'NONE';
my $depth = 0;
my $col = 0;

sub formatContentModel {
    my $count = shift;
    my $cm = shift;
    my $node = $cm->getFirstChild();
    my $html = "";

    $state = "NONE";
    $depth = 0;
    $col = 0;
    while ($node) {
	if ($node->getNodeType == XML::DOM::ELEMENT_NODE) {
	    $html .= formatContentModelElement($node);
	}
	$node = $node->getNextSibling();
    }

    return $html;
}

sub formatContentModelElement {
    my $node = shift;
    my $html = "";

    if ($node->getNodeType == XML::DOM::ELEMENT_NODE) {
	if ($node->getTagName() eq 'sequence-group') {
	    $html .= &formatCMGroup($node, ",");
	} elsif ($node->getTagName() eq 'or-group') {
	    $html .= &formatCMGroup($node, "|");
	} elsif ($node->getTagName() eq 'and-group') {
	    $html .= &formatCMGroup($node, "&");
	} elsif ($node->getTagName() eq 'element-name') {
	    $html .= &formatCMElement($node);
	} elsif ($node->getTagName() eq 'parament-name') {
	    $html .= &formatCMParament($node);
	} elsif ($node->getTagName() eq 'pcdata') {
	    $html .= &formatCMPCDATA($node);
	} elsif ($node->getTagName() eq 'cdata') {
	    $html .= &formatCMCDATA($node);
	} elsif ($node->getTagName() eq 'rcdata') {
	    $html .= &formatCMRCDATA($node);
	} elsif ($node->getTagName() eq 'empty') {
	    $html .= &formatCMEMPTY($node);
	} elsif ($node->getTagName() eq 'any') {
	    $html .= &formatCMANY($node);
	} else {
	    die "Unexpected node: \"" . $node->getTagName() . "\"\n";
	}
	$node = $node->getNextSibling();
    } else {
	die "Unexpected node type.\n";
    }

    return $html;
}

sub formatCMGroup {
    my $group = shift;
    my $occur = $group->getAttribute('occurrence');
    my $sep = shift;
    my $first = 1;
    my $html = "";

    if ($state ne 'NONE' && $state ne 'OPEN') {
	$html .= "\n";
	$html .= " " x $depth if $depth > 0;
	$col = $depth;
	$state = 'NEWLINE';
    }

    $html .= "(";
    $state = 'OPEN';
    $depth++;
    $col++;

    my $node = $group->getFirstChild();
    while ($node) {
	if ($node->getNodeType == XML::DOM::ELEMENT_NODE) {
	    if (!$first) {
		$html .= $sep;
		$col++;

		if ($state ne 'NEWLINE' && ($col > 60)) {
		    $html .= "\n";
		    $html .= " " x $depth if $depth > 0;
		    $col = $depth;
		    $state = 'NEWLINE';
		}
	    }
	    $html .= &formatContentModelElement($node);
	    $first = 0;
	}
	$node = $node->getNextSibling();
    }

    $html .= ")";
    $col++;

    if ($occur) {
	$html .= $occur;
	$col++;
    }

    $state = 'CLOSE';
    $depth--;

    return $html;
}

sub formatCMElement {
    my $element = shift;
    my $name = $element->getAttribute('name');
    my $occur = $element->getAttribute('occurrence');
    my $href = "";
    my $html = "";

    $name = lc($name) if !$option{'case-sensitive'};

    if ($state eq 'CLOSE') {
	$html .= "\n";
	$html .= " " x $depth if $depth > 0;
	$col = $depth;
	$state = 'NEWLINE';
    }

    $html .= "<link linkend='$baseid.elem.$name'>";
    $html .= $element->getAttribute('name');
    $html .= "</link>";
    $col += length($name);

    if ($occur) {
	$html .= $occur;
	$col++;
    }

    $state = 'ELEMENT';

    return $html;
}

sub formatCMParament {
    my $element = shift;
    my $name = $element->getAttribute('name');
    my $html = "";

    if ($state eq 'CLOSE') {
	$html .= "\n";
	$html .= " " x $depth if $depth > 0;
	$col = $depth;
	$state = 'NEWLINE';
    }

    $html .= "<link linkend='$baseid.param.$name'>";
    $html .= "\%" . $name . ";";
    $html .= "</link>";
    $col += length($name) + 2;

    $state = 'PARAMENT';

    return $html;
}

sub formatCMPCDATA {
    my $html = "";

    $html .= "#PCDATA";
    $col += 7;
    $state = 'PCDATA';

    return $html;
}

sub formatCMCDATA {
    my $html = "";

    $html .= "CDATA";
    $col += 5;
    $state = 'CDATA';

    return $html;
}

sub formatCMRCDATA {
    my $html = "";

    $html .= "RCDATA";
    $col += 5;
    $state = 'RCDATA';

    return $html;
}

sub formatCMEMPTY {
    my $html = "";

    $html .= "EMPTY";
    $col += 5;
    $state = 'EMPTY';

    return $html;
}

sub formatCMANY {
    my $html = "";

    $html .= "ANY";
    $col += 3;
    $state = 'ANY';

    return $html;
}

# ======================================================================

sub formatEntity {
    my $count   = shift;
    my $name    = $entities[$count];
    my $entity  = $entities{$name};
    my $html    = "";
    my $textnl;

    if ($expanded eq 'expanded') {
	$textnl = $entity->getElementsByTagName("text-expanded");
    } else {
	$textnl = $entity->getElementsByTagName("text");
    }

    $html .= &formatEntityHeader($count);

    $html .= &formatEntityTitle($count);

    $html .= &formatEntitySynopsis($count, $textnl)
	if $option{'synopsis'};

    $html .= &formatEntityAppearsIn($count) if $option{'appears-in'};

    $html .= &formatEntityDescription($count) if $option{'description'};

    $html .= &formatEntityExamples($count) if $option{'examples'};

    $html .= &formatEntityFooter($count);

    return $html;
}

sub formatEntityHeader {
    my $count     = shift;
    my $html      = "";
    my $name      = $entities[$count];

    $html .= "<refentry id=\"$baseid.param.$name\">\n";
    $html .= "<!-- Generated by DTDParse version $main::VERSION -->\n";
    $html .= "<!-- see $dtdparseHomepage -->\n\n";

    return $html;
}

sub formatEntityTitle {
    my $count  = shift;
    my $name   = $entities[$count];
    my $entity = $entities{$name};
    my $type   = $entity->getAttribute("type");
    my $html   = "";

    $html .= "<refmeta>\n";
    $html .= "<refentrytitle>";
    $html .= $entity->getAttribute('name');
    $html .= "</refentrytitle>\n";

    if ($type eq 'gen') {
	$html .= "<refmiscinfo>General Entity</refmiscinfo>\n";
    } elsif ($type eq 'ndata'
	     || $type eq 'cdata'
	     || $type eq 'sdata'
	     || $type eq 'pi') {
	$html .= "<refmiscinfo>" . uc($type) . " Entity</refmiscinfo>\n";
    } else {
	$html .= "<refmiscinfo>Parameter Entity</refmiscinfo>\n";
    }

    $html .= "</refmeta>\n\n";

    $html .= "<refnamediv>\n";
    $html .= "<refname>" . $entity->getAttribute('name') . "</refname>\n";
    $html .= "<refpurpose>";
    $html .= &entityRefpurpose($count);
    $html .= "</refpurpose>\n";
    $html .= "</refnamediv>\n\n";
}

sub formatEntitySynopsis {
    my $count   = shift;
    my $textnl  = shift;
    my $name    = $entities[$count];
    my $entity  = $entities{$name};
    my $html    = "";
    my $type    = $entity->getAttribute("type");
    my $public  = $entity->getAttribute("public");
    my $system  = $entity->getAttribute("system");
    my $text    = "";

    if ($textnl->getLength() > 0) {
	my $textnode = $textnl->item(0);
	my $content = $textnode->getFirstChild();
	if ($content) {
	    $text = $content->getData();
	} else {
	    $text = "";
	}
    }

    $html .= "<refsynopsisdiv>\n";
    $html .= "<informaltable frame='all' role='elemsynop'>\n";
    $html .= "<tgroup cols='3'>\n";
    $html .= "<colspec colnum='1' colname='c1'/>\n";
    $html .= "<colspec colnum='2' colname='c2'/>\n";
    $html .= "<colspec colnum='3' colname='c3'/>\n";
    $html .= "<tbody>\n";

    $html .= "<row rowsep='0' role='cmtitle'>\n";
    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
    $html .= "><emphasis role='bold'>";

    if ($type eq 'gen') {
	if ($public || $system) {
	    $html .= "External General Entity";
	    $html .= "</emphasis></entry>\n";
	    $html .= "</row>\n";
	    $html .= "<row rowsep='1' role='cmsynop'>\n";
	    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
	    $html .= ">";

	    $html .= "<para><emphasis role='bold'>Public identifier</emphasis>: $public\n</para>" if $public;
	    $html .= "<para><emphasis role='bold'>System identifier</emphasis>: $system\n</para>" if $system;

	    $html .= "</entry>\n";
	    $html .= "</row>\n";
	} else {
	    $html .= "General Entity";
	    $html .= "</emphasis></entry>\n";
	    $html .= "</row>\n";
	    $html .= "<row rowsep='1' role='cmsynop'>\n";
	    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
	    $html .= "><synopsis>";

	    if ($text =~ /\"/) {
		$html .= "'$text'\n";
	    } else {
		$html .= "\"$text\"\n";
	    }

	    $html .= "</synopsis></entry>\n";
	    $html .= "</row>\n";
	}
    }

    if ($type eq 'param') {
	if ($public || $system) {
	    $html .= "External Entity";
	    $html .= "</emphasis></entry>\n";
	    $html .= "</row>\n";
	    $html .= "<row rowsep='1' role='cmsynop'>\n";
	    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
	    $html .= ">";

	    $html .= "<para><emphasis role='bold'>Public identifier</emphasis>: $public\n</para>" if $public;
	    $html .= "<para><emphasis role='bold'>System identifier</emphasis>: $system\n</para>" if $system;

	    $html .= "</entry>\n";
	    $html .= "</row>\n";
	} else {
	    $html .= "Parameter Entity\n";
	    $html .= "</emphasis></entry>\n";
	    $html .= "</row>\n";
	    $html .= "<row rowsep='1' role='cmsynop'>\n";
	    $html .= "<entry namest='c1' nameend='c3' align='left'\n";
	    $html .= "><synopsis>";

	    # OK, it's a parameter entity. Now, does it look like a
	    # content model fragment

	    my $cmfragment = &cmFragment($text);

	    while ($text =~ /\%?[-a-z0-9.:_]+;?/is) {
		my $pre = $`;
		my $match = $&;
		$text = $';

		$html .= $pre;

		if ($pre =~ /\#$/) {
		    # if it comes after a '#', it's a keyword...
		    $html .= $match;
		    next;
		}

		if ($match =~ /\%([^;]+);?/) {
		    $name = $1;
		    if (exists $entities{$name}) {
			$html .= $match;
		    } else {
			$html .= $match;
		    }
		} elsif ($cmfragment) {
		    $name = $match;
		    $name = lc($name) if !$option{'case-sensitive'};
		    if (exists $elements{$name}) {
			my $linkend = "$baseid.elem.$name";
			$html .= "<link linkend=\"$linkend\">$match</link>";
		    } else {
			$html .= $match;
		    }
		} else {
		    $html .= $match;
		}
	    }
	    $html .= $text;
	    $html .= "</synopsis></entry>\n";
	    $html .= "</row>\n";
	}
    }

    if ($type eq 'ndata' || $type eq 'cdata') {
	my $notation = $entity->getAttribute('notation');

	$html .= uc($type) . " Entity";
	$html .= "</emphasis></entry>\n";
	$html .= "</row>\n";
	$html .= "<row rowsep='1' role='cmsynop'>\n";
	$html .= "<entry namest='c1' nameend='c3' align='left'\n";
	$html .= ">";

	$html .= "<para><emphasis role='bold'>Notation</emphasis>: $notation\n</para>";
	$html .= "<para><emphasis role='bold'>Public identifier</emphasis>: $public\n</para>" if $public;
	$html .= "<para><emphasis role='bold'>System identifier</emphasis>: $system\n</para>" if $system;

	$html .= "</entry>\n";
	$html .= "</row>\n";
    }

    if ($type eq 'sdata' || $type eq 'pi') {
	$html .= uc($type) . " Entity";
	$html .= "</emphasis></entry>\n";
	$html .= "</row>\n";
	$html .= "<row rowsep='1' role='cmsynop'>\n";
	$html .= "<entry namest='c1' nameend='c3' align='left'\n";
	$html .= "><synopsis>";

	if ($text =~ /\"/) {
	    $html .= "'$text'\n";
	} else {
	    $html .= "\"$text\"\n";
	}

	$html .= "</synopsis></entry>\n";
	$html .= "</row>\n";
    }

    $html .= "</tbody>\n";
    $html .= "</tgroup>\n";
    $html .= "</informaltable>\n";
    $html .= "</refsynopsisdiv>\n\n";

    return $html;
}

sub formatEntityAppearsIn {
    my $count = shift;
    my $html = "";
    my $entityname = $entities[$count];
    my $entity = $entities{$entityname};
    my %appears = ();
    my $key = "%$entityname";

    %appears = %{$APPEARSIN{$key}} if exists $APPEARSIN{$key};

    if (%appears) {
	my @ents = sort { uc($a) cmp uc($b) } keys %appears;

	$html .= "<refsect1><title>Parameter Entities</title>\n";
	$html .= "<para>The following parameter entities contain ";
	$html .= $entity->getAttribute('name') . ":\n";
	$html .= "<simplelist type='inline'>\n";

	for (my $count = 0; $count <= $#ents; $count++) {
	    my $entity = $entities{$ents[$count]};
	    $html .= "<member>";
	    $html .= "<link linkend=\"$baseid.";
	    $html .= $entity->getAttribute('type') . ".";
	    $html .= $entity->getAttribute('name') . "\">";
	    $html .= $entity->getAttribute('name');
	    $html .= "</link>";
	    $html .= "</member>\n";
	}
	$html .= "</simplelist></para>\n";
	$html .= "</refsect1>\n";
    }

    return $html;
}

sub formatEntityDescription {
    my $count   = shift;
    my $name    = $entities[$count];
    my $entity  = $entities{$name};
    my $desc    = &entityDescription($count);
    my $html    = "";

    return "" if !defined($desc);

    $html .= "<refsect1><title>Description</title>\n";
    $html .= $desc;
    $html .= "</refsect1>\n\n";

    return $html;
}

sub formatEntityExamples {
    my $count   = shift;
    my $name    = $entities[$count];
    my $entity  = $entities{$name};

    return "";
}

sub formatEntityFooter {
    my $count = shift;
    my $html = "";

    $html .= "</refentry>\n";

    return $html;
}

# ======================================================================

sub formatNotation {
    my $count   = shift;
    my $html    = "";

    my $name    = $notations[$count];
    my $element = $notations{$name};

    $html .= &formatNotationHeader($count);

    $html .= &formatNotationTitle($count);

    if ($option{'synopsis'}) {
	$html .= &formatNotationSynopsis($count);
    }

    $html .= &formatNotationDescription($count)
        if $option{'description'};

    $html .= &formatNotationExamples($count) if $option{'examples'};

    $html .= &formatNotationFooter($count);
}

sub formatNotationHeader {
    my $count     = shift;
    my $html      = "";
    my $name      = $notations[$count];

    $html .= "<refentry id=\"$baseid.notn.$name\">\n";
    $html .= "<!-- Generated by DTDParse version $main::VERSION -->\n";
    $html .= "<!-- see $dtdparseHomepage -->\n\n";

    return $html;
}

sub formatNotationTitle {
    my $count    = shift;
    my $name     = $notations[$count];
    my $notation = $notations{$name};
    my $html = "";

    $html .= "<refmeta>\n";
    $html .= "<refentrytitle>";
    $html .= $notation->getAttribute('name');
    $html .= "</refentrytitle>\n";
    $html .= "<refmiscinfo>Notation</refmiscinfo>\n";
    $html .= "</refmeta>\n\n";

    $html .= "<refnamediv>\n";
    $html .= "<refname>" . $notation->getAttribute('name') . "</refname>\n";
    $html .= "<refpurpose>";
    $html .= &notationRefpurpose($count);
    $html .= "</refpurpose>\n";
    $html .= "</refnamediv>\n\n";
}

sub formatNotationSynopsis {
    my $count    = shift;
    my $name     = $notations[$count];
    my $notation = $notations{$name};
    my $html    = "";
    my $public  = $notation->getAttribute("public");
    my $system  = $notation->getAttribute("system");

    $html .= "<refsynopsisdiv>\n";

    if ($public) {
	$html .= "<para>\nPublic identifier:\n";
	$html .= "<emphasis>$public</emphasis>.";
	$html .= "</para>\n\n";
    }

    if ($system) {
	$html .= "<para>\nSystem identifier:\n";
	$html .= "<emphasis>$system</emphasis>.";
	$html .= "</para>\n\n";
    }

    if (!$public && !$system) {
	$html .= "<para>\n<quote>System</quote> specified\n";
	$html .= "without a system identifier.";
	$html .= "</para>\n\n";
    }

    return $html;
}

sub formatNotationDescription {
    my $count    = shift;
    my $name     = $notations[$count];
    my $notation = $notations{$name};
    my $desc     = &notationDescription($count);
    my $html     = "";

    return "" if !defined($desc);

    $html .= "<refsect1><title>Description</title>\n";
    $html .= $desc;
    $html .= "</refsect1>\n\n";

    return $html;
}

sub formatNotationExamples {
    my $count   = shift;
    my $name    = $notations[$count];
    my $element = $notations{$name};

    return "";
}

sub formatNotationFooter {
    my $count = shift;
    my $html = "";

    $html .= "</refentry>\n";

    return $html;
}

# ======================================================================

sub writeElementIndexes {
    my $basedir   = shift;
    my $title     = $dtd->getDocumentElement->getAttribute('title');
    my ($entfile, $sgmfile, $sysdir);
    local (*F, $_);

    $entfile = $basedir . "/" . $config{$expanded . "-element-index"} . ".ent";
    $sgmfile = $basedir . "/" . $config{$expanded . "-element-index"} . $fileext;
    $sysdir  = $config{$expanded . "-element-dir"};

    open (F, ">$entfile");
    foreach $name (@elements) {
	my $basename = $ELEMBASE{$name};
	print F "<!ENTITY $baseid.elem.$name ";
	print F "SYSTEM \"$sysdir/$basename$fileext\">\n";
	print F "<!ENTITY $baseid.purp.elem.$name \"purpose\">\n";
    }
    close (F);

    open (F, ">$sgmfile");
    print F "<reference><title>$title Element Reference</title>\n";
    foreach $name (@elements) {
	print F "&$baseid.elem.$name;\n";
    }
    print F "</reference>\n";
    close (F);
}

sub writeEntityIndexes {
    my $basedir   = shift;
    my $title     = $dtd->getDocumentElement->getAttribute('title');
    my ($entfile, $sgmfile, $sysdir);
    local (*F, $_);

    $entfile = $basedir . "/" . $config{$expanded . "-entity-index"} . ".ent";
    $sgmfile = $basedir . "/" . $config{$expanded . "-entity-index"} . $fileext;
    $sysdir  = $config{$expanded . "-entity-dir"};

    open (F, ">$entfile");
    foreach $name (@entities) {
	my $entity = $entities{$name};
	my $basename = $ENTBASE{$name};
	print F "<!ENTITY $baseid.param.$name ";
	print F "SYSTEM \"$sysdir/$basename$fileext\">\n";
	print F "<!ENTITY $baseid.purp.", $entity->getAttribute('type'), ".$name \"purpose\">\n";
    }
    close (F);

    open (F, ">$sgmfile");
    print F "<reference><title>$title Entity Reference</title>\n";
    foreach $name (@entities) {
	print F "&$baseid.param.$name;\n";
    }
    print F "</reference>\n";
    close (F);
}

sub writeNotationIndexes {
    my $basedir   = shift;
    my $title     = $dtd->getDocumentElement->getAttribute('title');
    my ($notnfile, $sgmfile, $sysdir);
    local (*F, $_);

    $notnfile = $basedir . "/" . $config{"notation-index"} . ".ent";
    $sgmfile = $basedir . "/" . $config{"notation-index"} . $fileext;
    $sysdir  = $config{"notation-dir"};

    open (F, ">$notnfile");
    foreach $name (@notations) {
	my $notation = $notations{$name};
	my $basename = $NOTBASE{$name};
	print F "<!ENTITY $baseid.notn.$name ";
	print F "SYSTEM \"$sysdir/$basename$fileext\">\n";
	print F "<!ENTITY $baseid.purp.notn.$name \"purpose\">\n";
    }
    close (F);

    open (F, ">$sgmfile");
    print F "<reference><title>$title Notation Reference</title>\n";
    foreach $name (@notations) {
	print F "&$baseid.notn.$name;\n";
    }
    print F "</reference>\n";
    close (F);
}

sub writeIndex {
    my $basedir   = shift;
    my $title     = $dtd->getDocumentElement->getAttribute('title');
    my $entfile   = $config{"expanded-entity-index"};
    my $elemfile  = $config{"expanded-element-index"};
    my $notfile   = $config{"notation-index"};
    my $root = $dtd->getDocumentElement();
    my $elements = $root->getElementsByTagName('element');
    my $entities = $root->getElementsByTagName('entity');
    my $notations = $root->getElementsByTagName('notation');
    my $elemcount = $elements->getLength();
    my $entcount = $entities->getLength();
    my $notcount = $notations->getLength();
    local (*F, $_);

    # nop;
}

1;
