1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2026-01-26 21:41:34 +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); if (currentNode != NULL) xmlFree(currentNode);
} }
/** xmlNodePtr
* htmlParseContent: htmlCtxtParseContentInternal(htmlParserCtxtPtr ctxt, xmlParserInputPtr input) {
* @ctxt: an HTML parser context xmlNodePtr root;
* xmlNodePtr list = NULL;
* Parse a content: comment, sub-element, reference or text. xmlChar *rootName = BAD_CAST "#root";
* This is the entry point when called from parser.c
*/
void root = xmlNewDocNode(ctxt->myDoc, NULL, rootName, NULL);
__htmlParseContent(void *ctxt) { if (root == NULL) {
if (ctxt != NULL) htmlErrMemory(ctxt);
htmlParseContentInternal((htmlParserCtxtPtr) 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='xmlCtxtGetStandalone' type='function'/>
<exports symbol='xmlCtxtGetStatus' type='function'/> <exports symbol='xmlCtxtGetStatus' type='function'/>
<exports symbol='xmlCtxtGetVersion' type='function'/> <exports symbol='xmlCtxtGetVersion' type='function'/>
<exports symbol='xmlCtxtParseContent' type='function'/>
<exports symbol='xmlCtxtParseDocument' type='function'/> <exports symbol='xmlCtxtParseDocument' type='function'/>
<exports symbol='xmlCtxtReadDoc' type='function'/> <exports symbol='xmlCtxtReadDoc' type='function'/>
<exports symbol='xmlCtxtReadFd' 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.'/> <return type='const xmlChar *' info='the version from the XML declaration.'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info=''/> <arg name='ctxt' type='xmlParserCtxtPtr' info=''/>
</function> </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'> <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> <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'/> <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'/> <arg name='ctxt' type='xmlParserCtxtPtr' info='an XML parser context'/>
</function> </function>
<function name='xmlParseContent' file='parserInternals' module='parser'> <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'/> <return type='void'/>
<arg name='ctxt' type='xmlParserCtxtPtr' info='an XML parser context'/> <arg name='ctxt' type='xmlParserCtxtPtr' info='an XML parser context'/>
</function> </function>
@@ -11471,13 +11480,13 @@ crash if you try to modify the tree)'/>
<arg name='filename' type='const char *' info='the filename'/> <arg name='filename' type='const char *' info='the filename'/>
</function> </function>
<function name='xmlParseInNodeContext' file='parser' module='parser'> <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'/> <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='node' type='xmlNodePtr' info='the context node'/>
<arg name='data' type='const char *' info='the input string'/> <arg name='data' type='const char *' info='the input string'/>
<arg name='datalen' type='int' info='the input string length in bytes'/> <arg name='datalen' type='int' info='the input string length in bytes'/>
<arg name='options' type='int' info='a combination of xmlParserOption'/> <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>
<function name='xmlParseMarkupDecl' file='parserInternals' module='parser'> <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> <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 XMLPUBFUN xmlDocPtr
xmlCtxtParseDocument (xmlParserCtxtPtr ctxt, xmlCtxtParseDocument (xmlParserCtxtPtr ctxt,
xmlParserInputPtr input); xmlParserInputPtr input);
XMLPUBFUN xmlNodePtr
xmlCtxtParseContent (xmlParserCtxtPtr ctxt,
xmlParserInputPtr input,
xmlNodePtr node,
int hasTextDecl);
XMLPUBFUN xmlDocPtr XMLPUBFUN xmlDocPtr
xmlCtxtReadDoc (xmlParserCtxtPtr ctxt, xmlCtxtReadDoc (xmlParserCtxtPtr ctxt,
const xmlChar *cur, const xmlChar *cur,

View File

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

329
parser.c
View File

@@ -7573,7 +7573,7 @@ xmlHandleUndeclaredEntity(xmlParserCtxtPtr ctxt, const xmlChar *name) {
*/ */
xmlValidityError(ctxt, XML_ERR_UNDECLARED_ENTITY, xmlValidityError(ctxt, XML_ERR_UNDECLARED_ENTITY,
"Entity '%s' not defined\n", name, NULL); "Entity '%s' not defined\n", name, NULL);
} else if ((ctxt->loadsubset) || } else if ((ctxt->loadsubset & ~XML_SKIP_IDS) ||
((ctxt->replaceEntities) && ((ctxt->replaceEntities) &&
((ctxt->options & XML_PARSE_NO_XXE) == 0))) { ((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 * Parse XML element content. This is useful if you're only interested
* in custom SAX callbacks. If you want a node list, use * in custom SAX callbacks. If you want a node list, use
* xmlParseInNodeContext. * xmlCtxtParseContent.
*/ */
void void
xmlParseContent(xmlParserCtxtPtr ctxt) { xmlParseContent(xmlParserCtxtPtr ctxt) {
@@ -12000,7 +12000,7 @@ xmlParseDTD(const xmlChar *ExternalID, const xmlChar *SystemID) {
************************************************************************/ ************************************************************************/
static xmlNodePtr static xmlNodePtr
xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, xmlCtxtParseContentInternal(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
int hasTextDecl, int buildTree) { int hasTextDecl, int buildTree) {
xmlNodePtr root = NULL; xmlNodePtr root = NULL;
xmlNodePtr list = NULL; xmlNodePtr list = NULL;
@@ -12056,17 +12056,13 @@ xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
xmlNodePtr cur; xmlNodePtr cur;
/* /*
* Return the newly created nodeset after unlinking it from * Unlink newly created node list.
* its pseudo parent.
*/ */
cur = root->children; list = root->children;
list = cur;
while (cur != NULL) {
cur->parent = NULL;
cur = cur->next;
}
root->children = NULL; root->children = NULL;
root->last = 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: * This initiates a recursive call chain:
* *
* - xmlCtxtParseContent * - xmlCtxtParseContentInternal
* - xmlParseContentInternal * - xmlParseContentInternal
* - xmlParseReference * - xmlParseReference
* - xmlCtxtParseEntity * - xmlCtxtParseEntity
@@ -12157,7 +12153,7 @@ xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) {
ent->flags |= XML_ENT_EXPANDING; ent->flags |= XML_ENT_EXPANDING;
list = xmlCtxtParseContent(ctxt, input, isExternal, buildTree); list = xmlCtxtParseContentInternal(ctxt, input, isExternal, buildTree);
ent->flags &= ~XML_ENT_EXPANDING; ent->flags &= ~XML_ENT_EXPANDING;
@@ -12232,7 +12228,7 @@ xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctxt, const xmlChar *URL,
xmlCtxtInitializeLate(ctxt); xmlCtxtInitializeLate(ctxt);
list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 1, 1); list = xmlCtxtParseContentInternal(ctxt, input, /* hasTextDecl */ 1, 1);
if (listOut != NULL) if (listOut != NULL)
*listOut = list; *listOut = list;
else else
@@ -12318,127 +12314,120 @@ xmlParseBalancedChunkMemory(xmlDocPtr doc, xmlSAXHandlerPtr sax,
#endif /* LIBXML_SAX1_ENABLED */ #endif /* LIBXML_SAX1_ENABLED */
/** /**
* xmlParseInNodeContext: * xmlCtxtParseContent:
* @node: the context node * @ctxt: parser context
* @data: the input string * @input: parser input
* @datalen: the input string length in bytes * @node: target node or document
* @options: a combination of xmlParserOption * @hasTextDecl: whether to parse text declaration
* @lst: the return value for the set of parsed nodes
* *
* Parse a well-balanced chunk of an XML document * Parse a well-balanced chunk of XML matching the 'content' production.
* within the context (DTD, namespaces, etc ...) of the given node.
* *
* The allowed sequence for the data is a Well Balanced Chunk defined by * Namespaces in scope of @node and entities of @node's document are
* the content production in the XML grammar: * recognized. When validating, the DTD of @node's document is used.
* *
* [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* * Always consumes @input even in error case.
* *
* Returns XML_ERR_OK if the chunk is well balanced, and the parser * Available since 2.14.0.
* error code otherwise *
* Returns a node list or NULL in case of error.
*/ */
xmlParserErrors xmlNodePtr
xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input,
int options, xmlNodePtr *lst) { xmlNodePtr node, int hasTextDecl) {
xmlParserCtxtPtr ctxt; xmlDocPtr doc;
xmlDocPtr doc = NULL; xmlNodePtr cur, list = NULL;
xmlNodePtr fake, cur;
int nsnr = 0; int nsnr = 0;
xmlDictPtr oldDict;
int oldOptions, oldDictNames, oldLoadSubset;
xmlParserErrors ret = XML_ERR_OK; 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;
}
/*
* check all input parameters, grab the document
*/
if ((lst == NULL) || (node == NULL) || (data == NULL) || (datalen < 0))
return(XML_ERR_ARGUMENT);
switch (node->type) { switch (node->type) {
case XML_ELEMENT_NODE: case XML_ELEMENT_NODE:
case XML_DOCUMENT_NODE:
case XML_HTML_DOCUMENT_NODE:
break;
case XML_ATTRIBUTE_NODE: case XML_ATTRIBUTE_NODE:
case XML_TEXT_NODE: case XML_TEXT_NODE:
case XML_CDATA_SECTION_NODE: case XML_CDATA_SECTION_NODE:
case XML_ENTITY_REF_NODE: case XML_ENTITY_REF_NODE:
case XML_PI_NODE: case XML_PI_NODE:
case XML_COMMENT_NODE: case XML_COMMENT_NODE:
case XML_DOCUMENT_NODE: for (cur = node->parent; cur != NULL; cur = node->parent) {
case XML_HTML_DOCUMENT_NODE: if ((cur->type == XML_ELEMENT_NODE) ||
(cur->type == XML_DOCUMENT_NODE) ||
(cur->type == XML_HTML_DOCUMENT_NODE)) {
node = cur;
break; break;
default:
return(XML_ERR_INTERNAL_ERROR);
} }
while ((node != NULL) && (node->type != XML_ELEMENT_NODE) && }
(node->type != XML_DOCUMENT_NODE) && break;
(node->type != XML_HTML_DOCUMENT_NODE))
node = node->parent; default:
if (node == NULL) xmlFatalErr(ctxt, XML_ERR_ARGUMENT, NULL);
return(XML_ERR_INTERNAL_ERROR); goto exit;
if (node->type == XML_ELEMENT_NODE) }
doc = node->doc;
#ifdef LIBXML_HTML_ENABLED
if (ctxt->html)
htmlCtxtReset(ctxt);
else else
doc = (xmlDocPtr) node; #endif
if (doc == NULL) xmlCtxtReset(ctxt);
return(XML_ERR_INTERNAL_ERROR);
oldDict = ctxt->dict;
oldOptions = ctxt->options;
oldDictNames = ctxt->dictNames;
oldLoadSubset = ctxt->loadsubset;
/* /*
* allocate a context and set-up everything not related to the * Use input doc's dict if present, else assure XML_PARSE_NODICT is set.
* node position in the tree
*/ */
if (doc->type == XML_DOCUMENT_NODE) if (doc->dict != NULL) {
ctxt = xmlCreateMemoryParserCtxt((char *) data, datalen); 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 #ifdef LIBXML_HTML_ENABLED
else if (doc->type == XML_HTML_DOCUMENT_NODE) { if (ctxt->html) {
ctxt = htmlCreateMemoryParserCtxt((char *) data, datalen);
/* /*
* When parsing in context, it makes no sense to add implied * When parsing in context, it makes no sense to add implied
* elements like html/body/etc... * elements like html/body/etc...
*/ */
options |= HTML_PARSE_NOIMPLIED; ctxt->options |= HTML_PARSE_NOIMPLIED;
}
list = htmlCtxtParseContentInternal(ctxt, input);
} else
#endif #endif
else {
return(XML_ERR_INTERNAL_ERROR);
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;
}
if (doc->encoding != NULL)
xmlSwitchEncodingName(ctxt, (const char *) doc->encoding);
xmlCtxtUseOptions(ctxt, options);
xmlCtxtInitializeLate(ctxt); xmlCtxtInitializeLate(ctxt);
ctxt->myDoc = doc;
/* parsing in context, i.e. as within existing content */
ctxt->input_id = 2;
/* /*
* TODO: Use xmlCtxtParseContent * 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;
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 * initialize the SAX2 namespaces stack
*/ */
@@ -12456,65 +12445,100 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen,
} }
cur = cur->parent; cur = cur->parent;
} }
list = xmlCtxtParseContentInternal(ctxt, input, hasTextDecl, 1);
if (nsnr > 0)
xmlParserNsPop(ctxt, nsnr);
} }
if ((ctxt->validate) || (ctxt->replaceEntities != 0)) { ctxt->dict = oldDict;
/* ctxt->options = oldOptions;
* ID/IDREF registration will be done in xmlValidateElement below ctxt->dictNames = oldDictNames;
*/ ctxt->loadsubset = oldLoadSubset;
ctxt->loadsubset |= XML_SKIP_IDS; 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
* @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.
*
* 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'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 *listOut) {
xmlParserCtxtPtr ctxt;
xmlParserInputPtr input;
xmlDocPtr doc;
xmlNodePtr list;
xmlParserErrors ret;
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 #ifdef LIBXML_HTML_ENABLED
if (doc->type == XML_HTML_DOCUMENT_NODE) if (doc->type == XML_HTML_DOCUMENT_NODE) {
__htmlParseContent(ctxt); ctxt = htmlNewParserCtxt();
}
else else
#endif #endif
xmlParseContentInternal(ctxt); ctxt = xmlNewParserCtxt();
if (ctxt->input->cur < ctxt->input->end) if (ctxt == NULL)
xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); return(XML_ERR_NO_MEMORY);
xmlParserNsPop(ctxt, nsnr); 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 ((ctxt->wellFormed) || xmlCtxtUseOptions(ctxt, options);
((ctxt->recovery) && (ctxt->errNo != XML_ERR_NO_MEMORY))) {
ret = XML_ERR_OK; list = xmlCtxtParseContent(ctxt, input, node, /* hasTextDecl */ 0);
if (list == NULL) {
ret = ctxt->errNo;
if (ret == XML_ERR_ARGUMENT)
ret = XML_ERR_INTERNAL_ERROR;
} else { } 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); xmlFreeParserCtxt(ctxt);
return(ret); return(ret);
@@ -12574,10 +12598,12 @@ xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc, xmlSAXHandlerPtr sax,
} }
input = xmlNewStringInputStream(ctxt, string); input = xmlNewStringInputStream(ctxt, string);
if (input == NULL) if (input == NULL) {
return(ctxt->errNo); ret = ctxt->errNo;
goto error;
}
list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 0, 1); list = xmlCtxtParseContentInternal(ctxt, input, /* hasTextDecl */ 0, 1);
if (listOut != NULL) if (listOut != NULL)
*listOut = list; *listOut = list;
else else
@@ -12588,6 +12614,7 @@ xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc, xmlSAXHandlerPtr sax,
else else
ret = XML_ERR_OK; ret = XML_ERR_OK;
error:
xmlFreeInputStream(input); xmlFreeInputStream(input);
xmlFreeParserCtxt(ctxt); xmlFreeParserCtxt(ctxt);
return(ret); return(ret);

View File

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

134
runtest.c
View File

@@ -33,6 +33,7 @@
#include <libxml/tree.h> #include <libxml/tree.h>
#include <libxml/uri.h> #include <libxml/uri.h>
#include <libxml/encoding.h> #include <libxml/encoding.h>
#include <libxml/xmlsave.h>
#ifdef LIBXML_OUTPUT_ENABLED #ifdef LIBXML_OUTPUT_ENABLED
#ifdef LIBXML_READER_ENABLED #ifdef LIBXML_READER_ENABLED
@@ -2060,6 +2061,78 @@ pushBoundaryTest(const char *filename, const char *result,
} }
#endif #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: * memParseTest:
* @filename: the file to parse * @filename: the file to parse
@@ -2075,10 +2148,14 @@ pushBoundaryTest(const char *filename, const char *result,
static int static int
memParseTest(const char *filename, const char *result, memParseTest(const char *filename, const char *result,
const char *err ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED,
int options ATTRIBUTE_UNUSED) { int options) {
xmlParserCtxtPtr ctxt;
xmlDocPtr doc; xmlDocPtr doc;
const char *base; const char *base;
int size, res; int size, res;
int ret = 0;
options |= XML_PARSE_NOWARNING;
nb_tests++; nb_tests++;
/* /*
@@ -2089,22 +2166,26 @@ memParseTest(const char *filename, const char *result,
return(-1); return(-1);
} }
doc = xmlReadMemory(base, size, filename, NULL, XML_PARSE_NOWARNING); ctxt = xmlNewParserCtxt();
doc = xmlCtxtReadMemory(ctxt, base, size, filename, NULL, options);
unloadMem(base); unloadMem(base);
if (doc == NULL) { if (doc == NULL) {
return(1); return(1);
} }
xmlDocDumpMemory(doc, (xmlChar **) &base, &size); xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
xmlFreeDoc(doc);
res = compareFileMem(result, base, size); res = compareFileMem(result, base, size);
if ((base == NULL) || (res != 0)) { if ((base == NULL) || (res != 0)) {
if (base != NULL)
xmlFree((char *)base);
fprintf(stderr, "Result for %s failed in %s\n", filename, result); 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); xmlFree((char *)base);
return(0); return(ret);
} }
/** /**
@@ -2181,8 +2262,8 @@ errParseTest(const char *filename, const char *result, const char *err,
int options) { int options) {
xmlParserCtxtPtr ctxt; xmlParserCtxtPtr ctxt;
xmlDocPtr doc; xmlDocPtr doc;
const char *base = NULL;
int size, res = 0; int size, res = 0;
int ret = 0;
nb_tests++; nb_tests++;
#ifdef LIBXML_HTML_ENABLED #ifdef LIBXML_HTML_ENABLED
@@ -2190,14 +2271,12 @@ errParseTest(const char *filename, const char *result, const char *err,
ctxt = htmlNewParserCtxt(); ctxt = htmlNewParserCtxt();
xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL); xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL);
doc = htmlCtxtReadFile(ctxt, filename, NULL, options); doc = htmlCtxtReadFile(ctxt, filename, NULL, options);
htmlFreeParserCtxt(ctxt);
} else } else
#endif #endif
{ {
ctxt = xmlNewParserCtxt(); ctxt = xmlNewParserCtxt();
xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL); xmlCtxtSetErrorHandler(ctxt, testStructuredErrorHandler, NULL);
doc = xmlCtxtReadFile(ctxt, filename, NULL, options); doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
xmlFreeParserCtxt(ctxt);
#ifdef LIBXML_XINCLUDE_ENABLED #ifdef LIBXML_XINCLUDE_ENABLED
if (options & XML_PARSE_XINCLUDE) { if (options & XML_PARSE_XINCLUDE) {
xmlXIncludeCtxtPtr xinc = NULL; xmlXIncludeCtxtPtr xinc = NULL;
@@ -2215,40 +2294,45 @@ errParseTest(const char *filename, const char *result, const char *err,
#endif #endif
} }
if (result) { if (result) {
xmlChar *base = NULL;
if (doc == NULL) { if (doc == NULL) {
base = ""; base = xmlStrdup(BAD_CAST "");
size = 0; size = 0;
} else { } else {
#ifdef LIBXML_HTML_ENABLED #ifdef LIBXML_HTML_ENABLED
if (options & XML_PARSE_HTML) { if (options & XML_PARSE_HTML) {
htmlDocDumpMemory(doc, (xmlChar **) &base, &size); htmlDocDumpMemory(doc, &base, &size);
} else } else
#endif #endif
xmlDocDumpMemory(doc, (xmlChar **) &base, &size); xmlDocDumpMemory(doc, &base, &size);
} }
res = compareFileMem(result, base, size); res = compareFileMem(result, (char *) base, size);
} xmlFree(base);
if (doc != NULL) {
if (base != NULL)
xmlFree((char *)base);
xmlFreeDoc(doc);
} }
if (res != 0) { if (res != 0) {
fprintf(stderr, "Result for %s failed in %s\n", filename, result); fprintf(stderr, "Result for %s failed in %s\n", filename, result);
return(-1); ret = -1;
} } else if (err != NULL) {
if (err != NULL) {
res = compareFileMem(err, testErrors, testErrorsSize); res = compareFileMem(err, testErrors, testErrorsSize);
if (res != 0) { if (res != 0) {
fprintf(stderr, "Error for %s failed\n", filename); fprintf(stderr, "Error for %s failed\n", filename);
return(-1); ret = -1;
} }
} else if (options & XML_PARSE_DTDVALID) { } else if (options & XML_PARSE_DTDVALID) {
if (testErrorsSize != 0) if (testErrorsSize != 0) {
fprintf(stderr, "Validation for %s failed\n", filename); 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) #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 static int
test_xmlCtxtParseDocument(void) { test_xmlCtxtParseDocument(void) {
int test_ret = 0; int test_ret = 0;
@@ -13602,29 +13655,29 @@ test_xmlParseInNodeContext(void) {
int n_datalen; int n_datalen;
int options; /* a combination of xmlParserOption */ int options; /* a combination of xmlParserOption */
int n_options; int n_options;
xmlNodePtr * lst; /* the return value for the set of parsed nodes */ xmlNodePtr * listOut; /* the return value for the set of parsed nodes */
int n_lst; int n_listOut;
for (n_node = 0;n_node < gen_nb_xmlNodePtr;n_node++) { 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_data = 0;n_data < gen_nb_const_char_ptr;n_data++) {
for (n_datalen = 0;n_datalen < gen_nb_int;n_datalen++) { for (n_datalen = 0;n_datalen < gen_nb_int;n_datalen++) {
for (n_options = 0;n_options < gen_nb_parseroptions;n_options++) { 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(); mem_base = xmlMemBlocks();
node = gen_xmlNodePtr(n_node, 0); node = gen_xmlNodePtr(n_node, 0);
data = gen_const_char_ptr(n_data, 1); data = gen_const_char_ptr(n_data, 1);
datalen = gen_int(n_datalen, 2); datalen = gen_int(n_datalen, 2);
options = gen_parseroptions(n_options, 3); 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); desret_xmlParserErrors(ret_val);
call_tests++; call_tests++;
des_xmlNodePtr(n_node, node, 0); des_xmlNodePtr(n_node, node, 0);
des_const_char_ptr(n_data, data, 1); des_const_char_ptr(n_data, data, 1);
des_int(n_datalen, datalen, 2); des_int(n_datalen, datalen, 2);
des_parseroptions(n_options, options, 3); des_parseroptions(n_options, options, 3);
des_xmlNodePtr_ptr(n_lst, lst, 4); des_xmlNodePtr_ptr(n_listOut, listOut, 4);
xmlResetLastError(); xmlResetLastError();
if (mem_base != xmlMemBlocks()) { if (mem_base != xmlMemBlocks()) {
printf("Leak of %d blocks found in xmlParseInNodeContext", printf("Leak of %d blocks found in xmlParseInNodeContext",
@@ -13634,7 +13687,7 @@ test_xmlParseInNodeContext(void) {
printf(" %d", n_data); printf(" %d", n_data);
printf(" %d", n_datalen); printf(" %d", n_datalen);
printf(" %d", n_options); printf(" %d", n_options);
printf(" %d", n_lst); printf(" %d", n_listOut);
printf("\n"); printf("\n");
} }
} }
@@ -15081,7 +15134,7 @@ static int
test_parser(void) { test_parser(void) {
int test_ret = 0; 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_xmlByteConsumed();
test_ret += test_xmlCleanupGlobals(); test_ret += test_xmlCleanupGlobals();
test_ret += test_xmlClearNodeInfoSeq(); test_ret += test_xmlClearNodeInfoSeq();
@@ -15095,6 +15148,7 @@ test_parser(void) {
test_ret += test_xmlCtxtGetStandalone(); test_ret += test_xmlCtxtGetStandalone();
test_ret += test_xmlCtxtGetStatus(); test_ret += test_xmlCtxtGetStatus();
test_ret += test_xmlCtxtGetVersion(); test_ret += test_xmlCtxtGetVersion();
test_ret += test_xmlCtxtParseContent();
test_ret += test_xmlCtxtParseDocument(); test_ret += test_xmlCtxtParseDocument();
test_ret += test_xmlCtxtReadDoc(); test_ret += test_xmlCtxtReadDoc();
test_ret += test_xmlCtxtReadFile(); test_ret += test_xmlCtxtReadFile();

View File

@@ -8,8 +8,10 @@
#include "libxml.h" #include "libxml.h"
#include <libxml/parser.h> #include <libxml/parser.h>
#include <libxml/parserInternals.h>
#include <libxml/uri.h> #include <libxml/uri.h>
#include <libxml/xmlreader.h> #include <libxml/xmlreader.h>
#include <libxml/xmlsave.h>
#include <libxml/xmlwriter.h> #include <libxml/xmlwriter.h>
#include <libxml/HTMLparser.h> #include <libxml/HTMLparser.h>
@@ -126,6 +128,84 @@ testCFileIO(void) {
return err; 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 #ifdef LIBXML_SAX1_ENABLED
static int static int
testBalancedChunk(void) { testBalancedChunk(void) {
@@ -681,6 +761,9 @@ main(void) {
err |= testUnsupportedEncoding(); err |= testUnsupportedEncoding();
err |= testNodeGetContent(); err |= testNodeGetContent();
err |= testCFileIO(); err |= testCFileIO();
#ifdef LIBXML_OUTPUT_ENABLED
err |= testCtxtParseContent();
#endif
#ifdef LIBXML_SAX1_ENABLED #ifdef LIBXML_SAX1_ENABLED
err |= testBalancedChunk(); err |= testBalancedChunk();
#endif #endif