=head1 NAME
Message::Markup::XML::Parser --- manakai: Simple XML parser
=head1 DESCRIPTION
This is a simple XML parser intended to be used with Message::Markup::XML.
After parsing of the XML document, this module returns a Message::Markup::XML
object so that you can handle XML document with that module (and other modules
implementing same interface).
This module is part of manakai.
=cut
package Message::Markup::XML::Parser;
use strict;
our $VERSION = do{my @r=(q$Revision: 1.24 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
use Char::Class::XML qw!InXML_NameStartChar InXMLNameChar InXMLChar
InXML_deprecated_noncharacter InXML_unicode_xml_not_suitable!;
require Message::Markup::XML;
require Message::Markup::XML::Error;
*_raise_error = \&Message::Markup::XML::Error::raise;
our %NS;
*NS = \%Message::Markup::XML::NS;
=head1 METHODS
WARNING: This module is under construction. Interface of this module is not yet fixed.
=cut
our %xml_re;
# [1] document = prolog element *Misc
# [2] Char = %x09 / %x0A / %x0D / U+0020-U+D7FF / U+E000-U+FFFD / U+10000-U+10FFFF ;; 1.0
# [2] Char = %x09 / %x0A / %x0D / %x20-7E / U+0085 / U+00A0-U+D7FF / U+E000-U+FFFD
# / U+10000-U+10FFFF ;; 1.1
# [3] s = 1*(%x20 / %x09 / %x0D / %x0A)
$xml_re{s} = qr/[\x09\x0A\x0D\x20]+/s;
$xml_re{_s__chars} = qr/\x09\x0A\x0D\x20/s;
# [4] NameChar = Letter / Digit / "." / "-" / "_" / ":" / CombiningChar / Extender ;; 1.0
# [4] NameStartChar = ":" / ALPHA / "_" / U+00C0-U+02FF / U+0370-U+037D
# / U+037F-U+1FFF / U+200C-U+200D / U+2070-U+218F
# / U+2C00-U+2FEF / U+3001-U+D7FF / U+F900-U+EFFFF ;; 1.1
# [4a] NameChar = NameStartChar / "-" / "." / DIGIT / U+00B7 / U+0300-U+036F
# / U+203F-U+2040 ;; 1.1
# $xml_re{NameChar} = qr/[A-Za-z0-9._:-]|[^\x00-\x7F]/;
#$xml_re{NameChar} = qr/\p{InXMLNameChar}/;
# [5] Name = (Letter / "_" / ":") *NameChar ;; 1.0
# [5] Name = NameStartChar *NameChar ;; 1.1
# $xml_re{Name} = qr/(?:[A-Z_:]|[^\x00-\x7F])(?:$xml_re{NameChar})*/;
$xml_re{Name} = qr/\p{InXML_NameStartChar}\p{InXMLNameChar}*/;
# [6] Names = Name *(S Name) ;; 1.0 FE & 1.0 FE-errata (2000-09-27) & 1.0 SE
# [6] Names = Name *(%x20 Name) ;; 1.0 FE-errata (2000-04-09) & 1.0 SE-errata
#$xml_re{Names} = qr/$xml_re{Name}(?:$xml_re{s}$xml_re{Name})*/s;
# [7] Nmtoken = 1*NameChar
#$xml_re{Nmtoken} = qr/(?:$xml_re{NameChar})+/;
#$xml_re{Nmtoken} = qr/\p{InXMLNameChar}+/;
# [8] Nmtokens = Nmtoken *(S Nmtoken) ;; 1.0 FE & 1.0 FE-errata (2000-09-27) & 1.0 SE
# [8] Nmtokens = Nmtoken *(%x20 Nmtoken) ;; 1.0 FE-errata (2000-04-09) & 1.0 SE-errata
#$xml_re{Nmtokens} = qr/$xml_re{Nmtoken}(?:$xml_re{s}$xml_re{Nmtoken})*/s;
# [9] EntityValue = <"> *((Char - ("%" / "&" / <">)) / PEReference / Reference) <">
# / "'" *((Char - ("%" / "&" / "'")) / PEReference / Reference) "'"
# [10] AttValue = <"> *((Char - ("<" / "&" / <">)) / Reference) <">
# / "'" *((Char - ("<" / "&" / "'")) / Reference) "'"
$xml_re{__AttValue_simple} = qr/"[^"]*"|'[^']*'/s;
# [11] SystemLiteral = <"> *(Char - <">) <"> / "'" *(Char - "'") "'"
#$xml_re{SystemLiteral} = qr/"[^"]*"|'[^']*'/;
# [12] PublicLiteral = <"> *PubidChar <"> / "'" *(PubidChar - "'") "'"
# [13] PubidChar = %x20 / %x0D / %x0A / ALPHA / DIGIT / "-" / "'" / "(" / ")"
# / "+" / "," / "." / "/" / ":" / "=" / "?" / ";" / "!" / "*"
# / "#" / "@" / "$" / "_" / "%"
#$xml_re{PubidChar} = qr[[\x0D\x0A\x20!\x24#%'()+*,./0-9:;=?\x40A-Z_a-z-]];
#$xml_re{__non_PubidChar} = qr[[^\x0D\x0A\x20!\x24#%'()+*,./0-9:;=?\x40A-Z_a-z-]];
#$xml_re{__PubidChar2} = qr[[\x0D\x0A\x20!\x24#%()+*,./0-9:;=?\x40A-Z_a-z-]];
#$xml_re{PublicLiteral} = qr/"(?:$xml_re{PubidChar})*"|'(?:$xml_re{__PubidChar2})*'/;
# [14] CharData = *(Char - ("<" / "&")) - (*(Char - ("<" / "&")) "]]>" *(Char - ("<" / "&")))
#$xml_re{CharData} = qr/(?:(?!\]\]>)[^<&])*/s;
$xml_re{__CharDataP} = qr/(?:(?!\]\]>)[^<&])+/s;
# [15] Comment = ""
#$xml_re{Comment_M} = qr//s;
# [16] PI = "" PITarget [S (*Char - (*Char "?>" *Char))] "?>"
$xml_re{PI_M} = qr/<\?($xml_re{Name})(?:$xml_re{s}((?:(?!\?>).)*))?\?>/s;
$xml_re{_xml_PI_M} = qr/<\?xml(?:$xml_re{s}((?:(?!\?>).)*))?\?>/s;
# [17] PITarget = Name - "xml"
# [18] CDSect = CDStart CData CDEnd
# [19] CDStart = '" *Char))
# [21] CDEnd = "]]>"
$xml_re{CDSect_M} = qr/).)*)\]\]>/s;
# [22] prolog = [XMLDecl] *Misc [doctypedecl *Misc]
# [23] XMLDecl = '"
# [24] VersionInfo = S 'version' Eq ("'" VersionNum "'" / <"> VersionNum <">)
# [25] Eq = [S] "=" [S]
# [26] VersionNum = 1*(ALPHA / DIGIT / "_" / "." / ":" / "-") ;; 1.0 FE & 1.0 SE
# [26] VersionNum = "1.0" ;; 1.0 SE-errata
# [26] VersionNum = "1.1" ;; 1.1
# [27] Misc = Comment / PI / S
# [28] doctypedecl = '" ;; 1.0 FE
# [28] doctypedecl = '"
# ;; 1.0 FE-errata & 1.0 SE
# [28] doctypedecl = '"
# ;; 1.0 SE-errata
#$xml_re{__doctypedecl_start_simple_M} = qr/( ('yes' / 'no') <">)
# [33] LanguageID = Langcode *("-" Subcode) ;; 1.0 FE (removed by 1.0 SE)
# [34] Langcode = ISO639Cocde / IanaCode / UserCode ;; 1.0 FE (ditto)
# [35] ISO639Code = 2ALPHA ;; 1.0 FE (ditto)
# [36] IanaCode = "i-" 1*ALPHA ;; 1.0 FE (ditto)
# [37] UserCode = "x-" 1*ALPHA ;; 1.0 FE (ditto)
# [38] Subcode = 1*ALPHA ;; 1.0 FE (ditto)
# [39] element = EmptyElemTag / STag content ETag
# [40] STag = "<" Name *(S Attribute) [S] ">"
# [41] Attribute = Name Eq AttValue
#$xml_re{Attribute} = qr/$xml_re{Name}(?:$xml_re{s})?=(?:$xml_re{s})?$xml_re{__AttValue_simple}/s;
#$xml_re{Attribute_M} = qr/($xml_re{Name})(?:$xml_re{s})?=(?:$xml_re{s})?($xml_re{__AttValue_simple})/s;
#$xml_re{STag} = qr/<$xml_re{Name}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?>/s;
# [42] ETag = "" Name [S] ">"
$xml_re{ETag_M} = qr!($xml_re{Name})(?:$xml_re{s})?>!s;
# [43] content = *(element / CharData / Reference / CDSect / PI / Comment) ;; 1.0 FE
# [43] content = [CharData] *((element / Reference / CDSect / PI / Comment) [CharData])
# ;; 1.0 FE-errata & 1.0 SE
# [44] EmptyElemTag = "<" Name *(S Attribute) [S] "/>"
#$xml_re{__STag_or_EmptyElemTag} = qr!<$xml_re{Name}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?/?>!s;
#$xml_re{__STag_or_EmptyElemTag_simple} = qr!<$xml_re{Name}(?:$xml_re{s}|$xml_re{Name}|$xml_re{__AttValue_simple}|=)*/?>!s;
# [45] elementdecl = '"
# [46] contentspec = 'EMPTY' / 'ANY' / Mixed / children
#$xml_re{__contentspec_simple} = qr/(?:$xml_re{Name}|\#PCDATA|[()|,?*+]|$xml_re{s})/s;
# [47] children = (choice / seq) ["?" / "*" / "+"]
# [48] cp = (Name / choice / seq) ["?" / "*" / "+"]
# [49] choice = "(" [S] cp *([S] "|" [S] cp) [S] ")" ;; 1.0 FE
# [49] choice = "(" [S] cp 1*([S] "|" [S] cp) [S] ")" ;; 1.0 FE-errata & 1.0 SE
# [50] seq = "(" [S] cp *([S] "," [S] cp) [S] ")"
# [51] Mixed = "(" [S] '#PCDATA' *([S] "|" [S] Name) [S] ")*"
# / "(" [S] '#PCDATA' [S] ")"
#$xml_re{seq} = qr/\($xml_re{cp}(?:(?:$xml_re{s})?,(?:$xml_re{s})?$xml_re{cp})*(?:$xml_re{s})?\)/;
#$xml_re{cp} = qr/(?:$xml_re{Name}|$xml_re{choice}|$xml_re{seq})[?*+]/;
#$xml_re{choice} = qr/\($xml_re{cp}(?:(?:$xml_re{s})?\|(?:$xml_re{s})?$xml_re{cp})+(?:$xml_re{s})?\)/;
#$xml_re{children} = qr/(?:$xml_re{choice}|$xml_re{seq})[?*+]/;
#$xml_re{Mixed} = qr/(?:\((?:$xml_re{s})?\#PCDATA(?:$xml_re{s})?(?:(?:$xml_re{s})?|(?:$xml_re{s})?$xml_re{Name})*(?:$xml_re{s})?\)|\((?:$xml_re{s})?\#PCDATA(?:$xml_re{s})?\))/;
#$xml_re{contentspec} = qr/(?:EMPTY|ANY|$xml_re{Mixed}|$xml_re{children})/;
# [52] AttlistDecl = '"
# [53] AttDef = S Name S AttType S DefaultDecl
# [54] AttType = StringType / TokenizedType / EnumeratedType
# [55] StringType = 'CDATA'
# [56] TokenizedType = 'ID' / 'IDREF' / 'IDREFS' / 'ENTITY' / 'ENTITIES' / 'NMTOKEN' / 'NMTOKENS'
# [57] EnumeratedType = NotationType / Enumeration
# [58] NotationType = 'NOTATION' S "(" [S] Name *([S] "|" [S] Name) [S] ")"
# [59] Enumeration = "(" [S] Nmtoken *([S] "|" [S] Nmtoken) [S} ")"
# [60] DefaultDecl = '#REQUIRED' / '#IMPLIED' / ['#FIXED' S] AttValue
# [61] conditionalSect = includeSect / ignoreSect
# [62] includeSect = ""
# [63] ignoreSect = ""
# [64] ignoreSectContents = Ignore *("" Ignore)
# [65] Ignore = *Char - (*Char ("") *Char)
# [66] CharRef = '' 1*DIGIT ";" / '' 1*HEXDIGIT ";"
$xml_re{CharRef} = qr/[0-9]+;|[0-9A-Fa-f]+;/;
# [67] Reference = EntityRef / CharRef
# [68] EntityRef = "&" Name ";"
$xml_re{EntityRef} = qr/&$xml_re{Name};/;
$xml_re{EntityRef_M} = qr/&($xml_re{Name});/;
$xml_re{Reference} = qr/$xml_re{EntityRef}|$xml_re{CharRef}/;
#$xml_re{AttValue} = qr/"(?:$xml_re{Reference}|[^&<"])*"|'(?:$xml_re{Reference}|[^&<'])*'/s;
# [69] PEReference = "%" Name ";"
$xml_re{PEReference} = qr/%(?:$xml_re{Name});/;
$xml_re{PEReference_M} = qr/%($xml_re{Name});/;
$xml_re{__elementdecl_simple} = qr//s;
$xml_re{__AttlistDecl_simple} = qr//s;
# [70] EntityDecl = GEDecl / PEDecl
#$xml_re{__EntityDecl_simple} = qr/])*>/s;
# [71] GEDecl = '"
# [72] PEDecl = '"
# [73] EntityDef = EntityValue / ExternalID [NDataDecl]
# [74] PEDef = EntityValue / ExternalID
# [75] ExternalID = 'SYSTEM' S SystemLiteral / 'PUBLIC' S PublicLiteral S SystemLiteral
# [76] NDataDecl = S 'NDATA' S Name
# [77] TextDecl = '?xml' [VersionInfo] EncodingDecl [S] "?>"
# [78] extParsedEnt = [TextDecl] content
# [79] extPE ;; 1.0 FE (removed by errata)
# [80] EncodingDecl = S 'encoding' Eq (<"> EncName <"> / "'" EncName "'")
# [81] EncName = ALPHA *(ALPHA / DIGIT / "." / "_" / "-")
# [82] NotationDecl = '"
#$xml_re{__NotationDecl_simple} = qr/])*>/s;
# [83] PublicID = 'PUBLIC' S PubidLiteral
# [84] Letter = BaseChar / Ideographic
# [85] Basechar = ...
# [86] Ideographic = ...
# [87] CombiningChar = ...
# [88] Digit = ...
# [89] Extender = U+00B7 / U+02D0 / U+02D1 / U+0387 / U+0640 / U+0E46 / U+0EC6
# / U+3005 / U+3031-U+3035 / U+309D / U+309E / U+30FC-U+30FE
## [84]-[89] removed by 1.1
# [XMLNames]
# [1] NSAttName = PrefixedAttName / DefaultAttName
# [2] PrefixedAttName = 'xmlns:' NCName
# [3] DefaultAttName = 'xmlns'
# [4] NCName = (Letter / "_") *NCNameChar ;; 1.0
# [4] NCName = NCNameStartChar *NCNameChar ;; 1.1
# [5] NCNameChar = Letter / Digit / "." / "-" / "_" / CombiningChar / Extender ;; 1.0
# [5] NCNameChar = NameChar - ":" ;; 1.1
# [5a] NCNameStartChar = NameStartChar - ":" ;; 1.1
# [6] QName = [Prefix ":"] LocalPart ;; 1.0
# [6] QName = PrefixedName / UnprefixedName ;; 1.1
# [6a] PrefixedName = Prefix ":" LocalPart ;; 1.1
# [6b] UnprefixedName = LocalPart ;; 1.1
# [7] Prefix = NCName
# [8] LocalPart = NCName
# [9] STag = "<" QName *(S Attribute) [S] ">"
#$xml_re{__NCSTag} = qr/<$xml_re{QName}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?>/s;
# [10] ETag = "" QName [S] ">"
#$xml_re{__NCETag} = qr!$xml_re{QName}(?:$xml_re{s})?>!s;
# [11] EmptyElemTag = "<" QName *(S Attribute) [S] "/>"
# [12] Attribute = NSAttName Eq AttValue / QName Eq AttValue
# [13] doctypedecl = '"
# [14] elementdecl = '"
# [15] cp = (QName / choice / sep) ["?" / "*" / "+"]
# [16] Mixed = "(" [S] '#PCDATA' *([S] "|" [S] QName) [S] ")*" / "(" [S] '#PCDATA' [S] ")"
# [17] AttlistDecl = '"
# [18] AttDef = S (QName / NSAttName) S AttType S DefaultDecl
# [19] Name = NameStartChar *NameChar ;; 1.1 draft
# [20] NameChar = {XML1.1}.NameChar ;; 1.1 draft
# [21] NameStartChar = {XML1.1}.NameStartChar ;; 1.1 draft
sub new ($;%) {
my $class = shift;
my $self = bless {@_}, $class;
$self;
}
sub parse_text ($$;$%) {
my ($self, $s, $o, %opt) = @_;
$o ||= {line => 0, pos => 0, entity_type => 'document_entity',
uri => $self->{option}->{document_entity_uri}};
my $r = Message::Markup::XML->new (type => '#document');
$r->base_uri ($self->{option}->{document_entity_base_uri})
if defined $self->{option}->{document_entity_base_uri};
unless ($opt{entMan}) {
$opt{entMan} = $r->_get_entity_manager;
$opt{entMan}->option (uri_resolver => $self->{option}->{uri_resolver});
$opt{entMan}->option (error_handler => $self->{option}->{error_handler});
} else {
$opt{entMan}->set_root_node ($r);
}
$r->flag (smxp__entity_manager => $opt{entMan});
## Line-break normalization
$s =~ s/\x0D\x0A/\x0A/g;
$s =~ tr/\x0D/\x0A/;
## NOTE: Even if there are more than one non-XML-Char, error is raised only one time.
$self->_warn_char_val ($self->_make_clone_of ($o), $s);
if ($s =~ s/^($xml_re{_xml_PI_M})//s) { #
my ($data, $all) = ($2, $1);
$self->_clp (_____ => $o);
if (length ($data)) {
$self->_clp (_ => $o); #
$self->_parse_xml_declaration ($r->append_new_node (type => '#pi',
local_name => 'xml'), $data, $o);
} else {
$self->_raise_error ($o, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
$r->append_new_node (type => '#pi', local_name => 'xml')
->set_attribute (version => '1.0');
}
$self->_clp (__ => $o);
}
$self->_parse_document_entity ($r, \$s, $o, %opt);
wantarray ? ($r, $o) : $r;
}
sub _parse_document_entity ($$\$$;%) {
my ($self, $c, $s, $o, %opt) = @_;
$o->{entity_type} = 'document_entity';
my %occur;
my $no_doctype = sub {
my ($o) = @_;
$self->_raise_error ($o, c => $c, type => 'WARN_DOCTYPE_NOT_FOUND');
if ($opt{alt_dtd_external_subset}) {
my $D = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'doctype');
$opt{entMan}->set_doctype_node ($D);
$D->set_attribute (SYSTEM => $opt{alt_dtd_external_subset});
$self->_raise_error ($o, type => 'MSG_EXTERNAL_DTD_SUBSET_USED',
t => [$opt{alt_dtd_external_subset}]);
$self->_parse_dtd_external_subset ($o, $D, \%opt);
}
};
while ($$s) {
if ($$s =~ /^<$xml_re{Name}/) { #
&$no_doctype ($o) unless $occur{doctype};
$self->_parse_element_content ($c, $s, $o, entMan => $opt{entMan});
$occur{element} = 1; $occur{pi} = 0;
} elsif ($$s =~ s/^($xml_re{s})//s) { # s
$c->append_text ($1);
$self->_clp ($1 => $o);
} elsif ($$s =~ s/^append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'doctype');
$opt{entMan}->set_doctype_node ($D);
$self->_clp (_________ => $o);
if ($$s =~ s/^($xml_re{s}($xml_re{Name}))//s) {
$D->set_attribute (qname => $2);
$occur{doctype} = $2;
$self->_clp ($1 => $o);
} else {
$occur{doctype} = 1;
$self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_NAME_NOT_FOUND', c => $D);
}
my $have_sysid = 0;
if ($$s =~ s/^($xml_re{s}PUBLIC)//s) {
$self->_clp ($1 => $o);
if ($$s =~ s/^($xml_re{s})($xml_re{__AttValue_simple})//s) {
$self->_clp ($1 => $o);
my $pid = $2; $pid = substr ($pid, 1, length ($pid) - 2);
$D->set_attribute (PUBLIC => $opt{entMan}->check_public_id ($o, $pid));
$self->_clp ($2 => $o);
$have_sysid = 1;
} else {
$self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_PID_LITERAL_NOT_FOUND', c => $D);
}
} elsif ($$s =~ s/^($xml_re{s}SYSTEM)//s) {
$self->_clp ($1 => $o);
$have_sysid = 2;
}
if ($have_sysid) {
if ($$s =~ s/^($xml_re{s})($xml_re{__AttValue_simple})//s) {
$self->_clp ($1 => $o);
my $sid = $2; $sid = substr ($sid, 1, length ($sid) - 2);
$D->set_attribute (SYSTEM => $opt{entMan}->check_system_id ($o, $sid));
$self->_clp ($2 => $o);
} elsif ($have_sysid) {
$self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_SYSID_LITERAL_NOT_FOUND', c => $D);
$D->set_attribute (SYSTEM => $NS{internal_invalid_sysid}) if $have_sysid == 1;
undef $have_sysid if $have_sysid == 2;
}
}
if ($opt{alt_dtd_external_subset}) {
$D->remove_attribute ('PUBLIC');
$D->set_attribute (SYSTEM => $opt{alt_dtd_external_subset});
$self->_raise_error ($o, type => 'MSG_EXTERNAL_DTD_SUBSET_USED',
t => [$opt{alt_dtd_external_subset}]);
$have_sysid = 1;
}
## dso for the internal subset
if ($$s =~ s/^((?:$xml_re{s})?\[)//s) {
$self->_clp ($1 => $o);
$self->_parse_dtd ($D, $s, $o, return_with_dsc => 1, validate_notation_declared => 1,
entMan => $opt{entMan});
## dsc and mdo is processed by _parse_dtd
} elsif ($$s =~ s/^((?:$xml_re{s})?>)//s) {
$self->_clp ($1 => $o);
} else {
$self->_raise_error ($o, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', c => $D, t => $D);
}
## Read and parse the external subset
$self->_parse_dtd_external_subset ($o, $D, \%opt) if $have_sysid;
} elsif ($$s =~ s/^($xml_re{PI_M})//s) { # ## TODO: pi parsing
## PI before DOCTYPE declaration
if ($2 eq 'xml') {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_POSITION');
} else {
$c->append_new_node (type => '#pi', local_name => $2, value => $3)
->flag (smxp__src_pos => $self->_make_clone_of ($o));
$self->_clp ($1 => $o);
}
$occur{pi} = 1;
} elsif ($$s =~ /^)
sub _parse_comment_declaration ($$$$;%) {
my ($self, $s, $c, $o, %opt) = @_;
my $in_com = 0;
my $has_com = 0;
substr ($$s, 0, 2) = ''; # _clp (__ => $o);
while ($$s) {
if ($in_com && ($$s =~ s/^([^-]+(?:[^-]|-[^-])*|-[^-](?:[^-]|-[^-])*)//s)) {
$c->append_new_node (type => '#comment', value => $1);
$self->_clp ($1 => $o);
} elsif ($$s =~ s/^--//) {
unless ($in_com) { # open
$in_com = 1;
$self->_raise_error ($o, c => $c, type => 'SYNTAX_MD_COMMENT_MULTIPLE')
if $has_com;
$has_com = 1;
} else { # close
$in_com = 0;
}
$self->_clp (__ => $o);
} elsif (!$in_com && ($$s =~ s/^($xml_re{s})//s)) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_MD_COMMENT_DS');
$self->_clp ($1 => $o);
} elsif (!$in_com && ($$s =~ s/^>//)) {
$self->_clp (_ => $o);
return undef;
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
$self->_clp (substr ($$s, 0, 1) => $o);
substr ($$s, 0, 1) = '';
}
}
$self->_raise_error ($o, c => $c, type => 'SYNTAX_MD_COMMENT_COM_NOT_CLOSED')
if $in_com;
$self->_raise_error ($o, c => $c, type => 'SYNTAX_MD_NOT_CLOSED');
undef;
}
sub _parse_attr_value_literal_data ($$$$;%) {
my ($self, $c, $s, $o, %opt) = @_;
my $rt = '';
while (length $$s) {
if ($$s =~ s/^(?:x([0-9A-Fa-f]+)|([0-9]+));//) {
my $char = chr (defined $1 ? hex $1 : 0 + $2);
$self->_warn_char_val ($o, $char, ref => 1);
$c->append_new_node (type => '#reference', value => ord $char,
namespace_uri => $NS{SGML}.'char:ref'.(defined $1?':hex':''));
$rt .= $char;
} elsif ($$s =~ s/^&($xml_re{Name});//) {
my ($ename, $entity_ref) = ($1, '&'.$1.';');
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ename)
if index ($ename, ':') > -1;
my $eref_node = $c->append_new_node (type => '#reference', local_name => $ename,
namespace_uri => $NS{SGML}.'entity');
my $entity = $opt{entMan}->get_entity ($ename, dont_use_predefined_entities => 1);
if (!ref ($entity) && {qw/lt 1 gt 1 amp 1 quot 1 apos 1/}->{$ename}) {
$self->_raise_error ($o, c => $eref_node, type => 'WARN_PREDEFINED_ENTITY_NOT_DECLARED',
t => $entity_ref);
$entity = $opt{entMan}->get_entity ($ename);
}
if (!ref $entity) {
$self->_raise_error ($o, c => $eref_node,
type => ($opt{entMan}->is_standalone_document_1?
'WF':'V').'C_ENTITY_DECLARED',
t => $entity_ref);
$rt .= $entity_ref;
} else {
my $o2 = $self->_make_clone_of ($o);
if ($o2->{__entities}->{$entity_ref}) {
$self->_raise_error ($o, c => $eref_node, type => 'WFC_NO_RECURSION', t => $entity_ref);
$rt .= $entity_ref;
} else {
my $entity_value = $entity->get_attribute ('value');
if (ref $entity_value) {
$o2->{__entities}->{$entity_ref} = 1;
$o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
$o2->{entity} = $entity_ref; $o2->{line} = 0; $o2->{pos} = 0;
my $ev = $entity_value->_entity_parameter_literal_value;
$rt .= $self->_parse_attr_value_literal_data ($eref_node, \$ev, $o2,
entMan => $opt{entMan});
$eref_node->flag (smxp__ref_expanded => 1);
} else {
$self->_raise_error ($o, type => 'WFC_NO_EXTERNAL_ENTITY_REFERENCE',
c => $eref_node, t => $entity_ref);
$rt .= $entity_ref;
}
} # ref recursive?
} # entity declared or not declared
$self->_clp ($entity_ref => $o);
} elsif ($$s =~ s/^([^&<]+)//s) {
my $tt = $1;
$tt =~ tr/\x09\x0A\x0D/\x20\x20\x20/;
$c->append_text ($tt);
$rt .= $tt;
$self->_clp ($tt => $o);
} elsif ($$s =~ s/^/) {
$self->_raise_error ($o, c => $c, type => 'WFC_NO_LE_IN_ATTRIBUTE_VALUE');
$rt .= '<';
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
$rt .= substr ($$s, 0, 1);
substr ($$s, 0, 1) = '';
}
} # $$s
## Note: Implementation of normalize for non-CDATA attribute values should be done
## out of this module.
#if ({qw/ID 1 IDREF 1 IDREFS 1 NMTOKEN 1 NMTOKENS 1
# NOTATION 1 NOTATIONS 1/}->{$opt{attr_type}}) {
# $rt =~ s/\x20\x20+/\x20/g;
# $rt =~ s/^\x20+//; $rt =~ s/\x20+$//;
#}
$rt;
}
sub _parse_rpdata ($$\$$;%) {
my ($self, $c, $s, $o, %opt) = @_;
my $tt = '';
while ($$s) {
if ($$s =~ s/^$xml_re{PEReference_M}//) {
my ($ref, $ename) = ('%'.$1.';', $1);
$self->_raise_error ($o, type => 'WFC_PE_IN_INTERNAL_SUBSET', t => $ref)
if $o->{entity_type} eq 'document_entity';
$self->_raise_error ($o, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ref)
if index ($ename, ':') > -1;
my $eref = $c->append_new_node (type => '#reference', local_name => $ename,
namespace_uri => $NS{SGML}.'entity:parameter');
unless ($opt{dont_resolve_entity_ref}) {
my $entity = $opt{entMan}->get_entity ($ename,
namespace_uri => $NS{SGML}.'entity:parameter');
if (!ref $entity) {
$self->_raise_error ($o, c => $c, type => 'VC_ENTITY_DECLARED', t => $ref);
} else {
my $o2 = $self->_make_clone_of ($o);
if ($o2->{__entities}->{$ref}) {
$self->_raise_error ($o, c => $c, type => 'WFC_NO_RECURSION', t => $ref);
} elsif (defined ($entity->flag ('smxp__entity_replacement_text_rpdata'))) {
my $ev = $entity->flag ('smxp__entity_replacement_text_rpdata');
$eref->append_text ($ev);
$tt .= $ev;
} else {
$o2->{entity} = $ref;
my $entity_value = $entity->get_attribute ('value');
if (ref $entity_value) { ## Internal entity
$o2->{__entities}->{$ref} = 1;
$o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
$o2->{line} = 0; $o2->{pos} = 0;
my $ev = $entity_value->_entity_parameter_literal_value;
$ev = $self->_parse_rpdata ($eref, \$ev, $o2, entMan => $opt{entMan});
$entity->flag (smxp__entity_replacement_text_rpdata => $ev);
$tt .= $ev;
} else { ## External entity
$o2->{entity_type} = 'external_parameter_entity';
$c->root_node->flag (smxp__declaration_may_not_be_read => 1)
if !(index ($o->{entity_type}, 'external') > -1)
|| !$opt{entMan}->is_standalone_document;
my $ext_ent = $opt{entMan}->get_external_entity ($self, $entity, $o2);
if ($ext_ent->{NDATA}) { ## non-parsed entity
$self->_raise_error ($o, type => 'WFC_PARSED_ENTITY', c => $entity, t => $ref);
} elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
$self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
t => [$ref, $o2->{uri}, $ext_ent->{error}->{reason_text}]);
$c->root_node->flag (smxp__stop_read_dtd => 1)
unless $opt{entMan}->is_standalone_document;
} else { ## parsed entity
$o2->{__entities}->{$ref} = 1;
my $ev = $ext_ent->{text};
$ev = $self->_parse_rpdata ($eref, \$ev, $o2, entMan => $opt{entMan});
$entity->flag (smxp__entity_replacement_text_rpdata => $ev);
$tt .= $ev;
$eref->flag (smxp__ref_expanded => 1);
} # external parsed entity
} # external entity
} # not recursive
} # entity defined
} # read not stopped
$self->_clp ($ref => $o);
} elsif ($$s =~ s/^([^%]+)//) {
my $t = $1; my $r = '';
while ($t) {
if ($t =~ s/^(?:x([0-9A-Fa-f]+)|([0-9]+));//) {
for (chr ($1 ? hex ($1) : $2)) {
$self->_warn_char_val ($o, $_, ref => 1);
$r .= $_;
$c->append_new_node (type => '#reference', value => ord $_,
namespace_uri => $NS{SGML}.'char:ref'.(defined $1 ? ':hex' : ''));
}
$self->_clp ((defined $1 ? '___'.$1:'__'.$2) => $o);
} elsif ($t =~ s/^&($xml_re{Name});//) {
my ($entity_ref, $eref) = ($1, '&'.$1.';');
$r .= $entity_ref;
$self->_raise_error ($o, type => 'NS_SYNTAX_NAME_IS_NCNAME', c => $c, t => $entity_ref)
if index ($eref, ':') > -1;
my $entity = $opt{entMan}->get_entity ($eref);
if (ref ($entity) && ref ($entity->get_attribute ('NDATA'))) {
$self->_raise_error ($o, type => 'ERR_XML_NDATA_REF_IN_ENTITY_VALUE',
c => $c, t => $entity_ref);
## Note: this error is not raisen when the entity referred is declared after
## the EntityValue occurs.
## Note: this error was a fatal error, but refined by XML 1.0 SE Errata.
}
$c->append_new_node (type => '#reference', namespace_uri => $NS{SGML}.'entity',
local_name => $eref);
$self->_clp ($entity_ref => $o);
} elsif ($t =~ s/^&//) {
$self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', c => $c, t => '&');
$r .= '&';
$c->append_new_node (type => '#reference', namespace_uri => $NS{SGML}.'char:ref',
value => 0x26);
$self->_clp (_ => $o);
} elsif ($t =~ s/^([^&]+)//s) {
$r .= $1;
$c->append_new_node (type => '#text', value => $1);
$self->_clp ($1 => $o);
}
}
$tt .= $r;
} else {
$self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
$self->_clp (substr ($$s, 0, 1) => $o);
substr ($$s, 0, 1) = '';
}
} # while $$s
$tt;
}
sub _parse_md_params ($$$$$;%) {
my ($self, $c, $s, $o, %opt) = @_;
my $t = '';
while ($$s) {
if ($$s =~ s/^$xml_re{PEReference_M}//) {
my ($ref, $ename) = ('%'.$1.';', $1);
$self->_raise_error ($o, c => $c, type => 'WFC_PE_IN_INTERNAL_SUBSET', t => $ref)
if $o->{entity_type} eq 'document_entity';
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ref)
if index ($ename, ':') > -1;
my $eref = $c->append_new_node (type => '#reference', local_name => $ename,
namespace_uri => $NS{SGML}.'entity:parameter');
unless ($opt{dont_resolve_entity_ref}) {
my $entity = $opt{entMan}->get_entity ($ename,
namespace_uri => $NS{SGML}.'entity:parameter');
if (!ref $entity) {
$self->_raise_error ($o, c => $c, type => 'VC_ENTITY_DECLARED', t => $ref);
} else {
if ($o->{__entities}->{$ref}) {
$self->_raise_error ($o, c => $c, type => 'WFC_NO_RECURSION', t => $ref);
} elsif (defined ($entity->flag ('smxp__entity_replacement_text_md_params'))) {
$t .= ' '.$entity->flag ('smxp__entity_replacement_text_md_params').' ';
} else {
my $o2 = $self->_make_clone_of ($o);
$o2->{entity} = $ref;
my $entity_value = $entity->get_attribute ('value');
if (ref $entity_value) { ## Internal entity
$o2->{__entities}->{$ref} = 1;
$o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
$o2->{line} = 0; $o2->{pos} = 0;
my $ev = $entity_value->_entity_parameter_literal_value;
$ev = $self->_parse_md_params ($eref, \$ev, $o2, entMan => $opt{entMan});
$entity->flag (smxp__entity_replacement_text_md_params => $ev);
$t .= ' '.$ev.' ';
$eref->flag (smxp__ref_expanded => 1);
} else { ## External entity
$o2->{entity_type} = 'external_parameter_entity';
$c->root_node->flag (smxp__declaration_may_not_be_read => 1)
if !(index ($o->{entity_type}, 'external') > -1)
|| !$opt{entMan}->is_standalone_document;
my $ext_ent = $opt{entMan}->get_external_entity ($self, $entity, $o2);
if ($ext_ent->{NDATA}) { ## non-parsed entity
$self->_raise_error ($o, type => 'WFC_PARSED_ENTITY', c => $entity, t => $ref);
} elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
$self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
t => [$ref, $o2->{uri}, $ext_ent->{error}->{reason_text}]);
$c->root_node->flag (smxp__stop_read_dtd => 1)
unless $opt{entMan}->is_standalone_document;
} else { ## parsed entity
$o2->{__entities}->{$ref} = 1;
my $ev = $ext_ent->{text};
$ev = $self->_parse_md_params ($eref, \$ev, $o2, entMan => $opt{entMan});
$entity->flag (smxp__entity_replacement_text_md_params => $ev);
$t .= ' '.$ev.' ';
$eref->flag (smxp__ref_expanded => 1);
} # external parsed entity
} # external entity
} # not recursive
} # entity defined
} # not stopped
$c->flag (smxp__defined_with_param_ref => 1);
$self->_clp ($ref => $o);
} elsif ($$s =~ s/^($xml_re{__AttValue_simple})//s) {
my $all = $1;
$t .= $all;
$c->append_new_node (type => '#xml', value => $all); ## Note: is this safe?
$self->_clp ($all => $o);
} elsif ($$s =~ s/^(\p{InXMLNameChar}+)//) {
$c->append_text ($1);
$t .= $1;
$self->_clp ($1 => $o);
if ($$s =~ s/^([+*?])//) {
$c->append_text ($1);
$t .= $1;
$self->_clp (_ => $o);
}
} elsif ($$s =~ s/^\(//) {
$c->append_text ('(');
my $grp = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'group');
$t .= '('.$self->_parse_md_params ($grp, $s, $o, entMan => $opt{entMan}, return_by_grpc => 1);
$c->flag (smxp__defined_with_param_ref => 1)
if $grp->flag ('smxp__defined_with_param_ref');
} elsif ($$s =~ s/^([%#,|])//) {
$c->append_text ($1);
$t .= $1;
$self->_clp ($1 => $o);
} elsif ($$s =~ s/^($xml_re{s})//s) {
$c->append_text ($1);
$t .= $1;
$self->_clp ($1 => $o);
} elsif ($opt{return_by_mdc} && ($$s =~ s/^>//)) { ## mdc
$self->_clp (_ => $o);
return $t;
} elsif (($opt{return_by_mdc}||$opt{return_by_grpc}) && ($$s =~ m/^)) { ## maybe mdo
$self->_raise_error ($o, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', t => $c, c => $c);
return $t;
} elsif ($opt{return_by_grpc} && ($$s =~ s/^\)//)) { ## grpc
$self->_clp (_ => $o);
$t .= ')';
$c->parent_node->append_text (')');
if ($$s =~ s/^([+*?])//) {
$t .= $1;
$c->parent_node->append_text ($1);
$self->_clp (_ => $o);
}
return $t;
} else {
$self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
$self->_clp (substr ($$s, 0, 1) => $o);
substr ($$s, 0, 1) = '';
}
} # while
if ($opt{return_by_grpc}) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_GROUP_NOT_CLOSED');
$t .= ')';
}
$t;
}
sub _warn_char_val ($$$%) {
my ($self, $o, $ch, %o) = @_;
if ($ch =~ /^(.*?)(\P{InXMLChar})/) {
$self->_clp ($1 => $o);
$self->_raise_error ($o, type => (($o{ref}?'WFC':'SYNTAX').'_LEGAL_CHARACTER'), t => ord $2);
} elsif ($ch =~ /^(.*?)(\p{InXML_deprecated_noncharacter})/) {
$self->_clp ($1 => $o);
$self->_raise_error ($o, type => 'WARN_UNICODE_NONCHARACTER', t => ord $2);
# } elsif ($ch =~ /^(.*?)(\p{Compat})/) {
# $self->_clp ($1 => $o);
# $self->_raise_error ($o, type => 'WARN_UNICODE_COMPAT_CHARACTER', t => ord $2);
} elsif ($ch =~ /^(.*?)(\p{InXML_unicode_xml_not_suitable})/) {
$self->_clp ($1 => $o);
$self->_raise_error ($o, type => 'WARN_UNICODE_XML_NOT_SUITABLE_CHARACTER', t => ord $2);
}
}
sub _parse_reference ($$$$) {
my ($self, $c, $ref, $o) = @_;
my $r;
if ($ref =~ /$xml_re{EntityRef_M}/) { ## BUG: QName
$r = $c->append_new_node (type => '#reference', local_name => $1,
namespace_uri => $NS{SGML}.'entity');
} elsif ($ref =~ /x([0-9A-Fa-f]+)/) {
my $ch = hex $1;
$self->_warn_char_val ($o, chr $ch, ref => 1);
$r = $c->append_new_node (type => '#reference', value => $ch,
namespace_uri => $NS{SGML}.'char:ref:hex');
} elsif ($ref =~ /([0-9]+)/) {
my $ch = 0+$1;
$self->_warn_char_val ($o, chr $ch, ref => 1);
$r = $c->append_new_node (type => '#reference', value => $ch,
namespace_uri => $NS{SGML}.'char:ref');
} else {
$self->_raise_error ($o, type => 'UNKNOWN', t => $ref);
}
$self->_clp ($ref => $o);
$r;
}
sub _parse_ignored_marked_section ($$\$$;%) {
my ($self, $c, $s, $o) = @_;
while ($$s) {
if ($$s =~ s/^_clp (___ => $o);
$self->_parse_ignored_marked_section ($c->append_new_node (type => '#section'), $s, $o);
} elsif ($$s =~ s/^\]\]>//) {
$self->_clp (___ => $o);
return undef;
} elsif ($$s =~ s/^((?:(?!).)+)//s) {
$c->append_text ($1);
$self->_clp ($1 => $o);
}
} # $$s
$self->_raise_error ($o, c => $c, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', t => $c);
undef;
}
sub _parse_xml_or_text_declaration ($$\$$) {
my ($self, $c, $s, $o) = @_;
if ($$s =~ s/^$xml_re{_xml_PI_M}//s) {
my $data = $1;
$self->_clp (_____ => $o);
if (length $data) {
$self->_parse_xml_declaration ($c, $data, $o);
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
}
$self->_clp (__ => $o);
}
}
sub _parse_xml_declaration ($$$$) {
my ($self, $c, $attrs, $o) = @_;
my $stage = 0; # 0:
$attrs = ' ' . $attrs;
while ($attrs) {
if ($attrs =~ s/^($xml_re{s}version(?:$xml_re{s})?=(?:$xml_re{s})?("[A-Za-z0-9_.:-]+"|'[A-Za-z0-9_.:-]+'))//s) {
my $version = substr ($2, 1, length ($2) - 2);
if ($stage > 0) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'version');
}
$c->set_attribute (version => $version);
## TODO: XML 1.1 support
if ($version ne '1.0') {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_UNSUPPORTED_XML_VERSION', t => $version);
}
$self->_clp ($1 => $o); $stage++;
} elsif ($attrs =~ s/^($xml_re{s}encoding(?:$xml_re{s})?=(?:$xml_re{s})?("[A-Za-z0-9_.-]+"|'[A-Za-z0-9_.:-]+'))//s) {
if ($stage > 2) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'encoding');
} elsif ($stage == 0) { ## No version pseudo-attr
if ($o->{entity_type} eq 'document_entity') {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_VERSION_ATTR');
$c->set_attribute (version => '1.0');
} else {
$self->_raise_error ($o, c => $attrs, type => 'WARN_XML_DECLARE_NO_VERSION_ATTR');
$o->{entity_type} = 'external_parsed_entity';
}
}
$c->set_attribute (encoding => substr ($2, 1, length ($2) - 2));
$self->_clp ($1 => $o); $stage = 2;
} elsif ($attrs =~ s/^($xml_re{s}standalone(?:$xml_re{s})?=(?:$xml_re{s})?("(?:yes|no)"|'(?:yes|no)'))//s) {
if ($stage == 0) { ## 'version' or 'encoding' is expected
if ($o->{entity_type} eq 'document_entity') { ## XML declaration
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_VERSION_ATTR');
$c->set_attribute (version => '1.0');
} else { ## Text declaration
$self->_raise_error ($o, c => $attrs, type => 'WARN_XML_DECLARE_NO_VERSION_ATTR');
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ENCODING_ATTR');
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_STANDALONE_ATTR');
}
} elsif ($stage > 3) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'standalone');
}
$c->set_attribute (standalone => (substr ($2, 1, 1) eq 'y' ? 'yes' : 'no'));
$self->_clp ($1 => $o); $stage = 3;
} elsif ($attrs =~ s/^($xml_re{s})//s) {
my $s = $1;
if ($stage == 0) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
$c->set_attribute (version => '1.0');
}
$self->_clp ($s, $o); $stage = 4;
} else {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => $attrs);
$self->_clp ($attrs => $o); undef $attrs;
}
} # while
if ($stage == 0) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
$c->set_attribute (version => '1.0');
} elsif ($stage == 1 && index ($o->{entity_type}, 'external') > -1) {
$self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ENCODING_ATTR');
}
}
sub _parse_entity_declaration ($$$$;%) {
my ($self, $s, $c, $o, %opt) = @_;
my $p = ''; ## notation ? 'n' : parameter entity ? '%' : '';
my $root_node = $c->root_node;
my $e = $c->append_new_node (type => '#declaration');
$e->flag (smxp__uri_in_which_declaration_is => $o->{uri});
$e->flag (smxp__declaration_may_not_be_read
=> $root_node->flag ('smxp__declaration_may_not_be_read'));
## Entity? or notation?
if ($$s =~ s/^namespace_uri ($NS{SGML}.'entity');
$self->_clp (________ => $o);
} else {
$$s =~ s/^namespace_uri ($NS{SGML}.'notation');
$self->_clp (__________ => $o); $p = 'n';
}
## Parameters
my $t;
my $dont_process = $root_node->flag ('smxp__stop_read_dtd');
$t = $self->_parse_md_params ($e, $s, $o, dont_resolve_entity_ref => $dont_process,
return_by_mdc => 1, entMan => $opt{entMan});
$dont_process = $root_node->flag ('smxp__stop_read_dtd');
unless ($dont_process) {
my $o = $self->_make_clone_of ($o);
my $is_internal = 1;
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
if ($t =~ s/^%//) {
if ($p eq 'n') {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => '%');
} else {
$e->namespace_uri ($NS{SGML}.'entity:parameter'); $p = '%';
}
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
}
my $ename;
if ($t =~ s/^($xml_re{Name})//) {
$ename = $1;
if ($opt{entMan}->is_declared_entity ($ename, namespace_uri => $e->namespace_uri,
dont_use_predefined_entities => 1,
seek => 0)) {
if ($p eq 'n') {
$self->_raise_error ($o, c => $e,
type => 'VC_UNIQUE_NOTATION_NAME', t => $ename);
} else {
$self->_raise_error ($o, c => $e, t => $ename,
type => 'WARN_UNIQUE_'.($p eq '%' ? 'PARAMETER_' : '').'ENTITY_NAME');
}
} else { ## Regist to entMan
$opt{entMan}->is_declared_entity ($ename, namespace_uri => $e->namespace_uri,
dont_use_predefined_entities => 1,
set_value => $e, seek => 0);
}
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ename)
if index ($ename, ':') > -1;
$e->local_name ($ename);
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
} else {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_NAME_NOT_FOUND',
t => substr ($t, 0, 10));
}
if ($t =~ s/^PUBLIC//) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
unless ($t =~ s/^($xml_re{__AttValue_simple})//s) { ## TODO: new error
$self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_PID_NOT_FOUND',
t => substr ($t, 0, 10));
} else {
$e->set_attribute (PUBLIC => $opt{entMan}->check_public_id
($o, substr ($1, 1, length ($1) - 2)));
my $f = $t =~ s/^$xml_re{s}//s ? 1 : 0;
if ($t =~ s/^($xml_re{__AttValue_simple})//s) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $f;
$e->set_attribute (SYSTEM => $opt{entMan}->check_system_id
($o, substr ($1, 1, length ($1) - 2)));
} else {
if ($p ne 'n') {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_SYSID_NOT_FOUND',
t => substr ($t, 0, 10));
$e->set_attribute (SYSTEM => $NS{internal_invalid_sysid});
}
}
}
$is_internal = 0;
} elsif ($t =~ s/^SYSTEM//) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
if ($t =~ s/^($xml_re{__AttValue_simple})//s) {
$e->set_attribute (SYSTEM => $opt{entMan}->check_system_id
($o, substr ($1, 1, length ($1) - 2)));
} else { ## TODO: error text
$self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_SYSID_NOT_FOUND',
t => substr ($t, 0, 10));
$e->set_attribute (SYSTEM => $NS{internal_invalid_sysid});
}
$is_internal = 0;
} elsif ($p ne 'n' && $t =~ s/^($xml_re{__AttValue_simple})//s) { ## EntityValue (ENTITY only)
## TOTO: $o
my $ev = $1; $ev = substr ($ev, 1, length ($ev) - 2);
$ev = $self->_parse_rpdata ($e->set_attribute ('value'), \$ev, $o, entMan => $opt{entMan});
unless (defined $e->flag ('smxp__entity_replacement_text_rpdata')) {
$e->flag (smxp__entity_replacement_text_rpdata => $ev);
}
if (($p ne '%') && {qw/lt 1 gt 1 amp 1 quot 1 apos 1/}->{$ename}) {
## TODO: check when external entity too
$self->_raise_error ($o, c => $e,
type => 'FATAL_ERR_PREDEFINED_ENTITY', t => [$ename, $ev])
unless {'lt|<' => 1, 'gt|>' => 1,
'amp|&' => 1, 'apos|'' => 1,
'quot|"' => 1,
'lt|<' => 1, 'gt|>' => 1,
'amp|&' => 1, 'apos|'' => 1,
'quot|"' => 1,
'gt|>' => 1, "apos|'" => 1, 'quot|"' => 1,
}->{$ename.'|'.lc ($ev)};
}
}
if ($t =~ s/^$xml_re{s}NDATA//s) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
unless $t =~ s/^$xml_re{s}//s;
unless ($t =~ s/^($xml_re{Name})//s) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
} else {
my $nname = $1;
if ($p eq '%') { ## parameter entity
$self->_raise_error ($o, c => $e, type => 'SYNTAX_PE_NDATA', t => $nname);
} elsif ($is_internal) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_KEYWORD', t => 'NDATA');
} else {
$e->set_attribute (NDATA => $nname)
->flag (smxp__src_pos => $o);
}
}
}
$t =~ s/^$xml_re{s}//s;
if (length $t) {
$self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => $t);
}
} else { ## dont_process
$c->flag (smxp__non_processed_declaration => 1);
$self->_raise_error ($o, c => $c, type => 'WARN_ENTITY_DECLARATION_NOT_PROCESSED');
}
}
sub _parse_element_declaration ($$$$;%) {
my ($self, $s, $c, $o, %opt) = (@_);
$c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'element');
unless ($$s =~ s/^_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
return;
}
$self->_clp (_________ => $o);
my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
## Element type name
if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
my $type_qname = $2;
if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
$type_qname =~ tr/:/_/;
}
if ($opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'element',
seek => 0)) {
$self->_raise_error ($o, c => $c,
type => 'VC_UNIQUE_ELEMENT_TYPE_NAME', t => $type_qname);
} else { ## Regist to entMan
$opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'element',
set_value => $c, seek => 0);
}
$c->set_attribute (qname => $type_qname);
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
return;
}
## No content model declaration
$t =~ s/^$xml_re{s}//s;
unless ($t) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
return;
}
## Content model
my $delimited = 1;
my $in_group = 0;
my @grp_connector;
my $is_pcdata;
my $r = $c;
while ($t) {
if ($t =~ s/^$xml_re{s}//s) {
#
} elsif ($t =~ s/^($xml_re{Name})//) {
my $name = $1;
if ($in_group) {
unless ($delimited) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
t => $name);
} else {
$delimited = 0;
}
if (substr ($name, 0, 1) eq ':' || substr ($name, -1, 1) eq ':') {
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $name);
$name =~ tr/:/_/;
}
my $enode = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
local_name => 'element');
$enode->set_attribute (qname => $name);
if ($t =~ s/^([+*?])//) {
$enode->set_attribute (occurence => $1);
}
} else {
if ({qw/EMPTY 1 ANY 1/}->{$name}) {
#
} elsif ({qw/PCDATA 1 CDATA 1/}->{$name}) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_KWD',
t => $name);
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
t => $name);
}
$r->set_attribute (content => $name);
last;
} # out of group
} elsif ($in_group && $t =~ s/^\#($xml_re{Name})//) {
my $kwd = $1;
unless ($delimited) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
t => $kwd);
} else {
$delimited = 0;
}
if ($in_group > 1 || $grp_connector[$in_group]) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_KWD_POSITION',
t => $kwd);
}
if ($kwd ne 'PCDATA') {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
t => $kwd);
} else {
$is_pcdata = 1;
$r->set_attribute (content => 'mixed');
}
} elsif ($in_group && $t =~ s/^([|,&])//) {
my $connector = $1;
if ($delimited) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
t => $connector.substr ($t, 0, 9));
} else {
$delimited = 1;
}
if ($connector eq '&') {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_CONNECTOR',
t => $connector);
$connector = ',';
} elsif ($is_pcdata && $connector ne '|') {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_PCDATA_CONNECTOR',
t => $connector);
$connector = '|';
}
unless ($grp_connector[$in_group]) {
$grp_connector[$in_group] = $connector;
$c->set_attribute (connector => $connector);
} elsif ($grp_connector[$in_group] ne $connector) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SAME_CONNECTOR',
t => [$grp_connector[$in_group], $connector]);
}
} elsif ($t =~ s/^\(//) {
unless ($delimited) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
t => '('.substr ($t, 0, 9));
}
if ($is_pcdata) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_NESTED');
} else {
$c = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
local_name => 'group');
}
$in_group++;
$grp_connector[$in_group] = undef;
$delimited = 1;
} elsif ($in_group && $t =~ s/^\)//) {
if ($delimited) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
t => ')'.substr ($t, 0, 9));
$delimited = 0;
}
if ($t =~ s/^([+*?])//) {
my $occur = $1;
if ($is_pcdata && $occur ne '*') {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE',
t => $occur);
}
$c->set_attribute (occurence => $occur);
} elsif ($is_pcdata && $grp_connector[$in_group]) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE');
}
$c = $c->parent_node;
$in_group--;
last if !$in_group;
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
substr ($t, 0, 1) = '';
}
} # $t
if ($in_group) {
## Note: Maybe this error will not happen since md_params parsing report it
$self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_GROUP_NOT_CLOSED');
}
if ($t =~ /[^$xml_re{_s__chars}]/) {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => $t);
}
}
sub _parse_attlist_declaration ($$$$;%) {
my ($self, $s, $c, $o, %opt) = (@_);
my $root_node = $c->root_node;
$c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'attlist');
$c->flag (smxp__declaration_may_not_be_read
=> $root_node->flag ('smxp__declaration_may_not_be_read'));
unless ($$s =~ s/^_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
return;
}
$self->_clp (_________ => $o);
my $dont_process = $root_node->flag ('smxp__stop_read_dtd');
my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
$dont_process = $root_node->flag ('smxp__stop_read_dtd');
unless ($dont_process) {
## Element type name
my $type_qname;
if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
$type_qname = $2;
if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
$type_qname =~ tr/:/_/;
}
if ($opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'attlist',
dont_use_predefined_entities => 1,
seek => 0)) {
$self->_raise_error ($o, c => $c,
type => 'WARN_XML_ATTLIST_AT_MOST_ONE_DECLARATION', t => $type_qname);
} else { ## Regist to entMan
$opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'attlist',
dont_use_predefined_entities => 1,
set_value => $c, seek => 0)
}
$c->set_attribute (qname => $type_qname);
} else {
$self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
return;
}
## Definition
my %defined;
while ($t) {
if ($t =~ s/^$xml_re{s}($xml_re{Name})//s) {
my %attr = (name => $1, type => undef);
if (substr ($attr{name}, 0, 1) eq ':' || substr ($attr{name}, -1, 1) eq ':') {
$self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $attr{name});
$attr{name} =~ tr/:/_/;
}
if ($defined{$attr{name}}) {
$self->_raise_error ($o, c => $c, type => 'WARN_XML_ATTLIST_AT_MOST_ONE_ATTR_DEF',
t => $attr{name});
} else {
$defined{$attr{name}} = 1;
}
$attr{node} = $c->append_new_node (type => '#element', namespace_uri => $NS{XML}.'attlist',
local_name => 'AttDef');
$attr{node}->set_attribute (qname => $attr{name});
if ($t =~ s/^$xml_re{s}//s) {
if ($t =~ s/^NOTATION//) {
$attr{type} = 'NOTATION';
unless ($t =~ s/^$xml_re{s}//s) {
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c,
t => substr ($t, 0, 10));
}
}
if (!$attr{type} && $t =~ s/^([A-Za-z]+)//) { # attname type
$attr{type} = $1;
unless ({qw/CDATA 1 ID 1 IDREF 1 IDREFS 1 NMTOKEN 1 NMTOKENS 1
ENTITY 1 ENTITIES 1/}->{$attr{type}}) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_TYPE',
c => $c, t => $attr{type});
}
} elsif ($t =~ s/^\(([^)]+)\)//) { # attname (group)
my $grp = $1;
if (index ($grp, '(') > -1) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NESTED_GROUP',
c => $c, t => $grp);
}
if (index ($grp, '&') > -1 || index ($grp, ',') > -1) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NON_BAR_CONNECTOR',
c => $c, t => $grp);
}
if ($grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//s) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CHAR',
c => $c, t => $1);
$grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//sg;
} else {
$grp =~ tr/\x09\x0A\x0D\x20//d; # $xml_re{_s__chars}
if ($grp =~ /(^\||\|\||\|$)/) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CONNECTOR',
c => $c, t => $1);
}
}
my @grp = grep {$_} split /\P{InXMLNameChar}+/, $grp;
if ($attr{type}) { ## NOTATION
for (@grp) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_NOTATION_NAME',
c => $c, t => $_)
unless /^$xml_re{Name}$/;
}
} else {
$attr{type} = 'enum';
}
for (@grp) {
if ($attr{enum}->{$_}) {
$self->_raise_error ($o, type => 'VC_NO_DUPLICATE_TOKENS', c => $c, t => $_);
} else {
$attr{enum}->{$_} = 1;
$attr{node}->append_new_node (type => '#element',
namespace_uri => $NS{XML}.'attlist',
local_name => 'enum')
->append_text ($_);
}
}
} else { # attname #somewhat or attname NOTATION non-group
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
$attr{type} ||= 'CDATA';
next;
}
$attr{node}->set_attribute (type => $attr{type});
## DefaultDecl
if ($t =~ s/^$xml_re{s}\#FIXED//s) {
$attr{deftype} = 'FIXED';
$attr{node}->set_attribute (default_type => $attr{deftype});
}
if ($t =~ s/^$xml_re{s}//s) {
if (!$attr{deftype} && $t =~ s/^\#([A-Za-z-]+)//) {
$attr{deftype} = $1;
unless ({qw/IMPLIED 1 REQUIRED 1/}->{$attr{deftype}}) {
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_DEFAULT', c => $c,
t => '#'.$attr{deftype});
}
$attr{node}->set_attribute (default_type => $attr{deftype});
} elsif ($t =~ s/^($xml_re{__AttValue_simple})//s) { # attname type "literal"
$attr{defvalue} = $attr{node}->set_attribute ('default_value');
my $pcdata = substr ($1, 1, length ($1) - 2);
$self->_parse_attr_value_literal_data ($attr{defvalue}, \$pcdata, $o,
entMan => $opt{entMan});
} elsif ($attr{deftype}) { # deftype eq FIXED
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
t => [$attr{name}, substr ($t, 0, 10)]);
next;
} else { # attname type $invalid
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c,
t => substr ($t, 0, 10));
next;
}
} elsif ($attr{deftype}) { # deftype eq FIXED
$self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
t => [$attr{name}, substr ($t, 0, 10)]);
next;
} else { # attname type$invalid
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
next;
}
} else { # attname#somewhat
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
next;
}
} elsif ($t =~ s/$xml_re{s}$//s) {
} else {
$self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
substr ($t, 0, 1) = '';
}
} # $t
unless (scalar keys %defined) {
$self->_raise_error ($o, c => $c, type => 'WARN_XML_ATTLIST_AT_LEAST_ONE_ATTR_DEF',
t => $type_qname);
}
} else { ## dont_process
$c->flag (smxp__non_processed_declaration => 1);
$self->_raise_error ($o, c => $c, type => 'WARN_ATTLIST_DECLARATION_NOT_PROCESSED');
}
}
sub _is_brother_of_root_element ($$) {
my ($self, $c) = @_;
for (@{$c->child_nodes}) {
if ($_->node_type eq '#element') {
return 1;
}
}
return 0;
}
## [internal] URI escaping unsafe characters
## $s = $parser->_uri_escape ($s)
sub _uri_escape ($$) {
shift;
my $s = shift; ## TODO: support utf8 flag
$s =~ s/([^0-9A-Za-z_.-])/sprintf '%%%02X', ord $1/ge;
$s;
}
## [internal] Duplication of HASH reference
## $ref = $parser->_make_clone_of ($ref)
sub _make_clone_of ($$;%) {
my ($self, $mother, %o) = @_;
if (ref $mother eq 'HASH') {
my $child = {};
$o{m_vs_c}->{$mother} = $child;
for (keys %$mother) {
if (ref ($mother->{$_}) eq 'HASH') {
$child->{$_} = $o{m_vs_c}->{$mother->{$_}} || $self->_make_clone_of ($mother->{$_});
} elsif (index (ref ($mother->{$_}), 'URI') > -1) {
$child->{$_} = $mother->{$_}->clone;
## BUG: $mother->{$A} === $mother->{$B}, then two clones are created
## BUG: if CODE, ARRAY, blessed,...
} else {
$child->{$_} = $mother->{$_};
}
}
return $child;
} else {
## BUG: not supported
}
}
## [internal] Count up line/position
## $self->_clp ($string, $o)
sub _clp ($$$) {
my (undef, $s => $o) = @_;
$s =~ s/\G[^\x0A]*\x0A/($o->{line}++, $o->{pos} = 0, '')/ges;
$o->{pos} += length $s;
}
## [internal] Split QName into prefix and NCName
## ($prefix or undef, $NCName) = $parser->_ns_parse_qname ($QName)
sub _ns_parse_qname ($$) {
my $qname = $_[1];
if ($qname =~ /:/) {
return split /:/, $qname, 2;
} else {
return (undef, $qname);
}
}
sub option ($$;$) {
my ($self, $name, $value) = @_;
if (defined $value) {
$self->{option}->{$name} = $value;
}
$self->{option}->{$name};
}
sub flag ($$;$) {
my ($self, $name, $value) = @_;
if (defined $value) {
$self->{flag}->{$name} = $value;
}
$self->{flag}->{$name};
}
=head1 FLAG NAMES DEFINED BY THIS MODULE
=head2 Flag for Message::Markup::XML instance
=over 4
=item smxp__declaration_may_not_be_read = 1/0 (root node)
Whether after this flag is trued entity/attlist declarations may not
be processed (bacause of declared in external entity or declared after
some external entity reference) or not.
=item smxp__declaration_may_not_be_read = 1/0 (#declaration node)
Whether entity/attlist declaration may not be processed or not.
=item smxp__defined_with_param_ref = 1/0 (#declaration node)
Whether the declaration is defined with one or more parameter entity or not.
=item smxp__entity_manager = instance of entity manager (root node)
=item smxp__entity_replacement_text_md_params = semi-parsed text (Parameter entity #declaration node)
=item smxp__entity_replacement_text_rpdata = semi-parsed text (Parameter entity #declaration node)
=item smxp__is_dtd_default = 1/0 (#attribute node)
Whether the attribute (name and value) is not specified explicily in STag
so completed from DTD's attlist declaration or not.
=item smxp__non_processed_declaration = 1/0 (Entity/attlist #declaration node)
Whether the declaration is not read 'cause of smxp__stop_read_dtd'ed or not.
=item smxp__original_qname = QName as specified in STag
(Only used to check STag-ETag matching.)
=item smxp__ref_expanded = 1/0 (#reference node)
Whether the reference is expanded or not
=item smxp__src_pos = "$o" object
Start position in source entity (is this flag obsolete??)
=item smxp__stop_read_dtd = 1/0 (root node)
Whether reading entity/attlist declaration is stopped 'cause of
some of external entity required to interpret the DTD is not processed
or not.
=item smxp__uri_in_which_declaration_is = uri (Entity #declaration node)
=cut
=head1 LICENSE
Copyright 2003 Wakaba
This program is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=cut
1; # $Date: 2006/09/10 11:19:24 $