'di';
'ig00';
# true-dtd -- Henry S. Thompson <HThompson@ed.ac.uk>
#
# Copyright Henry S. Thompson
# No warranty expressed or implied
# Use/distribute under terms of GNU Public License
#
# Version 1.0 16 October 1993
#
# This file is also a (modest) man page, hence the rubbish above.
# Just feed it to ...roff -man to get the manual page itself.
#
# Take an sgml file and produce the actual single stream processed
# by a conformant application.
#
# Usage: true-dtd [switches] file(s . . .)
# Give -help to get a list of switches and arguments
#
# !!! Assumes A Conformant Document !!! (Indeed Depends on It!!!)
# (but can sometimes be of use in debugging a failing document)
#
# Assumes a vanilla sgml declaration for now.
#
# Prompts for filenames for all external references, but provides
# for saving the results and re-using them (see -f and -d switches below).
#
# Various things either not handled at all (SUBDOC, LINK) or
# stubs are present but not yet exercised (SHORTREF, probably others).
# Contributions to extend coverage welcome!
#
# handle switches
# -x or -help or -? prints a list
%bsw_array=("err","\$t_err:0:log and display positions of failure",
	 "decl","\$t_decl:1:trace DOCTYPE, ELEMENT, ATTLIST and ENTITY",
	 "baddoc","\$t_losing_doc:1:dump the state of the document on failure",
	 "ext","\$t_ext_refs:1:show open and close of external references",
	 "gent","\$r_gen_ent:1:retain general entity definitions - NIY",
	 "pent","\$r_parm_ent:1:retain parameter entity definitions - NIY",
	 "elt","\$r_elt:1:retain element definitions - NIY");
%fsw_array=("i","&fake_incl:fake an INCLUDE definition for ARG a la sgmls",
	    "x","&help:print info on calling sequence",
	    "?","&help:print info on calling sequence",
	    "h","&help:print info on calling sequence",
	    "f","&ext_tab:read an entity lookup table from file ARG.
\tThe file should contain name:prompt,filename,...,name:prompt,filename",
           "d","&d_ext_tab:dump the acquired entity lookup table to file ARG");
# initialise the switch variables
foreach $sd (values %bsw_array) {
    ($name,$def)=split(":",$sd);
    eval "$name=$def";
};
# then interpret the command line
while (($polarity,$switch)=($ARGV[0]=~/^([+-])(.*)$/)) {
#    print "$switch:$polarity\n";
    last unless $switch; # - as filename
    shift(@ARGV);
    last if ($switch eq "-");
    if ($sd=$bsw_array{$switch}) {
	# a binary switch
	($name)=split(":",$sd);
	eval "$name=(\$polarity eq '+')";
    }
    elsif ($sd=$fsw_array{substr($switch,0,1)}) {
	# an arg-taking switch
	($sub)=split(":",$sd);
	$arg=substr($switch,1);
	die $@ unless (eval "$sub(\"$arg\")");
    }
    else {
	warn "unrecognised switch: $sw\n";
	&help;
	die;
    };
};

sub fake_incl {
    local ($parm_name)=@_;
    $p_entities{$parm_name}="0:9";
}

sub d_ext_tab {
    ($d_ext_file)=@_;
    warn "no file specified for -d\n" unless $d_ext_file;
    $d_ext_file;
}

sub ext_tab {
    ($ext_tab_file)=@_;
    if ($ext_tab_file) {
	if (! open(READ,$ext_tab_file)) {
	    warn "couldn't open $ext_tab_file to read entity lookup table: $?";
	    die;
	};
	$tmp="\%ext_tab=(";
	while (<READ>) {
	    $tmp.=$_;
	};
	eval $tmp.")";
	%ext_tab;
    }
    else {
	warn "no lookup table file provided for -f\n";
	die;
    };
}

sub dump {
    if ($d_ext_file) {
	if (open(DUMP,">$d_ext_file")) {
	    foreach $id (sort keys %ext_tab) {
		$val=$ext_tab{$id};
		$id=~s/"/\\"/g;
		if ($not_first) {
		    print DUMP ",\n";
		}
		else {
		    $not_first=1;
		};
		print DUMP "\"$id\",\"",$val,"\"";
	    };
	    print DUMP "\n";
	    close(DUMP);
	}
	else {
	    warn "can't open $d_ext_file to dump entity table: $?";
	};
    };
}

sub help {
    print STDERR "Calling sequence:  true-dtd switches* --? files+
There are two kinds of switches, binary, turning on or off as they are
set plus or minus, and argument taking, of the form -<letter>
with an adjoined string argument.
The binary switches are listed below, with their default indicated.
NIY means \"Not Implemented Yet\", i.e. the indicated default is the only
behaviour available.\n";
    foreach $bsw (sort keys %bsw_array) {
	($var,$def,$help)=split(":",$bsw_array{$bsw});
	print STDERR $def?"+":"-","$bsw\t$help\n";
    };
    print STDERR "The argument taking switches are as follows:\n";
    foreach $fsw (sort keys %fsw_array) {
	($sub,$help)=split(":",$fsw_array{$fsw});
	next if ($sub eq "&help");
	print STDERR "${fsw}ARG\t$help\n";
    };
    print STDERR "-x, -? and -help all print this message.
-- can be used to indicate the end of the switches and the beginning of
the input filenames.
- is valid as a filename, indicated standard input.\n";
    0;
}
#
# a few constants
%charTbl=("RS","\n","RE","\r","SPACE"," ");
@keyPriority=("IGNORE","CDATA","RCDATA","INCLUDE");
#
# cheat a bit, to provide a def'n for -i switches and get past 0
$doc="\"INCLUDE\"\n";
$deleted=0;
#
# read in the whole thing
while (<>) {
    $doc.=$_;
};
$bound=length $doc;
$end=&SGMLdocEnt(10);
&dump;
if ($end) {
    $result=substr($doc,10,$end-10)."\n";
#    $result=substr($doc,10)."\n";
    $result=~tr/ \t/ /s;
    $result=~s/\n /\n/g;
    $result=~tr/\n\r/\n/s;
    $result=~s/^\n//;
#   print "end=$end;bound=$bound,deleted=$deleted\n";
    print $result;
}
else {
    warn "Failed to parse";
    if (@loss_trail) {
	warn " -- furthest frontier at char $furthest\n.";
	warn "Failures at:\n";
	warn join("\n",@loss_trail),"\n";
    }
    else {
	warn ".\n";
    };
    &trace($t_losing_doc,substr($doc,11));
    die;
};

sub trace {
    ($switch,@message)=@_;
    print STDERR @message if ($switch);
#    if (($message[0] eq "ATL ") && ($message[1] eq "EMPH")) {
#	print "gotit";
#    };
    1;
}

sub die {
    &dump;
    die(@_);
}

sub del_doc {
    ($where,$how_much)=@_;
    substr($doc,$where,$how_much)="";
    $deleted+=$how_much;
    $bound-=$how_much;
    $where;
}

sub add_doc {
    local ($where,$what)=@_;
    substr($doc,$where,0)=$what;
    $wl=length $what;
    $deleted-=$wl;
    $bound+=$wl;
    $where+$wl;
}

sub force_case {
    ($from,$to)=@_;
    ($sub=substr($doc,$from,$to-$from))=~tr/a-z/A-Z/;
    substr($doc,$from,$to-$from)=$sub;
}

sub p_fail {
    ($what,$where)=@_;
    $true_where=$where+$deleted-1; # not accurate within a marked section, alas
    push(@loss_trail,sprintf("%4d %s",$true_where,$what))if ($t_err);
    $furthest=$true_where unless ($furthest >= $true_where);
    0;
}
    
sub thru_delim {
    local ($ptr,$pat)=@_;
    index($doc,$pat,$ptr);
}

sub const {
    # only for real constant patterns, with alphabetics in -- implements
    # forced upper-casing
    local ($pattern,$ptr,$length)=@_;
    if (($ptr+$length<=$bound) &&
	((($sub=substr($doc,$ptr,$length))=~tr/a-z/A-Z/) || 1) &&
        ($sub eq $pattern)) {
	substr($doc,$ptr,$length)=$pattern;
	$ptr+$length;
    }
    else {
#	if ($ptr+$length>$bound) {warn "$ptr+$length>$bound\n"};
#	&p_fail("const:$pattern",$ptr);
	0;
    };
}

sub const_p {
    # only for real constant patterns, with no alphabetics in
    local ($pattern,$ptr,$length)=@_;
    if (($ptr+$length<=$bound) &&
	(substr($doc,$ptr,$length) eq $pattern)) {
	$ptr+$length;
    }
    else {
#	if ($ptr+$length>$bound) {warn "$ptr+$length>$bound\n"};
#	&p_fail("const:$pattern",$ptr);
	0;
    };
}
sub star {
    # 0 or more occurences
    local ($name,$ptr)=@_;
    local ($res);
    while ($res=&$name($ptr)) {
	$ptr=$res;
    };
    $ptr;
}

sub star_t {
    # 0 or more occurences, with a termination pattern, used directly
    local ($name,$ptr,$term)=@_;
    local ($res);
    $res=&thru_delim($ptr,$term);
    &bounded_star($name,$ptr,$res);
}

sub bounded_star {
    local ($name,$from,$to)=@_;
    local ($res,$sub_bound)=&sub_star($name,$from,$to);
    # reflect any change below up here
    $bound+=$sub_bound-$to;
    $res;
}

sub sub_star {
    # impose local bound
    local ($name,$ptr,$bound)=@_;
    (&star($name,$ptr),$bound);
}

sub plus {
    # 1 or more occurences
    local ($name,$ptr)=@_;
    local ($res);
    if ($res=&$name($ptr)) {
	$ptr=$res;
	while ($res=&$name($ptr)) {
	    $ptr=$res;
	};
	$ptr;
    }
    else {
#	&p_fail("plus:$name",$ptr);
    };
};

sub opt {
    local ($name,$ptr)=@_;
    (&$name($ptr) || $ptr);
}

sub pat {
    # for simple disjunctive character set patterns only
    local ($pat,$ptr)=@_;
    # there must be a better way . . .
    if (($ptr<$bound) &&
# loses if $ptr>65525    (($pm)=($doc=~/^[^\000]{$ptr}($pat)/))) {
        (($pm)=(substr($doc,$ptr,1)=~/^($pat)/))) {
	$ptr+1;
    }
    else {
#	if ($ptr>=$bound) {warn "$ptr>=$bound\n"};
#	&p_fail("pat:$pat",$ptr);
	0;
    };
}

# 295
# SGMLdocEnt = s* SGMLdecl prolog docInstSet EOF
sub SGMLdocEnt {
 local ($ptr)=@_;
 local ($res_prolog,$res_SGMLdecl,$res_s);
 if (($res_s=&star("s",$ptr)) &&
# don't handle SGML declaration yet
#     ($res_SGMLdecl=&SGMLdecl($res_s)) &&
#     ($res_prolog=&prolog($res_SGMLdecl)) &&
      ($res_prolog=&prolog($res_s))) {
  # semantics
  $res_prolog;
 }
 else {
# &p_fail("SGMLdocEnt",$ptr);
 };
}

# 297
# s = "[ \n\r\t]"
sub s {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[ \n\r\t]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("s",$ptr);
 };
}

# no CONCUR or LINK
# 303
# prolog = otherProlog* dtd
sub prolog {
 local ($ptr)=@_;
 local ($res_dtd,$res_otherProlog);
 if (($res_otherProlog=&star("otherProlog",$ptr)) &&
      ($res_dtd=&dtd($res_otherProlog))) {
  # semantics
  $res_dtd;
 }
 else {
# &p_fail("prolog",$ptr);
 };
}
# otherProlog = commDecl | procInst | s
sub otherProlog {
 local ($ptr)=@_;
 local ($res);
 if ($res=&commDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&procInst($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&s($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("otherProlog",$ptr);
 };
}

# 327
# genID = name
sub genID {
 local ($ptr)=@_;
 local ($res_name);
 if (($res_name=&name($ptr))) {
  # semantics
  $res_name;
 }
 else {
# &p_fail("genID",$ptr);
 };
}

# attrSpecList = attrSpec*
sub attrSpecList {
 local ($ptr)=@_;
 local ($res_attrSpec);
 if (($res_attrSpec=&star("attrSpec",$ptr))) {
  # semantics
  $res_attrSpec;
 }
 else {
# &p_fail("attrSpecList",$ptr);
 };
}

# attrSpec = s* as1? attrValueSpec
sub attrSpec {
 local ($ptr)=@_;
 local ($res_attrValueSpec,$res_as1,$res_s);
 if (($res_s=&star("s",$ptr)) &&
      ($res_as1=&opt("as1",$res_s)) &&
      ($res_attrValueSpec=&attrValueSpec($res_as1))) {
  # semantics
  $res_attrValueSpec;
 }
 else {
# &p_fail("attrSpec",$ptr);
 };
}
# as1 = name s* "=" s*
sub as1 {
 local ($ptr)=@_;
 local ($res_s,$res_3,$res_s,$res_name);
 if (($res_name=&name($ptr)) &&
      ($res_s=&star("s",$res_name)) &&
      ($res_3=&const_p("=",$res_s,1)) &&
      ($res_s=&star("s",$res_3))) {
  # semantics
  &force_case($ptr,$res_name);
  $res_s;
 }
 else {
# &p_fail("as1",$ptr);
 };
}

# 331
# attrValueSpec = attrValue | attrValueLit
# but for our purposes, ONLY a literal or a name
sub attrValueSpec {
 local ($ptr)=@_;
 local ($res);
 if ($res=&attrValueLit($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&name($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("attrValueSpec",$ptr);
 };
}

# attrValueLit = alit | alita
sub attrValueLit {
 local ($ptr)=@_;
 local ($res);
 if ($res=&alit($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&alita($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("attrValueLit",$ptr);
 };
}
# alit = "[\"]" replCharData "[\"]"
sub alit {
 local ($ptr)=@_;
 local ($res_3,$res_replCharData,$res_1);
 if (($res_1=&const_p("\"",$ptr,1)) &&
      ($res_replCharData=&replCharData($res_1,"\"")) &&
      ($res_3=&const_p("\"",$res_replCharData,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("alit",$ptr);
 };
}
# alita = "[']" replCharData "[']"
sub alita {
 local ($ptr)=@_;
 local ($res_3,$res_replCharData,$res_1);
 if (($res_1=&const_p("'",$ptr,1)) &&
      ($res_replCharData=&replCharData($res_1,"'")) &&
      ($res_3=&const_p("'",$res_replCharData,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("alita",$ptr);
 };
}

# 333 (note we collapse a lot here)
# attrValue = charData | genEntityName | nameToken | nameTokenList
sub attrValue {
 local ($ptr)=@_;
 local ($res);
 if ($res=&charData($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&genEntityName($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&nameToken($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&nameTokenList($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("attrValue",$ptr);
 };
}
# nameTokenList = nameToken ntl*
sub nameTokenList {
 local ($ptr)=@_;
 local ($res_ntl,$res_nameToken);
 if (($res_nameToken=&nameToken($ptr)) &&
      ($res_ntl=&star("ntl",$res_nameToken))) {
  # semantics
  $res_ntl;
 }
 else {
# &p_fail("nameTokenList",$ptr);
 };
}
# ntl = "[ ]" nameToken
sub ntl {
 local ($ptr)=@_;
 local ($res_nameToken,$res_1);
 if (($res_1=&const_p(" ",$ptr,1)) &&
      ($res_nameToken=&nameToken($res_1))) {
  # semantics
  $res_nameToken;
 }
 else {
# &p_fail("ntl",$ptr);
 };
}

# notName = name
sub notName {
 local ($ptr)=@_;
 local ($res_name);
 if (($res_name=&name($ptr))) {
  # semantics
  $res_name;
  &force_case($ptr,$res_name);
 }
 else {
# &p_fail("notName",$ptr);
 };
}

# 339
# procInst = "<?" sysData ">"
sub procInst {
 local ($ptr)=@_;
 local ($res_3,$res_sysData,$res_1);
 if (($res_1=&const_p("<?",$ptr,2)) &&
      ($res_sysData=&sysData($res_1,">")) &&
      ($res_3=&const_p(">",$res_sysData,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("procInst",$ptr);
 };
}

# sysData = dataChar*
# changed from charData, to compensate for making charData SMGLChar+
sub sysData {
 local ($ptr,$term)=@_;
 local ($res_charData);
 if (($res_charData=&star_t("dataChar",$ptr,$term))) {
  # semantics
  $res_charData;
 }
 else {
# &p_fail("sysData",$ptr);
 };
}

# 343
# replCharData = rCD1*
sub replCharData {
 local ($ptr,$term)=@_;
 local ($res_rCD1);
 if (($res_rCD1=&star_t("rCD1",$ptr,$term))) {
  # semantics
  $res_rCD1;
 }
 else {
# &p_fail("replCharData",$ptr);
 };
}
# rCD1 = dataChar | charRef | genEntRef
# reordered to get references out first
sub rCD1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&charRef($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&genEntRef($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&dataChar($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("rCD1",$ptr);
 };
}

# 344
# charData = dataChar+
# changed from * -- note sysData is now star_t("dataChar") to compensate
sub charData {
 local ($ptr)=@_;
 local ($res_dataChar);
 if (($res_dataChar=&plus("dataChar",$ptr))) {
  # semantics
  $res_dataChar;
 }
 else {
# &p_fail("charData",$ptr);
 };
}
# dataChar = SGMLChar
sub dataChar {
 local ($ptr)=@_;
 local ($res_SGMLChar);
 if (($res_SGMLChar=&SGMLChar($ptr))) {
  # semantics
  $res_SGMLChar;
 }
 else {
# &p_fail("dataChar",$ptr);
 };
}

# 345
# SGMLChar = markupChar | DATACHAR
sub SGMLChar {
 local ($ptr)=@_;
 local ($res);
 if ($res=&markupChar($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&DATACHAR($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("SGMLChar",$ptr);
 };
}
# markupChar = nameChar | funcChar |  DELMCHAR
sub markupChar {
 local ($ptr)=@_;
 local ($res);
 if ($res=&nameChar($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&funcChar($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&DELMCHAR($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("markupChar",$ptr);
 };
}

# no effective definitions of these exist
# DATACHAR = "[()+,/:=?{_}@$^*|~`\\\\]"
sub DATACHAR {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[()+,/:=?{_}@$^*|~`\\\\\241-\377]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("DATACHAR",$ptr);
 };
}
# DELMCHAR = "[]<>[&%'\";#!]"
sub DELMCHAR {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[]<>[&%'\";#!]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("DELMCHAR",$ptr);
 };
}

# name = nameStartChar nameChar*
sub name {
 local ($ptr)=@_;
 local ($res_nameChar,$res_nameStartChar);
 if (($res_nameStartChar=&nameStartChar($ptr)) &&
      ($res_nameChar=&star("nameChar",$res_nameStartChar))) {
  # semantics
  $res_nameChar;
 }
 else {
# &p_fail("name",$ptr);
 };
}
# nameChar = "[-a-zA-Z0-9.]"
sub nameChar {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[-a-zA-Z0-9.]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("nameChar",$ptr);
 };
}

# 346
# nameStartChar = "[a-zA-Z]"
sub nameStartChar {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[a-zA-Z]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("nameStartChar",$ptr);
 };
}

# funcChar = "[\n\r\t ]"
sub funcChar {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[\n\r\t ]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("funcChar",$ptr);
 };
}

# 347
# number = Digit+
# plus actually handled one level up
sub number {
    local ($ptr)=@_;
    local ($res);
    if (($res=&pat("[0-9]",$ptr))) {
     # semantics
     $res;
 }
 else {
# &p_fail("number",$ptr);
 };
}

# nameToken = nameChar+
sub nameToken {
 local ($ptr)=@_;
 local ($res_nameChar);
 if (($res_nameChar=&plus("nameChar",$ptr))) {
  # semantics
  $res_nameChar;
 }
 else {
# &p_fail("nameToken",$ptr);
 };
}


# 350
# genEntRef = "&" nameGroup? name refEnd?
# doesn't handle defaulting
sub genEntRef {
 local ($ptr)=@_;
 local ($res_refEnd,$res_name,$res_nameGroup,$res_1,$p_name);
 if (($res_1=&const_p("&",$ptr,1)) &&
      ($res_nameGroup=&opt("nameGroup",$res_1)) &&
      ($res_name=&name($res_nameGroup)) &&
      ($res_refEnd=&opt("refEnd",$res_name))) {
  # semantics
     $p_name=substr($doc,$res_nameGroup,$res_name-$res_nameGroup);
     &del_doc($ptr,$res_refEnd-$ptr);
     &insert_parm($ptr,$g_entities{$p_name},"GENERAL ENTITY",$p_name);
     $ptr;
 }
 else {
# &p_fail("genEntRef",$ptr);
 };
}

# 350
# parmEntRef = "%" nameGroup? name refEnd?
# note we implement none of the group stuff
sub parmEntRef {
 local ($ptr,$reprocess)=@_;
 local ($res_refEnd,$res_name,$res_nameGroup,$res_1,$end,$p_name);
 if (($res_1=&const_p("%",$ptr,1)) &&
      ($res_nameGroup=&opt("nameGroup",$res_1)) &&
      ($res_name=&name($res_nameGroup)) &&
      ($res_refEnd=&opt("refEnd",$res_name))) {
  # semantics
     $p_name=substr($doc,$res_nameGroup,$res_name-$res_nameGroup);
     &del_doc($ptr,$res_refEnd-$ptr);
     $end=&insert_parm($ptr,$p_entities{$p_name},"PARAMETER ENTITY",$p_name);
     if ($reprocess) {
	 $ptr;
     }
     else {
	 $end;
     };
 }
 else {
# &p_fail("parmEntRef",$ptr);
 };
}

sub insert_parm {
    local ($ptr,$t_loc,$p_type,$p_name)=@_;
    &die("no def'n for $p_type $p_name") unless $t_loc;
    ($from,$to)=split(/:/,$t_loc);
    $e_text=substr($doc,$from,$to-$from);
    # check the four possibilities for entity text
    # parameter literal
    if (($e_text=~/^["']([^\000]*)['"]$/)) {
	&add_doc($ptr,$1);
    }
    # data text 
    elsif ($e_text=~/^(CDATA|SDATA|PI).*["']([^\000]*)['"]$/) {
        # no special processing
	warn "data text not implemented yet: handled as vanilla at $ptr\n";
	&add_doc($ptr,$2); 
    }
    # bracketed text
    elsif ($e_text=~/^(STARTTAG|ENDTAG|MS|MD).*["']([^\000]*)['"]$/) {
	$new=$o_brackets{$1}.$2.$c_brackets{$1};
	&add_doc($ptr,$new);
    }
    # external spec or die
    else {
	&insert_ext($ptr,$e_text,$p_type,$p_name) ||
	 (warn("couldn't interpret entity text:$from:$to:$t_loc\n$e_text\n")&&
	   &die("a:",join("::",%p_entities,%g_entities),"\n"));
    };
}

sub insert_ext {
    local ($ptr,$e_text,$p_type,$p_name,$new,$eoi)=@_;
    if (($id_type,$id_spec,$x,$type)=
	 ($e_text=~/^[ \t\n\r]*(SYSTEM|PUBLIC)[ \t\n\r]+(.*)([ \t\n\r]+(SUBDOC|[CSN]DATA).*)?$/)) {
	warn "entity type on external entities not implemented yet: handled as vanilla at $ptr\n" if ($type);
	$new=&external_text($p_name,"$p_type $id_type $id_spec");
	$eoi=&add_doc($ptr,$new);
	&add_doc($eoi,"<!---*-true-trace-*-End of $p_name\n-->\n")
		 if ($t_ext_refs);
	$eoi;
    };
}

sub external_text {
    # demand a file for an external text reference
    local ($p_name,$id)=@_;
    local ($idata);
    if (! ($input=$ext_tab{"$p_name:$id"})) {
	print STDERR
	    "To define $p_name, please supply a full file name for the following id:\n $id\n";
	chop ($input=<STDIN>);
    };
    if (-r $input) {
	&die("Couldn't open $input after all: $?") unless open(EXT,$input);
	print STDERR "Starting $input for $p_name\n" if ($t_ext_refs);
	$ext_tab{"$p_name:$id"}=$input;
	while (<EXT>) {
	    $idata.=$_;
	};
	close(EXT);
	$idata;
    }
    else {
	print STDERR "$input non-existant or unreadable, please try again.\n";
	&external_text($p_name,$id);
    };
}

# 352
# refEnd = ";" | "\r"
sub refEnd {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const_p(";",$ptr,1)) {
  # semantics
  $res;
 }
 elsif ($res=&const("\r",$ptr,2)) {
  # semantics
  $res;
 }
 else {
# &p_fail("refEnd",$ptr);
 };
}

# 356
# charRef = "&#" cR1 refEnd?
sub charRef {
 local ($ptr)=@_;
 local ($res_refEnd,$res_cR1,$res_1);
 if (($res_1=&const_p("&#",$ptr,2)) &&
      ($res_cR1=&cR1($res_1)) &&
      ($res_refEnd=&opt("refEnd",$res_cR1))) {
  # semantics
  # not sure this is correct
     $ccs=substr($doc,$res_1,$res_cR1-$res_1);
     &del_doc($ptr,$res_refEnd-$ptr);
     if ($ccs=~/^[0-9]/) {
	 # a number, insert an pass by
	 &add_doc($ptr,sprintf("%c",$ccs+0));
     }
     else {
	 &add_doc($ptr,$charTbl{$ccs});
	 # note we leave attention BEFORE the insertion, by the book
	 $ptr;
     };
 }
 else {
# &p_fail("charRef",$ptr);
 };
}
# cR1 = "RE" | "SPACE" | "RS" | number
sub cR1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("RE",$ptr,2)) {
  # semantics
  $res;
 }
 elsif ($res=&const("SPACE",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("RS",$ptr,2)) {
  # semantics
  $res;
 }
 elsif ($res=&plus("number",$ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("cR1",$ptr);
 };
}

# 372
# ps = s | EOF | parmEntRef | comment
sub ps {
 local ($ptr)=@_;
 local ($res);
 if ($res=&s($ptr)) {
  # semantics
  $res;
 }
# elsif ($res=&EOF($ptr)) {
#  # semantics
#  $res;
# }
 elsif ($res=&parmEntRef($ptr,1)) {
  # semantics
  $res;
 }
 elsif ($res=&comment($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("ps",$ptr);
 };
}

# 373
# parmLit = plit | plita
sub parmLit {
 local ($ptr)=@_;
 local ($res);
 if ($res=&plit($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&plita($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("parmLit",$ptr);
 };
}
# plit = "[\"]" replParmDat "[\"]"
sub plit {
 local ($ptr)=@_;
 local ($res_3,$res_replParmDat,$res_1);
 if (($res_1=&const_p("\"",$ptr,1)) &&
      ($res_replParmDat=&replParmDat($res_1,"\"")) &&
      ($res_3=&const_p("\"",$res_replParmDat,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("plit",$ptr);
 };
}
# plita = "[']" replParmDat "[']"
sub plita {
 local ($ptr)=@_;
 local ($res_3,$res_replParmDat,$res_1);
 if (($res_1=&const_p("'",$ptr,1)) &&
      ($res_replParmDat=&replParmDat($res_1,"'")) &&
      ($res_3=&const_p("'",$res_replParmDat,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("plita",$ptr);
 };
}

# 373
# replParmDat = rPD*
# should open-code this for speed
sub replParmDat {
 local ($ptr,$term)=@_;
 local ($res_rPD);
 if (($res_rPD=&star_t("rPD",$ptr,$term))) {
  # semantics
  $res_rPD;
 }
 else {
# &p_fail("replParmDat",$ptr);
 };
}
# rPD = dataChar | charRef | parmEntRef | EOF
# reordered so Refs will be spotted
sub rPD {
 local ($ptr)=@_;
 local ($res);
 if ($res=&parmEntRef($ptr,1)) {
  # semantics
  $res;
# }
# elsif ($res=&EOF($ptr)) {
#  # semantics
#  $res;
 }
 elsif ($res=&charRef($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&dataChar($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("rPD",$ptr);
 };
}

# 374
# nameTokGrp = "[(]" ts* nameToken nt* ts* "[)]"
sub nameTokGrp {
 local ($ptr)=@_;
 local ($res_5,$res_ts,$res_nt,$res_nameToken,$res_ts,$res_1);
 if (($res_1=&const_p("(",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_nameToken=&nameToken($res_ts)) &&
      ($res_nt=&star("nt",$res_nameToken)) &&
      ($res_ts=&star("ts",$res_nt)) &&
      ($res_5=&const_p(")",$res_ts,1))) {
  # semantics
  $res_5;
 }
 else {
# &p_fail("nameTokGrp",$ptr);
 };
}
# nt = ts* connector ts* nameToken
sub nt {
 local ($ptr)=@_;
 local ($res_nameToken,$res_ts,$res_connector,$res_ts);
 if (($res_ts=&star("ts",$ptr)) &&
      ($res_connector=&connector($res_ts)) &&
      ($res_ts=&star("ts",$res_connector)) &&
      ($res_nameToken=&nameToken($res_ts))) {
  # semantics
  $res_nameToken;
 }
 else {
# &p_fail("nt",$ptr);
 };
}

# nameGroup = "[(]" ts* name nn* ts* "[)]"
sub nameGroup {
 local ($ptr)=@_;
 local ($res_5,$res_ts,$res_nn,$res_name,$res_ts,$res_1);
 if (($res_1=&const_p("(",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_name=&name($res_ts)) &&
      ($res_nn=&star("nn",$res_name)) &&
      ($res_ts=&star("ts",$res_nn)) &&
      ($res_5=&const_p(")",$res_ts,1))) {
  # semantics
  $res_5;
 }
 else {
# &p_fail("nameGroup",$ptr);
 };
}
# nn = ts* connector ts* name
sub nn {
 local ($ptr)=@_;
 local ($res_name,$res_ts,$res_connector,$res_ts);
 if (($res_ts=&star("ts",$ptr)) &&
      ($res_connector=&connector($res_ts)) &&
      ($res_ts=&star("ts",$res_connector)) &&
      ($res_name=&name($res_ts))) {
  # semantics
  $res_name;
 }
 else {
# &p_fail("nn",$ptr);
 };
}

# ts = s | EOF | parmEntRef
sub ts {
 local ($ptr)=@_;
 local ($res);
 if ($res=&s($ptr)) {
  # semantics
  $res;
# }
# elsif ($res=&EOF($ptr)) {
#  # semantics
#  $res;
 }
 elsif ($res=&parmEntRef($ptr,1)) {
  # semantics
  $res;
 }
 else {
# &p_fail("ts",$ptr);
 };
}

# 376
# ds = s | EOF | parmEntRef | commDecl | procInst | markedSectDecl
sub ds {
 local ($ptr)=@_;
 local ($res);
 if ($res=&s($ptr)) {
  # semantics
  $res;
# }
# elsif ($res=&EOF($ptr)) {
#  # semantics
#  $res;
 }
 elsif ($res=&parmEntRef($ptr,1)) {
  # semantics
  $res;
 }
 elsif ($res=&commDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&procInst($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&markedSectDecl($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("ds",$ptr);
 };
}

# 377
# assocEltType = genID | nameGroup
sub assocEltType {
 local ($ptr)=@_;
 local ($res);
 if ($res=&genID($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&nameGroup($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 else {
# &p_fail("assocEltType",$ptr);
 };
}

# 379
# extID = SPI SI?
sub extID {
 local ($ptr)=@_;
 local ($res_SI,$res_SPI);
 if (($res_SPI=&SPI($ptr)) &&
      ($res_SI=&opt("SI",$res_SPI))) {
  # semantics
  $res_SI;
 }
 else {
# &p_fail("extID",$ptr);
 };
}
# SPI = "SYSTEM" | PI
sub SPI {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("SYSTEM",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&PI($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("SPI",$ptr);
 };
}
# PI = "PUBLIC" ps+ pubID
sub PI {
 local ($ptr)=@_;
 local ($res_pubID,$res_ps,$res_1);
 if (($res_1=&const("PUBLIC",$ptr,6)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_pubID=&pubID($res_ps))) {
  # semantics
  $res_pubID;
 }
 else {
# &p_fail("PI",$ptr);
 };
}
# SI = ps+ sysID
sub SI {
 local ($ptr)=@_;
 local ($res_sysID,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_sysID=&sysID($res_ps))) {
  # semantics
  $res_sysID;
 }
 else {
# &p_fail("SI",$ptr);
 };
}

# 379
# pubID = minLit
sub pubID {
 local ($ptr)=@_;
 local ($res_minLit);
 if (($res_minLit=&minLit($ptr))) {
  # semantics
  $res_minLit;
 }
 else {
# &p_fail("pubID",$ptr);
 };
}

# 379
# sysID = slit | slita
sub sysID {
 local ($ptr)=@_;
 local ($res);
 if ($res=&slit($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&slita($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("sysID",$ptr);
 };
}
# slit = "[\"]" sysData "[\"]"
sub slit {
 local ($ptr)=@_;
 local ($res_3,$res_sysData,$res_1);
 if (($res_1=&const_p("\"",$ptr,1)) &&
      ($res_sysData=&sysData($res_1,"\"")) &&
      ($res_3=&const_p("\"",$res_sysData,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("slit",$ptr);
 };
}
# slita = "[']" sysData "[']"
sub slita {
 local ($ptr)=@_;
 local ($res_3,$res_sysData,$res_1);
 if (($res_1=&const_p("'",$ptr,1)) &&
      ($res_sysData=&sysData($res_1,"'")) &&
      ($res_3=&const_p("'",$res_sysData,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("slita",$ptr);
 };
}

# 381
# minLit = mlit | mlita
sub minLit {
 local ($ptr)=@_;
 local ($res,$lit);
 if (($res=&mlit($ptr)) || ($res=&mlita($ptr))) {
  # semantics
     $lit=substr($doc,$ptr+1,($res-$ptr)-2);
     $lit=~tr/\n//d;
     $lit=~tr/\r\t / /s;
     $lit=~s/^ +(.*) +$/$1/;
     if ($lit ne substr($doc,$ptr+1,($res-$ptr)-2)) {
	 &del_doc($ptr+1,($res-$ptr)-2);
	 &add_doc($ptr+1,$lit)+1;
     }
     else {
	 $res;
     };
 }
 else {
# &p_fail("minLit",$ptr);
 };
}
# mlit = "[\"]" minDat "[\"]"
sub mlit {
 local ($ptr)=@_;
 local ($res_3,$res_minDat,$res_1);
 if (($res_1=&const_p("\"",$ptr,1)) &&
      ($res_minDat=&minDat($res_1,"\"")) &&
      ($res_3=&const_p("\"",$res_minDat,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("mlit",$ptr);
 };
}
# mlita = "[']" minDat "[']"
sub mlita {
 local ($ptr)=@_;
 local ($res_3,$res_minDat,$res_1);
 if (($res_1=&const_p("'",$ptr,1)) &&
      ($res_minDat=&minDat($res_1,"'")) &&
      ($res_3=&const_p("'",$res_minDat,1))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("mlita",$ptr);
 };
}
# minDat = "[-\r\n a-zA-Z0-9'()+,-./:=?]"*
sub minDat {
 local ($ptr,$term)=@_;
 local ($res_1);
 if (($res_1=&star_t("mdchars",$ptr,$term))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("minDat",$ptr);
 };
}

sub mdchars {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[-\r\n a-zA-Z0-9()+,-./:=?']",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("mdchars",$ptr);
 };
}

# 391
# commDecl = "<!" commSeq? ">"
sub commDecl {
 local ($ptr)=@_;
 local ($res_3,$res_comSeq,$res_1);
 if (($res_1=&const_p("<!",$ptr,2)) &&
      ($res_comSeq=&opt("comSeq",$res_1)) &&
      ($res_3=&const_p(">",$res_comSeq,1))) {
  # semantics
  &del_doc($ptr,$res_3-$ptr);
 }
 else {
# &p_fail("commDecl",$ptr);
 };
}
# comSeq = comment cs1*
sub comSeq {
 local ($ptr)=@_;
 local ($res_cs1,$res_comment);
 if (($res_comment=&comment($ptr)) &&
      ($res_cs1=&star("cs1",$res_comment))) {
  # semantics
  &del_doc($ptr,$res_cs1-$ptr);
 }
 else {
# &p_fail("comSeq",$ptr);
 };
}
# cs1 = s | comment
sub cs1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&s($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&comment($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("cs1",$ptr);
 };
}
# comment = "--" SGMLChar* "--"
sub comment {
 local ($ptr)=@_;
 local ($res_2,$res_1);
 if ($res_1=&const_p("--",$ptr,2)) {
     # hand-compilation here to cope with exclusion
     $res_2=&thru_delim($res_1,"--",2)+2;
     # delete it all!
     if (substr($doc,$ptr+2,16) eq "-*-true-trace-*-") {
	 # treat specially
	 print STDERR substr($doc,$ptr+18,$res_2-($ptr+20));
     };
     &del_doc($ptr,$res_2-$ptr);
 }
 else {
# &p_fail("comment",$ptr);
 };
}

# 391
# markedSectDecl = mss statKeySpec "[[]" markedSect mse
sub markedSectDecl {
 local ($ptr)=@_;
 local ($res_mse,$res_markedSect,$res_3,$res_statKeySpec,$res_mss,
	$spec,$status);
 if (($res_mss=&mss($ptr)) &&
      ($res_statKeySpec=&statKeySpec($res_mss)) &&
      ($res_3=&const_p("[",$res_statKeySpec,1))) {
     # 393
     # sort out what the effective status is
     $spec=substr($doc,$res_mss,$res_statKeySpec-$res_mss);
     $status="INCLUDE"; # default
     foreach $stat (@keyPriority) {
	 if ($spec=~/$stat/) {
	     $status=$stat;
	     last;
	 };
     };
     &del_doc($ptr,$res_3-$ptr);
     if ($res_markedSect=&markedSect($ptr,$status)) {
	 $res_markedSect;
     }
     else {
	 # &p_fail("markedSectDecl",$ptr);
     };
#     $res_mse=&mse($res_markedSect); not necessary, handled in markedSect
 }
 else {
# &p_fail("markedSectDecl",$ptr);
 };
}
# mss = "<![[]"
sub mss {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&const_p("<![",$ptr,3))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("mss",$ptr);
 };
}
# mse = "]]>"
sub mse {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&const_p("]]>",$ptr,3))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("mse",$ptr);
 };
}
# markedSect = SGMLChar*
sub markedSect {
 local ($ptr,$status)=@_;
 local ($res_SGMLChar,$res_rec,$sub_mss);
 if (($res_SGMLChar=&star_t("SGMLChar",$ptr,"]]>"))) {
     # check for recursion if necessary
     $res_rec=$ptr;
     if ($status!~/DATA/) {
	 while ((($sub_mss=index($doc,"<![",$res_rec)) > $res_rec) &&
		($sub_mss < $res_SGMLChar)) {
	     # got one
	     # any more deeply nested?
	     while((($sub_mss=index($doc,"<![",$sub_mss+3)) > $res_rec) &&
		   ($sub_mss < $res_SGMLChar)) {
		 $res_SGMLChar=&star_t("SGMLChar",$res_SGMLChar+3,"]]>");
	     };
	     # any more just one level down?
	     $res_rec=$res_SGMLChar+3;
	     $res_SGMLChar=&star_t("SGMLChar",$res_rec,"]]>");
	 };
     };
     # relevant bit now lies between $ptr and $res_SGMLChar, no matter what
     if ($status eq "RCDATA") {
	 &del_doc(&bounded_star("rCD1",$ptr,$res_SGMLChar),3);
     }
     elsif ($status eq "IGNORE") {
	 &del_doc(&del_doc($ptr,$res_SGMLChar-$ptr),3);
     }
     else {
	 # CDATA, TEMP or INCLUDE, just leave them looking at it
	 # note we should never see CDATA or RCDATA in a conformant prolog
	 &del_doc($res_SGMLChar,3);
     };
     $ptr;
 }
 else {
# &p_fail("markedSect",$ptr);
 };
}

# 393
# statKeySpec = sks1* ps*
sub statKeySpec {
 local ($ptr)=@_;
 local ($res_ps,$res_sks1);
 if (($res_sks1=&star("sks1",$ptr)) &&
      ($res_ps=&star("ps",$res_sks1))) {
  # semantics
  $res_ps;
 }
 else {
# &p_fail("statKeySpec",$ptr);
 };
}
# sks1 = ps+ sks2
sub sks1 {
 local ($ptr)=@_;
 local ($res_sks2,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_sks2=&sks2($res_ps))) {
  # semantics
  $res_sks2;
 }
 else {
# &p_fail("sks1",$ptr);
 };
}
# sks2 = "CDATA" | "IGNORE" | "INCLUDE" | "RCDATA" | "TEMP"
sub sks2 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("CDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("IGNORE",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("INCLUDE",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&const("RCDATA",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("TEMP",$ptr,4)) {
  # semantics
  $res;
 }
 else {
# &p_fail("sks2",$ptr);
 };
}

# 394
# entityDecl = "<!ENTITY" ps+ entityName ps+ entityText ps* ">"
sub entityDecl {
 local ($ptr)=@_;
 local ($res_7,$res_ps2,$res_entityText,$res_ps1,$res_entityName,$res_ps,$res_1);
 if (($res_1=&const("<!ENTITY",$ptr,8)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_entityName=&entityName($res_ps)) &&
      ($ent_name=substr($doc,$res_ps,$res_entityName-$res_ps)) &&
      &trace($t_decl,"ENT ",$ent_name) &&
      ($res_ps1=&plus("ps",$res_entityName)) &&
      ($res_entityText=&entityText($res_ps1)) &&
      ($res_ps2=&star("ps",$res_entityText)) &&
      ($res_7=&const_p(">",$res_ps2,1))) {
  # semantics
     ($percent,$ent_name)=($ent_name=~/^(%?)[^-a-z0-9A-Z.]*([-a-z0-9A-Z.]+)$/);
     # warn "en:$ent_name\n";
     if ($percent?$p_entities{$ent_name}:$g_entities{$ent_name}) {
	 # previous one overrides
	 &del_doc($ptr,$res_7-$ptr);
	 &trace($t_decl," ignored\n");
	 $ptr;
     }
     else {
	 &trace($t_decl,"\n");
	 if ($percent) {
	     $p_entities{$ent_name}="$res_ps1:$res_entityText";
	 }
	 else {
	     $g_entities{$ent_name}="$res_ps1:$res_entityText";
	 };
	 $res_7;
     };
 }
 else {
# &p_fail("entityDecl",$ptr);
 };
}

# 395
# entityName = genEntityName | parmEntityName
sub entityName {
 local ($ptr)=@_;
 local ($res);
 if ($res=&genEntityName($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&parmEntityName($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("entityName",$ptr);
 };
}
# genEntityName = name | "#DEFAULT"
sub genEntityName {
 local ($ptr)=@_;
 local ($res);
 if ($res=&name($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&const("#DEFAULT",$ptr,8)) {
  # semantics
  $res;
 }
 else {
# &p_fail("genEntityName",$ptr);
 };
}
# parmEntityName = "%" ps+ name
sub parmEntityName {
 local ($ptr)=@_;
 local ($res_name,$res_ps,$res_1);
 if (($res_1=&const_p("%",$ptr,1)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_name=&name($res_ps))) {
  # semantics
  $res_name;
 }
 else {
# &p_fail("parmEntityName",$ptr);
 };
}

# 396
# entityText = parmLit | dataText | brackText | extEntitySpec
sub entityText {
 local ($ptr)=@_;
 local ($res);
 if ($res=&parmLit($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&dataText($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&brackText($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&extEntitySpec($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("entityText",$ptr);
 };
}

# 397
# dataText = dtKey ps+ parmLit
sub dataText {
 local ($ptr)=@_;
 local ($res_parmLit,$res_ps,$res_dtKey);
 if (($res_dtKey=&dtKey($ptr)) &&
      ($res_ps=&plus("ps",$res_dtKey)) &&
      ($res_parmLit=&parmLit($res_ps))) {
  # semantics
  $res_parmLit;
 }
 else {
# &p_fail("dataText",$ptr);
 };
}
# dtKey = "CDATA" | "SDATA" | "PI"
sub dtKey {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("CDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("SDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("PI",$ptr,2)) {
  # semantics
  $res;
 }
 else {
# &p_fail("dtKey",$ptr);
 };
}

# 399
# brackText = bKey ps+ parmLit
sub brackText {
 local ($ptr)=@_;
 local ($res_parmLit,$res_ps,$res_bKey);
 if (($res_bKey=&bKey($ptr)) &&
      ($res_ps=&plus("ps",$res_bKey)) &&
      ($res_parmLit=&parmLit($res_ps))) {
  # semantics
  $res_parmLit;
 }
 else {
# &p_fail("brackText",$ptr);
 };
}
# bKey = "STARTTAG" | "ENDTAG" | "MS" | "MD"
sub bKey {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("STARTTAG",$ptr,8)) {
  # semantics
  $res;
 }
 elsif ($res=&const("ENDTAG",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("MS",$ptr,2)) {
  # semantics
  $res;
 }
 elsif ($res=&const("MD",$ptr,2)) {
  # semantics
  $res;
 }
 else {
# &p_fail("bKey",$ptr);
 };
}

# 400
# extEntitySpec = extID ees1?
sub extEntitySpec {
 local ($ptr)=@_;
 local ($res_ees1,$res_extID);
 if (($res_extID=&extID($ptr)) &&
      ($res_ees1=&opt("ees1",$res_extID))) {
  # semantics
  $res_ees1;
 }
 else {
# &p_fail("extEntitySpec",$ptr);
 };
}
# ees1 = ps+ entType
sub ees1 {
 local ($ptr)=@_;
 local ($res_entType,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_entType=&entType($res_ps))) {
  # semantics
  $res_entType;
 }
 else {
# &p_fail("ees1",$ptr);
 };
}
# entType = "SUBDOC" | et1
sub entType {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("SUBDOC",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&et1($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("entType",$ptr);
 };
}
# et1 = et2 ps+ notName dataAttrSpec?
sub et1 {
 local ($ptr)=@_;
 local ($res_dataAttrSpec,$res_notName,$res_ps,$res_et2);
 if (($res_et2=&et2($ptr)) &&
      ($res_ps=&plus("ps",$res_et2)) &&
      ($res_notName=&notName($res_ps)) &&
      ($res_dataAttrSpec=&opt("dataAttrSpec",$res_notName))) {
  # semantics
  $res_dataAttrSpec;
 }
 else {
# &p_fail("et1",$ptr);
 };
}
# et2 = "CDATA" | "NDATA" | "SDATA"
sub et2 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("CDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("SDATA",$ptr,5)) {
  # semantics
  $res;
 }
 else {
# &p_fail("et2",$ptr);
 };
}

# 403
# dtd = "<!DOCTYPE" ps+ docTypeName dtd1? dtd2? ps* ">"
sub dtd {
 local ($ptr)=@_;
 local ($res_7,$res_ps,$res_dtd2,$res_dtd1,$res_docTypeName,$res_ps,$res_1,
	$dtn,$dtl,$d_ext,$new_end);
 if (($res_1=&const("<!DOCTYPE",$ptr,9)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_docTypeName=&docTypeName($res_ps)) &&
      &trace($t_decl,"DOC ",
	     $dtn=substr($doc,$res_ps,$res_docTypeName-$res_ps),"\n") &&
      ($res_dtd1=&opt("dtd1",$res_docTypeName))) {
     # clean up and save a bit
     if (($dtl=$res_dtd1-$res_docTypeName)) {
	 # we have an external dtd as well
	 $d_ext=substr($doc,$res_docTypeName,$dtl);
     };
     &del_doc($ptr,$res_dtd1-$ptr);
     if (($res_dtd2=&opt("dtd2",$ptr)) &&
	 ($res_ps=&star("ps",$res_dtd2)) &&
	 ($res_7=&const_p(">",$res_ps,1))) {
	 &del_doc($res_dtd2,$res_7-$res_dtd2);
	 if ($dtl) {
	     # insert the external material now
	     $new_end=&insert_ext($res_dtd2,$d_ext,"DOCTYPE",$dtn);
	     # and process it
	     # Note we have to do it this way rather than simply dropping it
	     # where it will be seen, as there may not have BEEN any such place
	     &bounded_star("dtds1",$res_dtd2,$new_end);
	 }
	 else {
	     $res_dtd2;
	 };
     }
     else {
#	 &p_fail("dtd",$ptr);
     };
 }
 else {
# &p_fail("dtd",$ptr);
 };
}
# dtd1 = ps+ extID
sub dtd1 {
 local ($ptr)=@_;
 local ($res_extID,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_extID=&extID($res_ps))) {
  # semantics
  $res_extID;
 }
 else {
# &p_fail("dtd1",$ptr);
 };
}
# dtd2 = ps+ "[" dtdSubset "]"
sub dtd2 {
 local ($ptr)=@_;
 local ($res_4,$res_dtdSubset,$res_2,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_2=&const_p("[",$res_ps,1))) {
     # clean up
     &del_doc($ptr,$res_2-$ptr);
     if (($res_dtdSubset=&dtdSubset($ptr)) &&
	 ($res_4=&const_p("]",$res_dtdSubset,1))) {
	 &del_doc($res_dtdSubset,1);
	 $res_dtdSubset;
     }
     else {
	 # &p_fail("dtd2",$ptr);
     };
 }
 else {
# &p_fail("dtd2",$ptr);
 };
}


# 404
# docTypeName = genID
sub docTypeName {
 local ($ptr)=@_;
 local ($res_genID);
 if (($res_genID=&genID($ptr))) {
  # semantics
  &force_case($ptr,$res_genID);
  $res_genID;
 }
 else {
# &p_fail("docTypeName",$ptr);
 };
}
# dtdSubset = dtds1*
sub dtdSubset {
 local ($ptr)=@_;
 local ($res_dtds1);
 if (($res_dtds1=&star("dtds1",$ptr))) {
  # semantics
  $res_dtds1;
 }
 else {
# &p_fail("dtdSubset",$ptr);
 };
}
# dtds1 = entitySet | elementSet | shortRefSet
sub dtds1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&entitySet($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&elementSet($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&shortRefSet($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("dtds1",$ptr);
 };
}

# entitySet = es+
# made this + instead of * to defeat effective epsilon* problem
sub entitySet {
 local ($ptr)=@_;
 local ($res_es);
 if (($res_es=&plus("es",$ptr))) {
  # semantics
  $res_es;
 }
 else {
# &p_fail("entitySet",$ptr);
 };
}
sub es {
 local ($ptr)=@_;
 local ($res);
 if ($res=&entityDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&ds($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("es",$ptr);
 };
}

# elementSet = els+
# changed from *
sub elementSet {
 local ($ptr)=@_;
 local ($res_els);
 if (($res_els=&plus("els",$ptr))) {
  # semantics
  $res_els;
 }
 else {
# &p_fail("elementSet",$ptr);
 };
}
# els = elementDecl | attrDefnListDecl | notationDecl | ds
sub els {
 local ($ptr)=@_;
 local ($res);
 if ($res=&elementDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&attrDefnListDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&notationDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&ds($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("els",$ptr);
 };
}

# shortRefSet = srs+
# changed from *
sub shortRefSet {
 local ($ptr)=@_;
 local ($res_srs);
 if (($res_srs=&plus("srs",$ptr))) {
  # semantics
  $res_srs;
 }
 else {
# &p_fail("shortRefSet",$ptr);
 };
}
# srs = entityDecl | shortRefMapDecl | shortRefUseDecl | ds
sub srs {
 local ($ptr)=@_;
 local ($res);
 if ($res=&entityDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&shortRefMapDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&shortRefUseDecl($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&ds($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("srs",$ptr);
 };
}

# 405
# elementDecl = "<!ELEMENT" ps+ elementType otm? ps+ ed1 ps* ">"
sub elementDecl {
 local ($ptr)=@_;
 local ($res_8,$res_ps,$res_ed1,$res_ps,$res_otm,$res_elementType,$res_ps,$res_1);
 if (($res_1=&const("<!ELEMENT",$ptr,9)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_elementType=&elementType($res_ps)) &&
      ($elt_name=substr($doc,$res_ps,$res_elementType-$res_ps)) &&
      &trace($t_decl,"ELT ",$elt_name) &&
      ($res_otm=&opt("otm",$res_elementType)) &&
      ($res_ps=&plus("ps",$res_otm)) &&
      ($res_ed1=&ed1($res_ps)) &&
      ($res_ps=&star("ps",$res_ed1)) &&
      ($res_8=&const_p(">",$res_ps,1))) {
  # semantics
     if ($elements{$elt_name}) {
	 # previous one overrides
	 &del_doc($ptr,$res_8-$ptr);
	 &trace($t_decl," ignored\n");
	 $ptr;
     }
     else {
	 &trace($t_decl,"\n");
	 $elements{$elt_name}="$ptr:$res_8";
	 $res_8;
     };
 }
 else {
# &p_fail("elementDecl",$ptr);
 };
}
# otm = ps+ omittedTagMin
sub otm {
 local ($ptr)=@_;
 local ($res_omittedTagMin,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_omittedTagMin=&omittedTagMin($res_ps))) {
  # semantics
  $res_omittedTagMin;
 }
 else {
# &p_fail("otm",$ptr);
 };
}
# ed1 = declaredContent | contentModel
sub ed1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&declaredContent($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&contentModel($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("ed1",$ptr);
 };
}

# 406
# elementType = genID | nameGroup | rankedElt | rankedGroup
sub elementType {
 local ($ptr)=@_;
 local ($res);
 if ($res=&genID($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&nameGroup($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&rankedElt($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&rankedGroup($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 else {
# &p_fail("elementType",$ptr);
 };
}

# 407
# rankedElt = name ps+ number
sub rankedElt {
 local ($ptr)=@_;
 local ($res_3,$res_ps,$res_name);
 if (($res_name=&name($ptr)) &&
      ($res_ps=&plus("ps",$res_name)) &&
      ($res_3=&plus("number",$res_ps))) {
  # semantics
  $res_3;
 }
 else {
# &p_fail("rankedElt",$ptr);
 };
}

# rankedGroup = "[(]" ts* name rg1* ts* "[)]" ps+ number
sub rankedGroup {
 local ($ptr)=@_;
 local ($res_8,$res_ps,$res_6,$res_ts,$res_rg1,$res_name,$res_ts,$res_1);
 if (($res_1=&const_p("(",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_name=&name($res_ts)) &&
      ($res_rg1=&star("rg1",$res_name)) &&
      ($res_ts=&star("ts",$res_rg1)) &&
      ($res_6=&const_p(")",$res_ts,1)) &&
      ($res_ps=&plus("ps",$res_6)) &&
      ($res_8=&plus("number",$res_ps))) {
  # semantics
  $res_8;
 }
 else {
# &p_fail("rankedGroup",$ptr);
 };
}
# rg1 = ts* connector ts* name
sub rg1 {
 local ($ptr)=@_;
 local ($res_name,$res_ts,$res_connector,$res_ts);
 if (($res_ts=&star("ts",$ptr)) &&
      ($res_connector=&connector($res_ts)) &&
      ($res_ts=&star("ts",$res_connector)) &&
      ($res_name=&name($res_ts))) {
  # semantics
  $res_name;
 }
 else {
# &p_fail("rg1",$ptr);
 };
}

# 408
# omittedTagMin = "[Oo-]" ps+ "[Oo-]"
sub omittedTagMin {
 local ($ptr)=@_;
 local ($res_3,$res_ps,$res_1);
 if (($res_1=&pat("[Oo-]",$ptr)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_3=&pat("[Oo-]",$res_ps))) {
  # semantics
  &force_case($ptr,$res_3);
  $res_3;
 }
 else {
# &p_fail("omittedTagMin",$ptr);
 };
}

# 409
# declaredContent = "CDATA" | "RCDATA" | "EMPTY"
sub declaredContent {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("CDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("RCDATA",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("EMPTY",$ptr,5)) {
  # semantics
  $res;
 }
 else {
# &p_fail("declaredContent",$ptr);
 };
}

# 410
# contentModel = cm1 cm2?
sub contentModel {
 local ($ptr)=@_;
 local ($res_cm2,$res_cm1);
 if (($res_cm1=&cm1($ptr)) &&
      ($res_cm2=&opt("cm2",$res_cm1))) {
  # semantics
  $res_cm2;
 }
 else {
# &p_fail("contentModel",$ptr);
 };
}
# cm1 = modelGroup | "ANY"
sub cm1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&modelGroup($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&const("ANY",$ptr,3)) {
  # semantics
  $res;
 }
 else {
# &p_fail("cm1",$ptr);
 };
}
# cm2 = ps+ exceptions
sub cm2 {
 local ($ptr)=@_;
 local ($res_exceptions,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_exceptions=&exceptions($res_ps))) {
  # semantics
  $res_exceptions;
 }
 else {
# &p_fail("cm2",$ptr);
 };
}

# modelGroup = "[(]" ts* contentToken mg1* ts* "[)]" occurenceIndicator?
sub modelGroup {
 local ($ptr)=@_;
 local ($res_occurenceIndicator,$res_6,$res_ts,$res_mg1,$res_contentToken,$res_ts,$res_1);
 if (($res_1=&const_p("(",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_contentToken=&contentToken($res_ts)) &&
      ($res_mg1=&star("mg1",$res_contentToken)) &&
      ($res_ts=&star("ts",$res_mg1)) &&
      ($res_6=&const_p(")",$res_ts,1)) &&
      ($res_occurenceIndicator=&opt("occurenceIndicator",$res_6))) {
  # semantics
  $res_occurenceIndicator;
 }
 else {
# &p_fail("modelGroup",$ptr);
 };
}
# mg1 = ts* connector ts* contentToken
sub mg1 {
 local ($ptr)=@_;
 local ($res_contentToken,$res_ts,$res_connector,$res_ts);
 if (($res_ts=&star("ts",$ptr)) &&
      ($res_connector=&connector($res_ts)) &&
      ($res_ts=&star("ts",$res_connector)) &&
      ($res_contentToken=&contentToken($res_ts))) {
  # semantics
  $res_contentToken;
 }
 else {
# &p_fail("mg1",$ptr);
 };
}

# contentToken = primContentToken | modelGroup
sub contentToken {
 local ($ptr)=@_;
 local ($res);
 if ($res=&primContentToken($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&modelGroup($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("contentToken",$ptr);
 };
}

# primContentToken = "#PCDATA" | elementToken | dataTagGroup
sub primContentToken {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("#PCDATA",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&elementToken($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&dataTagGroup($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("primContentToken",$ptr);
 };
}

# elementToken = genID occurenceIndicator?
sub elementToken {
 local ($ptr)=@_;
 local ($res_occurenceIndicator,$res_genID);
 if (($res_genID=&genID($ptr)) &&
      ($res_occurenceIndicator=&opt("occurenceIndicator",$res_genID))) {
  # semantics
  &force_case($ptr,$res_genID);
  $res_occurenceIndicator;
 }
 else {
# &p_fail("elementToken",$ptr);
 };
}

# 413
# connector = "[&|,]"
sub connector {
 local ($ptr)=@_;
 local ($res);
 if ($res=&pat("[&|,]",$ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("connector",$ptr);
 };
}

# occurenceIndicator = "[?+*]"
sub occurenceIndicator {
 local ($ptr)=@_;
 local ($res_1);
 if (($res_1=&pat("[?+*]",$ptr))) {
  # semantics
  $res_1;
 }
 else {
# &p_fail("occurenceIndicator",$ptr);
 };
}

# 415
# dataTagGroup = "[[]" ts* genID ts* "," ts* dataTagPattern ts* "[]]"
sub dataTagGroup {
 local ($ptr)=@_;
 local ($res_9,$res_ts3,$res_dataTagPattern,$res_ts2,$res_5,$res_ts1,$res_genID,$res_ts,$res_1);
 if (($res_1=&const_p("[",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_genID=&genID($res_ts)) &&
      ($res_ts1=&star("ts",$res_genID)) &&
      ($res_5=&const_p(",",$res_ts1,1)) &&
      ($res_ts2=&star("ts",$res_5)) &&
      ($res_dataTagPattern=&dataTagPattern($res_ts2)) &&
      ($res_ts3=&star("ts",$res_dataTagPattern)) &&
      ($res_9=&const_p("]",$res_ts3,1))) {
  # semantics
  &force_case($res_ts,$res_genID);
  $res_9;
 }
 else {
# &p_fail("dataTagGroup",$ptr);
 };
}


# 416
# dataTagPattern = dtp1 dtp2?
sub dataTagPattern {
 local ($ptr)=@_;
 local ($res_dtp2,$res_dtp1);
 if (($res_dtp1=&dtp1($ptr)) &&
      ($res_dtp2=&opt("dtp2",$res_dtp1))) {
  # semantics
  $res_dtp2;
 }
 else {
# &p_fail("dataTagPattern",$ptr);
 };
}
# dtp1 = dttGroup | parmLit
sub dtp1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&dttGroup($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&parmLit($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("dtp1",$ptr);
 };
}
# dtp2 = ts* "," ts* parmLit
sub dtp2 {
 local ($ptr)=@_;
 local ($res_parmLit,$res_ts,$res_2,$res_ts);
 if (($res_ts=&star("ts",$ptr)) &&
      ($res_2=&const_p(",",$res_ts,1)) &&
      ($res_ts=&star("ts",$res_2)) &&
      ($res_parmLit=&parmLit($res_ts))) {
  # semantics
  $res_parmLit;
 }
 else {
# &p_fail("dtp2",$ptr);
 };
}

# dttGroup = "[(]" ts* parmLit dtg1* ts* "[)]"
sub dttGroup {
 local ($ptr)=@_;
 local ($res_6,$res_ts,$res_dtg1,$res_parmLit,$res_ts,$res_1);
 if (($res_1=&const_p("(",$ptr,1)) &&
      ($res_ts=&star("ts",$res_1)) &&
      ($res_parmLit=&parmLit($res_ts)) &&
      ($res_dtg1=&star("dtg1",$res_parmLit)) &&
      ($res_ts=&star("ts",$res_dtg1)) &&
      ($res_6=&const_p(")",$res_ts,1))) {
  # semantics
  $res_6;
 }
 else {
# &p_fail("dttGroup",$ptr);
 };
}
# dtg1 = ts* "[|]" ts* parmLit
sub dtg1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&star("ts",$ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&const_p("|",$ptr,1)) {
  # semantics
  $res;
 }
 elsif ($res=&star("ts",$ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&parmLit($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("dtg1",$ptr);
 };
}

# 418
# exceptions = ex1 | inclusions
sub exceptions {
 local ($ptr)=@_;
 local ($res);
 if ($res=&ex1($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&inclusions($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 else {
# &p_fail("exceptions",$ptr);
 };
}
# ex1 = exclusions ex1?
sub ex1 {
 local ($ptr)=@_;
 local ($res_ex1,$res_exclusions);
 if (($res_exclusions=&exclusions($ptr)) &&
      ($res_ex1=&opt("ex1",$res_exclusions))) {
  # semantics
  $res_ex1;
 }
 else {
# &p_fail("ex1",$ptr);
 };
}
# ex2 = ps+ inclusions
sub ex2 {
 local ($ptr)=@_;
 local ($res_inclusions,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_inclusions=&inclusions($res_ps))) {
  # semantics
  $res_inclusions;
 }
 else {
# &p_fail("ex2",$ptr);
 };
}

# inclusions = "[+]" nameGroup
sub inclusions {
 local ($ptr)=@_;
 local ($res_nameGroup,$res_1);
 if (($res_1=&const_p("+",$ptr,1)) &&
      ($res_nameGroup=&nameGroup($res_1))) {
  # semantics
  $res_nameGroup;
 }
 else {
# &p_fail("inclusions",$ptr);
 };
}

# 419
# exclusions = "[-]" nameGroup
sub exclusions {
 local ($ptr)=@_;
 local ($res_nameGroup,$res_1);
 if (($res_1=&const_p("-",$ptr,1)) &&
      ($res_nameGroup=&nameGroup($res_1))) {
  # semantics
  $res_nameGroup;
 }
 else {
# &p_fail("exclusions",$ptr);
 };
}

# 420
# attrDefnListDecl = "<!ATTLIST" ps+ adld1 ps+ adl ps* ">"
sub attrDefnListDecl {
 local ($ptr)=@_;
 local ($res_7,$res_ps,$res_adl,$res_ps,$res_adld1,$res_ps,$res_1);
 if (($res_1=&const("<!ATTLIST",$ptr,9)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_adld1=&adld1($res_ps)) &&
      ($attr_name=substr($doc,$res_ps,$res_adld1-$res_ps)) &&
      &trace($t_decl,"ATL ",$attr_name) &&
      ($res_ps=&plus("ps",$res_adld1)) &&
      ($res_adl=&adl($res_ps)) &&
      ($res_ps=&star("ps",$res_adl)) &&
      ($res_7=&const_p(">",$res_ps,1))) {
  # semantics
     if ($attributes{$attr_name}) {
	 # previous one overrides
	 &del_doc($ptr,$res_7-$ptr);
	 &trace($t_decl," ignored\n");
	 $ptr;
     }
     else {
	 &trace($t_decl,"\n");
	 $attributes{$attr_name}="$ptr:$res_7";
	 $res_7;
     };
 }
 else {
# &p_fail("attrDefnListDecl",$ptr);
 };
}
# adld1 = assocEltType | assocNotName
sub adld1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&assocEltType($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&assocNotName($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 else {
# &p_fail("adld1",$ptr);
 };
}

# adl = attrDef adl1*
sub adl {
 local ($ptr)=@_;
 local ($res_adl1,$res_attrDef);
 if (($res_attrDef=&attrDef($ptr)) &&
      ($res_adl1=&star("adl1",$res_attrDef))) {
  # semantics
  $res_adl1;
 }
 else {
# &p_fail("adl",$ptr);
 };
}
# adl1 = ps+ attrDef
sub adl1 {
 local ($ptr)=@_;
 local ($res_attrDef,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_attrDef=&attrDef($res_ps))) {
  # semantics
  $res_attrDef;
 }
 else {
# &p_fail("adl1",$ptr);
 };
}

# 421
# attrDef = name ps+ declValue ps+ defValue
sub attrDef {
 local ($ptr)=@_;
 local ($res_defValue,$res_ps,$res_declValue,$res_ps,$res_name);
 if (($res_name=&name($ptr)) &&
      ($res_ps=&plus("ps",$res_name)) &&
      ($res_declValue=&declValue($res_ps)) &&
      ($res_ps=&plus("ps",$res_declValue)) &&
      ($res_defValue=&defValue($res_ps))) {
  # semantics
  &force_case($ptr,$res_name);
  $res_defValue;
 }
 else {
# &p_fail("attrDef",$ptr);
 };
}

# 422
# declValue = "CDATA" | "ENTITY" | "ENTITIES" | "ID" | "IDREF" | "IDREFS" | "NAME" | "NAMES" | "NMTOKEN" | "NMTOKENS" | "NUMBER" | "NUMBERS" | "NUTOKEN" | "NUTOKENS" | notation | nameTokGrp
sub declValue {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("CDATA",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("ENTITY",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("ENTITIES",$ptr,8)) {
  # semantics
  $res;
 }
 elsif ($res=&const("IDREFS",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("IDREF",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("ID",$ptr,2)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NAMES",$ptr,5)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NAME",$ptr,4)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NMTOKENS",$ptr,8)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NMTOKEN",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NUMBERS",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NUMBER",$ptr,6)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NUTOKENS",$ptr,8)) {
  # semantics
  $res;
 }
 elsif ($res=&const("NUTOKEN",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&notation($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&nameTokGrp($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("declValue",$ptr);
 };
}

# notation = "NOTATION" ps+ nameGroup
sub notation {
 local ($ptr)=@_;
 local ($res_nameGroup,$res_ps,$res_1);
 if (($res_1=&const("NOTATION",$ptr,8)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_nameGroup=&nameGroup($res_ps))) {
  # semantics
  &force_case($res_ps,$res_nameGroup);
  $res_nameGroup;
 }
 else {
# &p_fail("notation",$ptr);
 };
}

# 425
# defValue = dv1  | dv2
sub defValue {
 local ($ptr)=@_;
 local ($res);
 if ($res=&dv1($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&dv2($ptr)) {
  # semantics
  $res;
 }
 else {
# &p_fail("defValue",$ptr);
 };
}
# dv1 = dv3? attrValueSpec
sub dv1 {
 local ($ptr)=@_;
 local ($res_attrValueSpec,$res_dv3);
 if (($res_dv3=&opt("dv3",$ptr)) &&
      ($res_attrValueSpec=&attrValueSpec($res_dv3))) {
  # semantics
  $res_attrValueSpec;
 }
 else {
# &p_fail("dv1",$ptr);
 };
}
# dv2 = "#REQUIRED" | "#CURRENT" | "#CONREF" | "#IMPLIED"
sub dv2 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&const("#REQUIRED",$ptr,9)) {
  # semantics
  $res;
 }
 elsif ($res=&const("#CURRENT",$ptr,8)) {
  # semantics
  $res;
 }
 elsif ($res=&const("#CONREF",$ptr,7)) {
  # semantics
  $res;
 }
 elsif ($res=&const("#IMPLIED",$ptr,8)) {
  # semantics
  $res;
 }
 else {
# &p_fail("dv2",$ptr);
 };
}
# dv3 = "#FIXED" ps+
sub dv3 {
 local ($ptr)=@_;
 local ($res_ps,$res_1);
 if (($res_1=&const("#FIXED",$ptr,6)) &&
      ($res_ps=&plus("ps",$res_1))) {
  # semantics
  $res_ps;
 }
 else {
# &p_fail("dv3",$ptr);
 };
}

# 426
# notationDecl = "<!NOTATION" ps+ notName ps+ notID ps* ">"
sub notationDecl {
 local ($ptr)=@_;
 local ($res_7,$res_ps,$res_notID,$res_ps,$res_notName,$res_ps,$res_1);
 if (($res_1=&const("<!NOTATION",$ptr,10)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_notName=&notName($res_ps)) &&
      ($res_ps=&plus("ps",$res_notName)) &&
      ($res_notID=&notID($res_ps)) &&
      ($res_ps=&star("ps",$res_notID)) &&
      ($res_7=&const_p(">",$res_ps,1))) {
  # semantics
  $res_7;
 }
 else {
# &p_fail("notationDecl",$ptr);
 };
}

# 427
# notID = extID
sub notID {
 local ($ptr)=@_;
 local ($res_extID);
 if (($res_extID=&extID($ptr))) {
  # semantics
  $res_extID;
 }
 else {
# &p_fail("notID",$ptr);
 };
}

# 428
# assocNotName = "<!NOTATION" ps+ ann1
sub assocNotName {
 local ($ptr)=@_;
 local ($res_ann1,$res_ps,$res_1);
 if (($res_1=&const("<!NOTATION",$ptr,10)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_ann1=&ann1($res_ps))) {
  # semantics
  $res_ann1;
 }
 else {
# &p_fail("assocNotName",$ptr);
 };
}
# ann1 = notName | nameGroup
sub ann1 {
 local ($ptr)=@_;
 local ($res);
 if ($res=&notName($ptr)) {
  # semantics
  $res;
 }
 elsif ($res=&nameGroup($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 else {
# &p_fail("ann1",$ptr);
 };
}

# dataAttrSpec = ps+ "[[]" attrSpecList s* "[]]"
sub dataAttrSpec {
 local ($ptr)=@_;
 local ($res_5,$res_s,$res_attrSpecList,$res_2,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_2=&const_p("[",$res_ps,1)) &&
      ($res_attrSpecList=&attrSpecList($res_2)) &&
      ($res_s=&star("s",$res_attrSpecList)) &&
      ($res_5=&const_p("]",$res_s,1))) {
  # semantics
  $res_5;
 }
 else {
# &p_fail("dataAttrSpec",$ptr);
 };
}

# 429
# shortRefMapDecl = "<!SHORTREF" ps+ name srmd1+ ps* ">"
sub shortRefMapDecl {
 local ($ptr)=@_;
 local ($res_6,$res_ps2,$res_srmd1,$res_name,$res_ps,$res_1);
 if (($res_1=&const("<!SHORTREF",$ptr,10)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_name=&name($res_ps)) &&
      ($res_srmd1=&plus("srmd1",$res_name)) &&
      ($res_ps2=&star("ps",$res_srmd1)) &&
      ($res_6=&const_p(">",$res_ps2,1))) {
  # semantics
  &force_case($res_ps,$res_name);
  $res_6;
 }
 else {
# &p_fail("shortRefMapDecl",$ptr);
 };
}
# srmd1 = ps+ parmLit ps+ name
sub srmd1 {
 local ($ptr)=@_;
 local ($res_name,$res_ps,$res_parmLit,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_parmLit=&parmLit($res_ps)) &&
      ($res_ps=&plus("ps",$res_parmLit)) &&
      ($res_name=&name($res_ps))) {
  # semantics
  $res_name;
 }
 else {
# &p_fail("srmd1",$ptr);
 };
}

# 430
# shortRefUseDecl = "<!USEMAP" ps+ mapSpec srud1? ps* ">"
sub shortRefUseDecl {
 local ($ptr)=@_;
 local ($res_6,$res_ps,$res_srud1,$res_mapSpec,$res_ps,$res_1);
 if (($res_1=&const("<!USEMAP",$ptr,8)) &&
      ($res_ps=&plus("ps",$res_1)) &&
      ($res_mapSpec=&mapSpec($res_ps)) &&
      ($res_srud1=&opt("srud1",$res_mapSpec)) &&
      ($res_ps=&star("ps",$res_srud1)) &&
      ($res_6=&const_p(">",$res_ps,1))) {
  # semantics
  $res_6;
 }
 else {
# &p_fail("shortRefUseDecl",$ptr);
 };
}
# srud1 = ps+ assocEltType
sub srud1 {
 local ($ptr)=@_;
 local ($res_assocEltType,$res_ps);
 if (($res_ps=&plus("ps",$ptr)) &&
      ($res_assocEltType=&assocEltType($res_ps))) {
  # semantics
  $res_assocEltType;
 }
 else {
# &p_fail("srud1",$ptr);
 };
}

# mapSpec = name | "#EMPTY"
sub mapSpec {
 local ($ptr)=@_;
 local ($res);
 if ($res=&name($ptr)) {
  # semantics
  &force_case($ptr,$res);
  $res;
 }
 elsif ($res=&const("#EMPTY",$ptr,6)) {
  # semantics
  $res;
 }
 else {
# &p_fail("mapSpec",$ptr);
 };
}

# 433 LINK not handled
# 450 SGML Declaration not handled

########################################################

# These next few lines are legal in both Perl and Nroff.

.00;		# finish .ig

'di		\" finish diversion--previous line must be blank
.nr nl 0-1	\" fake up transition to first page again
.nr % 0		\" start at page 1
'; __END__	#### From here on it's a standard manual page ####
.TH TRUE-DTD 1 "Fri Oct 15 1993" ""
.SH NAME
true-dtd \- compute and display the true prolog of a conformant SGML document
.SH SYNOPSIS
.B true-dtd [options] [files]
.SH DESCRIPTION
This perl script will parse the DOCTYPE of an SGML document and output
the actual prolog which results, implementing external references,
precedence rules and handling parameter entity expansions and marked
sections.  Will trace the processing of each declaration if required.

Prompts for filenames for external (SYSTEM and/or PUBLIC) references,
and will save them for reuse if required.
.SH OPTIONS
Invoke with -help for introduction to switches and arguments.
.SH ENVIRONMENT
.SH EXAMPLES
If simple.sgm looks like this:
    <!DOCTYPE a [
    <!ENTITY % banana "IGNORE">
    <!ENTITY % cake "ANY" >
    <![ %banana [
    <!ELEMENT a - - (other)>
    ]]>
    <!ELEMENT a - - %cake;>
    ]>
    <a>
    </a>
 then the output of true-dtd with the default switch
 settings is
    DOC A
    ENT % banana
    ENT % cake
    ELT A
    <!ENTITY % banana "IGNORE">
    <!ENTITY % cake "ANY" >
    <!ELEMENT A - - ANY>
.SH FILES
.SH AUTHOR
Henry S. Thompson <HThompson@ed.ac.uk>
.SH SEE ALSO
.SH DIAGNOSTICS
.SH BUGS
Does not handle SUBDOC, LINK or SHORTREF as yet.  Contributions welcome.
Behaviour with non-conforming documents is capricious at best, but is
at least sometimes helpful in debugging complex problems.
