diff --git a/runtest.c b/runtest.c index 6961f648d..113e161e4 100644 --- a/runtest.c +++ b/runtest.c @@ -5435,6 +5435,48 @@ done: return ret; } +static int +xmlEntityValueNormTest(const char *filename ATTRIBUTE_UNUSED, + const char *result ATTRIBUTE_UNUSED, + const char *err ATTRIBUTE_UNUSED, + int options ATTRIBUTE_UNUSED) { + int ret = 0; + xmlAttrPtr prop = NULL; + const char *str = "\n" + "\n" + "\n" + "\n" + "]>\n" + ""; + xmlDoc *doc1 = xmlReadDoc(BAD_CAST str, NULL, NULL, 0); + xmlDoc *doc2 = xmlReadDoc(BAD_CAST str, NULL, NULL, XML_PARSE_NOENT); + + prop = doc1->children->next->properties; + while (prop != NULL) { + xmlChar *content1 = xmlGetProp(doc1->children->next, BAD_CAST prop->name); + xmlChar *content2 = xmlGetProp(doc2->children->next, BAD_CAST prop->name); + + if (!xmlStrEqual(content1, content2)) { + ret = 1; + fprintf(stderr, "entity resolution differs %s\n", prop->name); + } + + xmlFree(content1); + xmlFree(content2); + + prop = prop->next; + } + + xmlFreeDoc(doc1); + xmlFreeDoc(doc2); + + return ret; +} /************************************************************************ * * @@ -5684,6 +5726,7 @@ testDesc testDescriptions[] = { xmlTextReaderReadOuterXmlTest, NULL, NULL, NULL, NULL, 0 }, #endif { "xmlCopyEntity children test" , xmlCopyEntityTest, NULL, NULL, NULL, NULL, 0 }, + { "xml Entity values normalization" , xmlEntityValueNormTest, NULL, NULL, NULL, NULL, 0 }, {NULL, NULL, NULL, NULL, NULL, NULL, 0} }; diff --git a/tree.c b/tree.c index dafdb9a70..bb331ba13 100644 --- a/tree.c +++ b/tree.c @@ -77,7 +77,7 @@ xmlGetPropNodeInternal(const xmlNode *node, const xmlChar *name, static xmlChar* xmlGetPropNodeValueInternal(const xmlAttr *prop); static void -xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree); +xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree, int normalize); static void xmlUnlinkNodeInternal(xmlNodePtr cur); @@ -5122,8 +5122,43 @@ xmlNodeBufGetContent(xmlBuffer *buffer, const xmlNode *cur) return(0); } +/** + * TODO: move this to parserInternals? + * TODO: This function should also remove leading and trailing + * whitespaces, and also group whitespaces together, but right + * now the parse doesn't do that with XML_PARSE_NOENT, so this + * replacement is consistent with the parser normalization + * + * Normalize attritube entity values + * Replaces any space character with 0x20 + * https://www.w3.org/TR/REC-xml/#AVNormalize + */ +static xmlChar * +xmlAttrNormalize(xmlChar *src) +{ + xmlChar *out = NULL; + xmlChar *dst = NULL; + + if (src == NULL) + return(NULL); + + out = src; + dst = out; + while (*src != 0) { + if (*src < 0x20) { + src++; + *dst++ = 0x20; + } else { + *dst++ = *src++; + } + } + *dst = 0; + return(out); +} + + static void -xmlBufGetEntityRefContent(xmlBufPtr buf, const xmlNode *ref) { +xmlBufGetEntityRefContent(xmlBufPtr buf, const xmlNode *ref, int normalize) { xmlEntityPtr ent; if (ref->children != NULL) { @@ -5149,23 +5184,26 @@ xmlBufGetEntityRefContent(xmlBufPtr buf, const xmlNode *ref) { return; ent->flags |= XML_ENT_EXPANDING; - xmlBufGetChildContent(buf, (xmlNodePtr) ent); + xmlBufGetChildContent(buf, (xmlNodePtr) ent, normalize); ent->flags &= ~XML_ENT_EXPANDING; } static void -xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree) { +xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree, int normalize) { const xmlNode *cur = tree->children; while (cur != NULL) { switch (cur->type) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: - xmlBufCat(buf, cur->content); + if (normalize) + xmlBufCat(buf, xmlAttrNormalize(cur->content)); + else + xmlBufCat(buf, cur->content); break; case XML_ENTITY_REF_NODE: - xmlBufGetEntityRefContent(buf, cur); + xmlBufGetEntityRefContent(buf, cur, normalize); break; default: @@ -5208,9 +5246,11 @@ xmlBufGetNodeContent(xmlBuf *buf, const xmlNode *cur) case XML_HTML_DOCUMENT_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_ELEMENT_NODE: - case XML_ATTRIBUTE_NODE: case XML_ENTITY_DECL: - xmlBufGetChildContent(buf, cur); + xmlBufGetChildContent(buf, cur, 0); + break; + case XML_ATTRIBUTE_NODE: + xmlBufGetChildContent(buf, cur, 1); break; case XML_CDATA_SECTION_NODE: @@ -5221,7 +5261,7 @@ xmlBufGetNodeContent(xmlBuf *buf, const xmlNode *cur) break; case XML_ENTITY_REF_NODE: - xmlBufGetEntityRefContent(buf, cur); + xmlBufGetEntityRefContent(buf, cur, 0); break; case XML_NAMESPACE_DECL: