/[suikacvs]/messaging/manakai/lib/Message/Markup/XML/Parser.pm
Suika

Contents of /messaging/manakai/lib/Message/Markup/XML/Parser.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.9 - (hide annotations) (download)
Sun Jul 13 02:32:24 2003 UTC (23 years ago) by w
Branch: MAIN
Changes since 1.8: +261 -133 lines
More attribute default support

1 wakaba 1.1
2     =head1 NAME
3    
4     SuikaWiki::Markup::XML::Parser --- SuikaWiki: Simple XML parser
5    
6     =head1 DESCRIPTION
7    
8     This is a simple XML parser intended to be used with SuikaWiki::Markup::XML.
9     After parsing of the XML document, this module returns a SuikaWiki::Markup::XML
10     object so that you can handle XML document with that module (and other modules
11     implementing same interface).
12    
13     This module is part of SuikaWiki.
14    
15     =cut
16    
17     package SuikaWiki::Markup::XML::Parser;
18     use strict;
19 w 1.9 our $VERSION = do{my @r=(q$Revision: 1.8 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
20 wakaba 1.6 use Char::Class::XML qw!InXML_NameStartChar InXMLNameChar InXMLChar
21     InXML_deprecated_noncharacter InXML_unicode_xml_not_suitable!;
22     require SuikaWiki::Markup::XML;
23 wakaba 1.2 require SuikaWiki::Markup::XML::Error;
24     *_raise_error = \&SuikaWiki::Markup::XML::Error::raise;
25 wakaba 1.6 our %NS;
26     *NS = \%SuikaWiki::Markup::XML::NS;
27 wakaba 1.1
28     =head1 METHODS
29    
30     WARNING: This module is under construction. Interface of this module is not yet fixed.
31    
32     =cut
33    
34     my %xml_re;
35     # [1] document = prolog element *Misc
36     # [2] Char = %x09 / %x0A / %x0D / U+0020-U+D7FF / U+E000-U+FFFD / U+10000-U+10FFFF ;; 1.0
37     # [2] Char = %x09 / %x0A / %x0D / %x20-7E / U+0085 / U+00A0-U+D7FF / U+E000-U+FFFD
38     # / U+10000-U+10FFFF ;; 1.1
39     # [3] s = 1*(%x20 / %x09 / %x0D / %x0A)
40 wakaba 1.6 $xml_re{s} = qr/[\x09\x0A\x0D\x20]+/s;
41 w 1.8 $xml_re{_s__chars} = qr/\x09\x0A\x0D\x20/s;
42 wakaba 1.1 # [4] NameChar = Letter / Digit / "." / "-" / "_" / ":" / CombiningChar / Extender ;; 1.0
43     # [4] NameStartChar = ":" / ALPHA / "_" / U+00C0-U+02FF / U+0370-U+037D
44     # / U+037F-U+1FFF / U+200C-U+200D / U+2070-U+218F
45     # / U+2C00-U+2FEF / U+3001-U+D7FF / U+F900-U+EFFFF ;; 1.1
46     # [4a] NameChar = NameStartChar / "-" / "." / DIGIT / U+00B7 / U+0300-U+036F
47     # / U+203F-U+2040 ;; 1.1
48     # $xml_re{NameChar} = qr/[A-Za-z0-9._:-]|[^\x00-\x7F]/;
49 wakaba 1.4 #$xml_re{NameChar} = qr/\p{InXMLNameChar}/;
50 wakaba 1.1 # [5] Name = (Letter / "_" / ":") *NameChar ;; 1.0
51     # [5] Name = NameStartChar *NameChar ;; 1.1
52     # $xml_re{Name} = qr/(?:[A-Z_:]|[^\x00-\x7F])(?:$xml_re{NameChar})*/;
53     $xml_re{Name} = qr/\p{InXML_NameStartChar}\p{InXMLNameChar}*/;
54     # [6] Names = Name *(S Name) ;; 1.0 FE & 1.0 FE-errata (2000-09-27) & 1.0 SE
55     # [6] Names = Name *(%x20 Name) ;; 1.0 FE-errata (2000-04-09) & 1.0 SE-errata
56 wakaba 1.4 #$xml_re{Names} = qr/$xml_re{Name}(?:$xml_re{s}$xml_re{Name})*/s;
57 wakaba 1.1 # [7] Nmtoken = 1*NameChar
58     #$xml_re{Nmtoken} = qr/(?:$xml_re{NameChar})+/;
59 wakaba 1.4 #$xml_re{Nmtoken} = qr/\p{InXMLNameChar}+/;
60 wakaba 1.1 # [8] Nmtokens = Nmtoken *(S Nmtoken) ;; 1.0 FE & 1.0 FE-errata (2000-09-27) & 1.0 SE
61     # [8] Nmtokens = Nmtoken *(%x20 Nmtoken) ;; 1.0 FE-errata (2000-04-09) & 1.0 SE-errata
62 wakaba 1.4 #$xml_re{Nmtokens} = qr/$xml_re{Nmtoken}(?:$xml_re{s}$xml_re{Nmtoken})*/s;
63 wakaba 1.1 # [9] EntityValue = <"> *((Char - ("%" / "&" / <">)) / PEReference / Reference) <">
64     # / "'" *((Char - ("%" / "&" / "'")) / PEReference / Reference) "'"
65     # [10] AttValue = <"> *((Char - ("<" / "&" / <">)) / Reference) <">
66     # / "'" *((Char - ("<" / "&" / "'")) / Reference) "'"
67     $xml_re{__AttValue_simple} = qr/"[^"]*"|'[^']*'/s;
68     # [11] SystemLiteral = <"> *(Char - <">) <"> / "'" *(Char - "'") "'"
69 wakaba 1.4 #$xml_re{SystemLiteral} = qr/"[^"]*"|'[^']*'/;
70 wakaba 1.1 # [12] PublicLiteral = <"> *PubidChar <"> / "'" *(PubidChar - "'") "'"
71     # [13] PubidChar = %x20 / %x0D / %x0A / ALPHA / DIGIT / "-" / "'" / "(" / ")"
72     # / "+" / "," / "." / "/" / ":" / "=" / "?" / ";" / "!" / "*"
73     # / "#" / "@" / "$" / "_" / "%"
74 wakaba 1.6 #$xml_re{PubidChar} = qr[[\x0D\x0A\x20!\x24#%'()+*,./0-9:;=?\x40A-Z_a-z-]];
75     #$xml_re{__non_PubidChar} = qr[[^\x0D\x0A\x20!\x24#%'()+*,./0-9:;=?\x40A-Z_a-z-]];
76 wakaba 1.4 #$xml_re{__PubidChar2} = qr[[\x0D\x0A\x20!\x24#%()+*,./0-9:;=?\x40A-Z_a-z-]];
77     #$xml_re{PublicLiteral} = qr/"(?:$xml_re{PubidChar})*"|'(?:$xml_re{__PubidChar2})*'/;
78 wakaba 1.1 # [14] CharData = *(Char - ("<" / "&")) - (*(Char - ("<" / "&")) "]]>" *(Char - ("<" / "&")))
79 wakaba 1.4 #$xml_re{CharData} = qr/(?:(?!\]\]>)[^<&])*/s;
80 wakaba 1.1 $xml_re{__CharDataP} = qr/(?:(?!\]\]>)[^<&])+/s;
81     # [15] Comment = "<!--" *((Char - "-") / ("-" (Char - "-")))) "-->"
82     $xml_re{Comment_M} = qr/<!--((?:(?!--).)*)-->/s;
83     # [16] PI = "<?" PITarget [S (*Char - (*Char "?>" *Char))] "?>"
84     $xml_re{PI_M} = qr/<\?($xml_re{Name})(?:$xml_re{s}((?:(?!\?>).)*))?\?>/s;
85 wakaba 1.4 $xml_re{_xml_PI_M} = qr/<\?xml(?:$xml_re{s}((?:(?!\?>).)*))?\?>/s;
86 wakaba 1.1 # [17] PITarget = Name - "xml"
87     # [18] CDSect = CDStart CData CDEnd
88     # [19] CDStart = '<![CDATA['
89     # [20] CDATA = (*Char - (*Char "]]>" *Char))
90     # [21] CDEnd = "]]>"
91     $xml_re{CDSect_M} = qr/<!\[CDATA\[((?:(?!\]\]>).)*)\]\]>/s;
92     # [22] prolog = [XMLDecl] *Misc [doctypedecl *Misc]
93     # [23] XMLDecl = '<?xml' VersionInfo [EncodingDecl] [SDDecl] [S] "?>"
94     # [24] VersionInfo = S 'version' Eq ("'" VersionNum "'" / <"> VersionNum <">)
95     # [25] Eq = [S] "=" [S]
96     # [26] VersionNum = 1*(ALPHA / DIGIT / "_" / "." / ":" / "-") ;; 1.0 FE & 1.0 SE
97     # [26] VersionNum = "1.0" ;; 1.0 SE-errata
98     # [26] VersionNum = "1.1" ;; 1.1
99     # [27] Misc = Comment / PI / S
100     # [28] doctypedecl = '<!DOCTYPE' S Name [S ExternalID] [S]
101     # ["[" *(markupdecl / PEReference / S) "]" [S]] ">" ;; 1.0 FE
102     # [28] doctypedecl = '<!DOCTYPE' S Name [S ExternalID] [S] ["[" *(markupdecl / DeclSep) "]" [S]] ">"
103     # ;; 1.0 FE-errata & 1.0 SE
104     # [28] doctypedecl = '<!DOCTYPE' S Name [S ExternalID] [S] ["[" intSubset "]" [S]] ">"
105     # ;; 1.0 SE-errata
106 wakaba 1.6 #$xml_re{__doctypedecl_start_simple_M} = qr/(<!DOCTYPE$xml_re{s})($xml_re{Name})($xml_re{s}SYSTEM$xml_re{s}$xml_re{__AttValue_simple}|$xml_re{s}PUBLIC$xml_re{s}$xml_re{__AttValue_simple}$xml_re{s}$xml_re{__AttValue_simple})?/s;
107 wakaba 1.1 # [28a] DeclSep = PEReference / S ;; 1.0 FE-errata & 1.0 SE
108     # [28b] intSubset = *(markupdecl / DeclSep) ;; 1.0 SE-errata
109     # [29] markupdecl = elementdecl / AttlistDecl / EntityDecl / NotationDecl / PI / Comment
110     # [30] extSubset = [TextDecl] extSubsetDecl
111     # [31] extSubsetDecl = *(markupdecl / conditionalSect / PEReference / S) ;; 1.0 FE
112     # [31] extSubsetDecl = *(markupdecl / conditionalSect / DeclSep) ;; 1.0 FE-errata & 1.0 SE
113     # [32] SDDecl = S 'standalone' Eq ("'" ('yes' / 'no') "'" / <"> ('yes' / 'no') <">)
114     # [33] LanguageID = Langcode *("-" Subcode) ;; 1.0 FE (removed by 1.0 SE)
115     # [34] Langcode = ISO639Cocde / IanaCode / UserCode ;; 1.0 FE (ditto)
116     # [35] ISO639Code = 2ALPHA ;; 1.0 FE (ditto)
117     # [36] IanaCode = "i-" 1*ALPHA ;; 1.0 FE (ditto)
118     # [37] UserCode = "x-" 1*ALPHA ;; 1.0 FE (ditto)
119     # [38] Subcode = 1*ALPHA ;; 1.0 FE (ditto)
120     # [39] element = EmptyElemTag / STag content ETag
121     # [40] STag = "<" Name *(S Attribute) [S] ">"
122     # [41] Attribute = Name Eq AttValue
123     $xml_re{Attribute} = qr/$xml_re{Name}(?:$xml_re{s})?=(?:$xml_re{s})?$xml_re{__AttValue_simple}/s;
124     $xml_re{Attribute_M} = qr/($xml_re{Name})(?:$xml_re{s})?=(?:$xml_re{s})?($xml_re{__AttValue_simple})/s;
125 wakaba 1.4 #$xml_re{STag} = qr/<$xml_re{Name}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?>/s;
126 wakaba 1.1 # [42] ETag = "</" Name [S] ">"
127     $xml_re{ETag_M} = qr!</($xml_re{Name})(?:$xml_re{s})?>!s;
128     # [43] content = *(element / CharData / Reference / CDSect / PI / Comment) ;; 1.0 FE
129     # [43] content = [CharData] *((element / Reference / CDSect / PI / Comment) [CharData])
130     # ;; 1.0 FE-errata & 1.0 SE
131     # [44] EmptyElemTag = "<" Name *(S Attribute) [S] "/>"
132 wakaba 1.4 #$xml_re{__STag_or_EmptyElemTag} = qr!<$xml_re{Name}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?/?>!s;
133 wakaba 1.1 $xml_re{__STag_or_EmptyElemTag_simple} = qr!<$xml_re{ame}(?:$xml_re{s}|$xml_re{Name}|$xml_re{__AttValue_simple}|=)*/?>!s;
134     # [45] elementdecl = '<!ELEMENT' S Name S contentspec [S] ">"
135     # [46] contentspec = 'EMPTY' / 'ANY' / Mixed / children
136 wakaba 1.4 #$xml_re{__contentspec_simple} = qr/(?:$xml_re{Name}|\#PCDATA|[()|,?*+]|$xml_re{s})/s;
137 wakaba 1.1 # [47] children = (choice / seq) ["?" / "*" / "+"]
138     # [48] cp = (Name / choice / seq) ["?" / "*" / "+"]
139     # [49] choice = "(" [S] cp *([S] "|" [S] cp) [S] ")" ;; 1.0 FE
140     # [49] choice = "(" [S] cp 1*([S] "|" [S] cp) [S] ")" ;; 1.0 FE-errata & 1.0 SE
141     # [50] seq = "(" [S] cp *([S] "," [S] cp) [S] ")"
142     # [51] Mixed = "(" [S] '#PCDATA' *([S] "|" [S] Name) [S] ")*"
143     # / "(" [S] '#PCDATA' [S] ")"
144     #$xml_re{seq} = qr/\($xml_re{cp}(?:(?:$xml_re{s})?,(?:$xml_re{s})?$xml_re{cp})*(?:$xml_re{s})?\)/;
145     #$xml_re{cp} = qr/(?:$xml_re{Name}|$xml_re{choice}|$xml_re{seq})[?*+]/;
146     #$xml_re{choice} = qr/\($xml_re{cp}(?:(?:$xml_re{s})?\|(?:$xml_re{s})?$xml_re{cp})+(?:$xml_re{s})?\)/;
147     #$xml_re{children} = qr/(?:$xml_re{choice}|$xml_re{seq})[?*+]/;
148     #$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})?\))/;
149     #$xml_re{contentspec} = qr/(?:EMPTY|ANY|$xml_re{Mixed}|$xml_re{children})/;
150     # [52] AttlistDecl = '<!ATTLIST' S Name *AttDef [S] ">"
151     # [53] AttDef = S Name S AttType S DefaultDecl
152     # [54] AttType = StringType / TokenizedType / EnumeratedType
153     # [55] StringType = 'CDATA'
154     # [56] TokenizedType = 'ID' / 'IDREF' / 'IDREFS' / 'ENTITY' / 'ENTITIES' / 'NMTOKEN' / 'NMTOKENS'
155     # [57] EnumeratedType = NotationType / Enumeration
156     # [58] NotationType = 'NOTATION' S "(" [S] Name *([S] "|" [S] Name) [S] ")"
157     # [59] Enumeration = "(" [S] Nmtoken *([S] "|" [S] Nmtoken) [S} ")"
158     # [60] DefaultDecl = '#REQUIRED' / '#IMPLIED' / ['#FIXED' S] AttValue
159     # [61] conditionalSect = includeSect / ignoreSect
160     # [62] includeSect = "<![" [S} 'INCLUDE' [S] "[" extSubsetDecl "]]>"
161     # [63] ignoreSect = "<![" [S] 'IGNORE' [S] "[" *ignoreSectContents "]]>"
162     # [64] ignoreSectContents = Ignore *("<![" ignoreSectContents "]]>" Ignore)
163     # [65] Ignore = *Char - (*Char ("<![" / "]]>") *Char)
164     # [66] CharRef = '&#' 1*DIGIT ";" / '&#x' 1*HEXDIGIT ";"
165     $xml_re{CharRef} = qr/&#[0-9]+;|&#x[0-9A-Fa-f]+;/;
166     # [67] Reference = EntityRef / CharRef
167     # [68] EntityRef = "&" Name ";"
168     $xml_re{EntityRef} = qr/&$xml_re{Name};/;
169     $xml_re{EntityRef_M} = qr/&($xml_re{Name});/;
170     $xml_re{Reference} = qr/$xml_re{EntityRef}|$xml_re{CharRef}/;
171 wakaba 1.4 #$xml_re{AttValue} = qr/"(?:$xml_re{Reference}|[^&<"])*"|'(?:$xml_re{Reference}|[^&<'])*'/s;
172 wakaba 1.1 # [69] PEReference = "%" Name ";"
173     $xml_re{PEReference} = qr/%(?:$xml_re{Name});/;
174     $xml_re{PEReference_M} = qr/%($xml_re{Name});/;
175     $xml_re{__elementdecl_simple} = qr/<!ELEMENT(?:$xml_re{s}|$xml_re{PEReference}|$xml_re{Name}|\#PCDATA|[()|,?*+])+>/s;
176 wakaba 1.5 $xml_re{__AttlistDecl_simple} = qr/<!ATTLIST(?:$xml_re{PEReference}|$xml_re{Name}|[#()|]|$xml_re{s}|$xml_re{__AttValue_simple})*>/s;
177 wakaba 1.1 # [70] EntityDecl = GEDecl / PEDecl
178 wakaba 1.6 #$xml_re{__EntityDecl_simple} = qr/<!ENTITY(?:$xml_re{__AttValue_simple}|[^"'>])*>/s;
179 wakaba 1.1 # [71] GEDecl = '<!ENTITY' S Name S EntityDef [S] ">"
180     # [72] PEDecl = '<!ENTITY' S "%" S Name S PEDef [S] ">"
181     # [73] EntityDef = EntityValue / ExternalID [NDataDecl]
182     # [74] PEDef = EntityValue / ExternalID
183     # [75] ExternalID = 'SYSTEM' S SystemLiteral / 'PUBLIC' S PublicLiteral S SystemLiteral
184     # [76] NDataDecl = S 'NDATA' S Name
185     # [77] TextDecl = '?xml' [VersionInfo] EncodingDecl [S] "?>"
186     # [78] extParsedEnt = [TextDecl] content
187     # [79] extPE ;; 1.0 FE (removed by errata)
188     # [80] EncodingDecl = S 'encoding' Eq (<"> EncName <"> / "'" EncName "'")
189     # [81] EncName = ALPHA *(ALPHA / DIGIT / "." / "_" / "-")
190     # [82] NotationDecl = '<!NOTATION' S Name S (ExternalID / PublicID) [S] ">"
191 wakaba 1.6 #$xml_re{__NotationDecl_simple} = qr/<!NOTATION(?:$xml_re{__AttValue_simple}|[^"'>])*>/s;
192 wakaba 1.1 # [83] PublicID = 'PUBLIC' S PubidLiteral
193     # [84] Letter = BaseChar / Ideographic
194     # [85] Basechar = ...
195     # [86] Ideographic = ...
196     # [87] CombiningChar = ...
197     # [88] Digit = ...
198     # [89] Extender = U+00B7 / U+02D0 / U+02D1 / U+0387 / U+0640 / U+0E46 / U+0EC6
199     # / U+3005 / U+3031-U+3035 / U+309D / U+309E / U+30FC-U+30FE
200     ## [84]-[89] removed by 1.1
201    
202     # [XMLNames]
203     # [1] NSAttName = PrefixedAttName / DefaultAttName
204     # [2] PrefixedAttName = 'xmlns:' NCName
205     # [3] DefaultAttName = 'xmlns'
206     # [4] NCName = (Letter / "_") *NCNameChar ;; 1.0
207     # [4] NCName = NCNameStartChar *NCNameChar ;; 1.1
208     # [5] NCNameChar = Letter / Digit / "." / "-" / "_" / CombiningChar / Extender ;; 1.0
209     # [5] NCNameChar = NameChar - ":" ;; 1.1
210     # [5a] NCNameStartChar = NameStartChar - ":" ;; 1.1
211     # [6] QName = [Prefix ":"] LocalPart ;; 1.0
212     # [6] QName = PrefixedName / UnprefixedName ;; 1.1
213     # [6a] PrefixedName = Prefix ":" LocalPart ;; 1.1
214     # [6b] UnprefixedName = LocalPart ;; 1.1
215     # [7] Prefix = NCName
216     # [8] LocalPart = NCName
217     # [9] STag = "<" QName *(S Attribute) [S] ">"
218 wakaba 1.4 #$xml_re{__NCSTag} = qr/<$xml_re{QName}(?:$xml_re{s}$xml_re{Attribute})*(?:$xml_re{s})?>/s;
219 wakaba 1.1 # [10] ETag = "</" QName [S] ">"
220 wakaba 1.4 #$xml_re{__NCETag} = qr!</$xml_re{QName}(?:$xml_re{s})?>!s;
221 wakaba 1.1 # [11] EmptyElemTag = "<" QName *(S Attribute) [S] "/>"
222     # [12] Attribute = NSAttName Eq AttValue / QName Eq AttValue
223     # [13] doctypedecl = '<!DOCTYPE' S QName [S ExternalID] [S]
224     # ["[" *(markupdecl / PEReference / S) "]" [S]] ">"
225     # [14] elementdecl = '<!ELEMENT' S QName S contentspec [S] ">"
226     # [15] cp = (QName / choice / sep) ["?" / "*" / "+"]
227     # [16] Mixed = "(" [S] '#PCDATA' *([S] "|" [S] QName) [S] ")*" / "(" [S] '#PCDATA' [S] ")"
228     # [17] AttlistDecl = '<!ATTLIST' S QName *AttDef [S] ">"
229     # [18] AttDef = S (QName / NSAttName) S AttType S DefaultDecl
230     # [19] Name = NameStartChar *NameChar ;; 1.1 draft
231     # [20] NameChar = {XML1.1}.NameChar ;; 1.1 draft
232     # [21] NameStartChar = {XML1.1}.NameStartChar ;; 1.1 draft
233    
234    
235 wakaba 1.4 sub new ($;%) {
236 wakaba 1.7 my $class = shift;
237     my $self = bless {@_}, $class;
238 wakaba 1.4 $self;
239 wakaba 1.1 }
240    
241 wakaba 1.7 sub parse_text ($$;$%) {
242     my ($self, $s, $o, %opt) = @_;
243 wakaba 1.6 $o ||= {line => 0, pos => 0, entity_type => 'document_entity',
244 wakaba 1.7 uri => $self->{option}->{document_entity_uri}};
245 wakaba 1.1 my $r = SuikaWiki::Markup::XML->new (type => '#document');
246 wakaba 1.7 $r->base_uri ($self->{option}->{document_entity_base_uri})
247     if defined $self->{option}->{document_entity_base_uri};
248     unless ($opt{entMan}) {
249     $opt{entMan} = $r->_get_entity_manager;
250     $opt{entMan}->option (uri_resolver => $self->{option}->{uri_resolver});
251     $opt{entMan}->option (error_handler => $self->{option}->{error_handler});
252     } else {
253     $opt{entMan}->set_root_node ($r);
254     }
255     $r->flag (smxp__entity_manager => $opt{entMan});
256 wakaba 1.6
257     ## Line-break normalization
258     $s =~ s/\x0D\x0A/\x0A/g;
259     $s =~ tr/\x0D/\x0A/;
260    
261 wakaba 1.2 ## NOTE: Even if there are more than one non-XML-Char, error is raised only one time.
262     $self->_warn_char_val ($self->_make_clone_of ($o), $s);
263 wakaba 1.6 if ($s =~ s/^($xml_re{_xml_PI_M})//s) { # <?xml?>
264     my ($data, $all) = ($2, $1);
265     $self->_clp (_____ => $o);
266     if (length ($data)) {
267     $self->_clp (_ => $o); # <?xml* *ver...?>
268     $self->_parse_xml_declaration ($r->append_new_node (type => '#pi',
269     local_name => 'xml'), $data, $o);
270     } else {
271     $self->_raise_error ($o, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
272     $r->append_new_node (type => '#pi', local_name => 'xml')
273     ->set_attribute (version => '1.0');
274     }
275     $self->_clp (__ => $o);
276 wakaba 1.1 }
277 wakaba 1.7 $self->_parse_document_entity ($r, \$s, $o, entMan => $opt{entMan});
278 wakaba 1.5 wantarray ? ($r, $o) : $r;
279     }
280    
281     sub _parse_document_entity ($$\$$;%) {
282     my ($self, $c, $s, $o, %opt) = @_;
283     $o->{entity_type} = 'document_entity';
284     my %occur;
285 w 1.9 my $entMan = $opt{entMan};
286 wakaba 1.5 while ($$s) {
287 wakaba 1.6 if ($$s =~ /^<$xml_re{Name}/) { # <element/>
288 wakaba 1.5 $self->_raise_error ($o, c => $c, type => 'WARN_DOCTYPE_NOT_FOUND') unless $occur{doctype};
289     $self->_validate_notation_declared ($c) if $occur{pi} && $occur{doctype};
290 wakaba 1.6 $self->_parse_element_content ($c, $s, $o, entMan => $entMan);
291 wakaba 1.5 $occur{element} = 1; $occur{pi} = 0;
292     } elsif ($$s =~ s/^($xml_re{s})//s) { # s
293     $c->append_text ($1);
294     $self->_clp ($1 => $o);
295     } elsif ($$s =~ s/^<!DOCTYPE//) {
296     my $D = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'doctype');
297 wakaba 1.6 $entMan->set_doctype_node ($D);
298 wakaba 1.5 $self->_clp (_________ => $o);
299 wakaba 1.6 if ($$s =~ s/^($xml_re{s}($xml_re{Name}))//s) {
300     $D->set_attribute (qname => $2);
301     $occur{doctype} = $2;
302     $self->_clp ($1 => $o);
303 wakaba 1.2 } else {
304 wakaba 1.5 $occur{doctype} = 1;
305     $self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_NAME_NOT_FOUND', c => $D);
306 wakaba 1.1 }
307 wakaba 1.5 my $have_sysid = 0;
308     if ($$s =~ s/^($xml_re{s}PUBLIC)//s) {
309     $self->_clp ($1 => $o);
310     if ($$s =~ s/^($xml_re{s})($xml_re{__AttValue_simple})//s) {
311     $self->_clp ($1 => $o);
312     my $pid = $2; $pid = substr ($pid, 1, length ($pid) - 2);
313     $D->set_attribute (PUBLIC => $entMan->check_public_id ($o, $pid));
314     $self->_clp ($2 => $o);
315     $have_sysid = 1;
316     } else {
317     $self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_PID_LITERAL_NOT_FOUND', c => $D);
318     }
319     } elsif ($$s =~ s/^($xml_re{s}SYSTEM)//s) {
320     $self->_clp ($1 => $o);
321     $have_sysid = 2;
322     }
323     if ($have_sysid) {
324     if ($$s =~ s/^($xml_re{s})($xml_re{__AttValue_simple})//s) {
325     $self->_clp ($1 => $o);
326     $entMan ||= $c->_get_entity_manager;
327     my $sid = $2; $sid = substr ($sid, 1, length ($sid) - 2);
328     $D->set_attribute (SYSTEM => $entMan->check_system_id ($o, $sid));
329     $self->_clp ($2 => $o);
330     } elsif ($have_sysid) {
331     $self->_raise_error ($o, type => 'SYNTAX_DOCTYPE_SYSID_LITERAL_NOT_FOUND', c => $D);
332     $D->set_attribute (SYSTEM => $NS{internal_invalid_sysid}) if $have_sysid == 1;
333     undef $have_sysid if $have_sysid == 2;
334     }
335     }
336     ## dso for the internal subset
337 w 1.8 if ($$s =~ s/^((?:$xml_re{s})?\[)//s) {
338 wakaba 1.5 $self->_clp ($1 => $o);
339 w 1.8 $self->_parse_dtd ($D, $s, $o, return_with_dsc => 1, validate_notation_declared => 1,
340     entMan => $entMan);
341 wakaba 1.5 ## dsc and mdo is processed by _parse_dtd
342     } elsif ($$s =~ s/^((?:$xml_re{s})?>)//s) {
343     $self->_clp ($1 => $o);
344 wakaba 1.1 } else {
345 wakaba 1.5 $self->_raise_error ($o, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', c => $D, t => $D);
346     }
347     ## Read and parse the external subset
348     if ($have_sysid) {
349     my $xsub = $D->set_attribute ('external-subset');
350     my $o2 = $self->_make_clone_of ($o);
351     $o2->{entity_type} = 'dtd_external_subset';
352     $o2->{line} = 0; $o2->{pos} = 0;
353     my $ext_ent = $entMan->get_external_entity ($self, $D, $o2);
354     if ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
355     $self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $D,
356     t => ['<!DOCTYPE>', $o2->{uri}, $ext_ent->{error}->{reason_text}]);
357     } else { ## parsed entity
358     $xsub->base_uri ($ext_ent->{base_uri});
359     my $ev = $ext_ent->{text};
360 w 1.8 $self->_parse_dtd ($xsub, \$ev, $o2, entMan => $entMan);
361 wakaba 1.5 $xsub->flag (smxp__ref_expanded => 1);
362     }
363 wakaba 1.1 }
364 wakaba 1.5 } elsif ($$s =~ s/^$xml_re{PI_M}//s) { # <?pi?> ## TODO: pi parsing
365     ## PI before DOCTYPE declaration
366 wakaba 1.2 my ($target, $data, $all) = ($1, $2, $&);
367     if ($target eq 'xml') {
368 wakaba 1.4 $self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_POSITION');
369 wakaba 1.1 } else {
370 wakaba 1.5 $c->append_new_node (type => '#pi', local_name => $target, value => $data)
371     ->flag (smxp__src_pos => $self->_make_clone_of ($o));
372     $self->_clp ($all => $o);
373 wakaba 1.1 }
374 wakaba 1.5 $occur{pi} = 1;
375     } elsif ($$s =~ s/^$xml_re{Comment_M}//s) { # <!-- --> ## TODO: parse comment
376 wakaba 1.2 $c->append_new_node (type => '#comment', value => $1);
377 wakaba 1.5 $self->_clp ($& => $o);
378     $occur{comment} = 1;
379 wakaba 1.1 } else {
380 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
381 wakaba 1.5 substr ($$s, 0, 1) = '';
382 wakaba 1.1 }
383 wakaba 1.5 } # $$s
384    
385     unless ($occur{element}) {
386     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ROOT_ELEMENT_NOT_FOUND',
387     t => $occur{doctype} eq '1' ? '#IMPLIED' : $occur{doctype});
388     $self->_raise_error ($o, c => $c, type => 'WARN_DOCTYPE_NOT_FOUND') unless $occur{doctype};
389     $self->_validate_notation_declared ($c) if $occur{pi} && $occur{doctype};
390 wakaba 1.1 }
391 wakaba 1.5 if ($occur{element} && $occur{doctype}) {
392     my $root;
393     for (@{$c->child_nodes}) {
394     if ($_->node_type eq '#element') {
395     $root = $_->qname;
396     last;
397 wakaba 1.2 }
398 wakaba 1.1 }
399 wakaba 1.5 unless ($root eq $occur{doctype}) {
400     $self->_raise_error ($o, type => 'VC_ROOT_ELEMENT_TYPE', c => $c,
401     t => [$occur{doctype}, $root]);
402     }
403 wakaba 1.1 }
404     }
405    
406 wakaba 1.5 sub _parse_element_content ($$\$$;%) {
407     my ($self, $c, $s, $o, %opt) = @_;
408 wakaba 1.2 my $c_initial = overload::StrVal ($c);
409     while ($$s) {
410 wakaba 1.6 if ($$s =~ m:^<[^!?/]:) {
411     if ($c->node_type eq '#document' && $self->_is_brother_of_root_element ($c)) {
412     $self->_raise_error ($o, c => $c, type => 'SYNTAX_DATA_OUT_OF_ROOT_ELEMENT',
413     t => substr ($$s, 0, 10));
414 wakaba 1.2 }
415 w 1.9 $c = $self->_parse_start_tag ($c, $s, $o, entMan => $opt{entMan});
416 wakaba 1.2 } elsif ($$s =~ s/^$xml_re{ETag_M}//s) {
417     my $ename = $1;
418 wakaba 1.6 if ($ename eq $c->flag ('smxp__original_qname') || $ename eq $c->qname) {
419 wakaba 1.2 $c = $c->{parent};
420     } else { ## Element type name does not match
421     my $o_etn = $self->_make_clone_of ($o);
422     $o_etn->{pos} += 2;
423 wakaba 1.6 $self->_raise_error ($o_etn, c => $c, type => 'WFC_ELEMENT_TYPE_MATCH',
424     t => [$ename, $c->qname]);
425 wakaba 1.2 }
426     _count_lp ($&, $o);
427 wakaba 1.6 } elsif (($c->node_type eq '#document') && ($$s =~ s/^($xml_re{s})//s)) {
428     $c->append_text ($1);
429     $self->_clp ($1 => $o);
430     } elsif ($$s =~ s/^($xml_re{__CharDataP})//s) {
431     $self->_raise_error ($o, c => $c, type => 'SYNTAX_DATA_OUT_OF_ROOT_ELEMENT', t => $1)
432     if $c->node_type eq '#document';
433     $c->append_text ($1);
434     $self->_clp ($1 => $o);
435 wakaba 1.2 } elsif ($$s =~ s/^($xml_re{Reference})//s) { ## &foo; | &#1234; | &#x12AB;
436     my $entity_ref = $1;
437 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_DATA_OUT_OF_ROOT_ELEMENT',
438     t => $entity_ref) if $c->node_type eq '#document';
439 wakaba 1.2 my $eref = $self->_parse_reference ($c, $entity_ref, $o);
440 wakaba 1.6 unless (index ($eref->{namespace_uri}, 'char') > -1) { ## General entity reference
441 w 1.9 my $entity = $opt{entMan}->get_entity ($eref);
442 wakaba 1.2 if (!$entity && {qw/&lt; 1 &gt; 1 &amp; 1 &quot; 1 &apos; 1/}->{$entity_ref}) {
443     $self->_raise_error ($o, c => $c, type => 'WARN_PREDEFINED_ENTITY_NOT_DECLARED',
444     t => $entity_ref);
445 w 1.9 $entity = $opt{entMan}->get_entity ($eref);
446 wakaba 1.2 }
447     if (!$entity) {
448 wakaba 1.6 $self->_raise_error ($o, t => $entity_ref,
449 w 1.9 type => ($opt{entMan}->is_standalone_document_1?'WF':'V')
450     .'C_ENTITY_DECLARED');
451 wakaba 1.2 } else {
452 w 1.9 if ($entity->flag ('smxp__declaration_may_not_be_read')) {
453 wakaba 1.5 $self->_raise_error ($o, type => 'WARN_EXTERNALLY_DEFINED_ENTITY_REFERRED',
454     t => $entity_ref);
455     }
456 wakaba 1.2 my $o2 = $self->_make_clone_of ($o);
457     if ($o2->{__entities}->{$entity_ref}) {
458     $self->_raise_error ($o, type => 'WFC_NO_RECURSION', t => $entity_ref);
459     } else {
460 wakaba 1.4 $o2->{entity} = $entity_ref;
461 wakaba 1.2 my $entity_value = $entity->get_attribute ('value');
462     if (ref $entity_value) {
463     $o2->{__entities}->{$entity_ref} = 1;
464 wakaba 1.5 $o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
465 wakaba 1.4 $o2->{line} = 0; $o2->{pos} = 0;
466 wakaba 1.2 my $ev = $entity_value->_entity_parameter_literal_value;
467 w 1.9 $self->_parse_element_content ($eref, \$ev, $o2, entMan => $opt{entMan});
468 wakaba 1.2 $eref->flag (smxp__ref_expanded => 1);
469 wakaba 1.4 } else { ## External entity
470     $o2->{entity_type} = 'external_general_parsed_entity';
471 w 1.9 my $ext_ent = $opt{entMan}->get_external_entity ($self, $entity, $o2);
472 wakaba 1.4 if ($ext_ent->{NDATA}) { ## non-parsed entity
473 wakaba 1.6 $self->_raise_error ($o, type => 'WFC_PARSED_ENTITY',
474     c => $entity, t => $entity_ref);
475 wakaba 1.4 } elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
476     $self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
477     t => [$entity_ref, $o2->{uri},
478     $ext_ent->{error}->{reason_text}]);
479     } else { ## parsed entity
480     $o2->{__entities}->{$entity_ref} = 1;
481     $eref->base_uri ($ext_ent->{base_uri});
482     my $ev = $ext_ent->{text};
483 w 1.9 $self->_parse_element_content ($eref, \$ev, $o2, entMan => $opt{entMan});
484 wakaba 1.4 $eref->flag (smxp__ref_expanded => 1);
485     }
486 wakaba 1.2 }
487     }
488     }
489     } # if &foo;
490 wakaba 1.6 } elsif ($$s =~ s/^($xml_re{CDSect_M})//s) {
491     $self->_raise_error ($o, c => $c, type => 'SYNTAX_DATA_OUT_OF_ROOT_ELEMENT', t => '<![')
492     if $c->node_type eq '#document';
493     $c->append_new_node (type => '#section', value => $2)
494 wakaba 1.5 ->set_attribute (status => 'CDATA');
495 wakaba 1.6 $self->_clp ($1 => $o);
496     } elsif ($$s =~ s/^$xml_re{Comment_M}//s) { ## TODO:
497 wakaba 1.2 $c->append_new_node (type => '#comment', value => $1);
498     _count_lp ($&, $o);
499 wakaba 1.6 } elsif ($$s =~ s/^($xml_re{PI_M})//s) { ## TODO: warn unless declared
500     my ($target, $data, $all) = ($2, $3, $1);
501 wakaba 1.2 if ($target eq 'xml') {
502 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_POSITION');
503 wakaba 1.2 } else {
504 wakaba 1.6 $c->append_new_node (type => '#pi', local_name => $target, value => $data);
505     $self->_clp ($all => $o);
506 wakaba 1.2 }
507     } else {
508 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
509 wakaba 1.2 substr ($$s, 0, 1) = '';
510     }
511     } # while $s
512     while ($c_initial ne overload::StrVal ($c)) {
513     if (ref $c->{parent}) {
514     if ($c->node_type eq '#element') {
515     $self->_raise_error ($o, type => 'SYNTAX_END_TAG_NOT_FOUND', t => $c);
516     }
517     $c = $c->{parent};
518     } else {
519     last;
520     }
521     }
522     }
523    
524 wakaba 1.6 sub _parse_start_tag ($$\$$;%) {
525     my ($self, $c, $s, $o, %opt) = @_;
526 w 1.9 my ($type_pfx, $type_lname, $type_qname);
527 wakaba 1.6 ## Element type name (general identifier)
528     if ($$s =~ s/^<($xml_re{Name})//) {
529 w 1.9 $type_qname = $1;
530 wakaba 1.6 if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
531     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
532     $type_qname =~ tr/:/_/;
533     }
534     $self->_clp ('<'.$type_qname => $o);
535     ($type_pfx, $type_lname) = $self->_ns_parse_qname ($type_qname);
536     if ($type_pfx && $type_lname !~ /^\p{InXML_NameStartChar}/) {
537     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_LNAME_IS_NCNNAME', t => $type_lname);
538     $type_lname = '_' . $type_lname;
539     }
540     $c = $c->append_new_node (type => '#element', local_name => $type_lname);
541     $c->flag (smxp__original_qname => $type_qname);
542     } else {
543     $self->_clp (_ => $o);
544     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
545     $$s =~ s/^<//;
546     return $c;
547     }
548    
549 w 1.9 my $defattr = $opt{entMan}->get_attr_definitions (qname => $type_qname);
550 wakaba 1.6 ## Attribute spec list
551     my @attr;
552     my %defined_attr;
553     while ($$s) {
554     if ($$s =~ s/^($xml_re{s})($xml_re{Name})//s) {
555     my $attr_qname = $2;
556     my ($attr_pfx, $attr_lname);
557     my $ignore = 0;
558     ## Isn't already defined? Is valid QName?
559     if ($defined_attr{$attr_qname}) {
560     $self->_raise_error ($o, c => $c, type => 'WFC_UNIQUE_ATT_SPEC', t => $attr_qname);
561     $ignore = 1;
562     } elsif (substr ($attr_qname, 0, 1) eq ':' || substr ($attr_qname, -1, 1) eq ':') {
563     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $attr_qname);
564     $defined_attr{$attr_qname} = 1;
565     $attr_qname =~ tr/:/_/; $defined_attr{$attr_qname} = 1;
566     ($attr_pfx, $attr_lname) = (undef, $attr_qname);
567     } else {
568     $defined_attr{$attr_qname} = 1;
569     ($attr_pfx, $attr_lname) = $self->_ns_parse_qname ($attr_qname);
570     }
571     $self->_clp ($1.$attr_qname => $o);
572    
573     if ($attr_pfx && $attr_lname !~ /^\p{InXML_NameStartChar}/) {
574     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_LNAME_IS_NCNNAME', t => $attr_lname);
575     $attr_lname = '_' . $attr_lname;
576     }
577     my $attr_node = ref ($c)->new (type => '#attribute', local_name => $attr_lname);
578    
579     if ($$s =~ s/^($xml_re{s})//s) {
580     $self->_clp ($1);
581     }
582     if ($$s =~ s/^=//) {
583     $self->_clp (_ => $o);
584     if ($$s =~ s/^($xml_re{s})//s) {
585     $self->_clp ($1);
586     }
587    
588     if ($$s =~ s/^($xml_re{__AttValue_simple})//s) {
589     next if $ignore;
590     my $pcdata = substr ($1, 1, length ($1) - 2);
591     $self->_clp (_ => $o);
592 w 1.8 $self->_parse_attr_value_literal_data ($attr_node, \$pcdata, $o, entMan => $opt{entMan});
593 wakaba 1.6 $self->_clp (_ => $o);
594    
595     if ($attr_pfx eq 'xmlns') {
596     my $ns_name = $attr_node->inner_text;
597     $opt{entMan}->check_ns_uri ($o, $attr_lname => $ns_name) if length $ns_name;
598     $c->define_new_namespace ($attr_lname => $ns_name);
599     } elsif (!$attr_pfx && $attr_lname eq 'xmlns') {
600     my $ns_name = $attr_node->inner_text;
601     if (length ($ns_name) || lc (substr ($ns_name, 0, 3)) eq 'xml') {
602     $opt{entMan}->check_ns_uri ($o, '' => $ns_name);
603     } else {
604     $self->_raise_error ($o, type => 'WARN_XML_NS_URI_IS_RELATIVE', t => $attr_qname);
605     }
606     $c->define_new_namespace ('' => $ns_name);
607     } else {
608     push @attr, [$attr_pfx => $attr_lname, $attr_node];
609     }
610     } else {
611     $self->_raise_error ($o, type => 'SYNTAX_ATTR_LITERAL_NOT_FOUND', t => $attr_qname);
612     $attr_node->append_text ($attr_qname);
613     }
614     } else {
615     $self->_raise_error ($o, type => 'SYNTAX_ATTR_NAME_OMITTED', t => $attr_qname);
616     $attr_node->append_text ($attr_qname);
617     }
618     } elsif ($$s =~ s!^($xml_re{s})?(/)?>!!s) {
619     $self->_clp ($1.$2.'>');
620     $c->option (use_EmptyElemTag => 1) if $2;
621     last;
622     } elsif ($$s =~ m/^</) {
623     $self->_raise_error ($o, type => 'SYNTAX_TAG_NOT_CLOSED', t => substr ($$s, 0, 10));
624     last;
625     } else {
626     $self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
627     substr ($$s, 0, 1) = '';
628     }
629     } ## while
630    
631 w 1.9 ## Default attributes
632     for (keys %{$defattr->{attr}}) {
633     unless ($defined_attr{$_}) {
634     my $defval = $defattr->{attr}->{$_}->get_attribute ('default_value');
635     if ($defval) {
636     my ($attr_pfx, $attr_lname) = $self->_ns_parse_qname ($_);
637     ## TODO: keep default-declared namespace information
638     if ($attr_pfx eq 'xmlns') {
639     my $ns_name = $defval->inner_text;
640     $opt{entMan}->check_ns_uri ($o, $attr_lname => $ns_name) if length $ns_name;
641     if ($defattr->{attr_may_not_be_read}->{$_}
642     && $c->defined_namespace_prefix ($attr_lname) ne $ns_name) {
643     $self->_raise_error ($o, type => 'WARN_XMLNAMES_EXTERNAL_NS_ATTR',
644     t => [$attr_lname => $ns_name]);
645     }
646     $c->define_new_namespace ($attr_lname => $ns_name);
647     } elsif (!$attr_pfx && $attr_lname eq 'xmlns') {
648     my $ns_name = $defval->inner_text;
649     if (length ($ns_name) || lc (substr ($ns_name, 0, 3)) eq 'xml') {
650     $opt{entMan}->check_ns_uri ($o, '' => $ns_name);
651     } else {
652     $self->_raise_error ($o, type => 'WARN_XML_NS_URI_IS_RELATIVE', t => $_);
653     }
654     if ($defattr->{attr_may_not_be_read}->{$_}
655     && $c->defined_namespace_prefix ('') ne $ns_name) {
656     $self->_raise_error ($o, type => 'WARN_XMLNAMES_EXTERNAL_NS_ATTR',
657     t => ['#default' => $ns_name]);
658     }
659     $c->define_new_namespace ('' => $ns_name);
660     } else {
661     my $attr_node = ref ($c)->new (type => '#attribute', local_name => $attr_lname,
662     value => $defval->inner_text);
663     $attr_node->flag (smxp__is_dtd_default => 1);
664     if ($defattr->{attr_may_not_be_read}->{$_}) {
665     $self->_raise_error ($o, type => 'WARN_EXTERNAL_DEFAULT_ATTR', c => $c,
666     t => $_);
667     }
668     push @attr, [$attr_pfx => $attr_lname, $attr_node];
669     }
670     } # has default value
671     } # unspecified attr
672     }
673    
674 wakaba 1.6 ## Namespace of element type name
675     {
676     my $uri = $c->defined_namespace_prefix ($type_pfx || '');
677     if (defined $uri) {
678     $c->namespace_uri ($uri);
679     } elsif (!$type_pfx) { ## Default
680     ## <foo xmlns="">
681     $c->define_new_namespace ('' => '');
682     } else {
683     $self->_raise_error ($o, c => $c, type => 'NC_PREFIX_NOT_DEFINED', t => $type_pfx);
684     $c->namespace_uri ($NS{internal_ns_invalid}.$self->_uri_escape ($type_pfx));
685     }
686     }
687 w 1.9
688 wakaba 1.6 ## Namespace of attribute name
689     my %ns_attr_defined;
690     for (@attr) {
691     unless ($_->[0]) { ## No prefix
692     $c->append_node ($_->[2]);
693     } else {
694     my $uri = $c->defined_namespace_prefix ($_->[0]);
695     if (defined $uri) {
696     if ($ns_attr_defined{$uri}->{$_->[1]}) {
697     $self->_raise_error ($o, c => $_->[2], type => 'NC_UNIQUE_ATT_SPEC',
698     t => [$_->[0].':'.$_->[1], $uri, $_->[1]]);
699     } else {
700     $_->[2]->namespace_uri ($uri);
701     $ns_attr_defined{$uri}->{$_->[1]} = 1;
702     $c->append_node ($_->[2]);
703     }
704     } else {
705     $self->_raise_error ($o, c => $_->[2], type => 'NC_PREFIX_NOT_DEFINED', t => $_->[0]);
706     $_->[1]->namespace_uri ($NS{internal_ns_invalid}.$self->_uri_escape ($_->[0]));
707     }
708     } # have prefix
709     }
710     $c->option ('use_EmptyElemTag') ? $c->parent_node : $c;
711     }
712    
713 wakaba 1.2 sub _parse_dtd ($$\$$;%) {
714 wakaba 1.6 my ($self, $c, $s, $o, %opt) = @_;
715 wakaba 1.2 my $c_initial = overload::StrVal ($c);
716     while ($$s) {
717 wakaba 1.4 if ($$s =~ s/^($xml_re{PEReference_M})//s) {
718     my ($ref, $ename) = ($1, $2);
719 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ref)
720     if index ($ename, ':') > -1;
721 w 1.8 my $entity = $opt{entMan}->get_entity ($ename, namespace_uri => $NS{SGML}.'entity:parameter');
722 wakaba 1.2 my $eref = $c->append_new_node (type => '#reference', local_name => $ename,
723     namespace_uri => $NS{SGML}.'entity:parameter');
724 wakaba 1.6 if (!$entity) {
725 wakaba 1.4 $self->_raise_error ($o, c => $c, type => 'VC_ENTITY_DECLARED', t => $ref);
726 wakaba 1.2 } else {
727     my $o2 = $self->_make_clone_of ($o);
728 wakaba 1.4 if ($o2->{__entities}->{$ref}) {
729     $self->_raise_error ($o, c => $c, type => 'WFC_NO_RECURSION', t => $ref);
730 wakaba 1.2 } else {
731 wakaba 1.4 $o2->{entity} = $ref;
732 wakaba 1.2 my $entity_value = $entity->get_attribute ('value');
733 wakaba 1.4 if (ref $entity_value) { ## Internal entity
734     $o2->{__entities}->{$ref} = 1;
735 wakaba 1.5 $o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
736 wakaba 1.4 $o2->{line} = 0; $o2->{pos} = 0;
737     my $ev = $entity_value->_entity_parameter_literal_value;
738 w 1.8 $self->_parse_dtd ($eref, \$ev, $o2, entMan => $opt{entMan});
739 wakaba 1.2 $eref->flag (smxp__ref_expanded => 1);
740 wakaba 1.4 } else { ## External entity
741     $o2->{entity_type} = 'external_parameter_entity';
742 w 1.9 $c->root_node->flag (smxp__declaration_may_not_be_read => 1)
743     if !(index ($o->{entity_type}, 'external') > -1)
744     || !$opt{entMan}->is_standalone_document;
745 w 1.8 my $ext_ent = $opt{entMan}->get_external_entity ($self, $entity, $o2);
746 wakaba 1.4 if ($ext_ent->{NDATA}) { ## non-parsed entity
747     $self->_raise_error ($o, type => 'WFC_PARSED_ENTITY', c => $entity, t => $ref);
748     } elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
749     $self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
750     t => [$ref, $o2->{uri}, $ext_ent->{error}->{reason_text}]);
751 wakaba 1.6 ## Don't process ENTITY/ATTLIST declaration any more
752 w 1.8 $c->root_node->flag (smxp__stop_read_dtd => 1)
753     unless $opt{entMan}->is_standalone_document;
754 wakaba 1.4 } else { ## parsed entity
755     $o2->{__entities}->{$ref} = 1;
756     $eref->base_uri ($ext_ent->{base_uri});
757     my $ev = $ext_ent->{text};
758 w 1.8 $self->_parse_dtd ($eref, \$ev, $o2, entMan => $opt{entMan});
759 wakaba 1.4 $eref->flag (smxp__ref_expanded => 1);
760 wakaba 1.6 } # external parsed entity
761     } # external entity
762 wakaba 1.2 } # not recursive
763     } # entity defined
764 wakaba 1.6 $self->_clp ($ref => $o);
765 wakaba 1.5 } elsif ($$s =~ s/^($xml_re{s})//s) {
766     $c->append_text ($1);
767     $self->_clp ($1 => $o);
768 wakaba 1.6 } elsif ($$s =~ s/^($xml_re{Comment_M})//s) { ## TODO: reimple
769     $c->append_new_node (type => '#comment', value => $2);
770     $self->_clp ($1 => $o);
771 wakaba 1.5 } elsif ($$s =~ m/^<!(?:ENTITY|NOTATION)(?:$xml_re{s}|%)/s) {
772 w 1.8 $self->_parse_entity_declaration ($s, $c, $o, entMan => $opt{entMan});
773     } elsif ($$s =~ m/^<!ELEMENT(?:$xml_re{s}|%)/s) {
774     $self->_parse_element_declaration ($s, $c, $o, entMan => $opt{entMan});
775     } elsif ($$s =~ m/^<!ATTLIST(?:$xml_re{s}|%)/s) {
776     $self->_parse_attlist_declaration ($s, $c, $o, entMan => $opt{entMan});
777 wakaba 1.5 ## Markup section start (= mdo + mso)
778     } elsif ($$s =~ s/^<!\[//) {
779 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_MS_IN_INTERNAL_SUBSET')
780     if $o->{entity_type} eq 'document_entity';
781 wakaba 1.5 $self->_clp (___ => $o);
782     ## Status keyword list
783     if ($$s =~ s/^([^[]+)\[//s) {
784     my $skl = $1;
785     my $ms = $c->append_new_node (type => '#section');
786     my ($status, @params);
787 w 1.8 my $t = $self->_parse_md_params ($ms->set_attribute ('status_list'), \$skl, $o,
788     entMan => $opt{entMan});
789 wakaba 1.6 if ($t =~ /^(?:$xml_re{s})(IGNORE|INCLUDE)(?:$xml_re{s})?$/s) {
790     $status = $1;
791     } else {
792     $self->_raise_error ($o, c => $ms, type => 'SYNTAX_MS_INVALID_STATUS_STRING', t => $t);
793     $status = index ($t, 'IGNORE') > -1 ? 'IGNORE' : 'INCLUDE';
794 wakaba 1.5 }
795 wakaba 1.6 $self->_clp ('[' => $o);
796     $self->_raise_error ($o, c => $ms, type => 'SYNTAX_MS_NO_STATUS_KEYWORD') unless $status;
797 wakaba 1.5 $ms->set_attribute (status => ($status || 'INCLUDE'));
798 wakaba 1.6 ## Content and markup section end
799 wakaba 1.5 if ($status eq 'INCLUDE') {
800 w 1.8 $self->_parse_dtd ($ms, $s, $o, return_with_mse => 1, entMan => $opt{entMan});
801 wakaba 1.5 } else {
802     $self->_parse_ignored_marked_section ($ms, $s, $o);
803     }
804 wakaba 1.6 } else { ## Fatal error: Status keyword not found
805 wakaba 1.5 $self->_raise_error ($o, c => $c, type => 'SYNTAX_MS_NO_STATUS');
806 wakaba 1.6 ## Note: parse as a section is stopped (following is parsed as DTD)
807 wakaba 1.5 }
808     ## Markup section end (= msc + mdc)
809     } elsif ($opt{return_with_mse} && $$s =~ s/^\]\]>//) {
810     $self->_clp (___ => $o);
811     last;
812 wakaba 1.6 ## DOCTYPE declaration end
813     } elsif ($opt{return_with_dsc} && $$s =~ s/^(\](?:$xml_re{s})?>)//s) {
814     $self->_clp ($1 => $o);
815 wakaba 1.2 $c = $c->{parent};
816 wakaba 1.3 last;
817 wakaba 1.6 } elsif ($$s =~ s/^($xml_re{PI_M})//s) {
818     my ($target, $data, $all) = ($2, $3, $1);
819 wakaba 1.2 if ($target eq 'xml') {
820 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_POSITION');
821 wakaba 1.2 } else {
822 wakaba 1.5 $c->append_new_node (type => '#pi', local_name => $target, value => $data)
823     ->flag (smxp__src_pos => $self->_make_clone_of ($o));
824 wakaba 1.6 ## Notation declared warning is checked after rest of the DTD is read
825 wakaba 1.2 }
826 wakaba 1.6 $self->_clp ($all => $o);
827 wakaba 1.2 } else {
828 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
829 wakaba 1.2 substr ($$s, 0, 1) = '';
830 wakaba 1.6 $self->_clp (_ => $o);
831 wakaba 1.2 }
832     } # while $$s
833 wakaba 1.5 while (overload::StrVal ($c_initial) ne overload::StrVal ($c)) {
834 wakaba 1.2 if (ref $c->{parent}) {
835     $self->_raise_error ($o, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', t => $c);
836     $c = $c->{parent};
837     } else {
838     last;
839     }
840 wakaba 1.3 } # while
841 wakaba 1.5
842 w 1.8 $self->_validate_notation_declared ($c, $o, entMan => $opt{entMan})
843 wakaba 1.6 if $opt{validate_notation_declared};
844 wakaba 1.5 }
845    
846 wakaba 1.6 sub _validate_notation_declared ($$$;%) {
847     my ($self, $c, $o, %opt) = @_;
848 wakaba 1.3 my $l = [];
849 wakaba 1.6 my $entMan = $opt{entMan} || $c->root_node->flag ('smxp__entity_manager');
850 wakaba 1.3 my %defined;
851 wakaba 1.5 ## NDATA notation declared?
852     $entMan->get_entities ($l, parent_node => $c, namespace_uri => $NS{SGML}.'entity');
853 wakaba 1.3 for my $ent (@$l) {
854     for ($ent->get_attribute ('NDATA')) {
855     if (ref $_) {
856     my $nname = $_->inner_text;
857     if ($defined{$nname} > 0
858     || $entMan->get_entity ($nname, namespace_uri => $NS{SGML}.'notation')) {
859     $defined{$nname} = 1;
860     } else {
861     $self->_raise_error ($_->flag ('smxp__src_pos'), type => 'VC_NOTATION_DECLARED',
862     t => $nname, c => $_);
863     $defined{$nname} = -1;
864     }
865     }} # NDATA exist
866     }
867 wakaba 1.5 ## PI target name notation declared?
868     @$l = ();
869     $entMan->get_entities ($l, parent_node => $c, type => '#pi', namespace_uri => '');
870     for my $pi (@$l) {
871     my $nname = $pi->local_name;
872     if ($defined{$nname} > 0
873     || $entMan->get_entity ($nname, namespace_uri => $NS{SGML}.'notation')) {
874     $defined{$nname} = 1;
875     } else {
876     $self->_raise_error ($pi->flag ('smxp__src_pos'), type => 'WARN_PI_TARGET_NOTATION',
877     t => $nname, c => $pi)
878     unless lc (substr ($nname, 0, 3)) eq 'xml'; ## Target name xml* is maintained by W3C
879     $defined{$nname} = -1;
880     }
881     } # pi
882 wakaba 1.2 }
883    
884 w 1.8 sub _parse_attr_value_literal_data ($$\$$;%) {
885     my ($self, $c, $s, $o, %opt) = @_;
886     my $rt = '';
887     while ($$s) {
888     if ($$s =~ s/^&#(?:x([0-9A-Fa-f]+)|([0-9]+));//) {
889     my $char = chr (defined $1 ? hex $1 : 0 + $2);
890     $self->_warn_char_val ($o, $char, ref => 1);
891     $c->append_new_node (type => '#reference', value => ord $char,
892     namespace_uri => $NS{SGML}.'char:ref'.(defined $1?':hex':''));
893     $rt .= $char;
894     } elsif ($$s =~ s/^(&($xml_re{Name});)//) {
895     my ($ename, $entity_ref) = ($2, $1);
896     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ename)
897     if index ($ename, ':') > -1;
898     my $eref_node = $c->append_new_node (type => '#reference', local_name => $ename,
899     namespace_uri => $NS{SGML}.'entity');
900     my $entity = $opt{entMan}->get_entity ($ename, dont_use_predefined_entities => 1);
901     if (!$entity && {qw/&lt; 1 &gt; 1 &amp; 1 &quot; 1 &apos; 1/}->{$entity_ref}) {
902     $self->_raise_error ($o, c => $eref_node, type => 'WARN_PREDEFINED_ENTITY_NOT_DECLARED',
903     t => $entity_ref);
904     $entity = $opt{entMan}->get_entity ($ename);
905 wakaba 1.1 }
906 w 1.8 if (!$entity) {
907     $self->_raise_error ($o, c => $eref_node,
908     type => ($opt{entMan}->is_standalone_document_1?
909     'WF':'V').'C_ENTITY_DECLARED',
910     t => $entity_ref);
911     $rt .= $entity_ref;
912 wakaba 1.1 } else {
913 w 1.8 my $o2 = $self->_make_clone_of ($o);
914     if ($o2->{__entities}->{$entity_ref}) {
915     $self->_raise_error ($o, c => $eref_node, type => 'WFC_NO_RECURSION', t => $entity_ref);
916     $rt .= $entity_ref;
917 wakaba 1.2 } else {
918 w 1.8 my $entity_value = $entity->get_attribute ('value');
919     if (ref $entity_value) {
920     $o2->{__entities}->{$entity_ref} = 1;
921     $o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
922     $o2->{entity} = $entity_ref; $o2->{line} = 0; $o2->{pos} = 0;
923     my $ev = $entity_value->_entity_parameter_literal_value;
924     $rt .= $self->_parse_attr_value_literal_data ($eref_node, \$ev, $o2,
925     entMan => $opt{entMan});
926     $eref_node->flag (smxp__ref_expanded => 1);
927 wakaba 1.2 } else {
928 w 1.8 $self->_raise_error ($o, type => 'WFC_NO_EXTERNAL_ENTITY_REFERENCE',
929     c => $eref_node, t => $entity_ref);
930     $rt .= $entity_ref;
931 wakaba 1.2 }
932 w 1.8 } # ref recursive?
933     } # entity declared or not declared
934     $self->_clp ($entity_ref => $o);
935     } elsif ($$s =~ s/^([^&<]+)//s) {
936     my $tt = $1;
937     $tt =~ tr/\x09\x0A\x0D/\x20\x20\x20/;
938     $c->append_text ($tt);
939     $rt .= $tt;
940     $self->_clp ($tt => $o);
941 wakaba 1.2 } elsif ($$s =~ s/^<//) {
942 w 1.8 $self->_raise_error ($o, c => $c, type => 'WFC_NO_LE_IN_ATTRIBUTE_VALUE');
943     $rt .= '<';
944 wakaba 1.1 } else {
945 w 1.8 $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
946     $rt .= substr ($$s, 0, 1);
947 wakaba 1.2 substr ($$s, 0, 1) = '';
948 wakaba 1.1 }
949 w 1.8 } # $$s
950 w 1.9 ## Note: Implementation of normalize for non-CDATA attribute values should be done
951     ## out of this module.
952     #if ({qw/ID 1 IDREF 1 IDREFS 1 NMTOKEN 1 NMTOKENS 1
953     # NOTATION 1 NOTATIONS 1/}->{$opt{attr_type}}) {
954     # $rt =~ s/\x20\x20+/\x20/g;
955     # $rt =~ s/^\x20+//; $rt =~ s/\x20+$//;
956     #}
957 w 1.8 $rt;
958 wakaba 1.1 }
959 wakaba 1.2
960 wakaba 1.5 sub _parse_rpdata ($$\$$;%) {
961     my ($self, $c, $s, $o, %opt) = @_;
962 wakaba 1.6 my $entMan = $opt{entMan} || $c->root_node->flag ('smxp__entity_manager');
963     my $tt = '';
964 wakaba 1.2 while ($$s) {
965     if ($$s =~ s/^($xml_re{PEReference_M})//) {
966     my ($ref, $ename) = ($1, $2);
967 wakaba 1.6 $self->_raise_error ($o, type => 'WFC_PE_IN_INTERNAL_SUBSET', t => $ref)
968     if $o->{entity_type} eq 'document_entity';
969     $self->_raise_error ($o, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ref)
970     if index ($ename, ':') > -1;
971 wakaba 1.2 my $eref = $c->append_new_node (type => '#reference', local_name => $ename,
972     namespace_uri => $NS{SGML}.'entity:parameter');
973 wakaba 1.5 unless ($opt{dont_resolve_entity_ref}) {
974     my $entity = $entMan->get_entity ($ename, namespace_uri => $NS{SGML}.'entity:parameter');
975     if (!$entity) {
976     $self->_raise_error ($o, c => $c, type => 'VC_ENTITY_DECLARED', t => $ref);
977 wakaba 1.2 } else {
978 wakaba 1.5 my $o2 = $self->_make_clone_of ($o);
979     if ($o2->{__entities}->{$ref}) {
980     $self->_raise_error ($o, c => $c, type => 'WFC_NO_RECURSION', t => $ref);
981 wakaba 1.6 } elsif (defined $entity->flag ('smxp__entity_replacement_text_rpdata')) {
982     my $ev = $entity->flag ('smxp__entity_replacement_text_rpdata');
983     $eref->append_text ($ev);
984     $tt .= $ev;
985 wakaba 1.5 } else {
986     $o2->{entity} = $ref;
987     my $entity_value = $entity->get_attribute ('value');
988     if (ref $entity_value) { ## Internal entity
989 wakaba 1.4 $o2->{__entities}->{$ref} = 1;
990 wakaba 1.5 $o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
991     $o2->{line} = 0; $o2->{pos} = 0;
992     my $ev = $entity_value->_entity_parameter_literal_value;
993 wakaba 1.6 $ev = $self->_parse_rpdata ($eref, \$ev, $o2, entMan => $entMan);
994     $entity->flag (smxp__entity_replacement_text_rpdata => $ev);
995     $tt .= $ev;
996 wakaba 1.5 } else { ## External entity
997     $o2->{entity_type} = 'external_parameter_entity';
998 w 1.9 $c->root_node->flag (smxp__declaration_may_not_be_read => 1)
999     if !(index ($o->{entity_type}, 'external') > -1)
1000     || !$opt{entMan}->is_standalone_document;
1001 wakaba 1.5 my $ext_ent = $entMan->get_external_entity ($self, $entity, $o2);
1002     if ($ext_ent->{NDATA}) { ## non-parsed entity
1003     $self->_raise_error ($o, type => 'WFC_PARSED_ENTITY', c => $entity, t => $ref);
1004     } elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
1005     $self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
1006     t => [$ref, $o2->{uri}, $ext_ent->{error}->{reason_text}]);
1007 wakaba 1.6 $c->root_node->flag (smxp__stop_read_dtd => 1)
1008     unless $entMan->is_standalone_document;
1009 wakaba 1.5 } else { ## parsed entity
1010     $o2->{__entities}->{$ref} = 1;
1011     my $ev = $ext_ent->{text};
1012 wakaba 1.6 $ev = $self->_parse_rpdata ($eref, \$ev, $o2, entMan => $entMan);
1013     $entity->flag (smxp__entity_replacement_text_rpdata => $ev);
1014     $tt .= $ev;
1015 wakaba 1.5 $eref->flag (smxp__ref_expanded => 1);
1016     } # external parsed entity
1017     } # external entity
1018     } # not recursive
1019     } # entity defined
1020     } # read not stopped
1021     $self->_clp ($ref => $o);
1022 wakaba 1.2 } elsif ($$s =~ s/^([^%]+)//) {
1023 wakaba 1.3 my $t = $1; my $r = '';
1024     while ($t) {
1025 wakaba 1.6 if ($t =~ s/^(&#(?:x([0-9A-Fa-f]+)|([0-9]+));)//) {
1026     for (chr ($2 ? hex ($2) : $3)) {
1027     $self->_warn_char_val ($o, $_, ref => 1);
1028     $r .= $_;
1029 w 1.8 $c->append_new_node (type => '#reference', value => ord $_,
1030     namespace_uri => $NS{SGML}.'char:ref'.(defined $2 ? ':hex' : ''));
1031 wakaba 1.6 }
1032     $self->_clp ($1 => $o);
1033 wakaba 1.5 } elsif ($t =~ s/^(&($xml_re{Name});)//) {
1034     my ($entity_ref, $eref) = ($1, $2);
1035     $r .= $entity_ref;
1036 wakaba 1.6 $self->_raise_error ($o, type => 'NS_SYNTAX_NAME_IS_NCNAME', c => $c, t => $entity_ref)
1037     if index ($eref, ':') > -1;
1038 wakaba 1.5 my $entity = $entMan->get_entity ($eref);
1039     if ($entity && $entity->get_attribute ('NDATA')) {
1040     $self->_raise_error ($o, type => 'ERR_XML_NDATA_REF_IN_ENTITY_VALUE',
1041     c => $c, t => $entity_ref);
1042     ## Note: this error is not raisen when the entity referred is declared after
1043     ## the EntityValue occurs.
1044     ## Note: this error was a fatal error, but refined by XML 1.0 SE Errata.
1045 wakaba 1.3 }
1046 w 1.8 $c->append_new_node (type => '#reference', namespace_uri => $NS{SGML}.'entity',
1047     local_name => $eref);
1048 wakaba 1.5 $self->_clp ($entity_ref => $o);
1049 wakaba 1.3 } elsif ($t =~ s/^&//) {
1050     $self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', c => $c, t => '&');
1051     $r .= '&';
1052 w 1.8 $c->append_new_node (type => '#reference', namespace_uri => $NS{SGML}.'char:ref',
1053     value => 0x26);
1054 wakaba 1.6 $self->_clp (_ => $o);
1055 wakaba 1.3 } elsif ($t =~ s/^([^&]+)//s) {
1056     $r .= $1;
1057 w 1.8 $c->append_new_node (type => '#text', value => $1);
1058 wakaba 1.6 $self->_clp ($1 => $o);
1059 wakaba 1.3 }
1060     }
1061 wakaba 1.6 $tt .= $r;
1062 wakaba 1.1 } else {
1063 wakaba 1.6 $self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
1064 wakaba 1.2 substr ($$s, 0, 1) = '';
1065 wakaba 1.6 $self->_clp (_ => $o);
1066 wakaba 1.1 }
1067 wakaba 1.6 } # while $$s
1068     $tt;
1069 wakaba 1.1 }
1070 wakaba 1.2
1071 wakaba 1.6 sub _parse_md_params ($$\$$$;%) {
1072     my ($self, $c, $s, $o, %opt) = @_;
1073     my $entMan = $opt{entMan} || $c->root_node->flag ('smxp__entity_manager');
1074     my $t = '';
1075     while ($$s) {
1076     if ($$s =~ s/^($xml_re{PEReference_M})//) {
1077     my ($ref, $ename) = ($1, $2);
1078     $self->_raise_error ($o, c => $c, type => 'WFC_PE_IN_INTERNAL_SUBSET', t => $ref)
1079     if $o->{entity_type} eq 'document_entity';
1080     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ref)
1081     if index ($ename, ':') > -1;
1082     my $eref = $c->append_new_node (type => '#reference', local_name => $ename,
1083     namespace_uri => $NS{SGML}.'entity:parameter');
1084     unless ($opt{dont_resolve_entity_ref}) {
1085     my $entity = $entMan->get_entity ($ename, namespace_uri => $NS{SGML}.'entity:parameter');
1086     if (!$entity) {
1087     $self->_raise_error ($o, c => $c, type => 'VC_ENTITY_DECLARED', t => $ref);
1088     } else {
1089     if ($o->{__entities}->{$ref}) {
1090     $self->_raise_error ($o, c => $c, type => 'WFC_NO_RECURSION', t => $ref);
1091     } elsif (defined $entity->flag ('smxp__entity_replacement_text_md_params')) {
1092     $t .= ' '.$entity->flag ('smxp__entity_replacement_text_md_params').' ';
1093     } else {
1094     my $o2 = $self->_make_clone_of ($o);
1095     $o2->{entity} = $ref;
1096     my $entity_value = $entity->get_attribute ('value');
1097     if (ref $entity_value) { ## Internal entity
1098     $o2->{__entities}->{$ref} = 1;
1099     $o2->{uri} = $entity->flag ('smxp__uri_in_which_declaration_is');
1100     $o2->{line} = 0; $o2->{pos} = 0;
1101     my $ev = $entity_value->_entity_parameter_literal_value;
1102     $ev = $self->_parse_md_params ($eref, \$ev, $o2, entMan => $entMan);
1103     $entity->flag (smxp__entity_replacement_text_md_params => $ev);
1104     $t .= ' '.$ev.' ';
1105     $eref->flag (smxp__ref_expanded => 1);
1106     } else { ## External entity
1107     $o2->{entity_type} = 'external_parameter_entity';
1108 w 1.9 $c->root_node->flag (smxp__declaration_may_not_be_read => 1)
1109     if !(index ($o->{entity_type}, 'external') > -1)
1110     || !$opt{entMan}->is_standalone_document;
1111 wakaba 1.6 my $ext_ent = $entMan->get_external_entity ($self, $entity, $o2);
1112     if ($ext_ent->{NDATA}) { ## non-parsed entity
1113     $self->_raise_error ($o, type => 'WFC_PARSED_ENTITY', c => $entity, t => $ref);
1114     } elsif ($ext_ent->{error}->{no_data}) { ## parsed entity but can't be retrived
1115     $self->_raise_error ($o, type => 'ERR_EXT_ENTITY_NOT_FOUND', c => $entity,
1116     t => [$ref, $o2->{uri}, $ext_ent->{error}->{reason_text}]);
1117     $c->root_node->flag (smxp__stop_read_dtd => 1)
1118 w 1.9 unless $opt{entMan}->is_standalone_document;
1119 wakaba 1.6 } else { ## parsed entity
1120     $o2->{__entities}->{$ref} = 1;
1121     my $ev = $ext_ent->{text};
1122 w 1.9 $ev = $self->_parse_md_params ($eref, \$ev, $o2, entMan => $opt{entMan});
1123 wakaba 1.6 $entity->flag (smxp__entity_replacement_text_md_params => $ev);
1124     $t .= ' '.$ev.' ';
1125     $eref->flag (smxp__ref_expanded => 1);
1126     } # external parsed entity
1127     } # external entity
1128     } # not recursive
1129     } # entity defined
1130     } # not stopped
1131     $c->flag (smxp__defined_with_param_ref => 1);
1132     $self->_clp ($ref => $o);
1133     } elsif ($$s =~ s/^($xml_re{__AttValue_simple})//s) {
1134     my $all = $1;
1135     $t .= $all;
1136     $c->append_new_node (type => '#xml', value => $all); ## Note: is this safe?
1137     $self->_clp ($all => $o);
1138 w 1.8 } elsif ($$s =~ s/^(\p{InXMLNameChar}+)//) {
1139 wakaba 1.6 $c->append_text ($1);
1140     $t .= $1;
1141     $self->_clp ($1 => $o);
1142 w 1.8 if ($$s =~ s/^([+*?])//) {
1143     $c->append_text ($1);
1144     $t .= $1;
1145     $self->_clp (_ => $o);
1146     }
1147     } elsif ($$s =~ s/^\(//) {
1148     $c->append_text ('(');
1149     my $grp = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'group');
1150     $t .= '('.$self->_parse_md_params ($grp, $s, $o, entMan => $entMan, return_by_grpc => 1);
1151     $c->flag (smxp__defined_with_param_ref => 1)
1152     if $grp->flag ('smxp__defined_with_param_ref');
1153     } elsif ($$s =~ s/^([%#,|])//) {
1154 wakaba 1.6 $c->append_text ($1);
1155     $t .= $1;
1156     $self->_clp ($1 => $o);
1157     } elsif ($$s =~ s/^($xml_re{s})//s) {
1158     $c->append_text ($1);
1159     $t .= $1;
1160     $self->_clp ($1 => $o);
1161     } elsif ($opt{return_by_mdc} && $$s =~ s/^>//) { ## mdc
1162     $self->_clp (_ => $o);
1163     return $t;
1164 w 1.8 } elsif (($opt{return_by_mdc}||$opt{return_by_grpc}) && $$s =~ m/^</) { ## maybe mdo
1165 wakaba 1.6 $self->_raise_error ($o, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', t => $c, c => $c);
1166     return $t;
1167 w 1.8 } elsif ($opt{return_by_grpc} && $$s =~ s/^\)//) { ## grpc
1168     $self->_clp (_ => $o);
1169     $t .= ')';
1170     $c->parent_node->append_text (')');
1171     if ($$s =~ s/^([+*?])//) {
1172     $t .= $1;
1173     $c->parent_node->append_text ($1);
1174     $self->_clp (_ => $o);
1175     }
1176     return $t;
1177 wakaba 1.6 } else {
1178     $self->_raise_error ($o, type => 'SYNTAX_INVALID_CHAR', t => substr ($$s, 0, 10));
1179     substr ($$s, 0, 1) = '';
1180     $self->_clp (_ => $o);
1181     }
1182     } # while
1183 w 1.8 if ($opt{return_by_grpc}) {
1184     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_GROUP_NOT_CLOSED');
1185     $t .= ')';
1186     }
1187 wakaba 1.6 $t;
1188     }
1189    
1190 wakaba 1.2 sub _warn_char_val ($$$%) {
1191     my ($self, $o, $ch, %o) = @_;
1192 wakaba 1.6 if ($ch =~ /^(.*?)(\P{InXMLChar})/) {
1193     $self->_clp ($1 => $o);
1194     $self->_raise_error ($o, type => (($o{ref}?'WFC':'SYNTAX').'_LEGAL_CHARACTER'), t => ord $2);
1195     } elsif ($ch =~ /^(.*?)(\p{InXML_deprecated_noncharacter})/) {
1196     $self->_clp ($1 => $o);
1197     $self->_raise_error ($o, type => 'WARN_UNICODE_NONCHARACTER', t => ord $2);
1198     } elsif ($ch =~ /^(.*?)(\p{Compat})/) {
1199     $self->_clp ($1 => $o);
1200     $self->_raise_error ($o, type => 'WARN_UNICODE_COMPAT_CHARACTER', t => ord $2);
1201     } elsif ($ch =~ /^(.*?)(\p{InXML_unicode_xml_not_suitable})/) {
1202     $self->_clp ($1 => $o);
1203     $self->_raise_error ($o, type => 'WARN_UNICODE_XML_NOT_SUITABLE_CHARACTER', t => ord $2);
1204 wakaba 1.1 }
1205     }
1206     sub _parse_reference ($$$$) {
1207     my ($self, $c, $ref, $o) = @_;
1208 wakaba 1.2 my $r;
1209 wakaba 1.1 if ($ref =~ /$xml_re{EntityRef_M}/) { ## BUG: QName
1210 wakaba 1.2 $r = $c->append_new_node (type => '#reference', local_name => $1,
1211     namespace_uri => $NS{SGML}.'entity');
1212 wakaba 1.1 } elsif ($ref =~ /x([0-9A-Fa-f]+)/) {
1213     my $ch = hex $1;
1214 wakaba 1.2 $self->_warn_char_val ($o, chr $ch, ref => 1);
1215     $r = $c->append_new_node (type => '#reference', value => $ch,
1216 wakaba 1.6 namespace_uri => $NS{SGML}.'char:ref:hex');
1217 wakaba 1.1 } elsif ($ref =~ /([0-9]+)/) {
1218 wakaba 1.2 my $ch = 0+$1;
1219     $self->_warn_char_val ($o, chr $ch, ref => 1);
1220     $r = $c->append_new_node (type => '#reference', value => $ch,
1221 wakaba 1.6 namespace_uri => $NS{SGML}.'char:ref');
1222 wakaba 1.1 } else {
1223     $self->_raise_error ($o, type => 'UNKNOWN', t => $ref);
1224     }
1225     _count_lp ($ref, $o);
1226 wakaba 1.2 $r;
1227 wakaba 1.1 }
1228 wakaba 1.4
1229 wakaba 1.6 sub _parse_ignored_marked_section ($$\$$;%) {
1230 wakaba 1.5 my ($self, $c, $s, $o) = @_;
1231     while ($$s) {
1232     if ($$s =~ s/^<!\[//) {
1233     $self->_clp (___ => $o);
1234     $self->_parse_ignored_marked_section ($c->append_new_node (type => '#section'), $s, $o);
1235     } elsif ($$s =~ s/^\]\]>//) {
1236     $self->_clp (___ => $o);
1237     return;
1238     } elsif ($$s =~ s/^((?:(?!<!\[|\]\]>).)+)//s) {
1239     $c->append_text ($1);
1240     $self->_clp ($1 => $o);
1241     }
1242     } # $$s
1243     $self->_raise_error ($o, c => $c, type => 'SYNTAX_END_OF_MARKUP_NOT_FOUND', t => $c);
1244     }
1245    
1246 wakaba 1.4 sub _parse_xml_or_text_declaration ($$\$$) {
1247     my ($self, $c, $s, $o) = @_;
1248     if ($$s =~ s/^$xml_re{_xml_PI_M}//s) {
1249 wakaba 1.6 my $data = $1;
1250     $self->_clp (_____ => $o);
1251     if (length $data) {
1252 wakaba 1.4 $self->_parse_xml_declaration ($c, $data, $o);
1253     } else {
1254     $self->_raise_error ($o, c => $c, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
1255     }
1256 wakaba 1.6 $self->_clp (__ => $o);
1257 wakaba 1.4 }
1258     }
1259 wakaba 1.1 sub _parse_xml_declaration ($$$$) {
1260     my ($self, $c, $attrs, $o) = @_;
1261     my $stage = 0; # 0: <?xml, 1: version="", 2: encoding="", 3: standalone="", 4: ?>
1262     $attrs = ' ' . $attrs;
1263     while ($attrs) {
1264     if ($attrs =~ s/^$xml_re{s}version(?:$xml_re{s})?=(?:$xml_re{s})?("[A-Za-z0-9_.:-]+"|'[A-Za-z0-9_.:-]+')//s) {
1265     my $version = substr ($1, 1, length ($1) - 2);
1266     if ($stage > 0) {
1267     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'version');
1268     }
1269 wakaba 1.4 $c->set_attribute (version => $version);
1270     ## TODO: XML 1.1 support
1271     if ($version ne '1.0') {
1272     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_UNSUPPORTED_XML_VERSION', t => $version);
1273 wakaba 1.1 }
1274     _count_lp ($&, $o); $stage++;
1275     } elsif ($attrs =~ s/^$xml_re{s}encoding(?:$xml_re{s})?=(?:$xml_re{s})?("[A-Za-z0-9_.-]+"|'[A-Za-z0-9_.:-]+')//s) {
1276     if ($stage > 2) {
1277     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'encoding');
1278     } elsif ($stage == 0) { ## No version pseudo-attr
1279     if ($o->{entity_type} eq 'document_entity') {
1280 wakaba 1.4 $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_VERSION_ATTR');
1281 wakaba 1.1 $c->set_attribute (version => '1.0');
1282     } else {
1283 wakaba 1.4 $self->_raise_error ($o, c => $attrs, type => 'WARN_XML_DECLARE_NO_VERSION_ATTR');
1284 wakaba 1.1 $o->{entity_type} = 'external_parsed_entity';
1285     }
1286     }
1287     $c->set_attribute (encoding => substr ($1, 1, length ($1) - 2));
1288     _count_lp ($&, $o); $stage = 2;
1289     } elsif ($attrs =~ s/^$xml_re{s}standalone(?:$xml_re{s})?=(?:$xml_re{s})?("(?:yes|no)"|'(?:yes|no)')//s) {
1290 wakaba 1.4 if ($stage == 0) { ## 'version' or 'encoding' is expected
1291     if ($o->{entity_type} eq 'document_entity') { ## XML declaration
1292     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_VERSION_ATTR');
1293     $c->set_attribute (version => '1.0');
1294     } else { ## Text declaration
1295     $self->_raise_error ($o, c => $attrs, type => 'WARN_XML_DECLARE_NO_VERSION_ATTR');
1296     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ENCODING_ATTR');
1297     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_STANDALONE_ATTR');
1298     }
1299     } elsif ($stage > 3) {
1300     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => 'standalone');
1301 wakaba 1.1 }
1302     $c->set_attribute (standalone => (substr ($1, 1, 1) eq 'y' ? 'yes' : 'no'));
1303     _count_lp ($&, $o); $stage = 3;
1304     } elsif ($attrs =~ s/^($xml_re{s})//s) {
1305     my $s = $1;
1306     if ($stage == 0) {
1307     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
1308     $c->set_attribute (version => '1.0');
1309     }
1310     _count_lp ($s, $o); $stage = 4;
1311     } else {
1312     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE', t => $attrs);
1313     _count_lp ($attrs, $o); undef $attrs;
1314     }
1315     } # while
1316     if ($stage == 0) {
1317     $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ATTR');
1318     $c->set_attribute (version => '1.0');
1319 wakaba 1.7 } elsif ($stage == 1 && index ($o->{entity_type}, 'external') > -1) {
1320 wakaba 1.4 $self->_raise_error ($o, c => $attrs, type => 'SYNTAX_XML_DECLARE_NO_ENCODING_ATTR');
1321 wakaba 1.1 }
1322     }
1323 wakaba 1.4
1324 wakaba 1.6 sub _parse_entity_declaration ($\$$$;%) {
1325     my ($self, $s, $c, $o, %opt) = @_;
1326 wakaba 1.2 my $p; ## notation ? 'n' : parameter entity ? '%' : undef;
1327 wakaba 1.6 my $entMan = $opt{entMan} || $c->root_node->flag ('smxp__entity_manager');
1328     my $dont_process = $c->root_node->flag ('smxp__stop_read_dtd');
1329 wakaba 1.2 my $e = $c->append_new_node (type => '#declaration');
1330 wakaba 1.6 $e->flag (smxp__uri_in_which_declaration_is => $o->{uri});
1331 w 1.9 $e->flag (smxp__declaration_may_not_be_read
1332     => $c->root_node->flag ('smxp__declaration_may_not_be_read'));
1333 wakaba 1.6 ## Entity? or notation?
1334 wakaba 1.5 if ($$s =~ s/^<!ENTITY//) {
1335 wakaba 1.2 $e->namespace_uri ($NS{SGML}.'entity');
1336 wakaba 1.5 $self->_clp (________ => $o);
1337     } else {
1338     $$s =~ s/^<!NOTATION//;
1339 wakaba 1.2 $e->namespace_uri ($NS{SGML}.'notation');
1340 wakaba 1.5 $self->_clp (__________ => $o); $p = 'n';
1341 wakaba 1.1 }
1342 wakaba 1.6 ## Parameters
1343     my $t;
1344     $t = $self->_parse_md_params ($e, $s, $o, dont_resolve_entity_ref => $dont_process,
1345     return_by_mdc => 1, entMan => $entMan);
1346 wakaba 1.5 $dont_process = $c->root_node->flag ('smxp__stop_read_dtd');
1347     unless ($dont_process) {
1348 wakaba 1.6 my $o = $self->_make_clone_of ($o);
1349     my $is_internal = 1;
1350     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1351     unless $t =~ s/^$xml_re{s}//s;
1352     if ($t =~ s/^%//) {
1353     if ($p eq 'n') {
1354     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => '%');
1355     } else {
1356     $e->namespace_uri ($NS{SGML}.'entity:parameter'); $p = '%';
1357     }
1358     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1359     unless $t =~ s/^$xml_re{s}//s;
1360     }
1361     my $ename;
1362     if ($t =~ s/^($xml_re{Name})//) {
1363     $ename = $1;
1364 wakaba 1.5 if ($entMan->is_declared_entity ($ename, namespace_uri => $e->namespace_uri,
1365     dont_use_predefined_entities => 1,
1366     seek => 0)) {
1367     if ($p eq 'n') {
1368 wakaba 1.6 $self->_raise_error ($o, c => $e,
1369 wakaba 1.5 type => 'VC_UNIQUE_NOTATION_NAME', t => $ename);
1370     } else {
1371 wakaba 1.6 $self->_raise_error ($o, c => $e, t => $t,
1372     type => 'WARN_UNIQUE_'.($p eq '%' ? 'PARAMETER_' : ''));
1373 wakaba 1.2 }
1374 wakaba 1.6 } else { ## Regist to entMan
1375 wakaba 1.5 $entMan->is_declared_entity ($ename, namespace_uri => $e->namespace_uri,
1376     dont_use_predefined_entities => 1,
1377     set_new_value => 1, seek => 0)
1378     }
1379 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_NCNAME', t => $ename)
1380     if index ($ename, ':') > -1;
1381 wakaba 1.5 $e->local_name ($ename);
1382 wakaba 1.6 $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1383     unless $t =~ s/^$xml_re{s}//s;
1384     } else {
1385     $self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_NAME_NOT_FOUND',
1386     t => substr ($t, 0, 10));
1387     }
1388     if ($t =~ s/^PUBLIC//) {
1389     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1390     unless $t =~ s/^$xml_re{s}//s;
1391     unless ($t =~ s/^($xml_re{__AttValue_simple})//s) { ## TODO: new error
1392     $self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_PID_NOT_FOUND',
1393     t => substr ($t, 0, 10));
1394     } else {
1395     $e->set_attribute (PUBLIC => $entMan->check_public_id
1396     ($o, substr ($1, 1, length ($1) - 2)));
1397     my $f = $t =~ s/^$xml_re{s}//s ? 1 : 0;
1398     if ($t =~ s/^($xml_re{__AttValue_simple})//s) {
1399     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1400     unless $f;
1401     $e->set_attribute (SYSTEM => $entMan->check_system_id
1402     ($o, substr ($1, 1, length ($1) - 2)));
1403     } else {
1404     if ($p ne 'n') {
1405     $self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_SYSID_NOT_FOUND',
1406     t => substr ($t, 0, 10));
1407     $e->set_attribute (SYSTEM => $NS{internal_invalid_sysid});
1408 wakaba 1.3 }
1409 wakaba 1.2 }
1410 wakaba 1.6 }
1411     $is_internal = 0;
1412     } elsif ($t =~ s/^SYSTEM//) {
1413     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1414     unless $t =~ s/^$xml_re{s}//s;
1415     if ($t =~ s/^($xml_re{__AttValue_simple})//s) {
1416     $e->set_attribute (SYSTEM => $entMan->check_system_id
1417     ($o, substr ($1, 1, length ($1) - 2)));
1418     } else { ## TODO: error text
1419     $self->_raise_error ($o, c => $e, type => 'SYNTAX_MD_SYSID_NOT_FOUND',
1420     t => substr ($t, 0, 10));
1421     $e->set_attribute (SYSTEM => $NS{internal_invalid_sysid});
1422     }
1423     $is_internal = 0;
1424     } elsif ($p ne 'n' && $t =~ s/^($xml_re{__AttValue_simple})//s) { ## EntityValue (ENTITY only)
1425     ## TOTO: $o
1426     my $ev = $1; $ev = substr ($ev, 1, length ($ev) - 2);
1427     $ev = $self->_parse_rpdata ($e->set_attribute ('value'), \$ev, $o);
1428     unless (defined $e->flag ('smxp__entity_replacement_text_rpdata')) {
1429     $e->flag (smxp__entity_replacement_text_rpdata => $ev);
1430     }
1431 wakaba 1.5 if (($p ne '%') && {qw/lt 1 gt 1 amp 1 quot 1 apos 1/}->{$ename}) {
1432 wakaba 1.6 ## TODO: check when external entity too
1433     $self->_raise_error ($o, c => $e,
1434     type => 'FATAL_ERR_PREDEFINED_ENTITY', t => [$ename, $ev])
1435     unless {qw/lt|&#60; 1 gt|>&#62; 1 amp|&#38; 1 apos|&#39; 1 quot|&#34; 1
1436     lt|&#x3c; 1 gt|&#x3e; 1 amp|&#x26; 1 apos|&#x27; 1 quot|&#x22; 1
1437     gt|> 1 apos|' 1 quot|" 1
1438     /}->{$ename.'|'.lc ($ev)};
1439 wakaba 1.5 }
1440 wakaba 1.6 }
1441     if ($t =~ s/^$xml_re{s}NDATA//s) {
1442     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1443     unless $t =~ s/^$xml_re{s}//s;
1444     unless ($t =~ s/^($xml_re{Name})//s) {
1445     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1446     } else {
1447     my $nname = $1;
1448     if ($p eq '%') { ## parameter entity
1449     $self->_raise_error ($o, c => $e, type => 'SYNTAX_PE_NDATA', t => $nname);
1450     } elsif ($is_internal) {
1451     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_KEYWORD', t => 'NDATA');
1452     } else {
1453     $e->set_attribute (NDATA => $nname)
1454     ->flag (smxp__src_pos => $o);
1455     }
1456 wakaba 1.5 }
1457 wakaba 1.2 }
1458 wakaba 1.6 $t =~ s/^$xml_re{s}//s;
1459     if (length $t) {
1460     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => $t);
1461     }
1462 wakaba 1.5 } else { ## dont_process
1463     $c->flag (smxp__non_processed_declaration => 1);
1464 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'WARN_ENTITY_DECLARATION_NOT_PROCESSED');
1465 wakaba 1.1 }
1466     }
1467    
1468 w 1.8 sub _parse_element_declaration ($$$$;%) {
1469     my ($self, $s, $c, $o, %opt) = (@_);
1470     $c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'element');
1471     unless ($$s =~ s/^<!ELEMENT//s) {
1472     $self->_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
1473     return;
1474     }
1475     $self->_clp (_________ => $o);
1476    
1477     my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
1478    
1479     ## Element type name
1480     if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
1481     my $type_qname = $2;
1482     if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
1483     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
1484     $type_qname =~ tr/:/_/;
1485 wakaba 1.1 }
1486 w 1.8 $c->set_attribute (qname => $type_qname);
1487     } else {
1488     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1489     return;
1490     }
1491    
1492     ## Content model
1493     my $delimited = 1;
1494     my $in_group = 0;
1495     my @grp_connector;
1496     my $is_pcdata;
1497     my $r = $c;
1498     while ($t) {
1499     if ($t =~ s/^$xml_re{s}//s) {
1500     #
1501     } elsif ($t =~ s/^($xml_re{Name})//) {
1502     my $name = $1;
1503     if ($in_group) {
1504     unless ($delimited) {
1505     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1506     t => $name);
1507     } else {
1508     $delimited = 0;
1509     }
1510     if (substr ($name, 0, 1) eq ':' || substr ($name, -1, 1) eq ':') {
1511     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $name);
1512     $name =~ tr/:/_/;
1513     }
1514     $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
1515     local_name => 'element')
1516     ->set_attribute (qname => $name);
1517     if ($t =~ s/^([+*?])//) {
1518     $c->set_attribute (occurence => $1);
1519     }
1520     } else {
1521     if ({qw/EMPTY 1 ANY 1/}->{$name}) {
1522     #
1523     } elsif ({qw/PCDATA 1 CDATA 1/}->{$name}) {
1524     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_KWD',
1525     t => $name);
1526     } else {
1527     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
1528     t => $name);
1529     }
1530     $r->set_attribute (content => $name);
1531     last;
1532     } # out of group
1533     } elsif ($in_group && $t =~ s/^\#($xml_re{Name})//) {
1534     my $kwd = $1;
1535     unless ($delimited) {
1536     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1537     t => $kwd);
1538     } else {
1539     $delimited = 0;
1540     }
1541     if ($in_group > 1 || $grp_connector[$in_group]) {
1542     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_KWD_POSITION',
1543     t => $kwd);
1544     }
1545     if ($kwd ne 'PCDATA') {
1546     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
1547     t => $kwd);
1548     } else {
1549     $is_pcdata = 1;
1550     $r->set_attribute (content => 'mixed');
1551     }
1552     } elsif ($in_group && $t =~ s/^([|,&])//) {
1553     my $connector = $1;
1554     if ($delimited) {
1555     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
1556     t => $connector.substr ($t, 0, 9));
1557     } else {
1558     $delimited = 1;
1559     }
1560     if ($connector eq '&') {
1561     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_CONNECTOR',
1562     t => $connector);
1563     $connector = ',';
1564     } elsif ($is_pcdata && $connector ne '|') {
1565     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_PCDATA_CONNECTOR',
1566     t => $connector);
1567     $connector = '|';
1568     }
1569     unless ($grp_connector[$in_group]) {
1570     $grp_connector[$in_group] = $connector;
1571     $c->set_attribute (connector => $connector);
1572     } elsif ($grp_connector[$in_group] ne $connector) {
1573     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SAME_CONNECTOR',
1574     t => [$grp_connector[$in_group], $connector]);
1575     }
1576     } elsif ($t =~ s/^\(//) {
1577     unless ($delimited) {
1578     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1579     t => '('.substr ($t, 0, 9));
1580     }
1581     if ($is_pcdata) {
1582     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_NESTED');
1583     } else {
1584     $c = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
1585     local_name => 'group');
1586     }
1587     $in_group++;
1588     $grp_connector[$in_group] = undef;
1589     $delimited = 1;
1590     } elsif ($in_group && $t =~ s/^\)//) {
1591     if ($delimited) {
1592     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
1593     t => ')'.substr ($t, 0, 9));
1594     $delimited = 0;
1595     }
1596     if ($t =~ s/^([+*?])//) {
1597     my $occur = $1;
1598     if ($is_pcdata && $occur ne '*') {
1599     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE',
1600     t => $occur);
1601     }
1602     $c->set_attribute (occurence => $occur);
1603     } elsif ($is_pcdata && $grp_connector[$in_group]) {
1604     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE');
1605     }
1606     $c = $c->parent_node;
1607     $in_group--;
1608     last if !$in_group;
1609     } else {
1610     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1611     substr ($t, 0, 1) = '';
1612     }
1613     } # $t
1614     if ($in_group) {
1615     ## Note: Maybe this error does not happen since md_params parsing report it
1616     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_GROUP_NOT_CLOSED');
1617     }
1618    
1619     if ($t =~ /[^$xml_re{_s__chars}]/) {
1620     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => $t);
1621     }
1622 wakaba 1.1 }
1623    
1624    
1625 w 1.8 sub _parse_attlist_declaration ($$$$;%) {
1626     my ($self, $s, $c, $o, %opt) = (@_);
1627     $c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'attlist');
1628 w 1.9 $c->flag (smxp__declaration_may_not_be_read
1629     => $c->root_node->flag ('smxp__declaration_may_not_be_read'));
1630 w 1.8 unless ($$s =~ s/^<!ATTLIST//s) {
1631     $self->_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
1632     return;
1633     }
1634     $self->_clp (_________ => $o);
1635    
1636 w 1.9 my $dont_process = $c->root_node->flag ('smxp__stop_read_dtd');
1637 w 1.8 my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
1638 w 1.9 $dont_process = $c->root_node->flag ('smxp__stop_read_dtd');
1639 w 1.8
1640 w 1.9 unless ($dont_process) {
1641     ## Element type name
1642     if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
1643     my $type_qname = $2;
1644     if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
1645     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
1646     $type_qname =~ tr/:/_/;
1647     }
1648     $c->set_attribute (qname => $type_qname);
1649     } else {
1650     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1651     return;
1652 wakaba 1.1 }
1653 w 1.9
1654     ## Definition
1655     my %defined;
1656     while ($t) {
1657     if ($t =~ s/^$xml_re{s}($xml_re{Name})//s) {
1658     my %attr = (name => $1, type => undef);
1659     if (substr ($attr{name}, 0, 1) eq ':' || substr ($attr{name}, -1, 1) eq ':') {
1660     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $attr{name});
1661     $attr{name} =~ tr/:/_/;
1662     }
1663     if ($defined{$attr{name}}) {
1664     $self->_raise_error ($o, c => $c, type => 'WARN_XML_ATTLIST_AT_MOST_ONE_ATTR_DEF',
1665     t => $attr{name});
1666     } else {
1667     $defined{$attr{name}} = 1;
1668     }
1669     $attr{node} = $c->append_new_node (type => '#element', namespace_uri => $NS{XML}.'attlist',
1670     local_name => 'AttDef');
1671     $attr{node}->set_attribute (qname => $attr{name});
1672     if ($t =~ s/^$xml_re{s}//s) {
1673     if ($t =~ s/^NOTATION$xml_re{s}//s) {
1674     $attr{type} = 'NOTATION';
1675 w 1.8 }
1676 w 1.9 if (!$attr{type} && $t =~ s/^([A-Za-z]+)//) { # attname type
1677     $attr{type} = $1;
1678     unless ({qw/CDATA 1 ID 1 IDREF 1 IDREFS 1 NMTOKEN 1 NMTOKENS 1
1679     ENTITY 1 ENTITIES 1/}->{$attr{type}}) {
1680     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_TYPE',
1681     c => $c, t => $attr{type});
1682     }
1683     } elsif ($t =~ s/^\(([^)]+)\)//) { # attname (group)
1684     my $grp = $1;
1685     if (index ($grp, '(') > -1) {
1686     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NESTED_GROUP',
1687     c => $c, t => $grp);
1688     }
1689     if (index ($grp, '&') > -1 || index ($grp, ',') > -1) {
1690     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NON_BAR_CONNECTOR',
1691     c => $c, t => $grp);
1692     }
1693     if ($grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//s) {
1694     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CHAR',
1695 w 1.8 c => $c, t => $1);
1696 w 1.9 $grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//sg;
1697     } else {
1698     $grp =~ tr/\x09\x0A\x0D\x20//d; # $xml_re{_s__chars}
1699     if ($grp =~ /(^\||\|\||\|$)/) {
1700     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CONNECTOR',
1701     c => $c, t => $1);
1702     }
1703     }
1704     my @grp = grep {$_} split /\P{InXMLNameChar}+/, $grp;
1705     if ($attr{type}) { ## NOTATION
1706     for (@grp) {
1707     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_NOTATION_NAME',
1708     c => $c, t => $_)
1709     unless /^$xml_re{Name}$/;
1710     }
1711     } else {
1712     $attr{type} = 'enum';
1713 w 1.8 }
1714     for (@grp) {
1715 w 1.9 if ($attr{enum}->{$_}) {
1716     $self->_raise_error ($o, type => 'VC_NO_DUPLICATE_TOKENS', c => $c, t => $_);
1717     } else {
1718     $attr{enum}->{$_} = 1;
1719     $attr{node}->append_new_node (type => '#element',
1720     namespace_uri => $NS{XML}.'attlist',
1721     local_name => 'enum')
1722     ->append_text ($_);
1723     }
1724 w 1.8 }
1725 w 1.9 } else { # attname #somewhat
1726     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1727     $attr{type} = 'CDATA';
1728     next;
1729 w 1.8 }
1730 w 1.9 $attr{node}->set_attribute (type => $attr{type});
1731    
1732     ## DefaultDecl
1733     if ($t =~ s/^$xml_re{s}\#FIXED//s) {
1734     $attr{deftype} = 'FIXED';
1735     $attr{node}->set_attribute (default_type => $attr{deftype});
1736 w 1.8 }
1737 w 1.9 if ($t =~ s/^$xml_re{s}//s) {
1738     if (!$attr{deftype} && $t =~ s/^\#([A-Za-z-]+)//) {
1739     $attr{deftype} = $1;
1740     unless ({qw/IMPLIED 1 REQUIRED 1/}->{$attr{deftype}}) {
1741     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_DEFAULT', c => $c,
1742     t => '#'.$attr{deftype});
1743     }
1744     $attr{node}->set_attribute (default_type => $attr{deftype});
1745     } elsif ($t =~ s/^($xml_re{__AttValue_simple})//s) { # attname type "literal"
1746     $attr{defvalue} = $attr{node}->set_attribute ('default_value');
1747     my $pcdata = substr ($1, 1, length ($1) - 2);
1748     $self->_parse_attr_value_literal_data ($attr{defvalue}, \$pcdata, $o,
1749     entMan => $opt{entMan});
1750     } elsif ($attr{deftype}) { # deftype eq FIXED
1751     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
1752     t => [$attr{name}, substr ($t, 0, 10)]);
1753     next;
1754     } else { # attname type $invalid
1755     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c,
1756     t => substr ($t, 0, 10));
1757     next;
1758 w 1.8 }
1759     } elsif ($attr{deftype}) { # deftype eq FIXED
1760     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
1761     t => [$attr{name}, substr ($t, 0, 10)]);
1762     next;
1763 w 1.9 } else { # attname type$invalid
1764     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1765 w 1.8 next;
1766     }
1767 w 1.9 } else { # attname#somewhat
1768 w 1.8 $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1769     next;
1770     }
1771 w 1.9 } elsif ($t =~ s/$xml_re{s}$//s) {
1772     } else {
1773 w 1.8 $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1774 w 1.9 substr ($t, 0, 1) = '';
1775 w 1.8 }
1776 w 1.9 } # $t
1777     } else { ## dont_process
1778     $c->flag (smxp__non_processed_declaration => 1);
1779     $self->_raise_error ($o, c => $c, type => 'WARN_ATTLIST_DECLARATION_NOT_PROCESSED');
1780     }
1781 wakaba 1.1 }
1782    
1783     sub _is_brother_of_root_element ($$) {
1784     my ($self, $c) = @_;
1785     for (@{$c->child_nodes}) {
1786     if ($_->node_type eq '#element') {
1787     return 1;
1788     }
1789     }
1790     return 0;
1791     }
1792    
1793 wakaba 1.5 ## [internal] URI escaping unsafe characters
1794     ## $s = $parser->_uri_escape ($s)
1795     sub _uri_escape ($$) {
1796     shift;
1797     my $s = shift; ## TODO: support utf8 flag
1798     $s =~ s/([^0-9A-Za-z_.-])/sprintf '%%%02X', ord $1/ge;
1799     $s;
1800     }
1801    
1802     ## [internal] Duplication of HASH reference
1803     ## $ref = $parser->_make_clone_of ($ref)
1804     sub _make_clone_of ($$;%) {
1805     my ($self, $mother, %o) = @_;
1806     if (ref $mother eq 'HASH') {
1807     my $child = {};
1808     $o{m_vs_c}->{$mother} = $child;
1809     for (keys %$mother) {
1810     if (ref ($mother->{$_}) eq 'HASH') {
1811     $child->{$_} = $o{m_vs_c}->{$mother->{$_}} || $self->_make_clone_of ($mother->{$_});
1812     } elsif (index (ref ($mother->{$_}), 'URI') > -1) {
1813     $child->{$_} = $mother->{$_}->clone;
1814     ## BUG: $mother->{$A} === $mother->{$B}, then two clones are created
1815     ## BUG: if CODE, ARRAY, blessed,...
1816     } else {
1817     $child->{$_} = $mother->{$_};
1818     }
1819     }
1820     return $child;
1821     } else {
1822     ## BUG: not supported
1823     }
1824     }
1825    
1826     ## [obsolete] [internal] Count up line/position
1827     ## _count_lp ($string, $o)
1828     sub _count_lp ($$) {
1829     my ($s, $o) = @_;
1830     $s =~ s/[^\x0A\x0D]*(?:\x0D\x0A?|\x0A)/$o->{line}++;$o->{pos}=0;''/ges;
1831     $o->{pos} += length $s;
1832     }
1833     ## [internal] Count up line/position
1834     ## $self->_clp ($string, $o)
1835     sub _clp ($$$) {
1836 wakaba 1.6 my (undef, $s => $o) = @_;
1837     $s =~ s/[^\x0A]*\x0A/$o->{line}++; $o->{pos} = 0; ''/ges;
1838 wakaba 1.5 $o->{pos} += length $s;
1839     }
1840    
1841     ## [internal] Split QName into prefix and NCName
1842     ## ($prefix or undef, $NCName) = $parser->_ns_parse_qname ($QName)
1843     sub _ns_parse_qname ($$) {
1844 wakaba 1.6 my $qname = $_[1];
1845 wakaba 1.5 if ($qname =~ /:/) {
1846     return split /:/, $qname, 2;
1847     } else {
1848     return (undef, $qname);
1849     }
1850     }
1851    
1852 wakaba 1.7
1853     sub option ($$;$) {
1854     my ($self, $name, $value) = @_;
1855     if (defined $value) {
1856     $self->{option}->{$name} = $value;
1857     }
1858     $self->{option}->{$name};
1859     }
1860    
1861     sub flag ($$;$) {
1862     my ($self, $name, $value) = @_;
1863     if (defined $value) {
1864     $self->{flag}->{$name} = $value;
1865     }
1866     $self->{flag}->{$name};
1867     }
1868    
1869 w 1.9 =head1 FLAG NAMES DEFINED BY THIS MODULE
1870    
1871     =head2 Flag for SuikaWiki::Markup::XML instance
1872    
1873     =over 4
1874    
1875     =item smxp__declaration_may_not_be_read = 1/0 (root node)
1876    
1877     Whether after this flag is trued entity/attlist declarations may not
1878     be processed (bacause of declared in external entity or declared after
1879     some external entity reference) or not.
1880    
1881     =item smxp__declaration_may_not_be_read = 1/0 (#declaration node)
1882    
1883     Whether entity/attlist declaration may not be processed or not.
1884    
1885     =item smxp__defined_with_param_ref = 1/0 (#declaration node)
1886    
1887     Whether the declaration is defined with one or more parameter entity or not.
1888    
1889     =item smxp__entity_manager = instance of entity manager (root node)
1890    
1891     =item smxp__entity_replacement_text_md_params = semi-parsed text (Parameter entity #declaration node)
1892    
1893     =item smxp__entity_replacement_text_rpdata = semi-parsed text (Parameter entity #declaration node)
1894    
1895     =item smxp__is_dtd_default = 1/0 (#attribute node)
1896    
1897     Whether the attribute (name and value) is not specified explicily in STag
1898     so completed from DTD's attlist declaration or not.
1899    
1900     =item smxp__non_processed_declaration = 1/0 (Entity/attlist #declaration node)
1901    
1902     Whether the declaration is not read 'cause of smxp__stop_read_dtd'ed or not.
1903    
1904     =item smxp__original_qname = QName as specified in STag
1905    
1906     (Only used to check STag-ETag matching.)
1907    
1908     =item smxp__ref_expanded = 1/0 (#reference node)
1909    
1910     Whether the reference is expanded or not
1911    
1912     =item smxp__src_pos = "$o" object
1913    
1914     Start position in source entity (is this flag obsolete??)
1915    
1916     =item smxp__stop_read_dtd = 1/0 (root node)
1917    
1918     Whether reading entity/attlist declaration is stopped 'cause of
1919     some of external entity required to interpret the DTD is not processed
1920     or not.
1921    
1922     =item smxp__uri_in_which_declaration_is = uri (Entity #declaration node)
1923    
1924     =cut
1925    
1926 wakaba 1.1 =head1 LICENSE
1927    
1928     Copyright 2003 Wakaba <[email protected]>
1929    
1930     This program is free software; you can redistribute it and/or
1931     modify it under the same terms as Perl itself.
1932    
1933     =cut
1934    
1935 w 1.9 1; # $Date: 2003/07/12 06:12:00 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24