1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-12-23 17:33:50 +03:00

fix various attribute normalisation problems reported by Ashwin this

* parser.c: fix various attribute normalisation problems reported
  by Ashwin
* result/c14n/without-comments/example-4
  result/c14n/with-comments/example-4: this impacted the result of
  two c14n tests :-\
* test/att9 test/att10 test/att11 result//att9* result//att10*
  result//att11*: added 3 specific regression tests coming from the
  XML spec revision and from Ashwin
Daniel

svn path=/trunk/; revision=3715
This commit is contained in:
Daniel Veillard 2008-03-25 16:52:41 +00:00
parent e54c3173b8
commit 97c9ce2e99
26 changed files with 519 additions and 52 deletions

View File

@ -1,3 +1,14 @@
Tue Mar 25 17:48:02 CET 2008 Daniel Veillard <daniel@veillard.com>
* parser.c: fix various attribute normalisation problems reported
by Ashwin
* result/c14n/without-comments/example-4
result/c14n/with-comments/example-4: this impacted the result of
two c14n tests :-\
* test/att9 test/att10 test/att11 result//att9* result//att10*
result//att11*: added 3 specific regression tests coming from the
XML spec revision and from Ashwin
Tue Mar 25 14:20:49 CET 2008 Daniel Veillard <daniel@veillard.com>
* uri.c: fix saving for file:///X:/ URI embedding Windows file paths

View File

@ -320,7 +320,8 @@ xmlCtxtGenericNodeCheck(xmlDebugCtxtPtr ctxt, xmlNodePtr node) {
}
if (node->next == NULL) {
if ((node->parent != NULL) && (node->type != XML_ATTRIBUTE_NODE) &&
(node->parent->last != node))
(node->parent->last != node) &&
(node->parent->type == XML_ELEMENT_NODE))
xmlDebugErr(ctxt, XML_CHECK_NO_NEXT,
"Node has no next and not last of parent list\n");
} else {

216
parser.c
View File

@ -862,6 +862,103 @@ struct _xmlDefAttrs {
const xmlChar *values[4]; /* array of localname/prefix/values */
};
/**
* xmlAttrNormalizeSpace:
* @src: the source string
* @dst: the target string
*
* Normalize the space in non CDATA attribute values:
* If the attribute type is not CDATA, then the XML processor MUST further
* process the normalized attribute value by discarding any leading and
* trailing space (#x20) characters, and by replacing sequences of space
* (#x20) characters by a single space (#x20) character.
* Note that the size of dst need to be at least src, and if one doesn't need
* to preserve dst (and it doesn't come from a dictionary or read-only) then
* passing src as dst is just fine.
*
* Returns a pointer to the normalized value (dst) or NULL if no conversion
* is needed.
*/
static xmlChar *
xmlAttrNormalizeSpace(const xmlChar *src, xmlChar *dst)
{
if ((src == NULL) || (dst == NULL))
return(NULL);
while (*src == 0x20) src++;
while (*src != 0) {
if (*src == 0x20) {
while (*src == 0x20) src++;
if (*src != 0)
*dst++ = 0x20;
} else {
*dst++ = *src++;
}
}
*dst = 0;
if (dst == src)
return(NULL);
return(dst);
}
/**
* xmlAttrNormalizeSpace2:
* @src: the source string
*
* Normalize the space in non CDATA attribute values, a slightly more complex
* front end to avoid allocation problems when running on attribute values
* coming from the input.
*
* Returns a pointer to the normalized value (dst) or NULL if no conversion
* is needed.
*/
static const xmlChar *
xmlAttrNormalizeSpace2(xmlParserCtxtPtr ctxt, const xmlChar *src, int *len)
{
int i;
int remove_head = 0;
int need_realloc = 0;
const xmlChar *cur;
if ((ctxt == NULL) || (src == NULL) || (len == NULL))
return(NULL);
i = *len;
if (i <= 0)
return(NULL);
cur = src;
while (*cur == 0x20) {
cur++;
remove_head++;
}
while (*cur != 0) {
if (*cur == 0x20) {
cur++;
if ((*cur == 0x20) || (*cur == 0)) {
need_realloc = 1;
break;
}
} else
cur++;
}
if (need_realloc) {
xmlChar *ret;
ret = xmlStrndup(src + remove_head, i - remove_head + 1);
if (ret == NULL) {
xmlErrMemory(ctxt, NULL);
return(NULL);
}
xmlAttrNormalizeSpace(ret, ret);
*len = (int) strlen((const char *)ret);
return(ret);
} else if (remove_head) {
*len -= remove_head;
return(src + remove_head);
}
return(NULL);
}
/**
* xmlAddDefAttrs:
* @ctxt: an XML parser context
@ -5020,6 +5117,8 @@ xmlParseAttributeListDecl(xmlParserCtxtPtr ctxt) {
xmlFreeEnumeration(tree);
break;
}
if ((type != XML_ATTRIBUTE_CDATA) && (defaultValue != NULL))
xmlAttrNormalizeSpace(defaultValue, defaultValue);
GROW;
if (RAW != '>') {
@ -7900,9 +7999,10 @@ need_complex:
static const xmlChar *
xmlParseAttribute2(xmlParserCtxtPtr ctxt,
const xmlChar *pref, const xmlChar *elem,
const xmlChar **prefix, xmlChar **value,
int *len, int *alloc) {
const xmlChar * pref, const xmlChar * elem,
const xmlChar ** prefix, xmlChar ** value,
int *len, int *alloc)
{
const xmlChar *name;
xmlChar *val, *internal_val = NULL;
int normalize = 0;
@ -7911,9 +8011,9 @@ xmlParseAttribute2(xmlParserCtxtPtr ctxt,
GROW;
name = xmlParseQName(ctxt, prefix);
if (name == NULL) {
xmlFatalErrMsg(ctxt, XML_ERR_NAME_REQUIRED,
"error parsing attribute name\n");
return(NULL);
xmlFatalErrMsg(ctxt, XML_ERR_NAME_REQUIRED,
"error parsing attribute name\n");
return (NULL);
}
/*
@ -7923,8 +8023,9 @@ xmlParseAttribute2(xmlParserCtxtPtr ctxt,
int type;
type = (int) (long) xmlHashQLookup2(ctxt->attsSpecial,
pref, elem, *prefix, name);
if (type != 0) normalize = 1;
pref, elem, *prefix, name);
if (type != 0)
normalize = 1;
}
/*
@ -7933,54 +8034,71 @@ xmlParseAttribute2(xmlParserCtxtPtr ctxt,
SKIP_BLANKS;
if (RAW == '=') {
NEXT;
SKIP_BLANKS;
val = xmlParseAttValueInternal(ctxt, len, alloc, normalize);
ctxt->instate = XML_PARSER_CONTENT;
SKIP_BLANKS;
val = xmlParseAttValueInternal(ctxt, len, alloc, normalize);
if (normalize) {
/*
* Sometimes a second normalisation pass for spaces is needed
* but that only happens if charrefs or entities refernces
* have been used in the attribute value, i.e. the attribute
* value have been extracted in an allocated string already.
*/
if (*alloc) {
const xmlChar *val2;
val2 = xmlAttrNormalizeSpace2(ctxt, val, len);
if (val2 != NULL) {
xmlFree(val);
val = val2;
}
}
}
ctxt->instate = XML_PARSER_CONTENT;
} else {
xmlFatalErrMsgStr(ctxt, XML_ERR_ATTRIBUTE_WITHOUT_VALUE,
"Specification mandate value for attribute %s\n", name);
return(NULL);
xmlFatalErrMsgStr(ctxt, XML_ERR_ATTRIBUTE_WITHOUT_VALUE,
"Specification mandate value for attribute %s\n",
name);
return (NULL);
}
if (*prefix == ctxt->str_xml) {
/*
* Check that xml:lang conforms to the specification
* No more registered as an error, just generate a warning now
* since this was deprecated in XML second edition
*/
if ((ctxt->pedantic) && (xmlStrEqual(name, BAD_CAST "lang"))) {
internal_val = xmlStrndup(val, *len);
if (!xmlCheckLanguageID(internal_val)) {
xmlWarningMsg(ctxt, XML_WAR_LANG_VALUE,
"Malformed value for xml:lang : %s\n",
internal_val, NULL);
}
}
if (*prefix == ctxt->str_xml) {
/*
* Check that xml:lang conforms to the specification
* No more registered as an error, just generate a warning now
* since this was deprecated in XML second edition
*/
if ((ctxt->pedantic) && (xmlStrEqual(name, BAD_CAST "lang"))) {
internal_val = xmlStrndup(val, *len);
if (!xmlCheckLanguageID(internal_val)) {
xmlWarningMsg(ctxt, XML_WAR_LANG_VALUE,
"Malformed value for xml:lang : %s\n",
internal_val, NULL);
}
}
/*
* Check that xml:space conforms to the specification
*/
if (xmlStrEqual(name, BAD_CAST "space")) {
internal_val = xmlStrndup(val, *len);
if (xmlStrEqual(internal_val, BAD_CAST "default"))
*(ctxt->space) = 0;
else if (xmlStrEqual(internal_val, BAD_CAST "preserve"))
*(ctxt->space) = 1;
else {
xmlWarningMsg(ctxt, XML_WAR_SPACE_VALUE,
"Invalid value \"%s\" for xml:space : \"default\" or \"preserve\" expected\n",
internal_val, NULL);
}
}
if (internal_val) {
xmlFree(internal_val);
}
}
/*
* Check that xml:space conforms to the specification
*/
if (xmlStrEqual(name, BAD_CAST "space")) {
internal_val = xmlStrndup(val, *len);
if (xmlStrEqual(internal_val, BAD_CAST "default"))
*(ctxt->space) = 0;
else if (xmlStrEqual(internal_val, BAD_CAST "preserve"))
*(ctxt->space) = 1;
else {
xmlWarningMsg(ctxt, XML_WAR_SPACE_VALUE,
"Invalid value \"%s\" for xml:space : \"default\" or \"preserve\" expected\n",
internal_val, NULL);
}
}
if (internal_val) {
xmlFree(internal_val);
}
}
*value = val;
return(name);
return (name);
}
/**
* xmlParseStartTag2:
* @ctxt: an XML parser context

18
result/att10 Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ELEMENT doc (tst)*>
<!ELEMENT tst (#PCDATA)>
<!ATTLIST tst a NMTOKENS #IMPLIED>
<!ATTLIST tst b CDATA #IMPLIED>
<!ENTITY d "&#xD;">
<!ENTITY a "&#xA;">
<!ENTITY da "&#xD;&#xA;">
]>
<doc>
<tst a="xyz" b=" xyz"/>
<tst a="&d;&d;A&a; &a;B&da;" b="&d;&d;A&a; &a;B&da;"/>
<tst a="&#13;&#13;A&#10;&#10;B&#13;&#10;" b="&#13;&#13;A&#10;&#10;B&#13;&#10;"/>
<tst a="x y" b=" x y "/>
<tst a="a b" b=" a b "/>
<tst a="a b" b=" a b "/>
</doc>

23
result/att10.rde Normal file
View File

@ -0,0 +1,23 @@
0 10 doc 0 0
0 1 doc 0 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
0 15 doc 0 0

23
result/att10.rdr Normal file
View File

@ -0,0 +1,23 @@
0 10 doc 0 0
0 1 doc 0 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
1 1 tst 1 0
1 14 #text 0 1
0 15 doc 0 0

61
result/att10.sax Normal file
View File

@ -0,0 +1,61 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(doc, , )
SAX.elementDecl(doc, 4, ...)
SAX.elementDecl(tst, 3, ...)
SAX.attributeDecl(tst, a, 8, 3, NULL, ...)
SAX.attributeDecl(tst, b, 1, 3, NULL, ...)
SAX.entityDecl(d, 1, (null), (null), )
SAX.getEntity(d)
SAX.entityDecl(a, 1, (null), (null),
)
SAX.getEntity(a)
SAX.entityDecl(da, 1, (null), (null),
)
SAX.getEntity(da)
SAX.externalSubset(doc, , )
SAX.startElement(doc)
SAX.characters(
, 1)
SAX.startElement(tst, a=' xyz', b=' xyz')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.getEntity(d)
SAX.getEntity(d)
SAX.getEntity(a)
SAX.getEntity(a)
SAX.getEntity(da)
SAX.getEntity(d)
SAX.getEntity(d)
SAX.getEntity(a)
SAX.getEntity(a)
SAX.getEntity(da)
SAX.startElement(tst, a='&d;&d;A&a; &a;B&da;', b='&d;&d;A&a; &a;B&da;')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.startElement(tst, a=' A
B
', b=' A
B
')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.startElement(tst, a=' x y ', b=' x y ')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.startElement(tst, a=' a b ', b=' a b ')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.startElement(tst, a=' a b ', b=' a b ')
SAX.endElement(tst)
SAX.characters(
, 1)
SAX.endElement(doc)
SAX.endDocument()

57
result/att10.sax2 Normal file
View File

@ -0,0 +1,57 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(doc, , )
SAX.elementDecl(doc, 4, ...)
SAX.elementDecl(tst, 3, ...)
SAX.attributeDecl(tst, a, 8, 3, NULL, ...)
SAX.attributeDecl(tst, b, 1, 3, NULL, ...)
SAX.entityDecl(d, 1, (null), (null), )
SAX.getEntity(d)
SAX.entityDecl(a, 1, (null), (null),
)
SAX.getEntity(a)
SAX.entityDecl(da, 1, (null), (null),
)
SAX.getEntity(da)
SAX.externalSubset(doc, , )
SAX.startElementNs(doc, NULL, NULL, 0, 0, 0)
SAX.characters(
, 1)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a='xyz"...', 3, b=' xy...', 5)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.getEntity(d)
SAX.getEntity(d)
SAX.getEntity(a)
SAX.getEntity(a)
SAX.getEntity(da)
SAX.getEntity(d)
SAX.getEntity(d)
SAX.getEntity(a)
SAX.getEntity(a)
SAX.getEntity(da)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a='&d;&...', 19, b='&d;&...', 19)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a=' A
...', 8, b=' A
...', 8)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a='x y...', 3, b=' x ...', 6)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a='a b ...', 3, b=' a b...', 5)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.startElementNs(tst, NULL, NULL, 0, 2, 0, a='a b...', 3, b=' a ...', 8)
SAX.endElementNs(tst, NULL, NULL)
SAX.characters(
, 1)
SAX.endElementNs(doc, NULL, NULL)
SAX.endDocument()

9
result/att11 Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE attributes [
<!ELEMENT attributes EMPTY>
<!ATTLIST attributes nmtoken NMTOKEN #IMPLIED>
<!ATTLIST attributes nmtokens NMTOKENS #IMPLIED>
<!ENTITY ent " entity&recursive; ">
<!ENTITY recursive "reference">
]>
<attributes nmtoken="&ent; &ent; &ent;" nmtokens="Test&#13;&#10; this normalization"/>

2
result/att11.rde Normal file
View File

@ -0,0 +1,2 @@
0 10 attributes 0 0
0 1 attributes 1 0

2
result/att11.rdr Normal file
View File

@ -0,0 +1,2 @@
0 10 attributes 0 0
0 1 attributes 1 0

21
result/att11.sax Normal file
View File

@ -0,0 +1,21 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(attributes, , )
SAX.elementDecl(attributes, 1, ...)
SAX.attributeDecl(attributes, nmtoken, 7, 3, NULL, ...)
SAX.attributeDecl(attributes, nmtokens, 8, 3, NULL, ...)
SAX.entityDecl(ent, 1, (null), (null), entity&recursive; )
SAX.getEntity(ent)
SAX.entityDecl(recursive, 1, (null), (null), reference)
SAX.getEntity(recursive)
SAX.externalSubset(attributes, , )
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.startElement(attributes, nmtoken=' &ent; &ent; &ent; ', nmtokens=' Test
this normalization ')
SAX.endElement(attributes)
SAX.endDocument()

20
result/att11.sax2 Normal file
View File

@ -0,0 +1,20 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(attributes, , )
SAX.elementDecl(attributes, 1, ...)
SAX.attributeDecl(attributes, nmtoken, 7, 3, NULL, ...)
SAX.attributeDecl(attributes, nmtokens, 8, 3, NULL, ...)
SAX.entityDecl(ent, 1, (null), (null), entity&recursive; )
SAX.getEntity(ent)
SAX.entityDecl(recursive, 1, (null), (null), reference)
SAX.getEntity(recursive)
SAX.externalSubset(attributes, , )
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.getEntity(ent)
SAX.getEntity(recursive)
SAX.startElementNs(attributes, NULL, NULL, 0, 2, 0, nmtoken='&ent...', 17, nmtokens='Test...', 25)
SAX.endElementNs(attributes, NULL, NULL)
SAX.endDocument()

6
result/att9 Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ATTLIST doc a1 NMTOKENS "1 2">
<!ELEMENT doc (#PCDATA)>
]>
<doc/>

2
result/att9.rde Normal file
View File

@ -0,0 +1,2 @@
0 10 doc 0 0
0 1 doc 1 0

2
result/att9.rdr Normal file
View File

@ -0,0 +1,2 @@
0 10 doc 0 0
0 1 doc 1 0

9
result/att9.sax Normal file
View File

@ -0,0 +1,9 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(doc, , )
SAX.attributeDecl(doc, a1, 8, 1, 1 2, ...)
SAX.elementDecl(doc, 3, ...)
SAX.externalSubset(doc, , )
SAX.startElement(doc)
SAX.endElement(doc)
SAX.endDocument()

9
result/att9.sax2 Normal file
View File

@ -0,0 +1,9 @@
SAX.setDocumentLocator()
SAX.startDocument()
SAX.internalSubset(doc, , )
SAX.attributeDecl(doc, a1, 8, 1, 1 2, ...)
SAX.elementDecl(doc, 3, ...)
SAX.externalSubset(doc, , )
SAX.startElementNs(doc, NULL, NULL, 0, 1, 1, a1='1 2...', 3)
SAX.endElementNs(doc, NULL, NULL)
SAX.endDocument()

View File

@ -5,5 +5,5 @@ Second line</text>
<compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>
<compute expr="value>&quot;0&quot; &amp;&amp; value&lt;&quot;10&quot; ?&quot;valid&quot;:&quot;error&quot;">valid</compute>
<norm attr=" ' &#xD;&#xA;&#x9; ' "></norm>
<normId id="' &#xD;&#xA;&#x9; '"></normId>
<normId id="' &#xD;&#xA;&#x9; '"></normId>
</doc>

View File

@ -5,5 +5,5 @@ Second line</text>
<compute>value&gt;"0" &amp;&amp; value&lt;"10" ?"valid":"error"</compute>
<compute expr="value>&quot;0&quot; &amp;&amp; value&lt;&quot;10&quot; ?&quot;valid&quot;:&quot;error&quot;">valid</compute>
<norm attr=" ' &#xD;&#xA;&#x9; ' "></norm>
<normId id="' &#xD;&#xA;&#x9; '"></normId>
<normId id="' &#xD;&#xA;&#x9; '"></normId>
</doc>

18
result/noent/att10 Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ELEMENT doc (tst)*>
<!ELEMENT tst (#PCDATA)>
<!ATTLIST tst a NMTOKENS #IMPLIED>
<!ATTLIST tst b CDATA #IMPLIED>
<!ENTITY d "&#xD;">
<!ENTITY a "&#xA;">
<!ENTITY da "&#xD;&#xA;">
]>
<doc>
<tst a="xyz" b=" xyz"/>
<tst a="&#13;&#13;A&#10; &#10;B&#13;&#10;" b="&#13;&#13;A&#10; &#10;B&#13;&#10;"/>
<tst a="&#13;&#13;A&#10;&#10;B&#13;&#10;" b="&#13;&#13;A&#10;&#10;B&#13;&#10;"/>
<tst a="x y" b=" x y "/>
<tst a="a b" b=" a b "/>
<tst a="a b" b=" a b "/>
</doc>

9
result/noent/att11 Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE attributes [
<!ELEMENT attributes EMPTY>
<!ATTLIST attributes nmtoken NMTOKEN #IMPLIED>
<!ATTLIST attributes nmtokens NMTOKENS #IMPLIED>
<!ENTITY ent " entity&recursive; ">
<!ENTITY recursive "reference">
]>
<attributes nmtoken="entityreference entityreference entityreference" nmtokens="Test&#13;&#10; this normalization"/>

6
result/noent/att9 Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0"?>
<!DOCTYPE doc [
<!ATTLIST doc a1 NMTOKENS "1 2">
<!ELEMENT doc (#PCDATA)>
]>
<doc/>

22
test/att10 Normal file
View File

@ -0,0 +1,22 @@
<!DOCTYPE doc [
<!ELEMENT doc (tst*)>
<!ELEMENT tst (#PCDATA)>
<!ATTLIST tst a NMTOKENS #IMPLIED>
<!ATTLIST tst b CDATA #IMPLIED>
<!ENTITY d "&#xD;">
<!ENTITY a "&#xA;">
<!ENTITY da "&#xD;&#xA;">
]>
<doc>
<tst a="
xyz" b="
xyz"/>
<tst a="&d;&d;A&a;&#x20;&a;B&da;" b="&d;&d;A&a;&#x20;&a;B&da;"/>
<tst a="&#xd;&#xd;A&#xa;&#xa;B&#xd;&#xa;" b="&#xd;&#xd;A&#xa;&#xa;B&#xd;&#xa;"/>
<tst a="&#32;x&#32;&#32;y&#32;" b="&#32;x&#32;&#32;y&#32;"/>
<tst a=" a b " b=" a b "/>
<tst a=" a b " b=" a b "/>
</doc>

13
test/att11 Normal file
View File

@ -0,0 +1,13 @@
<?xml version='1.0' standalone='yes'?>
<!DOCTYPE attributes [
<!ELEMENT attributes EMPTY>
<!ATTLIST attributes
nmtoken NMTOKEN #IMPLIED
nmtokens NMTOKENS #IMPLIED>
<!ENTITY ent " entity&recursive; ">
<!ENTITY recursive "reference">
]>
<attributes
nmtoken = " &ent; &ent; &ent; "
nmtokens = " Test&#x0d;&#x0a; this&#x20; normalization "
/>

5
test/att9 Normal file
View File

@ -0,0 +1,5 @@
<!DOCTYPE doc [
<!ATTLIST doc a1 NMTOKENS " 1 2 ">
<!ELEMENT doc (#PCDATA)>
]>
<doc/>