/[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.24 - (hide annotations) (download)
Sun Sep 10 11:19:24 2006 UTC (19 years, 10 months ago) by wakaba
Branch: MAIN
CVS Tags: manakai-release-0-4-0, manakai-200612, HEAD
Changes since 1.23: +5 -5 lines
++ manakai/bin/ChangeLog	10 Sep 2006 11:09:00 -0000
2006-09-10  Wakaba  <wakaba@suika.fam.cx>

	* daf.pl (--dtd-suffix, --create-dtd-driver): New options
	for DTD driver support.

++ manakai/lib/Message/Markup/ChangeLog	10 Sep 2006 11:12:09 -0000
2006-09-10  Wakaba  <wakaba@suika.fam.cx>

	* Atom.dis (Atom): The |mv:systemIdentifierBaseURI|
	property is set.  It is an empty value to allow to move
	DTD modules without modification.
	(Atom10): New DTD driver for ordinary Atom 1.0 documents.
	(AtomNameElement, AtomUriElement, AtomEmailElement): References
	for |Atom| module are added for |%ATOM.xmlns.attrib;|
	references in the |ATTLIST| declarations.
	(AtomContentElement): Content attribute definitions
	for |type| and |src| attributes are added.

	* Makefile (atom): Generate |Atom10| DTD driver.

++ manakai/lib/Message/Markup/XML/ChangeLog	10 Sep 2006 11:13:04 -0000
2006-09-10  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm: Comment out Unicode comparibility character
	checking clause since |\p{Compat}| regexp set is not
	supported in the current version of perl.

++ manakai/lib/manakai/ChangeLog	10 Sep 2006 11:19:19 -0000
2006-09-10  Wakaba  <wakaba@suika.fam.cx>

	* DISMarkup.dis (mv:systemIdentifierBaseURI): New property.
	(mv:XMLDTDAnyModule, mv:XMLDTDDriver): New resource types.

	* daf-dtd-modules.pl (daf_dtd_modules): Its main part
	is split into another function named |daf_dm_create_module_file|.
	(daf_dtd_driver): New function for DTD driver support.
	(daf_dm_create_module_file): New function.
	(daf_dm_dtd_driver_content): New function.
	(daf_dm_qname_module_content): What declarations
	are generated is changed so that generated DTD modules
	are more resemble to HTML WG's ones.
	(daf_dm_register_all_components): New function.
	(daf_dm_get_module_group): New function.
	(daf_dm_get_entity_name): Support for DTD drivers is added.  Use
	uppercase'ized name for DTD module sets (to align with
	HTML WG's DTD modules).

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.24 our $VERSION = do{my @r=(q$Revision: 1.23 $=~/\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 wakaba 1.23 if (defined $attr_pfx and $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.23 $self->_clp ((defined $1 ? '___'.$1:'__'.$2) => $o);
1080 wakaba 1.16 } 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 wakaba 1.24 # } elsif ($ch =~ /^(.*?)(\p{Compat})/) {
1246     # $self->_clp ($1 => $o);
1247     # $self->_raise_error ($o, type => 'WARN_UNICODE_COMPAT_CHARACTER', t => ord $2);
1248 wakaba 1.6 } 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.22 my $p = ''; ## notation ? 'n' : parameter entity ? '%' : '';
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.22 unless {'lt|&#60;' => 1, 'gt|&#62;' => 1,
1484     'amp|&#38;' => 1, 'apos|&#39;' => 1,
1485     'quot|&#34;' => 1,
1486     'lt|&#x3c;' => 1, 'gt|&#x3e;' => 1,
1487     'amp|&#x26;' => 1, 'apos|&#x27;' => 1,
1488     'quot|&#x22;' => 1,
1489     'gt|>' => 1, "apos|'" => 1, 'quot|"' => 1,
1490     }->{$ename.'|'.lc ($ev)};
1491 wakaba 1.5 }
1492 wakaba 1.6 }
1493     if ($t =~ s/^$xml_re{s}NDATA//s) {
1494     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10))
1495     unless $t =~ s/^$xml_re{s}//s;
1496     unless ($t =~ s/^($xml_re{Name})//s) {
1497     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1498     } else {
1499     my $nname = $1;
1500     if ($p eq '%') { ## parameter entity
1501     $self->_raise_error ($o, c => $e, type => 'SYNTAX_PE_NDATA', t => $nname);
1502     } elsif ($is_internal) {
1503     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_KEYWORD', t => 'NDATA');
1504     } else {
1505     $e->set_attribute (NDATA => $nname)
1506     ->flag (smxp__src_pos => $o);
1507     }
1508 wakaba 1.5 }
1509 wakaba 1.2 }
1510 wakaba 1.6 $t =~ s/^$xml_re{s}//s;
1511     if (length $t) {
1512     $self->_raise_error ($o, c => $e, type => 'SYNTAX_INVALID_MD', t => $t);
1513     }
1514 wakaba 1.5 } else { ## dont_process
1515     $c->flag (smxp__non_processed_declaration => 1);
1516 wakaba 1.6 $self->_raise_error ($o, c => $c, type => 'WARN_ENTITY_DECLARATION_NOT_PROCESSED');
1517 wakaba 1.1 }
1518     }
1519    
1520 w 1.8 sub _parse_element_declaration ($$$$;%) {
1521     my ($self, $s, $c, $o, %opt) = (@_);
1522     $c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'element');
1523     unless ($$s =~ s/^<!ELEMENT//s) {
1524     $self->_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
1525     return;
1526     }
1527     $self->_clp (_________ => $o);
1528    
1529     my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
1530    
1531     ## Element type name
1532     if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
1533     my $type_qname = $2;
1534     if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
1535     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
1536     $type_qname =~ tr/:/_/;
1537 wakaba 1.1 }
1538 w 1.10 if ($opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'element',
1539     seek => 0)) {
1540     $self->_raise_error ($o, c => $c,
1541     type => 'VC_UNIQUE_ELEMENT_TYPE_NAME', t => $type_qname);
1542     } else { ## Regist to entMan
1543     $opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'element',
1544 w 1.11 set_value => $c, seek => 0);
1545 w 1.10 }
1546 w 1.8 $c->set_attribute (qname => $type_qname);
1547     } else {
1548     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1549     return;
1550     }
1551    
1552 wakaba 1.14 ## No content model declaration
1553     $t =~ s/^$xml_re{s}//s;
1554     unless ($t) {
1555     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1556     return;
1557     }
1558    
1559 w 1.8 ## Content model
1560     my $delimited = 1;
1561     my $in_group = 0;
1562     my @grp_connector;
1563     my $is_pcdata;
1564     my $r = $c;
1565     while ($t) {
1566     if ($t =~ s/^$xml_re{s}//s) {
1567     #
1568     } elsif ($t =~ s/^($xml_re{Name})//) {
1569     my $name = $1;
1570     if ($in_group) {
1571     unless ($delimited) {
1572     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1573     t => $name);
1574     } else {
1575     $delimited = 0;
1576     }
1577     if (substr ($name, 0, 1) eq ':' || substr ($name, -1, 1) eq ':') {
1578     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $name);
1579     $name =~ tr/:/_/;
1580     }
1581 w 1.11 my $enode = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
1582     local_name => 'element');
1583     $enode->set_attribute (qname => $name);
1584 w 1.8 if ($t =~ s/^([+*?])//) {
1585 w 1.11 $enode->set_attribute (occurence => $1);
1586 w 1.8 }
1587     } else {
1588     if ({qw/EMPTY 1 ANY 1/}->{$name}) {
1589     #
1590     } elsif ({qw/PCDATA 1 CDATA 1/}->{$name}) {
1591     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_KWD',
1592     t => $name);
1593     } else {
1594     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
1595     t => $name);
1596     }
1597     $r->set_attribute (content => $name);
1598     last;
1599     } # out of group
1600     } elsif ($in_group && $t =~ s/^\#($xml_re{Name})//) {
1601     my $kwd = $1;
1602     unless ($delimited) {
1603     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1604     t => $kwd);
1605     } else {
1606     $delimited = 0;
1607     }
1608     if ($in_group > 1 || $grp_connector[$in_group]) {
1609     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_KWD_POSITION',
1610     t => $kwd);
1611     }
1612     if ($kwd ne 'PCDATA') {
1613     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_UNKNOWN_KWD',
1614     t => $kwd);
1615     } else {
1616     $is_pcdata = 1;
1617     $r->set_attribute (content => 'mixed');
1618     }
1619     } elsif ($in_group && $t =~ s/^([|,&])//) {
1620     my $connector = $1;
1621     if ($delimited) {
1622     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
1623     t => $connector.substr ($t, 0, 9));
1624     } else {
1625     $delimited = 1;
1626     }
1627     if ($connector eq '&') {
1628     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SGML_CONNECTOR',
1629     t => $connector);
1630     $connector = ',';
1631     } elsif ($is_pcdata && $connector ne '|') {
1632     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_PCDATA_CONNECTOR',
1633     t => $connector);
1634     $connector = '|';
1635     }
1636     unless ($grp_connector[$in_group]) {
1637     $grp_connector[$in_group] = $connector;
1638     $c->set_attribute (connector => $connector);
1639     } elsif ($grp_connector[$in_group] ne $connector) {
1640     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_SAME_CONNECTOR',
1641     t => [$grp_connector[$in_group], $connector]);
1642     }
1643     } elsif ($t =~ s/^\(//) {
1644     unless ($delimited) {
1645     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_NO_DELIMITER',
1646     t => '('.substr ($t, 0, 9));
1647     }
1648     if ($is_pcdata) {
1649     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_NESTED');
1650     } else {
1651     $c = $c->append_new_node (type => '#element', namespace_uri => $NS{SGML}.'element',
1652     local_name => 'group');
1653     }
1654     $in_group++;
1655     $grp_connector[$in_group] = undef;
1656     $delimited = 1;
1657     } elsif ($in_group && $t =~ s/^\)//) {
1658     if ($delimited) {
1659     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_INVALID_CONNECTOR',
1660     t => ')'.substr ($t, 0, 9));
1661     $delimited = 0;
1662     }
1663     if ($t =~ s/^([+*?])//) {
1664     my $occur = $1;
1665     if ($is_pcdata && $occur ne '*') {
1666     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE',
1667     t => $occur);
1668     }
1669     $c->set_attribute (occurence => $occur);
1670     } elsif ($is_pcdata && $grp_connector[$in_group]) {
1671     $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_MIXED_OCCURENCE');
1672     }
1673     $c = $c->parent_node;
1674     $in_group--;
1675     last if !$in_group;
1676     } else {
1677     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1678     substr ($t, 0, 1) = '';
1679     }
1680     } # $t
1681     if ($in_group) {
1682 wakaba 1.14 ## Note: Maybe this error will not happen since md_params parsing report it
1683 w 1.8 $self->_raise_error ($o, c => $c, type => 'SYNTAX_ELEMENT_CMODEL_GROUP_NOT_CLOSED');
1684     }
1685    
1686     if ($t =~ /[^$xml_re{_s__chars}]/) {
1687     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => $t);
1688     }
1689 wakaba 1.1 }
1690    
1691    
1692 w 1.8 sub _parse_attlist_declaration ($$$$;%) {
1693     my ($self, $s, $c, $o, %opt) = (@_);
1694 wakaba 1.17 my $root_node = $c->root_node;
1695 w 1.8 $c = $c->append_new_node (type => '#declaration', namespace_uri => $NS{SGML}.'attlist');
1696 w 1.9 $c->flag (smxp__declaration_may_not_be_read
1697 wakaba 1.17 => $root_node->flag ('smxp__declaration_may_not_be_read'));
1698 w 1.8 unless ($$s =~ s/^<!ATTLIST//s) {
1699     $self->_raise_error ($o, type => 'UNKNOWN', c => $c, t => substr ($$s, 0, 10));
1700     return;
1701     }
1702     $self->_clp (_________ => $o);
1703    
1704 wakaba 1.17 my $dont_process = $root_node->flag ('smxp__stop_read_dtd');
1705 w 1.8 my $t = $self->_parse_md_params ($c, $s, $o, entMan => $opt{entMan}, return_by_mdc => 1);
1706 wakaba 1.17 $dont_process = $root_node->flag ('smxp__stop_read_dtd');
1707 w 1.8
1708 w 1.9 unless ($dont_process) {
1709     ## Element type name
1710 w 1.10 my $type_qname;
1711 w 1.9 if ($t =~ s/^($xml_re{s}($xml_re{Name}))//s) {
1712 w 1.10 $type_qname = $2;
1713 w 1.9 if (substr ($type_qname, 0, 1) eq ':' || substr ($type_qname, -1, 1) eq ':') {
1714     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $type_qname);
1715     $type_qname =~ tr/:/_/;
1716     }
1717 w 1.10 if ($opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'attlist',
1718     dont_use_predefined_entities => 1,
1719     seek => 0)) {
1720     $self->_raise_error ($o, c => $c,
1721     type => 'WARN_XML_ATTLIST_AT_MOST_ONE_DECLARATION', t => $type_qname);
1722     } else { ## Regist to entMan
1723     $opt{entMan}->is_declared_entity ($type_qname, namespace_uri => $NS{SGML}.'attlist',
1724     dont_use_predefined_entities => 1,
1725 wakaba 1.17 set_value => $c, seek => 0)
1726 w 1.10 }
1727 w 1.9 $c->set_attribute (qname => $type_qname);
1728     } else {
1729     $self->_raise_error ($o, c => $c, type => 'SYNTAX_INVALID_MD', t => substr ($t, 0, 10));
1730     return;
1731 wakaba 1.1 }
1732 w 1.9
1733     ## Definition
1734     my %defined;
1735     while ($t) {
1736     if ($t =~ s/^$xml_re{s}($xml_re{Name})//s) {
1737     my %attr = (name => $1, type => undef);
1738     if (substr ($attr{name}, 0, 1) eq ':' || substr ($attr{name}, -1, 1) eq ':') {
1739     $self->_raise_error ($o, c => $c, type => 'NS_SYNTAX_NAME_IS_QNNAME', t => $attr{name});
1740     $attr{name} =~ tr/:/_/;
1741     }
1742     if ($defined{$attr{name}}) {
1743     $self->_raise_error ($o, c => $c, type => 'WARN_XML_ATTLIST_AT_MOST_ONE_ATTR_DEF',
1744     t => $attr{name});
1745     } else {
1746     $defined{$attr{name}} = 1;
1747     }
1748     $attr{node} = $c->append_new_node (type => '#element', namespace_uri => $NS{XML}.'attlist',
1749     local_name => 'AttDef');
1750     $attr{node}->set_attribute (qname => $attr{name});
1751     if ($t =~ s/^$xml_re{s}//s) {
1752 w 1.11 if ($t =~ s/^NOTATION//) {
1753 w 1.9 $attr{type} = 'NOTATION';
1754 w 1.11 unless ($t =~ s/^$xml_re{s}//s) {
1755     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c,
1756     t => substr ($t, 0, 10));
1757     }
1758 w 1.8 }
1759 w 1.9 if (!$attr{type} && $t =~ s/^([A-Za-z]+)//) { # attname type
1760     $attr{type} = $1;
1761     unless ({qw/CDATA 1 ID 1 IDREF 1 IDREFS 1 NMTOKEN 1 NMTOKENS 1
1762     ENTITY 1 ENTITIES 1/}->{$attr{type}}) {
1763     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_TYPE',
1764     c => $c, t => $attr{type});
1765     }
1766     } elsif ($t =~ s/^\(([^)]+)\)//) { # attname (group)
1767     my $grp = $1;
1768     if (index ($grp, '(') > -1) {
1769     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NESTED_GROUP',
1770     c => $c, t => $grp);
1771     }
1772     if (index ($grp, '&') > -1 || index ($grp, ',') > -1) {
1773     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_NON_BAR_CONNECTOR',
1774     c => $c, t => $grp);
1775     }
1776     if ($grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//s) {
1777     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CHAR',
1778 w 1.8 c => $c, t => $1);
1779 w 1.9 $grp =~ s/([^\p{InXMLNameChar}$xml_re{_s__chars}|])//sg;
1780     } else {
1781     $grp =~ tr/\x09\x0A\x0D\x20//d; # $xml_re{_s__chars}
1782     if ($grp =~ /(^\||\|\||\|$)/) {
1783     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_INVALID_CONNECTOR',
1784     c => $c, t => $1);
1785     }
1786     }
1787     my @grp = grep {$_} split /\P{InXMLNameChar}+/, $grp;
1788     if ($attr{type}) { ## NOTATION
1789     for (@grp) {
1790     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_GROUP_NOTATION_NAME',
1791     c => $c, t => $_)
1792     unless /^$xml_re{Name}$/;
1793     }
1794     } else {
1795     $attr{type} = 'enum';
1796 w 1.8 }
1797     for (@grp) {
1798 w 1.9 if ($attr{enum}->{$_}) {
1799     $self->_raise_error ($o, type => 'VC_NO_DUPLICATE_TOKENS', c => $c, t => $_);
1800     } else {
1801     $attr{enum}->{$_} = 1;
1802     $attr{node}->append_new_node (type => '#element',
1803     namespace_uri => $NS{XML}.'attlist',
1804     local_name => 'enum')
1805     ->append_text ($_);
1806     }
1807 w 1.8 }
1808 w 1.11 } else { # attname #somewhat or attname NOTATION non-group
1809 w 1.9 $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1810 w 1.11 $attr{type} ||= 'CDATA';
1811 w 1.9 next;
1812 w 1.8 }
1813 w 1.9 $attr{node}->set_attribute (type => $attr{type});
1814    
1815     ## DefaultDecl
1816     if ($t =~ s/^$xml_re{s}\#FIXED//s) {
1817     $attr{deftype} = 'FIXED';
1818     $attr{node}->set_attribute (default_type => $attr{deftype});
1819 w 1.8 }
1820 w 1.9 if ($t =~ s/^$xml_re{s}//s) {
1821     if (!$attr{deftype} && $t =~ s/^\#([A-Za-z-]+)//) {
1822     $attr{deftype} = $1;
1823     unless ({qw/IMPLIED 1 REQUIRED 1/}->{$attr{deftype}}) {
1824     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_UNKNOWN_DEFAULT', c => $c,
1825     t => '#'.$attr{deftype});
1826     }
1827     $attr{node}->set_attribute (default_type => $attr{deftype});
1828     } elsif ($t =~ s/^($xml_re{__AttValue_simple})//s) { # attname type "literal"
1829     $attr{defvalue} = $attr{node}->set_attribute ('default_value');
1830     my $pcdata = substr ($1, 1, length ($1) - 2);
1831     $self->_parse_attr_value_literal_data ($attr{defvalue}, \$pcdata, $o,
1832     entMan => $opt{entMan});
1833     } elsif ($attr{deftype}) { # deftype eq FIXED
1834     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
1835     t => [$attr{name}, substr ($t, 0, 10)]);
1836     next;
1837     } else { # attname type $invalid
1838     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c,
1839     t => substr ($t, 0, 10));
1840     next;
1841 w 1.8 }
1842     } elsif ($attr{deftype}) { # deftype eq FIXED
1843     $self->_raise_error ($o, type => 'SYNTAX_ATTLIST_ATTDEF_FIXED_NO_LITERAL', c => $c,
1844     t => [$attr{name}, substr ($t, 0, 10)]);
1845     next;
1846 w 1.9 } else { # attname type$invalid
1847     $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1848 w 1.8 next;
1849     }
1850 w 1.9 } else { # attname#somewhat
1851 w 1.8 $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1852     next;
1853     }
1854 w 1.9 } elsif ($t =~ s/$xml_re{s}$//s) {
1855     } else {
1856 w 1.8 $self->_raise_error ($o, type => 'SYNTAX_INVALID_MD', c => $c, t => substr ($t, 0, 10));
1857 w 1.9 substr ($t, 0, 1) = '';
1858 w 1.8 }
1859 w 1.9 } # $t
1860 w 1.10 unless (scalar keys %defined) {
1861     $self->_raise_error ($o, c => $c, type => 'WARN_XML_ATTLIST_AT_LEAST_ONE_ATTR_DEF',
1862     t => $type_qname);
1863     }
1864 w 1.9 } else { ## dont_process
1865     $c->flag (smxp__non_processed_declaration => 1);
1866     $self->_raise_error ($o, c => $c, type => 'WARN_ATTLIST_DECLARATION_NOT_PROCESSED');
1867     }
1868 wakaba 1.1 }
1869    
1870     sub _is_brother_of_root_element ($$) {
1871     my ($self, $c) = @_;
1872     for (@{$c->child_nodes}) {
1873     if ($_->node_type eq '#element') {
1874     return 1;
1875     }
1876     }
1877     return 0;
1878     }
1879    
1880 wakaba 1.5 ## [internal] URI escaping unsafe characters
1881     ## $s = $parser->_uri_escape ($s)
1882     sub _uri_escape ($$) {
1883     shift;
1884     my $s = shift; ## TODO: support utf8 flag
1885     $s =~ s/([^0-9A-Za-z_.-])/sprintf '%%%02X', ord $1/ge;
1886     $s;
1887     }
1888    
1889     ## [internal] Duplication of HASH reference
1890     ## $ref = $parser->_make_clone_of ($ref)
1891     sub _make_clone_of ($$;%) {
1892     my ($self, $mother, %o) = @_;
1893     if (ref $mother eq 'HASH') {
1894     my $child = {};
1895     $o{m_vs_c}->{$mother} = $child;
1896     for (keys %$mother) {
1897     if (ref ($mother->{$_}) eq 'HASH') {
1898     $child->{$_} = $o{m_vs_c}->{$mother->{$_}} || $self->_make_clone_of ($mother->{$_});
1899     } elsif (index (ref ($mother->{$_}), 'URI') > -1) {
1900     $child->{$_} = $mother->{$_}->clone;
1901     ## BUG: $mother->{$A} === $mother->{$B}, then two clones are created
1902     ## BUG: if CODE, ARRAY, blessed,...
1903     } else {
1904     $child->{$_} = $mother->{$_};
1905     }
1906     }
1907     return $child;
1908     } else {
1909     ## BUG: not supported
1910     }
1911     }
1912    
1913     ## [internal] Count up line/position
1914     ## $self->_clp ($string, $o)
1915     sub _clp ($$$) {
1916 wakaba 1.6 my (undef, $s => $o) = @_;
1917 wakaba 1.16 $s =~ s/\G[^\x0A]*\x0A/($o->{line}++, $o->{pos} = 0, '')/ges;
1918 wakaba 1.5 $o->{pos} += length $s;
1919     }
1920    
1921     ## [internal] Split QName into prefix and NCName
1922     ## ($prefix or undef, $NCName) = $parser->_ns_parse_qname ($QName)
1923     sub _ns_parse_qname ($$) {
1924 wakaba 1.6 my $qname = $_[1];
1925 wakaba 1.5 if ($qname =~ /:/) {
1926     return split /:/, $qname, 2;
1927     } else {
1928     return (undef, $qname);
1929     }
1930     }
1931    
1932 wakaba 1.7
1933     sub option ($$;$) {
1934     my ($self, $name, $value) = @_;
1935     if (defined $value) {
1936     $self->{option}->{$name} = $value;
1937     }
1938     $self->{option}->{$name};
1939     }
1940    
1941     sub flag ($$;$) {
1942     my ($self, $name, $value) = @_;
1943     if (defined $value) {
1944     $self->{flag}->{$name} = $value;
1945     }
1946     $self->{flag}->{$name};
1947     }
1948    
1949 w 1.9 =head1 FLAG NAMES DEFINED BY THIS MODULE
1950    
1951 wakaba 1.12 =head2 Flag for Message::Markup::XML instance
1952 w 1.9
1953     =over 4
1954    
1955     =item smxp__declaration_may_not_be_read = 1/0 (root node)
1956    
1957     Whether after this flag is trued entity/attlist declarations may not
1958     be processed (bacause of declared in external entity or declared after
1959     some external entity reference) or not.
1960    
1961     =item smxp__declaration_may_not_be_read = 1/0 (#declaration node)
1962    
1963     Whether entity/attlist declaration may not be processed or not.
1964    
1965     =item smxp__defined_with_param_ref = 1/0 (#declaration node)
1966    
1967     Whether the declaration is defined with one or more parameter entity or not.
1968    
1969     =item smxp__entity_manager = instance of entity manager (root node)
1970    
1971     =item smxp__entity_replacement_text_md_params = semi-parsed text (Parameter entity #declaration node)
1972    
1973     =item smxp__entity_replacement_text_rpdata = semi-parsed text (Parameter entity #declaration node)
1974    
1975     =item smxp__is_dtd_default = 1/0 (#attribute node)
1976    
1977     Whether the attribute (name and value) is not specified explicily in STag
1978     so completed from DTD's attlist declaration or not.
1979    
1980     =item smxp__non_processed_declaration = 1/0 (Entity/attlist #declaration node)
1981    
1982     Whether the declaration is not read 'cause of smxp__stop_read_dtd'ed or not.
1983    
1984     =item smxp__original_qname = QName as specified in STag
1985    
1986     (Only used to check STag-ETag matching.)
1987    
1988     =item smxp__ref_expanded = 1/0 (#reference node)
1989    
1990     Whether the reference is expanded or not
1991    
1992     =item smxp__src_pos = "$o" object
1993    
1994     Start position in source entity (is this flag obsolete??)
1995    
1996     =item smxp__stop_read_dtd = 1/0 (root node)
1997    
1998     Whether reading entity/attlist declaration is stopped 'cause of
1999     some of external entity required to interpret the DTD is not processed
2000     or not.
2001    
2002     =item smxp__uri_in_which_declaration_is = uri (Entity #declaration node)
2003    
2004     =cut
2005    
2006 wakaba 1.1 =head1 LICENSE
2007    
2008     Copyright 2003 Wakaba <[email protected]>
2009    
2010     This program is free software; you can redistribute it and/or
2011     modify it under the same terms as Perl itself.
2012    
2013     =cut
2014    
2015 wakaba 1.24 1; # $Date: 2005/11/16 10:07:13 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24