1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2025-03-27 18:50:07 +03:00

parser: Implement xmlCtxtParseContent

This implements xmlCtxtParseContent, a better alternative to
xmlParseInNodeContext or xmlParseBalancedChunkMemory. It accepts a
parser context and a parser input, making it a lot more versatile.

xmlParseInNodeContext is now implemented in terms of
xmlCtxtParseContent. This makes sure that xmlParseInNodeContext never
modifies the target document, improving thread safety.
xmlParseInNodeContext is also more lenient now with regard to undeclared
entities.

Fixes #727.
This commit is contained in:
Nick Wellnhofer 2024-07-10 03:27:47 +02:00
parent 673ca0edaf
commit 4f329dc524
9 changed files with 527 additions and 230 deletions

View File

@ -4716,18 +4716,50 @@ htmlParseContentInternal(htmlParserCtxtPtr ctxt) {
if (currentNode != NULL) xmlFree(currentNode);
}
/**
* htmlParseContent:
* @ctxt: an HTML parser context
*
* Parse a content: comment, sub-element, reference or text.
* This is the entry point when called from parser.c
*/
xmlNodePtr
htmlCtxtParseContentInternal(htmlParserCtxtPtr ctxt, xmlParserInputPtr input) {
xmlNodePtr root;
xmlNodePtr list = NULL;
xmlChar *rootName = BAD_CAST "#root";
void
__htmlParseContent(void *ctxt) {
if (ctxt != NULL)
htmlParseContentInternal((htmlParserCtxtPtr) ctxt);
root = xmlNewDocNode(ctxt->myDoc, NULL, rootName, NULL);
if (root == NULL) {
htmlErrMemory(ctxt);
return(NULL);
}
if (xmlPushInput(ctxt, input) < 0) {
xmlFreeNode(root);
return(NULL);
}
htmlnamePush(ctxt, rootName);
nodePush(ctxt, root);
htmlParseContentInternal(ctxt);
/* TODO: Use xmlCtxtIsCatastrophicError */
if (ctxt->errNo != XML_ERR_NO_MEMORY) {
xmlNodePtr cur;
/*
* Unlink newly created node list.
*/
list = root->children;
root->children = NULL;
root->last = NULL;
for (cur = list; cur != NULL; cur = cur->next)
cur->parent = NULL;
}
nodePop(ctxt);
htmlnamePop(ctxt);
/* xmlPopInput would free the stream */
inputPop(ctxt);
xmlFreeNode(root);
return(list);
}
/**

View File

@ -691,6 +691,7 @@
<exports symbol='xmlCtxtGetStandalone' type='function'/>
<exports symbol='xmlCtxtGetStatus' type='function'/>
<exports symbol='xmlCtxtGetVersion' type='function'/>
<exports symbol='xmlCtxtParseContent' type='function'/>
<exports symbol='xmlCtxtParseDocument' type='function'/>
<exports symbol='xmlCtxtReadDoc' type='function'/>
<exports symbol='xmlCtxtReadFd' type='function'/>
@ -8714,6 +8715,14 @@ crash if you try to modify the tree)'/>
<return type='const xmlChar *' info='the version from the XML declaration.'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info=''/>
</function>
<function name='xmlCtxtParseContent' file='parser' module='parser'>
<info>Parse a well-balanced chunk of XML matching the &apos;content&apos; production. Namespaces in scope of @node and entities of @node&apos;s document are recognized. When validating, the DTD of @node&apos;s document is used. Always consumes @input even in error case. Available since 2.14.0.</info>
<return type='xmlNodePtr' info='a node list or NULL in case of error.'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info='parser context'/>
<arg name='input' type='xmlParserInputPtr' info='parser input'/>
<arg name='node' type='xmlNodePtr' info='target node or document'/>
<arg name='hasTextDecl' type='int' info='whether to parse text declaration'/>
</function>
<function name='xmlCtxtParseDocument' file='parser' module='parser'>
<info>Parse an XML document and return the resulting document tree. Takes ownership of the input object. Available since 2.13.0.</info>
<return type='xmlDocPtr' info='the resulting document tree or NULL'/>
@ -11314,7 +11323,7 @@ crash if you try to modify the tree)'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info='an XML parser context'/>
</function>
<function name='xmlParseContent' file='parserInternals' module='parser'>
<info>Parse XML element content. This is useful if you&apos;re only interested in custom SAX callbacks. If you want a node list, use xmlParseInNodeContext.</info>
<info>Parse XML element content. This is useful if you&apos;re only interested in custom SAX callbacks. If you want a node list, use xmlCtxtParseContent.</info>
<return type='void'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info='an XML parser context'/>
</function>
@ -11471,13 +11480,13 @@ crash if you try to modify the tree)'/>
<arg name='filename' type='const char *' info='the filename'/>
</function>
<function name='xmlParseInNodeContext' file='parser' module='parser'>
<info>Parse a well-balanced chunk of an XML document within the context (DTD, namespaces, etc ...) of the given node. The allowed sequence for the data is a Well Balanced Chunk defined by the content production in the XML grammar: [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)*</info>
<info>Parse a well-balanced chunk of an XML document within the context (DTD, namespaces, etc ...) of the given node. The allowed sequence for the data is a Well Balanced Chunk defined by the content production in the XML grammar: [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* This function assumes the encoding of @node&apos;s document which is typically not what you want. A better alternative is xmlCtxtParseContent.</info>
<return type='xmlParserErrors' info='XML_ERR_OK if the chunk is well balanced, and the parser error code otherwise'/>
<arg name='node' type='xmlNodePtr' info='the context node'/>
<arg name='data' type='const char *' info='the input string'/>
<arg name='datalen' type='int' info='the input string length in bytes'/>
<arg name='options' type='int' info='a combination of xmlParserOption'/>
<arg name='lst' type='xmlNodePtr *' info='the return value for the set of parsed nodes'/>
<arg name='listOut' type='xmlNodePtr *' info='the return value for the set of parsed nodes'/>
</function>
<function name='xmlParseMarkupDecl' file='parserInternals' module='parser'>
<info>DEPRECATED: Internal function, don&apos;t use. Parse markup declarations. Always consumes &apos;&lt;!&apos; or &apos;&lt;?&apos;. [29] markupdecl ::= elementdecl | AttlistDecl | EntityDecl | NotationDecl | PI | Comment [ VC: Proper Declaration/PE Nesting ] Parameter-entity replacement text must be properly nested with markup declarations. That is to say, if either the first character or the last character of a markup declaration (markupdecl above) is contained in the replacement text for a parameter-entity reference, both must be contained in the same replacement text. [ WFC: PEs in Internal Subset ] In the internal DTD subset, parameter-entity references can occur only where markup declarations can occur, not within markup declarations. (This does not apply to references that occur in external parameter entities or to the external subset.)</info>

View File

@ -1480,6 +1480,11 @@ XMLPUBFUN xmlDocPtr
XMLPUBFUN xmlDocPtr
xmlCtxtParseDocument (xmlParserCtxtPtr ctxt,
xmlParserInputPtr input);
XMLPUBFUN xmlNodePtr
xmlCtxtParseContent (xmlParserCtxtPtr ctxt,
xmlParserInputPtr input,
xmlNodePtr node,
int hasTextDecl);
XMLPUBFUN xmlDocPtr
xmlCtxtReadDoc (xmlParserCtxtPtr ctxt,
const xmlChar *cur,

View File

@ -5,8 +5,8 @@
#ifdef LIBXML_HTML_ENABLED
XML_HIDDEN void
__htmlParseContent(void *ctx);
XML_HIDDEN xmlNodePtr
htmlCtxtParseContentInternal(xmlParserCtxtPtr ctxt, xmlParserInputPtr input);
#endif /* LIBXML_HTML_ENABLED */

389
parser.c
View File

@ -7573,7 +7573,7 @@ xmlHandleUndeclaredEntity(xmlParserCtxtPtr ctxt, const xmlChar *name) {
*/
xmlValidityError(ctxt, XML_ERR_UNDECLARED_ENTITY,
"Entity '%s' not defined\n", name, NULL);
} else if ((ctxt->loadsubset) ||
} else if ((ctxt->loadsubset & ~XML_SKIP_IDS) ||
((ctxt->replaceEntities) &&
((ctxt->options & XML_PARSE_NO_XXE) == 0))) {
/*
@ -9774,7 +9774,7 @@ xmlParseContentInternal(xmlParserCtxtPtr ctxt) {
*
* Parse XML element content. This is useful if you're only interested
* in custom SAX callbacks. If you want a node list, use
* xmlParseInNodeContext.
* xmlCtxtParseContent.
*/
void
xmlParseContent(xmlParserCtxtPtr ctxt) {
@ -12000,8 +12000,8 @@ xmlParseDTD(const xmlChar *ExternalID, const xmlChar *SystemID) {
************************************************************************/
static xmlNodePtr
xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
int hasTextDecl, int buildTree) {
xmlCtxtParseContentInternal(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
int hasTextDecl, int buildTree) {
xmlNodePtr root = NULL;
xmlNodePtr list = NULL;
xmlChar *rootName = BAD_CAST "#root";
@ -12056,17 +12056,13 @@ xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
xmlNodePtr cur;
/*
* Return the newly created nodeset after unlinking it from
* its pseudo parent.
* Unlink newly created node list.
*/
cur = root->children;
list = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
list = root->children;
root->children = NULL;
root->last = NULL;
for (cur = list; cur != NULL; cur = cur->next)
cur->parent = NULL;
}
}
@ -12143,7 +12139,7 @@ xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) {
*
* This initiates a recursive call chain:
*
* - xmlCtxtParseContent
* - xmlCtxtParseContentInternal
* - xmlParseContentInternal
* - xmlParseReference
* - xmlCtxtParseEntity
@ -12157,7 +12153,7 @@ xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) {
ent->flags |= XML_ENT_EXPANDING;
list = xmlCtxtParseContent(ctxt, input, isExternal, buildTree);
list = xmlCtxtParseContentInternal(ctxt, input, isExternal, buildTree);
ent->flags &= ~XML_ENT_EXPANDING;
@ -12232,7 +12228,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctxt, const xmlChar *URL,
xmlCtxtInitializeLate(ctxt);
list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 1, 1);
list = xmlCtxtParseContentInternal(ctxt, input, /* hasTextDecl */ 1, 1);
if (listOut != NULL)
*listOut = list;
else
@ -12317,13 +12313,164 @@ xmlParseBalancedChunkMemory(xmlDocPtr doc, xmlSAXHandlerPtr sax,
}
#endif /* LIBXML_SAX1_ENABLED */
/**
* xmlCtxtParseContent:
* @ctxt: parser context
* @input: parser input
* @node: target node or document
* @hasTextDecl: whether to parse text declaration
*
* Parse a well-balanced chunk of XML matching the 'content' production.
*
* Namespaces in scope of @node and entities of @node's document are
* recognized. When validating, the DTD of @node's document is used.
*
* Always consumes @input even in error case.
*
* Available since 2.14.0.
*
* Returns a node list or NULL in case of error.
*/
xmlNodePtr
xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
xmlNodePtr node, int hasTextDecl) {
xmlDocPtr doc;
xmlNodePtr cur, list = NULL;
int nsnr = 0;
xmlDictPtr oldDict;
int oldOptions, oldDictNames, oldLoadSubset;
if ((ctxt == NULL) || (input == NULL) || (node == NULL)) {
xmlFatalErr(ctxt, XML_ERR_ARGUMENT, NULL);
goto exit;
}
doc = node->doc;
if (doc == NULL) {
xmlFatalErr(ctxt, XML_ERR_ARGUMENT, NULL);
goto exit;
}
switch (node->type) {
case XML_ELEMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
break;
case XML_ATTRIBUTE_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
for (cur = node->parent; cur != NULL; cur = node->parent) {
if ((cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE)) {
node = cur;
break;
}
}
break;
default:
xmlFatalErr(ctxt, XML_ERR_ARGUMENT, NULL);
goto exit;
}
#ifdef LIBXML_HTML_ENABLED
if (ctxt->html)
htmlCtxtReset(ctxt);
else
#endif
xmlCtxtReset(ctxt);
oldDict = ctxt->dict;
oldOptions = ctxt->options;
oldDictNames = ctxt->dictNames;
oldLoadSubset = ctxt->loadsubset;
/*
* Use input doc's dict if present, else assure XML_PARSE_NODICT is set.
*/
if (doc->dict != NULL) {
ctxt->dict = doc->dict;
} else {
ctxt->options |= XML_PARSE_NODICT;
ctxt->dictNames = 0;
}
/*
* Disable IDs
*/
ctxt->loadsubset |= XML_SKIP_IDS;
ctxt->myDoc = doc;
#ifdef LIBXML_HTML_ENABLED
if (ctxt->html) {
/*
* When parsing in context, it makes no sense to add implied
* elements like html/body/etc...
*/
ctxt->options |= HTML_PARSE_NOIMPLIED;
list = htmlCtxtParseContentInternal(ctxt, input);
} else
#endif
{
xmlCtxtInitializeLate(ctxt);
/*
* This hack lowers the error level of undeclared entities
* from XML_ERR_FATAL (well-formedness error) to XML_ERR_ERROR
* or XML_ERR_WARNING.
*/
ctxt->hasExternalSubset = 1;
/*
* initialize the SAX2 namespaces stack
*/
cur = node;
while ((cur != NULL) && (cur->type == XML_ELEMENT_NODE)) {
xmlNsPtr ns = cur->nsDef;
xmlHashedString hprefix, huri;
while (ns != NULL) {
hprefix = xmlDictLookupHashed(ctxt->dict, ns->prefix, -1);
huri = xmlDictLookupHashed(ctxt->dict, ns->href, -1);
if (xmlParserNsPush(ctxt, &hprefix, &huri, ns, 1) > 0)
nsnr++;
ns = ns->next;
}
cur = cur->parent;
}
list = xmlCtxtParseContentInternal(ctxt, input, hasTextDecl, 1);
if (nsnr > 0)
xmlParserNsPop(ctxt, nsnr);
}
ctxt->dict = oldDict;
ctxt->options = oldOptions;
ctxt->dictNames = oldDictNames;
ctxt->loadsubset = oldLoadSubset;
ctxt->myDoc = NULL;
ctxt->node = NULL;
exit:
xmlFreeInputStream(input);
return(list);
}
/**
* xmlParseInNodeContext:
* @node: the context node
* @data: the input string
* @datalen: the input string length in bytes
* @options: a combination of xmlParserOption
* @lst: the return value for the set of parsed nodes
* @listOut: the return value for the set of parsed nodes
*
* Parse a well-balanced chunk of an XML document
* within the context (DTD, namespaces, etc ...) of the given node.
@ -12333,188 +12480,65 @@ xmlParseBalancedChunkMemory(xmlDocPtr doc, xmlSAXHandlerPtr sax,
*
* [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)*
*
* This function assumes the encoding of @node's document which is
* typically not what you want. A better alternative is
* xmlCtxtParseContent.
*
* Returns XML_ERR_OK if the chunk is well balanced, and the parser
* error code otherwise
*/
xmlParserErrors
xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen,
int options, xmlNodePtr *lst) {
int options, xmlNodePtr *listOut) {
xmlParserCtxtPtr ctxt;
xmlDocPtr doc = NULL;
xmlNodePtr fake, cur;
int nsnr = 0;
xmlParserInputPtr input;
xmlDocPtr doc;
xmlNodePtr list;
xmlParserErrors ret;
xmlParserErrors ret = XML_ERR_OK;
/*
* check all input parameters, grab the document
*/
if ((lst == NULL) || (node == NULL) || (data == NULL) || (datalen < 0))
return(XML_ERR_ARGUMENT);
switch (node->type) {
case XML_ELEMENT_NODE:
case XML_ATTRIBUTE_NODE:
case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE:
case XML_PI_NODE:
case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
break;
default:
return(XML_ERR_INTERNAL_ERROR);
}
while ((node != NULL) && (node->type != XML_ELEMENT_NODE) &&
(node->type != XML_DOCUMENT_NODE) &&
(node->type != XML_HTML_DOCUMENT_NODE))
node = node->parent;
if (node == NULL)
return(XML_ERR_INTERNAL_ERROR);
if (node->type == XML_ELEMENT_NODE)
doc = node->doc;
else
doc = (xmlDocPtr) node;
if (doc == NULL)
return(XML_ERR_INTERNAL_ERROR);
/*
* allocate a context and set-up everything not related to the
* node position in the tree
*/
if (doc->type == XML_DOCUMENT_NODE)
ctxt = xmlCreateMemoryParserCtxt((char *) data, datalen);
#ifdef LIBXML_HTML_ENABLED
else if (doc->type == XML_HTML_DOCUMENT_NODE) {
ctxt = htmlCreateMemoryParserCtxt((char *) data, datalen);
/*
* When parsing in context, it makes no sense to add implied
* elements like html/body/etc...
*/
options |= HTML_PARSE_NOIMPLIED;
}
#endif
else
if (listOut == NULL)
return(XML_ERR_INTERNAL_ERROR);
*listOut = NULL;
if ((node == NULL) || (data == NULL) || (datalen < 0))
return(XML_ERR_INTERNAL_ERROR);
doc = node->doc;
if (doc == NULL)
return(XML_ERR_INTERNAL_ERROR);
#ifdef LIBXML_HTML_ENABLED
if (doc->type == XML_HTML_DOCUMENT_NODE) {
ctxt = htmlNewParserCtxt();
}
else
#endif
ctxt = xmlNewParserCtxt();
if (ctxt == NULL)
return(XML_ERR_NO_MEMORY);
/*
* Use input doc's dict if present, else assure XML_PARSE_NODICT is set.
* We need a dictionary for xmlCtxtInitializeLate, so if there's no doc dict
* we must wait until the last moment to free the original one.
*/
if (doc->dict != NULL) {
if (ctxt->dict != NULL)
xmlDictFree(ctxt->dict);
ctxt->dict = doc->dict;
} else {
options |= XML_PARSE_NODICT;
ctxt->dictNames = 0;
input = xmlNewInputMemory(ctxt, NULL, data, datalen,
(const char *) doc->encoding,
XML_INPUT_BUF_STATIC);
if (input == NULL) {
xmlFreeParserCtxt(ctxt);
return(XML_ERR_NO_MEMORY);
}
if (doc->encoding != NULL)
xmlSwitchEncodingName(ctxt, (const char *) doc->encoding);
xmlCtxtUseOptions(ctxt, options);
xmlCtxtInitializeLate(ctxt);
ctxt->myDoc = doc;
/* parsing in context, i.e. as within existing content */
ctxt->input_id = 2;
/*
* TODO: Use xmlCtxtParseContent
*/
list = xmlCtxtParseContent(ctxt, input, node, /* hasTextDecl */ 0);
fake = xmlNewDocComment(node->doc, NULL);
if (fake == NULL) {
xmlFreeParserCtxt(ctxt);
return(XML_ERR_NO_MEMORY);
}
xmlAddChild(node, fake);
if (node->type == XML_ELEMENT_NODE)
nodePush(ctxt, node);
if ((ctxt->html == 0) && (node->type == XML_ELEMENT_NODE)) {
/*
* initialize the SAX2 namespaces stack
*/
cur = node;
while ((cur != NULL) && (cur->type == XML_ELEMENT_NODE)) {
xmlNsPtr ns = cur->nsDef;
xmlHashedString hprefix, huri;
while (ns != NULL) {
hprefix = xmlDictLookupHashed(ctxt->dict, ns->prefix, -1);
huri = xmlDictLookupHashed(ctxt->dict, ns->href, -1);
if (xmlParserNsPush(ctxt, &hprefix, &huri, ns, 1) > 0)
nsnr++;
ns = ns->next;
}
cur = cur->parent;
}
}
if ((ctxt->validate) || (ctxt->replaceEntities != 0)) {
/*
* ID/IDREF registration will be done in xmlValidateElement below
*/
ctxt->loadsubset |= XML_SKIP_IDS;
}
#ifdef LIBXML_HTML_ENABLED
if (doc->type == XML_HTML_DOCUMENT_NODE)
__htmlParseContent(ctxt);
else
#endif
xmlParseContentInternal(ctxt);
if (ctxt->input->cur < ctxt->input->end)
xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL);
xmlParserNsPop(ctxt, nsnr);
if ((ctxt->wellFormed) ||
((ctxt->recovery) && (ctxt->errNo != XML_ERR_NO_MEMORY))) {
ret = XML_ERR_OK;
if (list == NULL) {
ret = ctxt->errNo;
if (ret == XML_ERR_ARGUMENT)
ret = XML_ERR_INTERNAL_ERROR;
} else {
ret = (xmlParserErrors) ctxt->errNo;
ret = XML_ERR_OK;
*listOut = list;
}
/*
* Return the newly created nodeset after unlinking it from
* the pseudo sibling.
*/
cur = fake->next;
fake->next = NULL;
node->last = fake;
if (cur != NULL) {
cur->prev = NULL;
}
*lst = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
xmlUnlinkNode(fake);
xmlFreeNode(fake);
if (ret != XML_ERR_OK) {
xmlFreeNodeList(*lst);
*lst = NULL;
}
if (doc->dict != NULL)
ctxt->dict = NULL;
xmlFreeParserCtxt(ctxt);
return(ret);
@ -12574,10 +12598,12 @@ xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc, xmlSAXHandlerPtr sax,
}
input = xmlNewStringInputStream(ctxt, string);
if (input == NULL)
return(ctxt->errNo);
if (input == NULL) {
ret = ctxt->errNo;
goto error;
}
list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 0, 1);
list = xmlCtxtParseContentInternal(ctxt, input, /* hasTextDecl */ 0, 1);
if (listOut != NULL)
*listOut = list;
else
@ -12588,6 +12614,7 @@ xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc, xmlSAXHandlerPtr sax,
else
ret = XML_ERR_OK;
error:
xmlFreeInputStream(input);
xmlFreeParserCtxt(ctxt);
return(ret);

View File

@ -319,6 +319,9 @@ xmlCtxtVErr(xmlParserCtxtPtr ctxt, xmlNodePtr node, xmlErrorDomain domain,
return;
}
if (ctxt == NULL)
return;
if (PARSER_STOPPED(ctxt))
return;

134
runtest.c
View File

@ -33,6 +33,7 @@
#include <libxml/tree.h>
#include <libxml/uri.h>
#include <libxml/encoding.h>
#include <libxml/xmlsave.h>
#ifdef LIBXML_OUTPUT_ENABLED
#ifdef LIBXML_READER_ENABLED
@ -2060,6 +2061,78 @@ pushBoundaryTest(const char *filename, const char *result,
}
#endif
static char *
dumpNodeList(xmlNodePtr list) {
xmlBufferPtr buffer;
xmlSaveCtxtPtr save;
xmlNodePtr cur;
char *ret;
buffer = xmlBufferCreate();
save = xmlSaveToBuffer(buffer, "UTF-8", 0);
for (cur = list; cur != NULL; cur = cur->next)
xmlSaveTree(save, cur);
xmlSaveClose(save);
ret = (char *) xmlBufferDetach(buffer);
xmlBufferFree(buffer);
return(ret);
}
static int
testParseContent(xmlParserCtxtPtr ctxt, xmlDocPtr doc, const char *filename) {
xmlParserInputPtr input;
xmlNodePtr root = NULL, list;
char *content, *roundTrip;
int ret = 0;
if (ctxt->html) {
xmlNodePtr cur;
if (doc == NULL || doc->children == NULL)
return 0;
for (cur = doc->children->children; cur != NULL; cur = cur->next) {
if (xmlStrEqual(cur->name, BAD_CAST "body")) {
root = cur;
break;
}
}
} else {
root = xmlDocGetRootElement(doc);
}
if (root == NULL)
return 0;
content = dumpNodeList(root->children);
input = xmlInputCreateString(NULL, content, XML_INPUT_BUF_STATIC);
list = xmlCtxtParseContent(ctxt, input, root, 0);
roundTrip = dumpNodeList(list);
if (strcmp(content, roundTrip) != 0) {
fprintf(stderr, "xmlCtxtParseContent failed for %s\n", filename);
ret = -1;
}
xmlFree(roundTrip);
xmlFreeNodeList(list);
/* xmlParseInNodeContext uses the document's encoding. */
xmlFree((xmlChar *) doc->encoding);
doc->encoding = (const xmlChar *) xmlStrdup(BAD_CAST "UTF-8");
xmlParseInNodeContext(root, content, strlen(content),
ctxt->options | XML_PARSE_NOERROR,
&list);
roundTrip = dumpNodeList(list);
if (strcmp(content, roundTrip) != 0) {
fprintf(stderr, "xmlParseInNodeContext failed for %s\n", filename);
ret = -1;
}
xmlFree(roundTrip);
xmlFreeNodeList(list);
xmlFree(content);
return(ret);
}
/**
* memParseTest:
* @filename: the file to parse
@ -2075,10 +2148,14 @@ pushBoundaryTest(const char *filename, const char *result,
static int
memParseTest(const char *filename, const char *result,
const char *err ATTRIBUTE_UNUSED,
int options ATTRIBUTE_UNUSED) {
int options) {
xmlParserCtxtPtr ctxt;
xmlDocPtr doc;
const char *base;
int size, res;
int ret = 0;
options |= XML_PARSE_NOWARNING;
nb_tests++;
/*
@ -2089,22 +2166,26 @@ memParseTest(const char *filename, const char *result,
return(-1);
}
doc = xmlReadMemory(base, size, filename, NULL, XML_PARSE_NOWARNING);
ctxt = xmlNewParserCtxt();
doc = xmlCtxtReadMemory(ctxt, base, size, filename, NULL, options);
unloadMem(base);
if (doc == NULL) {
return(1);
}
xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
xmlFreeDoc(doc);
res = compareFileMem(result, base, size);
if ((base == NULL) || (res != 0)) {
if (base != NULL)
xmlFree((char *)base);
fprintf(stderr, "Result for %s failed in %s\n", filename, result);
return(-1);
ret = -1;
}
if (testParseContent(ctxt, doc, filename) < 0)
ret = -1;
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
xmlFree((char *)base);
return(0);
return(ret);
}
/**
@ -2181,8 +2262,8 @@ errParseTest(const char *filename, const char *result, const char *err,
int options) {
xmlParserCtxtPtr ctxt;
xmlDocPtr doc;
const char *base = NULL;
int size, res = 0;
int ret = 0;
nb_tests++;
#ifdef LIBXML_HTML_ENABLED
@ -2190,14 +2271,12 @@ errParseTest(const char *filename, const char *result, const char *err,
ctxt = htmlNewParserCtxt();
xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL);
doc = htmlCtxtReadFile(ctxt, filename, NULL, options);
htmlFreeParserCtxt(ctxt);
} else
#endif
{
ctxt = xmlNewParserCtxt();
xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL);
doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
xmlFreeParserCtxt(ctxt);
#ifdef LIBXML_XINCLUDE_ENABLED
if (options & XML_PARSE_XINCLUDE) {
xmlXIncludeCtxtPtr xinc = NULL;
@ -2215,40 +2294,45 @@ errParseTest(const char *filename, const char *result, const char *err,
#endif
}
if (result) {
xmlChar *base = NULL;
if (doc == NULL) {
base = "";
base = xmlStrdup(BAD_CAST "");
size = 0;
} else {
#ifdef LIBXML_HTML_ENABLED
if (options & XML_PARSE_HTML) {
htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
htmlDocDumpMemory(doc, &base, &size);
} else
#endif
xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
xmlDocDumpMemory(doc, &base, &size);
}
res = compareFileMem(result, base, size);
}
if (doc != NULL) {
if (base != NULL)
xmlFree((char *)base);
xmlFreeDoc(doc);
res = compareFileMem(result, (char *) base, size);
xmlFree(base);
}
if (res != 0) {
fprintf(stderr, "Result for %s failed in %s\n", filename, result);
return(-1);
}
if (err != NULL) {
ret = -1;
} else if (err != NULL) {
res = compareFileMem(err, testErrors, testErrorsSize);
if (res != 0) {
fprintf(stderr, "Error for %s failed\n", filename);
return(-1);
ret = -1;
}
} else if (options & XML_PARSE_DTDVALID) {
if (testErrorsSize != 0)
if (testErrorsSize != 0) {
fprintf(stderr, "Validation for %s failed\n", filename);
ret = -1;
}
}
return(0);
if (testParseContent(ctxt, doc, filename) < 0)
ret = -1;
xmlFreeDoc(doc);
xmlFreeParserCtxt(ctxt);
return(ret);
}
#if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_HTML_ENABLED)

View File

@ -12059,6 +12059,59 @@ test_xmlCtxtGetVersion(void) {
}
static int
test_xmlCtxtParseContent(void) {
int test_ret = 0;
int mem_base;
xmlNodePtr ret_val;
xmlParserCtxtPtr ctxt; /* parser context */
int n_ctxt;
xmlParserInputPtr input; /* parser input */
int n_input;
xmlNodePtr node; /* target node or document */
int n_node;
int hasTextDecl; /* whether to parse text declaration */
int n_hasTextDecl;
for (n_ctxt = 0;n_ctxt < gen_nb_xmlParserCtxtPtr;n_ctxt++) {
for (n_input = 0;n_input < gen_nb_xmlParserInputPtr;n_input++) {
for (n_node = 0;n_node < gen_nb_xmlNodePtr;n_node++) {
for (n_hasTextDecl = 0;n_hasTextDecl < gen_nb_int;n_hasTextDecl++) {
mem_base = xmlMemBlocks();
ctxt = gen_xmlParserCtxtPtr(n_ctxt, 0);
input = gen_xmlParserInputPtr(n_input, 1);
node = gen_xmlNodePtr(n_node, 2);
hasTextDecl = gen_int(n_hasTextDecl, 3);
ret_val = xmlCtxtParseContent(ctxt, input, node, hasTextDecl);
desret_xmlNodePtr(ret_val);
call_tests++;
des_xmlParserCtxtPtr(n_ctxt, ctxt, 0);
des_xmlParserInputPtr(n_input, input, 1);
des_xmlNodePtr(n_node, node, 2);
des_int(n_hasTextDecl, hasTextDecl, 3);
xmlResetLastError();
if (mem_base != xmlMemBlocks()) {
printf("Leak of %d blocks found in xmlCtxtParseContent",
xmlMemBlocks() - mem_base);
test_ret++;
printf(" %d", n_ctxt);
printf(" %d", n_input);
printf(" %d", n_node);
printf(" %d", n_hasTextDecl);
printf("\n");
}
}
}
}
}
function_tests++;
return(test_ret);
}
static int
test_xmlCtxtParseDocument(void) {
int test_ret = 0;
@ -13602,29 +13655,29 @@ test_xmlParseInNodeContext(void) {
int n_datalen;
int options; /* a combination of xmlParserOption */
int n_options;
xmlNodePtr * lst; /* the return value for the set of parsed nodes */
int n_lst;
xmlNodePtr * listOut; /* the return value for the set of parsed nodes */
int n_listOut;
for (n_node = 0;n_node < gen_nb_xmlNodePtr;n_node++) {
for (n_data = 0;n_data < gen_nb_const_char_ptr;n_data++) {
for (n_datalen = 0;n_datalen < gen_nb_int;n_datalen++) {
for (n_options = 0;n_options < gen_nb_parseroptions;n_options++) {
for (n_lst = 0;n_lst < gen_nb_xmlNodePtr_ptr;n_lst++) {
for (n_listOut = 0;n_listOut < gen_nb_xmlNodePtr_ptr;n_listOut++) {
mem_base = xmlMemBlocks();
node = gen_xmlNodePtr(n_node, 0);
data = gen_const_char_ptr(n_data, 1);
datalen = gen_int(n_datalen, 2);
options = gen_parseroptions(n_options, 3);
lst = gen_xmlNodePtr_ptr(n_lst, 4);
listOut = gen_xmlNodePtr_ptr(n_listOut, 4);
ret_val = xmlParseInNodeContext(node, data, datalen, options, lst);
ret_val = xmlParseInNodeContext(node, data, datalen, options, listOut);
desret_xmlParserErrors(ret_val);
call_tests++;
des_xmlNodePtr(n_node, node, 0);
des_const_char_ptr(n_data, data, 1);
des_int(n_datalen, datalen, 2);
des_parseroptions(n_options, options, 3);
des_xmlNodePtr_ptr(n_lst, lst, 4);
des_xmlNodePtr_ptr(n_listOut, listOut, 4);
xmlResetLastError();
if (mem_base != xmlMemBlocks()) {
printf("Leak of %d blocks found in xmlParseInNodeContext",
@ -13634,7 +13687,7 @@ test_xmlParseInNodeContext(void) {
printf(" %d", n_data);
printf(" %d", n_datalen);
printf(" %d", n_options);
printf(" %d", n_lst);
printf(" %d", n_listOut);
printf("\n");
}
}
@ -15081,7 +15134,7 @@ static int
test_parser(void) {
int test_ret = 0;
if (quiet == 0) printf("Testing parser : 81 of 95 functions ...\n");
if (quiet == 0) printf("Testing parser : 82 of 96 functions ...\n");
test_ret += test_xmlByteConsumed();
test_ret += test_xmlCleanupGlobals();
test_ret += test_xmlClearNodeInfoSeq();
@ -15095,6 +15148,7 @@ test_parser(void) {
test_ret += test_xmlCtxtGetStandalone();
test_ret += test_xmlCtxtGetStatus();
test_ret += test_xmlCtxtGetVersion();
test_ret += test_xmlCtxtParseContent();
test_ret += test_xmlCtxtParseDocument();
test_ret += test_xmlCtxtReadDoc();
test_ret += test_xmlCtxtReadFile();

View File

@ -8,8 +8,10 @@
#include "libxml.h"
#include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/uri.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlsave.h>
#include <libxml/xmlwriter.h>
#include <libxml/HTMLparser.h>
@ -126,6 +128,84 @@ testCFileIO(void) {
return err;
}
#ifdef LIBXML_OUTPUT_ENABLED
static xmlChar *
dumpNodeList(xmlNodePtr list) {
xmlBufferPtr buffer;
xmlSaveCtxtPtr save;
xmlNodePtr cur;
xmlChar *ret;
buffer = xmlBufferCreate();
save = xmlSaveToBuffer(buffer, "UTF-8", 0);
for (cur = list; cur != NULL; cur = cur->next)
xmlSaveTree(save, cur);
xmlSaveClose(save);
ret = xmlBufferDetach(buffer);
xmlBufferFree(buffer);
return ret;
}
static int
testCtxtParseContent(void) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr input;
xmlDocPtr doc;
xmlNodePtr node, list;
const char *content;
xmlChar *output;
int i, j;
int err = 0;
static const char *const tests[] = {
"<!-- c -->\xF0\x9F\x98\x84<a/><b/>end",
"text<a:foo><b:foo/></a:foo>text<!-- c -->"
};
doc = xmlReadDoc(BAD_CAST "<doc xmlns:a='a'><elem xmlns:b='b'/></doc>",
NULL, NULL, 0);
node = doc->children->children;
ctxt = xmlNewParserCtxt();
for (i = 0; (size_t) i < sizeof(tests) / sizeof(tests[0]); i++) {
content = tests[i];
for (j = 0; j < 2; j++) {
if (j == 0) {
input = xmlInputCreateString(NULL, content,
XML_INPUT_BUF_STATIC);
list = xmlCtxtParseContent(ctxt, input, node, 0);
} else {
xmlParseInNodeContext(node, content, strlen(content), 0,
&list);
}
output = dumpNodeList(list);
if ((j == 0 && ctxt->nsWellFormed == 0) ||
strcmp((char *) output, content) != 0) {
fprintf(stderr, "%s failed test %d, got:\n%s\n",
j == 0 ?
"xmlCtxtParseContent" :
"xmlParseInNodeContext",
i, output);
err = 1;
}
xmlFree(output);
xmlFreeNodeList(list);
}
}
xmlFreeParserCtxt(ctxt);
xmlFreeDoc(doc);
return err;
}
#endif /* LIBXML_OUTPUT_ENABLED */
#ifdef LIBXML_SAX1_ENABLED
static int
testBalancedChunk(void) {
@ -681,6 +761,9 @@ main(void) {
err |= testUnsupportedEncoding();
err |= testNodeGetContent();
err |= testCFileIO();
#ifdef LIBXML_OUTPUT_ENABLED
err |= testCtxtParseContent();
#endif
#ifdef LIBXML_SAX1_ENABLED
err |= testBalancedChunk();
#endif