/[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.21 - (hide annotations) (download)
Sun Oct 10 06:12:11 2004 UTC (21 years, 9 months ago) by wakaba
Branch: MAIN
Changes since 1.20: +3 -3 lines
Don't warned as uninitialized

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24