diff --git a/ChangeLog b/ChangeLog index 36e166bc..b87e1fb6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,7 +6,7 @@ dd.mm.yy hh:mm - smooth alpha 0.9.11 - upgraded fribidi to version 1.0.14 - upgraded libcpuid to version 0.6.4 -- upgraded libxml2 to version 2.12.5 +- upgraded libxml2 to version 2.13.3 - upgraded libcurl to version 8.6.0 - upgraded libpng to version 1.6.43 - upgraded libwebp to version 1.3.2 diff --git a/include/support/libxml2/libxml/HTMLparser.h b/include/support/libxml2/libxml/HTMLparser.h index e16d7749..7be3d2b8 100644 --- a/include/support/libxml2/libxml/HTMLparser.h +++ b/include/support/libxml2/libxml/HTMLparser.h @@ -80,22 +80,17 @@ struct _htmlEntityDesc { const char *desc; /* the description */ }; -/** DOC_DISABLE */ #ifdef LIBXML_SAX1_ENABLED - #define XML_GLOBALS_HTML \ - XML_OP(htmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED) -#else - #define XML_GLOBALS_HTML -#endif -#define XML_OP XML_DECLARE_GLOBAL -XML_GLOBALS_HTML -#undef XML_OP +XML_DEPRECATED +XMLPUBVAR const xmlSAXHandlerV1 htmlDefaultSAXHandler; -#if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION) - #define htmlDefaultSAXHandler XML_GLOBAL_MACRO(htmlDefaultSAXHandler) +#ifdef LIBXML_THREAD_ENABLED +XML_DEPRECATED +XMLPUBFUN const xmlSAXHandlerV1 *__htmlDefaultSAXHandler(void); #endif -/** DOC_ENABLE */ + +#endif /* LIBXML_SAX1_ENABLED */ /* * There is only few public functions. @@ -173,6 +168,7 @@ XMLPUBFUN int int *inlen, int quoteChar); XMLPUBFUN int htmlIsScriptAttribute(const xmlChar *name); +XML_DEPRECATED XMLPUBFUN int htmlHandleOmittedElem(int val); @@ -251,6 +247,9 @@ XMLPUBFUN htmlDocPtr const char *URL, const char *encoding, int options); +XMLPUBFUN htmlDocPtr + htmlCtxtParseDocument (htmlParserCtxtPtr ctxt, + xmlParserInputPtr input); XMLPUBFUN htmlDocPtr htmlCtxtReadDoc (xmlParserCtxtPtr ctxt, const xmlChar *cur, @@ -300,7 +299,7 @@ typedef enum { XMLPUBFUN htmlStatus htmlAttrAllowed(const htmlElemDesc*, const xmlChar*, int) ; XMLPUBFUN int htmlElementAllowedHere(const htmlElemDesc*, const xmlChar*) ; XMLPUBFUN htmlStatus htmlElementStatusHere(const htmlElemDesc*, const htmlElemDesc*) ; -XMLPUBFUN htmlStatus htmlNodeStatus(const htmlNodePtr, int) ; +XMLPUBFUN htmlStatus htmlNodeStatus(htmlNodePtr, int) ; /** * htmlDefaultSubelement: * @elt: HTML element @@ -333,11 +332,5 @@ XMLPUBFUN htmlStatus htmlNodeStatus(const htmlNodePtr, int) ; } #endif -#else /* LIBXML_HTML_ENABLED */ - -/** DOC_DISABLE */ -#define XML_GLOBALS_HTML -/** DOC_ENABLE */ - #endif /* LIBXML_HTML_ENABLED */ #endif /* __HTML_PARSER_H__ */ diff --git a/include/support/libxml2/libxml/debugXML.h b/include/support/libxml2/libxml/debugXML.h index 82746873..1332dd73 100644 --- a/include/support/libxml2/libxml/debugXML.h +++ b/include/support/libxml2/libxml/debugXML.h @@ -203,7 +203,7 @@ XMLPUBFUN int */ XMLPUBFUN void xmlShell (xmlDocPtr doc, - char *filename, + const char *filename, xmlShellReadlineFunc input, FILE *output); diff --git a/include/support/libxml2/libxml/encoding.h b/include/support/libxml2/libxml/encoding.h index 8594cffc..599a03e1 100644 --- a/include/support/libxml2/libxml/encoding.h +++ b/include/support/libxml2/libxml/encoding.h @@ -162,6 +162,13 @@ XMLPUBFUN void xmlCleanupCharEncodingHandlers (void); XMLPUBFUN void xmlRegisterCharEncodingHandler (xmlCharEncodingHandlerPtr handler); +XMLPUBFUN int + xmlLookupCharEncodingHandler (xmlCharEncoding enc, + xmlCharEncodingHandlerPtr *out); +XMLPUBFUN int + xmlOpenCharEncodingHandler (const char *name, + int output, + xmlCharEncodingHandlerPtr *out); XMLPUBFUN xmlCharEncodingHandlerPtr xmlGetCharEncodingHandler (xmlCharEncoding enc); XMLPUBFUN xmlCharEncodingHandlerPtr @@ -195,7 +202,9 @@ XMLPUBFUN xmlCharEncoding xmlDetectCharEncoding (const unsigned char *in, int len); +/** DOC_DISABLE */ struct _xmlBuffer; +/** DOC_ENABLE */ XMLPUBFUN int xmlCharEncOutFunc (xmlCharEncodingHandler *handler, struct _xmlBuffer *out, diff --git a/include/support/libxml2/libxml/entities.h b/include/support/libxml2/libxml/entities.h index f6793753..a0cfca81 100644 --- a/include/support/libxml2/libxml/entities.h +++ b/include/support/libxml2/libxml/entities.h @@ -11,10 +11,12 @@ #ifndef __XML_ENTITIES_H__ #define __XML_ENTITIES_H__ +/** DOC_DISABLE */ #include #define XML_TREE_INTERNALS #include #undef XML_TREE_INTERNALS +/** DOC_ENABLE */ #ifdef __cplusplus extern "C" { @@ -57,7 +59,7 @@ struct _xmlEntity { struct _xmlEntity *nexte; /* unused */ const xmlChar *URI; /* the full URI as computed */ - int owner; /* does the entity own the childrens */ + int owner; /* unused */ int flags; /* various flags */ unsigned long expandedSize; /* expanded size */ }; @@ -89,6 +91,15 @@ XMLPUBFUN xmlEntityPtr const xmlChar *content); XMLPUBFUN void xmlFreeEntity (xmlEntityPtr entity); +XMLPUBFUN int + xmlAddEntity (xmlDocPtr doc, + int extSubset, + const xmlChar *name, + int type, + const xmlChar *ExternalID, + const xmlChar *SystemID, + const xmlChar *content, + xmlEntityPtr *out); XMLPUBFUN xmlEntityPtr xmlAddDocEntity (xmlDocPtr doc, const xmlChar *name, diff --git a/include/support/libxml2/libxml/hash.h b/include/support/libxml2/libxml/hash.h index f4af09ee..135b6966 100644 --- a/include/support/libxml2/libxml/hash.h +++ b/include/support/libxml2/libxml/hash.h @@ -109,6 +109,10 @@ XMLPUBFUN void /* * Add a new entry to the hash table. */ +XMLPUBFUN int + xmlHashAdd (xmlHashTablePtr hash, + const xmlChar *name, + void *userdata); XMLPUBFUN int xmlHashAddEntry (xmlHashTablePtr hash, const xmlChar *name, @@ -118,6 +122,11 @@ XMLPUBFUN int const xmlChar *name, void *userdata, xmlHashDeallocator dealloc); +XMLPUBFUN int + xmlHashAdd2 (xmlHashTablePtr hash, + const xmlChar *name, + const xmlChar *name2, + void *userdata); XMLPUBFUN int xmlHashAddEntry2 (xmlHashTablePtr hash, const xmlChar *name, @@ -129,6 +138,12 @@ XMLPUBFUN int const xmlChar *name2, void *userdata, xmlHashDeallocator dealloc); +XMLPUBFUN int + xmlHashAdd3 (xmlHashTablePtr hash, + const xmlChar *name, + const xmlChar *name2, + const xmlChar *name3, + void *userdata); XMLPUBFUN int xmlHashAddEntry3 (xmlHashTablePtr hash, const xmlChar *name, @@ -199,6 +214,10 @@ XMLPUBFUN void * /* * Helpers. */ +XMLPUBFUN xmlHashTablePtr + xmlHashCopySafe (xmlHashTablePtr hash, + xmlHashCopier copy, + xmlHashDeallocator dealloc); XMLPUBFUN xmlHashTablePtr xmlHashCopy (xmlHashTablePtr hash, xmlHashCopier copy); diff --git a/include/support/libxml2/libxml/list.h b/include/support/libxml2/libxml/list.h index 5eab8f59..1fa76aff 100644 --- a/include/support/libxml2/libxml/list.h +++ b/include/support/libxml2/libxml/list.h @@ -119,10 +119,10 @@ XMLPUBFUN void xmlListMerge (xmlListPtr l1, xmlListPtr l2); XMLPUBFUN xmlListPtr - xmlListDup (const xmlListPtr old); + xmlListDup (xmlListPtr old); XMLPUBFUN int xmlListCopy (xmlListPtr cur, - const xmlListPtr old); + xmlListPtr old); /* Link operators */ XMLPUBFUN void * xmlLinkGetData (xmlLinkPtr lk); diff --git a/include/support/libxml2/libxml/nanohttp.h b/include/support/libxml2/libxml/nanohttp.h index 3b5e037f..c70d1c26 100644 --- a/include/support/libxml2/libxml/nanohttp.h +++ b/include/support/libxml2/libxml/nanohttp.h @@ -18,16 +18,21 @@ #ifdef __cplusplus extern "C" { #endif +XML_DEPRECATED XMLPUBFUN void xmlNanoHTTPInit (void); +XML_DEPRECATED XMLPUBFUN void xmlNanoHTTPCleanup (void); +XML_DEPRECATED XMLPUBFUN void xmlNanoHTTPScanProxy (const char *URL); +XML_DEPRECATED XMLPUBFUN int xmlNanoHTTPFetch (const char *URL, const char *filename, char **contentType); +XML_DEPRECATED XMLPUBFUN void * xmlNanoHTTPMethod (const char *URL, const char *method, @@ -35,6 +40,7 @@ XMLPUBFUN void * char **contentType, const char *headers, int ilen); +XML_DEPRECATED XMLPUBFUN void * xmlNanoHTTPMethodRedir (const char *URL, const char *method, @@ -43,34 +49,45 @@ XMLPUBFUN void * char **redir, const char *headers, int ilen); +XML_DEPRECATED XMLPUBFUN void * xmlNanoHTTPOpen (const char *URL, char **contentType); +XML_DEPRECATED XMLPUBFUN void * xmlNanoHTTPOpenRedir (const char *URL, char **contentType, char **redir); +XML_DEPRECATED XMLPUBFUN int xmlNanoHTTPReturnCode (void *ctx); +XML_DEPRECATED XMLPUBFUN const char * xmlNanoHTTPAuthHeader (void *ctx); +XML_DEPRECATED XMLPUBFUN const char * xmlNanoHTTPRedir (void *ctx); +XML_DEPRECATED XMLPUBFUN int xmlNanoHTTPContentLength( void * ctx ); +XML_DEPRECATED XMLPUBFUN const char * xmlNanoHTTPEncoding (void *ctx); +XML_DEPRECATED XMLPUBFUN const char * xmlNanoHTTPMimeType (void *ctx); +XML_DEPRECATED XMLPUBFUN int xmlNanoHTTPRead (void *ctx, void *dest, int len); #ifdef LIBXML_OUTPUT_ENABLED +XML_DEPRECATED XMLPUBFUN int xmlNanoHTTPSave (void *ctxt, const char *filename); #endif /* LIBXML_OUTPUT_ENABLED */ +XML_DEPRECATED XMLPUBFUN void xmlNanoHTTPClose (void *ctx); #ifdef __cplusplus diff --git a/include/support/libxml2/libxml/parser.h b/include/support/libxml2/libxml/parser.h index 87aacef9..78d29cad 100644 --- a/include/support/libxml2/libxml/parser.h +++ b/include/support/libxml2/libxml/parser.h @@ -10,6 +10,7 @@ #ifndef __XML_PARSER_H__ #define __XML_PARSER_H__ +/** DOC_DISABLE */ #include #define XML_TREE_INTERNALS #include @@ -26,6 +27,7 @@ /* for compatibility */ #include #include +/** DOC_ENABLE */ #ifdef __cplusplus extern "C" { @@ -62,11 +64,11 @@ struct _xmlParserInput { xmlParserInputBufferPtr buf; /* UTF-8 encoded buffer */ const char *filename; /* The file analyzed, if any */ - const char *directory; /* the directory/base of the file */ + const char *directory; /* unused */ const xmlChar *base; /* Base of the array to parse */ const xmlChar *cur; /* Current char being parsed */ const xmlChar *end; /* end of the array to parse */ - int length; /* length if known */ + int length; /* unused */ int line; /* Current line */ int col; /* Current column */ unsigned long consumed; /* How many xmlChars already consumed */ @@ -75,7 +77,7 @@ struct _xmlParserInput { const xmlChar *version; /* the version string for entity */ int flags; /* Flags */ int id; /* an unique identifier for the entity */ - unsigned long parentConsumed; /* consumed bytes from parents */ + unsigned long parentConsumed; /* unused */ xmlEntityPtr entity; /* entity, if any */ }; @@ -134,30 +136,14 @@ typedef enum { XML_PARSER_XML_DECL /* before XML decl (but after BOM) */ } xmlParserInputState; -/** - * XML_DETECT_IDS: - * - * Bit in the loadsubset context field to tell to do ID/REFs lookups. - * Use it to initialize xmlLoadExtDtdDefaultValue. +/** DOC_DISABLE */ +/* + * Internal bits in the 'loadsubset' context member */ #define XML_DETECT_IDS 2 - -/** - * XML_COMPLETE_ATTRS: - * - * Bit in the loadsubset context field to tell to do complete the - * elements attributes lists with the ones defaulted from the DTDs. - * Use it to initialize xmlLoadExtDtdDefaultValue. - */ #define XML_COMPLETE_ATTRS 4 - -/** - * XML_SKIP_IDS: - * - * Bit in the loadsubset context field to tell to not do ID/REFs registration. - * Used to initialize xmlLoadExtDtdDefaultValue in some special cases. - */ #define XML_SKIP_IDS 8 +/** DOC_ENABLE */ /** * xmlParserMode: @@ -222,16 +208,16 @@ struct _xmlParserCtxt { int hasExternalSubset; /* reference and external subset */ int hasPErefs; /* the internal subset has PE refs */ - int external; /* are we parsing an external entity */ + int external; /* unused */ int valid; /* is the document valid */ int validate; /* shall we try to validate ? */ xmlValidCtxt vctxt; /* The validity context */ - xmlParserInputState instate; /* current type of input */ - int token; /* next char look-ahead */ + xmlParserInputState instate; /* push parser state */ + int token; /* unused */ - char *directory; /* the data directory */ + char *directory; /* unused */ /* Node name stack */ const xmlChar *name; /* Current parsed Node */ @@ -255,7 +241,7 @@ struct _xmlParserCtxt { int * spaceTab; /* array of space infos */ int depth; /* to prevent entity substitution loops */ - xmlParserInputPtr entity; /* used to check entities boundaries */ + xmlParserInputPtr entity; /* unused */ int charset; /* unused */ int nodelen; /* Those two fields are there to */ int nodemem; /* Speed up large node parsing */ @@ -266,11 +252,11 @@ struct _xmlParserCtxt { int linenumbers; /* set line number in element content */ void *catalogs; /* document's own catalog */ int recovery; /* run in recovery mode */ - int progressive; /* is this a progressive parsing */ + int progressive; /* unused */ xmlDictPtr dict; /* dictionary for the parser */ const xmlChar * *atts; /* array for the attributes callbacks */ int maxatts; /* the size of the array */ - int docdict; /* use strings from dict to build tree */ + int docdict; /* unused */ /* * pre-interned strings @@ -308,7 +294,7 @@ struct _xmlParserCtxt { xmlError lastError; xmlParserMode parseMode; /* the parser mode */ unsigned long nbentities; /* unused */ - unsigned long sizeentities; /* size of parsed entities */ + unsigned long sizeentities; /* size of external entities */ /* for use by HTML non-recursive parser */ xmlParserNodeInfo *nodeInfo; /* Current NodeInfo */ @@ -327,6 +313,9 @@ struct _xmlParserCtxt { xmlParserNsData *nsdb; /* namespace database */ unsigned attrHashMax; /* allocated size */ xmlAttrHashBucket *attrHash; /* atttribute hash table */ + + xmlStructuredErrorFunc errorHandler; + void *errorCtxt; }; /** @@ -843,21 +832,39 @@ typedef xmlParserInputPtr (*xmlExternalEntityLoader) (const char *URL, */ XMLPUBVAR const char *const xmlParserVersion; +XML_DEPRECATED +XMLPUBVAR const int oldXMLWDcompatibility; +XML_DEPRECATED +XMLPUBVAR const int xmlParserDebugEntities; +XML_DEPRECATED +XMLPUBVAR const xmlSAXLocator xmlDefaultSAXLocator; +#ifdef LIBXML_SAX1_ENABLED +XML_DEPRECATED +XMLPUBVAR const xmlSAXHandlerV1 xmlDefaultSAXHandler; +#endif + #ifdef LIBXML_THREAD_ENABLED /* backward compatibility */ XMLPUBFUN const char *const *__xmlParserVersion(void); +XML_DEPRECATED +XMLPUBFUN const int *__oldXMLWDcompatibility(void); +XML_DEPRECATED +XMLPUBFUN const int *__xmlParserDebugEntities(void); +XML_DEPRECATED +XMLPUBFUN const xmlSAXLocator *__xmlDefaultSAXLocator(void); +#ifdef LIBXML_SAX1_ENABLED +XML_DEPRECATED +XMLPUBFUN const xmlSAXHandlerV1 *__xmlDefaultSAXHandler(void); +#endif #endif /** DOC_DISABLE */ #define XML_GLOBALS_PARSER_CORE \ - XML_OP(oldXMLWDcompatibility, int, XML_DEPRECATED) \ - XML_OP(xmlDefaultSAXLocator, xmlSAXLocator, XML_DEPRECATED) \ XML_OP(xmlDoValidityCheckingDefaultValue, int, XML_DEPRECATED) \ XML_OP(xmlGetWarningsDefaultValue, int, XML_DEPRECATED) \ XML_OP(xmlKeepBlanksDefaultValue, int, XML_DEPRECATED) \ XML_OP(xmlLineNumbersDefaultValue, int, XML_DEPRECATED) \ XML_OP(xmlLoadExtDtdDefaultValue, int, XML_DEPRECATED) \ - XML_OP(xmlParserDebugEntities, int, XML_DEPRECATED) \ XML_OP(xmlPedanticParserDefaultValue, int, XML_DEPRECATED) \ XML_OP(xmlSubstituteEntitiesDefaultValue, int, XML_DEPRECATED) @@ -870,26 +877,15 @@ XMLPUBFUN const char *const *__xmlParserVersion(void); #define XML_GLOBALS_PARSER_OUTPUT #endif -#ifdef LIBXML_SAX1_ENABLED - #define XML_GLOBALS_PARSER_SAX1 \ - XML_OP(xmlDefaultSAXHandler, xmlSAXHandlerV1, XML_DEPRECATED) -#else - #define XML_GLOBALS_PARSER_SAX1 -#endif - #define XML_GLOBALS_PARSER \ XML_GLOBALS_PARSER_CORE \ - XML_GLOBALS_PARSER_OUTPUT \ - XML_GLOBALS_PARSER_SAX1 + XML_GLOBALS_PARSER_OUTPUT #define XML_OP XML_DECLARE_GLOBAL XML_GLOBALS_PARSER #undef XML_OP #if defined(LIBXML_THREAD_ENABLED) && !defined(XML_GLOBALS_NO_REDEFINITION) - #define oldXMLWDcompatibility XML_GLOBAL_MACRO(oldXMLWDcompatibility) - #define xmlDefaultSAXHandler XML_GLOBAL_MACRO(xmlDefaultSAXHandler) - #define xmlDefaultSAXLocator XML_GLOBAL_MACRO(xmlDefaultSAXLocator) #define xmlDoValidityCheckingDefaultValue \ XML_GLOBAL_MACRO(xmlDoValidityCheckingDefaultValue) #define xmlGetWarningsDefaultValue \ @@ -898,7 +894,6 @@ XML_GLOBALS_PARSER #define xmlLineNumbersDefaultValue \ XML_GLOBAL_MACRO(xmlLineNumbersDefaultValue) #define xmlLoadExtDtdDefaultValue XML_GLOBAL_MACRO(xmlLoadExtDtdDefaultValue) - #define xmlParserDebugEntities XML_GLOBAL_MACRO(xmlParserDebugEntities) #define xmlPedanticParserDefaultValue \ XML_GLOBAL_MACRO(xmlPedanticParserDefaultValue) #define xmlSubstituteEntitiesDefaultValue \ @@ -953,7 +948,7 @@ XML_DEPRECATED XMLPUBFUN int xmlSubstituteEntitiesDefault(int val); XML_DEPRECATED XMLPUBFUN int xmlThrDefSubstituteEntitiesDefaultValue(int v); -XML_DEPRECATED XMLPUBFUN int +XMLPUBFUN int xmlKeepBlanksDefault (int val); XML_DEPRECATED XMLPUBFUN int xmlThrDefKeepBlanksDefaultValue(int v); @@ -1185,18 +1180,18 @@ XMLPUBFUN xmlParserInputPtr * Node infos. */ XMLPUBFUN const xmlParserNodeInfo* - xmlParserFindNodeInfo (const xmlParserCtxtPtr ctxt, - const xmlNodePtr node); + xmlParserFindNodeInfo (xmlParserCtxtPtr ctxt, + xmlNodePtr node); XMLPUBFUN void xmlInitNodeInfoSeq (xmlParserNodeInfoSeqPtr seq); XMLPUBFUN void xmlClearNodeInfoSeq (xmlParserNodeInfoSeqPtr seq); XMLPUBFUN unsigned long - xmlParserFindNodeInfoIndex(const xmlParserNodeInfoSeqPtr seq, - const xmlNodePtr node); + xmlParserFindNodeInfoIndex(xmlParserNodeInfoSeqPtr seq, + xmlNodePtr node); XMLPUBFUN void xmlParserAddNodeInfo (xmlParserCtxtPtr ctxt, - const xmlParserNodeInfoPtr info); + xmlParserNodeInfoPtr info); /* * External entities handling actually implemented in xmlIO. @@ -1251,7 +1246,8 @@ typedef enum { XML_PARSE_HUGE = 1<<19,/* relax any hardcoded limit from the parser */ XML_PARSE_OLDSAX = 1<<20,/* parse using SAX2 interface before 2.7.0 */ XML_PARSE_IGNORE_ENC= 1<<21,/* ignore internal document encoding hint */ - XML_PARSE_BIG_LINES = 1<<22 /* Store big lines numbers in text PSVI field */ + XML_PARSE_BIG_LINES = 1<<22,/* Store big lines numbers in text PSVI field */ + XML_PARSE_NO_XXE = 1<<23 /* disable loading of external content */ } xmlParserOption; XMLPUBFUN void @@ -1262,9 +1258,16 @@ XMLPUBFUN int int size, const char *filename, const char *encoding); +XMLPUBFUN int + xmlCtxtSetOptions (xmlParserCtxtPtr ctxt, + int options); XMLPUBFUN int xmlCtxtUseOptions (xmlParserCtxtPtr ctxt, int options); +XMLPUBFUN void + xmlCtxtSetErrorHandler (xmlParserCtxtPtr ctxt, + xmlStructuredErrorFunc handler, + void *data); XMLPUBFUN void xmlCtxtSetMaxAmplification(xmlParserCtxtPtr ctxt, unsigned maxAmpl); @@ -1295,6 +1298,9 @@ XMLPUBFUN xmlDocPtr const char *URL, const char *encoding, int options); +XMLPUBFUN xmlDocPtr + xmlCtxtParseDocument (xmlParserCtxtPtr ctxt, + xmlParserInputPtr input); XMLPUBFUN xmlDocPtr xmlCtxtReadDoc (xmlParserCtxtPtr ctxt, const xmlChar *cur, @@ -1368,7 +1374,7 @@ typedef enum { XML_WITH_MODULES = 27, XML_WITH_DEBUG = 28, XML_WITH_DEBUG_MEM = 29, - XML_WITH_DEBUG_RUN = 30, + XML_WITH_DEBUG_RUN = 30, /* unused */ XML_WITH_ZLIB = 31, XML_WITH_ICU = 32, XML_WITH_LZMA = 33, diff --git a/include/support/libxml2/libxml/parserInternals.h b/include/support/libxml2/libxml/parserInternals.h index 017ed273..c4d4363b 100644 --- a/include/support/libxml2/libxml/parserInternals.h +++ b/include/support/libxml2/libxml/parserInternals.h @@ -25,11 +25,14 @@ extern "C" { /** * xmlParserMaxDepth: * + * DEPRECATED: has no effect + * * arbitrary depth limit for the XML documents that we allow to * process. This is not a limitation of the parser but a safety * boundary feature, use XML_PARSE_HUGE option to override it. */ -XMLPUBVAR unsigned int xmlParserMaxDepth; +XML_DEPRECATED +XMLPUBVAR const unsigned int xmlParserMaxDepth; /** * XML_MAX_TEXT_LENGTH: @@ -313,9 +316,14 @@ XMLPUBFUN xmlParserCtxtPtr xmlCreateEntityParserCtxt(const xmlChar *URL, const xmlChar *ID, const xmlChar *base); +XMLPUBFUN void + xmlCtxtErrMemory (xmlParserCtxtPtr ctxt); XMLPUBFUN int xmlSwitchEncoding (xmlParserCtxtPtr ctxt, xmlCharEncoding enc); +XMLPUBFUN int + xmlSwitchEncodingName (xmlParserCtxtPtr ctxt, + const char *encoding); XMLPUBFUN int xmlSwitchToEncoding (xmlParserCtxtPtr ctxt, xmlCharEncodingHandlerPtr handler); diff --git a/include/support/libxml2/libxml/tree.h b/include/support/libxml2/libxml/tree.h index a90a174f..4070375b 100644 --- a/include/support/libxml2/libxml/tree.h +++ b/include/support/libxml2/libxml/tree.h @@ -173,13 +173,13 @@ typedef enum { XML_TEXT_NODE= 3, XML_CDATA_SECTION_NODE= 4, XML_ENTITY_REF_NODE= 5, - XML_ENTITY_NODE= 6, + XML_ENTITY_NODE= 6, /* unused */ XML_PI_NODE= 7, XML_COMMENT_NODE= 8, XML_DOCUMENT_NODE= 9, - XML_DOCUMENT_TYPE_NODE= 10, + XML_DOCUMENT_TYPE_NODE= 10, /* unused */ XML_DOCUMENT_FRAG_NODE= 11, - XML_NOTATION_NODE= 12, + XML_NOTATION_NODE= 12, /* unused */ XML_HTML_DOCUMENT_NODE= 13, XML_DTD_NODE= 14, XML_ELEMENT_DECL= 15, @@ -449,6 +449,7 @@ struct _xmlAttr { xmlNs *ns; /* pointer to the associated namespace */ xmlAttributeType atype; /* the attribute type if validating */ void *psvi; /* for type/PSVI information */ + struct _xmlID *id; /* the ID struct */ }; /** @@ -1011,10 +1012,10 @@ XMLPUBFUN void xmlFreeNodeList (xmlNodePtr cur); XMLPUBFUN void xmlFreeNode (xmlNodePtr cur); -XMLPUBFUN void +XMLPUBFUN int xmlSetTreeDoc (xmlNodePtr tree, xmlDocPtr doc); -XMLPUBFUN void +XMLPUBFUN int xmlSetListDoc (xmlNodePtr list, xmlDocPtr doc); /* @@ -1030,6 +1031,10 @@ XMLPUBFUN xmlNsPtr const xmlChar *href); #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || \ defined(LIBXML_SCHEMAS_ENABLED) +XMLPUBFUN int + xmlGetNsListSafe (const xmlDoc *doc, + const xmlNode *node, + xmlNsPtr **out); XMLPUBFUN xmlNsPtr * xmlGetNsList (const xmlDoc *doc, const xmlNode *node); @@ -1059,6 +1064,11 @@ XMLPUBFUN xmlAttrPtr const xmlChar *value); #endif /* defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED) || \ defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_HTML_ENABLED) */ +XMLPUBFUN int + xmlNodeGetAttrValue (const xmlNode *node, + const xmlChar *name, + const xmlChar *nsUri, + xmlChar **out); XMLPUBFUN xmlChar * xmlGetNoNsProp (const xmlNode *node, const xmlChar *name); @@ -1093,19 +1103,19 @@ XMLPUBFUN xmlChar * const xmlNode *list, int inLine); #endif /* LIBXML_TREE_ENABLED */ -XMLPUBFUN void +XMLPUBFUN int xmlNodeSetContent (xmlNodePtr cur, const xmlChar *content); #ifdef LIBXML_TREE_ENABLED -XMLPUBFUN void +XMLPUBFUN int xmlNodeSetContentLen (xmlNodePtr cur, const xmlChar *content, int len); #endif /* LIBXML_TREE_ENABLED */ -XMLPUBFUN void +XMLPUBFUN int xmlNodeAddContent (xmlNodePtr cur, const xmlChar *content); -XMLPUBFUN void +XMLPUBFUN int xmlNodeAddContentLen (xmlNodePtr cur, const xmlChar *content, int len); @@ -1124,18 +1134,22 @@ XMLPUBFUN xmlChar * XMLPUBFUN int xmlNodeGetSpacePreserve (const xmlNode *cur); #ifdef LIBXML_TREE_ENABLED -XMLPUBFUN void +XMLPUBFUN int xmlNodeSetLang (xmlNodePtr cur, const xmlChar *lang); -XMLPUBFUN void +XMLPUBFUN int xmlNodeSetSpacePreserve (xmlNodePtr cur, int val); #endif /* LIBXML_TREE_ENABLED */ +XMLPUBFUN int + xmlNodeGetBaseSafe (const xmlDoc *doc, + const xmlNode *cur, + xmlChar **baseOut); XMLPUBFUN xmlChar * xmlNodeGetBase (const xmlDoc *doc, const xmlNode *cur); #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED) -XMLPUBFUN void +XMLPUBFUN int xmlNodeSetBase (xmlNodePtr cur, const xmlChar *uri); #endif @@ -1283,8 +1297,10 @@ XMLPUBFUN int XMLPUBFUN void xmlSetDocCompressMode (xmlDocPtr doc, int mode); +XML_DEPRECATED XMLPUBFUN int xmlGetCompressMode (void); +XML_DEPRECATED XMLPUBFUN void xmlSetCompressMode (int mode); @@ -1338,12 +1354,16 @@ XMLPUBFUN xmlNodePtr xmlPreviousElementSibling (xmlNodePtr node); #endif +XML_DEPRECATED XMLPUBFUN xmlRegisterNodeFunc xmlRegisterNodeDefault (xmlRegisterNodeFunc func); +XML_DEPRECATED XMLPUBFUN xmlDeregisterNodeFunc xmlDeregisterNodeDefault (xmlDeregisterNodeFunc func); +XML_DEPRECATED XMLPUBFUN xmlRegisterNodeFunc xmlThrDefRegisterNodeDefault(xmlRegisterNodeFunc func); +XML_DEPRECATED XMLPUBFUN xmlDeregisterNodeFunc xmlThrDefDeregisterNodeDefault(xmlDeregisterNodeFunc func); diff --git a/include/support/libxml2/libxml/uri.h b/include/support/libxml2/libxml/uri.h index eb8631cf..19980b71 100644 --- a/include/support/libxml2/libxml/uri.h +++ b/include/support/libxml2/libxml/uri.h @@ -52,14 +52,25 @@ struct _xmlURI { */ XMLPUBFUN xmlURIPtr xmlCreateURI (void); +XMLPUBFUN int + xmlBuildURISafe (const xmlChar *URI, + const xmlChar *base, + xmlChar **out); XMLPUBFUN xmlChar * xmlBuildURI (const xmlChar *URI, const xmlChar *base); +XMLPUBFUN int + xmlBuildRelativeURISafe (const xmlChar *URI, + const xmlChar *base, + xmlChar **out); XMLPUBFUN xmlChar * xmlBuildRelativeURI (const xmlChar *URI, const xmlChar *base); XMLPUBFUN xmlURIPtr xmlParseURI (const char *str); +XMLPUBFUN int + xmlParseURISafe (const char *str, + xmlURIPtr *uri); XMLPUBFUN xmlURIPtr xmlParseURIRaw (const char *str, int raw); diff --git a/include/support/libxml2/libxml/valid.h b/include/support/libxml2/libxml/valid.h index 3e04b552..e1698d7a 100644 --- a/include/support/libxml2/libxml/valid.h +++ b/include/support/libxml2/libxml/valid.h @@ -11,6 +11,7 @@ #ifndef __XML_VALID_H__ #define __XML_VALID_H__ +/** DOC_DISABLE */ #include #include #define XML_TREE_INTERNALS @@ -19,6 +20,7 @@ #include #include #include +/** DOC_ENABLE */ #ifdef __cplusplus extern "C" { @@ -150,9 +152,11 @@ XMLPUBFUN xmlNotationTablePtr XMLPUBFUN void xmlFreeNotationTable (xmlNotationTablePtr table); #ifdef LIBXML_OUTPUT_ENABLED +XML_DEPRECATED XMLPUBFUN void xmlDumpNotationDecl (xmlBufferPtr buf, xmlNotationPtr nota); +/* XML_DEPRECATED, still used in lxml */ XMLPUBFUN void xmlDumpNotationTable (xmlBufferPtr buf, xmlNotationTablePtr table); @@ -184,13 +188,12 @@ XMLPUBFUN void xmlElementContentPtr content, int englob); #ifdef LIBXML_OUTPUT_ENABLED -/* DEPRECATED */ +XML_DEPRECATED XMLPUBFUN void xmlSprintfElementContent(char *buf, xmlElementContentPtr content, int englob); #endif /* LIBXML_OUTPUT_ENABLED */ -/* DEPRECATED */ /* Element */ XMLPUBFUN xmlElementPtr @@ -206,9 +209,11 @@ XMLPUBFUN xmlElementTablePtr XMLPUBFUN void xmlFreeElementTable (xmlElementTablePtr table); #ifdef LIBXML_OUTPUT_ENABLED +XML_DEPRECATED XMLPUBFUN void xmlDumpElementTable (xmlBufferPtr buf, xmlElementTablePtr table); +XML_DEPRECATED XMLPUBFUN void xmlDumpElementDecl (xmlBufferPtr buf, xmlElementPtr elem); @@ -242,15 +247,20 @@ XMLPUBFUN xmlAttributeTablePtr XMLPUBFUN void xmlFreeAttributeTable (xmlAttributeTablePtr table); #ifdef LIBXML_OUTPUT_ENABLED +XML_DEPRECATED XMLPUBFUN void xmlDumpAttributeTable (xmlBufferPtr buf, xmlAttributeTablePtr table); +XML_DEPRECATED XMLPUBFUN void xmlDumpAttributeDecl (xmlBufferPtr buf, xmlAttributePtr attr); #endif /* LIBXML_OUTPUT_ENABLED */ /* IDs */ +XMLPUBFUN int + xmlAddIDSafe (xmlAttrPtr attr, + const xmlChar *value); XMLPUBFUN xmlIDPtr xmlAddID (xmlValidCtxtPtr ctxt, xmlDocPtr doc, @@ -303,31 +313,38 @@ XMLPUBFUN xmlValidCtxtPtr XMLPUBFUN void xmlFreeValidCtxt(xmlValidCtxtPtr); +XML_DEPRECATED XMLPUBFUN int xmlValidateRoot (xmlValidCtxtPtr ctxt, xmlDocPtr doc); +XML_DEPRECATED XMLPUBFUN int xmlValidateElementDecl (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlElementPtr elem); +XML_DEPRECATED XMLPUBFUN xmlChar * xmlValidNormalizeAttributeValue(xmlDocPtr doc, xmlNodePtr elem, const xmlChar *name, const xmlChar *value); +XML_DEPRECATED XMLPUBFUN xmlChar * xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *name, const xmlChar *value); +XML_DEPRECATED XMLPUBFUN int xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlAttributePtr attr); +XML_DEPRECATED XMLPUBFUN int xmlValidateAttributeValue(xmlAttributeType type, const xmlChar *value); +XML_DEPRECATED XMLPUBFUN int xmlValidateNotationDecl (xmlValidCtxtPtr ctxt, xmlDocPtr doc, @@ -336,6 +353,7 @@ XMLPUBFUN int xmlValidateDtd (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd); +XML_DEPRECATED XMLPUBFUN int xmlValidateDtdFinal (xmlValidCtxtPtr ctxt, xmlDocPtr doc); @@ -346,16 +364,19 @@ XMLPUBFUN int xmlValidateElement (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem); +XML_DEPRECATED XMLPUBFUN int xmlValidateOneElement (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem); +XML_DEPRECATED XMLPUBFUN int xmlValidateOneAttribute (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value); +XML_DEPRECATED XMLPUBFUN int xmlValidateOneNamespace (xmlValidCtxtPtr ctxt, xmlDocPtr doc, @@ -363,12 +384,14 @@ XMLPUBFUN int const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value); +XML_DEPRECATED XMLPUBFUN int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc); #endif /* LIBXML_VALID_ENABLED */ #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +XML_DEPRECATED XMLPUBFUN int xmlValidateNotationUse (xmlValidCtxtPtr ctxt, xmlDocPtr doc, @@ -424,19 +447,23 @@ XMLPUBFUN int /* * Validation based on the regexp support */ +XML_DEPRECATED XMLPUBFUN int xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem); +XML_DEPRECATED XMLPUBFUN int xmlValidatePushElement (xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *qname); +XML_DEPRECATED XMLPUBFUN int xmlValidatePushCData (xmlValidCtxtPtr ctxt, const xmlChar *data, int len); +XML_DEPRECATED XMLPUBFUN int xmlValidatePopElement (xmlValidCtxtPtr ctxt, xmlDocPtr doc, diff --git a/include/support/libxml2/libxml/xmlIO.h b/include/support/libxml2/libxml/xmlIO.h index 2487be3b..5a35dc64 100644 --- a/include/support/libxml2/libxml/xmlIO.h +++ b/include/support/libxml2/libxml/xmlIO.h @@ -10,12 +10,14 @@ #ifndef __XML_IO_H__ #define __XML_IO_H__ +/** DOC_DISABLE */ #include #include #include #define XML_TREE_INTERNALS #include #undef XML_TREE_INTERNALS +/** DOC_ENABLE */ #ifdef __cplusplus extern "C" { @@ -323,12 +325,14 @@ xmlOutputBufferPtr #ifdef LIBXML_HTTP_ENABLED /* This function only exists if HTTP support built into the library */ +XML_DEPRECATED XMLPUBFUN void xmlRegisterHTTPPostCallbacks (void ); #endif /* LIBXML_HTTP_ENABLED */ #endif /* LIBXML_OUTPUT_ENABLED */ +XML_DEPRECATED XMLPUBFUN xmlParserInputPtr xmlCheckHTTPInput (xmlParserCtxtPtr ctxt, xmlParserInputPtr ret); @@ -341,26 +345,28 @@ XMLPUBFUN xmlParserInputPtr const char *ID, xmlParserCtxtPtr ctxt); -/* - * xmlNormalizeWindowsPath is obsolete, don't use it. - * Check xmlCanonicPath in uri.h for a better alternative. - */ +XML_DEPRECATED XMLPUBFUN xmlChar * xmlNormalizeWindowsPath (const xmlChar *path); +XML_DEPRECATED XMLPUBFUN int xmlCheckFilename (const char *path); /** * Default 'file://' protocol callbacks */ +XML_DEPRECATED XMLPUBFUN int xmlFileMatch (const char *filename); +XML_DEPRECATED XMLPUBFUN void * xmlFileOpen (const char *filename); +XML_DEPRECATED XMLPUBFUN int xmlFileRead (void * context, char * buffer, int len); +XML_DEPRECATED XMLPUBFUN int xmlFileClose (void * context); @@ -368,19 +374,24 @@ XMLPUBFUN int * Default 'http://' protocol callbacks */ #ifdef LIBXML_HTTP_ENABLED +XML_DEPRECATED XMLPUBFUN int xmlIOHTTPMatch (const char *filename); +XML_DEPRECATED XMLPUBFUN void * xmlIOHTTPOpen (const char *filename); #ifdef LIBXML_OUTPUT_ENABLED +XML_DEPRECATED XMLPUBFUN void * xmlIOHTTPOpenW (const char * post_uri, int compression ); #endif /* LIBXML_OUTPUT_ENABLED */ +XML_DEPRECATED XMLPUBFUN int xmlIOHTTPRead (void * context, char * buffer, int len); +XML_DEPRECATED XMLPUBFUN int xmlIOHTTPClose (void * context); #endif /* LIBXML_HTTP_ENABLED */ @@ -389,14 +400,18 @@ XMLPUBFUN int * Default 'ftp://' protocol callbacks */ #if defined(LIBXML_FTP_ENABLED) +XML_DEPRECATED XMLPUBFUN int xmlIOFTPMatch (const char *filename); +XML_DEPRECATED XMLPUBFUN void * xmlIOFTPOpen (const char *filename); +XML_DEPRECATED XMLPUBFUN int xmlIOFTPRead (void * context, char * buffer, int len); +XML_DEPRECATED XMLPUBFUN int xmlIOFTPClose (void * context); #endif /* defined(LIBXML_FTP_ENABLED) */ @@ -407,9 +422,11 @@ XMLPUBFUN xmlParserInputBufferCreateFilenameFunc XMLPUBFUN xmlOutputBufferCreateFilenameFunc xmlOutputBufferCreateFilenameDefault( xmlOutputBufferCreateFilenameFunc func); +XML_DEPRECATED XMLPUBFUN xmlOutputBufferCreateFilenameFunc xmlThrDefOutputBufferCreateFilenameDefault( xmlOutputBufferCreateFilenameFunc func); +XML_DEPRECATED XMLPUBFUN xmlParserInputBufferCreateFilenameFunc xmlThrDefParserInputBufferCreateFilenameDefault( xmlParserInputBufferCreateFilenameFunc func); diff --git a/include/support/libxml2/libxml/xmlerror.h b/include/support/libxml2/libxml/xmlerror.h index 1f0ab4a3..36381bec 100644 --- a/include/support/libxml2/libxml/xmlerror.h +++ b/include/support/libxml2/libxml/xmlerror.h @@ -211,6 +211,11 @@ typedef enum { XML_ERR_USER_STOP, /* 111 */ XML_ERR_COMMENT_ABRUPTLY_ENDED, /* 112 */ XML_WAR_ENCODING_MISMATCH, /* 113 */ + XML_ERR_RESOURCE_LIMIT, /* 114 */ + XML_ERR_ARGUMENT, /* 115 */ + XML_ERR_SYSTEM, /* 116 */ + XML_ERR_REDECL_PREDEF_ENTITY, /* 117 */ + XML_ERR_INT_SUBSET_NOT_FINISHED, /* 118 */ XML_NS_ERR_XML_NAMESPACE = 200, XML_NS_ERR_UNDEFINED_NAMESPACE, /* 201 */ XML_NS_ERR_QNAME, /* 202 */ @@ -473,6 +478,7 @@ typedef enum { XML_IO_EADDRINUSE, /* 1554 */ XML_IO_EALREADY, /* 1555 */ XML_IO_EAFNOSUPPORT, /* 1556 */ + XML_IO_UNSUPPORTED_PROTOCOL, /* 1557 */ XML_XINCLUDE_RECURSION=1600, XML_XINCLUDE_PARSE_VALUE, /* 1601 */ XML_XINCLUDE_ENTITY_DEF_MISMATCH, /* 1602 */ @@ -886,6 +892,7 @@ XML_GLOBALS_ERROR XMLPUBFUN void xmlSetGenericErrorFunc (void *ctx, xmlGenericErrorFunc handler); +XML_DEPRECATED XMLPUBFUN void xmlThrDefSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler); @@ -896,6 +903,7 @@ XMLPUBFUN void XMLPUBFUN void xmlSetStructuredErrorFunc (void *ctx, xmlStructuredErrorFunc handler); +XML_DEPRECATED XMLPUBFUN void xmlThrDefSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler); @@ -919,11 +927,17 @@ XMLPUBFUN void xmlParserValidityWarning (void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); +/** DOC_DISABLE */ struct _xmlParserInput; +/** DOC_ENABLE */ XMLPUBFUN void xmlParserPrintFileInfo (struct _xmlParserInput *input); XMLPUBFUN void xmlParserPrintFileContext (struct _xmlParserInput *input); +XMLPUBFUN void +xmlFormatError (const xmlError *err, + xmlGenericErrorFunc channel, + void *data); /* * Extended error information routines diff --git a/include/support/libxml2/libxml/xmlexports.h b/include/support/libxml2/libxml/xmlexports.h index 3b063e7d..e07ac6c0 100644 --- a/include/support/libxml2/libxml/xmlexports.h +++ b/include/support/libxml2/libxml/xmlexports.h @@ -9,6 +9,11 @@ #define __XML_EXPORTS_H__ /** DOC_DISABLE */ + +/* + * Symbol visibility + */ + #if defined(_WIN32) || defined(__CYGWIN__) #ifdef LIBXML_STATIC #define XMLPUBLIC @@ -20,30 +25,121 @@ #else /* not Windows */ #define XMLPUBLIC #endif /* platform switch */ -/** DOC_ENABLE */ -/* - * XMLPUBFUN: - * - * Macro which declares an exportable function - */ #define XMLPUBFUN XMLPUBLIC -/** - * XMLPUBVAR: - * - * Macro which declares an exportable variable - */ #define XMLPUBVAR XMLPUBLIC extern -/** DOC_DISABLE */ /* Compatibility */ #define XMLCALL #define XMLCDECL -#if !defined(LIBXML_DLL_IMPORT) -#define LIBXML_DLL_IMPORT XMLPUBVAR +#ifndef LIBXML_DLL_IMPORT + #define LIBXML_DLL_IMPORT XMLPUBVAR +#endif + +/* + * Attributes + */ + +#ifndef ATTRIBUTE_UNUSED + #if __GNUC__ * 100 + __GNUC_MINOR__ >= 207 || defined(__clang__) + #define ATTRIBUTE_UNUSED __attribute__((unused)) + #else + #define ATTRIBUTE_UNUSED + #endif +#endif + +#if !defined(__clang__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403) + #define LIBXML_ATTR_ALLOC_SIZE(x) __attribute__((alloc_size(x))) +#else + #define LIBXML_ATTR_ALLOC_SIZE(x) +#endif + +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 303 + #define LIBXML_ATTR_FORMAT(fmt,args) \ + __attribute__((__format__(__printf__,fmt,args))) +#else + #define LIBXML_ATTR_FORMAT(fmt,args) +#endif + +#ifndef XML_DEPRECATED + #if defined(IN_LIBXML) + #define XML_DEPRECATED + #elif __GNUC__ * 100 + __GNUC_MINOR__ >= 301 + #define XML_DEPRECATED __attribute__((deprecated)) + #elif _MSC_VER >= 1400 + /* Available since Visual Studio 2005 */ + #define XML_DEPRECATED __declspec(deprecated) + #else + #define XML_DEPRECATED + #endif +#endif + +/* + * Warnings pragmas, should be moved from public headers + */ + +#if defined(__LCC__) + + #define XML_IGNORE_FPTR_CAST_WARNINGS + #define XML_POP_WARNINGS \ + _Pragma("diag_default 1215") + +#elif defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) + + #if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 800) + #define XML_IGNORE_FPTR_CAST_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ + _Pragma("GCC diagnostic ignored \"-Wcast-function-type\"") + #else + #define XML_IGNORE_FPTR_CAST_WARNINGS \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wpedantic\"") + #endif + #define XML_POP_WARNINGS \ + _Pragma("GCC diagnostic pop") + +#elif _MSC_VER >= 1400 + + #define XML_IGNORE_FPTR_CAST_WARNINGS __pragma(warning(push)) + #define XML_POP_WARNINGS __pragma(warning(pop)) + +#else + + #define XML_IGNORE_FPTR_CAST_WARNINGS + #define XML_POP_WARNINGS + +#endif + +/* + * Accessors for globals + */ + +#define XML_NO_ATTR + +#ifdef LIBXML_THREAD_ENABLED + #define XML_DECLARE_GLOBAL(name, type, attrs) \ + attrs XMLPUBFUN type *__##name(void); + #define XML_GLOBAL_MACRO(name) (*__##name()) +#else + #define XML_DECLARE_GLOBAL(name, type, attrs) \ + attrs XMLPUBVAR type name; +#endif + +/* + * Originally declared in xmlversion.h which is generated + */ + +#ifdef __cplusplus +extern "C" { +#endif + +XMLPUBFUN void xmlCheckVersion(int version); + +#ifdef __cplusplus +} #endif -/** DOC_ENABLE */ #endif /* __XML_EXPORTS_H__ */ diff --git a/include/support/libxml2/libxml/xmlmemory.h b/include/support/libxml2/libxml/xmlmemory.h index 097e3c8f..1de3e9fc 100644 --- a/include/support/libxml2/libxml/xmlmemory.h +++ b/include/support/libxml2/libxml/xmlmemory.h @@ -147,12 +147,16 @@ XMLPUBFUN int xmlMemUsed (void); XMLPUBFUN int xmlMemBlocks (void); +XML_DEPRECATED XMLPUBFUN void xmlMemDisplay (FILE *fp); +XML_DEPRECATED XMLPUBFUN void xmlMemDisplayLast(FILE *fp, long nbBytes); +XML_DEPRECATED XMLPUBFUN void xmlMemShow (FILE *fp, int nr); +XML_DEPRECATED XMLPUBFUN void xmlMemoryDump (void); XMLPUBFUN void * @@ -163,60 +167,19 @@ XMLPUBFUN void xmlMemFree (void *ptr); XMLPUBFUN char * xmlMemoryStrdup (const char *str); +XML_DEPRECATED XMLPUBFUN void * xmlMallocLoc (size_t size, const char *file, int line) LIBXML_ATTR_ALLOC_SIZE(1); +XML_DEPRECATED XMLPUBFUN void * xmlReallocLoc (void *ptr, size_t size, const char *file, int line); +XML_DEPRECATED XMLPUBFUN void * xmlMallocAtomicLoc (size_t size, const char *file, int line) LIBXML_ATTR_ALLOC_SIZE(1); +XML_DEPRECATED XMLPUBFUN char * xmlMemStrdupLoc (const char *str, const char *file, int line); - -/** DOC_DISABLE */ -#ifdef DEBUG_MEMORY_LOCATION -/** - * xmlMalloc: - * @size: number of bytes to allocate - * - * Wrapper for the malloc() function used in the XML library. - * - * Returns the pointer to the allocated area or NULL in case of error. - */ -#define xmlMalloc(size) xmlMallocLoc((size), __FILE__, __LINE__) -/** - * xmlMallocAtomic: - * @size: number of bytes to allocate - * - * Wrapper for the malloc() function used in the XML library for allocation - * of block not containing pointers to other areas. - * - * Returns the pointer to the allocated area or NULL in case of error. - */ -#define xmlMallocAtomic(size) xmlMallocAtomicLoc((size), __FILE__, __LINE__) -/** - * xmlRealloc: - * @ptr: pointer to the existing allocated area - * @size: number of bytes to allocate - * - * Wrapper for the realloc() function used in the XML library. - * - * Returns the pointer to the allocated area or NULL in case of error. - */ -#define xmlRealloc(ptr, size) xmlReallocLoc((ptr), (size), __FILE__, __LINE__) -/** - * xmlMemStrdup: - * @str: pointer to the existing string - * - * Wrapper for the strdup() function, xmlStrdup() is usually preferred. - * - * Returns the pointer to the allocated area or NULL in case of error. - */ -#define xmlMemStrdup(str) xmlMemStrdupLoc((str), __FILE__, __LINE__) - -#endif /* DEBUG_MEMORY_LOCATION */ -/** DOC_ENABLE */ - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/include/support/libxml2/libxml/xmlreader.h b/include/support/libxml2/libxml/xmlreader.h index b9f69897..5d4fc5d5 100644 --- a/include/support/libxml2/libxml/xmlreader.h +++ b/include/support/libxml2/libxml/xmlreader.h @@ -127,6 +127,8 @@ XMLPUBFUN int XMLPUBFUN void xmlTextReaderSetMaxAmplification(xmlTextReaderPtr reader, unsigned maxAmpl); +XMLPUBFUN const xmlError * + xmlTextReaderGetLastError(xmlTextReaderPtr reader); /* * Iterators diff --git a/include/support/libxml2/libxml/xmlsave.h b/include/support/libxml2/libxml/xmlsave.h index fbf293a2..e266e467 100644 --- a/include/support/libxml2/libxml/xmlsave.h +++ b/include/support/libxml2/libxml/xmlsave.h @@ -73,6 +73,8 @@ XMLPUBFUN int xmlSaveFlush (xmlSaveCtxtPtr ctxt); XMLPUBFUN int xmlSaveClose (xmlSaveCtxtPtr ctxt); +XMLPUBFUN int + xmlSaveFinish (xmlSaveCtxtPtr ctxt); XMLPUBFUN int xmlSaveSetEscape (xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape); @@ -80,10 +82,13 @@ XMLPUBFUN int xmlSaveSetAttrEscape (xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape); +XML_DEPRECATED XMLPUBFUN int xmlThrDefIndentTreeOutput(int v); +XML_DEPRECATED XMLPUBFUN const char * xmlThrDefTreeIndentString(const char * v); +XML_DEPRECATED XMLPUBFUN int xmlThrDefSaveNoEmptyTags(int v); diff --git a/include/support/libxml2/libxml/xmlwriter.h b/include/support/libxml2/libxml/xmlwriter.h index 339f2511..55f88bc7 100644 --- a/include/support/libxml2/libxml/xmlwriter.h +++ b/include/support/libxml2/libxml/xmlwriter.h @@ -478,6 +478,7 @@ extern "C" { * misc */ XMLPUBFUN int xmlTextWriterFlush(xmlTextWriterPtr writer); + XMLPUBFUN int xmlTextWriterClose(xmlTextWriterPtr writer); #ifdef __cplusplus } diff --git a/include/support/libxml2/libxml/xpath.h b/include/support/libxml2/libxml/xpath.h new file mode 100644 index 00000000..b89e105c --- /dev/null +++ b/include/support/libxml2/libxml/xpath.h @@ -0,0 +1,579 @@ +/* + * Summary: XML Path Language implementation + * Description: API for the XML Path Language implementation + * + * XML Path Language implementation + * XPath is a language for addressing parts of an XML document, + * designed to be used by both XSLT and XPointer + * http://www.w3.org/TR/xpath + * + * Implements + * W3C Recommendation 16 November 1999 + * http://www.w3.org/TR/1999/REC-xpath-19991116 + * + * Copy: See Copyright for the status of this software. + * + * Author: Daniel Veillard + */ + +#ifndef __XML_XPATH_H__ +#define __XML_XPATH_H__ + +#include + +#ifdef LIBXML_XPATH_ENABLED + +#include +#include +#include +#endif /* LIBXML_XPATH_ENABLED */ + +#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +#ifdef __cplusplus +extern "C" { +#endif +#endif /* LIBXML_XPATH_ENABLED or LIBXML_SCHEMAS_ENABLED */ + +#ifdef LIBXML_XPATH_ENABLED + +typedef struct _xmlXPathContext xmlXPathContext; +typedef xmlXPathContext *xmlXPathContextPtr; +typedef struct _xmlXPathParserContext xmlXPathParserContext; +typedef xmlXPathParserContext *xmlXPathParserContextPtr; + +/** + * The set of XPath error codes. + */ + +typedef enum { + XPATH_EXPRESSION_OK = 0, + XPATH_NUMBER_ERROR, + XPATH_UNFINISHED_LITERAL_ERROR, + XPATH_START_LITERAL_ERROR, + XPATH_VARIABLE_REF_ERROR, + XPATH_UNDEF_VARIABLE_ERROR, + XPATH_INVALID_PREDICATE_ERROR, + XPATH_EXPR_ERROR, + XPATH_UNCLOSED_ERROR, + XPATH_UNKNOWN_FUNC_ERROR, + XPATH_INVALID_OPERAND, + XPATH_INVALID_TYPE, + XPATH_INVALID_ARITY, + XPATH_INVALID_CTXT_SIZE, + XPATH_INVALID_CTXT_POSITION, + XPATH_MEMORY_ERROR, + XPTR_SYNTAX_ERROR, + XPTR_RESOURCE_ERROR, + XPTR_SUB_RESOURCE_ERROR, + XPATH_UNDEF_PREFIX_ERROR, + XPATH_ENCODING_ERROR, + XPATH_INVALID_CHAR_ERROR, + XPATH_INVALID_CTXT, + XPATH_STACK_ERROR, + XPATH_FORBID_VARIABLE_ERROR, + XPATH_OP_LIMIT_EXCEEDED, + XPATH_RECURSION_LIMIT_EXCEEDED +} xmlXPathError; + +/* + * A node-set (an unordered collection of nodes without duplicates). + */ +typedef struct _xmlNodeSet xmlNodeSet; +typedef xmlNodeSet *xmlNodeSetPtr; +struct _xmlNodeSet { + int nodeNr; /* number of nodes in the set */ + int nodeMax; /* size of the array as allocated */ + xmlNodePtr *nodeTab; /* array of nodes in no particular order */ + /* @@ with_ns to check whether namespace nodes should be looked at @@ */ +}; + +/* + * An expression is evaluated to yield an object, which + * has one of the following four basic types: + * - node-set + * - boolean + * - number + * - string + * + * @@ XPointer will add more types ! + */ + +typedef enum { + XPATH_UNDEFINED = 0, + XPATH_NODESET = 1, + XPATH_BOOLEAN = 2, + XPATH_NUMBER = 3, + XPATH_STRING = 4, +#ifdef LIBXML_XPTR_LOCS_ENABLED + XPATH_POINT = 5, + XPATH_RANGE = 6, + XPATH_LOCATIONSET = 7, +#endif + XPATH_USERS = 8, + XPATH_XSLT_TREE = 9 /* An XSLT value tree, non modifiable */ +} xmlXPathObjectType; + +#ifndef LIBXML_XPTR_LOCS_ENABLED +/** DOC_DISABLE */ +#define XPATH_POINT 5 +#define XPATH_RANGE 6 +#define XPATH_LOCATIONSET 7 +/** DOC_ENABLE */ +#endif + +typedef struct _xmlXPathObject xmlXPathObject; +typedef xmlXPathObject *xmlXPathObjectPtr; +struct _xmlXPathObject { + xmlXPathObjectType type; + xmlNodeSetPtr nodesetval; + int boolval; + double floatval; + xmlChar *stringval; + void *user; + int index; + void *user2; + int index2; +}; + +/** + * xmlXPathConvertFunc: + * @obj: an XPath object + * @type: the number of the target type + * + * A conversion function is associated to a type and used to cast + * the new type to primitive values. + * + * Returns -1 in case of error, 0 otherwise + */ +typedef int (*xmlXPathConvertFunc) (xmlXPathObjectPtr obj, int type); + +/* + * Extra type: a name and a conversion function. + */ + +typedef struct _xmlXPathType xmlXPathType; +typedef xmlXPathType *xmlXPathTypePtr; +struct _xmlXPathType { + const xmlChar *name; /* the type name */ + xmlXPathConvertFunc func; /* the conversion function */ +}; + +/* + * Extra variable: a name and a value. + */ + +typedef struct _xmlXPathVariable xmlXPathVariable; +typedef xmlXPathVariable *xmlXPathVariablePtr; +struct _xmlXPathVariable { + const xmlChar *name; /* the variable name */ + xmlXPathObjectPtr value; /* the value */ +}; + +/** + * xmlXPathEvalFunc: + * @ctxt: an XPath parser context + * @nargs: the number of arguments passed to the function + * + * An XPath evaluation function, the parameters are on the XPath context stack. + */ + +typedef void (*xmlXPathEvalFunc)(xmlXPathParserContextPtr ctxt, + int nargs); + +/* + * Extra function: a name and a evaluation function. + */ + +typedef struct _xmlXPathFunct xmlXPathFunct; +typedef xmlXPathFunct *xmlXPathFuncPtr; +struct _xmlXPathFunct { + const xmlChar *name; /* the function name */ + xmlXPathEvalFunc func; /* the evaluation function */ +}; + +/** + * xmlXPathAxisFunc: + * @ctxt: the XPath interpreter context + * @cur: the previous node being explored on that axis + * + * An axis traversal function. To traverse an axis, the engine calls + * the first time with cur == NULL and repeat until the function returns + * NULL indicating the end of the axis traversal. + * + * Returns the next node in that axis or NULL if at the end of the axis. + */ + +typedef xmlXPathObjectPtr (*xmlXPathAxisFunc) (xmlXPathParserContextPtr ctxt, + xmlXPathObjectPtr cur); + +/* + * Extra axis: a name and an axis function. + */ + +typedef struct _xmlXPathAxis xmlXPathAxis; +typedef xmlXPathAxis *xmlXPathAxisPtr; +struct _xmlXPathAxis { + const xmlChar *name; /* the axis name */ + xmlXPathAxisFunc func; /* the search function */ +}; + +/** + * xmlXPathFunction: + * @ctxt: the XPath interprestation context + * @nargs: the number of arguments + * + * An XPath function. + * The arguments (if any) are popped out from the context stack + * and the result is pushed on the stack. + */ + +typedef void (*xmlXPathFunction) (xmlXPathParserContextPtr ctxt, int nargs); + +/* + * Function and Variable Lookup. + */ + +/** + * xmlXPathVariableLookupFunc: + * @ctxt: an XPath context + * @name: name of the variable + * @ns_uri: the namespace name hosting this variable + * + * Prototype for callbacks used to plug variable lookup in the XPath + * engine. + * + * Returns the XPath object value or NULL if not found. + */ +typedef xmlXPathObjectPtr (*xmlXPathVariableLookupFunc) (void *ctxt, + const xmlChar *name, + const xmlChar *ns_uri); + +/** + * xmlXPathFuncLookupFunc: + * @ctxt: an XPath context + * @name: name of the function + * @ns_uri: the namespace name hosting this function + * + * Prototype for callbacks used to plug function lookup in the XPath + * engine. + * + * Returns the XPath function or NULL if not found. + */ +typedef xmlXPathFunction (*xmlXPathFuncLookupFunc) (void *ctxt, + const xmlChar *name, + const xmlChar *ns_uri); + +/** + * xmlXPathFlags: + * Flags for XPath engine compilation and runtime + */ +/** + * XML_XPATH_CHECKNS: + * + * check namespaces at compilation + */ +#define XML_XPATH_CHECKNS (1<<0) +/** + * XML_XPATH_NOVAR: + * + * forbid variables in expression + */ +#define XML_XPATH_NOVAR (1<<1) + +/** + * xmlXPathContext: + * + * Expression evaluation occurs with respect to a context. + * he context consists of: + * - a node (the context node) + * - a node list (the context node list) + * - a set of variable bindings + * - a function library + * - the set of namespace declarations in scope for the expression + * Following the switch to hash tables, this need to be trimmed up at + * the next binary incompatible release. + * The node may be modified when the context is passed to libxml2 + * for an XPath evaluation so you may need to initialize it again + * before the next call. + */ + +struct _xmlXPathContext { + xmlDocPtr doc; /* The current document */ + xmlNodePtr node; /* The current node */ + + int nb_variables_unused; /* unused (hash table) */ + int max_variables_unused; /* unused (hash table) */ + xmlHashTablePtr varHash; /* Hash table of defined variables */ + + int nb_types; /* number of defined types */ + int max_types; /* max number of types */ + xmlXPathTypePtr types; /* Array of defined types */ + + int nb_funcs_unused; /* unused (hash table) */ + int max_funcs_unused; /* unused (hash table) */ + xmlHashTablePtr funcHash; /* Hash table of defined funcs */ + + int nb_axis; /* number of defined axis */ + int max_axis; /* max number of axis */ + xmlXPathAxisPtr axis; /* Array of defined axis */ + + /* the namespace nodes of the context node */ + xmlNsPtr *namespaces; /* Array of namespaces */ + int nsNr; /* number of namespace in scope */ + void *user; /* function to free */ + + /* extra variables */ + int contextSize; /* the context size */ + int proximityPosition; /* the proximity position */ + + /* extra stuff for XPointer */ + int xptr; /* is this an XPointer context? */ + xmlNodePtr here; /* for here() */ + xmlNodePtr origin; /* for origin() */ + + /* the set of namespace declarations in scope for the expression */ + xmlHashTablePtr nsHash; /* The namespaces hash table */ + xmlXPathVariableLookupFunc varLookupFunc;/* variable lookup func */ + void *varLookupData; /* variable lookup data */ + + /* Possibility to link in an extra item */ + void *extra; /* needed for XSLT */ + + /* The function name and URI when calling a function */ + const xmlChar *function; + const xmlChar *functionURI; + + /* function lookup function and data */ + xmlXPathFuncLookupFunc funcLookupFunc;/* function lookup func */ + void *funcLookupData; /* function lookup data */ + + /* temporary namespace lists kept for walking the namespace axis */ + xmlNsPtr *tmpNsList; /* Array of namespaces */ + int tmpNsNr; /* number of namespaces in scope */ + + /* error reporting mechanism */ + void *userData; /* user specific data block */ + xmlStructuredErrorFunc error; /* the callback in case of errors */ + xmlError lastError; /* the last error */ + xmlNodePtr debugNode; /* the source node XSLT */ + + /* dictionary */ + xmlDictPtr dict; /* dictionary if any */ + + int flags; /* flags to control compilation */ + + /* Cache for reusal of XPath objects */ + void *cache; + + /* Resource limits */ + unsigned long opLimit; + unsigned long opCount; + int depth; +}; + +/* + * The structure of a compiled expression form is not public. + */ + +typedef struct _xmlXPathCompExpr xmlXPathCompExpr; +typedef xmlXPathCompExpr *xmlXPathCompExprPtr; + +/** + * xmlXPathParserContext: + * + * An XPath parser context. It contains pure parsing information, + * an xmlXPathContext, and the stack of objects. + */ +struct _xmlXPathParserContext { + const xmlChar *cur; /* the current char being parsed */ + const xmlChar *base; /* the full expression */ + + int error; /* error code */ + + xmlXPathContextPtr context; /* the evaluation context */ + xmlXPathObjectPtr value; /* the current value */ + int valueNr; /* number of values stacked */ + int valueMax; /* max number of values stacked */ + xmlXPathObjectPtr *valueTab; /* stack of values */ + + xmlXPathCompExprPtr comp; /* the precompiled expression */ + int xptr; /* it this an XPointer expression */ + xmlNodePtr ancestor; /* used for walking preceding axis */ + + int valueFrame; /* always zero for compatibility */ +}; + +/************************************************************************ + * * + * Public API * + * * + ************************************************************************/ + +/** + * Objects and Nodesets handling + */ + +XMLPUBVAR double xmlXPathNAN; +XMLPUBVAR double xmlXPathPINF; +XMLPUBVAR double xmlXPathNINF; + +/* These macros may later turn into functions */ +/** + * xmlXPathNodeSetGetLength: + * @ns: a node-set + * + * Implement a functionality similar to the DOM NodeList.length. + * + * Returns the number of nodes in the node-set. + */ +#define xmlXPathNodeSetGetLength(ns) ((ns) ? (ns)->nodeNr : 0) +/** + * xmlXPathNodeSetItem: + * @ns: a node-set + * @index: index of a node in the set + * + * Implements a functionality similar to the DOM NodeList.item(). + * + * Returns the xmlNodePtr at the given @index in @ns or NULL if + * @index is out of range (0 to length-1) + */ +#define xmlXPathNodeSetItem(ns, index) \ + ((((ns) != NULL) && \ + ((index) >= 0) && ((index) < (ns)->nodeNr)) ? \ + (ns)->nodeTab[(index)] \ + : NULL) +/** + * xmlXPathNodeSetIsEmpty: + * @ns: a node-set + * + * Checks whether @ns is empty or not. + * + * Returns %TRUE if @ns is an empty node-set. + */ +#define xmlXPathNodeSetIsEmpty(ns) \ + (((ns) == NULL) || ((ns)->nodeNr == 0) || ((ns)->nodeTab == NULL)) + + +XMLPUBFUN void + xmlXPathFreeObject (xmlXPathObjectPtr obj); +XMLPUBFUN xmlNodeSetPtr + xmlXPathNodeSetCreate (xmlNodePtr val); +XMLPUBFUN void + xmlXPathFreeNodeSetList (xmlXPathObjectPtr obj); +XMLPUBFUN void + xmlXPathFreeNodeSet (xmlNodeSetPtr obj); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathObjectCopy (xmlXPathObjectPtr val); +XMLPUBFUN int + xmlXPathCmpNodes (xmlNodePtr node1, + xmlNodePtr node2); +/** + * Conversion functions to basic types. + */ +XMLPUBFUN int + xmlXPathCastNumberToBoolean (double val); +XMLPUBFUN int + xmlXPathCastStringToBoolean (const xmlChar * val); +XMLPUBFUN int + xmlXPathCastNodeSetToBoolean(xmlNodeSetPtr ns); +XMLPUBFUN int + xmlXPathCastToBoolean (xmlXPathObjectPtr val); + +XMLPUBFUN double + xmlXPathCastBooleanToNumber (int val); +XMLPUBFUN double + xmlXPathCastStringToNumber (const xmlChar * val); +XMLPUBFUN double + xmlXPathCastNodeToNumber (xmlNodePtr node); +XMLPUBFUN double + xmlXPathCastNodeSetToNumber (xmlNodeSetPtr ns); +XMLPUBFUN double + xmlXPathCastToNumber (xmlXPathObjectPtr val); + +XMLPUBFUN xmlChar * + xmlXPathCastBooleanToString (int val); +XMLPUBFUN xmlChar * + xmlXPathCastNumberToString (double val); +XMLPUBFUN xmlChar * + xmlXPathCastNodeToString (xmlNodePtr node); +XMLPUBFUN xmlChar * + xmlXPathCastNodeSetToString (xmlNodeSetPtr ns); +XMLPUBFUN xmlChar * + xmlXPathCastToString (xmlXPathObjectPtr val); + +XMLPUBFUN xmlXPathObjectPtr + xmlXPathConvertBoolean (xmlXPathObjectPtr val); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathConvertNumber (xmlXPathObjectPtr val); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathConvertString (xmlXPathObjectPtr val); + +/** + * Context handling. + */ +XMLPUBFUN xmlXPathContextPtr + xmlXPathNewContext (xmlDocPtr doc); +XMLPUBFUN void + xmlXPathFreeContext (xmlXPathContextPtr ctxt); +XMLPUBFUN void + xmlXPathSetErrorHandler(xmlXPathContextPtr ctxt, + xmlStructuredErrorFunc handler, + void *context); +XMLPUBFUN int + xmlXPathContextSetCache(xmlXPathContextPtr ctxt, + int active, + int value, + int options); +/** + * Evaluation functions. + */ +XMLPUBFUN long + xmlXPathOrderDocElems (xmlDocPtr doc); +XMLPUBFUN int + xmlXPathSetContextNode (xmlNodePtr node, + xmlXPathContextPtr ctx); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathNodeEval (xmlNodePtr node, + const xmlChar *str, + xmlXPathContextPtr ctx); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathEval (const xmlChar *str, + xmlXPathContextPtr ctx); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathEvalExpression (const xmlChar *str, + xmlXPathContextPtr ctxt); +XMLPUBFUN int + xmlXPathEvalPredicate (xmlXPathContextPtr ctxt, + xmlXPathObjectPtr res); +/** + * Separate compilation/evaluation entry points. + */ +XMLPUBFUN xmlXPathCompExprPtr + xmlXPathCompile (const xmlChar *str); +XMLPUBFUN xmlXPathCompExprPtr + xmlXPathCtxtCompile (xmlXPathContextPtr ctxt, + const xmlChar *str); +XMLPUBFUN xmlXPathObjectPtr + xmlXPathCompiledEval (xmlXPathCompExprPtr comp, + xmlXPathContextPtr ctx); +XMLPUBFUN int + xmlXPathCompiledEvalToBoolean(xmlXPathCompExprPtr comp, + xmlXPathContextPtr ctxt); +XMLPUBFUN void + xmlXPathFreeCompExpr (xmlXPathCompExprPtr comp); +#endif /* LIBXML_XPATH_ENABLED */ +#if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) +XML_DEPRECATED +XMLPUBFUN void + xmlXPathInit (void); +XMLPUBFUN int + xmlXPathIsNaN (double val); +XMLPUBFUN int + xmlXPathIsInf (double val); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBXML_XPATH_ENABLED or LIBXML_SCHEMAS_ENABLED*/ +#endif /* ! __XML_XPATH_H__ */ diff --git a/libraries/libxml2/SAX2.c b/libraries/libxml2/SAX2.c index ed21a559..32db6b4c 100644 --- a/libraries/libxml2/SAX2.c +++ b/libraries/libxml2/SAX2.c @@ -31,52 +31,16 @@ #include "private/parser.h" #include "private/tree.h" -/** - * TODO: - * - * macro to flag unimplemented blocks - * XML_CATALOG_PREFER user env to select between system/public preferred - * option. C.f. Richard Tobin - *> Just FYI, I am using an environment variable XML_CATALOG_PREFER with - *> values "system" and "public". I have made the default be "system" to - *> match yours. - */ -#define TODO \ - xmlGenericError(xmlGenericErrorContext, \ - "Unimplemented block at %s:%d\n", \ - __FILE__, __LINE__); +#define XML_MAX_URI_LENGTH 2000 /* * xmlSAX2ErrMemory: * @ctxt: an XML validation parser context * @msg: a string to accompany the error message */ -static void LIBXML_ATTR_FORMAT(2,0) -xmlSAX2ErrMemory(xmlParserCtxtPtr ctxt, const char *msg) { - xmlStructuredErrorFunc schannel = NULL; - const char *str1 = "out of memory\n"; - - if (ctxt != NULL) { - ctxt->errNo = XML_ERR_NO_MEMORY; - if ((ctxt->sax != NULL) && (ctxt->sax->initialized == XML_SAX2_MAGIC)) - schannel = ctxt->sax->serror; - __xmlRaiseError(schannel, - ctxt->vctxt.error, ctxt->vctxt.userData, - ctxt, NULL, XML_FROM_PARSER, XML_ERR_NO_MEMORY, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - NULL, NULL, 0, 0, - msg, (const char *) str1, NULL); - ctxt->errNo = XML_ERR_NO_MEMORY; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; - } else { - __xmlRaiseError(schannel, - NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, XML_ERR_NO_MEMORY, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - NULL, NULL, 0, 0, - msg, (const char *) str1, NULL); - } +static void +xmlSAX2ErrMemory(xmlParserCtxtPtr ctxt) { + xmlCtxtErrMemory(ctxt); } /** @@ -91,32 +55,12 @@ xmlSAX2ErrMemory(xmlParserCtxtPtr ctxt, const char *msg) { */ static void LIBXML_ATTR_FORMAT(3,0) xmlErrValid(xmlParserCtxtPtr ctxt, xmlParserErrors error, - const char *msg, const char *str1, const char *str2) + const char *msg, const xmlChar *str1, const xmlChar *str2) { - xmlStructuredErrorFunc schannel = NULL; - - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) { - ctxt->errNo = error; - if ((ctxt->sax != NULL) && (ctxt->sax->initialized == XML_SAX2_MAGIC)) - schannel = ctxt->sax->serror; - __xmlRaiseError(schannel, - ctxt->vctxt.error, ctxt->vctxt.userData, - ctxt, NULL, XML_FROM_DTD, error, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); + xmlCtxtErr(ctxt, NULL, XML_FROM_DTD, error, XML_ERR_ERROR, + str1, str2, NULL, 0, msg, str1, str2); + if (ctxt != NULL) ctxt->valid = 0; - } else { - __xmlRaiseError(schannel, - NULL, NULL, - ctxt, NULL, XML_FROM_DTD, error, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); - } } /** @@ -133,21 +77,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlFatalErrMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_FATAL, NULL, 0, - (const char *) str1, (const char *) str2, - NULL, 0, 0, msg, str1, str2); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - ctxt->valid = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, + str1, str2, NULL, 0, msg, str1, str2); } /** @@ -164,15 +95,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlWarnMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_WARNING, NULL, 0, - (const char *) str1, NULL, - NULL, 0, 0, msg, str1); + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_WARNING, + str1, NULL, NULL, 0, msg, str1); } /** @@ -188,15 +112,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlNsWarnMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_NAMESPACE, error, - XML_ERR_WARNING, NULL, 0, - (const char *) str1, (const char *) str2, - NULL, 0, 0, msg, str1, str2); + xmlCtxtErr(ctxt, NULL, XML_FROM_NAMESPACE, error, XML_ERR_WARNING, + str1, str2, NULL, 0, msg, str1, str2); } /** @@ -341,7 +258,7 @@ xmlSAX2InternalSubset(void *ctx, const xmlChar *name, ctxt->myDoc->intSubset = xmlCreateIntSubset(ctxt->myDoc, name, ExternalID, SystemID); if (ctxt->myDoc->intSubset == NULL) - xmlSAX2ErrMemory(ctxt, "xmlSAX2InternalSubset"); + xmlSAX2ErrMemory(ctxt); } /** @@ -359,8 +276,9 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name, { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; if (ctx == NULL) return; - if (((ExternalID != NULL) || (SystemID != NULL)) && - (((ctxt->validate) || (ctxt->loadsubset != 0)) && + if ((SystemID != NULL) && + ((ctxt->options & XML_PARSE_NO_XXE) == 0) && + (((ctxt->validate) || (ctxt->loadsubset)) && (ctxt->wellFormed && ctxt->myDoc))) { /* * Try to fetch and parse the external subset. @@ -371,7 +289,6 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name, xmlParserInputPtr *oldinputTab; xmlParserInputPtr input = NULL; const xmlChar *oldencoding; - int oldprogressive; unsigned long consumed; size_t buffered; @@ -385,7 +302,11 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name, return; } - xmlNewDtd(ctxt->myDoc, name, ExternalID, SystemID); + if (xmlNewDtd(ctxt->myDoc, name, ExternalID, SystemID) == NULL) { + xmlSAX2ErrMemory(ctxt); + xmlFreeInputStream(input); + return; + } /* * make sure we won't destroy the main document context @@ -395,21 +316,18 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name, oldinputMax = ctxt->inputMax; oldinputTab = ctxt->inputTab; oldencoding = ctxt->encoding; - oldprogressive = ctxt->progressive; ctxt->encoding = NULL; - ctxt->progressive = 0; ctxt->inputTab = (xmlParserInputPtr *) xmlMalloc(5 * sizeof(xmlParserInputPtr)); if (ctxt->inputTab == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2ExternalSubset"); + xmlSAX2ErrMemory(ctxt); xmlFreeInputStream(input); ctxt->input = oldinput; ctxt->inputNr = oldinputNr; ctxt->inputMax = oldinputMax; ctxt->inputTab = oldinputTab; ctxt->encoding = oldencoding; - ctxt->progressive = oldprogressive; return; } ctxt->inputNr = 0; @@ -463,7 +381,6 @@ xmlSAX2ExternalSubset(void *ctx, const xmlChar *name, (!xmlDictOwns(ctxt->dict, ctxt->encoding)))) xmlFree((xmlChar *) ctxt->encoding); ctxt->encoding = oldencoding; - ctxt->progressive = oldprogressive; /* ctxt->wellFormed = oldwellFormed; */ } } @@ -486,22 +403,44 @@ xmlParserInputPtr xmlSAX2ResolveEntity(void *ctx, const xmlChar *publicId, const xmlChar *systemId) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - xmlParserInputPtr ret; + xmlParserInputPtr ret = NULL; xmlChar *URI; - const char *base = NULL; + const xmlChar *base = NULL; + int res; if (ctx == NULL) return(NULL); if (ctxt->input != NULL) - base = ctxt->input->filename; + base = BAD_CAST ctxt->input->filename; + + /* + * We don't really need the 'directory' struct member, but some + * users set it manually to a base URI for memory streams. + */ if (base == NULL) - base = ctxt->directory; + base = BAD_CAST ctxt->directory; - URI = xmlBuildURI(systemId, (const xmlChar *) base); + if ((xmlStrlen(systemId) > XML_MAX_URI_LENGTH) || + (xmlStrlen(base) > XML_MAX_URI_LENGTH)) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long"); + return(NULL); + } + res = xmlBuildURISafe(systemId, base, &URI); + if (URI == NULL) { + if (res < 0) + xmlSAX2ErrMemory(ctxt); + else + xmlWarnMsg(ctxt, XML_ERR_INVALID_URI, + "Can't resolve URI: %s\n", systemId); + return(NULL); + } + if (xmlStrlen(URI) > XML_MAX_URI_LENGTH) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long"); + } else { + ret = xmlLoadExternalEntity((const char *) URI, + (const char *) publicId, ctxt); + } - ret = xmlLoadExternalEntity((const char *) URI, - (const char *) publicId, ctxt); - if (URI != NULL) - xmlFree(URI); + xmlFree(URI); return(ret); } @@ -590,50 +529,80 @@ xmlSAX2EntityDecl(void *ctx, const xmlChar *name, int type, { xmlEntityPtr ent; xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; + int extSubset; + int res; - if (ctx == NULL) return; - if (ctxt->inSubset == 1) { - ent = xmlAddDocEntity(ctxt->myDoc, name, type, publicId, - systemId, content); - if ((ent == NULL) && (ctxt->pedantic)) - xmlWarnMsg(ctxt, XML_WAR_ENTITY_REDEFINED, - "Entity(%s) already defined in the internal subset\n", - name); - if ((ent != NULL) && (ent->URI == NULL) && (systemId != NULL)) { - xmlChar *URI; - const char *base = NULL; - - if (ctxt->input != NULL) - base = ctxt->input->filename; - if (base == NULL) - base = ctxt->directory; - - URI = xmlBuildURI(systemId, (const xmlChar *) base); - ent->URI = URI; - } - } else if (ctxt->inSubset == 2) { - ent = xmlAddDtdEntity(ctxt->myDoc, name, type, publicId, - systemId, content); - if ((ent == NULL) && (ctxt->pedantic) && - (ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) - ctxt->sax->warning(ctxt->userData, - "Entity(%s) already defined in the external subset\n", name); - if ((ent != NULL) && (ent->URI == NULL) && (systemId != NULL)) { - xmlChar *URI; - const char *base = NULL; - - if (ctxt->input != NULL) - base = ctxt->input->filename; - if (base == NULL) - base = ctxt->directory; - - URI = xmlBuildURI(systemId, (const xmlChar *) base); - ent->URI = URI; - } - } else { - xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_PROCESSING, - "SAX.xmlSAX2EntityDecl(%s) called while not in subset\n", - name, NULL); + if ((ctxt == NULL) || (ctxt->myDoc == NULL)) + return; + + extSubset = ctxt->inSubset == 2; + res = xmlAddEntity(ctxt->myDoc, extSubset, name, type, publicId, systemId, + content, &ent); + switch (res) { + case XML_ERR_OK: + break; + case XML_ERR_NO_MEMORY: + xmlSAX2ErrMemory(ctxt); + return; + case XML_WAR_ENTITY_REDEFINED: + if (ctxt->pedantic) { + if (extSubset) + xmlWarnMsg(ctxt, res, "Entity(%s) already defined in the" + " external subset\n", name); + else + xmlWarnMsg(ctxt, res, "Entity(%s) already defined in the" + " internal subset\n", name); + } + return; + case XML_ERR_REDECL_PREDEF_ENTITY: + /* + * Technically an error but it's a common mistake to get double + * escaping according to "4.6 Predefined Entities" wrong. + */ + xmlWarnMsg(ctxt, res, "Invalid redeclaration of predefined" + " entity '%s'", name); + return; + default: + xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, + "Unexpected error code from xmlAddEntity\n", + NULL, NULL); + return; + } + + if ((ent->URI == NULL) && (systemId != NULL)) { + xmlChar *URI; + const char *base = NULL; + int i; + + for (i = ctxt->inputNr - 1; i >= 0; i--) { + if (ctxt->inputTab[i]->filename != NULL) { + base = ctxt->inputTab[i]->filename; + break; + } + } + + /* + * We don't really need the 'directory' struct member, but some + * users set it manually to a base URI for memory streams. + */ + if (base == NULL) + base = ctxt->directory; + + res = xmlBuildURISafe(systemId, (const xmlChar *) base, &URI); + + if (URI == NULL) { + if (res < 0) { + xmlSAX2ErrMemory(ctxt); + } else { + xmlWarnMsg(ctxt, XML_ERR_INVALID_URI, + "Can't resolve URI: %s\n", systemId); + } + } else if (xmlStrlen(URI) > XML_MAX_URI_LENGTH) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, "URI too long"); + xmlFree(URI); + } else { + ent->URI = URI; + } } } @@ -676,6 +645,8 @@ xmlSAX2AttributeDecl(void *ctx, const xmlChar *elem, const xmlChar *fullname, } /* TODO: optimize name/prefix allocation */ name = xmlSplitQName(ctxt, fullname, &prefix); + if (name == NULL) + xmlSAX2ErrMemory(ctxt); ctxt->vctxt.valid = 1; if (ctxt->inSubset == 1) attr = xmlAddAttributeDecl(&ctxt->vctxt, ctxt->myDoc->intSubset, elem, @@ -690,6 +661,7 @@ xmlSAX2AttributeDecl(void *ctx, const xmlChar *elem, const xmlChar *fullname, "SAX.xmlSAX2AttributeDecl(%s) called while not in subset\n", name, NULL); xmlFree(name); + xmlFree(prefix); xmlFreeEnumeration(tree); return; } @@ -814,54 +786,8 @@ xmlSAX2UnparsedEntityDecl(void *ctx, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId, const xmlChar *notationName) { - xmlEntityPtr ent; - xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - if (ctx == NULL) return; - if (ctxt->inSubset == 1) { - ent = xmlAddDocEntity(ctxt->myDoc, name, - XML_EXTERNAL_GENERAL_UNPARSED_ENTITY, - publicId, systemId, notationName); - if ((ent == NULL) && (ctxt->pedantic) && - (ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) - ctxt->sax->warning(ctxt->userData, - "Entity(%s) already defined in the internal subset\n", name); - if ((ent != NULL) && (ent->URI == NULL) && (systemId != NULL)) { - xmlChar *URI; - const char *base = NULL; - - if (ctxt->input != NULL) - base = ctxt->input->filename; - if (base == NULL) - base = ctxt->directory; - - URI = xmlBuildURI(systemId, (const xmlChar *) base); - ent->URI = URI; - } - } else if (ctxt->inSubset == 2) { - ent = xmlAddDtdEntity(ctxt->myDoc, name, - XML_EXTERNAL_GENERAL_UNPARSED_ENTITY, - publicId, systemId, notationName); - if ((ent == NULL) && (ctxt->pedantic) && - (ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) - ctxt->sax->warning(ctxt->userData, - "Entity(%s) already defined in the external subset\n", name); - if ((ent != NULL) && (ent->URI == NULL) && (systemId != NULL)) { - xmlChar *URI; - const char *base = NULL; - - if (ctxt->input != NULL) - base = ctxt->input->filename; - if (base == NULL) - base = ctxt->directory; - - URI = xmlBuildURI(systemId, (const xmlChar *) base); - ent->URI = URI; - } - } else { - xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, - "SAX.xmlSAX2UnparsedEntityDecl(%s) called while not in subset\n", - name, NULL); - } + xmlSAX2EntityDecl(ctx, name, XML_EXTERNAL_GENERAL_UNPARSED_ENTITY, + publicId, systemId, (xmlChar *) notationName); } /** @@ -891,25 +817,19 @@ xmlSAX2StartDocument(void *ctx) if (ctx == NULL) return; - if (ctxt->html) { #ifdef LIBXML_HTML_ENABLED + if (ctxt->html) { if (ctxt->myDoc == NULL) ctxt->myDoc = htmlNewDocNoDtD(NULL, NULL); if (ctxt->myDoc == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument"); + xmlSAX2ErrMemory(ctxt); return; } ctxt->myDoc->properties = XML_DOC_HTML; ctxt->myDoc->parseFlags = ctxt->options; -#else - xmlGenericError(xmlGenericErrorContext, - "libxml2 built without HTML support\n"); - ctxt->errNo = XML_ERR_INTERNAL_ERROR; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; - return; + } else #endif - } else { + { doc = ctxt->myDoc = xmlNewDoc(ctxt->version); if (doc != NULL) { doc->properties = 0; @@ -918,7 +838,7 @@ xmlSAX2StartDocument(void *ctx) doc->parseFlags = ctxt->options; doc->standalone = ctxt->standalone; } else { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument"); + xmlSAX2ErrMemory(ctxt); return; } if ((ctxt->dictNames) && (doc != NULL)) { @@ -930,7 +850,7 @@ xmlSAX2StartDocument(void *ctx) (ctxt->input != NULL) && (ctxt->input->filename != NULL)) { ctxt->myDoc->URL = xmlPathToURI((const xmlChar *)ctxt->input->filename); if (ctxt->myDoc->URL == NULL) - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartDocument"); + xmlSAX2ErrMemory(ctxt); } } @@ -955,26 +875,52 @@ xmlSAX2EndDocument(void *ctx) doc = ctxt->myDoc; if ((doc != NULL) && (doc->encoding == NULL)) { - const xmlChar *encoding = NULL; - - if ((ctxt->input->flags & XML_INPUT_USES_ENC_DECL) || - (ctxt->input->flags & XML_INPUT_AUTO_ENCODING)) { - /* Preserve encoding exactly */ - encoding = ctxt->encoding; - } else if ((ctxt->input->buf) && (ctxt->input->buf->encoder)) { - encoding = BAD_CAST ctxt->input->buf->encoder->name; - } else if (ctxt->input->flags & XML_INPUT_HAS_ENCODING) { - encoding = BAD_CAST "UTF-8"; - } + const xmlChar *encoding = xmlGetActualEncoding(ctxt); if (encoding != NULL) { doc->encoding = xmlStrdup(encoding); if (doc->encoding == NULL) - xmlSAX2ErrMemory(ctxt, "xmlSAX2EndDocument"); + xmlSAX2ErrMemory(ctxt); } } } +static void +xmlSAX2AppendChild(xmlParserCtxtPtr ctxt, xmlNodePtr node) { + xmlNodePtr parent; + xmlNodePtr last; + + if (ctxt->inSubset == 1) { + parent = (xmlNodePtr) ctxt->myDoc->intSubset; + } else if (ctxt->inSubset == 2) { + parent = (xmlNodePtr) ctxt->myDoc->extSubset; + } else { + parent = ctxt->node; + if (parent == NULL) + parent = (xmlNodePtr) ctxt->myDoc; + } + + last = parent->last; + if (last == NULL) { + parent->children = node; + } else { + last->next = node; + node->prev = last; + } + + parent->last = node; + node->parent = parent; + + if ((node->type != XML_TEXT_NODE) && + (ctxt->linenumbers) && + (ctxt->input != NULL)) { + if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX) + node->line = ctxt->input->line; + else + node->line = USHRT_MAX; + } +} + #if defined(LIBXML_SAX1_ENABLED) || defined(LIBXML_HTML_ENABLED) || defined(LIBXML_WRITER_ENABLED) || defined(LIBXML_LEGACY_ENABLED) /** * xmlNsErrMsg: @@ -990,15 +936,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlNsErrMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_NAMESPACE, error, - XML_ERR_ERROR, NULL, 0, - (const char *) str1, (const char *) str2, - NULL, 0, 0, msg, str1, str2); + xmlCtxtErr(ctxt, NULL, XML_FROM_NAMESPACE, error, XML_ERR_ERROR, + str1, str2, NULL, 0, msg, str1, str2); } /** @@ -1051,7 +990,7 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, } } if (name == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlSAX2ErrMemory(ctxt); if (ns != NULL) xmlFree(ns); return; @@ -1061,6 +1000,8 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, if ((ctxt->html) && (value == NULL) && (htmlIsBooleanAttr(fullname))) { nval = xmlStrdup(fullname); + if (nval == NULL) + xmlSAX2ErrMemory(ctxt); value = (const xmlChar *) nval; } else #endif @@ -1098,12 +1039,10 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, (void) nsret; if (!ctxt->replaceEntities) { - ctxt->depth++; - val = xmlStringDecodeEntities(ctxt, value, XML_SUBSTITUTE_REF, - 0,0,0); - ctxt->depth--; + /* TODO: normalize if needed */ + val = xmlExpandEntitiesInAttValue(ctxt, value, /* normalize */ 0); if (val == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlSAX2ErrMemory(ctxt); if (name != NULL) xmlFree(name); if (nval != NULL) @@ -1117,16 +1056,16 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, if (val[0] != 0) { xmlURIPtr uri; - uri = xmlParseURI((const char *)val); + if (xmlParseURISafe((const char *)val, &uri) < 0) + xmlSAX2ErrMemory(ctxt); if (uri == NULL) { - if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) - ctxt->sax->warning(ctxt->userData, - "xmlns: %s not a valid URI\n", val); + xmlNsWarnMsg(ctxt, XML_WAR_NS_URI, + "xmlns:%s: %s not a valid URI\n", name, value); } else { if (uri->scheme == NULL) { - if ((ctxt->sax != NULL) && (ctxt->sax->warning != NULL)) - ctxt->sax->warning(ctxt->userData, - "xmlns: URI %s is not absolute\n", val); + xmlNsWarnMsg(ctxt, XML_WAR_NS_URI_RELATIVE, + "xmlns:%s: URI %s is not absolute\n", + name, value); } xmlFreeURI(uri); } @@ -1134,16 +1073,19 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, /* a default namespace definition */ nsret = xmlNewNs(ctxt->node, val, NULL); - + if (nsret == NULL) { + xmlSAX2ErrMemory(ctxt); + } #ifdef LIBXML_VALID_ENABLED /* * Validate also for namespace decls, they are attributes from * an XML-1.0 perspective */ - if (nsret != NULL && ctxt->validate && ctxt->wellFormed && - ctxt->myDoc && ctxt->myDoc->intSubset) + else if (ctxt->validate && ctxt->wellFormed && + ctxt->myDoc && ctxt->myDoc->intSubset) { ctxt->valid &= xmlValidateOneNamespace(&ctxt->vctxt, ctxt->myDoc, ctxt->node, prefix, nsret, val); + } #endif /* LIBXML_VALID_ENABLED */ if (name != NULL) xmlFree(name); @@ -1163,12 +1105,10 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, (void) nsret; if (!ctxt->replaceEntities) { - ctxt->depth++; - val = xmlStringDecodeEntities(ctxt, value, XML_SUBSTITUTE_REF, - 0,0,0); - ctxt->depth--; + /* TODO: normalize if needed */ + val = xmlExpandEntitiesInAttValue(ctxt, value, /* normalize */ 0); if (val == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlSAX2ErrMemory(ctxt); xmlFree(ns); if (name != NULL) xmlFree(name); @@ -1187,7 +1127,8 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, if ((ctxt->pedantic != 0) && (val[0] != 0)) { xmlURIPtr uri; - uri = xmlParseURI((const char *)val); + if (xmlParseURISafe((const char *)val, &uri) < 0) + xmlSAX2ErrMemory(ctxt); if (uri == NULL) { xmlNsWarnMsg(ctxt, XML_WAR_NS_URI, "xmlns:%s: %s not a valid URI\n", name, value); @@ -1203,15 +1144,20 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, /* a standard namespace definition */ nsret = xmlNewNs(ctxt->node, val, name); xmlFree(ns); + + if (nsret == NULL) { + xmlSAX2ErrMemory(ctxt); + } #ifdef LIBXML_VALID_ENABLED /* * Validate also for namespace decls, they are attributes from * an XML-1.0 perspective */ - if (nsret != NULL && ctxt->validate && ctxt->wellFormed && - ctxt->myDoc && ctxt->myDoc->intSubset) + else if (ctxt->validate && ctxt->wellFormed && + ctxt->myDoc && ctxt->myDoc->intSubset) { ctxt->valid &= xmlValidateOneNamespace(&ctxt->vctxt, ctxt->myDoc, ctxt->node, prefix, nsret, value); + } #endif /* LIBXML_VALID_ENABLED */ if (name != NULL) xmlFree(name); @@ -1223,7 +1169,11 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, } if (ns != NULL) { - namespace = xmlSearchNs(ctxt->myDoc, ctxt->node, ns); + int res; + + res = xmlSearchNsSafe(ctxt->node, ns, &namespace); + if (res < 0) + xmlSAX2ErrMemory(ctxt); if (namespace == NULL) { xmlNsErrMsg(ctxt, XML_NS_ERR_UNDEFINED_NAMESPACE, @@ -1238,11 +1188,11 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, if ((xmlStrEqual(name, prop->name)) && ((namespace == prop->ns) || (xmlStrEqual(namespace->href, prop->ns->href)))) { - xmlNsErrMsg(ctxt, XML_ERR_ATTRIBUTE_REDEFINED, - "Attribute %s in %s redefined\n", - name, namespace->href); - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) ctxt->disableSAX = 1; + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, + XML_ERR_ATTRIBUTE_REDEFINED, XML_ERR_FATAL, + name, NULL, NULL, 0, + "Attribute %s in %s redefined\n", + name, namespace->href); if (name != NULL) xmlFree(name); goto error; @@ -1257,25 +1207,22 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, /* !!!!!! */ ret = xmlNewNsPropEatName(ctxt->node, namespace, name, NULL); - if (ret == NULL) + if (ret == NULL) { + xmlSAX2ErrMemory(ctxt); goto error; + } if ((ctxt->replaceEntities == 0) && (!ctxt->html)) { - xmlNodePtr tmp; - - ret->children = xmlStringGetNodeList(ctxt->myDoc, value); - tmp = ret->children; - while (tmp != NULL) { - tmp->parent = (xmlNodePtr) ret; - if (tmp->next == NULL) - ret->last = tmp; - tmp = tmp->next; - } + if (xmlNodeParseContent((xmlNodePtr) ret, value, INT_MAX) < 0) + xmlSAX2ErrMemory(ctxt); } else if (value != NULL) { ret->children = xmlNewDocText(ctxt->myDoc, value); - ret->last = ret->children; - if (ret->children != NULL) + if (ret->children == NULL) { + xmlSAX2ErrMemory(ctxt); + } else { + ret->last = ret->children; ret->children->parent = (xmlNodePtr) ret; + } } #ifdef LIBXML_VALID_ENABLED @@ -1289,10 +1236,8 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, if (!ctxt->replaceEntities) { xmlChar *val; - ctxt->depth++; - val = xmlStringDecodeEntities(ctxt, value, XML_SUBSTITUTE_REF, - 0,0,0); - ctxt->depth--; + /* TODO: normalize if needed */ + val = xmlExpandEntitiesInAttValue(ctxt, value, /* normalize */ 0); if (val == NULL) ctxt->valid &= xmlValidateOneAttribute(&ctxt->vctxt, @@ -1305,8 +1250,9 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, * It need to be done twice ... it's an extra burden related * to the ability to keep xmlSAX2References in attributes */ - nvalnorm = xmlValidNormalizeAttributeValue(ctxt->myDoc, - ctxt->node, fullname, val); + nvalnorm = xmlValidCtxtNormalizeAttributeValue( + &ctxt->vctxt, ctxt->myDoc, + ctxt->node, fullname, val); if (nvalnorm != NULL) { xmlFree(val); val = nvalnorm; @@ -1323,8 +1269,7 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, } else #endif /* LIBXML_VALID_ENABLED */ if (((ctxt->loadsubset & XML_SKIP_IDS) == 0) && - (((ctxt->replaceEntities == 0) && (ctxt->external != 2)) || - ((ctxt->replaceEntities != 0) && (ctxt->inSubset == 0))) && + (ctxt->input->entity == NULL) && /* Don't create IDs containing entity references */ (ret->children != NULL) && (ret->children->type == XML_TEXT_NODE) && @@ -1342,14 +1287,20 @@ xmlSAX2AttributeInternal(void *ctx, const xmlChar *fullname, */ if (xmlValidateNCName(content, 1) != 0) { xmlErrValid(ctxt, XML_DTD_XMLID_VALUE, - "xml:id : attribute value %s is not an NCName\n", - (const char *) content, NULL); + "xml:id : attribute value %s is not an NCName\n", + content, NULL); } xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); - } else if (xmlIsID(ctxt->myDoc, ctxt->node, ret)) - xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); - else if (xmlIsRef(ctxt->myDoc, ctxt->node, ret)) - xmlAddRef(&ctxt->vctxt, ctxt->myDoc, content, ret); + } else { + int res = xmlIsID(ctxt->myDoc, ctxt->node, ret); + + if (res < 0) + xmlCtxtErrMemory(ctxt); + else if (res > 0) + xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); + else if (xmlIsRef(ctxt->myDoc, ctxt->node, ret)) + xmlAddRef(&ctxt->vctxt, ctxt->myDoc, content, ret); + } } error: @@ -1401,13 +1352,15 @@ xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, if (attr->prefix != NULL) { fulln = xmlStrdup(attr->prefix); - fulln = xmlStrcat(fulln, BAD_CAST ":"); - fulln = xmlStrcat(fulln, attr->name); + if (fulln != NULL) + fulln = xmlStrcat(fulln, BAD_CAST ":"); + if (fulln != NULL) + fulln = xmlStrcat(fulln, attr->name); } else { fulln = xmlStrdup(attr->name); } if (fulln == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlSAX2ErrMemory(ctxt); break; } @@ -1429,8 +1382,8 @@ xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, if (att == NULL) { xmlErrValid(ctxt, XML_DTD_STANDALONE_DEFAULTED, "standalone: attribute %s on %s defaulted from external subset\n", - (const char *)fulln, - (const char *)attr->elem); + fulln, + attr->elem); } xmlFree(fulln); } @@ -1473,7 +1426,7 @@ xmlCheckDefaultedAttributes(xmlParserCtxtPtr ctxt, const xmlChar *name, fulln = xmlBuildQName(attr->name, attr->prefix, fn, 50); if (fulln == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlSAX2ErrMemory(ctxt); return; } @@ -1557,6 +1510,10 @@ xmlSAX2StartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) * Split the full name into a namespace prefix and the tag name */ name = xmlSplitQName(ctxt, fullname, &prefix); + if (name == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } } /* @@ -1566,26 +1523,22 @@ xmlSAX2StartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) */ ret = xmlNewDocNodeEatName(ctxt->myDoc, NULL, name, NULL); if (ret == NULL) { - if (prefix != NULL) - xmlFree(prefix); - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElement"); + xmlFree(prefix); + xmlSAX2ErrMemory(ctxt); return; } ctxt->nodemem = -1; - if (ctxt->linenumbers) { - if (ctxt->input != NULL) { - if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX) - ret->line = ctxt->input->line; - else - ret->line = USHRT_MAX; - } - } /* Initialize parent before pushing node */ parent = ctxt->node; if (parent == NULL) parent = (xmlNodePtr) ctxt->myDoc; + /* + * Link the child element + */ + xmlSAX2AppendChild(ctxt, ret); + /* * We are parsing a new node. */ @@ -1597,12 +1550,9 @@ xmlSAX2StartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) return; } - /* - * Link the child element - */ - xmlAddChild(parent, ret); - if (!ctxt->html) { + int res; + /* * Insert all the defaulted attributes from the DTD especially * namespaces @@ -1633,14 +1583,21 @@ xmlSAX2StartElement(void *ctx, const xmlChar *fullname, const xmlChar **atts) * Search the namespace, note that since the attributes have been * processed, the local namespaces are available. */ - ns = xmlSearchNs(ctxt->myDoc, ret, prefix); - if ((ns == NULL) && (parent != NULL)) - ns = xmlSearchNs(ctxt->myDoc, parent, prefix); + res = xmlSearchNsSafe(ret, prefix, &ns); + if (res < 0) + xmlSAX2ErrMemory(ctxt); + if ((ns == NULL) && (parent != NULL)) { + res = xmlSearchNsSafe(parent, prefix, &ns); + if (res < 0) + xmlSAX2ErrMemory(ctxt); + } if ((prefix != NULL) && (ns == NULL)) { - ns = xmlNewNs(ret, NULL, prefix); xmlNsWarnMsg(ctxt, XML_NS_ERR_UNDEFINED_NAMESPACE, "Namespace prefix %s is not defined\n", prefix, NULL); + ns = xmlNewNs(ret, NULL, prefix); + if (ns == NULL) + xmlSAX2ErrMemory(ctxt); } /* @@ -1761,7 +1718,7 @@ xmlSAX2TextNode(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) { ret = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); } if (ret == NULL) { - xmlErrMemory(ctxt, "xmlSAX2Characters"); + xmlCtxtErrMemory(ctxt); return(NULL); } memset(ret, 0, sizeof(xmlNode)); @@ -1782,6 +1739,11 @@ xmlSAX2TextNode(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) { } else if ((len <= 3) && ((cur == '"') || (cur == '\'') || ((cur == '<') && (str[len + 1] != '!')))) { intern = xmlDictLookup(ctxt->dict, str, len); + if (intern == NULL) { + xmlSAX2ErrMemory(ctxt); + xmlFree(ret); + return(NULL); + } } else if (IS_BLANK_CH(*str) && (len < 60) && (cur == '<') && (str[len + 1] != '!')) { int i; @@ -1790,6 +1752,11 @@ xmlSAX2TextNode(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) { if (!IS_BLANK_CH(str[i])) goto skip; } intern = xmlDictLookup(ctxt->dict, str, len); + if (intern == NULL) { + xmlSAX2ErrMemory(ctxt); + xmlFree(ret); + return(NULL); + } } } skip: @@ -1799,7 +1766,7 @@ xmlSAX2TextNode(xmlParserCtxtPtr ctxt, const xmlChar *str, int len) { if (intern == NULL) { ret->content = xmlStrndup(str, len); if (ret->content == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2TextNode"); + xmlSAX2ErrMemory(ctxt); xmlFree(ret); return(NULL); } @@ -1826,7 +1793,6 @@ static xmlChar * xmlSAX2DecodeAttrEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, const xmlChar *end) { const xmlChar *in; - xmlChar *ret; in = str; while (in < end) @@ -1834,11 +1800,12 @@ xmlSAX2DecodeAttrEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, goto decode; return(NULL); decode: - ctxt->depth++; - ret = xmlStringLenDecodeEntities(ctxt, str, end - str, - XML_SUBSTITUTE_REF, 0,0,0); - ctxt->depth--; - return(ret); + /* + * If the value contains '&', we can be sure it was allocated and is + * zero-terminated. + */ + /* TODO: normalize if needed */ + return(xmlExpandEntitiesInAttValue(ctxt, str, /* normalize */ 0)); } #endif /* LIBXML_VALID_ENABLED */ @@ -1875,7 +1842,11 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, if (prefix != NULL) { namespace = xmlParserNsLookupSax(ctxt, prefix); if ((namespace == NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))) { - namespace = xmlSearchNs(ctxt->myDoc, ctxt->node, prefix); + int res; + + res = xmlSearchNsSafe(ctxt->node, prefix, &namespace); + if (res < 0) + xmlSAX2ErrMemory(ctxt); } } @@ -1889,7 +1860,7 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, } else { ret = xmlMalloc(sizeof(*ret)); if (ret == NULL) { - xmlSAX2ErrMemory(ctxt, NULL); + xmlSAX2ErrMemory(ctxt); return(NULL); } } @@ -1910,10 +1881,13 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, ret->doc = ctxt->node->doc; ret->ns = namespace; - if (ctxt->dictNames) + if (ctxt->dictNames) { ret->name = localname; - else + } else { ret->name = xmlStrdup(localname); + if (ret->name == NULL) + xmlSAX2ErrMemory(ctxt); + } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)ret); @@ -1934,17 +1908,10 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, tmp->doc = ret->doc; tmp->parent = (xmlNodePtr) ret; } - } else { - ret->children = xmlStringLenGetNodeList(ctxt->myDoc, value, - valueend - value); - tmp = ret->children; - while (tmp != NULL) { - tmp->doc = ret->doc; - tmp->parent = (xmlNodePtr) ret; - if (tmp->next == NULL) - ret->last = tmp; - tmp = tmp->next; - } + } else if (valueend > value) { + if (xmlNodeParseContent((xmlNodePtr) ret, value, + valueend - value) < 0) + xmlSAX2ErrMemory(ctxt); } } else if (value != NULL) { xmlNodePtr tmp; @@ -1978,6 +1945,8 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, * entry points in the full validation code */ dup = xmlStrndup(value, valueend - value); + if (dup == NULL) + xmlSAX2ErrMemory(ctxt); ctxt->valid &= xmlValidateOneAttribute(&ctxt->vctxt, ctxt->myDoc, ctxt->node, ret, dup); @@ -1996,7 +1965,9 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, xmlChar *fullname; fullname = xmlBuildQName(localname, prefix, fn, 50); - if (fullname != NULL) { + if (fullname == NULL) { + xmlSAX2ErrMemory(ctxt); + } else { ctxt->vctxt.valid = 1; nvalnorm = xmlValidCtxtNormalizeAttributeValue( &ctxt->vctxt, ctxt->myDoc, @@ -2022,6 +1993,8 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, * the attribute as passed is already normalized */ dup = xmlStrndup(value, valueend - value); + if (dup == NULL) + xmlSAX2ErrMemory(ctxt); ctxt->valid &= xmlValidateOneAttribute(&ctxt->vctxt, ctxt->myDoc, ctxt->node, ret, dup); @@ -2029,8 +2002,7 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, } else #endif /* LIBXML_VALID_ENABLED */ if (((ctxt->loadsubset & XML_SKIP_IDS) == 0) && - (((ctxt->replaceEntities == 0) && (ctxt->external != 2)) || - ((ctxt->replaceEntities != 0) && (ctxt->inSubset == 0))) && + (ctxt->input->entity == NULL) && /* Don't create IDs containing entity references */ (ret->children != NULL) && (ret->children->type == XML_TEXT_NODE) && @@ -2050,14 +2022,19 @@ xmlSAX2AttributeNs(xmlParserCtxtPtr ctxt, */ if (xmlValidateNCName(content, 1) != 0) { xmlErrValid(ctxt, XML_DTD_XMLID_VALUE, - "xml:id : attribute value %s is not an NCName\n", - (const char *) content, NULL); + "xml:id : attribute value %s is not an NCName\n", + content, NULL); } xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); - } else if (xmlIsID(ctxt->myDoc, ctxt->node, ret)) { - xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); - } else if (xmlIsRef(ctxt->myDoc, ctxt->node, ret)) { - xmlAddRef(&ctxt->vctxt, ctxt->myDoc, content, ret); + } else { + int res = xmlIsID(ctxt->myDoc, ctxt->node, ret); + + if (res < 0) + xmlCtxtErrMemory(ctxt); + else if (res > 0) + xmlAddID(&ctxt->vctxt, ctxt->myDoc, content, ret); + else if (xmlIsRef(ctxt->myDoc, ctxt->node, ret)) + xmlAddRef(&ctxt->vctxt, ctxt->myDoc, content, ret); } } if (dup != NULL) @@ -2096,7 +2073,6 @@ xmlSAX2StartElementNs(void *ctx, { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlNodePtr ret; - xmlNodePtr parent; xmlNsPtr last = NULL, ns; const xmlChar *uri, *pref; xmlChar *lname = NULL; @@ -2125,10 +2101,17 @@ xmlSAX2StartElementNs(void *ctx, const xmlChar *fullname; fullname = xmlDictQLookup(ctxt->dict, prefix, localname); - if (fullname != NULL) - localname = fullname; + if (fullname == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } + localname = fullname; } else { lname = xmlBuildQName(localname, prefix, NULL, 0); + if (lname == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } } } /* @@ -2150,7 +2133,7 @@ xmlSAX2StartElementNs(void *ctx, else ret->name = lname; if (ret->name == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs"); + xmlSAX2ErrMemory(ctxt); xmlFree(ret); return; } @@ -2167,18 +2150,10 @@ xmlSAX2StartElementNs(void *ctx, ret = xmlNewDocNodeEatName(ctxt->myDoc, NULL, (xmlChar *) lname, NULL); if (ret == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs"); + xmlSAX2ErrMemory(ctxt); return; } } - if (ctxt->linenumbers) { - if (ctxt->input != NULL) { - if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX) - ret->line = ctxt->input->line; - else - ret->line = USHRT_MAX; - } - } /* * Build the namespace list @@ -2197,11 +2172,7 @@ xmlSAX2StartElementNs(void *ctx, if ((URI != NULL) && (prefix == pref)) ret->ns = ns; } else { - /* - * any out of memory error would already have been raised - * but we can't be guaranteed it's the actual error due to the - * API, best is to skip in this case - */ + xmlSAX2ErrMemory(ctxt); continue; } @@ -2217,10 +2188,10 @@ xmlSAX2StartElementNs(void *ctx, } ctxt->nodemem = -1; - /* Initialize parent before pushing node */ - parent = ctxt->node; - if (parent == NULL) - parent = (xmlNodePtr) ctxt->myDoc; + /* + * Link the child element + */ + xmlSAX2AppendChild(ctxt, ret); /* * We are parsing a new node. @@ -2231,11 +2202,6 @@ xmlSAX2StartElementNs(void *ctx, return; } - /* - * Link the child element - */ - xmlAddChild(parent, ret); - /* * Insert the defaulted attributes from the DTD only if requested: */ @@ -2250,13 +2216,17 @@ xmlSAX2StartElementNs(void *ctx, if ((URI != NULL) && (ret->ns == NULL)) { ret->ns = xmlParserNsLookupSax(ctxt, prefix); if ((ret->ns == NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))) { - ret->ns = xmlSearchNs(ctxt->myDoc, ret, prefix); + int res; + + res = xmlSearchNsSafe(ret, prefix, &ret->ns); + if (res < 0) + xmlSAX2ErrMemory(ctxt); } if (ret->ns == NULL) { ns = xmlNewNs(ret, NULL, prefix); if (ns == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2StartElementNs"); + xmlSAX2ErrMemory(ctxt); return; } if (prefix != NULL) @@ -2288,22 +2258,26 @@ xmlSAX2StartElementNs(void *ctx, fullname = xmlDictQLookup(ctxt->dict, attributes[j+1], attributes[j]); - if (fullname != NULL) { - attr = xmlSAX2AttributeNs(ctxt, fullname, NULL, - attributes[j+3], - attributes[j+4]); - goto have_attr; - } + if (fullname == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } + attr = xmlSAX2AttributeNs(ctxt, fullname, NULL, + attributes[j+3], + attributes[j+4]); + goto have_attr; } else { lname = xmlBuildQName(attributes[j], attributes[j+1], NULL, 0); - if (lname != NULL) { - attr = xmlSAX2AttributeNs(ctxt, lname, NULL, - attributes[j+3], - attributes[j+4]); - xmlFree(lname); - goto have_attr; - } + if (lname == NULL) { + xmlSAX2ErrMemory(ctxt); + return; + } + attr = xmlSAX2AttributeNs(ctxt, lname, NULL, + attributes[j+3], + attributes[j+4]); + xmlFree(lname); + goto have_attr; } } attr = xmlSAX2AttributeNs(ctxt, attributes[j], attributes[j+1], @@ -2393,9 +2367,12 @@ xmlSAX2Reference(void *ctx, const xmlChar *name) if (ctx == NULL) return; ret = xmlNewReference(ctxt->myDoc, name); - if (xmlAddChild(ctxt->node, ret) == NULL) { - xmlFreeNode(ret); + if (ret == NULL) { + xmlSAX2ErrMemory(ctxt); + return; } + + xmlSAX2AppendChild(ctxt, ret); } /** @@ -2442,7 +2419,7 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, ctxt->nodelen = len; ctxt->nodemem = len + 1; } else { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters"); + xmlSAX2ErrMemory(ctxt); return; } } else { @@ -2451,6 +2428,10 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, ((type != XML_TEXT_NODE) || (lastChild->name == xmlStringText)); if ((coalesceText) && (ctxt->nodemem != 0)) { + int maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_TEXT_LENGTH; + /* * The whole point of maintaining nodelen and nodemem, * xmlTextConcat is too costly, i.e. compute length, @@ -2466,16 +2447,13 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, lastChild->content = xmlStrdup(lastChild->content); } if (lastChild->content == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters: xmlStrdup returned NULL"); + xmlSAX2ErrMemory(ctxt); return; } - if (ctxt->nodelen > INT_MAX - len) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters overflow prevented"); - return; - } - if ((ctxt->nodelen + len > XML_MAX_TEXT_LENGTH) && - ((ctxt->options & XML_PARSE_HUGE) == 0)) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters: huge text node"); + if ((len > maxLength) || (ctxt->nodelen > maxLength - len)) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, + "Text node too long, try XML_PARSE_HUGE"); + xmlHaltParser(ctxt); return; } if (ctxt->nodelen + len >= ctxt->nodemem) { @@ -2488,7 +2466,7 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, size = size > INT_MAX / 2 ? INT_MAX : size * 2; newbuf = (xmlChar *) xmlRealloc(lastChild->content,size); if (newbuf == NULL) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters"); + xmlSAX2ErrMemory(ctxt); return; } ctxt->nodemem = size; @@ -2499,7 +2477,7 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, lastChild->content[ctxt->nodelen] = 0; } else if (coalesceText) { if (xmlTextConcat(lastChild, ch, len)) { - xmlSAX2ErrMemory(ctxt, "xmlSAX2Characters"); + xmlSAX2ErrMemory(ctxt); } if (ctxt->node->children != NULL) { ctxt->nodelen = xmlStrlen(lastChild->content); @@ -2513,8 +2491,10 @@ xmlSAX2Text(xmlParserCtxtPtr ctxt, const xmlChar *ch, int len, lastChild->doc = ctxt->myDoc; } else lastChild = xmlNewCDataBlock(ctxt->myDoc, ch, len); - if (lastChild != NULL) { - xmlAddChild(ctxt->node, lastChild); + if (lastChild == NULL) { + xmlSAX2ErrMemory(ctxt); + } else { + xmlSAX2AppendChild(ctxt, lastChild); if (ctxt->node->children != NULL) { ctxt->nodelen = len; ctxt->nodemem = len + 1; @@ -2579,38 +2559,16 @@ xmlSAX2ProcessingInstruction(void *ctx, const xmlChar *target, { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlNodePtr ret; - xmlNodePtr parent; if (ctx == NULL) return; - parent = ctxt->node; ret = xmlNewDocPI(ctxt->myDoc, target, data); - if (ret == NULL) return; - - if (ctxt->linenumbers) { - if (ctxt->input != NULL) { - if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX) - ret->line = ctxt->input->line; - else - ret->line = USHRT_MAX; - } - } - if (ctxt->inSubset == 1) { - xmlAddChild((xmlNodePtr) ctxt->myDoc->intSubset, ret); - return; - } else if (ctxt->inSubset == 2) { - xmlAddChild((xmlNodePtr) ctxt->myDoc->extSubset, ret); - return; - } - if (parent == NULL) { - xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret); - return; - } - if (parent->type == XML_ELEMENT_NODE) { - xmlAddChild(parent, ret); - } else { - xmlAddSibling(parent, ret); + if (ret == NULL) { + xmlSAX2ErrMemory(ctxt); + return; } + + xmlSAX2AppendChild(ctxt, ret); } /** @@ -2625,37 +2583,16 @@ xmlSAX2Comment(void *ctx, const xmlChar *value) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlNodePtr ret; - xmlNodePtr parent; if (ctx == NULL) return; - parent = ctxt->node; + ret = xmlNewDocComment(ctxt->myDoc, value); - if (ret == NULL) return; - if (ctxt->linenumbers) { - if (ctxt->input != NULL) { - if ((unsigned) ctxt->input->line < (unsigned) USHRT_MAX) - ret->line = ctxt->input->line; - else - ret->line = USHRT_MAX; - } + if (ret == NULL) { + xmlSAX2ErrMemory(ctxt); + return; } - if (ctxt->inSubset == 1) { - xmlAddChild((xmlNodePtr) ctxt->myDoc->intSubset, ret); - return; - } else if (ctxt->inSubset == 2) { - xmlAddChild((xmlNodePtr) ctxt->myDoc->extSubset, ret); - return; - } - if (parent == NULL) { - xmlAddChild((xmlNodePtr) ctxt->myDoc, (xmlNodePtr) ret); - return; - } - if (parent->type == XML_ELEMENT_NODE) { - xmlAddChild(parent, ret); - } else { - xmlAddSibling(parent, ret); - } + xmlSAX2AppendChild(ctxt, ret); } /** diff --git a/libraries/libxml2/buf.c b/libraries/libxml2/buf.c index 266395f4..f9f59b26 100644 --- a/libraries/libxml2/buf.c +++ b/libraries/libxml2/buf.c @@ -89,10 +89,9 @@ struct _xmlBuf { * To be improved... */ static void -xmlBufMemoryError(xmlBufPtr buf, const char *extra) +xmlBufMemoryError(xmlBufPtr buf) { - __xmlSimpleError(XML_FROM_BUFFER, XML_ERR_NO_MEMORY, NULL, NULL, extra); - if ((buf) && (buf->error == 0)) + if (buf->error == 0) buf->error = XML_ERR_NO_MEMORY; } @@ -104,10 +103,9 @@ xmlBufMemoryError(xmlBufPtr buf, const char *extra) * To be improved... */ static void -xmlBufOverflowError(xmlBufPtr buf, const char *extra) +xmlBufOverflowError(xmlBufPtr buf) { - __xmlSimpleError(XML_FROM_BUFFER, XML_BUF_OVERFLOW, NULL, NULL, extra); - if ((buf) && (buf->error == 0)) + if (buf->error == 0) buf->error = XML_BUF_OVERFLOW; } @@ -123,10 +121,8 @@ xmlBufCreate(void) { xmlBufPtr ret; ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf)); - if (ret == NULL) { - xmlBufMemoryError(NULL, "creating buffer"); + if (ret == NULL) return(NULL); - } ret->use = 0; ret->error = 0; ret->buffer = NULL; @@ -135,7 +131,6 @@ xmlBufCreate(void) { ret->alloc = xmlBufferAllocScheme; ret->content = (xmlChar *) xmlMallocAtomic(ret->size); if (ret->content == NULL) { - xmlBufMemoryError(ret, "creating buffer"); xmlFree(ret); return(NULL); } @@ -158,10 +153,8 @@ xmlBufCreateSize(size_t size) { if (size == SIZE_MAX) return(NULL); ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf)); - if (ret == NULL) { - xmlBufMemoryError(NULL, "creating buffer"); + if (ret == NULL) return(NULL); - } ret->use = 0; ret->error = 0; ret->buffer = NULL; @@ -171,7 +164,6 @@ xmlBufCreateSize(size_t size) { if (ret->size){ ret->content = (xmlChar *) xmlMallocAtomic(ret->size); if (ret->content == NULL) { - xmlBufMemoryError(ret, "creating buffer"); xmlFree(ret); return(NULL); } @@ -203,8 +195,16 @@ xmlBufDetach(xmlBufPtr buf) { if (buf->error) return(NULL); - ret = buf->content; + if ((buf->alloc == XML_BUFFER_ALLOC_IO) && + (buf->content != buf->contentIO)) { + ret = xmlStrndup(buf->content, buf->use); + xmlFree(buf->contentIO); + } else { + ret = buf->content; + } + buf->content = NULL; + buf->contentIO = NULL; buf->size = 0; buf->use = 0; UPDATE_COMPAT(buf); @@ -383,7 +383,7 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) { if (len < buf->size - buf->use) return(buf->size - buf->use - 1); if (len >= SIZE_MAX - buf->use) { - xmlBufMemoryError(buf, "growing buffer past SIZE_MAX"); + xmlBufMemoryError(buf); return(0); } @@ -400,7 +400,7 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) { */ if ((buf->use + len + 1 >= XML_MAX_TEXT_LENGTH) || (buf->size >= XML_MAX_TEXT_LENGTH)) { - xmlBufMemoryError(buf, "buffer error: text too long\n"); + xmlBufMemoryError(buf); return(0); } if (size >= XML_MAX_TEXT_LENGTH) @@ -411,7 +411,7 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) { newbuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + size); if (newbuf == NULL) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return(0); } buf->contentIO = newbuf; @@ -419,7 +419,7 @@ xmlBufGrowInternal(xmlBufPtr buf, size_t len) { } else { newbuf = (xmlChar *) xmlRealloc(buf->content, size); if (newbuf == NULL) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return(0); } buf->content = newbuf; @@ -641,7 +641,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) * Used to provide parsing limits */ if (size >= XML_MAX_TEXT_LENGTH) { - xmlBufMemoryError(buf, "buffer error: text too long\n"); + xmlBufMemoryError(buf); return(0); } } @@ -662,7 +662,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) } while (size > newSize) { if (newSize > SIZE_MAX / 2) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return 0; } newSize *= 2; @@ -678,7 +678,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) newSize = buf->size; while (size > newSize) { if (newSize > SIZE_MAX / 2) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return 0; } newSize *= 2; @@ -703,7 +703,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) } else { rebuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + newSize); if (rebuf == NULL) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return 0; } buf->contentIO = rebuf; @@ -731,7 +731,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) } } if (rebuf == NULL) { - xmlBufMemoryError(buf, "growing buffer"); + xmlBufMemoryError(buf); return 0; } buf->content = rebuf; @@ -751,8 +751,7 @@ xmlBufResize(xmlBufPtr buf, size_t size) * Add a string range to an XML buffer. if len == -1, the length of * str is recomputed. * - * Returns 0 successful, a positive error code number otherwise - * and -1 in case of internal or API error. + * Returns 0 if successful, -1 in case of error. */ int xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) { @@ -776,7 +775,7 @@ xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) { /* Note that both buf->size and buf->use can be zero here. */ if ((size_t) len >= buf->size - buf->use) { if ((size_t) len >= SIZE_MAX - buf->use) { - xmlBufMemoryError(buf, "growing buffer past SIZE_MAX"); + xmlBufMemoryError(buf); return(-1); } needSize = buf->use + len + 1; @@ -785,14 +784,12 @@ xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len) { * Used to provide parsing limits */ if (needSize >= XML_MAX_TEXT_LENGTH) { - xmlBufMemoryError(buf, "buffer error: text too long\n"); + xmlBufMemoryError(buf); return(-1); } } - if (!xmlBufResize(buf, needSize)){ - xmlBufMemoryError(buf, "growing buffer"); - return XML_ERR_NO_MEMORY; - } + if (!xmlBufResize(buf, needSize)) + return(-1); } memmove(&buf->content[buf->use], str, len); @@ -821,72 +818,6 @@ xmlBufCat(xmlBufPtr buf, const xmlChar *str) { return xmlBufAdd(buf, str, -1); } -/** - * xmlBufCCat: - * @buf: the buffer to dump - * @str: the C char string - * - * Append a zero terminated C string to an XML buffer. - * - * Returns 0 successful, a positive error code number otherwise - * and -1 in case of internal or API error. - */ -int -xmlBufCCat(xmlBufPtr buf, const char *str) { - return xmlBufCat(buf, (const xmlChar *) str); -} - -/** - * xmlBufWriteQuotedString: - * @buf: the XML buffer output - * @string: the string to add - * - * routine which manage and grows an output buffer. This one writes - * a quoted or double quoted #xmlChar string, checking first if it holds - * quote or double-quotes internally - * - * Returns 0 if successful, a positive error code number otherwise - * and -1 in case of internal or API error. - */ -int -xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string) { - const xmlChar *cur, *base; - if ((buf == NULL) || (buf->error)) - return(-1); - CHECK_COMPAT(buf) - if (xmlStrchr(string, '\"')) { - if (xmlStrchr(string, '\'')) { - xmlBufCCat(buf, "\""); - base = cur = string; - while(*cur != 0){ - if(*cur == '"'){ - if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST """, 6); - cur++; - base = cur; - } - else { - cur++; - } - } - if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufCCat(buf, "\""); - } - else{ - xmlBufCCat(buf, "\'"); - xmlBufCat(buf, string); - xmlBufCCat(buf, "\'"); - } - } else { - xmlBufCCat(buf, "\""); - xmlBufCat(buf, string); - xmlBufCCat(buf, "\""); - } - return(0); -} - /** * xmlBufFromBuffer: * @buffer: incoming old buffer to convert to a new one @@ -907,7 +838,6 @@ xmlBufFromBuffer(xmlBufferPtr buffer) { ret = (xmlBufPtr) xmlMalloc(sizeof(xmlBuf)); if (ret == NULL) { - xmlBufMemoryError(NULL, "creating buffer"); return(NULL); } ret->use = buffer->use; @@ -941,12 +871,19 @@ xmlBufBackToBuffer(xmlBufPtr buf) { if (buf == NULL) return(NULL); CHECK_COMPAT(buf) - if ((buf->error) || (buf->buffer == NULL)) { + ret = buf->buffer; + + if ((buf->error) || (ret == NULL)) { xmlBufFree(buf); + if (ret != NULL) { + ret->content = NULL; + ret->contentIO = NULL; + ret->use = 0; + ret->size = 0; + } return(NULL); } - ret = buf->buffer; /* * What to do in case of error in the buffer ??? */ @@ -956,7 +893,7 @@ xmlBufBackToBuffer(xmlBufPtr buf) { * maximum allowed memory for an xmlBuffer on this architecture. * Keep the buffer but provide a truncated size value. */ - xmlBufOverflowError(buf, "Used size too big for xmlBuffer"); + xmlBufOverflowError(buf); ret->use = INT_MAX; ret->size = INT_MAX; } else if (buf->size > INT_MAX) { @@ -966,7 +903,7 @@ xmlBufBackToBuffer(xmlBufPtr buf) { * limit. * Keep the buffer but provide a truncated size value. */ - xmlBufOverflowError(buf, "Allocated size too big for xmlBuffer"); + xmlBufOverflowError(buf); ret->use = buf->use; ret->size = INT_MAX; } else { @@ -980,32 +917,6 @@ xmlBufBackToBuffer(xmlBufPtr buf) { return(ret); } -/** - * xmlBufMergeBuffer: - * @buf: an xmlBufPtr - * @buffer: the buffer to consume into @buf - * - * The content of @buffer is appended to @buf and @buffer is freed - * - * Returns -1 in case of error, 0 otherwise, in any case @buffer is freed - */ -int -xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer) { - int ret = 0; - - if ((buf == NULL) || (buf->error)) { - xmlBufferFree(buffer); - return(-1); - } - CHECK_COMPAT(buf) - if ((buffer != NULL) && (buffer->content != NULL) && - (buffer->use > 0)) { - ret = xmlBufAdd(buf, buffer->content, buffer->use); - } - xmlBufferFree(buffer); - return(ret); -} - /** * xmlBufResetInput: * @buf: an xmlBufPtr @@ -1017,16 +928,7 @@ xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer) { */ int xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input) { - if (input == NULL) - return(-1); - if ((buf == NULL) || (buf->error)) { - input->base = input->cur = input->end = BAD_CAST ""; - return(-1); - } - CHECK_COMPAT(buf) - input->base = input->cur = buf->content; - input->end = &buf->content[buf->use]; - return(0); + return(xmlBufUpdateInput(buf, input, 0)); } /** @@ -1042,16 +944,8 @@ xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input) { */ int xmlBufUpdateInput(xmlBufPtr buf, xmlParserInputPtr input, size_t pos) { - if (input == NULL) + if ((buf == NULL) || (input == NULL)) return(-1); - /* - * TODO: It might be safer to keep using the buffer content if there - * was an error. - */ - if ((buf == NULL) || (buf->error)) { - input->base = input->cur = input->end = BAD_CAST ""; - return(-1); - } CHECK_COMPAT(buf) input->base = buf->content; input->cur = input->base + pos; diff --git a/libraries/libxml2/dict.c b/libraries/libxml2/dict.c index d7156ed3..49e1c6bf 100644 --- a/libraries/libxml2/dict.c +++ b/libraries/libxml2/dict.c @@ -19,11 +19,13 @@ #define IN_LIBXML #include "libxml.h" +#include #include +#include #include -#include #include "private/dict.h" +#include "private/globals.h" #include "private/threads.h" #include @@ -508,6 +510,15 @@ xmlDictHashQName(unsigned seed, const xmlChar *prefix, const xmlChar *name, return(h2 | MAX_HASH_SIZE); } +/** + * xmlDictComputeHash: + * @dict: dictionary + * @string: C string + * + * Compute the hash value of a C string. + * + * Returns the hash value. + */ unsigned xmlDictComputeHash(const xmlDict *dict, const xmlChar *string) { size_t len; @@ -516,6 +527,15 @@ xmlDictComputeHash(const xmlDict *dict, const xmlChar *string) { #define HASH_ROL31(x,n) ((x) << (n) | ((x) & 0x7FFFFFFF) >> (31 - (n))) +/** + * xmlDictCombineHash: + * @v1: first hash value + * @v2: second hash value + * + * Combine two hash values. + * + * Returns the combined hash value. + */ ATTRIBUTE_NO_SANITIZE_INTEGER unsigned xmlDictCombineHash(unsigned v1, unsigned v2) { @@ -904,30 +924,76 @@ xmlDictQLookup(xmlDictPtr dict, const xmlChar *prefix, const xmlChar *name) { * Pseudo-random generator */ +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include + #include +#elif defined(HAVE_GETENTROPY) + #ifdef HAVE_UNISTD_H + #include + #endif + #ifdef HAVE_SYS_RANDOM_H + #include + #endif +#else + #include +#endif + static xmlMutex xmlRngMutex; static unsigned globalRngState[2]; -#ifdef XML_THREAD_LOCAL -static XML_THREAD_LOCAL int localRngInitialized = 0; -static XML_THREAD_LOCAL unsigned localRngState[2]; -#endif - +/* + * xmlInitRandom: + * + * Initialize the PRNG. + */ ATTRIBUTE_NO_SANITIZE_INTEGER void xmlInitRandom(void) { - int var; - xmlInitMutex(&xmlRngMutex); - /* TODO: Get seed values from system PRNG */ - - globalRngState[0] = (unsigned) time(NULL) ^ - HASH_ROL((unsigned) (size_t) &xmlInitRandom, 8); - globalRngState[1] = HASH_ROL((unsigned) (size_t) &xmlRngMutex, 16) ^ - HASH_ROL((unsigned) (size_t) &var, 24); + { +#ifdef _WIN32 + NTSTATUS status; + + status = BCryptGenRandom(NULL, (unsigned char *) globalRngState, + sizeof(globalRngState), + BCRYPT_USE_SYSTEM_PREFERRED_RNG); + if (!BCRYPT_SUCCESS(status)) { + fprintf(stderr, "libxml2: BCryptGenRandom failed with " + "error code %lu\n", GetLastError()); + abort(); + } +#elif defined(HAVE_GETENTROPY) + while (1) { + if (getentropy(globalRngState, sizeof(globalRngState)) == 0) + break; + + if (errno != EINTR) { + fprintf(stderr, "libxml2: getentropy failed with " + "error code %d\n", errno); + abort(); + } + } +#else + int var; + + globalRngState[0] = + (unsigned) time(NULL) ^ + HASH_ROL((unsigned) ((size_t) &xmlInitRandom & 0xFFFFFFFF), 8); + globalRngState[1] = + HASH_ROL((unsigned) ((size_t) &xmlRngMutex & 0xFFFFFFFF), 16) ^ + HASH_ROL((unsigned) ((size_t) &var & 0xFFFFFFFF), 24); +#endif + } } +/* + * xmlCleanupRandom: + * + * Clean up PRNG globals. + */ void xmlCleanupRandom(void) { xmlCleanupMutex(&xmlRngMutex); @@ -947,19 +1013,15 @@ xoroshiro64ss(unsigned *s) { return(result & 0xFFFFFFFF); } +/* + * xmlGlobalRandom: + * + * Generate a pseudo-random value using the global PRNG. + * + * Returns a random value. + */ unsigned -xmlRandom(void) { -#ifdef XML_THREAD_LOCAL - if (!localRngInitialized) { - xmlMutexLock(&xmlRngMutex); - localRngState[0] = xoroshiro64ss(globalRngState); - localRngState[1] = xoroshiro64ss(globalRngState); - localRngInitialized = 1; - xmlMutexUnlock(&xmlRngMutex); - } - - return(xoroshiro64ss(localRngState)); -#else +xmlGlobalRandom(void) { unsigned ret; xmlMutexLock(&xmlRngMutex); @@ -967,6 +1029,21 @@ xmlRandom(void) { xmlMutexUnlock(&xmlRngMutex); return(ret); +} + +/* + * xmlRandom: + * + * Generate a pseudo-random value using the thread-local PRNG. + * + * Returns a random value. + */ +unsigned +xmlRandom(void) { +#ifdef LIBXML_THREAD_ENABLED + return(xoroshiro64ss(xmlGetLocalRngState())); +#else + return(xmlGlobalRandom()); #endif } diff --git a/libraries/libxml2/encoding.c b/libraries/libxml2/encoding.c index bf2e6d07..595e75f9 100644 --- a/libraries/libxml2/encoding.c +++ b/libraries/libxml2/encoding.c @@ -73,57 +73,6 @@ static int xmlCharEncodingAliasesMax = 0; static int xmlLittleEndian = 1; -#ifdef LIBXML_ICU_ENABLED -static uconv_t* -openIcuConverter(const char* name, int toUnicode) -{ - UErrorCode status = U_ZERO_ERROR; - uconv_t *conv = (uconv_t *) xmlMalloc(sizeof(uconv_t)); - if (conv == NULL) - return NULL; - - conv->pivot_source = conv->pivot_buf; - conv->pivot_target = conv->pivot_buf; - - conv->uconv = ucnv_open(name, &status); - if (U_FAILURE(status)) - goto error; - - status = U_ZERO_ERROR; - if (toUnicode) { - ucnv_setToUCallBack(conv->uconv, UCNV_TO_U_CALLBACK_STOP, - NULL, NULL, NULL, &status); - } - else { - ucnv_setFromUCallBack(conv->uconv, UCNV_FROM_U_CALLBACK_STOP, - NULL, NULL, NULL, &status); - } - if (U_FAILURE(status)) - goto error; - - status = U_ZERO_ERROR; - conv->utf8 = ucnv_open("UTF-8", &status); - if (U_SUCCESS(status)) - return conv; - -error: - if (conv->uconv) - ucnv_close(conv->uconv); - xmlFree(conv); - return NULL; -} - -static void -closeIcuConverter(uconv_t *conv) -{ - if (conv != NULL) { - ucnv_close(conv->uconv); - ucnv_close(conv->utf8); - xmlFree(conv); - } -} -#endif /* LIBXML_ICU_ENABLED */ - /************************************************************************ * * * Conversions To/From UTF8 encoding * @@ -1332,9 +1281,8 @@ DECLARE_ISO_FUNCS(16) { (char *) name, in, out EMPTY_ICONV EMPTY_UCONV } static const xmlCharEncodingHandler defaultHandlers[] = { - MAKE_HANDLER("UTF-8", UTF8ToUTF8, UTF8ToUTF8) #ifdef LIBXML_OUTPUT_ENABLED - ,MAKE_HANDLER("UTF-16LE", UTF16LEToUTF8, UTF8ToUTF16LE) + MAKE_HANDLER("UTF-16LE", UTF16LEToUTF8, UTF8ToUTF16LE) ,MAKE_HANDLER("UTF-16BE", UTF16BEToUTF8, UTF8ToUTF16BE) ,MAKE_HANDLER("UTF-16", UTF16LEToUTF8, UTF8ToUTF16) ,MAKE_HANDLER("ISO-8859-1", isolat1ToUTF8, UTF8Toisolat1) @@ -1344,7 +1292,7 @@ static const xmlCharEncodingHandler defaultHandlers[] = { ,MAKE_HANDLER("HTML", NULL, UTF8ToHtml) #endif #else - ,MAKE_HANDLER("UTF-16LE", UTF16LEToUTF8, NULL) + MAKE_HANDLER("UTF-16LE", UTF16LEToUTF8, NULL) ,MAKE_HANDLER("UTF-16BE", UTF16BEToUTF8, NULL) ,MAKE_HANDLER("UTF-16", UTF16LEToUTF8, NULL) ,MAKE_HANDLER("ISO-8859-1", isolat1ToUTF8, NULL) @@ -1374,8 +1322,13 @@ static const xmlCharEncodingHandler defaultHandlers[] = { #define NUM_DEFAULT_HANDLERS \ (sizeof(defaultHandlers) / sizeof(defaultHandlers[0])) -static const xmlCharEncodingHandler *xmlUTF16LEHandler = &defaultHandlers[1]; -static const xmlCharEncodingHandler *xmlUTF16BEHandler = &defaultHandlers[2]; +static const xmlCharEncodingHandler xmlUTF8Handler = + MAKE_HANDLER("UTF-8", UTF8ToUTF8, UTF8ToUTF8); + +static const xmlCharEncodingHandler *xmlUTF16LEHandler = &defaultHandlers[0]; +static const xmlCharEncodingHandler *xmlUTF16BEHandler = &defaultHandlers[1]; +static const xmlCharEncodingHandler *xmlLatin1Handler = &defaultHandlers[3]; +static const xmlCharEncodingHandler *xmlAsciiHandler = &defaultHandlers[4]; /* the size should be growable, but it's not a big deal ... */ #define MAX_ENCODING_HANDLERS 50 @@ -1537,162 +1490,470 @@ xmlRegisterCharEncodingHandler(xmlCharEncodingHandlerPtr handler) { } } +#ifdef LIBXML_ICONV_ENABLED +static int +xmlCreateIconvHandler(const char *name, xmlCharEncodingHandler **out) { + if (!use_iconv) + return XML_ERR_UNSUPPORTED_ENCODING; + + xmlCharEncodingHandlerPtr enc = NULL; + iconv_t icv_in = (iconv_t) -1; + iconv_t icv_out = (iconv_t) -1; + int ret; + + *out = NULL; + + icv_in = iconv_open("UTF-8", name); + if (icv_in == (iconv_t) -1) { + if (errno == EINVAL) + ret = XML_ERR_UNSUPPORTED_ENCODING; + else if (errno == ENOMEM) + ret = XML_ERR_NO_MEMORY; + else + ret = XML_ERR_SYSTEM; + goto error; + } + + icv_out = iconv_open(name, "UTF-8"); + if (icv_out == (iconv_t) -1) { + if (errno == EINVAL) + ret = XML_ERR_UNSUPPORTED_ENCODING; + else if (errno == ENOMEM) + ret = XML_ERR_NO_MEMORY; + else + ret = XML_ERR_SYSTEM; + goto error; + } + + enc = xmlMalloc(sizeof(*enc)); + if (enc == NULL) { + ret = XML_ERR_NO_MEMORY; + goto error; + } + memset(enc, 0, sizeof(*enc)); + + enc->name = xmlMemStrdup(name); + if (enc->name == NULL) { + ret = XML_ERR_NO_MEMORY; + goto error; + } + enc->iconv_in = icv_in; + enc->iconv_out = icv_out; + + *out = enc; + return(0); + +error: + if (enc != NULL) + xmlFree(enc); + if (icv_in != (iconv_t) -1) + iconv_close(icv_in); + if (icv_out != (iconv_t) -1) + iconv_close(icv_out); + return(ret); +} +#endif /* LIBXML_ICONV_ENABLED */ + +#ifdef LIBXML_ICU_ENABLED +static int +openIcuConverter(const char* name, int toUnicode, uconv_t **out) +{ + UErrorCode status; + uconv_t *conv; + + *out = NULL; + + conv = (uconv_t *) xmlMalloc(sizeof(uconv_t)); + if (conv == NULL) + return(XML_ERR_NO_MEMORY); + + conv->pivot_source = conv->pivot_buf; + conv->pivot_target = conv->pivot_buf; + + status = U_ZERO_ERROR; + conv->uconv = ucnv_open(name, &status); + if (U_FAILURE(status)) + goto error; + + status = U_ZERO_ERROR; + if (toUnicode) { + ucnv_setToUCallBack(conv->uconv, UCNV_TO_U_CALLBACK_STOP, + NULL, NULL, NULL, &status); + } + else { + ucnv_setFromUCallBack(conv->uconv, UCNV_FROM_U_CALLBACK_STOP, + NULL, NULL, NULL, &status); + } + if (U_FAILURE(status)) + goto error; + + status = U_ZERO_ERROR; + conv->utf8 = ucnv_open("UTF-8", &status); + if (U_FAILURE(status)) + goto error; + + *out = conv; + return(0); + +error: + if (conv->uconv) + ucnv_close(conv->uconv); + xmlFree(conv); + + if (status == U_FILE_ACCESS_ERROR) + return(XML_ERR_UNSUPPORTED_ENCODING); + if (status == U_MEMORY_ALLOCATION_ERROR) + return(XML_ERR_NO_MEMORY); + return(XML_ERR_SYSTEM); +} + +static void +closeIcuConverter(uconv_t *conv) +{ + if (conv == NULL) + return; + ucnv_close(conv->uconv); + ucnv_close(conv->utf8); + xmlFree(conv); +} + +static int +xmlCreateUconvHandler(const char *name, xmlCharEncodingHandler **out) { + xmlCharEncodingHandlerPtr enc = NULL; + uconv_t *ucv_in = NULL; + uconv_t *ucv_out = NULL; + int ret; + + ret = openIcuConverter(name, 1, &ucv_in); + if (ret != 0) + goto error; + ret = openIcuConverter(name, 0, &ucv_out); + if (ret != 0) + goto error; + + enc = (xmlCharEncodingHandlerPtr) + xmlMalloc(sizeof(xmlCharEncodingHandler)); + if (enc == NULL) { + ret = XML_ERR_NO_MEMORY; + goto error; + } + memset(enc, 0, sizeof(xmlCharEncodingHandler)); + + enc->name = xmlMemStrdup(name); + if (enc->name == NULL) { + ret = XML_ERR_NO_MEMORY; + goto error; + } + enc->input = NULL; + enc->output = NULL; + enc->uconv_in = ucv_in; + enc->uconv_out = ucv_out; + + *out = enc; + return(0); + +error: + if (enc != NULL) + xmlFree(enc); + if (ucv_in != NULL) + closeIcuConverter(ucv_in); + if (ucv_out != NULL) + closeIcuConverter(ucv_out); + return(ret); +} +#endif /* LIBXML_ICU_ENABLED */ + /** - * xmlGetCharEncodingHandler: + * xmlFindExtraHandler: + * @name: a string describing the char encoding. + * @output: boolean, use handler for output + * @out: pointer to resulting handler + * + * Search the non-default handlers for an exact match. + * + * Returns 0 on success, 1 if no handler was found, -1 if a memory + * allocation failed. + */ +static int +xmlFindExtraHandler(const char *name, int output, + xmlCharEncodingHandler **out) { + int ret; + int i; + + (void) ret; + + if (handlers != NULL) { + for (i = 0; i < nbCharEncodingHandler; i++) { + xmlCharEncodingHandler *handler = handlers[i]; + + if (!xmlStrcasecmp((const xmlChar *) name, + (const xmlChar *) handler->name)) { + if (output) { + if (handler->output != NULL) { + *out = handler; + return(0); + } + } else { + if (handler->input != NULL) { + *out = handler; + return(0); + } + } + } + } + } + +#ifdef LIBXML_ICONV_ENABLED + ret = xmlCreateIconvHandler(name, out); + if (*out != NULL) + return(0); + if (ret != XML_ERR_UNSUPPORTED_ENCODING) + return(ret); +#endif /* LIBXML_ICONV_ENABLED */ + +#ifdef LIBXML_ICU_ENABLED + ret = xmlCreateUconvHandler(name, out); + if (*out != NULL) + return(0); + if (ret != XML_ERR_UNSUPPORTED_ENCODING) + return(ret); +#endif /* LIBXML_ICU_ENABLED */ + + return(XML_ERR_UNSUPPORTED_ENCODING); +} + +/** + * xmlFindHandler: + * @name: a string describing the char encoding. + * @output: boolean, use handler for output + * @out: pointer to resulting handler + * + * Search all handlers for an exact match. + * + * Returns 0 on success, 1 if no handler was found, -1 if a memory + * allocation failed. + */ +static int +xmlFindHandler(const char *name, int output, xmlCharEncodingHandler **out) { + int i; + + /* + * Check for default handlers + */ + for (i = 0; i < (int) NUM_DEFAULT_HANDLERS; i++) { + xmlCharEncodingHandler *handler; + + handler = (xmlCharEncodingHandler *) &defaultHandlers[i]; + + if (xmlStrcasecmp((const xmlChar *) name, + (const xmlChar *) handler->name) == 0) { + if (output) { + if (handler->output != NULL) { + *out = handler; + return(0); + } + } else { + if (handler->input != NULL) { + *out = handler; + return(0); + } + } + } + } + + /* + * Check for other handlers + */ + return(xmlFindExtraHandler(name, output, out)); +} + +/** + * xmlLookupCharEncodingHandler: * @enc: an xmlCharEncoding value. + * @out: pointer to result * - * Search in the registered set the handler able to read/write that encoding. + * Find or create a handler matching the encoding. If no default or + * registered handler could be found, try to create a handler using + * iconv or ICU if supported. * - * Returns the handler or NULL if not found + * The handler must be closed with xmlCharEncCloseFunc. + * + * Available since 2.13.0. + * + * Returns an xmlParserErrors error code. */ -xmlCharEncodingHandlerPtr -xmlGetCharEncodingHandler(xmlCharEncoding enc) { - xmlCharEncodingHandlerPtr handler; +int +xmlLookupCharEncodingHandler(xmlCharEncoding enc, + xmlCharEncodingHandler **out) { + const char *name = NULL; + static const char *const ebcdicNames[] = { + "EBCDIC", "ebcdic", "EBCDIC-US", "IBM-037" + }; + static const char *const ucs4Names[] = { + "ISO-10646-UCS-4", "UCS-4", "UCS4" + }; + static const char *const ucs2Names[] = { + "ISO-10646-UCS-2", "UCS-2", "UCS2" + }; + static const char *const shiftJisNames[] = { + "SHIFT-JIS", "SHIFT_JIS", "Shift_JIS", + }; + const char *const *names = NULL; + int numNames = 0; + int ret; + int i; + + if (out == NULL) + return(XML_ERR_ARGUMENT); + *out = NULL; switch (enc) { case XML_CHAR_ENCODING_ERROR: - return(NULL); + return(XML_ERR_UNSUPPORTED_ENCODING); case XML_CHAR_ENCODING_NONE: - return(NULL); + return(0); case XML_CHAR_ENCODING_UTF8: - return(NULL); + return(0); case XML_CHAR_ENCODING_UTF16LE: - return((xmlCharEncodingHandlerPtr) xmlUTF16LEHandler); + *out = (xmlCharEncodingHandler *) xmlUTF16LEHandler; + return(0); case XML_CHAR_ENCODING_UTF16BE: - return((xmlCharEncodingHandlerPtr) xmlUTF16BEHandler); + *out = (xmlCharEncodingHandler *) xmlUTF16BEHandler; + return(0); case XML_CHAR_ENCODING_EBCDIC: - handler = xmlFindCharEncodingHandler("EBCDIC"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("ebcdic"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("EBCDIC-US"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("IBM-037"); - if (handler != NULL) return(handler); + names = ebcdicNames; + numNames = sizeof(ebcdicNames) / sizeof(ebcdicNames[0]); break; case XML_CHAR_ENCODING_UCS4BE: - handler = xmlFindCharEncodingHandler("ISO-10646-UCS-4"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS-4"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS4"); - if (handler != NULL) return(handler); - break; case XML_CHAR_ENCODING_UCS4LE: - handler = xmlFindCharEncodingHandler("ISO-10646-UCS-4"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS-4"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS4"); - if (handler != NULL) return(handler); + names = ucs4Names; + numNames = sizeof(ucs4Names) / sizeof(ucs4Names[0]); break; case XML_CHAR_ENCODING_UCS4_2143: break; case XML_CHAR_ENCODING_UCS4_3412: break; case XML_CHAR_ENCODING_UCS2: - handler = xmlFindCharEncodingHandler("ISO-10646-UCS-2"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS-2"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("UCS2"); - if (handler != NULL) return(handler); + names = ucs2Names; + numNames = sizeof(ucs2Names) / sizeof(ucs2Names[0]); break; - /* - * We used to keep ISO Latin encodings native in the - * generated data. This led to so many problems that - * this has been removed. One can still change this - * back by registering no-ops encoders for those - */ + case XML_CHAR_ENCODING_ASCII: + *out = (xmlCharEncodingHandler *) xmlAsciiHandler; + return(0); case XML_CHAR_ENCODING_8859_1: - handler = xmlFindCharEncodingHandler("ISO-8859-1"); - if (handler != NULL) return(handler); - break; + *out = (xmlCharEncodingHandler *) xmlLatin1Handler; + return(0); case XML_CHAR_ENCODING_8859_2: - handler = xmlFindCharEncodingHandler("ISO-8859-2"); - if (handler != NULL) return(handler); + name = "ISO-8859-2"; break; case XML_CHAR_ENCODING_8859_3: - handler = xmlFindCharEncodingHandler("ISO-8859-3"); - if (handler != NULL) return(handler); + name = "ISO-8859-3"; break; case XML_CHAR_ENCODING_8859_4: - handler = xmlFindCharEncodingHandler("ISO-8859-4"); - if (handler != NULL) return(handler); + name = "ISO-8859-4"; break; case XML_CHAR_ENCODING_8859_5: - handler = xmlFindCharEncodingHandler("ISO-8859-5"); - if (handler != NULL) return(handler); + name = "ISO-8859-5"; break; case XML_CHAR_ENCODING_8859_6: - handler = xmlFindCharEncodingHandler("ISO-8859-6"); - if (handler != NULL) return(handler); + name = "ISO-8859-6"; break; case XML_CHAR_ENCODING_8859_7: - handler = xmlFindCharEncodingHandler("ISO-8859-7"); - if (handler != NULL) return(handler); + name = "ISO-8859-7"; break; case XML_CHAR_ENCODING_8859_8: - handler = xmlFindCharEncodingHandler("ISO-8859-8"); - if (handler != NULL) return(handler); + name = "ISO-8859-8"; break; case XML_CHAR_ENCODING_8859_9: - handler = xmlFindCharEncodingHandler("ISO-8859-9"); - if (handler != NULL) return(handler); + name = "ISO-8859-9"; break; - case XML_CHAR_ENCODING_2022_JP: - handler = xmlFindCharEncodingHandler("ISO-2022-JP"); - if (handler != NULL) return(handler); + name = "ISO-2022-JP"; break; case XML_CHAR_ENCODING_SHIFT_JIS: - handler = xmlFindCharEncodingHandler("SHIFT-JIS"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("SHIFT_JIS"); - if (handler != NULL) return(handler); - handler = xmlFindCharEncodingHandler("Shift_JIS"); - if (handler != NULL) return(handler); + names = shiftJisNames; + numNames = sizeof(shiftJisNames) / sizeof(shiftJisNames[0]); break; case XML_CHAR_ENCODING_EUC_JP: - handler = xmlFindCharEncodingHandler("EUC-JP"); - if (handler != NULL) return(handler); + name = "EUC-JP"; break; default: break; } - return(NULL); + if (name != NULL) + return(xmlFindExtraHandler(name, 0, out)); + + if (names != NULL) { + for (i = 0; i < numNames; i++) { + ret = xmlFindExtraHandler(names[i], 0, out); + if (*out != NULL) + return(0); + if (ret != XML_ERR_UNSUPPORTED_ENCODING) + return(ret); + } + } + + return(XML_ERR_UNSUPPORTED_ENCODING); } /** - * xmlFindCharEncodingHandler: - * @name: a string describing the char encoding. + * xmlGetCharEncodingHandler: + * @enc: an xmlCharEncoding value. * - * Search in the registered set the handler able to read/write that encoding - * or create a new one. + * DEPRECATED: Use xmlLookupCharEncodingHandler which has better error + * reporting. * - * Returns the handler or NULL if not found + * Returns the handler or NULL if no handler was found or an error + * occurred. */ xmlCharEncodingHandlerPtr -xmlFindCharEncodingHandler(const char *name) { +xmlGetCharEncodingHandler(xmlCharEncoding enc) { + xmlCharEncodingHandler *ret; + + xmlLookupCharEncodingHandler(enc, &ret); + return(ret); +} + +/** + * xmlOpenCharEncodingHandler: + * @name: a string describing the char encoding. + * @output: boolean, use handler for output + * @out: pointer to result + * + * Find or create a handler matching the encoding. If no default or + * registered handler could be found, try to create a handler using + * iconv or ICU if supported. + * + * The handler must be closed with xmlCharEncCloseFunc. + * + * If the encoding is UTF-8, a NULL handler and no error code will + * be returned. + * + * Available since 2.13.0. + * + * Returns an xmlParserErrors error code. + */ +int +xmlOpenCharEncodingHandler(const char *name, int output, + xmlCharEncodingHandler **out) { const char *nalias; const char *norig; - xmlCharEncoding alias; -#ifdef LIBXML_ICONV_ENABLED - xmlCharEncodingHandlerPtr enc; - iconv_t icv_in, icv_out; -#endif /* LIBXML_ICONV_ENABLED */ -#ifdef LIBXML_ICU_ENABLED - xmlCharEncodingHandlerPtr encu; - uconv_t *ucv_in, *ucv_out; -#endif /* LIBXML_ICU_ENABLED */ - char upper[100]; - int i; + xmlCharEncoding enc; + int ret; - if (name == NULL) return(NULL); - if (name[0] == 0) return(NULL); + if (out == NULL) + return(XML_ERR_ARGUMENT); + *out = NULL; + + if (name == NULL) + return(XML_ERR_ARGUMENT); + + if ((xmlStrcasecmp(BAD_CAST name, BAD_CAST "UTF-8") == 0) || + (xmlStrcasecmp(BAD_CAST name, BAD_CAST "UTF8") == 0)) + return(XML_ERR_OK); /* * Do the alias resolution @@ -1702,112 +1963,46 @@ xmlFindCharEncodingHandler(const char *name) { if (nalias != NULL) name = nalias; + ret = xmlFindHandler(name, output, out); + if (*out != NULL) + return(0); + if (ret != XML_ERR_UNSUPPORTED_ENCODING) + return(ret); + /* - * Check first for directly registered encoding names + * Fallback using the canonical names + * + * TODO: We should make sure that the name of the returned + * handler equals norig. */ - for (i = 0;i < 99;i++) { - upper[i] = (char) toupper((unsigned char) name[i]); - if (upper[i] == 0) break; - } - upper[i] = 0; - - for (i = 0; i < (int) NUM_DEFAULT_HANDLERS; i++) { - if (strcmp(upper, defaultHandlers[i].name) == 0) - return((xmlCharEncodingHandlerPtr) &defaultHandlers[i]); - } - - if (handlers != NULL) { - for (i = 0;i < nbCharEncodingHandler; i++) { - if (!strcmp(upper, handlers[i]->name)) { - return(handlers[i]); - } - } - } + enc = xmlParseCharEncoding(norig); + return(xmlLookupCharEncodingHandler(enc, out)); +} -#ifdef LIBXML_ICONV_ENABLED -if (use_iconv) { - /* check whether iconv can handle this */ - icv_in = iconv_open("UTF-8", name); - icv_out = iconv_open(name, "UTF-8"); - if (icv_in == (iconv_t) -1) { - icv_in = iconv_open("UTF-8", upper); - } - if (icv_out == (iconv_t) -1) { - icv_out = iconv_open(upper, "UTF-8"); - } - if ((icv_in != (iconv_t) -1) && (icv_out != (iconv_t) -1)) { - enc = (xmlCharEncodingHandlerPtr) - xmlMalloc(sizeof(xmlCharEncodingHandler)); - if (enc == NULL) { - iconv_close(icv_in); - iconv_close(icv_out); - return(NULL); - } - memset(enc, 0, sizeof(xmlCharEncodingHandler)); - enc->name = xmlMemStrdup(name); - if (enc->name == NULL) { - xmlFree(enc); - iconv_close(icv_in); - iconv_close(icv_out); - return(NULL); - } - enc->input = NULL; - enc->output = NULL; - enc->iconv_in = icv_in; - enc->iconv_out = icv_out; - return enc; - } else if ((icv_in != (iconv_t) -1) || icv_out != (iconv_t) -1) { - if (icv_in != (iconv_t) -1) - iconv_close(icv_in); - else - iconv_close(icv_out); - }} -#endif /* LIBXML_ICONV_ENABLED */ -#ifdef LIBXML_ICU_ENABLED - /* check whether icu can handle this */ - ucv_in = openIcuConverter(name, 1); - ucv_out = openIcuConverter(name, 0); - if (ucv_in != NULL && ucv_out != NULL) { - encu = (xmlCharEncodingHandlerPtr) - xmlMalloc(sizeof(xmlCharEncodingHandler)); - if (encu == NULL) { - closeIcuConverter(ucv_in); - closeIcuConverter(ucv_out); - return(NULL); - } - memset(encu, 0, sizeof(xmlCharEncodingHandler)); - encu->name = xmlMemStrdup(name); - if (encu->name == NULL) { - xmlFree(encu); - closeIcuConverter(ucv_in); - closeIcuConverter(ucv_out); - return(NULL); - } - encu->input = NULL; - encu->output = NULL; - encu->uconv_in = ucv_in; - encu->uconv_out = ucv_out; - return encu; - } else if (ucv_in != NULL || ucv_out != NULL) { - closeIcuConverter(ucv_in); - closeIcuConverter(ucv_out); - } -#endif /* LIBXML_ICU_ENABLED */ +/** + * xmlFindCharEncodingHandler: + * @name: a string describing the char encoding. + * + * DEPRECATED: Use xmlOpenCharEncodingHandler which has better error + * reporting. + * + * Returns the handler or NULL if no handler was found or an error + * occurred. + */ +xmlCharEncodingHandlerPtr +xmlFindCharEncodingHandler(const char *name) { + xmlCharEncodingHandler *ret; /* - * Fallback using the canonical names + * This handler shouldn't be used, but we must return a non-NULL + * handler. */ - alias = xmlParseCharEncoding(norig); - if (alias != XML_CHAR_ENCODING_ERROR) { - const char* canon; - canon = xmlGetCharEncodingName(alias); - if ((canon != NULL) && (strcmp(name, canon))) { - return(xmlFindCharEncodingHandler(canon)); - } - } + if ((xmlStrcasecmp(BAD_CAST name, BAD_CAST "UTF-8") == 0) || + (xmlStrcasecmp(BAD_CAST name, BAD_CAST "UTF8") == 0)) + return((xmlCharEncodingHandlerPtr) &xmlUTF8Handler); - /* If "none of the above", give up */ - return(NULL); + xmlOpenCharEncodingHandler(name, 0, &ret); + return(ret); } /************************************************************************ @@ -2172,7 +2367,8 @@ xmlCharEncInput(xmlParserInputBufferPtr input) else input->rawconsumed += c_in; - if ((c_out == 0) && (ret != 0)) { + if (((ret != 0) && (c_out == 0)) || + (ret == XML_ENC_ERR_MEMORY)) { if (input->error == 0) input->error = xmlEncConvertError(ret); return(ret); @@ -2280,7 +2476,10 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init) if (toconv > 64 * 1024) toconv = 64 * 1024; if (toconv * 4 >= written) { - xmlBufGrow(out, toconv * 4); + if (xmlBufGrow(out, toconv * 4) < 0) { + ret = XML_ENC_ERR_MEMORY; + goto error; + } written = xmlBufAvail(out); } if (written > 256 * 1024) @@ -2317,7 +2516,6 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init) */ charrefLen = snprintf((char *) &charref[0], sizeof(charref), "&#%d;", cur); - xmlBufShrink(in, len); xmlBufGrow(out, charrefLen * 4); c_out = xmlBufAvail(out); c_in = charrefLen; @@ -2328,13 +2526,15 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init) goto error; } + xmlBufShrink(in, len); xmlBufAddLen(out, c_out); writtentot += c_out; goto retry; } error: - if ((writtentot <= 0) && (ret != 0)) { + if (((writtentot <= 0) && (ret != 0)) || + (ret == XML_ENC_ERR_MEMORY)) { if (output->error == 0) output->error = xmlEncConvertError(ret); return(ret); @@ -2765,7 +2965,7 @@ ISO8859xToUTF8(unsigned char* out, int *outlen, * Lookup tables for ISO-8859-2..ISO-8859-16 transcoding * ************************************************************************/ -static unsigned short const xmlunicodetable_ISO8859_2 [128] = { +static const unsigned short xmlunicodetable_ISO8859_2 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -2814,7 +3014,7 @@ static const unsigned char xmltranscodetable_ISO8859_2 [48 + 6 * 64] = { "\x00\x00\x00\xf3\xf4\x00\xf6\xf7\x00\x00\xfa\x00\xfc\xfd\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_3 [128] = { +static const unsigned short xmlunicodetable_ISO8859_3 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -2867,7 +3067,7 @@ static const unsigned char xmltranscodetable_ISO8859_3 [48 + 7 * 64] = { "\x00\xf1\xf2\xf3\xf4\x00\xf6\xf7\x00\xf9\xfa\xfb\xfc\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_4 [128] = { +static const unsigned short xmlunicodetable_ISO8859_4 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -2916,7 +3116,7 @@ static const unsigned char xmltranscodetable_ISO8859_4 [48 + 6 * 64] = { "\x00\x00\x00\x00\xf4\xf5\xf6\xf7\xf8\x00\xfa\xfb\xfc\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_5 [128] = { +static const unsigned short xmlunicodetable_ISO8859_5 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -2965,7 +3165,7 @@ static const unsigned char xmltranscodetable_ISO8859_5 [48 + 6 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_6 [128] = { +static const unsigned short xmlunicodetable_ISO8859_6 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3010,7 +3210,7 @@ static const unsigned char xmltranscodetable_ISO8859_6 [48 + 5 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_7 [128] = { +static const unsigned short xmlunicodetable_ISO8859_7 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3063,7 +3263,7 @@ static const unsigned char xmltranscodetable_ISO8859_7 [48 + 7 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_8 [128] = { +static const unsigned short xmlunicodetable_ISO8859_8 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3116,7 +3316,7 @@ static const unsigned char xmltranscodetable_ISO8859_8 [48 + 7 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_9 [128] = { +static const unsigned short xmlunicodetable_ISO8859_9 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3161,7 +3361,7 @@ static const unsigned char xmltranscodetable_ISO8859_9 [48 + 5 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_10 [128] = { +static const unsigned short xmlunicodetable_ISO8859_10 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3214,7 +3414,7 @@ static const unsigned char xmltranscodetable_ISO8859_10 [48 + 7 * 64] = { "\xf0\x00\x00\xf3\xf4\xf5\xf6\x00\xf8\x00\xfa\xfb\xfc\xfd\xfe\x00" }; -static unsigned short const xmlunicodetable_ISO8859_11 [128] = { +static const unsigned short xmlunicodetable_ISO8859_11 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3263,7 +3463,7 @@ static const unsigned char xmltranscodetable_ISO8859_11 [48 + 6 * 64] = { "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_13 [128] = { +static const unsigned short xmlunicodetable_ISO8859_13 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3316,7 +3516,7 @@ static const unsigned char xmltranscodetable_ISO8859_13 [48 + 7 * 64] = { "\x00\x00\x00\x00\x00\x00\xcd\xed\x00\x00\x00\xcf\xef\x00\x00\x00" }; -static unsigned short const xmlunicodetable_ISO8859_14 [128] = { +static const unsigned short xmlunicodetable_ISO8859_14 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3381,7 +3581,7 @@ static const unsigned char xmltranscodetable_ISO8859_14 [48 + 10 * 64] = { "\x00\xf1\xf2\xf3\xf4\xf5\xf6\x00\xf8\xf9\xfa\xfb\xfc\xfd\x00\xff" }; -static unsigned short const xmlunicodetable_ISO8859_15 [128] = { +static const unsigned short xmlunicodetable_ISO8859_15 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, @@ -3430,7 +3630,7 @@ static const unsigned char xmltranscodetable_ISO8859_15 [48 + 6 * 64] = { "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" }; -static unsigned short const xmlunicodetable_ISO8859_16 [128] = { +static const unsigned short xmlunicodetable_ISO8859_16 [128] = { 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, diff --git a/libraries/libxml2/entities.c b/libraries/libxml2/entities.c index aec51441..f7792a81 100644 --- a/libraries/libxml2/entities.c +++ b/libraries/libxml2/entities.c @@ -24,6 +24,7 @@ #include #include #include +#include #include "private/entities.h" #include "private/error.h" @@ -68,50 +69,11 @@ static xmlEntity xmlEntityApos = { NULL, NULL, NULL, NULL, 0, 0, 0 }; -/** - * xmlEntitiesErrMemory: - * @extra: extra information - * - * Handle an out of memory condition - */ -static void -xmlEntitiesErrMemory(const char *extra) -{ - __xmlSimpleError(XML_FROM_TREE, XML_ERR_NO_MEMORY, NULL, NULL, extra); -} - -/** - * xmlEntitiesErr: - * @code: the error code - * @msg: the message - * - * Raise an error. - */ -static void LIBXML_ATTR_FORMAT(2,0) -xmlEntitiesErr(xmlParserErrors code, const char *msg) -{ - __xmlSimpleError(XML_FROM_TREE, code, NULL, msg, NULL); -} - -/** - * xmlEntitiesWarn: - * @code: the error code - * @msg: the message - * - * Raise a warning. - */ -static void LIBXML_ATTR_FORMAT(2,0) -xmlEntitiesWarn(xmlParserErrors code, const char *msg, const xmlChar *str1) -{ - __xmlRaiseError(NULL, NULL, NULL, - NULL, NULL, XML_FROM_TREE, code, - XML_ERR_WARNING, NULL, 0, - (const char *)str1, NULL, NULL, 0, 0, - msg, (const char *)str1, NULL); -} - /* - * xmlFreeEntity : clean-up an entity record. + * xmlFreeEntity: + * @entity: an entity + * + * Frees the entity. */ void xmlFreeEntity(xmlEntityPtr entity) @@ -125,7 +87,7 @@ xmlFreeEntity(xmlEntityPtr entity) dict = entity->doc->dict; - if ((entity->children) && (entity->owner == 1) && + if ((entity->children) && (entity == (xmlEntityPtr) entity->children->parent)) xmlFreeNodeList(entity->children); if ((entity->name != NULL) && @@ -150,37 +112,43 @@ xmlFreeEntity(xmlEntityPtr entity) * internal routine doing the entity node structures allocations */ static xmlEntityPtr -xmlCreateEntity(xmlDictPtr dict, const xmlChar *name, int type, +xmlCreateEntity(xmlDocPtr doc, const xmlChar *name, int type, const xmlChar *ExternalID, const xmlChar *SystemID, const xmlChar *content) { xmlEntityPtr ret; ret = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity)); - if (ret == NULL) { - xmlEntitiesErrMemory("xmlCreateEntity: malloc failed"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlEntity)); + ret->doc = doc; ret->type = XML_ENTITY_DECL; /* * fill the structure. */ ret->etype = (xmlEntityType) type; - if (dict == NULL) { + if ((doc == NULL) || (doc->dict == NULL)) ret->name = xmlStrdup(name); - if (ExternalID != NULL) - ret->ExternalID = xmlStrdup(ExternalID); - if (SystemID != NULL) - ret->SystemID = xmlStrdup(SystemID); - } else { - ret->name = xmlDictLookup(dict, name, -1); - ret->ExternalID = xmlStrdup(ExternalID); - ret->SystemID = xmlStrdup(SystemID); + else + ret->name = xmlDictLookup(doc->dict, name, -1); + if (ret->name == NULL) + goto error; + if (ExternalID != NULL) { + ret->ExternalID = xmlStrdup(ExternalID); + if (ret->ExternalID == NULL) + goto error; + } + if (SystemID != NULL) { + ret->SystemID = xmlStrdup(SystemID); + if (ret->SystemID == NULL) + goto error; } if (content != NULL) { ret->length = xmlStrlen(content); ret->content = xmlStrndup(content, ret->length); + if (ret->content == NULL) + goto error; } else { ret->length = 0; ret->content = NULL; @@ -188,28 +156,53 @@ xmlCreateEntity(xmlDictPtr dict, const xmlChar *name, int type, ret->URI = NULL; /* to be computed by the layer knowing the defining entity */ ret->orig = NULL; - ret->owner = 0; return(ret); + +error: + xmlFreeEntity(ret); + return(NULL); } -/* - * xmlAddEntity : register a new entity for an entities table. +/** + * xmlAddEntity: + * @doc: the document + * @extSubset: add to the external or internal subset + * @name: the entity name + * @type: the entity type XML_xxx_yyy_ENTITY + * @ExternalID: the entity external ID if available + * @SystemID: the entity system ID if available + * @content: the entity content + * @out: pointer to resulting entity (optional) + * + * Register a new entity for this document. + * + * Available since 2.13.0. + * + * Returns an xmlParserErrors error code. */ -static xmlEntityPtr -xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type, +int +xmlAddEntity(xmlDocPtr doc, int extSubset, const xmlChar *name, int type, const xmlChar *ExternalID, const xmlChar *SystemID, - const xmlChar *content) { + const xmlChar *content, xmlEntityPtr *out) { + xmlDtdPtr dtd; xmlDictPtr dict = NULL; xmlEntitiesTablePtr table = NULL; xmlEntityPtr ret, predef; + int res; - if (name == NULL) - return(NULL); + if (out != NULL) + *out = NULL; + if ((doc == NULL) || (name == NULL)) + return(XML_ERR_ARGUMENT); + dict = doc->dict; + + if (extSubset) + dtd = doc->extSubset; + else + dtd = doc->intSubset; if (dtd == NULL) - return(NULL); - if (dtd->doc != NULL) - dict = dtd->doc->dict; + return(XML_DTD_NO_DTD); switch (type) { case XML_INTERNAL_GENERAL_ENTITY: @@ -246,41 +239,60 @@ xmlAddEntity(xmlDtdPtr dtd, const xmlChar *name, int type, } } } - if (!valid) { - xmlEntitiesWarn(XML_ERR_ENTITY_PROCESSING, - "xmlAddEntity: invalid redeclaration of predefined" - " entity '%s'", name); - return(NULL); - } + if (!valid) + return(XML_ERR_REDECL_PREDEF_ENTITY); } - if (dtd->entities == NULL) + if (dtd->entities == NULL) { dtd->entities = xmlHashCreateDict(0, dict); + if (dtd->entities == NULL) + return(XML_ERR_NO_MEMORY); + } table = dtd->entities; break; case XML_INTERNAL_PARAMETER_ENTITY: case XML_EXTERNAL_PARAMETER_ENTITY: - if (dtd->pentities == NULL) + if (dtd->pentities == NULL) { dtd->pentities = xmlHashCreateDict(0, dict); + if (dtd->pentities == NULL) + return(XML_ERR_NO_MEMORY); + } table = dtd->pentities; break; - case XML_INTERNAL_PREDEFINED_ENTITY: - return(NULL); + default: + return(XML_ERR_ARGUMENT); } - if (table == NULL) - return(NULL); - ret = xmlCreateEntity(dict, name, type, ExternalID, SystemID, content); + ret = xmlCreateEntity(dtd->doc, name, type, ExternalID, SystemID, content); if (ret == NULL) - return(NULL); - ret->doc = dtd->doc; + return(XML_ERR_NO_MEMORY); - if (xmlHashAddEntry(table, name, ret)) { + res = xmlHashAdd(table, name, ret); + if (res < 0) { + xmlFreeEntity(ret); + return(XML_ERR_NO_MEMORY); + } else if (res == 0) { /* * entity was already defined at another level. */ xmlFreeEntity(ret); - return(NULL); + return(XML_WAR_ENTITY_REDEFINED); } - return(ret); + + /* + * Link it to the DTD + */ + ret->parent = dtd; + ret->doc = dtd->doc; + if (dtd->last == NULL) { + dtd->children = dtd->last = (xmlNodePtr) ret; + } else { + dtd->last->next = (xmlNodePtr) ret; + ret->prev = dtd->last; + dtd->last = (xmlNodePtr) ret; + } + + if (out != NULL) + *out = ret; + return(0); } /** @@ -337,34 +349,8 @@ xmlAddDtdEntity(xmlDocPtr doc, const xmlChar *name, int type, const xmlChar *ExternalID, const xmlChar *SystemID, const xmlChar *content) { xmlEntityPtr ret; - xmlDtdPtr dtd; - if (doc == NULL) { - xmlEntitiesErr(XML_DTD_NO_DOC, - "xmlAddDtdEntity: document is NULL"); - return(NULL); - } - if (doc->extSubset == NULL) { - xmlEntitiesErr(XML_DTD_NO_DTD, - "xmlAddDtdEntity: document without external subset"); - return(NULL); - } - dtd = doc->extSubset; - ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content); - if (ret == NULL) return(NULL); - - /* - * Link it to the DTD - */ - ret->parent = dtd; - ret->doc = dtd->doc; - if (dtd->last == NULL) { - dtd->children = dtd->last = (xmlNodePtr) ret; - } else { - dtd->last->next = (xmlNodePtr) ret; - ret->prev = dtd->last; - dtd->last = (xmlNodePtr) ret; - } + xmlAddEntity(doc, 1, name, type, ExternalID, SystemID, content, &ret); return(ret); } @@ -386,34 +372,8 @@ xmlAddDocEntity(xmlDocPtr doc, const xmlChar *name, int type, const xmlChar *ExternalID, const xmlChar *SystemID, const xmlChar *content) { xmlEntityPtr ret; - xmlDtdPtr dtd; - - if (doc == NULL) { - xmlEntitiesErr(XML_DTD_NO_DOC, - "xmlAddDocEntity: document is NULL"); - return(NULL); - } - if (doc->intSubset == NULL) { - xmlEntitiesErr(XML_DTD_NO_DTD, - "xmlAddDocEntity: document without internal subset"); - return(NULL); - } - dtd = doc->intSubset; - ret = xmlAddEntity(dtd, name, type, ExternalID, SystemID, content); - if (ret == NULL) return(NULL); - /* - * Link it to the DTD - */ - ret->parent = dtd; - ret->doc = dtd->doc; - if (dtd->last == NULL) { - dtd->children = dtd->last = (xmlNodePtr) ret; - } else { - dtd->last->next = (xmlNodePtr) ret; - ret->prev = dtd->last; - dtd->last = (xmlNodePtr) ret; - } + xmlAddEntity(doc, 0, name, type, ExternalID, SystemID, content, &ret); return(ret); } @@ -438,21 +398,12 @@ xmlEntityPtr xmlNewEntity(xmlDocPtr doc, const xmlChar *name, int type, const xmlChar *ExternalID, const xmlChar *SystemID, const xmlChar *content) { - xmlEntityPtr ret; - xmlDictPtr dict; - if ((doc != NULL) && (doc->intSubset != NULL)) { return(xmlAddDocEntity(doc, name, type, ExternalID, SystemID, content)); } - if (doc != NULL) - dict = doc->dict; - else - dict = NULL; - ret = xmlCreateEntity(dict, name, type, ExternalID, SystemID, content); - if (ret == NULL) + if (name == NULL) return(NULL); - ret->doc = doc; - return(ret); + return(xmlCreateEntity(doc, name, type, ExternalID, SystemID, content)); } /** @@ -604,10 +555,8 @@ xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) { */ buffer_size = 1000; buffer = (xmlChar *) xmlMalloc(buffer_size); - if (buffer == NULL) { - xmlEntitiesErrMemory("xmlEncodeEntities: malloc failed"); + if (buffer == NULL) return(NULL); - } out = buffer; while (*cur != '\0') { @@ -707,53 +656,18 @@ xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) { * cur[3] is 10xxxxxx if cur[0] is 1111xxxx * cur[0] is not 11111xxx */ - char buf[11], *ptr; - int val = 0, l = 1; - - if (((cur[0] & 0xC0) != 0xC0) || - ((cur[1] & 0xC0) != 0x80) || - (((cur[0] & 0xE0) == 0xE0) && ((cur[2] & 0xC0) != 0x80)) || - (((cur[0] & 0xF0) == 0xF0) && ((cur[3] & 0xC0) != 0x80)) || - (((cur[0] & 0xF8) == 0xF8))) { - xmlEntitiesErr(XML_CHECK_NOT_UTF8, - "xmlEncodeEntities: input not UTF-8"); - snprintf(buf, sizeof(buf), "&#%d;", *cur); - buf[sizeof(buf) - 1] = 0; - ptr = buf; - while (*ptr != 0) *out++ = *ptr++; - cur++; - continue; - } else if (*cur < 0xE0) { - val = (cur[0]) & 0x1F; - val <<= 6; - val |= (cur[1]) & 0x3F; - l = 2; - } else if (*cur < 0xF0) { - val = (cur[0]) & 0x0F; - val <<= 6; - val |= (cur[1]) & 0x3F; - val <<= 6; - val |= (cur[2]) & 0x3F; - l = 3; - } else if (*cur < 0xF8) { - val = (cur[0]) & 0x07; - val <<= 6; - val |= (cur[1]) & 0x3F; - val <<= 6; - val |= (cur[2]) & 0x3F; - val <<= 6; - val |= (cur[3]) & 0x3F; - l = 4; - } - if ((l == 1) || (!IS_CHAR(val))) { - xmlEntitiesErr(XML_ERR_INVALID_CHAR, - "xmlEncodeEntities: char out of range\n"); - snprintf(buf, sizeof(buf), "&#%d;", *cur); - buf[sizeof(buf) - 1] = 0; - ptr = buf; - while (*ptr != 0) *out++ = *ptr++; - cur++; - continue; + char buf[13], *ptr; + int val, l; + + l = 4; + val = xmlGetUTF8Char(cur, &l); + if (val < 0) { + val = 0xFFFD; + cur++; + } else { + if (!IS_CHAR(val)) + val = 0xFFFD; + cur += l; } /* * We could do multiple things here. Just save as a char ref @@ -762,7 +676,6 @@ xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) { buf[sizeof(buf) - 1] = 0; ptr = buf; while (*ptr != 0) *out++ = *ptr++; - cur += l; continue; } } else if (IS_BYTE_CHAR(*cur)) { @@ -779,7 +692,6 @@ xmlEncodeEntitiesInternal(xmlDocPtr doc, const xmlChar *input, int attr) { return(buffer); mem_error: - xmlEntitiesErrMemory("xmlEncodeEntities: realloc failed"); xmlFree(buffer); return(NULL); } @@ -840,10 +752,8 @@ xmlEncodeSpecialChars(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlChar *input) */ buffer_size = 1000; buffer = (xmlChar *) xmlMalloc(buffer_size); - if (buffer == NULL) { - xmlEntitiesErrMemory("xmlEncodeSpecialChars: malloc failed"); + if (buffer == NULL) return(NULL); - } out = buffer; while (*cur != '\0') { @@ -899,7 +809,6 @@ xmlEncodeSpecialChars(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlChar *input) return(buffer); mem_error: - xmlEntitiesErrMemory("xmlEncodeSpecialChars: realloc failed"); xmlFree(buffer); return(NULL); } @@ -956,27 +865,47 @@ xmlCopyEntity(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { xmlEntityPtr cur; cur = (xmlEntityPtr) xmlMalloc(sizeof(xmlEntity)); - if (cur == NULL) { - xmlEntitiesErrMemory("xmlCopyEntity:: malloc failed"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlEntity)); cur->type = XML_ENTITY_DECL; cur->etype = ent->etype; - if (ent->name != NULL) + if (ent->name != NULL) { cur->name = xmlStrdup(ent->name); - if (ent->ExternalID != NULL) + if (cur->name == NULL) + goto error; + } + if (ent->ExternalID != NULL) { cur->ExternalID = xmlStrdup(ent->ExternalID); - if (ent->SystemID != NULL) + if (cur->ExternalID == NULL) + goto error; + } + if (ent->SystemID != NULL) { cur->SystemID = xmlStrdup(ent->SystemID); - if (ent->content != NULL) + if (cur->SystemID == NULL) + goto error; + } + if (ent->content != NULL) { cur->content = xmlStrdup(ent->content); - if (ent->orig != NULL) + if (cur->content == NULL) + goto error; + } + if (ent->orig != NULL) { cur->orig = xmlStrdup(ent->orig); - if (ent->URI != NULL) + if (cur->orig == NULL) + goto error; + } + if (ent->URI != NULL) { cur->URI = xmlStrdup(ent->URI); + if (cur->URI == NULL) + goto error; + } return(cur); + +error: + xmlFreeEntity(cur); + return(NULL); } /** @@ -989,52 +918,12 @@ xmlCopyEntity(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { */ xmlEntitiesTablePtr xmlCopyEntitiesTable(xmlEntitiesTablePtr table) { - return(xmlHashCopy(table, xmlCopyEntity)); + return(xmlHashCopySafe(table, xmlCopyEntity, xmlFreeEntityWrapper)); } #endif /* LIBXML_TREE_ENABLED */ #ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlDumpEntityContent: - * @buf: An XML buffer. - * @content: The entity content. - * - * This will dump the quoted string value, taking care of the special - * treatment required by % - */ -static void -xmlDumpEntityContent(xmlBufferPtr buf, const xmlChar *content) { - if (xmlStrchr(content, '%')) { - const xmlChar * base, *cur; - - xmlBufferCCat(buf, "\""); - base = cur = content; - while (*cur != 0) { - if (*cur == '"') { - if (base != cur) - xmlBufferAdd(buf, base, cur - base); - xmlBufferAdd(buf, BAD_CAST """, 6); - cur++; - base = cur; - } else if (*cur == '%') { - if (base != cur) - xmlBufferAdd(buf, base, cur - base); - xmlBufferAdd(buf, BAD_CAST "%", 6); - cur++; - base = cur; - } else { - cur++; - } - } - if (base != cur) - xmlBufferAdd(buf, base, cur - base); - xmlBufferCCat(buf, "\""); - } else { - xmlBufferWriteQuotedString(buf, content); - } -} - /** * xmlDumpEntityDecl: * @buf: An XML buffer. @@ -1044,81 +933,15 @@ xmlDumpEntityContent(xmlBufferPtr buf, const xmlChar *content) { */ void xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) { - if ((buf == NULL) || (ent == NULL)) return; - switch (ent->etype) { - case XML_INTERNAL_GENERAL_ENTITY: - xmlBufferWriteChar(buf, "name); - xmlBufferWriteChar(buf, " "); - if (ent->orig != NULL) - xmlBufferWriteQuotedString(buf, ent->orig); - else - xmlDumpEntityContent(buf, ent->content); - xmlBufferWriteChar(buf, ">\n"); - break; - case XML_EXTERNAL_GENERAL_PARSED_ENTITY: - xmlBufferWriteChar(buf, "name); - if (ent->ExternalID != NULL) { - xmlBufferWriteChar(buf, " PUBLIC "); - xmlBufferWriteQuotedString(buf, ent->ExternalID); - xmlBufferWriteChar(buf, " "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } else { - xmlBufferWriteChar(buf, " SYSTEM "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } - xmlBufferWriteChar(buf, ">\n"); - break; - case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: - xmlBufferWriteChar(buf, "name); - if (ent->ExternalID != NULL) { - xmlBufferWriteChar(buf, " PUBLIC "); - xmlBufferWriteQuotedString(buf, ent->ExternalID); - xmlBufferWriteChar(buf, " "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } else { - xmlBufferWriteChar(buf, " SYSTEM "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } - if (ent->content != NULL) { /* Should be true ! */ - xmlBufferWriteChar(buf, " NDATA "); - if (ent->orig != NULL) - xmlBufferWriteCHAR(buf, ent->orig); - else - xmlBufferWriteCHAR(buf, ent->content); - } - xmlBufferWriteChar(buf, ">\n"); - break; - case XML_INTERNAL_PARAMETER_ENTITY: - xmlBufferWriteChar(buf, "name); - xmlBufferWriteChar(buf, " "); - if (ent->orig == NULL) - xmlDumpEntityContent(buf, ent->content); - else - xmlBufferWriteQuotedString(buf, ent->orig); - xmlBufferWriteChar(buf, ">\n"); - break; - case XML_EXTERNAL_PARAMETER_ENTITY: - xmlBufferWriteChar(buf, "name); - if (ent->ExternalID != NULL) { - xmlBufferWriteChar(buf, " PUBLIC "); - xmlBufferWriteQuotedString(buf, ent->ExternalID); - xmlBufferWriteChar(buf, " "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } else { - xmlBufferWriteChar(buf, " SYSTEM "); - xmlBufferWriteQuotedString(buf, ent->SystemID); - } - xmlBufferWriteChar(buf, ">\n"); - break; - default: - xmlEntitiesErr(XML_DTD_UNKNOWN_ENTITY, - "xmlDumpEntitiesDecl: internal: unknown type entity type"); - } + xmlSaveCtxtPtr save; + + if ((buf == NULL) || (ent == NULL)) + return; + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlSaveTree(save, (xmlNodePtr) ent); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } /** @@ -1129,9 +952,9 @@ xmlDumpEntityDecl(xmlBufferPtr buf, xmlEntityPtr ent) { * When using the hash table scan function, arguments need to be reversed */ static void -xmlDumpEntityDeclScan(void *ent, void *buf, +xmlDumpEntityDeclScan(void *ent, void *save, const xmlChar *name ATTRIBUTE_UNUSED) { - xmlDumpEntityDecl((xmlBufferPtr) buf, (xmlEntityPtr) ent); + xmlSaveTree(save, ent); } /** @@ -1143,6 +966,14 @@ xmlDumpEntityDeclScan(void *ent, void *buf, */ void xmlDumpEntitiesTable(xmlBufferPtr buf, xmlEntitiesTablePtr table) { - xmlHashScan(table, xmlDumpEntityDeclScan, buf); + xmlSaveCtxtPtr save; + + if ((buf == NULL) || (table == NULL)) + return; + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlHashScan(table, xmlDumpEntityDeclScan, save); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } #endif /* LIBXML_OUTPUT_ENABLED */ diff --git a/libraries/libxml2/error.c b/libraries/libxml2/error.c index c87cf2aa..b678a0ee 100644 --- a/libraries/libxml2/error.c +++ b/libraries/libxml2/error.c @@ -11,45 +11,169 @@ #include #include +#include #include #include #include #include "private/error.h" +#include "private/string.h" -#define XML_MAX_ERRORS 100 - -#define XML_GET_VAR_STR(msg, str) { \ - int size, prev_size = -1; \ - int chars; \ - char *larger; \ - va_list ap; \ - \ - str = (char *) xmlMalloc(150); \ - if (str != NULL) { \ - \ - size = 150; \ - \ - while (size < 64000) { \ - va_start(ap, msg); \ - chars = vsnprintf(str, size, msg, ap); \ - va_end(ap); \ - if ((chars > -1) && (chars < size)) { \ - if (prev_size == chars) { \ - break; \ - } else { \ - prev_size = chars; \ - } \ - } \ - if (chars > -1) \ - size += chars + 1; \ - else \ - size += 100; \ - if ((larger = (char *) xmlRealloc(str, size)) == NULL) {\ - break; \ - } \ - str = larger; \ - }} \ +/************************************************************************ + * * + * Error struct * + * * + ************************************************************************/ + +static int +xmlVSetError(xmlError *err, + void *ctxt, xmlNodePtr node, + int domain, int code, xmlErrorLevel level, + const char *file, int line, + const char *str1, const char *str2, const char *str3, + int int1, int col, + const char *fmt, va_list ap) +{ + char *message = NULL; + char *fileCopy = NULL; + char *str1Copy = NULL; + char *str2Copy = NULL; + char *str3Copy = NULL; + + if (code == XML_ERR_OK) { + xmlResetError(err); + return(0); + } + + /* + * Formatting the message + */ + if (fmt == NULL) { + message = xmlMemStrdup("No error message provided"); + } else { + xmlChar *tmp; + int res; + + res = xmlStrVASPrintf(&tmp, MAX_ERR_MSG_SIZE, fmt, ap); + if (res < 0) + goto err_memory; + message = (char *) tmp; + } + if (message == NULL) + goto err_memory; + + if (file != NULL) { + fileCopy = (char *) xmlStrdup((const xmlChar *) file); + if (fileCopy == NULL) + goto err_memory; + } + if (str1 != NULL) { + str1Copy = (char *) xmlStrdup((const xmlChar *) str1); + if (str1Copy == NULL) + goto err_memory; + } + if (str2 != NULL) { + str2Copy = (char *) xmlStrdup((const xmlChar *) str2); + if (str2Copy == NULL) + goto err_memory; + } + if (str3 != NULL) { + str3Copy = (char *) xmlStrdup((const xmlChar *) str3); + if (str3Copy == NULL) + goto err_memory; + } + + xmlResetError(err); + + err->domain = domain; + err->code = code; + err->message = message; + err->level = level; + err->file = fileCopy; + err->line = line; + err->str1 = str1Copy; + err->str2 = str2Copy; + err->str3 = str3Copy; + err->int1 = int1; + err->int2 = col; + err->node = node; + err->ctxt = ctxt; + + return(0); + +err_memory: + xmlFree(message); + xmlFree(fileCopy); + xmlFree(str1Copy); + xmlFree(str2Copy); + xmlFree(str3Copy); + return(-1); +} + +static int LIBXML_ATTR_FORMAT(14,15) +xmlSetError(xmlError *err, + void *ctxt, xmlNodePtr node, + int domain, int code, xmlErrorLevel level, + const char *file, int line, + const char *str1, const char *str2, const char *str3, + int int1, int col, + const char *fmt, ...) +{ + va_list ap; + int res; + + va_start(ap, fmt); + res = xmlVSetError(err, ctxt, node, domain, code, level, file, line, + str1, str2, str3, int1, col, fmt, ap); + va_end(ap); + + return(res); +} + +static int +xmlVUpdateError(xmlError *err, + void *ctxt, xmlNodePtr node, + int domain, int code, xmlErrorLevel level, + const char *file, int line, + const char *str1, const char *str2, const char *str3, + int int1, int col, + const char *fmt, va_list ap) +{ + int res; + + /* + * Find first element parent. + */ + if (node != NULL) { + int i; + + for (i = 0; i < 10; i++) { + if ((node->type == XML_ELEMENT_NODE) || + (node->parent == NULL)) + break; + node = node->parent; + } + } + + /* + * Get file and line from node. + */ + if (node != NULL) { + if ((file == NULL) && (node->doc != NULL)) + file = (const char *) node->doc->URL; + + if (line == 0) { + if (node->type == XML_ELEMENT_NODE) + line = node->line; + if ((line == 0) || (line == 65535)) + line = xmlGetLineNo(node); + } + } + + res = xmlVSetError(err, ctxt, node, domain, code, level, file, line, + str1, str2, str3, int1, col, fmt, ap); + + return(res); } /************************************************************************ @@ -101,14 +225,21 @@ initGenericErrorDefaultFunc(xmlGenericErrorFunc * handler) * @ctx: the new error handling context * @handler: the new handler function * - * Function to reset the handler and the error context for out of - * context error messages. - * This simply means that @handler will be called for subsequent - * error messages while not parsing nor validating. And @ctx will - * be passed as first argument to @handler - * One can simply force messages to be emitted to another FILE * than - * stderr by setting @ctx to this file handle and @handler to NULL. - * For multi-threaded applications, this must be set separately for each thread. + * DEPRECATED: See xmlSetStructuredErrorFunc for alternatives. + * + * Set the global "generic" handler and context for error messages. + * The generic error handler will only receive fragments of error + * messages which should be concatenated or printed to a stream. + * + * If handler is NULL, use the built-in default handler which prints + * to stderr. + * + * Since this is a global setting, it's a good idea to reset the + * error handler to its default value after collecting the errors + * you're interested in. + * + * For multi-threaded applications, this must be set separately for + * each thread. */ void xmlSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) { @@ -124,12 +255,30 @@ xmlSetGenericErrorFunc(void *ctx, xmlGenericErrorFunc handler) { * @ctx: the new error handling context * @handler: the new handler function * - * Function to reset the handler and the error context for out of - * context structured error messages. - * This simply means that @handler will be called for subsequent - * error messages while not parsing nor validating. And @ctx will - * be passed as first argument to @handler - * For multi-threaded applications, this must be set separately for each thread. + * DEPRECATED: Use a per-context error handler. + * + * It's recommended to use the per-context error handlers instead: + * + * - xmlCtxtSetErrorHandler (since 2.13.0) + * - xmlTextReaderSetStructuredErrorHandler + * - xmlXPathSetErrorHandler (since 2.13.0) + * - xmlXIncludeSetErrorHandler (since 2.13.0) + * - xmlSchemaSetParserStructuredErrors + * - xmlSchemaSetValidStructuredErrors + * - xmlRelaxNGSetParserStructuredErrors + * - xmlRelaxNGSetValidStructuredErrors + * + * Set the global "structured" handler and context for error messages. + * If handler is NULL, the error handler is deactivated. + * + * The structured error handler takes precedence over "generic" + * handlers, even per-context generic handlers. + * + * Since this is a global setting, it's a good idea to deactivate the + * error handler after collecting the errors you're interested in. + * + * For multi-threaded applications, this must be set separately for + * each thread. */ void xmlSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler) { @@ -147,6 +296,8 @@ xmlSetStructuredErrorFunc(void *ctx, xmlStructuredErrorFunc handler) { * xmlParserPrintFileInfo: * @input: an xmlParserInputPtr input * + * DEPRECATED: Use xmlFormatError. + * * Displays the associated file and line information for the current input */ @@ -238,6 +389,8 @@ xmlParserPrintFileContextInternal(xmlParserInputPtr input , * xmlParserPrintFileContext: * @input: an xmlParserInputPtr input * + * DEPRECATED: Use xmlFormatError. + * * Displays current context within the input content for error tracking */ void @@ -247,35 +400,37 @@ xmlParserPrintFileContext(xmlParserInputPtr input) { } /** - * xmlReportError: - * @err: the error - * @ctx: the parser context or NULL - * @str: the formatted error message + * xmlFormatError: + * @err: the error + * @channel: callback + * @data: user data for callback + * + * Report a formatted error to a printf-like callback. * - * Report an error with its context, replace the 4 old error/warning - * routines. + * This can result in a verbose multi-line report including additional + * information from the parser context. + * + * Available since 2.13.0. */ -static void -xmlReportError(xmlErrorPtr err, xmlParserCtxtPtr ctxt, const char *str, - xmlGenericErrorFunc channel, void *data) +void +xmlFormatError(const xmlError *err, xmlGenericErrorFunc channel, void *data) { - char *file = NULL; - int line = 0; - int code = -1; + const char *message; + const char *file; + int line; + int code; int domain; const xmlChar *name = NULL; xmlNodePtr node; xmlErrorLevel level; + xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlParserInputPtr cur = NULL; - if (err == NULL) + if ((err == NULL) || (channel == NULL)) return; - if (channel == NULL) { - channel = xmlGenericError; - data = xmlGenericErrorContext; - } + message = err->message; file = err->file; line = err->line; code = err->code; @@ -286,25 +441,30 @@ xmlReportError(xmlErrorPtr err, xmlParserCtxtPtr ctxt, const char *str, if (code == XML_ERR_OK) return; - if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) + if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || + (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || + (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { + ctxt = err->ctxt; + } + + if ((node != NULL) && (node->type == XML_ELEMENT_NODE) && + (domain != XML_FROM_SCHEMASV)) name = node->name; /* * Maintain the compatibility with the legacy error handling */ - if (ctxt != NULL) { + if ((ctxt != NULL) && (ctxt->input != NULL)) { input = ctxt->input; - if ((input != NULL) && (input->filename == NULL) && + if ((input->filename == NULL) && (ctxt->inputNr > 1)) { cur = input; input = ctxt->inputTab[ctxt->inputNr - 2]; } - if (input != NULL) { - if (input->filename) - channel(data, "%s:%d: ", input->filename, input->line); - else if ((line != 0) && (domain == XML_FROM_PARSER)) - channel(data, "Entity: line %d: ", input->line); - } + if (input->filename) + channel(data, "%s:%d: ", input->filename, input->line); + else if ((line != 0) && (domain == XML_FROM_PARSER)) + channel(data, "Entity: line %d: ", input->line); } else { if (file != NULL) channel(data, "%s:%d: ", file, line); @@ -405,19 +565,35 @@ xmlReportError(xmlErrorPtr err, xmlParserCtxtPtr ctxt, const char *str, channel(data, "error : "); break; } - if (str != NULL) { + if (message != NULL) { int len; - len = xmlStrlen((const xmlChar *)str); - if ((len > 0) && (str[len - 1] != '\n')) - channel(data, "%s\n", str); + len = xmlStrlen((const xmlChar *) message); + if ((len > 0) && (message[len - 1] != '\n')) + channel(data, "%s\n", message); else - channel(data, "%s", str); + channel(data, "%s", message); } else { - channel(data, "%s\n", "out of memory error"); + channel(data, "%s\n", "No error message provided"); } if (ctxt != NULL) { + if ((input != NULL) && + ((input->buf == NULL) || (input->buf->encoder == NULL)) && + (code == XML_ERR_INVALID_ENCODING) && + (input->cur < input->end)) { + int i; + + channel(data, "Bytes:"); + for (i = 0; i < 4; i++) { + if (input->cur + i >= input->end) + break; + channel(data, " 0x%02X", input->cur[i]); + } + channel(data, "\n"); + } + xmlParserPrintFileContextInternal(input, channel, data); + if (cur != NULL) { if (cur->filename) channel(data, "%s:%d: \n", cur->filename, cur->line); @@ -442,12 +618,53 @@ xmlReportError(xmlErrorPtr err, xmlParserCtxtPtr ctxt, const char *str, } /** - * __xmlRaiseError: + * xmlRaiseMemoryError: + * @schannel: the structured callback channel + * @channel: the old callback channel + * @data: the callback data + * @domain: the domain for the error + * @error: optional error struct to be filled + * + * Update the global and optional error structure, then forward the + * error to an error handler. + * + * This function doesn't make memory allocations which are likely + * to fail after an OOM error. + */ +void +xmlRaiseMemoryError(xmlStructuredErrorFunc schannel, xmlGenericErrorFunc channel, + void *data, int domain, xmlError *error) +{ + xmlError *lastError = &xmlLastError; + + xmlResetLastError(); + lastError->domain = domain; + lastError->code = XML_ERR_NO_MEMORY; + lastError->level = XML_ERR_FATAL; + + if (error != NULL) { + xmlResetError(error); + error->domain = domain; + error->code = XML_ERR_NO_MEMORY; + error->level = XML_ERR_FATAL; + } + + if (schannel != NULL) { + schannel(data, lastError); + } else if (xmlStructuredError != NULL) { + xmlStructuredError(xmlStructuredErrorContext, lastError); + } else if (channel != NULL) { + channel(data, "libxml2: out of memory\n"); + } +} + +/** + * xmlVRaiseError: * @schannel: the structured callback channel * @channel: the old callback channel * @data: the callback data * @ctx: the parser context or NULL - * @ctx: the parser context or NULL + * @node: the current node or NULL * @domain: the domain for the error * @code: the code for the error * @level: the xmlErrorLevel for the error @@ -459,258 +676,128 @@ xmlReportError(xmlErrorPtr err, xmlParserCtxtPtr ctxt, const char *str, * @int1: extra int info * @col: column number of the error or 0 if N/A * @msg: the message to display/transmit - * @...: extra parameters for the message display + * @ap: extra parameters for the message display * * Update the appropriate global or contextual error structure, * then forward the error message down the parser or generic * error callback handler + * + * Returns 0 on success, -1 if a memory allocation failed. */ -void -__xmlRaiseError(xmlStructuredErrorFunc schannel, - xmlGenericErrorFunc channel, void *data, void *ctx, - void *nod, int domain, int code, xmlErrorLevel level, - const char *file, int line, const char *str1, - const char *str2, const char *str3, int int1, int col, - const char *msg, ...) +int +xmlVRaiseError(xmlStructuredErrorFunc schannel, + xmlGenericErrorFunc channel, void *data, void *ctx, + xmlNode *node, int domain, int code, xmlErrorLevel level, + const char *file, int line, const char *str1, + const char *str2, const char *str3, int int1, int col, + const char *msg, va_list ap) { xmlParserCtxtPtr ctxt = NULL; - xmlNodePtr node = (xmlNodePtr) nod; - char *str = NULL; - xmlParserInputPtr input = NULL; - xmlErrorPtr to = &xmlLastError; - xmlNodePtr baseptr = NULL; + /* xmlLastError is a macro retrieving the per-thread global. */ + xmlErrorPtr lastError = &xmlLastError; + xmlErrorPtr to = lastError; if (code == XML_ERR_OK) - return; + return(0); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (code == XML_ERR_INTERNAL_ERROR) { + fprintf(stderr, "Unexpected error: %d\n", code); + abort(); + } +#endif if ((xmlGetWarningsDefaultValue == 0) && (level == XML_ERR_WARNING)) - return; + return(0); + if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { ctxt = (xmlParserCtxtPtr) ctx; - if (ctxt != NULL) { - if (level == XML_ERR_WARNING) { - if (ctxt->nbWarnings >= XML_MAX_ERRORS) - return; - ctxt->nbWarnings += 1; - } else { - if (ctxt->nbErrors >= XML_MAX_ERRORS) - return; - ctxt->nbErrors += 1; - } - - if ((schannel == NULL) && (ctxt->sax != NULL) && - (ctxt->sax->initialized == XML_SAX2_MAGIC) && - (ctxt->sax->serror != NULL)) { - schannel = ctxt->sax->serror; - data = ctxt->userData; - } - } - } - /* - * Check if structured error handler set - */ - if (schannel == NULL) { - schannel = xmlStructuredError; - /* - * if user has defined handler, change data ptr to user's choice - */ - if (schannel != NULL) - data = xmlStructuredErrorContext; - } - /* - * Formatting the message - */ - if (msg == NULL) { - str = (char *) xmlStrdup(BAD_CAST "No error message provided"); - } else { - XML_GET_VAR_STR(msg, str); + if (ctxt != NULL) + to = &ctxt->lastError; } - /* - * specific processing if a parser context is provided - */ - if (ctxt != NULL) { - if (file == NULL) { - input = ctxt->input; - if ((input != NULL) && (input->filename == NULL) && - (ctxt->inputNr > 1)) { - input = ctxt->inputTab[ctxt->inputNr - 2]; - } - if (input != NULL) { - file = input->filename; - line = input->line; - col = input->col; - } - } - to = &ctxt->lastError; - } else if ((node != NULL) && (file == NULL)) { - int i; - - if ((node->doc != NULL) && (node->doc->URL != NULL)) { - baseptr = node; -/* file = (const char *) node->doc->URL; */ - } - for (i = 0; - ((i < 10) && (node != NULL) && (node->type != XML_ELEMENT_NODE)); - i++) - node = node->parent; - if ((baseptr == NULL) && (node != NULL) && - (node->doc != NULL) && (node->doc->URL != NULL)) - baseptr = node; - - if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) - line = node->line; - if ((line == 0) || (line == 65535)) - line = xmlGetLineNo(node); - } + if (xmlVUpdateError(to, ctxt, node, domain, code, level, file, line, + str1, str2, str3, int1, col, msg, ap)) + return(-1); - /* - * Save the information about the error - */ - xmlResetError(to); - to->domain = domain; - to->code = code; - to->message = str; - to->level = level; - if (file != NULL) - to->file = (char *) xmlStrdup((const xmlChar *) file); - else if (baseptr != NULL) { -#ifdef LIBXML_XINCLUDE_ENABLED - /* - * We check if the error is within an XInclude section and, - * if so, attempt to print out the href of the XInclude instead - * of the usual "base" (doc->URL) for the node (bug 152623). - */ - xmlNodePtr prev = baseptr; - char *href = NULL; - int inclcount = 0; - while (prev != NULL) { - if (prev->prev == NULL) - prev = prev->parent; - else { - prev = prev->prev; - if (prev->type == XML_XINCLUDE_START) { - if (inclcount > 0) { - --inclcount; - } else { - href = (char *) xmlGetProp(prev, BAD_CAST "href"); - if (href != NULL) - break; - } - } else if (prev->type == XML_XINCLUDE_END) - inclcount++; - } - } - if (href != NULL) - to->file = href; - else -#endif - to->file = (char *) xmlStrdup(baseptr->doc->URL); - if ((to->file == NULL) && (node != NULL) && (node->doc != NULL)) { - to->file = (char *) xmlStrdup(node->doc->URL); - } + if (to != lastError) { + if (xmlCopyError(to, lastError) < 0) + return(-1); } - to->line = line; - if (str1 != NULL) - to->str1 = (char *) xmlStrdup((const xmlChar *) str1); - if (str2 != NULL) - to->str2 = (char *) xmlStrdup((const xmlChar *) str2); - if (str3 != NULL) - to->str3 = (char *) xmlStrdup((const xmlChar *) str3); - to->int1 = int1; - to->int2 = col; - to->node = node; - to->ctxt = ctx; - - if (to != &xmlLastError) - xmlCopyError(to,&xmlLastError); if (schannel != NULL) { schannel(data, to); - return; - } - - /* - * Find the callback channel if channel param is NULL - */ - if ((ctxt != NULL) && (channel == NULL) && - (xmlStructuredError == NULL) && (ctxt->sax != NULL)) { - if (level == XML_ERR_WARNING) - channel = ctxt->sax->warning; + } else if (xmlStructuredError != NULL) { + xmlStructuredError(xmlStructuredErrorContext, to); + } else if (channel != NULL) { + /* Don't invoke legacy error handlers */ + if ((channel == xmlGenericErrorDefaultFunc) || + (channel == xmlParserError) || + (channel == xmlParserWarning) || + (channel == xmlParserValidityError) || + (channel == xmlParserValidityWarning)) + xmlFormatError(to, xmlGenericError, xmlGenericErrorContext); else - channel = ctxt->sax->error; - data = ctxt->userData; - } else if (channel == NULL) { - channel = xmlGenericError; - if (ctxt != NULL) { - data = ctxt; - } else { - data = xmlGenericErrorContext; - } + channel(data, "%s", to->message); } - if (channel == NULL) - return; - if ((channel == xmlParserError) || - (channel == xmlParserWarning) || - (channel == xmlParserValidityError) || - (channel == xmlParserValidityWarning)) - xmlReportError(to, ctxt, str, NULL, NULL); - else if (((void(*)(void)) channel == (void(*)(void)) fprintf) || - (channel == xmlGenericErrorDefaultFunc)) - xmlReportError(to, ctxt, str, channel, data); - else - channel(data, "%s", str); + return(0); } /** - * __xmlSimpleError: - * @domain: where the error comes from - * @code: the error code - * @node: the context node - * @extra: extra information - * - * Handle an out of memory condition - */ -void -__xmlSimpleError(int domain, int code, xmlNodePtr node, - const char *msg, const char *extra) -{ - - if (code == XML_ERR_NO_MEMORY) { - if (extra) - __xmlRaiseError(NULL, NULL, NULL, NULL, node, domain, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, extra, - NULL, NULL, 0, 0, - "Memory allocation failed : %s\n", extra); - else - __xmlRaiseError(NULL, NULL, NULL, NULL, node, domain, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, NULL, - NULL, NULL, 0, 0, "Memory allocation failed\n"); - } else { - __xmlRaiseError(NULL, NULL, NULL, NULL, node, domain, - code, XML_ERR_ERROR, NULL, 0, extra, - NULL, NULL, 0, 0, msg, extra); - } -} -/** - * xmlParserError: - * @ctx: an XML parser context + * __xmlRaiseError: + * @schannel: the structured callback channel + * @channel: the old callback channel + * @data: the callback data + * @ctx: the parser context or NULL + * @nod: the node or NULL + * @domain: the domain for the error + * @code: the code for the error + * @level: the xmlErrorLevel for the error + * @file: the file source of the error (or NULL) + * @line: the line of the error or 0 if N/A + * @str1: extra string info + * @str2: extra string info + * @str3: extra string info + * @int1: extra int info + * @col: column number of the error or 0 if N/A * @msg: the message to display/transmit * @...: extra parameters for the message display * - * Display and format an error messages, gives file, line, position and - * extra parameters. + * Update the appropriate global or contextual error structure, + * then forward the error message down the parser or generic + * error callback handler + * + * Returns 0 on success, -1 if a memory allocation failed. */ -void -xmlParserError(void *ctx, const char *msg, ...) +int +__xmlRaiseError(xmlStructuredErrorFunc schannel, + xmlGenericErrorFunc channel, void *data, void *ctx, + xmlNode *node, int domain, int code, xmlErrorLevel level, + const char *file, int line, const char *str1, + const char *str2, const char *str3, int int1, int col, + const char *msg, ...) { + va_list ap; + int res; + + va_start(ap, msg); + res = xmlVRaiseError(schannel, channel, data, ctx, node, domain, code, + level, file, line, str1, str2, str3, int1, col, msg, + ap); + va_end(ap); + + return(res); +} + +static void +xmlVFormatLegacyError(void *ctx, const char *level, + const char *fmt, va_list ap) { xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlParserInputPtr input = NULL; xmlParserInputPtr cur = NULL; - char * str; + xmlChar *str = NULL; if (ctxt != NULL) { input = ctxt->input; @@ -722,11 +809,13 @@ xmlParserError(void *ctx, const char *msg, ...) xmlParserPrintFileInfo(input); } - xmlGenericError(xmlGenericErrorContext, "error: "); - XML_GET_VAR_STR(msg, str); - xmlGenericError(xmlGenericErrorContext, "%s", str); - if (str != NULL) + xmlGenericError(xmlGenericErrorContext, "%s: ", level); + + xmlStrVASPrintf(&str, MAX_ERR_MSG_SIZE, fmt, ap); + if (str != NULL) { + xmlGenericError(xmlGenericErrorContext, "%s", (char *) str); xmlFree(str); + } if (ctxt != NULL) { xmlParserPrintFileContext(input); @@ -739,54 +828,43 @@ xmlParserError(void *ctx, const char *msg, ...) } /** - * xmlParserWarning: + * xmlParserError: * @ctx: an XML parser context * @msg: the message to display/transmit * @...: extra parameters for the message display * - * Display and format a warning messages, gives file, line, position and + * Display and format an error messages, gives file, line, position and * extra parameters. */ void -xmlParserWarning(void *ctx, const char *msg, ...) +xmlParserError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { - xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - xmlParserInputPtr input = NULL; - xmlParserInputPtr cur = NULL; - char * str; + va_list ap; - if (ctxt != NULL) { - input = ctxt->input; - if ((input != NULL) && (input->filename == NULL) && - (ctxt->inputNr > 1)) { - cur = input; - input = ctxt->inputTab[ctxt->inputNr - 2]; - } - xmlParserPrintFileInfo(input); - } + va_start(ap, msg); + xmlVFormatLegacyError(ctx, "error", msg, ap); + va_end(ap); +} - xmlGenericError(xmlGenericErrorContext, "warning: "); - XML_GET_VAR_STR(msg, str); - xmlGenericError(xmlGenericErrorContext, "%s", str); - if (str != NULL) - xmlFree(str); +/** + * xmlParserWarning: + * @ctx: an XML parser context + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Display and format a warning messages, gives file, line, position and + * extra parameters. + */ +void +xmlParserWarning(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) +{ + va_list ap; - if (ctxt != NULL) { - xmlParserPrintFileContext(input); - if (cur != NULL) { - xmlParserPrintFileInfo(cur); - xmlGenericError(xmlGenericErrorContext, "\n"); - xmlParserPrintFileContext(cur); - } - } + va_start(ap, msg); + xmlVFormatLegacyError(ctx, "warning", msg, ap); + va_end(ap); } -/************************************************************************ - * * - * Handling of validation errors * - * * - ************************************************************************/ - /** * xmlParserValidityError: * @ctx: an XML parser context @@ -797,38 +875,13 @@ xmlParserWarning(void *ctx, const char *msg, ...) * line, position and extra parameters. */ void -xmlParserValidityError(void *ctx, const char *msg, ...) +xmlParserValidityError(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { - xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - xmlParserInputPtr input = NULL; - char * str; - int len = xmlStrlen((const xmlChar *) msg); - static int had_info = 0; - - if ((len > 1) && (msg[len - 2] != ':')) { - if (ctxt != NULL) { - input = ctxt->input; - if ((input->filename == NULL) && (ctxt->inputNr > 1)) - input = ctxt->inputTab[ctxt->inputNr - 2]; - - if (had_info == 0) { - xmlParserPrintFileInfo(input); - } - } - xmlGenericError(xmlGenericErrorContext, "validity error: "); - had_info = 0; - } else { - had_info = 1; - } + va_list ap; - XML_GET_VAR_STR(msg, str); - xmlGenericError(xmlGenericErrorContext, "%s", str); - if (str != NULL) - xmlFree(str); - - if ((ctxt != NULL) && (input != NULL)) { - xmlParserPrintFileContext(input); - } + va_start(ap, msg); + xmlVFormatLegacyError(ctx, "validity error", msg, ap); + va_end(ap); } /** @@ -841,30 +894,13 @@ xmlParserValidityError(void *ctx, const char *msg, ...) * position and extra parameters. */ void -xmlParserValidityWarning(void *ctx, const char *msg, ...) +xmlParserValidityWarning(void *ctx, const char *msg ATTRIBUTE_UNUSED, ...) { - xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - xmlParserInputPtr input = NULL; - char * str; - int len = xmlStrlen((const xmlChar *) msg); - - if ((ctxt != NULL) && (len != 0) && (msg[len - 1] != ':')) { - input = ctxt->input; - if ((input->filename == NULL) && (ctxt->inputNr > 1)) - input = ctxt->inputTab[ctxt->inputNr - 2]; + va_list ap; - xmlParserPrintFileInfo(input); - } - - xmlGenericError(xmlGenericErrorContext, "validity warning: "); - XML_GET_VAR_STR(msg, str); - xmlGenericError(xmlGenericErrorContext, "%s", str); - if (str != NULL) - xmlFree(str); - - if (ctxt != NULL) { - xmlParserPrintFileContext(input); - } + va_start(ap, msg); + xmlVFormatLegacyError(ctx, "validity warning", msg, ap); + va_end(ap); } @@ -982,42 +1018,351 @@ xmlCtxtResetLastError(void *ctx) */ int xmlCopyError(const xmlError *from, xmlErrorPtr to) { - char *message, *file, *str1, *str2, *str3; + const char *fmt = NULL; if ((from == NULL) || (to == NULL)) return(-1); - message = (char *) xmlStrdup((xmlChar *) from->message); - file = (char *) xmlStrdup ((xmlChar *) from->file); - str1 = (char *) xmlStrdup ((xmlChar *) from->str1); - str2 = (char *) xmlStrdup ((xmlChar *) from->str2); - str3 = (char *) xmlStrdup ((xmlChar *) from->str3); - - if (to->message != NULL) - xmlFree(to->message); - if (to->file != NULL) - xmlFree(to->file); - if (to->str1 != NULL) - xmlFree(to->str1); - if (to->str2 != NULL) - xmlFree(to->str2); - if (to->str3 != NULL) - xmlFree(to->str3); - to->domain = from->domain; - to->code = from->code; - to->level = from->level; - to->line = from->line; - to->node = from->node; - to->int1 = from->int1; - to->int2 = from->int2; - to->node = from->node; - to->ctxt = from->ctxt; - to->message = message; - to->file = file; - to->str1 = str1; - to->str2 = str2; - to->str3 = str3; - - return 0; + if (from->message != NULL) + fmt = "%s"; + + return(xmlSetError(to, from->ctxt, from->node, + from->domain, from->code, from->level, + from->file, from->line, + from->str1, from->str2, from->str3, + from->int1, from->int2, + fmt, from->message)); } +/** + * xmlErrString: + * @code: an xmlParserErrors code + * + * Returns an error message for a code. + */ +const char * +xmlErrString(xmlParserErrors code) { + const char *errmsg; + + switch (code) { + case XML_ERR_INVALID_HEX_CHARREF: + errmsg = "CharRef: invalid hexadecimal value"; + break; + case XML_ERR_INVALID_DEC_CHARREF: + errmsg = "CharRef: invalid decimal value"; + break; + case XML_ERR_INVALID_CHARREF: + errmsg = "CharRef: invalid value"; + break; + case XML_ERR_INTERNAL_ERROR: + errmsg = "internal error"; + break; + case XML_ERR_PEREF_AT_EOF: + errmsg = "PEReference at end of document"; + break; + case XML_ERR_PEREF_IN_PROLOG: + errmsg = "PEReference in prolog"; + break; + case XML_ERR_PEREF_IN_EPILOG: + errmsg = "PEReference in epilog"; + break; + case XML_ERR_PEREF_NO_NAME: + errmsg = "PEReference: no name"; + break; + case XML_ERR_PEREF_SEMICOL_MISSING: + errmsg = "PEReference: expecting ';'"; + break; + case XML_ERR_ENTITY_LOOP: + errmsg = "Detected an entity reference loop"; + break; + case XML_ERR_ENTITY_NOT_STARTED: + errmsg = "EntityValue: \" or ' expected"; + break; + case XML_ERR_ENTITY_PE_INTERNAL: + errmsg = "PEReferences forbidden in internal subset"; + break; + case XML_ERR_ENTITY_NOT_FINISHED: + errmsg = "EntityValue: \" or ' expected"; + break; + case XML_ERR_ATTRIBUTE_NOT_STARTED: + errmsg = "AttValue: \" or ' expected"; + break; + case XML_ERR_LT_IN_ATTRIBUTE: + errmsg = "Unescaped '<' not allowed in attributes values"; + break; + case XML_ERR_LITERAL_NOT_STARTED: + errmsg = "SystemLiteral \" or ' expected"; + break; + case XML_ERR_LITERAL_NOT_FINISHED: + errmsg = "Unfinished System or Public ID \" or ' expected"; + break; + case XML_ERR_MISPLACED_CDATA_END: + errmsg = "Sequence ']]>' not allowed in content"; + break; + case XML_ERR_URI_REQUIRED: + errmsg = "SYSTEM or PUBLIC, the URI is missing"; + break; + case XML_ERR_PUBID_REQUIRED: + errmsg = "PUBLIC, the Public Identifier is missing"; + break; + case XML_ERR_HYPHEN_IN_COMMENT: + errmsg = "Comment must not contain '--' (double-hyphen)"; + break; + case XML_ERR_PI_NOT_STARTED: + errmsg = "xmlParsePI : no target name"; + break; + case XML_ERR_RESERVED_XML_NAME: + errmsg = "Invalid PI name"; + break; + case XML_ERR_NOTATION_NOT_STARTED: + errmsg = "NOTATION: Name expected here"; + break; + case XML_ERR_NOTATION_NOT_FINISHED: + errmsg = "'>' required to close NOTATION declaration"; + break; + case XML_ERR_VALUE_REQUIRED: + errmsg = "Entity value required"; + break; + case XML_ERR_URI_FRAGMENT: + errmsg = "Fragment not allowed"; + break; + case XML_ERR_ATTLIST_NOT_STARTED: + errmsg = "'(' required to start ATTLIST enumeration"; + break; + case XML_ERR_NMTOKEN_REQUIRED: + errmsg = "NmToken expected in ATTLIST enumeration"; + break; + case XML_ERR_ATTLIST_NOT_FINISHED: + errmsg = "')' required to finish ATTLIST enumeration"; + break; + case XML_ERR_MIXED_NOT_STARTED: + errmsg = "MixedContentDecl : '|' or ')*' expected"; + break; + case XML_ERR_PCDATA_REQUIRED: + errmsg = "MixedContentDecl : '#PCDATA' expected"; + break; + case XML_ERR_ELEMCONTENT_NOT_STARTED: + errmsg = "ContentDecl : Name or '(' expected"; + break; + case XML_ERR_ELEMCONTENT_NOT_FINISHED: + errmsg = "ContentDecl : ',' '|' or ')' expected"; + break; + case XML_ERR_PEREF_IN_INT_SUBSET: + errmsg = + "PEReference: forbidden within markup decl in internal subset"; + break; + case XML_ERR_GT_REQUIRED: + errmsg = "expected '>'"; + break; + case XML_ERR_CONDSEC_INVALID: + errmsg = "XML conditional section '[' expected"; + break; + case XML_ERR_INT_SUBSET_NOT_FINISHED: + errmsg = "Content error in the internal subset"; + break; + case XML_ERR_EXT_SUBSET_NOT_FINISHED: + errmsg = "Content error in the external subset"; + break; + case XML_ERR_CONDSEC_INVALID_KEYWORD: + errmsg = + "conditional section INCLUDE or IGNORE keyword expected"; + break; + case XML_ERR_CONDSEC_NOT_FINISHED: + errmsg = "XML conditional section not closed"; + break; + case XML_ERR_XMLDECL_NOT_STARTED: + errmsg = "Text declaration '' expected"; + break; + case XML_ERR_EXT_ENTITY_STANDALONE: + errmsg = "external parsed entities cannot be standalone"; + break; + case XML_ERR_ENTITYREF_SEMICOL_MISSING: + errmsg = "EntityRef: expecting ';'"; + break; + case XML_ERR_DOCTYPE_NOT_FINISHED: + errmsg = "DOCTYPE improperly terminated"; + break; + case XML_ERR_LTSLASH_REQUIRED: + errmsg = "EndTag: ' #include #include -#include #include #include #include #include #include +#include "private/dict.h" #include "private/error.h" #include "private/globals.h" #include "private/threads.h" @@ -76,10 +76,13 @@ struct _xmlGlobalState { void *waitHandle; #endif +#ifdef LIBXML_THREAD_ENABLED + unsigned localRngState[2]; +#endif + #define XML_OP XML_DECLARE_MEMBER XML_GLOBALS_ALLOC XML_GLOBALS_ERROR -XML_GLOBALS_HTML XML_GLOBALS_IO XML_GLOBALS_PARSER XML_GLOBALS_TREE @@ -164,17 +167,14 @@ xmlFreeGlobalState(void *state); * * ************************************************************************/ +#ifdef LIBXML_THREAD_ENABLED +static unsigned xmlMainThreadRngState[2]; +#endif + /* * Memory allocation routines */ -#if defined(DEBUG_MEMORY_LOCATION) -xmlFreeFunc xmlFree = (xmlFreeFunc) xmlMemFree; -xmlMallocFunc xmlMalloc = (xmlMallocFunc) xmlMemMalloc; -xmlMallocFunc xmlMallocAtomic = (xmlMallocFunc) xmlMemMalloc; -xmlReallocFunc xmlRealloc = (xmlReallocFunc) xmlMemRealloc; -xmlStrdupFunc xmlMemStrdup = (xmlStrdupFunc) xmlMemoryStrdup; -#else /** * xmlFree: * @mem: an already allocated block of memory @@ -233,7 +233,6 @@ xmlPosixStrdup(const char *cur) { * Returns the copy of the string or NULL in case of error */ xmlStrdupFunc xmlMemStrdup = xmlPosixStrdup; -#endif /* DEBUG_MEMORY_LOCATION */ /** * xmlBufferAllocScheme: @@ -264,7 +263,7 @@ static int xmlDefaultBufferSizeThrDef = BASE_BUFFER_SIZE; * * Global setting, DEPRECATED. */ -int oldXMLWDcompatibility = 0; /* DEPRECATED */ +const int oldXMLWDcompatibility = 0; /* DEPRECATED */ /** * xmlParserDebugEntities: * @@ -274,8 +273,7 @@ int oldXMLWDcompatibility = 0; /* DEPRECATED */ * while handling entities. * Disabled by default */ -int xmlParserDebugEntities = 0; -static int xmlParserDebugEntitiesThrDef = 0; +const int xmlParserDebugEntities = 0; /** * xmlDoValidityCheckingDefaultValue: * @@ -289,7 +287,7 @@ static int xmlDoValidityCheckingDefaultValueThrDef = 0; /** * xmlGetWarningsDefaultValue: * - * DEPRECATED: Don't use + * DEPRECATED: Use the modern options API with XML_PARSE_NOWARNING. * * Global setting, indicate that the DTD validation should provide warnings. * Activated by default. @@ -460,7 +458,7 @@ static int xmlSaveNoEmptyTagsThrDef = 0; * * Default SAX version1 handler for XML, builds the DOM tree */ -xmlSAXHandlerV1 xmlDefaultSAXHandler = { +const xmlSAXHandlerV1 xmlDefaultSAXHandler = { xmlSAX2InternalSubset, xmlSAX2IsStandalone, xmlSAX2HasInternalSubset, @@ -500,7 +498,7 @@ xmlSAXHandlerV1 xmlDefaultSAXHandler = { * The default SAX Locator * { getPublicId, getSystemId, getLineNumber, getColumnNumber} */ -xmlSAXLocator xmlDefaultSAXLocator = { +const xmlSAXLocator xmlDefaultSAXLocator = { xmlSAX2GetPublicId, xmlSAX2GetSystemId, xmlSAX2GetLineNumber, @@ -516,7 +514,7 @@ xmlSAXLocator xmlDefaultSAXLocator = { * * Default old SAX v1 handler for HTML, builds the DOM tree */ -xmlSAXHandlerV1 htmlDefaultSAXHandler = { +const xmlSAXHandlerV1 htmlDefaultSAXHandler = { xmlSAX2InternalSubset, NULL, NULL, @@ -596,6 +594,11 @@ void xmlInitGlobalsInternal(void) { #endif mainthread = GetCurrentThreadId(); #endif + +#ifdef LIBXML_THREAD_ENABLED + xmlMainThreadRngState[0] = xmlGlobalRandom(); + xmlMainThreadRngState[1] = xmlGlobalRandom(); +#endif } /** @@ -756,36 +759,21 @@ static void xmlInitGlobalState(xmlGlobalStatePtr gs) { xmlMutexLock(&xmlThrDefMutex); -#if defined(LIBXML_HTML_ENABLED) && defined(LIBXML_LEGACY_ENABLED) && defined(LIBXML_SAX1_ENABLED) - inithtmlDefaultSAXHandler(&gs->gs_htmlDefaultSAXHandler); +#ifdef LIBXML_THREAD_ENABLED + gs->localRngState[0] = xmlGlobalRandom(); + gs->localRngState[1] = xmlGlobalRandom(); #endif - gs->gs_oldXMLWDcompatibility = 0; gs->gs_xmlBufferAllocScheme = xmlBufferAllocSchemeThrDef; gs->gs_xmlDefaultBufferSize = xmlDefaultBufferSizeThrDef; -#if defined(LIBXML_SAX1_ENABLED) && defined(LIBXML_LEGACY_ENABLED) - initxmlDefaultSAXHandler(&gs->gs_xmlDefaultSAXHandler, 1); -#endif /* LIBXML_SAX1_ENABLED */ - gs->gs_xmlDefaultSAXLocator.getPublicId = xmlSAX2GetPublicId; - gs->gs_xmlDefaultSAXLocator.getSystemId = xmlSAX2GetSystemId; - gs->gs_xmlDefaultSAXLocator.getLineNumber = xmlSAX2GetLineNumber; - gs->gs_xmlDefaultSAXLocator.getColumnNumber = xmlSAX2GetColumnNumber; gs->gs_xmlDoValidityCheckingDefaultValue = xmlDoValidityCheckingDefaultValueThrDef; #ifdef LIBXML_THREAD_ALLOC_ENABLED -#ifdef DEBUG_MEMORY_LOCATION - gs->gs_xmlFree = xmlMemFree; - gs->gs_xmlMalloc = xmlMemMalloc; - gs->gs_xmlMallocAtomic = xmlMemMalloc; - gs->gs_xmlRealloc = xmlMemRealloc; - gs->gs_xmlMemStrdup = xmlMemoryStrdup; -#else gs->gs_xmlFree = free; gs->gs_xmlMalloc = malloc; gs->gs_xmlMallocAtomic = malloc; gs->gs_xmlRealloc = realloc; gs->gs_xmlMemStrdup = xmlPosixStrdup; -#endif #endif gs->gs_xmlGetWarningsDefaultValue = xmlGetWarningsDefaultValueThrDef; #ifdef LIBXML_OUTPUT_ENABLED @@ -796,7 +784,6 @@ xmlInitGlobalState(xmlGlobalStatePtr gs) { gs->gs_xmlKeepBlanksDefaultValue = xmlKeepBlanksDefaultValueThrDef; gs->gs_xmlLineNumbersDefaultValue = xmlLineNumbersDefaultValueThrDef; gs->gs_xmlLoadExtDtdDefaultValue = xmlLoadExtDtdDefaultValueThrDef; - gs->gs_xmlParserDebugEntities = xmlParserDebugEntitiesThrDef; gs->gs_xmlPedanticParserDefaultValue = xmlPedanticParserDefaultValueThrDef; gs->gs_xmlSubstituteEntitiesDefaultValue = xmlSubstituteEntitiesDefaultValueThrDef; @@ -904,12 +891,26 @@ xmlGetThreadLocalStorage(int allowFailure) { #define XML_OP XML_DEFINE_GLOBAL_WRAPPER XML_GLOBALS_ALLOC XML_GLOBALS_ERROR -XML_GLOBALS_HTML XML_GLOBALS_IO XML_GLOBALS_PARSER XML_GLOBALS_TREE #undef XML_OP +#ifdef LIBXML_THREAD_ENABLED +/** + * xmlGetLocalRngState: + * + * Returns the local RNG state. + */ +unsigned * +xmlGetLocalRngState(void) { + if (IS_MAIN_THREAD) + return(xmlMainThreadRngState); + else + return(xmlGetThreadLocalStorage(0)->localRngState); +} +#endif + /* For backward compatibility */ const char *const * @@ -917,6 +918,35 @@ __xmlParserVersion(void) { return &xmlParserVersion; } +const int * +__oldXMLWDcompatibility(void) { + return &oldXMLWDcompatibility; +} + +const int * +__xmlParserDebugEntities(void) { + return &xmlParserDebugEntities; +} + +const xmlSAXLocator * +__xmlDefaultSAXLocator(void) { + return &xmlDefaultSAXLocator; +} + +#ifdef LIBXML_SAX1_ENABLED +const xmlSAXHandlerV1 * +__xmlDefaultSAXHandler(void) { + return &xmlDefaultSAXHandler; +} + +#ifdef LIBXML_HTML_ENABLED +const xmlSAXHandlerV1 * +__htmlDefaultSAXHandler(void) { + return &htmlDefaultSAXHandler; +} +#endif /* LIBXML_HTML_ENABLED */ +#endif /* LIBXML_SAX1_ENABLED */ + #endif /* LIBXML_THREAD_ENABLED */ /** @@ -950,6 +980,8 @@ xmlCheckThreadLocalStorage(void) { return(0); } +/** DOC_DISABLE */ + /** * DllMain: * @hinstDLL: handle to DLL instance @@ -1115,13 +1147,8 @@ int xmlThrDefLoadExtDtdDefaultValue(int v) { return ret; } -int xmlThrDefParserDebugEntities(int v) { - int ret; - xmlMutexLock(&xmlThrDefMutex); - ret = xmlParserDebugEntitiesThrDef; - xmlParserDebugEntitiesThrDef = v; - xmlMutexUnlock(&xmlThrDefMutex); - return ret; +int xmlThrDefParserDebugEntities(int v ATTRIBUTE_UNUSED) { + return(xmlParserDebugEntities); } int xmlThrDefPedanticParserDefaultValue(int v) { @@ -1207,3 +1234,5 @@ xmlThrDefOutputBufferCreateFilenameDefault(xmlOutputBufferCreateFilenameFunc fun return(old); } +/** DOC_ENABLE */ + diff --git a/libraries/libxml2/hash.c b/libraries/libxml2/hash.c index 38341d78..6af8527e 100644 --- a/libraries/libxml2/hash.c +++ b/libraries/libxml2/hash.c @@ -445,16 +445,9 @@ xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key, if (dealloc) dealloc(entry->payload, entry->key); entry->payload = payload; - return(0); - } else { - /* - * xmlHashAddEntry found an existing entry. - * - * TODO: We should return a different error code here to - * distinguish from malloc failures. - */ - return(-1); } + + return(0); } /* @@ -589,7 +582,7 @@ xmlHashUpdateInternal(xmlHashTablePtr hash, const xmlChar *key, hash->nbElems++; - return(0); + return(1); } /** @@ -604,6 +597,70 @@ xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) { xmlFree(entry); } +/** + * xmlHashAdd: + * @hash: hash table + * @key: string key + * @payload: pointer to the payload + * + * Add a hash table entry. If an entry with this key already exists, + * payload will not be updated and 0 is returned. This return value + * can't be distinguished from out-of-memory errors, so this function + * should be used with care. + * + * Available since 2.13.0. + * + * Returns 1 on success, 0 if an entry exists and -1 in case of error. + */ +int +xmlHashAdd(xmlHashTablePtr hash, const xmlChar *key, void *payload) { + return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0)); +} + +/** + * xmlHashAdd2: + * @hash: hash table + * @key: first string key + * @key2: second string key + * @payload: pointer to the payload + * + * Add a hash table entry with two strings as key. + * + * See xmlHashAdd. + * + * Available since 2.13.0. + * + * Returns 1 on success, 0 if an entry exists and -1 in case of error. + */ +int +xmlHashAdd2(xmlHashTablePtr hash, const xmlChar *key, + const xmlChar *key2, void *payload) { + return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0)); +} + +/** + * xmlHashAdd3: + * @hash: hash table + * @key: first string key + * @key2: second string key + * @key3: third string key + * @payload: pointer to the payload + * + * Add a hash table entry with three strings as key. + * + * See xmlHashAdd. + * + * Available since 2.13.0. + * + * Returns 1 on success, 0 if an entry exists and -1 in case of error. + */ +int +xmlHashAdd3(xmlHashTablePtr hash, const xmlChar *key, + const xmlChar *key2, const xmlChar *key3, + void *payload) { + return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0)); +} + /** * xmlHashAddEntry: * @hash: hash table @@ -615,11 +672,21 @@ xmlHashDefaultDeallocator(void *entry, const xmlChar *key ATTRIBUTE_UNUSED) { * can't be distinguished from out-of-memory errors, so this function * should be used with care. * + * NOTE: This function doesn't allow to distinguish malloc failures from + * existing entries. Use xmlHashAdd instead. + * * Returns 0 on success and -1 in case of error. */ int xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) { - return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0)); + int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, NULL, 0); + + if (res == 0) + res = -1; + else if (res == 1) + res = 0; + + return(res); } /** @@ -638,7 +705,14 @@ xmlHashAddEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload) { int xmlHashAddEntry2(xmlHashTablePtr hash, const xmlChar *key, const xmlChar *key2, void *payload) { - return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0)); + int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, NULL, 0); + + if (res == 0) + res = -1; + else if (res == 1) + res = 0; + + return(res); } /** @@ -659,7 +733,14 @@ int xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key, const xmlChar *key2, const xmlChar *key3, void *payload) { - return(xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0)); + int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, NULL, 0); + + if (res == 0) + res = -1; + else if (res == 1) + res = 0; + + return(res); } /** @@ -677,8 +758,13 @@ xmlHashAddEntry3(xmlHashTablePtr hash, const xmlChar *key, int xmlHashUpdateEntry(xmlHashTablePtr hash, const xmlChar *key, void *payload, xmlHashDeallocator dealloc) { - return(xmlHashUpdateInternal(hash, key, NULL, NULL, payload, - dealloc, 1)); + int res = xmlHashUpdateInternal(hash, key, NULL, NULL, payload, + dealloc, 1); + + if (res == 1) + res = 0; + + return(res); } /** @@ -699,8 +785,13 @@ int xmlHashUpdateEntry2(xmlHashTablePtr hash, const xmlChar *key, const xmlChar *key2, void *payload, xmlHashDeallocator dealloc) { - return(xmlHashUpdateInternal(hash, key, key2, NULL, payload, - dealloc, 1)); + int res = xmlHashUpdateInternal(hash, key, key2, NULL, payload, + dealloc, 1); + + if (res == 1) + res = 0; + + return(res); } /** @@ -722,8 +813,13 @@ int xmlHashUpdateEntry3(xmlHashTablePtr hash, const xmlChar *key, const xmlChar *key2, const xmlChar *key3, void *payload, xmlHashDeallocator dealloc) { - return(xmlHashUpdateInternal(hash, key, key2, key3, payload, - dealloc, 1)); + int res = xmlHashUpdateInternal(hash, key, key2, key3, payload, + dealloc, 1); + + if (res == 1) + res = 0; + + return(res); } /** @@ -1037,21 +1133,25 @@ xmlHashScanFull3(xmlHashTablePtr hash, const xmlChar *key, } } -/** - * xmlHashCopy: +/* + * xmlHashCopySafe: * @hash: hash table - * @copy: copier function for items in the hash + * @copyFunc: copier function for items in the hash + * @deallocFunc: deallocation function in case of errors + * + * Copy the hash table using @copyFunc to copy payloads. * - * Copy the hash @table using @copy to copy payloads. + * Available since 2.13.0. * * Returns the new table or NULL if a memory allocation failed. */ xmlHashTablePtr -xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) { +xmlHashCopySafe(xmlHashTablePtr hash, xmlHashCopier copyFunc, + xmlHashDeallocator deallocFunc) { const xmlHashEntry *entry, *end; xmlHashTablePtr ret; - if ((hash == NULL) || (copy == NULL)) + if ((hash == NULL) || (copyFunc == NULL)) return(NULL); ret = xmlHashCreate(hash->size); @@ -1064,12 +1164,42 @@ xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) { end = &hash->table[hash->size]; for (entry = hash->table; entry < end; entry++) { - if (entry->hashValue != 0) - xmlHashAddEntry3(ret, entry->key, entry->key2, entry->key3, - copy(entry->payload, entry->key)); + if (entry->hashValue != 0) { + void *copy; + + copy = copyFunc(entry->payload, entry->key); + if (copy == NULL) + goto error; + if (xmlHashAdd3(ret, entry->key, entry->key2, entry->key3, + copy) <= 0) { + if (deallocFunc != NULL) + deallocFunc(copy, entry->key); + goto error; + } + } } return(ret); + +error: + xmlHashFree(ret, deallocFunc); + return(NULL); +} + +/* + * xmlHashCopy: + * @hash: hash table + * @copy: copier function for items in the hash + * + * DEPRECATED: Leaks memory in error case. + * + * Copy the hash table using @copy to copy payloads. + * + * Returns the new table or NULL if a memory allocation failed. + */ +xmlHashTablePtr +xmlHashCopy(xmlHashTablePtr hash, xmlHashCopier copy) { + return(xmlHashCopySafe(hash, copy, NULL)); } /** diff --git a/libraries/libxml2/libxml.h b/libraries/libxml2/libxml.h index 2f72e0ac..d94677b5 100644 --- a/libraries/libxml2/libxml.h +++ b/libraries/libxml2/libxml.h @@ -38,11 +38,6 @@ #define SYSCONFDIR "/etc" #endif -#ifdef WITH_TRIO - #define TRIO_REPLACE_STDIO - #include "trio.h" -#endif - #if !defined(_WIN32) && \ !defined(__CYGWIN__) && \ (defined(__clang__) || \ @@ -53,16 +48,21 @@ #endif #if defined(__clang__) || \ - (defined(__GNUC__) && (__GNUC__ >= 8)) + (defined(__GNUC__) && (__GNUC__ >= 8) && !defined(__EDG__)) #define ATTRIBUTE_NO_SANITIZE(arg) __attribute__((no_sanitize(arg))) #else #define ATTRIBUTE_NO_SANITIZE(arg) #endif #ifdef __clang__ - #define ATTRIBUTE_NO_SANITIZE_INTEGER \ - ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") \ - ATTRIBUTE_NO_SANITIZE("unsigned-shift-base") + #if __clang_major__ >= 12 + #define ATTRIBUTE_NO_SANITIZE_INTEGER \ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") \ + ATTRIBUTE_NO_SANITIZE("unsigned-shift-base") + #else + #define ATTRIBUTE_NO_SANITIZE_INTEGER \ + ATTRIBUTE_NO_SANITIZE("unsigned-integer-overflow") + #endif #else #define ATTRIBUTE_NO_SANITIZE_INTEGER #endif diff --git a/libraries/libxml2/list.c b/libraries/libxml2/list.c index 4927a263..20df26c8 100644 --- a/libraries/libxml2/list.c +++ b/libraries/libxml2/list.c @@ -188,18 +188,13 @@ xmlListPtr xmlListCreate(xmlListDeallocator deallocator, xmlListDataCompare compare) { xmlListPtr l; - if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for list"); + if (NULL == (l = (xmlListPtr )xmlMalloc( sizeof(xmlList)))) return (NULL); - } /* Initialize the list to NULL */ memset(l, 0, sizeof(xmlList)); /* Add the sentinel */ if (NULL ==(l->sentinel = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for sentinel"); xmlFree(l); return (NULL); } @@ -279,11 +274,8 @@ xmlListInsert(xmlListPtr l, void *data) lkPlace = xmlListLowerSearch(l, data); /* Add the new link */ lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); - if (lkNew == NULL) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for new link"); + if (lkNew == NULL) return (1); - } lkNew->data = data; lkPlace = lkPlace->prev; lkNew->next = lkPlace->next; @@ -311,11 +303,8 @@ int xmlListAppend(xmlListPtr l, void *data) lkPlace = xmlListHigherSearch(l, data); /* Add the new link */ lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); - if (lkNew == NULL) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for new link"); + if (lkNew == NULL) return (1); - } lkNew->data = data; lkNew->next = lkPlace->next; (lkPlace->next)->prev = lkNew; @@ -548,11 +537,8 @@ xmlListPushFront(xmlListPtr l, void *data) lkPlace = l->sentinel; /* Add the new link */ lkNew = (xmlLinkPtr) xmlMalloc(sizeof(xmlLink)); - if (lkNew == NULL) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for new link"); + if (lkNew == NULL) return (0); - } lkNew->data = data; lkNew->next = lkPlace->next; (lkPlace->next)->prev = lkNew; @@ -579,11 +565,8 @@ xmlListPushBack(xmlListPtr l, void *data) return(0); lkPlace = l->sentinel->prev; /* Add the new link */ - if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) { - xmlGenericError(xmlGenericErrorContext, - "Cannot initialize memory for new link"); + if (NULL ==(lkNew = (xmlLinkPtr )xmlMalloc(sizeof(xmlLink)))) return (0); - } lkNew->data = data; lkNew->next = lkPlace->next; (lkPlace->next)->prev = lkNew; @@ -729,7 +712,7 @@ xmlListMerge(xmlListPtr l1, xmlListPtr l2) * Returns a new copy of the list or NULL in case of error */ xmlListPtr -xmlListDup(const xmlListPtr old) +xmlListDup(xmlListPtr old) { xmlListPtr cur; @@ -758,7 +741,7 @@ xmlListDup(const xmlListPtr old) * Returns 0 in case of success 1 in case of error */ int -xmlListCopy(xmlListPtr cur, const xmlListPtr old) +xmlListCopy(xmlListPtr cur, xmlListPtr old) { /* Walk the old tree and insert the data into the new one */ xmlLinkPtr lk; diff --git a/libraries/libxml2/parser.c b/libraries/libxml2/parser.c index b7534ae3..10695ecf 100644 --- a/libraries/libxml2/parser.c +++ b/libraries/libxml2/parser.c @@ -79,6 +79,10 @@ #define URI_HASH_EMPTY 0xD943A04E #define URI_HASH_XML 0xF0451F02 +#ifndef STDIN_FILENO + #define STDIN_FILENO 0 +#endif + struct _xmlStartTag { const xmlChar *prefix; const xmlChar *URI; @@ -108,23 +112,25 @@ struct _xmlParserNsData { unsigned elementId; int defaultNsIndex; + int minNsIndex; }; struct _xmlAttrHashBucket { int index; }; -static xmlParserCtxtPtr -xmlCreateEntityParserCtxtInternal(xmlSAXHandlerPtr sax, void *userData, - const xmlChar *URL, const xmlChar *ID, const xmlChar *base, - xmlParserCtxtPtr pctx); - static int xmlParseElementStart(xmlParserCtxtPtr ctxt); static void xmlParseElementEnd(xmlParserCtxtPtr ctxt); +static xmlEntityPtr +xmlLookupGeneralEntity(xmlParserCtxtPtr ctxt, const xmlChar *name, int inAttr); + +static const xmlChar * +xmlParseEntityRefInternal(xmlParserCtxtPtr ctxt); + /************************************************************************ * * * Arbitrary limits set in the parser. See XML_PARSE_HUGE * @@ -159,7 +165,7 @@ xmlParseElementEnd(xmlParserCtxtPtr ctxt); * boundary feature. It can be disabled with the XML_PARSE_HUGE * parser option. */ -unsigned int xmlParserMaxDepth = 256; +const unsigned int xmlParserMaxDepth = 256; @@ -201,23 +207,8 @@ static const char* const xmlW3CPIs[] = { static xmlEntityPtr xmlParseStringPEReference(xmlParserCtxtPtr ctxt, const xmlChar **str); -static xmlParserErrors -xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt, - xmlSAXHandlerPtr sax, - void *user_data, int depth, const xmlChar *URL, - const xmlChar *ID, xmlNodePtr *list); - -static int -xmlCtxtUseOptionsInternal(xmlParserCtxtPtr ctxt, int options); -#ifdef LIBXML_LEGACY_ENABLED static void -xmlAddEntityReference(xmlEntityPtr ent, xmlNodePtr firstNode, - xmlNodePtr lastNode); -#endif /* LIBXML_LEGACY_ENABLED */ - -static xmlParserErrors -xmlParseBalancedChunkMemoryInternal(xmlParserCtxtPtr oldctxt, - const xmlChar *string, void *user_data, xmlNodePtr *lst); +xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent); static int xmlLoadEntityContent(xmlParserCtxtPtr ctxt, xmlEntityPtr entity); @@ -228,6 +219,11 @@ xmlLoadEntityContent(xmlParserCtxtPtr ctxt, xmlEntityPtr entity); * * ************************************************************************/ +static void +xmlErrMemory(xmlParserCtxtPtr ctxt) { + xmlCtxtErrMemory(ctxt); +} + /** * xmlErrAttributeDup: * @ctxt: an XML parser context @@ -240,28 +236,14 @@ static void xmlErrAttributeDup(xmlParserCtxtPtr ctxt, const xmlChar * prefix, const xmlChar * localname) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = XML_ERR_ATTRIBUTE_REDEFINED; - if (prefix == NULL) - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, - XML_ERR_ATTRIBUTE_REDEFINED, XML_ERR_FATAL, NULL, 0, - (const char *) localname, NULL, NULL, 0, 0, - "Attribute %s redefined\n", localname); + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, XML_ERR_ATTRIBUTE_REDEFINED, + XML_ERR_FATAL, localname, NULL, NULL, 0, + "Attribute %s redefined\n", localname); else - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, - XML_ERR_ATTRIBUTE_REDEFINED, XML_ERR_FATAL, NULL, 0, - (const char *) prefix, (const char *) localname, - NULL, 0, 0, "Attribute %s:%s redefined\n", prefix, - localname); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, XML_ERR_ATTRIBUTE_REDEFINED, + XML_ERR_FATAL, prefix, localname, NULL, 0, + "Attribute %s:%s redefined\n", prefix, localname); } /** @@ -276,18 +258,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlFatalErrMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, "%s", msg); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, + NULL, NULL, NULL, 0, "%s", msg); } /** @@ -304,29 +276,8 @@ void LIBXML_ATTR_FORMAT(3,0) xmlWarningMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2) { - xmlStructuredErrorFunc schannel = NULL; - - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if ((ctxt != NULL) && (ctxt->sax != NULL) && - (ctxt->sax->initialized == XML_SAX2_MAGIC)) - schannel = ctxt->sax->serror; - if (ctxt != NULL) { - __xmlRaiseError(schannel, - (ctxt->sax) ? ctxt->sax->warning : NULL, - ctxt->userData, - ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_WARNING, NULL, 0, - (const char *) str1, (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); - } else { - __xmlRaiseError(schannel, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_WARNING, NULL, 0, - (const char *) str1, (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_WARNING, + str1, str2, NULL, 0, msg, str1, str2); } /** @@ -342,31 +293,10 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlValidityError(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2) { - xmlStructuredErrorFunc schannel = NULL; + ctxt->valid = 0; - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) { - ctxt->errNo = error; - if ((ctxt->sax != NULL) && (ctxt->sax->initialized == XML_SAX2_MAGIC)) - schannel = ctxt->sax->serror; - } - if (ctxt != NULL) { - __xmlRaiseError(schannel, - ctxt->vctxt.error, ctxt->vctxt.userData, - ctxt, NULL, XML_FROM_DTD, error, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); - ctxt->valid = 0; - } else { - __xmlRaiseError(schannel, NULL, NULL, - ctxt, NULL, XML_FROM_DTD, error, - XML_ERR_ERROR, NULL, 0, (const char *) str1, - (const char *) str2, NULL, 0, 0, - msg, (const char *) str1, (const char *) str2); - } + xmlCtxtErr(ctxt, NULL, XML_FROM_DTD, error, XML_ERR_ERROR, + str1, str2, NULL, 0, msg, str1, str2); } /** @@ -382,19 +312,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlFatalErrMsgInt(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, int val) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, - NULL, 0, NULL, NULL, NULL, val, 0, msg, val); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, + NULL, NULL, NULL, val, msg, val); } /** @@ -413,20 +332,8 @@ xmlFatalErrMsgStrIntStr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, int val, const xmlChar *str2) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, - NULL, 0, (const char *) str1, (const char *) str2, - NULL, val, 0, msg, str1, val, str2); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, + str1, str2, NULL, val, msg, str1, val, str2); } /** @@ -442,20 +349,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlFatalErrMsgStr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar * val) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, - XML_FROM_PARSER, error, XML_ERR_FATAL, - NULL, 0, (const char *) val, NULL, NULL, 0, 0, msg, - val); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, + val, NULL, NULL, 0, msg, val); } /** @@ -471,15 +366,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlErrMsgStr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar * val) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, - XML_FROM_PARSER, error, XML_ERR_ERROR, - NULL, 0, (const char *) val, NULL, NULL, 0, 0, msg, - val); + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_ERROR, + val, NULL, NULL, 0, msg, val); } /** @@ -498,17 +386,10 @@ xmlNsErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const xmlChar * info1, const xmlChar * info2, const xmlChar * info3) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_NAMESPACE, error, - XML_ERR_ERROR, NULL, 0, (const char *) info1, - (const char *) info2, (const char *) info3, 0, 0, msg, - info1, info2, info3); - if (ctxt != NULL) - ctxt->nsWellFormed = 0; + ctxt->nsWellFormed = 0; + + xmlCtxtErr(ctxt, NULL, XML_FROM_NAMESPACE, error, XML_ERR_ERROR, + info1, info2, info3, 0, msg, info1, info2, info3); } /** @@ -527,13 +408,8 @@ xmlNsWarn(xmlParserCtxtPtr ctxt, xmlParserErrors error, const xmlChar * info1, const xmlChar * info2, const xmlChar * info3) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_NAMESPACE, error, - XML_ERR_WARNING, NULL, 0, (const char *) info1, - (const char *) info2, (const char *) info3, 0, 0, msg, - info1, info2, info3); + xmlCtxtErr(ctxt, NULL, XML_FROM_NAMESPACE, error, XML_ERR_WARNING, + info1, info2, info3, 0, msg, info1, info2, info3); } static void @@ -559,7 +435,7 @@ xmlSaturatedAddSizeT(unsigned long *dst, unsigned long val) { * * Check for non-linear entity expansion behaviour. * - * In some cases like xmlStringDecodeEntities, this function is called + * In some cases like xmlExpandEntityInAttValue, this function is called * for each, possibly nested entity and its unexpanded content length. * * In other cases like xmlParseReference, it's only called for each @@ -580,37 +456,41 @@ static int xmlParserEntityCheck(xmlParserCtxtPtr ctxt, unsigned long extra) { unsigned long consumed; + unsigned long *expandedSize; xmlParserInputPtr input = ctxt->input; xmlEntityPtr entity = input->entity; + if ((entity) && (entity->flags & XML_ENT_CHECKED)) + return(0); + /* * Compute total consumed bytes so far, including input streams of * external entities. */ - consumed = input->parentConsumed; - if ((entity == NULL) || - ((entity->etype == XML_EXTERNAL_PARAMETER_ENTITY) && - ((entity->flags & XML_ENT_PARSED) == 0))) { - xmlSaturatedAdd(&consumed, input->consumed); - xmlSaturatedAddSizeT(&consumed, input->cur - input->base); - } + consumed = input->consumed; + xmlSaturatedAddSizeT(&consumed, input->cur - input->base); xmlSaturatedAdd(&consumed, ctxt->sizeentities); + if (entity) + expandedSize = &entity->expandedSize; + else + expandedSize = &ctxt->sizeentcopy; + /* * Add extra cost and some fixed cost. */ - xmlSaturatedAdd(&ctxt->sizeentcopy, extra); - xmlSaturatedAdd(&ctxt->sizeentcopy, XML_ENT_FIXED_COST); + xmlSaturatedAdd(expandedSize, extra); + xmlSaturatedAdd(expandedSize, XML_ENT_FIXED_COST); /* * It's important to always use saturation arithmetic when tracking * entity sizes to make the size checks reliable. If "sizeentcopy" * overflows, we have to abort. */ - if ((ctxt->sizeentcopy > XML_PARSER_ALLOWED_EXPANSION) && - ((ctxt->sizeentcopy >= ULONG_MAX) || - (ctxt->sizeentcopy / ctxt->maxAmpl > consumed))) { - xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_LOOP, + if ((*expandedSize > XML_PARSER_ALLOWED_EXPANSION) && + ((*expandedSize >= ULONG_MAX) || + (*expandedSize / ctxt->maxAmpl > consumed))) { + xmlFatalErrMsg(ctxt, XML_ERR_RESOURCE_LIMIT, "Maximum entity amplification factor exceeded, see " "xmlCtxtSetMaxAmplification.\n"); xmlHaltParser(ctxt); @@ -809,12 +689,6 @@ xmlHasFeature(xmlFeature feature) return(0); #endif case XML_WITH_DEBUG_MEM: -#ifdef DEBUG_MEMORY_LOCATION - return(1); -#else - return(0); -#endif - case XML_WITH_DEBUG_RUN: return(0); case XML_WITH_ZLIB: #ifdef LIBXML_ZLIB_ENABLED @@ -840,6 +714,220 @@ xmlHasFeature(xmlFeature feature) return(0); } +/************************************************************************ + * * + * Simple string buffer * + * * + ************************************************************************/ + +typedef struct { + xmlChar *mem; + unsigned size; + unsigned cap; /* size < cap */ + unsigned max; /* size <= max */ + xmlParserErrors code; +} xmlSBuf; + +static void +xmlSBufInit(xmlSBuf *buf, unsigned max) { + buf->mem = NULL; + buf->size = 0; + buf->cap = 0; + buf->max = max; + buf->code = XML_ERR_OK; +} + +static int +xmlSBufGrow(xmlSBuf *buf, unsigned len) { + xmlChar *mem; + unsigned cap; + + if (len >= UINT_MAX / 2 - buf->size) { + if (buf->code == XML_ERR_OK) + buf->code = XML_ERR_RESOURCE_LIMIT; + return(-1); + } + + cap = (buf->size + len) * 2; + if (cap < 240) + cap = 240; + + mem = xmlRealloc(buf->mem, cap); + if (mem == NULL) { + buf->code = XML_ERR_NO_MEMORY; + return(-1); + } + + buf->mem = mem; + buf->cap = cap; + + return(0); +} + +static void +xmlSBufAddString(xmlSBuf *buf, const xmlChar *str, unsigned len) { + if (buf->max - buf->size < len) { + if (buf->code == XML_ERR_OK) + buf->code = XML_ERR_RESOURCE_LIMIT; + return; + } + + if (buf->cap - buf->size <= len) { + if (xmlSBufGrow(buf, len) < 0) + return; + } + + if (len > 0) + memcpy(buf->mem + buf->size, str, len); + buf->size += len; +} + +static void +xmlSBufAddCString(xmlSBuf *buf, const char *str, unsigned len) { + xmlSBufAddString(buf, (const xmlChar *) str, len); +} + +static void +xmlSBufAddChar(xmlSBuf *buf, int c) { + xmlChar *end; + + if (buf->max - buf->size < 4) { + if (buf->code == XML_ERR_OK) + buf->code = XML_ERR_RESOURCE_LIMIT; + return; + } + + if (buf->cap - buf->size <= 4) { + if (xmlSBufGrow(buf, 4) < 0) + return; + } + + end = buf->mem + buf->size; + + if (c < 0x80) { + *end = (xmlChar) c; + buf->size += 1; + } else { + buf->size += xmlCopyCharMultiByte(end, c); + } +} + +static void +xmlSBufAddReplChar(xmlSBuf *buf) { + xmlSBufAddCString(buf, "\xEF\xBF\xBD", 3); +} + +static void +xmlSBufReportError(xmlSBuf *buf, xmlParserCtxtPtr ctxt, const char *errMsg) { + if (buf->code == XML_ERR_NO_MEMORY) + xmlCtxtErrMemory(ctxt); + else + xmlFatalErr(ctxt, buf->code, errMsg); +} + +static xmlChar * +xmlSBufFinish(xmlSBuf *buf, int *sizeOut, xmlParserCtxtPtr ctxt, + const char *errMsg) { + if (buf->mem == NULL) { + buf->mem = xmlMalloc(1); + if (buf->mem == NULL) { + buf->code = XML_ERR_NO_MEMORY; + } else { + buf->mem[0] = 0; + } + } else { + buf->mem[buf->size] = 0; + } + + if (buf->code == XML_ERR_OK) { + if (sizeOut != NULL) + *sizeOut = buf->size; + return(buf->mem); + } + + xmlSBufReportError(buf, ctxt, errMsg); + + xmlFree(buf->mem); + + if (sizeOut != NULL) + *sizeOut = 0; + return(NULL); +} + +static void +xmlSBufCleanup(xmlSBuf *buf, xmlParserCtxtPtr ctxt, const char *errMsg) { + if (buf->code != XML_ERR_OK) + xmlSBufReportError(buf, ctxt, errMsg); + + xmlFree(buf->mem); +} + +static int +xmlUTF8MultibyteLen(xmlParserCtxtPtr ctxt, const xmlChar *str, + const char *errMsg) { + int c = str[0]; + int c1 = str[1]; + + if ((c1 & 0xC0) != 0x80) + goto encoding_error; + + if (c < 0xE0) { + /* 2-byte sequence */ + if (c < 0xC2) + goto encoding_error; + + return(2); + } else { + int c2 = str[2]; + + if ((c2 & 0xC0) != 0x80) + goto encoding_error; + + if (c < 0xF0) { + /* 3-byte sequence */ + if (c == 0xE0) { + /* overlong */ + if (c1 < 0xA0) + goto encoding_error; + } else if (c == 0xED) { + /* surrogate */ + if (c1 >= 0xA0) + goto encoding_error; + } else if (c == 0xEF) { + /* U+FFFE and U+FFFF are invalid Chars */ + if ((c1 == 0xBF) && (c2 >= 0xBE)) + xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, errMsg); + } + + return(3); + } else { + /* 4-byte sequence */ + if ((str[3] & 0xC0) != 0x80) + goto encoding_error; + if (c == 0xF0) { + /* overlong */ + if (c1 < 0x90) + goto encoding_error; + } else if (c >= 0xF4) { + /* greater than 0x10FFFF */ + if ((c > 0xF4) || (c1 >= 0x90)) + goto encoding_error; + } + + return(4); + } + } + +encoding_error: + /* Only report the first error */ + if ((ctxt->input->flags & XML_INPUT_ENCODING_ERROR) == 0) { + xmlCtxtErrIO(ctxt, XML_ERR_INVALID_ENCODING, NULL); + ctxt->input->flags |= XML_INPUT_ENCODING_ERROR; + } + + return(0); +} + /************************************************************************ * * * SAX2 defaulted attributes handling * @@ -847,18 +935,25 @@ xmlHasFeature(xmlFeature feature) ************************************************************************/ /** - * xmlDetectSAX2: + * xmlCtxtInitializeLate: * @ctxt: an XML parser context * - * Do the SAX2 detection and specific initialization + * Final initialization of the parser context before starting to parse. + * + * This accounts for users modifying struct members of parser context + * directly. */ static void -xmlDetectSAX2(xmlParserCtxtPtr ctxt) { +xmlCtxtInitializeLate(xmlParserCtxtPtr ctxt) { xmlSAXHandlerPtr sax; /* Avoid unused variable warning if features are disabled. */ (void) sax; + /* + * Changing the SAX struct directly is still widespread practice + * in internal and external code. + */ if (ctxt == NULL) return; sax = ctxt->sax; #ifdef LIBXML_SAX1_ENABLED @@ -866,7 +961,9 @@ xmlDetectSAX2(xmlParserCtxtPtr ctxt) { * Only enable SAX2 if there SAX2 element handlers, except when there * are no element handlers at all. */ - if ((sax) && (sax->initialized == XML_SAX2_MAGIC) && + if (((ctxt->options & XML_PARSE_SAX1) == 0) && + (sax) && + (sax->initialized == XML_SAX2_MAGIC) && ((sax->startElementNs != NULL) || (sax->endElementNs != NULL) || ((sax->startElement == NULL) && (sax->endElement == NULL)))) @@ -875,12 +972,16 @@ xmlDetectSAX2(xmlParserCtxtPtr ctxt) { ctxt->sax2 = 1; #endif /* LIBXML_SAX1_ENABLED */ + /* + * Some users replace the dictionary directly in the context struct. + * We really need an API function to do that cleanly. + */ ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3); ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5); ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36); if ((ctxt->str_xml==NULL) || (ctxt->str_xmlns==NULL) || (ctxt->str_xml_ns == NULL)) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); } } @@ -945,65 +1046,6 @@ xmlAttrNormalizeSpace(const xmlChar *src, xmlChar *dst) 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, 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 = strlen((const char *)ret); - return(ret); - } else if (remove_head) { - *len -= remove_head; - memmove(src, src + remove_head, 1 + *len); - return(src); - } - return(NULL); -} - /** * xmlAddDefAttrs: * @ctxt: an XML parser context @@ -1115,13 +1157,13 @@ xmlAddDefAttrs(xmlParserCtxtPtr ctxt, attr->prefix = prefix; attr->value = hvalue; attr->valueEnd = hvalue.name + len; - attr->external = ctxt->external; + attr->external = PARSER_EXTERNAL(ctxt); attr->expandedSize = expandedSize; return; mem_error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return; } @@ -1146,15 +1188,13 @@ xmlAddSpecialAttr(xmlParserCtxtPtr ctxt, goto mem_error; } - if (xmlHashLookup2(ctxt->attsSpecial, fullname, fullattr) != NULL) - return; - - xmlHashAddEntry2(ctxt->attsSpecial, fullname, fullattr, - (void *) (ptrdiff_t) type); + if (xmlHashAdd2(ctxt->attsSpecial, fullname, fullattr, + (void *) (ptrdiff_t) type) < 0) + goto mem_error; return; mem_error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return; } @@ -1399,8 +1439,8 @@ xmlCheckLanguageID(const xmlChar * lang) * * ************************************************************************/ -static xmlEntityPtr xmlParseStringEntityRef(xmlParserCtxtPtr ctxt, - const xmlChar ** str); +static xmlChar * +xmlParseStringEntityRef(xmlParserCtxtPtr ctxt, const xmlChar **str); /** * xmlParserNsCreate: @@ -1489,7 +1529,7 @@ xmlParserNsStartElement(xmlParserNsData *nsdb) { static int xmlParserNsLookup(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, xmlParserNsBucket **bucketPtr) { - xmlParserNsBucket *bucket; + xmlParserNsBucket *bucket, *tombstone; unsigned index, hashValue; if (prefix->name == NULL) @@ -1501,10 +1541,13 @@ xmlParserNsLookup(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, hashValue = prefix->hashValue; index = hashValue & (ctxt->nsdb->hashSize - 1); bucket = &ctxt->nsdb->hash[index]; + tombstone = NULL; while (bucket->hashValue) { - if ((bucket->hashValue == hashValue) && - (bucket->index != INT_MAX)) { + if (bucket->index == INT_MAX) { + if (tombstone == NULL) + tombstone = bucket; + } else if (bucket->hashValue == hashValue) { if (ctxt->nsTab[bucket->index * 2] == prefix->name) { if (bucketPtr != NULL) *bucketPtr = bucket; @@ -1521,7 +1564,7 @@ xmlParserNsLookup(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, } if (bucketPtr != NULL) - *bucketPtr = bucket; + *bucketPtr = tombstone ? tombstone : bucket; return(INT_MAX); } @@ -1542,8 +1585,12 @@ xmlParserNsLookupUri(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix) { if (prefix->name == ctxt->str_xml) return(ctxt->str_xml_ns); + /* + * minNsIndex is used when building an entity tree. We must + * ignore namespaces declared outside the entity. + */ nsIndex = xmlParserNsLookup(ctxt, prefix, NULL); - if (nsIndex == INT_MAX) + if ((nsIndex == INT_MAX) || (nsIndex < ctxt->nsdb->minNsIndex)) return(NULL); ret = ctxt->nsTab[nsIndex * 2 + 1]; @@ -1576,7 +1623,7 @@ xmlParserNsLookupSax(xmlParserCtxtPtr ctxt, const xmlChar *prefix) { else hprefix.hashValue = 0; nsIndex = xmlParserNsLookup(ctxt, &hprefix, NULL); - if (nsIndex == INT_MAX) + if ((nsIndex == INT_MAX) || (nsIndex < ctxt->nsdb->minNsIndex)) return(NULL); return(ctxt->nsdb->extra[nsIndex].saxData); @@ -1609,7 +1656,7 @@ xmlParserNsUpdateSax(xmlParserCtxtPtr ctxt, const xmlChar *prefix, else hprefix.hashValue = 0; nsIndex = xmlParserNsLookup(ctxt, &hprefix, NULL); - if (nsIndex == INT_MAX) + if ((nsIndex == INT_MAX) || (nsIndex < ctxt->nsdb->minNsIndex)) return(-1); ctxt->nsdb->extra[nsIndex].saxData = saxData; @@ -1648,7 +1695,7 @@ xmlParserNsGrow(xmlParserCtxtPtr ctxt) { return(0); error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(-1); } @@ -1677,7 +1724,7 @@ xmlParserNsPush(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, return(0); if ((ctxt->nsNr >= ctxt->nsMax) && (xmlParserNsGrow(ctxt) < 0)) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(-1); } @@ -1743,13 +1790,13 @@ xmlParserNsPush(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, unsigned newSize, i, index; if (ctxt->nsdb->hashSize > UINT_MAX / 2) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(-1); } newSize = ctxt->nsdb->hashSize ? ctxt->nsdb->hashSize * 2 : 16; newHash = xmlMalloc(newSize * sizeof(newHash[0])); if (newHash == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(-1); } memset(newHash, 0, newSize * sizeof(newHash[0])); @@ -1758,7 +1805,7 @@ xmlParserNsPush(xmlParserCtxtPtr ctxt, const xmlHashedString *prefix, unsigned hv = ctxt->nsdb->hash[i].hashValue; unsigned newIndex; - if (hv == 0) + if ((hv == 0) || (ctxt->nsdb->hash[i].index == INT_MAX)) continue; newIndex = hv & (newSize - 1); @@ -1877,7 +1924,7 @@ xmlCtxtGrowAttrs(xmlParserCtxtPtr ctxt, int nr) { } return(ctxt->maxatts); mem_error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(-1); } @@ -1893,8 +1940,11 @@ xmlCtxtGrowAttrs(xmlParserCtxtPtr ctxt, int nr) { int inputPush(xmlParserCtxtPtr ctxt, xmlParserInputPtr value) { + char *directory = NULL; + if ((ctxt == NULL) || (value == NULL)) return(-1); + if (ctxt->inputNr >= ctxt->inputMax) { size_t newSize = ctxt->inputMax * 2; xmlParserInputPtr *tmp; @@ -1902,15 +1952,30 @@ inputPush(xmlParserCtxtPtr ctxt, xmlParserInputPtr value) tmp = (xmlParserInputPtr *) xmlRealloc(ctxt->inputTab, newSize * sizeof(*tmp)); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return (-1); } ctxt->inputTab = tmp; ctxt->inputMax = newSize; } + + if ((ctxt->inputNr == 0) && (value->filename != NULL)) { + directory = xmlParserGetDirectory(value->filename); + if (directory == NULL) { + xmlErrMemory(ctxt); + return(-1); + } + } + ctxt->inputTab[ctxt->inputNr] = value; ctxt->input = value; - return (ctxt->inputNr++); + + if (ctxt->inputNr == 0) { + xmlFree(ctxt->directory); + ctxt->directory = directory; + } + + return(ctxt->inputNr++); } /** * inputPop: @@ -1952,7 +2017,19 @@ inputPop(xmlParserCtxtPtr ctxt) int nodePush(xmlParserCtxtPtr ctxt, xmlNodePtr value) { - if (ctxt == NULL) return(0); + int maxDepth; + + if (ctxt == NULL) + return(0); + + maxDepth = (ctxt->options & XML_PARSE_HUGE) ? 2048 : 256; + if (ctxt->nodeNr > maxDepth) { + xmlFatalErrMsgInt(ctxt, XML_ERR_RESOURCE_LIMIT, + "Excessive depth in document: %d use XML_PARSE_HUGE option\n", + ctxt->nodeNr); + xmlHaltParser(ctxt); + return(-1); + } if (ctxt->nodeNr >= ctxt->nodeMax) { xmlNodePtr *tmp; @@ -1960,20 +2037,12 @@ nodePush(xmlParserCtxtPtr ctxt, xmlNodePtr value) ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0])); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return (-1); } ctxt->nodeTab = tmp; ctxt->nodeMax *= 2; } - if ((((unsigned int) ctxt->nodeNr) > xmlParserMaxDepth) && - ((ctxt->options & XML_PARSE_HUGE) == 0)) { - xmlFatalErrMsgInt(ctxt, XML_ERR_INTERNAL_ERROR, - "Excessive depth in document: %d use XML_PARSE_HUGE option\n", - xmlParserMaxDepth); - xmlHaltParser(ctxt); - return(-1); - } ctxt->nodeTab[ctxt->nodeNr] = value; ctxt->node = value; return (ctxt->nodeNr++); @@ -2061,7 +2130,7 @@ nameNsPush(xmlParserCtxtPtr ctxt, const xmlChar * value, tag->nsNr = nsNr; return (ctxt->nameNr++); mem_error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return (-1); } #ifdef LIBXML_PUSH_ENABLED @@ -2122,7 +2191,7 @@ namePush(xmlParserCtxtPtr ctxt, const xmlChar * value) ctxt->name = value; return (ctxt->nameNr++); mem_error: - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return (-1); } @@ -2161,7 +2230,7 @@ static int spacePush(xmlParserCtxtPtr ctxt, int val) { tmp = (int *) xmlRealloc(ctxt->spaceTab, ctxt->spaceMax * sizeof(ctxt->spaceTab[0])); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); ctxt->spaceMax /=2; return(-1); } @@ -2262,18 +2331,21 @@ static int spacePop(xmlParserCtxtPtr ctxt) { xmlParserGrow(ctxt); \ } while (0) -/* Don't shrink push parser buffer. */ #define SHRINK \ - if (((ctxt->progressive == 0) || (ctxt->inputNr > 1)) && \ + if ((!PARSER_PROGRESSIVE(ctxt)) && \ (ctxt->input->cur - ctxt->input->base > 2 * INPUT_CHUNK) && \ (ctxt->input->end - ctxt->input->cur < 2 * INPUT_CHUNK)) \ xmlParserShrink(ctxt); -#define GROW if (ctxt->input->end - ctxt->input->cur < INPUT_CHUNK) \ +#define GROW \ + if ((!PARSER_PROGRESSIVE(ctxt)) && \ + (ctxt->input->end - ctxt->input->cur < INPUT_CHUNK)) \ xmlParserGrow(ctxt); #define SKIP_BLANKS xmlSkipBlankChars(ctxt) +#define SKIP_BLANKS_PE xmlSkipBlankCharsPE(ctxt) + #define NEXT xmlNextChar(ctxt) #define NEXT1 { \ @@ -2303,112 +2375,161 @@ static int spacePop(xmlParserCtxtPtr ctxt) { * * DEPRECATED: Internal function, do not use. * - * skip all blanks character found at that point in the input streams. - * It pops up finished entities in the process if allowable at that point. + * Skip whitespace in the input stream. * * Returns the number of space chars skipped */ - int xmlSkipBlankChars(xmlParserCtxtPtr ctxt) { + const xmlChar *cur; int res = 0; /* * It's Okay to use CUR/NEXT here since all the blanks are on * the ASCII range. */ - if (((ctxt->inputNr == 1) && (ctxt->instate != XML_PARSER_DTD)) || - (ctxt->instate == XML_PARSER_START)) { - const xmlChar *cur; - /* - * if we are in the document content, go really fast - */ - cur = ctxt->input->cur; - while (IS_BLANK_CH(*cur)) { - if (*cur == '\n') { - ctxt->input->line++; ctxt->input->col = 1; - } else { - ctxt->input->col++; - } - cur++; - if (res < INT_MAX) - res++; - if (*cur == 0) { - ctxt->input->cur = cur; - xmlParserGrow(ctxt); - cur = ctxt->input->cur; - } - } - ctxt->input->cur = cur; - } else { - int expandPE = ((ctxt->external != 0) || (ctxt->inputNr != 1)); + cur = ctxt->input->cur; + while (IS_BLANK_CH(*cur)) { + if (*cur == '\n') { + ctxt->input->line++; ctxt->input->col = 1; + } else { + ctxt->input->col++; + } + cur++; + if (res < INT_MAX) + res++; + if (*cur == 0) { + ctxt->input->cur = cur; + xmlParserGrow(ctxt); + cur = ctxt->input->cur; + } + } + ctxt->input->cur = cur; - while (ctxt->instate != XML_PARSER_EOF) { - if (IS_BLANK_CH(CUR)) { /* CHECKED tstblanks.xml */ - NEXT; - } else if (CUR == '%') { - /* - * Need to handle support of entities branching here - */ - if ((expandPE == 0) || (IS_BLANK_CH(NXT(1))) || (NXT(1) == 0)) - break; - xmlParsePEReference(ctxt); - } else if (CUR == 0) { - unsigned long consumed; - xmlEntityPtr ent; + return(res); +} - if (ctxt->inputNr <= 1) - break; +static void +xmlPopPE(xmlParserCtxtPtr ctxt) { + unsigned long consumed; + xmlEntityPtr ent; - consumed = ctxt->input->consumed; - xmlSaturatedAddSizeT(&consumed, - ctxt->input->cur - ctxt->input->base); + ent = ctxt->input->entity; - /* - * Add to sizeentities when parsing an external entity - * for the first time. - */ - ent = ctxt->input->entity; - if ((ent->etype == XML_EXTERNAL_PARAMETER_ENTITY) && - ((ent->flags & XML_ENT_PARSED) == 0)) { - ent->flags |= XML_ENT_PARSED; + ent->flags &= ~XML_ENT_EXPANDING; - xmlSaturatedAdd(&ctxt->sizeentities, consumed); - } + if ((ent->flags & XML_ENT_CHECKED) == 0) { + int result; + + /* + * Read the rest of the stream in case of errors. We want + * to account for the whole entity size. + */ + do { + ctxt->input->cur = ctxt->input->end; + xmlParserShrink(ctxt); + result = xmlParserGrow(ctxt); + } while (result > 0); - xmlParserEntityCheck(ctxt, consumed); + consumed = ctxt->input->consumed; + xmlSaturatedAddSizeT(&consumed, + ctxt->input->end - ctxt->input->base); - xmlPopInput(ctxt); - } else { - break; - } + xmlSaturatedAdd(&ent->expandedSize, consumed); - /* - * Also increase the counter when entering or exiting a PERef. - * The spec says: "When a parameter-entity reference is recognized - * in the DTD and included, its replacement text MUST be enlarged - * by the attachment of one leading and one following space (#x20) - * character." - */ - if (res < INT_MAX) - res++; + /* + * Add to sizeentities when parsing an external entity + * for the first time. + */ + if (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY) { + xmlSaturatedAdd(&ctxt->sizeentities, consumed); } + + ent->flags |= XML_ENT_CHECKED; } - return(res); -} -/************************************************************************ - * * - * Commodity functions to handle entities * - * * - ************************************************************************/ + xmlPopInput(ctxt); + + xmlParserEntityCheck(ctxt, ent->expandedSize); +} /** - * xmlPopInput: - * @ctxt: an XML parser context + * xmlSkipBlankCharsPE: + * @ctxt: the XML parser context * - * xmlPopInput: the current input pointed by ctxt->input came to an end - * pop it and return the next char. + * Skip whitespace in the input stream, also handling parameter + * entities. + * + * Returns the number of space chars skipped + */ +static int +xmlSkipBlankCharsPE(xmlParserCtxtPtr ctxt) { + int res = 0; + int inParam; + int expandParam; + + inParam = PARSER_IN_PE(ctxt); + expandParam = PARSER_EXTERNAL(ctxt); + + if (!inParam && !expandParam) + return(xmlSkipBlankChars(ctxt)); + + while (PARSER_STOPPED(ctxt) == 0) { + if (IS_BLANK_CH(CUR)) { /* CHECKED tstblanks.xml */ + NEXT; + } else if (CUR == '%') { + if ((expandParam == 0) || + (IS_BLANK_CH(NXT(1))) || (NXT(1) == 0)) + break; + + /* + * Expand parameter entity. We continue to consume + * whitespace at the start of the entity and possible + * even consume the whole entity and pop it. We might + * even pop multiple PEs in this loop. + */ + xmlParsePEReference(ctxt); + + inParam = PARSER_IN_PE(ctxt); + expandParam = PARSER_EXTERNAL(ctxt); + } else if (CUR == 0) { + if (inParam == 0) + break; + + xmlPopPE(ctxt); + + inParam = PARSER_IN_PE(ctxt); + expandParam = PARSER_EXTERNAL(ctxt); + } else { + break; + } + + /* + * Also increase the counter when entering or exiting a PERef. + * The spec says: "When a parameter-entity reference is recognized + * in the DTD and included, its replacement text MUST be enlarged + * by the attachment of one leading and one following space (#x20) + * character." + */ + if (res < INT_MAX) + res++; + } + + return(res); +} + +/************************************************************************ + * * + * Commodity functions to handle entities * + * * + ************************************************************************/ + +/** + * xmlPopInput: + * @ctxt: an XML parser context + * + * xmlPopInput: the current input pointed by ctxt->input came to an end + * pop it and return the next char. * * Returns the current xmlChar in the parser context */ @@ -2417,16 +2538,7 @@ xmlPopInput(xmlParserCtxtPtr ctxt) { xmlParserInputPtr input; if ((ctxt == NULL) || (ctxt->inputNr <= 1)) return(0); - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "Popping input %d\n", ctxt->inputNr); - if ((ctxt->inputNr > 1) && (ctxt->inSubset == 0) && - (ctxt->instate != XML_PARSER_EOF)) - xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR, - "Unfinished entity outside the DTD"); input = inputPop(ctxt); - if (input->entity != NULL) - input->entity->flags &= ~XML_ENT_EXPANDING; xmlFreeInputStream(input); if (*ctxt->input->cur == 0) xmlParserGrow(ctxt); @@ -2438,33 +2550,26 @@ xmlPopInput(xmlParserCtxtPtr ctxt) { * @ctxt: an XML parser context * @input: an XML parser input fragment (entity, XML fragment ...). * - * xmlPushInput: switch to a new input stream which is stacked on top - * of the previous one(s). + * Push an input stream onto the stack. + * * Returns -1 in case of error or the index in the input stack */ int xmlPushInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr input) { + int maxDepth; int ret; - if (input == NULL) return(-1); - - if (xmlParserDebugEntities) { - if ((ctxt->input != NULL) && (ctxt->input->filename)) - xmlGenericError(xmlGenericErrorContext, - "%s(%d): ", ctxt->input->filename, - ctxt->input->line); - xmlGenericError(xmlGenericErrorContext, - "Pushing input %d : %.30s\n", ctxt->inputNr+1, input->cur); - } - if (((ctxt->inputNr > 40) && ((ctxt->options & XML_PARSE_HUGE) == 0)) || - (ctxt->inputNr > 100)) { - xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); - while (ctxt->inputNr > 1) - xmlFreeInputStream(inputPop(ctxt)); + + if ((ctxt == NULL) || (input == NULL)) + return(-1); + + maxDepth = (ctxt->options & XML_PARSE_HUGE) ? 40 : 20; + if (ctxt->inputNr > maxDepth) { + xmlFatalErrMsg(ctxt, XML_ERR_RESOURCE_LIMIT, + "Maximum entity nesting depth exceeded"); + xmlHaltParser(ctxt); return(-1); } ret = inputPush(ctxt, input); - if (ctxt->instate == XML_PARSER_EOF) - return(-1); GROW; return(ret); } @@ -2498,12 +2603,10 @@ xmlParseCharRef(xmlParserCtxtPtr ctxt) { (NXT(2) == 'x')) { SKIP(3); GROW; - while (RAW != ';') { /* loop blocked by count */ + while ((RAW != ';') && (PARSER_STOPPED(ctxt) == 0)) { if (count++ > 20) { count = 0; GROW; - if (ctxt->instate == XML_PARSER_EOF) - return(0); } if ((RAW >= '0') && (RAW <= '9')) val = val * 16 + (CUR - '0'); @@ -2534,8 +2637,6 @@ xmlParseCharRef(xmlParserCtxtPtr ctxt) { if (count++ > 20) { count = 0; GROW; - if (ctxt->instate == XML_PARSER_EOF) - return(0); } if ((RAW >= '0') && (RAW <= '9')) val = val * 10 + (CUR - '0'); @@ -2710,288 +2811,9 @@ xmlParseStringCharRef(xmlParserCtxtPtr ctxt, const xmlChar **str) { */ void xmlParserHandlePEReference(xmlParserCtxtPtr ctxt) { - switch(ctxt->instate) { - case XML_PARSER_CDATA_SECTION: - return; - case XML_PARSER_COMMENT: - return; - case XML_PARSER_START_TAG: - return; - case XML_PARSER_END_TAG: - return; - case XML_PARSER_EOF: - xmlFatalErr(ctxt, XML_ERR_PEREF_AT_EOF, NULL); - return; - case XML_PARSER_PROLOG: - case XML_PARSER_START: - case XML_PARSER_XML_DECL: - case XML_PARSER_MISC: - xmlFatalErr(ctxt, XML_ERR_PEREF_IN_PROLOG, NULL); - return; - case XML_PARSER_ENTITY_DECL: - case XML_PARSER_CONTENT: - case XML_PARSER_ATTRIBUTE_VALUE: - case XML_PARSER_PI: - case XML_PARSER_SYSTEM_LITERAL: - case XML_PARSER_PUBLIC_LITERAL: - /* we just ignore it there */ - return; - case XML_PARSER_EPILOG: - xmlFatalErr(ctxt, XML_ERR_PEREF_IN_EPILOG, NULL); - return; - case XML_PARSER_ENTITY_VALUE: - /* - * NOTE: in the case of entity values, we don't do the - * substitution here since we need the literal - * entity value to be able to save the internal - * subset of the document. - * This will be handled by xmlStringDecodeEntities - */ - return; - case XML_PARSER_DTD: - /* - * [WFC: Well-Formedness Constraint: PEs in Internal Subset] - * In the internal DTD subset, parameter-entity references - * can occur only where markup declarations can occur, not - * within markup declarations. - * In that case this is handled in xmlParseMarkupDecl - */ - if ((ctxt->external == 0) && (ctxt->inputNr == 1)) - return; - if (IS_BLANK_CH(NXT(1)) || NXT(1) == 0) - return; - break; - case XML_PARSER_IGNORE: - return; - } - xmlParsePEReference(ctxt); } -/* - * Macro used to grow the current buffer. - * buffer##_size is expected to be a size_t - * mem_error: is expected to handle memory allocation failures - */ -#define growBuffer(buffer, n) { \ - xmlChar *tmp; \ - size_t new_size = buffer##_size * 2 + n; \ - if (new_size < buffer##_size) goto mem_error; \ - tmp = (xmlChar *) xmlRealloc(buffer, new_size); \ - if (tmp == NULL) goto mem_error; \ - buffer = tmp; \ - buffer##_size = new_size; \ -} - -/** - * xmlStringDecodeEntitiesInt: - * @ctxt: the parser context - * @str: the input string - * @len: the string length - * @what: combination of XML_SUBSTITUTE_REF and XML_SUBSTITUTE_PEREF - * @end: an end marker xmlChar, 0 if none - * @end2: an end marker xmlChar, 0 if none - * @end3: an end marker xmlChar, 0 if none - * @check: whether to perform entity checks - */ -static xmlChar * -xmlStringDecodeEntitiesInt(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, - int what, xmlChar end, xmlChar end2, xmlChar end3, - int check) { - xmlChar *buffer = NULL; - size_t buffer_size = 0; - size_t nbchars = 0; - - xmlChar *current = NULL; - xmlChar *rep = NULL; - const xmlChar *last; - xmlEntityPtr ent; - int c,l; - - if (str == NULL) - return(NULL); - last = str + len; - - if (((ctxt->depth > 40) && - ((ctxt->options & XML_PARSE_HUGE) == 0)) || - (ctxt->depth > 100)) { - xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_LOOP, - "Maximum entity nesting depth exceeded"); - return(NULL); - } - - /* - * allocate a translation buffer. - */ - buffer_size = XML_PARSER_BIG_BUFFER_SIZE; - buffer = (xmlChar *) xmlMallocAtomic(buffer_size); - if (buffer == NULL) goto mem_error; - - /* - * OK loop until we reach one of the ending char or a size limit. - * we are operating on already parsed values. - */ - if (str < last) - c = CUR_SCHAR(str, l); - else - c = 0; - while ((c != 0) && (c != end) && /* non input consuming loop */ - (c != end2) && (c != end3) && - (ctxt->instate != XML_PARSER_EOF)) { - - if (c == 0) break; - if ((c == '&') && (str[1] == '#')) { - int val = xmlParseStringCharRef(ctxt, &str); - if (val == 0) - goto int_error; - COPY_BUF(buffer, nbchars, val); - if (nbchars + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, XML_PARSER_BUFFER_SIZE); - } - } else if ((c == '&') && (what & XML_SUBSTITUTE_REF)) { - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "String decoding Entity Reference: %.30s\n", - str); - ent = xmlParseStringEntityRef(ctxt, &str); - if ((ent != NULL) && - (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { - if (ent->content != NULL) { - COPY_BUF(buffer, nbchars, ent->content[0]); - if (nbchars + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, XML_PARSER_BUFFER_SIZE); - } - } else { - xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, - "predefined entity has no content\n"); - goto int_error; - } - } else if ((ent != NULL) && (ent->content != NULL)) { - if ((check) && (xmlParserEntityCheck(ctxt, ent->length))) - goto int_error; - - if (ent->flags & XML_ENT_EXPANDING) { - xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); - xmlHaltParser(ctxt); - ent->content[0] = 0; - goto int_error; - } - - ent->flags |= XML_ENT_EXPANDING; - ctxt->depth++; - rep = xmlStringDecodeEntitiesInt(ctxt, ent->content, - ent->length, what, 0, 0, 0, check); - ctxt->depth--; - ent->flags &= ~XML_ENT_EXPANDING; - - if (rep == NULL) { - ent->content[0] = 0; - goto int_error; - } - - current = rep; - while (*current != 0) { /* non input consuming loop */ - buffer[nbchars++] = *current++; - if (nbchars + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, XML_PARSER_BUFFER_SIZE); - } - } - xmlFree(rep); - rep = NULL; - } else if (ent != NULL) { - int i = xmlStrlen(ent->name); - const xmlChar *cur = ent->name; - - buffer[nbchars++] = '&'; - if (nbchars + i + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, i + XML_PARSER_BUFFER_SIZE); - } - for (;i > 0;i--) - buffer[nbchars++] = *cur++; - buffer[nbchars++] = ';'; - } - } else if (c == '%' && (what & XML_SUBSTITUTE_PEREF)) { - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "String decoding PE Reference: %.30s\n", str); - ent = xmlParseStringPEReference(ctxt, &str); - if (ent != NULL) { - if (ent->content == NULL) { - /* - * Note: external parsed entities will not be loaded, - * it is not required for a non-validating parser to - * complete external PEReferences coming from the - * internal subset - */ - if (((ctxt->options & XML_PARSE_NOENT) != 0) || - ((ctxt->options & XML_PARSE_DTDVALID) != 0) || - (ctxt->validate != 0)) { - xmlLoadEntityContent(ctxt, ent); - } else { - xmlWarningMsg(ctxt, XML_ERR_ENTITY_PROCESSING, - "not validating will not read content for PE entity %s\n", - ent->name, NULL); - } - } - - if ((check) && (xmlParserEntityCheck(ctxt, ent->length))) - goto int_error; - - if (ent->flags & XML_ENT_EXPANDING) { - xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); - xmlHaltParser(ctxt); - if (ent->content != NULL) - ent->content[0] = 0; - goto int_error; - } - - ent->flags |= XML_ENT_EXPANDING; - ctxt->depth++; - rep = xmlStringDecodeEntitiesInt(ctxt, ent->content, - ent->length, what, 0, 0, 0, check); - ctxt->depth--; - ent->flags &= ~XML_ENT_EXPANDING; - - if (rep == NULL) { - if (ent->content != NULL) - ent->content[0] = 0; - goto int_error; - } - current = rep; - while (*current != 0) { /* non input consuming loop */ - buffer[nbchars++] = *current++; - if (nbchars + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, XML_PARSER_BUFFER_SIZE); - } - } - xmlFree(rep); - rep = NULL; - } - } else { - COPY_BUF(buffer, nbchars, c); - str += l; - if (nbchars + XML_PARSER_BUFFER_SIZE > buffer_size) { - growBuffer(buffer, XML_PARSER_BUFFER_SIZE); - } - } - if (str < last) - c = CUR_SCHAR(str, l); - else - c = 0; - } - buffer[nbchars] = 0; - return(buffer); - -mem_error: - xmlErrMemory(ctxt, NULL); -int_error: - if (rep != NULL) - xmlFree(rep); - if (buffer != NULL) - xmlFree(buffer); - return(NULL); -} - /** * xmlStringLenDecodeEntities: * @ctxt: the parser context @@ -3004,23 +2826,21 @@ xmlStringDecodeEntitiesInt(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, * * DEPRECATED: Internal function, don't use. * - * Takes a entity string content and process to do the adequate substitutions. - * - * [67] Reference ::= EntityRef | CharRef - * - * [69] PEReference ::= '%' Name ';' - * * Returns A newly allocated string with the substitution done. The caller * must deallocate it ! */ xmlChar * xmlStringLenDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, - int what, xmlChar end, xmlChar end2, - xmlChar end3) { + int what ATTRIBUTE_UNUSED, + xmlChar end, xmlChar end2, xmlChar end3) { if ((ctxt == NULL) || (str == NULL) || (len < 0)) return(NULL); - return(xmlStringDecodeEntitiesInt(ctxt, str, len, what, - end, end2, end3, 0)); + + if ((str[len] != 0) || + (end != 0) || (end2 != 0) || (end3 != 0)) + return(NULL); + + return(xmlExpandEntitiesInAttValue(ctxt, str, 0)); } /** @@ -3034,21 +2854,20 @@ xmlStringLenDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, * * DEPRECATED: Internal function, don't use. * - * Takes a entity string content and process to do the adequate substitutions. - * - * [67] Reference ::= EntityRef | CharRef - * - * [69] PEReference ::= '%' Name ';' - * * Returns A newly allocated string with the substitution done. The caller * must deallocate it ! */ xmlChar * -xmlStringDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int what, +xmlStringDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, + int what ATTRIBUTE_UNUSED, xmlChar end, xmlChar end2, xmlChar end3) { - if ((ctxt == NULL) || (str == NULL)) return(NULL); - return(xmlStringDecodeEntitiesInt(ctxt, str, xmlStrlen(str), what, - end, end2, end3, 0)); + if ((ctxt == NULL) || (str == NULL)) + return(NULL); + + if ((end != 0) || (end2 != 0) || (end3 != 0)) + return(NULL); + + return(xmlExpandEntitiesInAttValue(ctxt, str, 0)); } /************************************************************************ @@ -3071,7 +2890,7 @@ xmlStringDecodeEntities(xmlParserCtxtPtr ctxt, const xmlChar *str, int what, static int areBlanks(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, int blank_chars) { - int i, ret; + int i; xmlNodePtr lastChild; /* @@ -3101,9 +2920,25 @@ static int areBlanks(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, */ if (ctxt->node == NULL) return(0); if (ctxt->myDoc != NULL) { - ret = xmlIsMixedElement(ctxt->myDoc, ctxt->node->name); - if (ret == 0) return(1); - if (ret == 1) return(0); + xmlElementPtr elemDecl = NULL; + xmlDocPtr doc = ctxt->myDoc; + const xmlChar *prefix = NULL; + + if (ctxt->node->ns) + prefix = ctxt->node->ns->prefix; + if (doc->intSubset != NULL) + elemDecl = xmlHashLookup2(doc->intSubset->elements, ctxt->node->name, + prefix); + if ((elemDecl == NULL) && (doc->extSubset != NULL)) + elemDecl = xmlHashLookup2(doc->extSubset->elements, ctxt->node->name, + prefix); + if (elemDecl != NULL) { + if (elemDecl->etype == XML_ELEMENT_TYPE_ELEMENT) + return(1); + if ((elemDecl->etype == XML_ELEMENT_TYPE_ANY) || + (elemDecl->etype == XML_ELEMENT_TYPE_MIXED)) + return(0); + } } /* @@ -3136,7 +2971,7 @@ static int areBlanks(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, * xmlSplitQName: * @ctxt: an XML parser context * @name: an XML parser context - * @prefix: a xmlChar ** + * @prefixOut: a xmlChar ** * * parse an UTF8 encoded XML qualified name string * @@ -3151,27 +2986,21 @@ static int areBlanks(xmlParserCtxtPtr ctxt, const xmlChar *str, int len, */ xmlChar * -xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { +xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefixOut) { xmlChar buf[XML_MAX_NAMELEN + 5]; xmlChar *buffer = NULL; int len = 0; int max = XML_MAX_NAMELEN; xmlChar *ret = NULL; + xmlChar *prefix; const xmlChar *cur = name; int c; - if (prefix == NULL) return(NULL); - *prefix = NULL; + if (prefixOut == NULL) return(NULL); + *prefixOut = NULL; if (cur == NULL) return(NULL); -#ifndef XML_XML_NAMESPACE - /* xml: prefix is not really a namespace */ - if ((cur[0] == 'x') && (cur[1] == 'm') && - (cur[2] == 'l') && (cur[3] == ':')) - return(xmlStrdup(name)); -#endif - /* nasty but well=formed */ if (cur[0] == ':') return(xmlStrdup(name)); @@ -3190,7 +3019,7 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { buffer = (xmlChar *) xmlMallocAtomic(max); if (buffer == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } memcpy(buffer, buf, len); @@ -3202,7 +3031,7 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { tmp = (xmlChar *) xmlRealloc(buffer, max); if (tmp == NULL) { xmlFree(buffer); - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } buffer = tmp; @@ -3216,13 +3045,16 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { if ((c == ':') && (*cur == 0)) { if (buffer != NULL) xmlFree(buffer); - *prefix = NULL; return(xmlStrdup(name)); } - if (buffer == NULL) + if (buffer == NULL) { ret = xmlStrndup(buf, len); - else { + if (ret == NULL) { + xmlErrMemory(ctxt); + return(NULL); + } + } else { ret = buffer; buffer = NULL; max = XML_MAX_NAMELEN; @@ -3231,9 +3063,15 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { if (c == ':') { c = *cur; - *prefix = ret; + prefix = ret; if (c == 0) { - return(xmlStrndup(BAD_CAST "", 0)); + ret = xmlStrndup(BAD_CAST "", 0); + if (ret == NULL) { + xmlFree(prefix); + return(NULL); + } + *prefixOut = prefix; + return(ret); } len = 0; @@ -3268,7 +3106,8 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { buffer = (xmlChar *) xmlMallocAtomic(max); if (buffer == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); + xmlFree(prefix); return(NULL); } memcpy(buffer, buf, len); @@ -3279,7 +3118,8 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { max *= 2; tmp = (xmlChar *) xmlRealloc(buffer, max); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); + xmlFree(prefix); xmlFree(buffer); return(NULL); } @@ -3291,11 +3131,17 @@ xmlSplitQName(xmlParserCtxtPtr ctxt, const xmlChar *name, xmlChar **prefix) { buffer[len] = 0; } - if (buffer == NULL) + if (buffer == NULL) { ret = xmlStrndup(buf, len); - else { + if (ret == NULL) { + xmlFree(prefix); + return(NULL); + } + } else { ret = buffer; } + + *prefixOut = prefix; } return(ret); @@ -3394,11 +3240,9 @@ xmlIsNameChar(xmlParserCtxtPtr ctxt, int c) { return(0); } -static xmlChar * xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, - int *len, int *alloc, int normalize); - static const xmlChar * xmlParseNameComplex(xmlParserCtxtPtr ctxt) { + const xmlChar *ret; int len = 0, l; int c; int maxLength = (ctxt->options & XML_PARSE_HUGE) ? @@ -3483,8 +3327,6 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { c = CUR_CHAR(l); } } - if (ctxt->instate == XML_PARSER_EOF) - return(NULL); if (len > maxLength) { xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "Name"); return(NULL); @@ -3500,8 +3342,12 @@ xmlParseNameComplex(xmlParserCtxtPtr ctxt) { return (NULL); } if ((*ctxt->input->cur == '\n') && (ctxt->input->cur[-1] == '\r')) - return(xmlDictLookup(ctxt->dict, ctxt->input->cur - (len + 1), len)); - return(xmlDictLookup(ctxt->dict, ctxt->input->cur - len, len)); + ret = xmlDictLookup(ctxt->dict, ctxt->input->cur - (len + 1), len); + else + ret = xmlDictLookup(ctxt->dict, ctxt->input->cur - len, len); + if (ret == NULL) + xmlErrMemory(ctxt); + return(ret); } /** @@ -3532,8 +3378,6 @@ xmlParseName(xmlParserCtxtPtr ctxt) { XML_MAX_NAME_LENGTH; GROW; - if (ctxt->instate == XML_PARSER_EOF) - return(NULL); /* * Accelerator for simple ASCII names @@ -3559,7 +3403,7 @@ xmlParseName(xmlParserCtxtPtr ctxt) { ctxt->input->cur = in; ctxt->input->col += count; if (ret == NULL) - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(ret); } } @@ -3597,13 +3441,13 @@ xmlParseNCNameComplex(xmlParserCtxtPtr ctxt) { NEXTL(l); c = CUR_CHAR(l); } - if (ctxt->instate == XML_PARSER_EOF) - return(ret); if (len > maxLength) { xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NCName"); return(ret); } ret = xmlDictLookupHashed(ctxt->dict, (BASE_PTR + startPosition), len); + if (ret.name == NULL) + xmlErrMemory(ctxt); return(ret); } @@ -3660,7 +3504,7 @@ xmlParseNCName(xmlParserCtxtPtr ctxt) { ctxt->input->cur = in; ctxt->input->col += count; if (ret.name == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); } return(ret); } @@ -3687,8 +3531,6 @@ xmlParseNameAndCompare(xmlParserCtxtPtr ctxt, xmlChar const *other) { const xmlChar *ret; GROW; - if (ctxt->instate == XML_PARSER_EOF) - return(NULL); in = ctxt->input->cur; while (*in != 0 && *in == *cmp) { @@ -3731,6 +3573,7 @@ xmlParseNameAndCompare(xmlParserCtxtPtr ctxt, xmlChar const *other) { static xmlChar * xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { xmlChar buf[XML_MAX_NAMELEN + 5]; + xmlChar *ret; const xmlChar *cur = *str; int len = 0, l; int c; @@ -3760,7 +3603,7 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { buffer = (xmlChar *) xmlMallocAtomic(max); if (buffer == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } memcpy(buffer, buf, len); @@ -3771,7 +3614,7 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { max *= 2; tmp = (xmlChar *) xmlRealloc(buffer, max); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); xmlFree(buffer); return(NULL); } @@ -3796,7 +3639,10 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { return(NULL); } *str = cur; - return(xmlStrndup(buf, len)); + ret = xmlStrndup(buf, len); + if (ret == NULL) + xmlErrMemory(ctxt); + return(ret); } /** @@ -3817,6 +3663,7 @@ xmlParseStringName(xmlParserCtxtPtr ctxt, const xmlChar** str) { xmlChar * xmlParseNmtoken(xmlParserCtxtPtr ctxt) { xmlChar buf[XML_MAX_NAMELEN + 5]; + xmlChar *ret; int len = 0, l; int c; int maxLength = (ctxt->options & XML_PARSE_HUGE) ? @@ -3839,7 +3686,7 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { buffer = (xmlChar *) xmlMallocAtomic(max); if (buffer == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } memcpy(buffer, buf, len); @@ -3850,7 +3697,7 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { max *= 2; tmp = (xmlChar *) xmlRealloc(buffer, max); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); xmlFree(buffer); return(NULL); } @@ -3866,436 +3713,820 @@ xmlParseNmtoken(xmlParserCtxtPtr ctxt) { c = CUR_CHAR(l); } buffer[len] = 0; - if (ctxt->instate == XML_PARSER_EOF) { - xmlFree(buffer); - return(NULL); - } return(buffer); } } - if (ctxt->instate == XML_PARSER_EOF) - return(NULL); if (len == 0) return(NULL); if (len > maxLength) { xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "NmToken"); return(NULL); } - return(xmlStrndup(buf, len)); + ret = xmlStrndup(buf, len); + if (ret == NULL) + xmlErrMemory(ctxt); + return(ret); } /** - * xmlParseEntityValue: - * @ctxt: an XML parser context - * @orig: if non-NULL store a copy of the original entity value - * - * DEPRECATED: Internal function, don't use. - * - * parse a value for ENTITY declarations - * - * [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | - * "'" ([^%&'] | PEReference | Reference)* "'" + * xmlExpandPEsInEntityValue: + * @ctxt: parser context + * @buf: string buffer + * @str: entity value + * @length: size of entity value + * @depth: nesting depth * - * Returns the EntityValue parsed with reference substituted or NULL + * Validate an entity value and expand parameter entities. */ - -xmlChar * -xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) { - xmlChar *buf = NULL; - int len = 0; - int size = XML_PARSER_BUFFER_SIZE; +static void +xmlExpandPEsInEntityValue(xmlParserCtxtPtr ctxt, xmlSBuf *buf, + const xmlChar *str, int length, int depth) { + int maxDepth = (ctxt->options & XML_PARSE_HUGE) ? 40 : 20; + const xmlChar *end, *chunk; int c, l; - int maxLength = (ctxt->options & XML_PARSE_HUGE) ? - XML_MAX_HUGE_LENGTH : - XML_MAX_TEXT_LENGTH; - xmlChar stop; - xmlChar *ret = NULL; - const xmlChar *cur = NULL; - xmlParserInputPtr input; - if (RAW == '"') stop = '"'; - else if (RAW == '\'') stop = '\''; - else { - xmlFatalErr(ctxt, XML_ERR_ENTITY_NOT_STARTED, NULL); - return(NULL); - } - buf = (xmlChar *) xmlMallocAtomic(size); - if (buf == NULL) { - xmlErrMemory(ctxt, NULL); - return(NULL); - } + if (str == NULL) + return; - /* - * The content of the entity definition is copied in a buffer. - */ + depth += 1; + if (depth > maxDepth) { + xmlFatalErrMsg(ctxt, XML_ERR_RESOURCE_LIMIT, + "Maximum entity nesting depth exceeded"); + return; + } - ctxt->instate = XML_PARSER_ENTITY_VALUE; - input = ctxt->input; - GROW; - if (ctxt->instate == XML_PARSER_EOF) - goto error; - NEXT; - c = CUR_CHAR(l); - /* - * NOTE: 4.4.5 Included in Literal - * When a parameter entity reference appears in a literal entity - * value, ... a single or double quote character in the replacement - * text is always treated as a normal data character and will not - * terminate the literal. - * In practice it means we stop the loop only when back at parsing - * the initial entity and the quote is found - */ - while (((IS_CHAR(c)) && ((c != stop) || /* checked */ - (ctxt->input != input))) && (ctxt->instate != XML_PARSER_EOF)) { - if (len + 5 >= size) { - xmlChar *tmp; + end = str + length; + chunk = str; - size *= 2; - tmp = (xmlChar *) xmlRealloc(buf, size); - if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); - goto error; - } - buf = tmp; - } - COPY_BUF(buf, len, c); - NEXTL(l); + while ((str < end) && (!PARSER_STOPPED(ctxt))) { + c = *str; - GROW; - c = CUR_CHAR(l); - if (c == 0) { - GROW; - c = CUR_CHAR(l); - } + if (c >= 0x80) { + l = xmlUTF8MultibyteLen(ctxt, str, + "invalid character in entity value\n"); + if (l == 0) { + if (chunk < str) + xmlSBufAddString(buf, chunk, str - chunk); + xmlSBufAddReplChar(buf); + str += 1; + chunk = str; + } else { + str += l; + } + } else if (c == '&') { + if (str[1] == '#') { + if (chunk < str) + xmlSBufAddString(buf, chunk, str - chunk); - if (len > maxLength) { - xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_NOT_FINISHED, - "entity value too long\n"); - goto error; + c = xmlParseStringCharRef(ctxt, &str); + if (c == 0) + return; + + xmlSBufAddChar(buf, c); + + chunk = str; + } else { + xmlChar *name; + + /* + * General entity references are checked for + * syntactic validity. + */ + str++; + name = xmlParseStringName(ctxt, &str); + + if ((name == NULL) || (*str++ != ';')) { + xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_CHAR_ERROR, + "EntityValue: '&' forbidden except for entities " + "references\n"); + xmlFree(name); + return; + } + + xmlFree(name); + } + } else if (c == '%') { + xmlEntityPtr ent; + + if (chunk < str) + xmlSBufAddString(buf, chunk, str - chunk); + + ent = xmlParseStringPEReference(ctxt, &str); + if (ent == NULL) + return; + + if (!PARSER_EXTERNAL(ctxt)) { + xmlFatalErr(ctxt, XML_ERR_ENTITY_PE_INTERNAL, NULL); + return; + } + + if (ent->content == NULL) { + /* + * Note: external parsed entities will not be loaded, + * it is not required for a non-validating parser to + * complete external PEReferences coming from the + * internal subset + */ + if (((ctxt->options & XML_PARSE_NO_XXE) == 0) && + ((ctxt->replaceEntities) || + (ctxt->validate))) { + xmlLoadEntityContent(ctxt, ent); + } else { + xmlWarningMsg(ctxt, XML_ERR_ENTITY_PROCESSING, + "not validating will not read content for " + "PE entity %s\n", ent->name, NULL); + } + } + + /* + * TODO: Skip if ent->content is still NULL. + */ + + if (xmlParserEntityCheck(ctxt, ent->length)) + return; + + if (ent->flags & XML_ENT_EXPANDING) { + xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); + xmlHaltParser(ctxt); + return; + } + + ent->flags |= XML_ENT_EXPANDING; + xmlExpandPEsInEntityValue(ctxt, buf, ent->content, ent->length, + depth); + ent->flags &= ~XML_ENT_EXPANDING; + + chunk = str; + } else { + /* Normal ASCII char */ + if (!IS_BYTE_CHAR(c)) { + xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, + "invalid character in entity value\n"); + if (chunk < str) + xmlSBufAddString(buf, chunk, str - chunk); + xmlSBufAddReplChar(buf); + str += 1; + chunk = str; + } else { + str += 1; + } } } - buf[len] = 0; - if (ctxt->instate == XML_PARSER_EOF) - goto error; - if (c != stop) { - xmlFatalErr(ctxt, XML_ERR_ENTITY_NOT_FINISHED, NULL); - goto error; + + if (chunk < str) + xmlSBufAddString(buf, chunk, str - chunk); + + return; +} + +/** + * xmlParseEntityValue: + * @ctxt: an XML parser context + * @orig: if non-NULL store a copy of the original entity value + * + * DEPRECATED: Internal function, don't use. + * + * parse a value for ENTITY declarations + * + * [9] EntityValue ::= '"' ([^%&"] | PEReference | Reference)* '"' | + * "'" ([^%&'] | PEReference | Reference)* "'" + * + * Returns the EntityValue parsed with reference substituted or NULL + */ +xmlChar * +xmlParseEntityValue(xmlParserCtxtPtr ctxt, xmlChar **orig) { + unsigned maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_TEXT_LENGTH; + xmlSBuf buf; + const xmlChar *start; + int quote, length; + + xmlSBufInit(&buf, maxLength); + + GROW; + + quote = CUR; + if ((quote != '"') && (quote != '\'')) { + xmlFatalErr(ctxt, XML_ERR_ATTRIBUTE_NOT_STARTED, NULL); + return(NULL); } - NEXT; + CUR_PTR++; + + length = 0; /* - * Raise problem w.r.t. '&' and '%' being used in non-entities - * reference constructs. Note Charref will be handled in - * xmlStringDecodeEntities() + * Copy raw content of the entity into a buffer */ - cur = buf; - while (*cur != 0) { /* non input consuming */ - if ((*cur == '%') || ((*cur == '&') && (cur[1] != '#'))) { - xmlChar *name; - xmlChar tmp = *cur; - int nameOk = 0; + while (1) { + int c; - cur++; - name = xmlParseStringName(ctxt, &cur); - if (name != NULL) { - nameOk = 1; - xmlFree(name); - } - if ((nameOk == 0) || (*cur != ';')) { - xmlFatalErrMsgInt(ctxt, XML_ERR_ENTITY_CHAR_ERROR, - "EntityValue: '%c' forbidden except for entities references\n", - tmp); - goto error; - } - if ((tmp == '%') && (ctxt->inSubset == 1) && - (ctxt->inputNr == 1)) { - xmlFatalErr(ctxt, XML_ERR_ENTITY_PE_INTERNAL, NULL); - goto error; - } - if (*cur == 0) - break; - } - cur++; + if (PARSER_STOPPED(ctxt)) + goto error; + + if (CUR_PTR >= ctxt->input->end) { + xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_NOT_FINISHED, NULL); + goto error; + } + + c = CUR; + + if (c == 0) { + xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, + "invalid character in entity value\n"); + goto error; + } + if (c == quote) + break; + NEXTL(1); + length += 1; + + /* + * TODO: Check growth threshold + */ + if (ctxt->input->end - CUR_PTR < 10) + GROW; } - /* - * Then PEReference entities are substituted. - * - * NOTE: 4.4.7 Bypassed - * When a general entity reference appears in the EntityValue in - * an entity declaration, it is bypassed and left as is. - * so XML_SUBSTITUTE_REF is not set here. - */ - ++ctxt->depth; - ret = xmlStringDecodeEntitiesInt(ctxt, buf, len, XML_SUBSTITUTE_PEREF, - 0, 0, 0, /* check */ 1); - --ctxt->depth; + start = CUR_PTR - length; if (orig != NULL) { - *orig = buf; - buf = NULL; + *orig = xmlStrndup(start, length); + if (*orig == NULL) + xmlErrMemory(ctxt); } + xmlExpandPEsInEntityValue(ctxt, &buf, start, length, ctxt->inputNr); + + NEXTL(1); + + return(xmlSBufFinish(&buf, NULL, ctxt, "entity length too long")); + error: - if (buf != NULL) - xmlFree(buf); - return(ret); + xmlSBufCleanup(&buf, ctxt, "entity length too long"); + return(NULL); } /** - * xmlParseAttValueComplex: - * @ctxt: an XML parser context - * @len: the resulting attribute len - * @normalize: whether to apply the inner normalization - * - * parse a value for an attribute, this is the fallback function - * of xmlParseAttValue() when the attribute parsing requires handling - * of non-ASCII characters, or normalization compaction. + * xmlCheckEntityInAttValue: + * @ctxt: parser context + * @pent: entity + * @depth: nesting depth * - * Returns the AttValue parsed or NULL. The value has to be freed by the caller. + * Check an entity reference in an attribute value for validity + * without expanding it. */ -static xmlChar * -xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { - xmlChar limit = 0; - xmlChar *buf = NULL; - xmlChar *rep = NULL; - size_t len = 0; - size_t buf_size = 0; - size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? - XML_MAX_HUGE_LENGTH : - XML_MAX_TEXT_LENGTH; - int c, l, in_space = 0; - xmlChar *current = NULL; - xmlEntityPtr ent; +static void +xmlCheckEntityInAttValue(xmlParserCtxtPtr ctxt, xmlEntityPtr pent, int depth) { + int maxDepth = (ctxt->options & XML_PARSE_HUGE) ? 40 : 20; + const xmlChar *str; + unsigned long expandedSize = pent->length; + int c, flags; + + depth += 1; + if (depth > maxDepth) { + xmlFatalErrMsg(ctxt, XML_ERR_RESOURCE_LIMIT, + "Maximum entity nesting depth exceeded"); + return; + } - if (NXT(0) == '"') { - ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE; - limit = '"'; - NEXT; - } else if (NXT(0) == '\'') { - limit = '\''; - ctxt->instate = XML_PARSER_ATTRIBUTE_VALUE; - NEXT; - } else { - xmlFatalErr(ctxt, XML_ERR_ATTRIBUTE_NOT_STARTED, NULL); - return(NULL); + if (pent->flags & XML_ENT_EXPANDING) { + xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); + xmlHaltParser(ctxt); + return; } /* - * allocate a translation buffer. + * If we're parsing a default attribute value in DTD content, + * the entity might reference other entities which weren't + * defined yet, so the check isn't reliable. */ - buf_size = XML_PARSER_BUFFER_SIZE; - buf = (xmlChar *) xmlMallocAtomic(buf_size); - if (buf == NULL) goto mem_error; + if (ctxt->inSubset == 0) + flags = XML_ENT_CHECKED | XML_ENT_VALIDATED; + else + flags = XML_ENT_VALIDATED; + + str = pent->content; + if (str == NULL) + goto done; /* - * OK loop until we reach one of the ending char or a size limit. + * Note that entity values are already validated. We only check + * for illegal less-than signs and compute the expanded size + * of the entity. No special handling for multi-byte characters + * is needed. */ - c = CUR_CHAR(l); - while (((NXT(0) != limit) && /* checked */ - (IS_CHAR(c)) && (c != '<')) && - (ctxt->instate != XML_PARSER_EOF)) { - if (c == '&') { - in_space = 0; - if (NXT(1) == '#') { - int val = xmlParseCharRef(ctxt); - - if (val == '&') { - if (ctxt->replaceEntities) { - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - buf[len++] = '&'; - } else { - /* - * The reparsing will be done in xmlStringGetNodeList() - * called by the attribute() function in SAX.c - */ - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - buf[len++] = '&'; - buf[len++] = '#'; - buf[len++] = '3'; - buf[len++] = '8'; - buf[len++] = ';'; - } - } else if (val != 0) { - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - len += xmlCopyChar(0, &buf[len], val); - } - } else { - ent = xmlParseEntityRef(ctxt); - if ((ent != NULL) && - (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - if ((ctxt->replaceEntities == 0) && - (ent->content[0] == '&')) { - buf[len++] = '&'; - buf[len++] = '#'; - buf[len++] = '3'; - buf[len++] = '8'; - buf[len++] = ';'; - } else { - buf[len++] = ent->content[0]; - } - } else if ((ent != NULL) && - (ctxt->replaceEntities != 0)) { - if (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) { - if (xmlParserEntityCheck(ctxt, ent->length)) - goto error; + while (!PARSER_STOPPED(ctxt)) { + c = *str; - ++ctxt->depth; - rep = xmlStringDecodeEntitiesInt(ctxt, ent->content, - ent->length, XML_SUBSTITUTE_REF, 0, 0, 0, - /* check */ 1); - --ctxt->depth; - if (rep != NULL) { - current = rep; - while (*current != 0) { /* non input consuming */ - if ((*current == 0xD) || (*current == 0xA) || - (*current == 0x9)) { - buf[len++] = 0x20; - current++; - } else - buf[len++] = *current++; - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - } - xmlFree(rep); - rep = NULL; - } - } else { - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } - if (ent->content != NULL) - buf[len++] = ent->content[0]; - } - } else if (ent != NULL) { - int i = xmlStrlen(ent->name); - const xmlChar *cur = ent->name; + if (c != '&') { + if (c == 0) + break; - /* - * We also check for recursion and amplification - * when entities are not substituted. They're - * often expanded later. - */ - if ((ent->etype != XML_INTERNAL_PREDEFINED_ENTITY) && - (ent->content != NULL)) { - if ((ent->flags & XML_ENT_CHECKED) == 0) { - unsigned long oldCopy = ctxt->sizeentcopy; - - ctxt->sizeentcopy = ent->length; - - ++ctxt->depth; - rep = xmlStringDecodeEntitiesInt(ctxt, - ent->content, ent->length, - XML_SUBSTITUTE_REF, 0, 0, 0, - /* check */ 1); - --ctxt->depth; - - /* - * If we're parsing DTD content, the entity - * might reference other entities which - * weren't defined yet, so the check isn't - * reliable. - */ - if (ctxt->inSubset == 0) { - ent->flags |= XML_ENT_CHECKED; - ent->expandedSize = ctxt->sizeentcopy; - } + if (c == '<') + xmlFatalErrMsgStr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, + "'<' in entity '%s' is not allowed in attributes " + "values\n", pent->name); - if (rep != NULL) { - xmlFree(rep); - rep = NULL; - } else { - ent->content[0] = 0; - } + str += 1; + } else if (str[1] == '#') { + int val; - if (xmlParserEntityCheck(ctxt, oldCopy)) - goto error; - } else { - if (xmlParserEntityCheck(ctxt, ent->expandedSize)) - goto error; - } - } + val = xmlParseStringCharRef(ctxt, &str); + if (val == 0) { + pent->content[0] = 0; + break; + } + } else { + xmlChar *name; + xmlEntityPtr ent; - /* - * Just output the reference - */ - buf[len++] = '&'; - while (len + i + 10 > buf_size) { - growBuffer(buf, i + 10); - } - for (;i > 0;i--) - buf[len++] = *cur++; - buf[len++] = ';'; - } - } + name = xmlParseStringEntityRef(ctxt, &str); + if (name == NULL) { + pent->content[0] = 0; + break; + } + + ent = xmlLookupGeneralEntity(ctxt, name, /* inAttr */ 1); + xmlFree(name); + + if ((ent != NULL) && + (ent->etype != XML_INTERNAL_PREDEFINED_ENTITY)) { + if ((ent->flags & flags) != flags) { + pent->flags |= XML_ENT_EXPANDING; + xmlCheckEntityInAttValue(ctxt, ent, depth); + pent->flags &= ~XML_ENT_EXPANDING; + } + + xmlSaturatedAdd(&expandedSize, ent->expandedSize); + xmlSaturatedAdd(&expandedSize, XML_ENT_FIXED_COST); + } + } + } + +done: + if (ctxt->inSubset == 0) + pent->expandedSize = expandedSize; + + pent->flags |= flags; +} + +/** + * xmlExpandEntityInAttValue: + * @ctxt: parser context + * @buf: string buffer + * @str: entity or attribute value + * @pent: entity for entity value, NULL for attribute values + * @normalize: whether to collapse whitespace + * @inSpace: whitespace state + * @depth: nesting depth + * @check: whether to check for amplification + * + * Expand general entity references in an entity or attribute value. + * Perform attribute value normalization. + */ +static void +xmlExpandEntityInAttValue(xmlParserCtxtPtr ctxt, xmlSBuf *buf, + const xmlChar *str, xmlEntityPtr pent, int normalize, + int *inSpace, int depth, int check) { + int maxDepth = (ctxt->options & XML_PARSE_HUGE) ? 40 : 20; + int c, chunkSize; + + if (str == NULL) + return; + + depth += 1; + if (depth > maxDepth) { + xmlFatalErrMsg(ctxt, XML_ERR_RESOURCE_LIMIT, + "Maximum entity nesting depth exceeded"); + return; + } + + if (pent != NULL) { + if (pent->flags & XML_ENT_EXPANDING) { + xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); + xmlHaltParser(ctxt); + return; + } + + if (check) { + if (xmlParserEntityCheck(ctxt, pent->length)) + return; + } + } + + chunkSize = 0; + + /* + * Note that entity values are already validated. No special + * handling for multi-byte characters is needed. + */ + while (!PARSER_STOPPED(ctxt)) { + c = *str; + + if (c != '&') { + if (c == 0) + break; + + /* + * If this function is called without an entity, it is used to + * expand entities in an attribute content where less-than was + * already unscaped and is allowed. + */ + if ((pent != NULL) && (c == '<')) { + xmlFatalErrMsgStr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, + "'<' in entity '%s' is not allowed in attributes " + "values\n", pent->name); + break; + } + + if (c <= 0x20) { + if ((normalize) && (*inSpace)) { + /* Skip char */ + if (chunkSize > 0) { + xmlSBufAddString(buf, str - chunkSize, chunkSize); + chunkSize = 0; + } + } else if (c < 0x20) { + if (chunkSize > 0) { + xmlSBufAddString(buf, str - chunkSize, chunkSize); + chunkSize = 0; + } + + xmlSBufAddCString(buf, " ", 1); + } else { + chunkSize += 1; + } + + *inSpace = 1; + } else { + chunkSize += 1; + *inSpace = 0; + } + + str += 1; + } else if (str[1] == '#') { + int val; + + if (chunkSize > 0) { + xmlSBufAddString(buf, str - chunkSize, chunkSize); + chunkSize = 0; + } + + val = xmlParseStringCharRef(ctxt, &str); + if (val == 0) { + if (pent != NULL) + pent->content[0] = 0; + break; + } + + if (val == ' ') { + if ((!normalize) || (!*inSpace)) + xmlSBufAddCString(buf, " ", 1); + *inSpace = 1; + } else { + xmlSBufAddChar(buf, val); + *inSpace = 0; + } } else { - if ((c == 0x20) || (c == 0xD) || (c == 0xA) || (c == 0x9)) { - if ((len != 0) || (!normalize)) { - if ((!normalize) || (!in_space)) { - COPY_BUF(buf, len, 0x20); - while (len + 10 > buf_size) { - growBuffer(buf, 10); - } - } - in_space = 1; - } - } else { - in_space = 0; - COPY_BUF(buf, len, c); - if (len + 10 > buf_size) { - growBuffer(buf, 10); - } + xmlChar *name; + xmlEntityPtr ent; + + if (chunkSize > 0) { + xmlSBufAddString(buf, str - chunkSize, chunkSize); + chunkSize = 0; + } + + name = xmlParseStringEntityRef(ctxt, &str); + if (name == NULL) { + if (pent != NULL) + pent->content[0] = 0; + break; + } + + ent = xmlLookupGeneralEntity(ctxt, name, /* inAttr */ 1); + xmlFree(name); + + if ((ent != NULL) && + (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { + if (ent->content == NULL) { + xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, + "predefined entity has no content\n"); + break; + } + + xmlSBufAddString(buf, ent->content, ent->length); + + *inSpace = 0; + } else if ((ent != NULL) && (ent->content != NULL)) { + if (pent != NULL) + pent->flags |= XML_ENT_EXPANDING; + xmlExpandEntityInAttValue(ctxt, buf, ent->content, ent, + normalize, inSpace, depth, check); + if (pent != NULL) + pent->flags &= ~XML_ENT_EXPANDING; } - NEXTL(l); - } - GROW; - c = CUR_CHAR(l); - if (len > maxLength) { - xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, - "AttValue length too long\n"); - goto mem_error; } } - if (ctxt->instate == XML_PARSER_EOF) - goto error; - if ((in_space) && (normalize)) { - while ((len > 0) && (buf[len - 1] == 0x20)) len--; + if (chunkSize > 0) + xmlSBufAddString(buf, str - chunkSize, chunkSize); + + return; +} + +/** + * xmlExpandEntitiesInAttValue: + * @ctxt: parser context + * @str: entity or attribute value + * @normalize: whether to collapse whitespace + * + * Expand general entity references in an entity or attribute value. + * Perform attribute value normalization. + * + * Returns the expanded attribtue value. + */ +xmlChar * +xmlExpandEntitiesInAttValue(xmlParserCtxtPtr ctxt, const xmlChar *str, + int normalize) { + unsigned maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_TEXT_LENGTH; + xmlSBuf buf; + int inSpace = 1; + + xmlSBufInit(&buf, maxLength); + + xmlExpandEntityInAttValue(ctxt, &buf, str, NULL, normalize, &inSpace, + ctxt->inputNr, /* check */ 0); + + if ((normalize) && (inSpace) && (buf.size > 0)) + buf.size--; + + return(xmlSBufFinish(&buf, NULL, ctxt, "AttValue length too long")); +} + +/** + * xmlParseAttValueInternal: + * @ctxt: an XML parser context + * @len: attribute len result + * @alloc: whether the attribute was reallocated as a new string + * @normalize: if 1 then further non-CDATA normalization must be done + * + * parse a value for an attribute. + * NOTE: if no normalization is needed, the routine will return pointers + * directly from the data buffer. + * + * 3.3.3 Attribute-Value Normalization: + * Before the value of an attribute is passed to the application or + * checked for validity, the XML processor must normalize it as follows: + * - a character reference is processed by appending the referenced + * character to the attribute value + * - an entity reference is processed by recursively processing the + * replacement text of the entity + * - a whitespace character (#x20, #xD, #xA, #x9) is processed by + * appending #x20 to the normalized value, except that only a single + * #x20 is appended for a "#xD#xA" sequence that is part of an external + * parsed entity or the literal entity value of an internal parsed entity + * - other characters are processed by appending them to the normalized value + * If the declared value 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. + * All attributes for which no declaration has been read should be treated + * by a non-validating parser as if declared CDATA. + * + * Returns the AttValue parsed or NULL. The value has to be freed by the + * caller if it was copied, this can be detected by val[*len] == 0. + */ +static xmlChar * +xmlParseAttValueInternal(xmlParserCtxtPtr ctxt, int *attlen, int *alloc, + int normalize, int isNamespace) { + unsigned maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_TEXT_LENGTH; + xmlSBuf buf; + xmlChar *ret; + int c, l, quote, flags, chunkSize; + int inSpace = 1; + int replaceEntities; + + /* Always expand namespace URIs */ + replaceEntities = (ctxt->replaceEntities) || (isNamespace); + + xmlSBufInit(&buf, maxLength); + + GROW; + + quote = CUR; + if ((quote != '"') && (quote != '\'')) { + xmlFatalErr(ctxt, XML_ERR_ATTRIBUTE_NOT_STARTED, NULL); + return(NULL); } - buf[len] = 0; - if (RAW == '<') { - xmlFatalErr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, NULL); - } else if (RAW != limit) { - if ((c != 0) && (!IS_CHAR(c))) { - xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, - "invalid character in attribute value\n"); - } else { - xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, - "AttValue: ' expected\n"); + NEXTL(1); + + if (ctxt->inSubset == 0) + flags = XML_ENT_CHECKED | XML_ENT_VALIDATED; + else + flags = XML_ENT_VALIDATED; + + inSpace = 1; + chunkSize = 0; + + while (1) { + if (PARSER_STOPPED(ctxt)) + goto error; + + if (CUR_PTR >= ctxt->input->end) { + xmlFatalErrMsg(ctxt, XML_ERR_ATTRIBUTE_NOT_FINISHED, + "AttValue: ' expected\n"); + goto error; } - } else - NEXT; - if (attlen != NULL) *attlen = len; - return(buf); + /* + * TODO: Check growth threshold + */ + if (ctxt->input->end - CUR_PTR < 10) + GROW; + + c = CUR; + + if (c >= 0x80) { + l = xmlUTF8MultibyteLen(ctxt, CUR_PTR, + "invalid character in attribute value\n"); + if (l == 0) { + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + xmlSBufAddReplChar(&buf); + NEXTL(1); + } else { + chunkSize += l; + NEXTL(l); + } + + inSpace = 0; + } else if (c != '&') { + if (c > 0x20) { + if (c == quote) + break; + + if (c == '<') + xmlFatalErr(ctxt, XML_ERR_LT_IN_ATTRIBUTE, NULL); + + chunkSize += 1; + inSpace = 0; + } else if (!IS_BYTE_CHAR(c)) { + xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, + "invalid character in attribute value\n"); + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + xmlSBufAddReplChar(&buf); + inSpace = 0; + } else { + /* Whitespace */ + if ((normalize) && (inSpace)) { + /* Skip char */ + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + } else if (c < 0x20) { + /* Convert to space */ + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + + xmlSBufAddCString(&buf, " ", 1); + } else { + chunkSize += 1; + } + + inSpace = 1; + + if ((c == 0xD) && (NXT(1) == 0xA)) + CUR_PTR++; + } + + NEXTL(1); + } else if (NXT(1) == '#') { + int val; + + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + + val = xmlParseCharRef(ctxt); + if (val == 0) + goto error; + + if ((val == '&') && (!replaceEntities)) { + /* + * The reparsing will be done in xmlStringGetNodeList() + * called by the attribute() function in SAX.c + */ + xmlSBufAddCString(&buf, "&", 5); + inSpace = 0; + } else if (val == ' ') { + if ((!normalize) || (!inSpace)) + xmlSBufAddCString(&buf, " ", 1); + inSpace = 1; + } else { + xmlSBufAddChar(&buf, val); + inSpace = 0; + } + } else { + const xmlChar *name; + xmlEntityPtr ent; + + if (chunkSize > 0) { + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + chunkSize = 0; + } + + name = xmlParseEntityRefInternal(ctxt); + if (name == NULL) { + /* + * Probably a literal '&' which wasn't escaped. + * TODO: Handle gracefully in recovery mode. + */ + continue; + } + + ent = xmlLookupGeneralEntity(ctxt, name, /* isAttr */ 1); + if (ent == NULL) + continue; + + if (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY) { + if ((ent->content[0] == '&') && (!replaceEntities)) + xmlSBufAddCString(&buf, "&", 5); + else + xmlSBufAddString(&buf, ent->content, ent->length); + inSpace = 0; + } else if (replaceEntities) { + xmlExpandEntityInAttValue(ctxt, &buf, ent->content, ent, + normalize, &inSpace, ctxt->inputNr, + /* check */ 1); + } else { + if ((ent->flags & flags) != flags) + xmlCheckEntityInAttValue(ctxt, ent, ctxt->inputNr); + + if (xmlParserEntityCheck(ctxt, ent->expandedSize)) { + ent->content[0] = 0; + goto error; + } + + /* + * Just output the reference + */ + xmlSBufAddCString(&buf, "&", 1); + xmlSBufAddString(&buf, ent->name, xmlStrlen(ent->name)); + xmlSBufAddCString(&buf, ";", 1); + + inSpace = 0; + } + } + } + + if ((buf.mem == NULL) && (alloc != NULL)) { + ret = (xmlChar *) CUR_PTR - chunkSize; + + if (attlen != NULL) + *attlen = chunkSize; + if ((normalize) && (inSpace) && (chunkSize > 0)) + *attlen -= 1; + *alloc = 0; + + /* Report potential error */ + xmlSBufCleanup(&buf, ctxt, "AttValue length too long"); + } else { + if (chunkSize > 0) + xmlSBufAddString(&buf, CUR_PTR - chunkSize, chunkSize); + + if ((normalize) && (inSpace) && (buf.size > 0)) + buf.size--; + + ret = xmlSBufFinish(&buf, attlen, ctxt, "AttValue length too long"); + + if (ret != NULL) { + if (attlen != NULL) + *attlen = buf.size; + if (alloc != NULL) + *alloc = 1; + } + } + + NEXTL(1); + + return(ret); -mem_error: - xmlErrMemory(ctxt, NULL); error: - if (buf != NULL) - xmlFree(buf); - if (rep != NULL) - xmlFree(rep); + xmlSBufCleanup(&buf, ctxt, "AttValue length too long"); return(NULL); } @@ -4338,7 +4569,7 @@ xmlParseAttValueComplex(xmlParserCtxtPtr ctxt, int *attlen, int normalize) { xmlChar * xmlParseAttValue(xmlParserCtxtPtr ctxt) { if ((ctxt == NULL) || (ctxt->input == NULL)) return(NULL); - return(xmlParseAttValueInternal(ctxt, NULL, NULL, 0)); + return(xmlParseAttValueInternal(ctxt, NULL, NULL, 0, 0)); } /** @@ -4364,7 +4595,6 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { XML_MAX_TEXT_LENGTH : XML_MAX_NAME_LENGTH; xmlChar stop; - int state = ctxt->instate; if (RAW == '"') { NEXT; @@ -4379,10 +4609,9 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { buf = (xmlChar *) xmlMallocAtomic(size); if (buf == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } - ctxt->instate = XML_PARSER_SYSTEM_LITERAL; cur = CUR_CHAR(l); while ((IS_CHAR(cur)) && (cur != stop)) { /* checked */ if (len + 5 >= size) { @@ -4392,8 +4621,7 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { tmp = (xmlChar *) xmlRealloc(buf, size); if (tmp == NULL) { xmlFree(buf); - xmlErrMemory(ctxt, NULL); - ctxt->instate = (xmlParserInputState) state; + xmlErrMemory(ctxt); return(NULL); } buf = tmp; @@ -4402,18 +4630,12 @@ xmlParseSystemLiteral(xmlParserCtxtPtr ctxt) { if (len > maxLength) { xmlFatalErr(ctxt, XML_ERR_NAME_TOO_LONG, "SystemLiteral"); xmlFree(buf); - ctxt->instate = (xmlParserInputState) state; return(NULL); } NEXTL(l); cur = CUR_CHAR(l); } buf[len] = 0; - if (ctxt->instate == XML_PARSER_EOF) { - xmlFree(buf); - return(NULL); - } - ctxt->instate = (xmlParserInputState) state; if (!IS_CHAR(cur)) { xmlFatalErr(ctxt, XML_ERR_LITERAL_NOT_FINISHED, NULL); } else { @@ -4445,7 +4667,6 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { XML_MAX_NAME_LENGTH; xmlChar cur; xmlChar stop; - xmlParserInputState oldstate = ctxt->instate; if (RAW == '"') { NEXT; @@ -4459,19 +4680,19 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { } buf = (xmlChar *) xmlMallocAtomic(size); if (buf == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return(NULL); } - ctxt->instate = XML_PARSER_PUBLIC_LITERAL; cur = CUR; - while ((IS_PUBIDCHAR_CH(cur)) && (cur != stop)) { /* checked */ + while ((IS_PUBIDCHAR_CH(cur)) && (cur != stop) && + (PARSER_STOPPED(ctxt) == 0)) { /* checked */ if (len + 1 >= size) { xmlChar *tmp; size *= 2; tmp = (xmlChar *) xmlRealloc(buf, size); if (tmp == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); xmlFree(buf); return(NULL); } @@ -4487,16 +4708,11 @@ xmlParsePubidLiteral(xmlParserCtxtPtr ctxt) { cur = CUR; } buf[len] = 0; - if (ctxt->instate == XML_PARSER_EOF) { - xmlFree(buf); - return(NULL); - } if (cur != stop) { xmlFatalErr(ctxt, XML_ERR_LITERAL_NOT_FINISHED, NULL); } else { NEXTL(1); } - ctxt->instate = oldstate; return(buf); } @@ -4627,8 +4843,7 @@ xmlParseCharDataInternal(xmlParserCtxtPtr ctxt, int partial) { if (*in == ']') { if ((in[1] == ']') && (in[2] == '>')) { xmlFatalErr(ctxt, XML_ERR_MISPLACED_CDATA_END, NULL); - if (ctxt->instate != XML_PARSER_EOF) - ctxt->input->cur = in + 1; + ctxt->input->cur = in + 1; return; } in++; @@ -4666,8 +4881,6 @@ xmlParseCharDataInternal(xmlParserCtxtPtr ctxt, int partial) { line = ctxt->input->line; col = ctxt->input->col; } - if (ctxt->instate == XML_PARSER_EOF) - return; } ctxt->input->cur = in; if (*in == 0xD) { @@ -4688,8 +4901,6 @@ xmlParseCharDataInternal(xmlParserCtxtPtr ctxt, int partial) { } SHRINK; GROW; - if (ctxt->instate == XML_PARSER_EOF) - return; in = ctxt->input->cur; } while (((*in >= 0x20) && (*in <= 0x7F)) || (*in == 0x09) || (*in == 0x0a)); @@ -4746,15 +4957,10 @@ xmlParseCharDataComplex(xmlParserCtxtPtr ctxt, int partial) { } } nbchar = 0; - /* something really bad happened in the SAX callback */ - if (ctxt->instate != XML_PARSER_CONTENT) - return; SHRINK; } cur = CUR_CHAR(l); } - if (ctxt->instate == XML_PARSER_EOF) - return; if (nbchar != 0) { buf[nbchar] = 0; /* @@ -4776,9 +4982,8 @@ xmlParseCharDataComplex(xmlParserCtxtPtr ctxt, int partial) { /* * cur == 0 can mean * - * - XML_PARSER_EOF or memory error. This is checked above. - * - An actual 0 character. * - End of buffer. + * - An actual 0 character. * - An incomplete UTF-8 sequence. This is allowed if partial is set. */ if (ctxt->input->cur < ctxt->input->end) { @@ -4907,16 +5112,13 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? XML_MAX_HUGE_LENGTH : XML_MAX_TEXT_LENGTH; - int inputid; - - inputid = ctxt->input->id; if (buf == NULL) { len = 0; size = XML_PARSER_BUFFER_SIZE; buf = (xmlChar *) xmlMallocAtomic(size); if (buf == NULL) { - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return; } } @@ -4959,7 +5161,7 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, new_buf = (xmlChar *) xmlRealloc(buf, new_size); if (new_buf == NULL) { xmlFree (buf); - xmlErrMemory(ctxt, NULL); + xmlErrMemory(ctxt); return; } buf = new_buf; @@ -4983,10 +5185,6 @@ xmlParseCommentComplex(xmlParserCtxtPtr ctxt, xmlChar *buf, } buf[len] = 0; - if (ctxt->instate == XML_PARSER_EOF) { - xmlFree(buf); - return; - } if (cur == 0) { xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED, "Comment not terminated \n", 3))) goto done; xmlParseComment(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; ctxt->instate = XML_PARSER_CONTENT; break; } @@ -11871,8 +11304,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { else xmlParseEndTag1(ctxt, 0); #endif /* LIBXML_SAX1_ENABLED */ - if (ctxt->instate == XML_PARSER_EOF) - goto done; if (ctxt->nameNr == 0) { ctxt->instate = XML_PARSER_EPILOG; } else { @@ -11924,8 +11355,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { ctxt->sax->characters(ctxt->userData, ctxt->input->cur, tmp); } - if (ctxt->instate == XML_PARSER_EOF) - goto done; SKIPL(tmp); } else { int base = term - CUR_PTR; @@ -11959,8 +11388,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { ctxt->sax->characters(ctxt->userData, ctxt->input->cur, base); } - if (ctxt->instate == XML_PARSER_EOF) - goto done; SKIPL(base + 3); ctxt->instate = XML_PARSER_CONTENT; } @@ -11982,8 +11409,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { (!xmlParseLookupString(ctxt, 2, "?>", 2))) goto done; xmlParsePI(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; break; } else if (next == '!') { if ((!terminate) && (avail < 3)) @@ -11997,8 +11422,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { (!xmlParseLookupString(ctxt, 4, "-->", 3))) goto done; xmlParseComment(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; break; } } else if (ctxt->instate == XML_PARSER_MISC) { @@ -12015,8 +11438,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { goto done; ctxt->inSubset = 1; xmlParseDocTypeDecl(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; if (RAW == '[') { ctxt->instate = XML_PARSER_DTD; } else { @@ -12034,8 +11455,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { ctxt->extSubURI); ctxt->inSubset = 0; xmlCleanSpecialAttr(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; ctxt->instate = XML_PARSER_PROLOG; } break; @@ -12048,8 +11467,7 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { if (ctxt->errNo == XML_ERR_OK) xmlFatalErr(ctxt, XML_ERR_DOCUMENT_END, NULL); ctxt->instate = XML_PARSER_EOF; - if ((ctxt->sax) && (ctxt->sax->endDocument != NULL)) - ctxt->sax->endDocument(ctxt->userData); + xmlFinishDocument(ctxt); } else { ctxt->instate = XML_PARSER_START_TAG; } @@ -12058,8 +11476,6 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { if ((!terminate) && (!xmlParseLookupInternalSubset(ctxt))) goto done; xmlParseInternalSubset(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; ctxt->inSubset = 2; if ((ctxt->sax != NULL) && (!ctxt->disableSAX) && (ctxt->sax->externalSubset != NULL)) @@ -12067,13 +11483,11 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { ctxt->extSubSystem, ctxt->extSubURI); ctxt->inSubset = 0; xmlCleanSpecialAttr(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - goto done; ctxt->instate = XML_PARSER_PROLOG; break; } default: - xmlGenericError(xmlGenericErrorContext, + xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, "PP: internal error\n"); ctxt->instate = XML_PARSER_EOF; break; @@ -12082,19 +11496,10 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { done: return(ret); encoding_error: - if (ctxt->input->end - ctxt->input->cur < 4) { - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n", - NULL, NULL); - } else { - char buffer[150]; - - snprintf(buffer, 149, "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n", - ctxt->input->cur[0], ctxt->input->cur[1], - ctxt->input->cur[2], ctxt->input->cur[3]); - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n%s", - BAD_CAST buffer, NULL); + /* Only report the first error */ + if ((ctxt->input->flags & XML_INPUT_ENCODING_ERROR) == 0) { + xmlCtxtErrIO(ctxt, XML_ERR_INVALID_ENCODING, NULL); + ctxt->input->flags |= XML_INPUT_ENCODING_ERROR; } return(0); } @@ -12102,31 +11507,42 @@ xmlParseTryOrFinish(xmlParserCtxtPtr ctxt, int terminate) { /** * xmlParseChunk: * @ctxt: an XML parser context - * @chunk: an char array - * @size: the size in byte of the chunk + * @chunk: chunk of memory + * @size: size of chunk in bytes * @terminate: last chunk indicator * - * Parse a Chunk of memory + * Parse a chunk of memory in push parser mode. + * + * Assumes that the parser context was initialized with + * xmlCreatePushParserCtxt. * - * Returns zero if no error, the xmlParserErrors otherwise. + * The last chunk, which will often be empty, must be marked with + * the @terminate flag. With the default SAX callbacks, the resulting + * document will be available in ctxt->myDoc. This pointer will not + * be freed by the library. + * + * If the document isn't well-formed, ctxt->myDoc is set to NULL. + * The push parser doesn't support recovery mode. + * + * Returns an xmlParserErrors code (0 on success). */ int xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, int terminate) { + size_t curBase; + size_t maxLength; int end_in_lf = 0; - if (ctxt == NULL) - return(XML_ERR_INTERNAL_ERROR); - if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1)) + if ((ctxt == NULL) || (size < 0)) + return(XML_ERR_ARGUMENT); + if (ctxt->disableSAX != 0) return(ctxt->errNo); - if (ctxt->instate == XML_PARSER_EOF) - return(-1); if (ctxt->input == NULL) - return(-1); + return(XML_ERR_INTERNAL_ERROR); - ctxt->progressive = 1; + ctxt->input->flags |= XML_INPUT_PROGRESSIVE; if (ctxt->instate == XML_PARSER_START) - xmlDetectSAX2(ctxt); + xmlCtxtInitializeLate(ctxt); if ((size > 0) && (chunk != NULL) && (!terminate) && (chunk[size - 1] == '\r')) { end_in_lf = 1; @@ -12134,30 +11550,31 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, } if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) && - (ctxt->input->buf != NULL) && (ctxt->instate != XML_PARSER_EOF)) { + (ctxt->input->buf != NULL)) { size_t pos = ctxt->input->cur - ctxt->input->base; int res; res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk); xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos); if (res < 0) { - xmlFatalErr(ctxt, ctxt->input->buf->error, NULL); + xmlCtxtErrIO(ctxt, ctxt->input->buf->error, NULL); xmlHaltParser(ctxt); return(ctxt->errNo); } } xmlParseTryOrFinish(ctxt, terminate); - if (ctxt->instate == XML_PARSER_EOF) - return(ctxt->errNo); - if ((ctxt->input != NULL) && - (((ctxt->input->end - ctxt->input->cur) > XML_MAX_LOOKUP_LIMIT) || - ((ctxt->input->cur - ctxt->input->base) > XML_MAX_LOOKUP_LIMIT)) && - ((ctxt->options & XML_PARSE_HUGE) == 0)) { - xmlFatalErr(ctxt, XML_ERR_INTERNAL_ERROR, "Huge input lookup"); + curBase = ctxt->input->cur - ctxt->input->base; + maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_LOOKUP_LIMIT; + if (curBase > maxLength) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, + "Buffer size limit exceeded, try XML_PARSE_HUGE\n"); xmlHaltParser(ctxt); } + if ((ctxt->errNo != XML_ERR_OK) && (ctxt->disableSAX == 1)) return(ctxt->errNo); @@ -12169,7 +11586,7 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, res = xmlParserInputBufferPush(ctxt->input->buf, 1, "\r"); xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos); if (res < 0) { - xmlFatalErr(ctxt, ctxt->input->buf->error, NULL); + xmlCtxtErrIO(ctxt, ctxt->input->buf->error, NULL); xmlHaltParser(ctxt); return(ctxt->errNo); } @@ -12194,15 +11611,15 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, } } else if ((ctxt->input->buf != NULL) && (ctxt->input->buf->encoder != NULL) && + (ctxt->input->buf->error == 0) && (!xmlBufIsEmpty(ctxt->input->buf->raw))) { xmlFatalErrMsg(ctxt, XML_ERR_INVALID_CHAR, "Truncated multi-byte sequence at EOF\n"); } if (ctxt->instate != XML_PARSER_EOF) { - if ((ctxt->sax) && (ctxt->sax->endDocument != NULL)) - ctxt->sax->endDocument(ctxt->userData); + ctxt->instate = XML_PARSER_EOF; + xmlFinishDocument(ctxt); } - ctxt->instate = XML_PARSER_EOF; } if (ctxt->wellFormed == 0) return((xmlParserErrors) ctxt->errNo); @@ -12218,81 +11635,42 @@ xmlParseChunk(xmlParserCtxtPtr ctxt, const char *chunk, int size, /** * xmlCreatePushParserCtxt: - * @sax: a SAX handler - * @user_data: The user data returned on SAX callbacks - * @chunk: a pointer to an array of chars - * @size: number of chars in the array - * @filename: an optional file name or URI + * @sax: a SAX handler (optional) + * @user_data: user data for SAX callbacks (optional) + * @chunk: initial chunk (optional, deprecated) + * @size: size of initial chunk in bytes + * @filename: file name or URI (optional) * * Create a parser context for using the XML parser in push mode. - * If @buffer and @size are non-NULL, the data is used to detect - * the encoding. The remaining characters will be parsed so they - * don't need to be fed in again through xmlParseChunk. - * To allow content encoding detection, @size should be >= 4 - * The value of @filename is used for fetching external entities - * and error/warning reports. + * See xmlParseChunk. * - * Returns the new parser context or NULL + * Passing an initial chunk is useless and deprecated. + * + * @filename is used as base URI to fetch external entities and for + * error reports. + * + * Returns the new parser context or NULL in case of error. */ xmlParserCtxtPtr xmlCreatePushParserCtxt(xmlSAXHandlerPtr sax, void *user_data, const char *chunk, int size, const char *filename) { xmlParserCtxtPtr ctxt; - xmlParserInputPtr inputStream; - xmlParserInputBufferPtr buf; - - buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); - if (buf == NULL) return(NULL); + xmlParserInputPtr input; ctxt = xmlNewSAXParserCtxt(sax, user_data); - if (ctxt == NULL) { - xmlErrMemory(NULL, "creating parser: out of memory\n"); - xmlFreeParserInputBuffer(buf); + if (ctxt == NULL) return(NULL); - } + + ctxt->options &= ~XML_PARSE_NODICT; ctxt->dictNames = 1; - if (filename == NULL) { - ctxt->directory = NULL; - } else { - ctxt->directory = xmlParserGetDirectory(filename); - } - inputStream = xmlNewInputStream(ctxt); - if (inputStream == NULL) { + input = xmlNewInputPush(ctxt, filename, chunk, size, NULL); + if (input == NULL) { xmlFreeParserCtxt(ctxt); - xmlFreeParserInputBuffer(buf); return(NULL); } - - if (filename == NULL) - inputStream->filename = NULL; - else { - inputStream->filename = (char *) - xmlCanonicPath((const xmlChar *) filename); - if (inputStream->filename == NULL) { - xmlFreeInputStream(inputStream); - xmlFreeParserCtxt(ctxt); - xmlFreeParserInputBuffer(buf); - return(NULL); - } - } - inputStream->buf = buf; - xmlBufResetInput(inputStream->buf->buffer, inputStream); - inputPush(ctxt, inputStream); - - if ((size != 0) && (chunk != NULL) && - (ctxt->input != NULL) && (ctxt->input->buf != NULL)) { - size_t pos = ctxt->input->cur - ctxt->input->base; - int res; - - res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk); - xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos); - if (res < 0) { - xmlFatalErr(ctxt, ctxt->input->buf->error, NULL); - xmlHaltParser(ctxt); - } - } + inputPush(ctxt, input); return(ctxt); } @@ -12309,17 +11687,18 @@ xmlStopParser(xmlParserCtxtPtr ctxt) { if (ctxt == NULL) return; xmlHaltParser(ctxt); - ctxt->errNo = XML_ERR_USER_STOP; + if (ctxt->errNo != XML_ERR_NO_MEMORY) + ctxt->errNo = XML_ERR_USER_STOP; } /** * xmlCreateIOParserCtxt: - * @sax: a SAX handler - * @user_data: The user data returned on SAX callbacks + * @sax: a SAX handler (optional) + * @user_data: user data for SAX callbacks (optional) * @ioread: an I/O read function - * @ioclose: an I/O close function + * @ioclose: an I/O close function (optional) * @ioctx: an I/O handler - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) * * Create a parser context for using the XML parser with an existing * I/O stream @@ -12328,33 +11707,24 @@ xmlStopParser(xmlParserCtxtPtr ctxt) { */ xmlParserCtxtPtr xmlCreateIOParserCtxt(xmlSAXHandlerPtr sax, void *user_data, - xmlInputReadCallback ioread, xmlInputCloseCallback ioclose, - void *ioctx, xmlCharEncoding enc) { + xmlInputReadCallback ioread, + xmlInputCloseCallback ioclose, + void *ioctx, xmlCharEncoding enc) { xmlParserCtxtPtr ctxt; - xmlParserInputPtr inputStream; - xmlParserInputBufferPtr buf; - - if (ioread == NULL) return(NULL); - - buf = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, enc); - if (buf == NULL) { - if (ioclose != NULL) - ioclose(ioctx); - return (NULL); - } + xmlParserInputPtr input; + const char *encoding; ctxt = xmlNewSAXParserCtxt(sax, user_data); - if (ctxt == NULL) { - xmlFreeParserInputBuffer(buf); + if (ctxt == NULL) return(NULL); - } - inputStream = xmlNewIOInputStream(ctxt, buf, enc); - if (inputStream == NULL) { + encoding = xmlGetCharEncodingName(enc); + input = xmlNewInputIO(ctxt, NULL, ioread, ioclose, ioctx, encoding, 0); + if (input == NULL) { xmlFreeParserCtxt(ctxt); - return(NULL); + return (NULL); } - inputPush(ctxt, inputStream); + inputPush(ctxt, input); return(ctxt); } @@ -12394,11 +11764,6 @@ xmlIOParseDTD(xmlSAXHandlerPtr sax, xmlParserInputBufferPtr input, return(NULL); } - /* We are loading a DTD */ - ctxt->options |= XML_PARSE_DTDLOAD; - - xmlDetectSAX2(ctxt); - /* * generate a parser input from the I/O handler */ @@ -12424,18 +11789,15 @@ xmlIOParseDTD(xmlSAXHandlerPtr sax, xmlParserInputBufferPtr input, /* * let's parse that entity knowing it's an external subset. */ - ctxt->inSubset = 2; ctxt->myDoc = xmlNewDoc(BAD_CAST "1.0"); if (ctxt->myDoc == NULL) { - xmlErrMemory(ctxt, "New Doc failed"); + xmlErrMemory(ctxt); return(NULL); } ctxt->myDoc->properties = XML_DOC_INTERNAL; ctxt->myDoc->extSubset = xmlNewDtd(ctxt->myDoc, BAD_CAST "none", BAD_CAST "none", BAD_CAST "none"); - xmlDetectEncoding(ctxt); - xmlParseExternalSubset(ctxt, BAD_CAST "none", BAD_CAST "none"); if (ctxt->myDoc != NULL) { @@ -12491,9 +11853,6 @@ xmlSAXParseDTD(xmlSAXHandlerPtr sax, const xmlChar *ExternalID, return(NULL); } - /* We are loading a DTD */ - ctxt->options |= XML_PARSE_DTDLOAD; - /* * Canonicalise the system ID */ @@ -12537,16 +11896,20 @@ xmlSAXParseDTD(xmlSAXHandlerPtr sax, const xmlChar *ExternalID, /* * let's parse that entity knowing it's an external subset. */ - ctxt->inSubset = 2; ctxt->myDoc = xmlNewDoc(BAD_CAST "1.0"); if (ctxt->myDoc == NULL) { - xmlErrMemory(ctxt, "New Doc failed"); + xmlErrMemory(ctxt); xmlFreeParserCtxt(ctxt); return(NULL); } ctxt->myDoc->properties = XML_DOC_INTERNAL; ctxt->myDoc->extSubset = xmlNewDtd(ctxt->myDoc, BAD_CAST "none", ExternalID, SystemID); + if (ctxt->myDoc->extSubset == NULL) { + xmlFreeDoc(ctxt->myDoc); + xmlFreeParserCtxt(ctxt); + return(NULL); + } xmlParseExternalSubset(ctxt, ExternalID, SystemID); if (ctxt->myDoc != NULL) { @@ -12597,258 +11960,246 @@ xmlParseDTD(const xmlChar *ExternalID, const xmlChar *SystemID) { * * ************************************************************************/ -/** - * xmlParseCtxtExternalEntity: - * @ctx: the existing parsing context - * @URL: the URL for the entity to load - * @ID: the System ID for the entity to load - * @lst: the return value for the set of parsed nodes - * - * Parse an external general entity within an existing parsing context - * An external general parsed entity is well-formed if it matches the - * production labeled extParsedEnt. - * - * [78] extParsedEnt ::= TextDecl? content - * - * Returns 0 if the entity is well formed, -1 in case of args problem and - * the parser error code otherwise - */ +static xmlNodePtr +xmlCtxtParseContent(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, + int hasTextDecl, int buildTree) { + xmlNodePtr root = NULL; + xmlNodePtr list = NULL; + xmlChar *rootName = BAD_CAST "#root"; + int result; -int -xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctx, const xmlChar *URL, - const xmlChar *ID, xmlNodePtr *lst) { - void *userData; + if (buildTree) { + root = xmlNewDocNode(ctxt->myDoc, NULL, rootName, NULL); + if (root == NULL) { + xmlErrMemory(ctxt); + goto error; + } + } - if (ctx == NULL) return(-1); - /* - * If the user provided their own SAX callbacks, then reuse the - * userData callback field, otherwise the expected setup in a - * DOM builder is to have userData == ctxt - */ - if (ctx->userData == ctx) - userData = NULL; - else - userData = ctx->userData; - return xmlParseExternalEntityPrivate(ctx->myDoc, ctx, ctx->sax, - userData, ctx->depth + 1, - URL, ID, lst); -} + if (xmlPushInput(ctxt, input) < 0) + goto error; -/** - * xmlParseExternalEntityPrivate: - * @doc: the document the chunk pertains to - * @oldctxt: the previous parser context if available - * @sax: the SAX handler block (possibly NULL) - * @user_data: The user data returned on SAX callbacks (possibly NULL) - * @depth: Used for loop detection, use 0 - * @URL: the URL for the entity to load - * @ID: the System ID for the entity to load - * @list: the return value for the set of parsed nodes - * - * Private version of xmlParseExternalEntity() - * - * Returns 0 if the entity is well formed, -1 in case of args problem and - * the parser error code otherwise - */ + nameNsPush(ctxt, rootName, NULL, NULL, 0, 0); + spacePush(ctxt, -1); -static xmlParserErrors -xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt, - xmlSAXHandlerPtr sax, - void *user_data, int depth, const xmlChar *URL, - const xmlChar *ID, xmlNodePtr *list) { - xmlParserCtxtPtr ctxt; - xmlDocPtr newDoc; - xmlNodePtr newRoot; - xmlParserErrors ret = XML_ERR_OK; + if (buildTree) + nodePush(ctxt, root); - if (((depth > 40) && - ((oldctxt == NULL) || (oldctxt->options & XML_PARSE_HUGE) == 0)) || - (depth > 100)) { - xmlFatalErrMsg(oldctxt, XML_ERR_ENTITY_LOOP, - "Maximum entity nesting depth exceeded"); - return(XML_ERR_ENTITY_LOOP); + if (hasTextDecl) { + xmlDetectEncoding(ctxt); + + /* + * Parse a possible text declaration first + */ + if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) && + (IS_BLANK_CH(NXT(5)))) { + xmlParseTextDecl(ctxt); + /* + * An XML-1.0 document can't reference an entity not XML-1.0 + */ + if ((xmlStrEqual(ctxt->version, BAD_CAST "1.0")) && + (!xmlStrEqual(ctxt->input->version, BAD_CAST "1.0"))) { + xmlFatalErrMsg(ctxt, XML_ERR_VERSION_MISMATCH, + "Version mismatch between document and " + "entity\n"); + } + } } - if (list != NULL) - *list = NULL; - if ((URL == NULL) && (ID == NULL)) - return(XML_ERR_INTERNAL_ERROR); - if (doc == NULL) - return(XML_ERR_INTERNAL_ERROR); + xmlParseContentInternal(ctxt); - ctxt = xmlCreateEntityParserCtxtInternal(sax, user_data, URL, ID, NULL, - oldctxt); - if (ctxt == NULL) return(XML_WAR_UNDECLARED_ENTITY); - if (oldctxt != NULL) { - ctxt->nbErrors = oldctxt->nbErrors; - ctxt->nbWarnings = oldctxt->nbWarnings; - } - xmlDetectSAX2(ctxt); + if (ctxt->input->cur < ctxt->input->end) + xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - newDoc = xmlNewDoc(BAD_CAST "1.0"); - if (newDoc == NULL) { - xmlFreeParserCtxt(ctxt); - return(XML_ERR_INTERNAL_ERROR); - } - newDoc->properties = XML_DOC_INTERNAL; - if (doc) { - newDoc->intSubset = doc->intSubset; - newDoc->extSubset = doc->extSubset; - if (doc->dict) { - newDoc->dict = doc->dict; - xmlDictReference(newDoc->dict); - } - if (doc->URL != NULL) { - newDoc->URL = xmlStrdup(doc->URL); + if ((ctxt->wellFormed) || + ((ctxt->recovery) && (ctxt->errNo != XML_ERR_NO_MEMORY))) { + if (root != NULL) { + xmlNodePtr cur; + + /* + * Return the newly created nodeset after unlinking it from + * its pseudo parent. + */ + cur = root->children; + list = cur; + while (cur != NULL) { + cur->parent = NULL; + cur = cur->next; + } + root->children = NULL; + root->last = NULL; } } - newRoot = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL); - if (newRoot == NULL) { - if (sax != NULL) - xmlFreeParserCtxt(ctxt); - newDoc->intSubset = NULL; - newDoc->extSubset = NULL; - xmlFreeDoc(newDoc); - return(XML_ERR_INTERNAL_ERROR); - } - xmlAddChild((xmlNodePtr) newDoc, newRoot); - nodePush(ctxt, newDoc->children); - if (doc == NULL) { - ctxt->myDoc = newDoc; - } else { - ctxt->myDoc = doc; - newRoot->doc = doc; - } - xmlDetectEncoding(ctxt); + /* + * Read the rest of the stream in case of errors. We want + * to account for the whole entity size. + */ + do { + ctxt->input->cur = ctxt->input->end; + xmlParserShrink(ctxt); + result = xmlParserGrow(ctxt); + } while (result > 0); + + if (buildTree) + nodePop(ctxt); + + namePop(ctxt); + spacePop(ctxt); + + /* xmlPopInput would free the stream */ + inputPop(ctxt); + +error: + xmlFreeNode(root); + + return(list); +} + +static void +xmlCtxtParseEntity(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) { + xmlParserInputPtr input; + xmlNodePtr list; + unsigned long consumed; + int isExternal; + int buildTree; + int oldMinNsIndex; + int oldNodelen, oldNodemem; + + isExternal = (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY); + buildTree = (ctxt->node != NULL); /* - * Parse a possible text declaration first + * Recursion check */ - if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) && (IS_BLANK_CH(NXT(5)))) { - xmlParseTextDecl(ctxt); - /* - * An XML-1.0 document can't reference an entity not XML-1.0 - */ - if ((xmlStrEqual(oldctxt->version, BAD_CAST "1.0")) && - (!xmlStrEqual(ctxt->input->version, BAD_CAST "1.0"))) { - xmlFatalErrMsg(ctxt, XML_ERR_VERSION_MISMATCH, - "Version mismatch between document and entity\n"); - } + if (ent->flags & XML_ENT_EXPANDING) { + xmlFatalErr(ctxt, XML_ERR_ENTITY_LOOP, NULL); + xmlHaltParser(ctxt); + goto error; } - ctxt->instate = XML_PARSER_CONTENT; - ctxt->depth = depth; - if (oldctxt != NULL) { - ctxt->_private = oldctxt->_private; - ctxt->loadsubset = oldctxt->loadsubset; - ctxt->validate = oldctxt->validate; - ctxt->valid = oldctxt->valid; - ctxt->replaceEntities = oldctxt->replaceEntities; - if (oldctxt->validate) { - ctxt->vctxt.error = oldctxt->vctxt.error; - ctxt->vctxt.warning = oldctxt->vctxt.warning; - ctxt->vctxt.userData = oldctxt->vctxt.userData; - ctxt->vctxt.flags = oldctxt->vctxt.flags; + /* + * Load entity + */ + input = xmlNewEntityInputStream(ctxt, ent); + if (input == NULL) + goto error; + + /* + * When building a tree, we need to limit the scope of namespace + * declarations, so that entities don't reference xmlNs structs + * from the parent of a reference. + */ + oldMinNsIndex = ctxt->nsdb->minNsIndex; + if (buildTree) + ctxt->nsdb->minNsIndex = ctxt->nsNr; + + oldNodelen = ctxt->nodelen; + oldNodemem = ctxt->nodemem; + ctxt->nodelen = 0; + ctxt->nodemem = 0; + + /* + * Parse content + * + * This initiates a recursive call chain: + * + * - xmlCtxtParseContent + * - xmlParseContentInternal + * - xmlParseReference + * - xmlCtxtParseEntity + * + * The nesting depth is limited by the maximum number of inputs, + * see xmlPushInput. + * + * It's possible to make this non-recursive (minNsIndex must be + * stored in the input struct) at the expense of code readability. + */ + + ent->flags |= XML_ENT_EXPANDING; + + list = xmlCtxtParseContent(ctxt, input, isExternal, buildTree); + + ent->flags &= ~XML_ENT_EXPANDING; + + ctxt->nsdb->minNsIndex = oldMinNsIndex; + ctxt->nodelen = oldNodelen; + ctxt->nodemem = oldNodemem; + + /* + * Entity size accounting + */ + consumed = input->consumed; + xmlSaturatedAddSizeT(&consumed, input->end - input->base); + + if ((ent->flags & XML_ENT_CHECKED) == 0) + xmlSaturatedAdd(&ent->expandedSize, consumed); + + if ((ent->flags & XML_ENT_PARSED) == 0) { + if (isExternal) + xmlSaturatedAdd(&ctxt->sizeentities, consumed); + + ent->children = list; + + while (list != NULL) { + list->parent = (xmlNodePtr) ent; + if (list->next == NULL) + ent->last = list; + list = list->next; } - ctxt->external = oldctxt->external; - if (ctxt->dict) xmlDictFree(ctxt->dict); - ctxt->dict = oldctxt->dict; - ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3); - ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5); - ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36); - ctxt->dictNames = oldctxt->dictNames; - ctxt->attsDefault = oldctxt->attsDefault; - ctxt->attsSpecial = oldctxt->attsSpecial; - ctxt->linenumbers = oldctxt->linenumbers; - ctxt->record_info = oldctxt->record_info; - ctxt->node_seq.maximum = oldctxt->node_seq.maximum; - ctxt->node_seq.length = oldctxt->node_seq.length; - ctxt->node_seq.buffer = oldctxt->node_seq.buffer; } else { - /* - * Doing validity checking on chunk without context - * doesn't make sense - */ - ctxt->_private = NULL; - ctxt->validate = 0; - ctxt->external = 2; - ctxt->loadsubset = 0; + xmlFreeNodeList(list); } - xmlParseContent(ctxt); + xmlFreeInputStream(input); - if ((RAW == '<') && (NXT(1) == '/')) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } else if (RAW != 0) { - xmlFatalErr(ctxt, XML_ERR_EXTRA_CONTENT, NULL); - } - if (ctxt->node != newDoc->children) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } +error: + ent->flags |= XML_ENT_PARSED | XML_ENT_CHECKED; +} - if (!ctxt->wellFormed) { - ret = (xmlParserErrors)ctxt->errNo; - if (oldctxt != NULL) { - oldctxt->errNo = ctxt->errNo; - oldctxt->wellFormed = 0; - xmlCopyError(&ctxt->lastError, &oldctxt->lastError); - } - } else { - if (list != NULL) { - xmlNodePtr cur; +/** + * xmlParseCtxtExternalEntity: + * @ctxt: the existing parsing context + * @URL: the URL for the entity to load + * @ID: the System ID for the entity to load + * @listOut: the return value for the set of parsed nodes + * + * Parse an external general entity within an existing parsing context + * An external general parsed entity is well-formed if it matches the + * production labeled extParsedEnt. + * + * [78] extParsedEnt ::= TextDecl? content + * + * Returns 0 if the entity is well formed, -1 in case of args problem and + * the parser error code otherwise + */ - /* - * Return the newly created nodeset after unlinking it from - * they pseudo parent. - */ - cur = newDoc->children->children; - *list = cur; - while (cur != NULL) { - cur->parent = NULL; - cur = cur->next; - } - newDoc->children->children = NULL; - } - ret = XML_ERR_OK; - } +int +xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctxt, const xmlChar *URL, + const xmlChar *ID, xmlNodePtr *listOut) { + xmlParserInputPtr input; + xmlNodePtr list; - /* - * Also record the size of the entity parsed - */ - if (ctxt->input != NULL && oldctxt != NULL) { - unsigned long consumed = ctxt->input->consumed; + if (listOut != NULL) + *listOut = NULL; - xmlSaturatedAddSizeT(&consumed, ctxt->input->cur - ctxt->input->base); + if (ctxt == NULL) + return(XML_ERR_ARGUMENT); - xmlSaturatedAdd(&oldctxt->sizeentities, consumed); - xmlSaturatedAdd(&oldctxt->sizeentities, ctxt->sizeentities); + input = xmlLoadExternalEntity((char *)URL, (char *)ID, ctxt); + if (input == NULL) + return(ctxt->errNo); - xmlSaturatedAdd(&oldctxt->sizeentcopy, consumed); - xmlSaturatedAdd(&oldctxt->sizeentcopy, ctxt->sizeentcopy); - } + xmlCtxtInitializeLate(ctxt); - if (oldctxt != NULL) { - ctxt->dict = NULL; - ctxt->attsDefault = NULL; - ctxt->attsSpecial = NULL; - oldctxt->nbErrors = ctxt->nbErrors; - oldctxt->nbWarnings = ctxt->nbWarnings; - oldctxt->validate = ctxt->validate; - oldctxt->valid = ctxt->valid; - oldctxt->node_seq.maximum = ctxt->node_seq.maximum; - oldctxt->node_seq.length = ctxt->node_seq.length; - oldctxt->node_seq.buffer = ctxt->node_seq.buffer; - } - ctxt->node_seq.maximum = 0; - ctxt->node_seq.length = 0; - ctxt->node_seq.buffer = NULL; - xmlFreeParserCtxt(ctxt); - newDoc->intSubset = NULL; - newDoc->extSubset = NULL; - xmlFreeDoc(newDoc); + list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 1, 1); + if (listOut != NULL) + *listOut = list; + else + xmlFreeNodeList(list); - return(ret); + xmlFreeInputStream(input); + return(ctxt->errNo); } #ifdef LIBXML_SAX1_ENABLED @@ -12860,7 +12211,9 @@ xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt, * @depth: Used for loop detection, use 0 * @URL: the URL for the entity to load * @ID: the System ID for the entity to load - * @lst: the return value for the set of parsed nodes + * @list: the return value for the set of parsed nodes + * + * DEPRECATED: Use xmlParseCtxtExternalEntity. * * Parse an external general entity * An external general parsed entity is well-formed if it matches the @@ -12874,9 +12227,26 @@ xmlParseExternalEntityPrivate(xmlDocPtr doc, xmlParserCtxtPtr oldctxt, int xmlParseExternalEntity(xmlDocPtr doc, xmlSAXHandlerPtr sax, void *user_data, - int depth, const xmlChar *URL, const xmlChar *ID, xmlNodePtr *lst) { - return(xmlParseExternalEntityPrivate(doc, NULL, sax, user_data, depth, URL, - ID, lst)); + int depth, const xmlChar *URL, const xmlChar *ID, xmlNodePtr *list) { + xmlParserCtxtPtr ctxt; + int ret; + + if (list != NULL) + *list = NULL; + + if (doc == NULL) + return(XML_ERR_ARGUMENT); + + ctxt = xmlNewSAXParserCtxt(sax, user_data); + if (ctxt == NULL) + return(XML_ERR_NO_MEMORY); + + ctxt->depth = depth; + ctxt->myDoc = doc; + ret = xmlParseCtxtExternalEntity(ctxt, URL, ID, list); + + xmlFreeParserCtxt(ctxt); + return(ret); } /** @@ -12907,229 +12277,6 @@ xmlParseBalancedChunkMemory(xmlDocPtr doc, xmlSAXHandlerPtr sax, } #endif /* LIBXML_SAX1_ENABLED */ -/** - * xmlParseBalancedChunkMemoryInternal: - * @oldctxt: the existing parsing context - * @string: the input string in UTF8 or ISO-Latin (zero terminated) - * @user_data: the user data field for the parser context - * @lst: the return value for the set of parsed nodes - * - * - * Parse a well-balanced chunk of an XML document - * called by the parser - * The allowed sequence for the Well Balanced Chunk is the one defined by - * the content production in the XML grammar: - * - * [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* - * - * Returns XML_ERR_OK if the chunk is well balanced, and the parser - * error code otherwise - * - * In case recover is set to 1, the nodelist will not be empty even if - * the parsed chunk is not well balanced. - */ -static xmlParserErrors -xmlParseBalancedChunkMemoryInternal(xmlParserCtxtPtr oldctxt, - const xmlChar *string, void *user_data, xmlNodePtr *lst) { - xmlParserCtxtPtr ctxt; - xmlDocPtr newDoc = NULL; - xmlNodePtr newRoot; - xmlSAXHandlerPtr oldsax = NULL; - xmlNodePtr content = NULL; - xmlNodePtr last = NULL; - xmlParserErrors ret = XML_ERR_OK; - xmlHashedString hprefix, huri; - unsigned i; - - if (((oldctxt->depth > 40) && ((oldctxt->options & XML_PARSE_HUGE) == 0)) || - (oldctxt->depth > 100)) { - xmlFatalErrMsg(oldctxt, XML_ERR_ENTITY_LOOP, - "Maximum entity nesting depth exceeded"); - return(XML_ERR_ENTITY_LOOP); - } - - - if (lst != NULL) - *lst = NULL; - if (string == NULL) - return(XML_ERR_INTERNAL_ERROR); - - ctxt = xmlCreateDocParserCtxt(string); - if (ctxt == NULL) return(XML_WAR_UNDECLARED_ENTITY); - ctxt->nbErrors = oldctxt->nbErrors; - ctxt->nbWarnings = oldctxt->nbWarnings; - if (user_data != NULL) - ctxt->userData = user_data; - else - ctxt->userData = ctxt; - if (ctxt->dict != NULL) xmlDictFree(ctxt->dict); - ctxt->dict = oldctxt->dict; - ctxt->input_id = oldctxt->input_id; - ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3); - ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5); - ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36); - - /* - * Propagate namespaces down the entity - * - * Making entities and namespaces work correctly requires additional - * changes, see xmlParseReference. - */ - - /* Default namespace */ - hprefix.name = NULL; - hprefix.hashValue = 0; - huri.name = xmlParserNsLookupUri(oldctxt, &hprefix); - huri.hashValue = 0; - if (huri.name != NULL) - xmlParserNsPush(ctxt, NULL, &huri, NULL, 0); - - for (i = 0; i < oldctxt->nsdb->hashSize; i++) { - xmlParserNsBucket *bucket = &oldctxt->nsdb->hash[i]; - const xmlChar **ns; - xmlParserNsExtra *extra; - unsigned nsIndex; - - if ((bucket->hashValue != 0) && - (bucket->index != INT_MAX)) { - nsIndex = bucket->index; - ns = &oldctxt->nsTab[nsIndex * 2]; - extra = &oldctxt->nsdb->extra[nsIndex]; - - hprefix.name = ns[0]; - hprefix.hashValue = bucket->hashValue; - huri.name = ns[1]; - huri.hashValue = extra->uriHashValue; - /* - * Don't copy SAX data to avoid a use-after-free with XML reader. - * This matches the pre-2.12 behavior. - */ - xmlParserNsPush(ctxt, &hprefix, &huri, NULL, 0); - } - } - - oldsax = ctxt->sax; - ctxt->sax = oldctxt->sax; - xmlDetectSAX2(ctxt); - ctxt->replaceEntities = oldctxt->replaceEntities; - ctxt->options = oldctxt->options; - - ctxt->_private = oldctxt->_private; - if (oldctxt->myDoc == NULL) { - newDoc = xmlNewDoc(BAD_CAST "1.0"); - if (newDoc == NULL) { - ret = XML_ERR_INTERNAL_ERROR; - goto error; - } - newDoc->properties = XML_DOC_INTERNAL; - newDoc->dict = ctxt->dict; - xmlDictReference(newDoc->dict); - ctxt->myDoc = newDoc; - } else { - ctxt->myDoc = oldctxt->myDoc; - content = ctxt->myDoc->children; - last = ctxt->myDoc->last; - } - newRoot = xmlNewDocNode(ctxt->myDoc, NULL, BAD_CAST "pseudoroot", NULL); - if (newRoot == NULL) { - ret = XML_ERR_INTERNAL_ERROR; - goto error; - } - ctxt->myDoc->children = NULL; - ctxt->myDoc->last = NULL; - xmlAddChild((xmlNodePtr) ctxt->myDoc, newRoot); - nodePush(ctxt, ctxt->myDoc->children); - ctxt->instate = XML_PARSER_CONTENT; - ctxt->depth = oldctxt->depth; - - ctxt->validate = 0; - ctxt->loadsubset = oldctxt->loadsubset; - if ((oldctxt->validate) || (oldctxt->replaceEntities != 0)) { - /* - * ID/IDREF registration will be done in xmlValidateElement below - */ - ctxt->loadsubset |= XML_SKIP_IDS; - } - ctxt->dictNames = oldctxt->dictNames; - ctxt->attsDefault = oldctxt->attsDefault; - ctxt->attsSpecial = oldctxt->attsSpecial; - - xmlParseContent(ctxt); - if ((RAW == '<') && (NXT(1) == '/')) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } else if (RAW != 0) { - xmlFatalErr(ctxt, XML_ERR_EXTRA_CONTENT, NULL); - } - if (ctxt->node != ctxt->myDoc->children) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } - - if (!ctxt->wellFormed) { - ret = (xmlParserErrors)ctxt->errNo; - oldctxt->errNo = ctxt->errNo; - oldctxt->wellFormed = 0; - xmlCopyError(&ctxt->lastError, &oldctxt->lastError); - } else { - ret = XML_ERR_OK; - } - - if ((lst != NULL) && (ret == XML_ERR_OK)) { - xmlNodePtr cur; - - /* - * Return the newly created nodeset after unlinking it from - * they pseudo parent. - */ - cur = ctxt->myDoc->children->children; - *lst = cur; - while (cur != NULL) { -#ifdef LIBXML_VALID_ENABLED - if ((oldctxt->validate) && (oldctxt->wellFormed) && - (oldctxt->myDoc) && (oldctxt->myDoc->intSubset) && - (cur->type == XML_ELEMENT_NODE)) { - oldctxt->valid &= xmlValidateElement(&oldctxt->vctxt, - oldctxt->myDoc, cur); - } -#endif /* LIBXML_VALID_ENABLED */ - cur->parent = NULL; - cur = cur->next; - } - ctxt->myDoc->children->children = NULL; - } - if (ctxt->myDoc != NULL) { - xmlFreeNode(ctxt->myDoc->children); - ctxt->myDoc->children = content; - ctxt->myDoc->last = last; - } - - /* - * Also record the size of the entity parsed - */ - if (ctxt->input != NULL && oldctxt != NULL) { - unsigned long consumed = ctxt->input->consumed; - - xmlSaturatedAddSizeT(&consumed, ctxt->input->cur - ctxt->input->base); - - xmlSaturatedAdd(&oldctxt->sizeentcopy, consumed); - xmlSaturatedAdd(&oldctxt->sizeentcopy, ctxt->sizeentcopy); - } - - oldctxt->nbErrors = ctxt->nbErrors; - oldctxt->nbWarnings = ctxt->nbWarnings; - -error: - ctxt->sax = oldsax; - ctxt->dict = NULL; - ctxt->attsDefault = NULL; - ctxt->attsSpecial = NULL; - xmlFreeParserCtxt(ctxt); - if (newDoc != NULL) { - xmlFreeDoc(newDoc); - } - - return(ret); -} - /** * xmlParseInNodeContext: * @node: the context node @@ -13163,7 +12310,7 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, * check all input parameters, grab the document */ if ((lst == NULL) || (node == NULL) || (data == NULL) || (datalen < 0)) - return(XML_ERR_INTERNAL_ERROR); + return(XML_ERR_ARGUMENT); switch (node->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: @@ -13216,33 +12363,30 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, /* * Use input doc's dict if present, else assure XML_PARSE_NODICT is set. - * We need a dictionary for xmlDetectSAX2, so if there's no doc dict + * 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 + } else { options |= XML_PARSE_NODICT; - - if (doc->encoding != NULL) { - xmlCharEncodingHandlerPtr hdlr; - - hdlr = xmlFindCharEncodingHandler((const char *) doc->encoding); - if (hdlr != NULL) { - xmlSwitchToEncoding(ctxt, hdlr); - } else { - return(XML_ERR_UNSUPPORTED_ENCODING); - } + ctxt->dictNames = 0; } - xmlCtxtUseOptionsInternal(ctxt, options); - xmlDetectSAX2(ctxt); + 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; - ctxt->instate = XML_PARSER_CONTENT; + + /* + * TODO: Use xmlCtxtParseContent + */ fake = xmlNewDocComment(node->doc, NULL); if (fake == NULL) { @@ -13286,26 +12430,18 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, __htmlParseContent(ctxt); else #endif - xmlParseContent(ctxt); + xmlParseContentInternal(ctxt); - xmlParserNsPop(ctxt, nsnr); - if ((RAW == '<') && (NXT(1) == '/')) { + if (ctxt->input->cur < ctxt->input->end) xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } else if (RAW != 0) { - xmlFatalErr(ctxt, XML_ERR_EXTRA_CONTENT, NULL); - } - if ((ctxt->node != NULL) && (ctxt->node != node)) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - ctxt->wellFormed = 0; - } - if (!ctxt->wellFormed) { - if (ctxt->errNo == 0) - ret = XML_ERR_INTERNAL_ERROR; - else - ret = (xmlParserErrors)ctxt->errNo; - } else { + xmlParserNsPop(ctxt, nsnr); + + if ((ctxt->wellFormed) || + ((ctxt->recovery) && (ctxt->errNo != XML_ERR_NO_MEMORY))) { ret = XML_ERR_OK; + } else { + ret = (xmlParserErrors) ctxt->errNo; } /* @@ -13352,19 +12488,18 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, * @user_data: The user data returned on SAX callbacks (possibly NULL) * @depth: Used for loop detection, use 0 * @string: the input string in UTF8 or ISO-Latin (zero terminated) - * @lst: the return value for the set of parsed nodes + * @listOut: the return value for the set of parsed nodes * @recover: return nodes even if the data is broken (use 0) * - * * Parse a well-balanced chunk of an XML document - * called by the parser + * * The allowed sequence for the Well Balanced Chunk is the one defined by * the content production in the XML grammar: * * [43] content ::= (element | CharData | Reference | CDSect | PI | Comment)* * - * Returns 0 if the chunk is well balanced, -1 in case of args problem and - * the parser error code otherwise + * Returns 0 if the chunk is well balanced, or thehe parser error code + * otherwise. * * In case recover is set to 1, the nodelist will not be empty even if * the parsed chunk is not well balanced, assuming the parsing succeeded to @@ -13372,142 +12507,49 @@ xmlParseInNodeContext(xmlNodePtr node, const char *data, int datalen, */ int xmlParseBalancedChunkMemoryRecover(xmlDocPtr doc, xmlSAXHandlerPtr sax, - void *user_data, int depth, const xmlChar *string, xmlNodePtr *lst, + void *user_data, int depth, const xmlChar *string, xmlNodePtr *listOut, int recover) { xmlParserCtxtPtr ctxt; - xmlDocPtr newDoc; - xmlSAXHandlerPtr oldsax = NULL; - xmlNodePtr content, newRoot; - int ret = 0; - - if (depth > 40) { - return(XML_ERR_ENTITY_LOOP); - } + xmlParserInputPtr input; + xmlNodePtr list; + int ret; + if (listOut != NULL) + *listOut = NULL; - if (lst != NULL) - *lst = NULL; if (string == NULL) - return(-1); + return(XML_ERR_ARGUMENT); - ctxt = xmlCreateDocParserCtxt(string); - if (ctxt == NULL) return(-1); - ctxt->userData = ctxt; - if (sax != NULL) { - oldsax = ctxt->sax; - ctxt->sax = sax; - if (user_data != NULL) - ctxt->userData = user_data; - } - newDoc = xmlNewDoc(BAD_CAST "1.0"); - if (newDoc == NULL) { - xmlFreeParserCtxt(ctxt); - return(-1); - } - newDoc->properties = XML_DOC_INTERNAL; - if ((doc != NULL) && (doc->dict != NULL)) { - xmlDictFree(ctxt->dict); - ctxt->dict = doc->dict; - xmlDictReference(ctxt->dict); - ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3); - ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5); - ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36); - ctxt->dictNames = 1; - newDoc->dict = ctxt->dict; - xmlDictReference(newDoc->dict); - } else { - xmlCtxtUseOptionsInternal(ctxt, XML_PARSE_NODICT); - } - /* doc == NULL is only supported for historic reasons */ - if (doc != NULL) { - newDoc->intSubset = doc->intSubset; - newDoc->extSubset = doc->extSubset; - } - newRoot = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL); - if (newRoot == NULL) { - if (sax != NULL) - ctxt->sax = oldsax; - xmlFreeParserCtxt(ctxt); - newDoc->intSubset = NULL; - newDoc->extSubset = NULL; - xmlFreeDoc(newDoc); - return(-1); - } - xmlAddChild((xmlNodePtr) newDoc, newRoot); - nodePush(ctxt, newRoot); - /* doc == NULL is only supported for historic reasons */ - if (doc == NULL) { - ctxt->myDoc = newDoc; - } else { - ctxt->myDoc = newDoc; - /* Ensure that doc has XML spec namespace */ - xmlSearchNsByHref(doc, (xmlNodePtr)doc, XML_XML_NAMESPACE); - newDoc->oldNs = doc->oldNs; - } - ctxt->instate = XML_PARSER_CONTENT; - ctxt->input_id = 2; - ctxt->depth = depth; + ctxt = xmlNewSAXParserCtxt(sax, user_data); + if (ctxt == NULL) + return(XML_ERR_NO_MEMORY); - /* - * Doing validity checking on chunk doesn't make sense - */ - ctxt->validate = 0; - ctxt->loadsubset = 0; - xmlDetectSAX2(ctxt); + xmlCtxtInitializeLate(ctxt); - if ( doc != NULL ){ - content = doc->children; - doc->children = NULL; - xmlParseContent(ctxt); - doc->children = content; - } - else { - xmlParseContent(ctxt); - } - if ((RAW == '<') && (NXT(1) == '/')) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); - } else if (RAW != 0) { - xmlFatalErr(ctxt, XML_ERR_EXTRA_CONTENT, NULL); - } - if (ctxt->node != newDoc->children) { - xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL); + ctxt->depth = depth; + ctxt->myDoc = doc; + if (recover) { + ctxt->options |= XML_PARSE_RECOVER; + ctxt->recovery = 1; } - if (!ctxt->wellFormed) { - if (ctxt->errNo == 0) - ret = 1; - else - ret = ctxt->errNo; - } else { - ret = 0; - } + input = xmlNewStringInputStream(ctxt, string); + if (input == NULL) + return(ctxt->errNo); - if ((lst != NULL) && ((ret == 0) || (recover == 1))) { - xmlNodePtr cur; + list = xmlCtxtParseContent(ctxt, input, /* hasTextDecl */ 0, 1); + if (listOut != NULL) + *listOut = list; + else + xmlFreeNodeList(list); - /* - * Return the newly created nodeset after unlinking it from - * they pseudo parent. - */ - cur = newDoc->children->children; - *lst = cur; - while (cur != NULL) { - xmlSetTreeDoc(cur, doc); - cur->parent = NULL; - cur = cur->next; - } - newDoc->children->children = NULL; - } + if (!ctxt->wellFormed) + ret = ctxt->errNo; + else + ret = XML_ERR_OK; - if (sax != NULL) - ctxt->sax = oldsax; + xmlFreeInputStream(input); xmlFreeParserCtxt(ctxt); - newDoc->intSubset = NULL; - newDoc->extSubset = NULL; - /* This leaks the namespace list if doc == NULL */ - newDoc->oldNs = NULL; - xmlFreeDoc(newDoc); - return(ret); } @@ -13539,23 +12581,24 @@ xmlSAXParseEntity(xmlSAXHandlerPtr sax, const char *filename) { return(NULL); } if (sax != NULL) { - if (ctxt->sax != NULL) - xmlFree(ctxt->sax); - ctxt->sax = sax; + if (sax->initialized == XML_SAX2_MAGIC) { + *ctxt->sax = *sax; + } else { + memset(ctxt->sax, 0, sizeof(*ctxt->sax)); + memcpy(ctxt->sax, sax, sizeof(xmlSAXHandlerV1)); + } ctxt->userData = NULL; } xmlParseExtParsedEnt(ctxt); - if (ctxt->wellFormed) + if (ctxt->wellFormed) { ret = ctxt->myDoc; - else { + } else { ret = NULL; xmlFreeDoc(ctxt->myDoc); - ctxt->myDoc = NULL; } - if (sax != NULL) - ctxt->sax = NULL; + xmlFreeParserCtxt(ctxt); return(ret); @@ -13568,88 +12611,17 @@ xmlSAXParseEntity(xmlSAXHandlerPtr sax, const char *filename) { * parse an XML external entity out of context and build a tree. * * [78] extParsedEnt ::= TextDecl? content - * - * This correspond to a "Well Balanced" chunk - * - * Returns the resulting document tree - */ - -xmlDocPtr -xmlParseEntity(const char *filename) { - return(xmlSAXParseEntity(NULL, filename)); -} -#endif /* LIBXML_SAX1_ENABLED */ - -/** - * xmlCreateEntityParserCtxtInternal: - * @URL: the entity URL - * @ID: the entity PUBLIC ID - * @base: a possible base for the target URI - * @pctx: parser context used to set options on new context - * - * Create a parser context for an external entity - * Automatic support for ZLIB/Compress compressed document is provided - * by default if found at compile-time. - * - * Returns the new parser context or NULL - */ -static xmlParserCtxtPtr -xmlCreateEntityParserCtxtInternal(xmlSAXHandlerPtr sax, void *userData, - const xmlChar *URL, const xmlChar *ID, const xmlChar *base, - xmlParserCtxtPtr pctx) { - xmlParserCtxtPtr ctxt; - xmlParserInputPtr inputStream; - char *directory = NULL; - xmlChar *uri; - - ctxt = xmlNewSAXParserCtxt(sax, userData); - if (ctxt == NULL) { - return(NULL); - } - - if (pctx != NULL) { - ctxt->options = pctx->options; - ctxt->_private = pctx->_private; - ctxt->input_id = pctx->input_id; - } - - /* Don't read from stdin. */ - if (xmlStrcmp(URL, BAD_CAST "-") == 0) - URL = BAD_CAST "./-"; - - uri = xmlBuildURI(URL, base); - - if (uri == NULL) { - inputStream = xmlLoadExternalEntity((char *)URL, (char *)ID, ctxt); - if (inputStream == NULL) { - xmlFreeParserCtxt(ctxt); - return(NULL); - } - - inputPush(ctxt, inputStream); - - if ((ctxt->directory == NULL) && (directory == NULL)) - directory = xmlParserGetDirectory((char *)URL); - if ((ctxt->directory == NULL) && (directory != NULL)) - ctxt->directory = directory; - } else { - inputStream = xmlLoadExternalEntity((char *)uri, (char *)ID, ctxt); - if (inputStream == NULL) { - xmlFree(uri); - xmlFreeParserCtxt(ctxt); - return(NULL); - } - - inputPush(ctxt, inputStream); + * + * This correspond to a "Well Balanced" chunk + * + * Returns the resulting document tree + */ - if ((ctxt->directory == NULL) && (directory == NULL)) - directory = xmlParserGetDirectory((char *)uri); - if ((ctxt->directory == NULL) && (directory != NULL)) - ctxt->directory = directory; - xmlFree(uri); - } - return(ctxt); +xmlDocPtr +xmlParseEntity(const char *filename) { + return(xmlSAXParseEntity(NULL, filename)); } +#endif /* LIBXML_SAX1_ENABLED */ /** * xmlCreateEntityParserCtxt: @@ -13657,6 +12629,8 @@ xmlCreateEntityParserCtxtInternal(xmlSAXHandlerPtr sax, void *userData, * @ID: the entity PUBLIC ID * @base: a possible base for the target URI * + * DEPRECATED: Don't use. + * * Create a parser context for an external entity * Automatic support for ZLIB/Compress compressed document is provided * by default if found at compile-time. @@ -13666,8 +12640,35 @@ xmlCreateEntityParserCtxtInternal(xmlSAXHandlerPtr sax, void *userData, xmlParserCtxtPtr xmlCreateEntityParserCtxt(const xmlChar *URL, const xmlChar *ID, const xmlChar *base) { - return xmlCreateEntityParserCtxtInternal(NULL, NULL, URL, ID, base, NULL); + xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; + xmlChar *uri = NULL; + + ctxt = xmlNewParserCtxt(); + if (ctxt == NULL) + return(NULL); + + if (base != NULL) { + if (xmlBuildURISafe(URL, base, &uri) < 0) + goto error; + if (uri != NULL) + URL = uri; + } + + input = xmlLoadExternalEntity((char *)URL, (char *)ID, ctxt); + if (input == NULL) + goto error; + + if (inputPush(ctxt, input) < 0) + goto error; + + xmlFree(uri); + return(ctxt); +error: + xmlFree(uri); + xmlFreeParserCtxt(ctxt); + return(NULL); } /************************************************************************ @@ -13681,6 +12682,8 @@ xmlCreateEntityParserCtxt(const xmlChar *URL, const xmlChar *ID, * @filename: the filename or URL * @options: a combination of xmlParserOption * + * DEPRECATED: Use xmlNewParserCtxt and xmlCtxtReadFile. + * * Create a parser context for a file or URL content. * Automatic support for ZLIB/Compress compressed document is provided * by default if found at compile-time and for file accesses @@ -13691,30 +12694,21 @@ xmlParserCtxtPtr xmlCreateURLParserCtxt(const char *filename, int options) { xmlParserCtxtPtr ctxt; - xmlParserInputPtr inputStream; - char *directory = NULL; + xmlParserInputPtr input; ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) { - xmlErrMemory(NULL, "cannot allocate parser context"); + if (ctxt == NULL) return(NULL); - } - if (options) - xmlCtxtUseOptionsInternal(ctxt, options); + xmlCtxtUseOptions(ctxt, options); ctxt->linenumbers = 1; - inputStream = xmlLoadExternalEntity(filename, NULL, ctxt); - if (inputStream == NULL) { + input = xmlLoadExternalEntity(filename, NULL, ctxt); + if (input == NULL) { xmlFreeParserCtxt(ctxt); return(NULL); } - - inputPush(ctxt, inputStream); - if ((ctxt->directory == NULL) && (directory == NULL)) - directory = xmlParserGetDirectory(filename); - if ((ctxt->directory == NULL) && (directory != NULL)) - ctxt->directory = directory; + inputPush(ctxt, input); return(ctxt); } @@ -13723,6 +12717,8 @@ xmlCreateURLParserCtxt(const char *filename, int options) * xmlCreateFileParserCtxt: * @filename: the filename * + * DEPRECATED: Use xmlNewParserCtxt and xmlCtxtReadFile. + * * Create a parser context for a file content. * Automatic support for ZLIB/Compress compressed document is provided * by default if found at compile-time. @@ -13762,48 +12758,28 @@ xmlSAXParseFileWithData(xmlSAXHandlerPtr sax, const char *filename, int recovery, void *data) { xmlDocPtr ret; xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; - xmlInitParser(); - - ctxt = xmlCreateFileParserCtxt(filename); - if (ctxt == NULL) { + ctxt = xmlNewSAXParserCtxt(sax, NULL); + if (ctxt == NULL) return(NULL); - } - if (sax != NULL) { - if (ctxt->sax != NULL) - xmlFree(ctxt->sax); - ctxt->sax = sax; - } - xmlDetectSAX2(ctxt); - if (data!=NULL) { + + if (data != NULL) ctxt->_private = data; - } - if (ctxt->directory == NULL) - ctxt->directory = xmlParserGetDirectory(filename); + if (recovery) { + ctxt->options |= XML_PARSE_RECOVER; + ctxt->recovery = 1; + } - ctxt->recovery = recovery; + if ((filename != NULL) && (filename[0] == '-') && (filename[1] == 0)) + input = xmlNewInputFd(ctxt, filename, STDIN_FILENO, NULL, 0); + else + input = xmlNewInputURL(ctxt, filename, NULL, NULL, 0); - xmlParseDocument(ctxt); + ret = xmlCtxtParseDocument(ctxt, input); - if ((ctxt->wellFormed) || recovery) { - ret = ctxt->myDoc; - if ((ret != NULL) && (ctxt->input->buf != NULL)) { - if (ctxt->input->buf->compressed > 0) - ret->compression = 9; - else - ret->compression = ctxt->input->buf->compressed; - } - } - else { - ret = NULL; - xmlFreeDoc(ctxt->myDoc); - ctxt->myDoc = NULL; - } - if (sax != NULL) - ctxt->sax = NULL; xmlFreeParserCtxt(ctxt); - return(ret); } @@ -13907,19 +12883,11 @@ xmlSetupParserForBuffer(xmlParserCtxtPtr ctxt, const xmlChar* buffer, if ((ctxt == NULL) || (buffer == NULL)) return; - input = xmlNewInputStream(ctxt); - if (input == NULL) { - xmlErrMemory(NULL, "parsing new buffer: out of memory\n"); - xmlClearParserCtxt(ctxt); - return; - } - xmlClearParserCtxt(ctxt); - if (filename != NULL) - input->filename = (char *) xmlCanonicPath((const xmlChar *)filename); - input->base = buffer; - input->cur = buffer; - input->end = &buffer[xmlStrlen(buffer)]; + + input = xmlNewInputString(ctxt, filename, (const char *) buffer, NULL, 0); + if (input == NULL) + return; inputPush(ctxt, input); } @@ -13944,13 +12912,15 @@ xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data, ctxt = xmlCreateFileParserCtxt(filename); if (ctxt == NULL) return -1; - if (ctxt->sax != (xmlSAXHandlerPtr) &xmlDefaultSAXHandler) - xmlFree(ctxt->sax); - ctxt->sax = sax; - xmlDetectSAX2(ctxt); - - if (user_data != NULL) + if (sax != NULL) { + if (sax->initialized == XML_SAX2_MAGIC) { + *ctxt->sax = *sax; + } else { + memset(ctxt->sax, 0, sizeof(*ctxt->sax)); + memcpy(ctxt->sax, sax, sizeof(xmlSAXHandlerV1)); + } ctxt->userData = user_data; + } xmlParseDocument(ctxt); @@ -13962,8 +12932,6 @@ xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data, else ret = -1; } - if (sax != NULL) - ctxt->sax = NULL; if (ctxt->myDoc != NULL) { xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL; @@ -13985,7 +12953,8 @@ xmlSAXUserParseFile(xmlSAXHandlerPtr sax, void *user_data, * @buffer: a pointer to a char array * @size: the size of the array * - * Create a parser context for an XML in-memory document. + * Create a parser context for an XML in-memory document. The input buffer + * must not contain a terminating null byte. * * Returns the new parser context or NULL */ @@ -13993,35 +12962,21 @@ xmlParserCtxtPtr xmlCreateMemoryParserCtxt(const char *buffer, int size) { xmlParserCtxtPtr ctxt; xmlParserInputPtr input; - xmlParserInputBufferPtr buf; - if (buffer == NULL) - return(NULL); - if (size <= 0) + if (size < 0) return(NULL); ctxt = xmlNewParserCtxt(); if (ctxt == NULL) return(NULL); - buf = xmlParserInputBufferCreateMem(buffer, size, XML_CHAR_ENCODING_NONE); - if (buf == NULL) { - xmlFreeParserCtxt(ctxt); - return(NULL); - } - - input = xmlNewInputStream(ctxt); + input = xmlNewInputMemory(ctxt, NULL, buffer, size, NULL, 0); if (input == NULL) { - xmlFreeParserInputBuffer(buf); xmlFreeParserCtxt(ctxt); return(NULL); } - - input->filename = NULL; - input->buf = buf; - xmlBufResetInput(input->buf->buffer, input); - inputPush(ctxt, input); + return(ctxt); } @@ -14049,38 +13004,32 @@ xmlCreateMemoryParserCtxt(const char *buffer, int size) { xmlDocPtr xmlSAXParseMemoryWithData(xmlSAXHandlerPtr sax, const char *buffer, - int size, int recovery, void *data) { + int size, int recovery, void *data) { xmlDocPtr ret; xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; - xmlInitParser(); + if (size < 0) + return(NULL); - ctxt = xmlCreateMemoryParserCtxt(buffer, size); - if (ctxt == NULL) return(NULL); - if (sax != NULL) { - if (ctxt->sax != NULL) - xmlFree(ctxt->sax); - ctxt->sax = sax; - } - xmlDetectSAX2(ctxt); - if (data!=NULL) { + ctxt = xmlNewSAXParserCtxt(sax, NULL); + if (ctxt == NULL) + return(NULL); + + if (data != NULL) ctxt->_private=data; + + if (recovery) { + ctxt->options |= XML_PARSE_RECOVER; + ctxt->recovery = 1; } - ctxt->recovery = recovery; + input = xmlNewInputMemory(ctxt, NULL, buffer, size, NULL, + XML_INPUT_BUF_STATIC); - xmlParseDocument(ctxt); + ret = xmlCtxtParseDocument(ctxt, input); - if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc; - else { - ret = NULL; - xmlFreeDoc(ctxt->myDoc); - ctxt->myDoc = NULL; - } - if (sax != NULL) - ctxt->sax = NULL; xmlFreeParserCtxt(ctxt); - return(ret); } @@ -14158,17 +13107,17 @@ int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data, int ret = 0; xmlParserCtxtPtr ctxt; - xmlInitParser(); - ctxt = xmlCreateMemoryParserCtxt(buffer, size); if (ctxt == NULL) return -1; - if (ctxt->sax != (xmlSAXHandlerPtr) &xmlDefaultSAXHandler) - xmlFree(ctxt->sax); - ctxt->sax = sax; - xmlDetectSAX2(ctxt); - - if (user_data != NULL) + if (sax != NULL) { + if (sax->initialized == XML_SAX2_MAGIC) { + *ctxt->sax = *sax; + } else { + memset(ctxt->sax, 0, sizeof(*ctxt->sax)); + memcpy(ctxt->sax, sax, sizeof(xmlSAXHandlerV1)); + } ctxt->userData = user_data; + } xmlParseDocument(ctxt); @@ -14180,8 +13129,6 @@ int xmlSAXUserParseMemory(xmlSAXHandlerPtr sax, void *user_data, else ret = -1; } - if (sax != NULL) - ctxt->sax = NULL; if (ctxt->myDoc != NULL) { xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL; @@ -14204,33 +13151,18 @@ xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar *str) { xmlParserCtxtPtr ctxt; xmlParserInputPtr input; - xmlParserInputBufferPtr buf; - - if (str == NULL) - return(NULL); ctxt = xmlNewParserCtxt(); if (ctxt == NULL) return(NULL); - buf = xmlParserInputBufferCreateString(str); - if (buf == NULL) { - xmlFreeParserCtxt(ctxt); - return(NULL); - } - - input = xmlNewInputStream(ctxt); + input = xmlNewInputString(ctxt, NULL, (const char *) str, NULL, 0); if (input == NULL) { - xmlFreeParserInputBuffer(buf); xmlFreeParserCtxt(ctxt); return(NULL); } - - input->filename = NULL; - input->buf = buf; - xmlBufResetInput(input->buf->buffer, input); - inputPush(ctxt, input); + return(ctxt); } @@ -14267,7 +13199,6 @@ xmlSAXParseDoc(xmlSAXHandlerPtr sax, const xmlChar *cur, int recovery) { ctxt->sax = sax; ctxt->userData = NULL; } - xmlDetectSAX2(ctxt); xmlParseDocument(ctxt); if ((ctxt->wellFormed) || recovery) ret = ctxt->myDoc; @@ -14300,47 +13231,6 @@ xmlParseDoc(const xmlChar *cur) { } #endif /* LIBXML_SAX1_ENABLED */ -#ifdef LIBXML_LEGACY_ENABLED -/************************************************************************ - * * - * Specific function to keep track of entities references * - * and used by the XSLT debugger * - * * - ************************************************************************/ - -static xmlEntityReferenceFunc xmlEntityRefFunc = NULL; - -/** - * xmlAddEntityReference: - * @ent : A valid entity - * @firstNode : A valid first node for children of entity - * @lastNode : A valid last node of children entity - * - * Notify of a reference to an entity of type XML_EXTERNAL_GENERAL_PARSED_ENTITY - */ -static void -xmlAddEntityReference(xmlEntityPtr ent, xmlNodePtr firstNode, - xmlNodePtr lastNode) -{ - if (xmlEntityRefFunc != NULL) { - (*xmlEntityRefFunc) (ent, firstNode, lastNode); - } -} - - -/** - * xmlSetEntityReferenceFunc: - * @func: A valid function - * - * Set the function to call call back when a xml reference has been made - */ -void -xmlSetEntityReferenceFunc(xmlEntityReferenceFunc func) -{ - xmlEntityRefFunc = func; -} -#endif /* LIBXML_LEGACY_ENABLED */ - /************************************************************************ * * * New set (2.6.0) of simpler and more flexible APIs * @@ -14404,12 +13294,16 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt) ctxt->version = NULL; DICT_FREE(ctxt->encoding); ctxt->encoding = NULL; - DICT_FREE(ctxt->directory); - ctxt->directory = NULL; DICT_FREE(ctxt->extSubURI); ctxt->extSubURI = NULL; DICT_FREE(ctxt->extSubSystem); ctxt->extSubSystem = NULL; + + if (ctxt->directory != NULL) { + xmlFree(ctxt->directory); + ctxt->directory = NULL; + } + if (ctxt->myDoc != NULL) xmlFreeDoc(ctxt->myDoc); ctxt->myDoc = NULL; @@ -14418,9 +13312,7 @@ xmlCtxtReset(xmlParserCtxtPtr ctxt) ctxt->hasExternalSubset = 0; ctxt->hasPErefs = 0; ctxt->html = 0; - ctxt->external = 0; ctxt->instate = XML_PARSER_START; - ctxt->token = 0; ctxt->wellFormed = 1; ctxt->nsWellFormed = 1; @@ -14477,208 +13369,258 @@ int xmlCtxtResetPush(xmlParserCtxtPtr ctxt, const char *chunk, int size, const char *filename, const char *encoding) { - xmlParserInputPtr inputStream; - xmlParserInputBufferPtr buf; + xmlParserInputPtr input; if (ctxt == NULL) return(1); - buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); - if (buf == NULL) - return(1); - - if (ctxt == NULL) { - xmlFreeParserInputBuffer(buf); - return(1); - } - xmlCtxtReset(ctxt); - if (filename == NULL) { - ctxt->directory = NULL; - } else { - ctxt->directory = xmlParserGetDirectory(filename); - } - - inputStream = xmlNewInputStream(ctxt); - if (inputStream == NULL) { - xmlFreeParserInputBuffer(buf); + input = xmlNewInputPush(ctxt, filename, chunk, size, encoding); + if (input == NULL) return(1); - } - - if (filename == NULL) - inputStream->filename = NULL; - else - inputStream->filename = (char *) - xmlCanonicPath((const xmlChar *) filename); - inputStream->buf = buf; - xmlBufResetInput(buf->buffer, inputStream); + inputPush(ctxt, input); - inputPush(ctxt, inputStream); + return(0); +} - if ((size > 0) && (chunk != NULL) && (ctxt->input != NULL) && - (ctxt->input->buf != NULL)) { - size_t pos = ctxt->input->cur - ctxt->input->base; - int res; +static int +xmlCtxtSetOptionsInternal(xmlParserCtxtPtr ctxt, int options, int keepMask) +{ + int allMask; - res = xmlParserInputBufferPush(ctxt->input->buf, size, chunk); - xmlBufUpdateInput(ctxt->input->buf->buffer, ctxt->input, pos); - if (res < 0) { - xmlFatalErr(ctxt, ctxt->input->buf->error, NULL); - xmlHaltParser(ctxt); - return(1); - } - } + if (ctxt == NULL) + return(-1); - if (encoding != NULL) { - xmlCharEncodingHandlerPtr hdlr; + /* + * XInclude options aren't handled by the parser. + * + * XML_PARSE_XINCLUDE + * XML_PARSE_NOXINCNODE + * XML_PARSE_NOBASEFIX + */ + allMask = XML_PARSE_RECOVER | + XML_PARSE_NOENT | + XML_PARSE_DTDLOAD | + XML_PARSE_DTDATTR | + XML_PARSE_DTDVALID | + XML_PARSE_NOERROR | + XML_PARSE_NOWARNING | + XML_PARSE_PEDANTIC | + XML_PARSE_NOBLANKS | +#ifdef LIBXML_SAX1_ENABLED + XML_PARSE_SAX1 | +#endif + XML_PARSE_NONET | + XML_PARSE_NODICT | + XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | + XML_PARSE_COMPACT | + XML_PARSE_OLD10 | + XML_PARSE_HUGE | + XML_PARSE_OLDSAX | + XML_PARSE_IGNORE_ENC | + XML_PARSE_BIG_LINES | + XML_PARSE_NO_XXE; + + ctxt->options = (ctxt->options & keepMask) | (options & allMask); + + /* + * For some options, struct members are historically the source + * of truth. The values are initalized from global variables and + * old code could also modify them directly. Several older API + * functions that don't take an options argument rely on these + * deprecated mechanisms. + * + * Once public access to struct members and the globals are + * disabled, we can use the options bitmask as source of + * truth, making all these struct members obsolete. + * + * The XML_DETECT_IDS flags is misnamed. It simply enables + * loading of the external subset. + */ + ctxt->recovery = (options & XML_PARSE_RECOVER) ? 1 : 0; + ctxt->replaceEntities = (options & XML_PARSE_NOENT) ? 1 : 0; + ctxt->loadsubset = (options & XML_PARSE_DTDLOAD) ? XML_DETECT_IDS : 0; + ctxt->loadsubset |= (options & XML_PARSE_DTDATTR) ? XML_COMPLETE_ATTRS : 0; + ctxt->validate = (options & XML_PARSE_DTDVALID) ? 1 : 0; + ctxt->pedantic = (options & XML_PARSE_PEDANTIC) ? 1 : 0; + ctxt->keepBlanks = (options & XML_PARSE_NOBLANKS) ? 0 : 1; + ctxt->dictNames = (options & XML_PARSE_NODICT) ? 0 : 1; - hdlr = xmlFindCharEncodingHandler(encoding); - if (hdlr != NULL) { - xmlSwitchToEncoding(ctxt, hdlr); - } else { - xmlFatalErrMsgStr(ctxt, XML_ERR_UNSUPPORTED_ENCODING, - "Unsupported encoding %s\n", BAD_CAST encoding); - } + /* + * Changing SAX callbacks is a bad idea. This should be fixed. + */ + if (options & XML_PARSE_NOBLANKS) { + ctxt->sax->ignorableWhitespace = xmlSAX2IgnorableWhitespace; + } + if (options & XML_PARSE_NOCDATA) { + ctxt->sax->cdataBlock = NULL; + } + if (options & XML_PARSE_HUGE) { + if (ctxt->dict != NULL) + xmlDictSetLimit(ctxt->dict, 0); } - return(0); -} + ctxt->linenumbers = 1; + return(options & ~allMask); +} /** - * xmlCtxtUseOptionsInternal: + * xmlCtxtSetOptions: * @ctxt: an XML parser context - * @options: a combination of xmlParserOption - * @encoding: the user provided encoding to use + * @options: a bitmask of xmlParserOption values + * + * Applies the options to the parser context. Unset options are + * cleared. + * + * Available since 2.13.0. With older versions, you can use + * xmlCtxtUseOptions. + * + * XML_PARSE_RECOVER + * + * Enable "recovery" mode which allows non-wellformed documents. + * How this mode behaves exactly is unspecified and may change + * without further notice. Use of this feature is DISCOURAGED. + * + * XML_PARSE_NOENT + * + * Despite the confusing name, this option enables substitution + * of entities. The resulting tree won't contain any entity + * reference nodes. + * + * This option also enables loading of external entities (both + * general and parameter entities) which is dangerous. If you + * process untrusted data, it's recommended to set the + * XML_PARSE_NO_XXE option to disable loading of external + * entities. + * + * XML_PARSE_DTDLOAD + * + * Enables loading of an external DTD and the loading and + * substitution of external parameter entities. Has no effect + * if XML_PARSE_NO_XXE is set. + * + * XML_PARSE_DTDATTR + * + * Adds default attributes from the DTD to the result document. + * + * Implies XML_PARSE_DTDLOAD, but loading of external content + * can be disabled with XML_PARSE_NO_XXE. + * + * XML_PARSE_DTDVALID + * + * This option enables DTD validation which requires to load + * external DTDs and external entities (both general and + * parameter entities) unless XML_PARSE_NO_XXE was set. + * + * XML_PARSE_NO_XXE + * + * Disables loading of external DTDs or entities. + * + * XML_PARSE_NOERROR + * + * Disable error and warning reports to the error handlers. + * Errors are still accessible with xmlCtxtGetLastError. + * + * XML_PARSE_NOWARNING + * + * Disable warning reports. + * + * XML_PARSE_PEDANTIC + * + * Enable some pedantic warnings. + * + * XML_PARSE_NOBLANKS + * + * Remove some text nodes containing only whitespace from the + * result document. Which nodes are removed depends on DTD + * element declarations or a conservative heuristic. The + * reindenting feature of the serialization code relies on this + * option to be set when parsing. Use of this option is + * DISCOURAGED. + * + * XML_PARSE_SAX1 + * + * Always invoke the deprecated SAX1 startElement and endElement + * handlers. This option is DEPRECATED. + * + * XML_PARSE_NONET + * + * Disable network access with the builtin HTTP and FTP clients. + * + * XML_PARSE_NODICT + * + * Create a document without interned strings, making all + * strings separate memory allocations. + * + * XML_PARSE_NSCLEAN + * + * Remove redundant namespace declarations from the result + * document. + * + * XML_PARSE_NOCDATA + * + * Output normal text nodes instead of CDATA nodes. + * + * XML_PARSE_COMPACT + * + * Store small strings directly in the node struct to save + * memory. + * + * XML_PARSE_OLD10 * - * Applies the options to the parser context + * Use old Name productions from before XML 1.0 Fifth Edition. + * This options is DEPRECATED. + * + * XML_PARSE_HUGE + * + * Relax some internal limits. + * + * Maximum size of text nodes, tags, comments, processing instructions, + * CDATA sections, entity values + * + * normal: 10M + * huge: 1B + * + * Maximum size of names, system literals, pubid literals + * + * normal: 50K + * huge: 10M + * + * Maximum nesting depth of elements + * + * normal: 256 + * huge: 2048 + * + * Maximum nesting depth of entities + * + * normal: 20 + * huge: 40 + * + * XML_PARSE_OLDSAX + * + * Enable an unspecified legacy mode for SAX parsers. This + * option is DEPRECATED. + * + * XML_PARSE_IGNORE_ENC + * + * Ignore the encoding in the XML declaration. This option is + * mostly unneeded these days. The only effect is to enforce + * UTF-8 decoding of ASCII-like data. + * + * XML_PARSE_BIG_LINES + * + * Enable reporting of line numbers larger than 65535. * * Returns 0 in case of success, the set of unknown or unimplemented options * in case of error. */ -static int -xmlCtxtUseOptionsInternal(xmlParserCtxtPtr ctxt, int options) +int +xmlCtxtSetOptions(xmlParserCtxtPtr ctxt, int options) { - if (ctxt == NULL) - return(-1); - if (options & XML_PARSE_RECOVER) { - ctxt->recovery = 1; - options -= XML_PARSE_RECOVER; - ctxt->options |= XML_PARSE_RECOVER; - } else - ctxt->recovery = 0; - if (options & XML_PARSE_DTDLOAD) { - ctxt->loadsubset = XML_DETECT_IDS; - options -= XML_PARSE_DTDLOAD; - ctxt->options |= XML_PARSE_DTDLOAD; - } else - ctxt->loadsubset = 0; - if (options & XML_PARSE_DTDATTR) { - ctxt->loadsubset |= XML_COMPLETE_ATTRS; - options -= XML_PARSE_DTDATTR; - ctxt->options |= XML_PARSE_DTDATTR; - } - if (options & XML_PARSE_NOENT) { - ctxt->replaceEntities = 1; - /* ctxt->loadsubset |= XML_DETECT_IDS; */ - options -= XML_PARSE_NOENT; - ctxt->options |= XML_PARSE_NOENT; - } else - ctxt->replaceEntities = 0; - if (options & XML_PARSE_PEDANTIC) { - ctxt->pedantic = 1; - options -= XML_PARSE_PEDANTIC; - ctxt->options |= XML_PARSE_PEDANTIC; - } else - ctxt->pedantic = 0; - if (options & XML_PARSE_NOBLANKS) { - ctxt->keepBlanks = 0; - ctxt->sax->ignorableWhitespace = xmlSAX2IgnorableWhitespace; - options -= XML_PARSE_NOBLANKS; - ctxt->options |= XML_PARSE_NOBLANKS; - } else - ctxt->keepBlanks = 1; - if (options & XML_PARSE_DTDVALID) { - ctxt->validate = 1; - if (options & XML_PARSE_NOWARNING) - ctxt->vctxt.warning = NULL; - if (options & XML_PARSE_NOERROR) - ctxt->vctxt.error = NULL; - options -= XML_PARSE_DTDVALID; - ctxt->options |= XML_PARSE_DTDVALID; - } else - ctxt->validate = 0; - if (options & XML_PARSE_NOWARNING) { - ctxt->sax->warning = NULL; - options -= XML_PARSE_NOWARNING; - } - if (options & XML_PARSE_NOERROR) { - ctxt->sax->error = NULL; - ctxt->sax->fatalError = NULL; - options -= XML_PARSE_NOERROR; - } -#ifdef LIBXML_SAX1_ENABLED - if (options & XML_PARSE_SAX1) { - ctxt->sax->startElementNs = NULL; - ctxt->sax->endElementNs = NULL; - ctxt->sax->initialized = 1; - options -= XML_PARSE_SAX1; - ctxt->options |= XML_PARSE_SAX1; - } -#endif /* LIBXML_SAX1_ENABLED */ - if (options & XML_PARSE_NODICT) { - ctxt->dictNames = 0; - options -= XML_PARSE_NODICT; - ctxt->options |= XML_PARSE_NODICT; - } else { - ctxt->dictNames = 1; - } - if (options & XML_PARSE_NOCDATA) { - ctxt->sax->cdataBlock = NULL; - options -= XML_PARSE_NOCDATA; - ctxt->options |= XML_PARSE_NOCDATA; - } - if (options & XML_PARSE_NSCLEAN) { - ctxt->options |= XML_PARSE_NSCLEAN; - options -= XML_PARSE_NSCLEAN; - } - if (options & XML_PARSE_NONET) { - ctxt->options |= XML_PARSE_NONET; - options -= XML_PARSE_NONET; - } - if (options & XML_PARSE_COMPACT) { - ctxt->options |= XML_PARSE_COMPACT; - options -= XML_PARSE_COMPACT; - } - if (options & XML_PARSE_OLD10) { - ctxt->options |= XML_PARSE_OLD10; - options -= XML_PARSE_OLD10; - } - if (options & XML_PARSE_NOBASEFIX) { - ctxt->options |= XML_PARSE_NOBASEFIX; - options -= XML_PARSE_NOBASEFIX; - } - if (options & XML_PARSE_HUGE) { - ctxt->options |= XML_PARSE_HUGE; - options -= XML_PARSE_HUGE; - if (ctxt->dict != NULL) - xmlDictSetLimit(ctxt->dict, 0); - } - if (options & XML_PARSE_OLDSAX) { - ctxt->options |= XML_PARSE_OLDSAX; - options -= XML_PARSE_OLDSAX; - } - if (options & XML_PARSE_IGNORE_ENC) { - ctxt->options |= XML_PARSE_IGNORE_ENC; - options -= XML_PARSE_IGNORE_ENC; - } - if (options & XML_PARSE_BIG_LINES) { - ctxt->options |= XML_PARSE_BIG_LINES; - options -= XML_PARSE_BIG_LINES; - } - ctxt->linenumbers = 1; - return (options); + return(xmlCtxtSetOptionsInternal(ctxt, options, 0)); } /** @@ -14686,7 +13628,22 @@ xmlCtxtUseOptionsInternal(xmlParserCtxtPtr ctxt, int options) * @ctxt: an XML parser context * @options: a combination of xmlParserOption * - * Applies the options to the parser context + * DEPRECATED: Use xmlCtxtSetOptions. + * + * Applies the options to the parser context. The following options + * are never cleared and can only be enabled: + * + * XML_PARSE_NOERROR + * XML_PARSE_NOWARNING + * XML_PARSE_NONET + * XML_PARSE_NSCLEAN + * XML_PARSE_NOCDATA + * XML_PARSE_COMPACT + * XML_PARSE_OLD10 + * XML_PARSE_HUGE + * XML_PARSE_OLDSAX + * XML_PARSE_IGNORE_ENC + * XML_PARSE_BIG_LINES * * Returns 0 in case of success, the set of unknown or unimplemented options * in case of error. @@ -14694,7 +13651,24 @@ xmlCtxtUseOptionsInternal(xmlParserCtxtPtr ctxt, int options) int xmlCtxtUseOptions(xmlParserCtxtPtr ctxt, int options) { - return(xmlCtxtUseOptionsInternal(ctxt, options)); + int keepMask; + + /* + * For historic reasons, some options can only be enabled. + */ + keepMask = XML_PARSE_NOERROR | + XML_PARSE_NOWARNING | + XML_PARSE_NONET | + XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | + XML_PARSE_COMPACT | + XML_PARSE_OLD10 | + XML_PARSE_HUGE | + XML_PARSE_OLDSAX | + XML_PARSE_IGNORE_ENC | + XML_PARSE_BIG_LINES; + + return(xmlCtxtSetOptionsInternal(ctxt, options, keepMask)); } /** @@ -14717,90 +13691,102 @@ xmlCtxtSetMaxAmplification(xmlParserCtxtPtr ctxt, unsigned maxAmpl) } /** - * xmlDoRead: + * xmlCtxtParseDocument: * @ctxt: an XML parser context - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL - * @options: a combination of xmlParserOption - * @reuse: keep the context for reuse + * @input: parser input * - * Common front-end for the xmlRead functions + * Parse an XML document and return the resulting document tree. + * Takes ownership of the input object. + * + * Available since 2.13.0. * * Returns the resulting document tree or NULL */ -static xmlDocPtr -xmlDoRead(xmlParserCtxtPtr ctxt, const char *URL, const char *encoding, - int options, int reuse) +xmlDocPtr +xmlCtxtParseDocument(xmlParserCtxtPtr ctxt, xmlParserInputPtr input) { - xmlDocPtr ret; + xmlDocPtr ret = NULL; - xmlCtxtUseOptionsInternal(ctxt, options); - if (encoding != NULL) { - xmlCharEncodingHandlerPtr hdlr; + if ((ctxt == NULL) || (input == NULL)) + return(NULL); - /* - * TODO: We should consider to set XML_PARSE_IGNORE_ENC if the - * caller provided an encoding. Otherwise, we might switch to - * the encoding from the XML declaration which is likely to - * break things. Also see xmlSwitchInputEncoding. - */ - hdlr = xmlFindCharEncodingHandler(encoding); - if (hdlr != NULL) - xmlSwitchToEncoding(ctxt, hdlr); + /* assert(ctxt->inputNr == 0); */ + while (ctxt->inputNr > 0) + xmlFreeInputStream(inputPop(ctxt)); + + if (inputPush(ctxt, input) < 0) { + xmlFreeInputStream(input); + return(NULL); } - if ((URL != NULL) && (ctxt->input != NULL) && - (ctxt->input->filename == NULL)) - ctxt->input->filename = (char *) xmlStrdup((const xmlChar *) URL); + xmlParseDocument(ctxt); - if ((ctxt->wellFormed) || ctxt->recovery) + + if ((ctxt->wellFormed) || + ((ctxt->recovery) && (ctxt->errNo != XML_ERR_NO_MEMORY))) { ret = ctxt->myDoc; - else { + } else { + if (ctxt->errNo == XML_ERR_OK) + xmlFatalErrMsg(ctxt, XML_ERR_INTERNAL_ERROR, "unknown error\n"); + ret = NULL; - if (ctxt->myDoc != NULL) { - xmlFreeDoc(ctxt->myDoc); - } + xmlFreeDoc(ctxt->myDoc); } ctxt->myDoc = NULL; - if (!reuse) { - xmlFreeParserCtxt(ctxt); - } - return (ret); + /* assert(ctxt->inputNr == 1); */ + while (ctxt->inputNr > 0) + xmlFreeInputStream(inputPop(ctxt)); + + return(ret); } /** * xmlReadDoc: * @cur: a pointer to a zero terminated string - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML in-memory document and build a tree. + * Convenience function to parse an XML document from a + * zero-terminated string. + * + * See xmlCtxtReadDoc for details. * * Returns the resulting document tree */ xmlDocPtr -xmlReadDoc(const xmlChar * cur, const char *URL, const char *encoding, int options) +xmlReadDoc(const xmlChar *cur, const char *URL, const char *encoding, + int options) { xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; + xmlDocPtr doc; - if (cur == NULL) - return (NULL); - xmlInitParser(); - - ctxt = xmlCreateDocParserCtxt(cur); + ctxt = xmlNewParserCtxt(); if (ctxt == NULL) - return (NULL); - return (xmlDoRead(ctxt, URL, encoding, options, 0)); + return(NULL); + + xmlCtxtUseOptions(ctxt, options); + + input = xmlNewInputString(ctxt, URL, (const char *) cur, encoding, + XML_INPUT_BUF_STATIC); + + doc = xmlCtxtParseDocument(ctxt, input); + + xmlFreeParserCtxt(ctxt); + return(doc); } /** * xmlReadFile: * @filename: a file or URL - * @encoding: the document encoding, or NULL + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML file from the filesystem or the network. + * Convenience function to parse an XML file from the filesystem, + * the network or a global user-define resource loader. + * + * See xmlCtxtReadFile for details. * * Returns the resulting document tree */ @@ -14808,48 +13794,85 @@ xmlDocPtr xmlReadFile(const char *filename, const char *encoding, int options) { xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; + xmlDocPtr doc; - xmlInitParser(); - ctxt = xmlCreateURLParserCtxt(filename, options); + ctxt = xmlNewParserCtxt(); if (ctxt == NULL) - return (NULL); - return (xmlDoRead(ctxt, NULL, encoding, options, 0)); + return(NULL); + + xmlCtxtUseOptions(ctxt, options); + + /* + * Backward compatibility for users of command line utilities like + * xmlstarlet expecting "-" to mean stdin. This is dangerous and + * should be removed at some point. + */ + if ((filename != NULL) && (filename[0] == '-') && (filename[1] == 0)) + input = xmlNewInputFd(ctxt, filename, STDIN_FILENO, encoding, 0); + else + input = xmlNewInputURL(ctxt, filename, NULL, encoding, 0); + + doc = xmlCtxtParseDocument(ctxt, input); + + xmlFreeParserCtxt(ctxt); + return(doc); } /** * xmlReadMemory: * @buffer: a pointer to a char array * @size: the size of the array - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @url: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML in-memory document and build a tree. + * Parse an XML in-memory document and build a tree. The input buffer must + * not contain a terminating null byte. + * + * See xmlCtxtReadMemory for details. * * Returns the resulting document tree */ xmlDocPtr -xmlReadMemory(const char *buffer, int size, const char *URL, const char *encoding, int options) +xmlReadMemory(const char *buffer, int size, const char *url, + const char *encoding, int options) { xmlParserCtxtPtr ctxt; + xmlParserInputPtr input; + xmlDocPtr doc; - xmlInitParser(); - ctxt = xmlCreateMemoryParserCtxt(buffer, size); + if (size < 0) + return(NULL); + + ctxt = xmlNewParserCtxt(); if (ctxt == NULL) - return (NULL); - return (xmlDoRead(ctxt, URL, encoding, options, 0)); + return(NULL); + + xmlCtxtUseOptions(ctxt, options); + + input = xmlNewInputMemory(ctxt, url, buffer, size, encoding, + XML_INPUT_BUF_STATIC); + + doc = xmlCtxtParseDocument(ctxt, input); + + xmlFreeParserCtxt(ctxt); + return(doc); } /** * xmlReadFd: * @fd: an open file descriptor - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML from a file descriptor and build a tree. + * Parse an XML from a file descriptor and build a tree. + * + * See xmlCtxtReadFd for details. + * * NOTE that the file descriptor will not be closed when the - * reader is closed or reset. + * context is freed or reset. * * Returns the resulting document tree */ @@ -14857,42 +13880,35 @@ xmlDocPtr xmlReadFd(int fd, const char *URL, const char *encoding, int options) { xmlParserCtxtPtr ctxt; - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; - - if (fd < 0) - return (NULL); - xmlInitParser(); + xmlParserInputPtr input; + xmlDocPtr doc; - input = xmlParserInputBufferCreateFd(fd, XML_CHAR_ENCODING_NONE); - if (input == NULL) - return (NULL); - input->closecallback = NULL; ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) { - xmlFreeParserInputBuffer(input); - return (NULL); - } - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - xmlFreeParserCtxt(ctxt); - return (NULL); - } - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 0)); + if (ctxt == NULL) + return(NULL); + + xmlCtxtUseOptions(ctxt, options); + + input = xmlNewInputFd(ctxt, URL, fd, encoding, 0); + + doc = xmlCtxtParseDocument(ctxt, input); + + xmlFreeParserCtxt(ctxt); + return(doc); } /** * xmlReadIO: * @ioread: an I/O read function - * @ioclose: an I/O close function + * @ioclose: an I/O close function (optional) * @ioctx: an I/O handler - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML document from I/O functions and source and build a tree. + * Parse an XML document from I/O functions and context and build a tree. + * + * See xmlCtxtReadIO for details. * * Returns the resulting document tree */ @@ -14901,45 +13917,37 @@ xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose, void *ioctx, const char *URL, const char *encoding, int options) { xmlParserCtxtPtr ctxt; - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; - - if (ioread == NULL) - return (NULL); - xmlInitParser(); + xmlParserInputPtr input; + xmlDocPtr doc; - input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, - XML_CHAR_ENCODING_NONE); - if (input == NULL) { - if (ioclose != NULL) - ioclose(ioctx); - return (NULL); - } ctxt = xmlNewParserCtxt(); - if (ctxt == NULL) { - xmlFreeParserInputBuffer(input); - return (NULL); - } - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - xmlFreeParserCtxt(ctxt); - return (NULL); - } - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 0)); + if (ctxt == NULL) + return(NULL); + + xmlCtxtUseOptions(ctxt, options); + + input = xmlNewInputIO(ctxt, URL, ioread, ioclose, ioctx, encoding, 0); + + doc = xmlCtxtParseDocument(ctxt, input); + + xmlFreeParserCtxt(ctxt); + return(doc); } /** * xmlCtxtReadDoc: * @ctxt: an XML parser context * @str: a pointer to a zero terminated string - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML in-memory document and build a tree. - * This reuses the existing @ctxt parser context + * Parse an XML in-memory document and build a tree. + * + * @URL is used as base to resolve external entities and for error + * reporting. + * + * See xmlCtxtUseOptions for details. * * Returns the resulting document tree */ @@ -14947,41 +13955,29 @@ xmlDocPtr xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar *str, const char *URL, const char *encoding, int options) { - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; + xmlParserInputPtr input; if (ctxt == NULL) - return (NULL); - if (str == NULL) - return (NULL); - xmlInitParser(); + return(NULL); xmlCtxtReset(ctxt); + xmlCtxtUseOptions(ctxt, options); - input = xmlParserInputBufferCreateString(str); - if (input == NULL) { - return(NULL); - } - - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - return(NULL); - } + input = xmlNewInputString(ctxt, URL, (const char *) str, encoding, + XML_INPUT_BUF_STATIC); - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 1)); + return(xmlCtxtParseDocument(ctxt, input)); } /** * xmlCtxtReadFile: * @ctxt: an XML parser context * @filename: a file or URL - * @encoding: the document encoding, or NULL + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML file from the filesystem or the network. - * This reuses the existing @ctxt parser context + * Parse an XML file from the filesystem, the network or a user-defined + * resource loader. * * Returns the resulting document tree */ @@ -14989,22 +13985,17 @@ xmlDocPtr xmlCtxtReadFile(xmlParserCtxtPtr ctxt, const char *filename, const char *encoding, int options) { - xmlParserInputPtr stream; + xmlParserInputPtr input; - if (filename == NULL) - return (NULL); if (ctxt == NULL) - return (NULL); - xmlInitParser(); + return(NULL); xmlCtxtReset(ctxt); + xmlCtxtUseOptions(ctxt, options); - stream = xmlLoadExternalEntity(filename, NULL, ctxt); - if (stream == NULL) { - return (NULL); - } - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, NULL, encoding, options, 1)); + input = xmlNewInputURL(ctxt, filename, NULL, encoding, 0); + + return(xmlCtxtParseDocument(ctxt, input)); } /** @@ -15012,12 +14003,17 @@ xmlCtxtReadFile(xmlParserCtxtPtr ctxt, const char *filename, * @ctxt: an XML parser context * @buffer: a pointer to a char array * @size: the size of the array - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML in-memory document and build a tree. - * This reuses the existing @ctxt parser context + * Parse an XML in-memory document and build a tree. The input buffer must + * not contain a terminating null byte. + * + * @URL is used as base to resolve external entities and for error + * reporting. + * + * See xmlCtxtUseOptions for details. * * Returns the resulting document tree */ @@ -15025,45 +14021,37 @@ xmlDocPtr xmlCtxtReadMemory(xmlParserCtxtPtr ctxt, const char *buffer, int size, const char *URL, const char *encoding, int options) { - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; + xmlParserInputPtr input; - if (ctxt == NULL) - return (NULL); - if (buffer == NULL) - return (NULL); - xmlInitParser(); + if ((ctxt == NULL) || (size < 0)) + return(NULL); xmlCtxtReset(ctxt); + xmlCtxtUseOptions(ctxt, options); - input = xmlParserInputBufferCreateStatic(buffer, size, - XML_CHAR_ENCODING_NONE); - if (input == NULL) { - return(NULL); - } - - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - return(NULL); - } + input = xmlNewInputMemory(ctxt, URL, buffer, size, encoding, + XML_INPUT_BUF_STATIC); - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 1)); + return(xmlCtxtParseDocument(ctxt, input)); } /** * xmlCtxtReadFd: * @ctxt: an XML parser context * @fd: an open file descriptor - * @URL: the base URL to use for the document - * @encoding: the document encoding, or NULL + * @URL: base URL (optional) + * @encoding: the document encoding (optional) * @options: a combination of xmlParserOption * - * parse an XML from a file descriptor and build a tree. - * This reuses the existing @ctxt parser context + * Parse an XML document from a file descriptor and build a tree. + * * NOTE that the file descriptor will not be closed when the - * reader is closed or reset. + * context is freed or reset. + * + * @URL is used as base to resolve external entities and for error + * reporting. + * + * See xmlCtxtUseOptions for details. * * Returns the resulting document tree */ @@ -15071,29 +14059,17 @@ xmlDocPtr xmlCtxtReadFd(xmlParserCtxtPtr ctxt, int fd, const char *URL, const char *encoding, int options) { - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; + xmlParserInputPtr input; - if (fd < 0) - return (NULL); if (ctxt == NULL) - return (NULL); - xmlInitParser(); + return(NULL); xmlCtxtReset(ctxt); + xmlCtxtUseOptions(ctxt, options); + input = xmlNewInputFd(ctxt, URL, fd, encoding, 0); - input = xmlParserInputBufferCreateFd(fd, XML_CHAR_ENCODING_NONE); - if (input == NULL) - return (NULL); - input->closecallback = NULL; - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - return (NULL); - } - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 1)); + return(xmlCtxtParseDocument(ctxt, input)); } /** @@ -15109,6 +14085,11 @@ xmlCtxtReadFd(xmlParserCtxtPtr ctxt, int fd, * parse an XML document from I/O functions and source and build a tree. * This reuses the existing @ctxt parser context * + * @URL is used as base to resolve external entities and for error + * reporting. + * + * See xmlCtxtUseOptions for details. + * * Returns the resulting document tree */ xmlDocPtr @@ -15117,30 +14098,16 @@ xmlCtxtReadIO(xmlParserCtxtPtr ctxt, xmlInputReadCallback ioread, const char *URL, const char *encoding, int options) { - xmlParserInputBufferPtr input; - xmlParserInputPtr stream; + xmlParserInputPtr input; - if (ioread == NULL) - return (NULL); if (ctxt == NULL) - return (NULL); - xmlInitParser(); + return(NULL); xmlCtxtReset(ctxt); + xmlCtxtUseOptions(ctxt, options); - input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, - XML_CHAR_ENCODING_NONE); - if (input == NULL) { - if (ioclose != NULL) - ioclose(ioctx); - return (NULL); - } - stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE); - if (stream == NULL) { - xmlFreeParserInputBuffer(input); - return (NULL); - } - inputPush(ctxt, stream); - return (xmlDoRead(ctxt, URL, encoding, options, 1)); + input = xmlNewInputIO(ctxt, URL, ioread, ioclose, ioctx, encoding, 0); + + return(xmlCtxtParseDocument(ctxt, input)); } diff --git a/libraries/libxml2/parserInternals.c b/libraries/libxml2/parserInternals.c index e6b4cb14..4456896e 100644 --- a/libraries/libxml2/parserInternals.c +++ b/libraries/libxml2/parserInternals.c @@ -35,6 +35,7 @@ #include #endif #include +#include #define CUR(ctxt) ctxt->input->cur #define END(ctxt) ctxt->input->end @@ -45,6 +46,8 @@ #include "private/io.h" #include "private/parser.h" +#define XML_MAX_ERRORS 100 + /* * XML_MAX_AMPLIFICATION_DEFAULT is the default maximum allowed amplification * factor of serialized output after entity expansion. @@ -60,7 +63,6 @@ * @version: the include version number * * check the compiled lib version against the include one. - * This can warn or immediately kill the application */ void xmlCheckVersion(int version) { @@ -69,15 +71,11 @@ xmlCheckVersion(int version) { xmlInitParser(); if ((myversion / 10000) != (version / 10000)) { - xmlGenericError(xmlGenericErrorContext, - "Fatal: program compiled against libxml %d using libxml %d\n", - (version / 10000), (myversion / 10000)); fprintf(stderr, "Fatal: program compiled against libxml %d using libxml %d\n", (version / 10000), (myversion / 10000)); - } - if ((myversion / 100) < (version / 100)) { - xmlGenericError(xmlGenericErrorContext, + } else if ((myversion / 100) < (version / 100)) { + fprintf(stderr, "Warning: program compiled against libxml %d using older %d\n", (version / 100), (myversion / 100)); } @@ -92,338 +90,293 @@ xmlCheckVersion(int version) { /** - * xmlErrMemory: + * xmlCtxtSetErrorHandler: * @ctxt: an XML parser context - * @extra: extra information + * @handler: error handler + * @data: data for error handler + * + * Register a callback function that will be called on errors and + * warnings. If handler is NULL, the error handler will be deactivated. + * + * This is the recommended way to collect errors from the parser and + * takes precedence over all other error reporting mechanisms. + * These are (in order of precedence): + * + * - per-context structured handler (xmlCtxtSetErrorHandler) + * - per-context structured "serror" SAX handler + * - global structured handler (xmlSetStructuredErrorFunc) + * - per-context generic "error" and "warning" SAX handlers + * - global generic handler (xmlSetGenericErrorFunc) + * - print to stderr * - * Handle a redefinition of attribute error + * Available since 2.13.0. */ void -xmlErrMemory(xmlParserCtxtPtr ctxt, const char *extra) +xmlCtxtSetErrorHandler(xmlParserCtxtPtr ctxt, xmlStructuredErrorFunc handler, + void *data) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) { - ctxt->errNo = XML_ERR_NO_MEMORY; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; - } - if (extra) - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, extra, - NULL, NULL, 0, 0, - "Memory allocation failed : %s\n", extra); - else - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, NULL, - NULL, NULL, 0, 0, "Memory allocation failed\n"); + if (ctxt == NULL) + return; + ctxt->errorHandler = handler; + ctxt->errorCtxt = data; } /** - * __xmlErrEncoding: + * xmlCtxtErrMemory: * @ctxt: an XML parser context - * @xmlerr: the error number - * @msg: the error message - * @str1: an string info - * @str2: an string info * - * Handle an encoding error + * Handle an out-of-memory error. + * + * Available since 2.13.0. */ void -__xmlErrEncoding(xmlParserCtxtPtr ctxt, xmlParserErrors xmlerr, - const char *msg, const xmlChar * str1, const xmlChar * str2) +xmlCtxtErrMemory(xmlParserCtxtPtr ctxt) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = xmlerr; - __xmlRaiseError(NULL, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, xmlerr, XML_ERR_FATAL, - NULL, 0, (const char *) str1, (const char *) str2, - NULL, 0, 0, msg, str1, str2); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + void *data; + + if (ctxt == NULL) + return; + + ctxt->errNo = XML_ERR_NO_MEMORY; + ctxt->instate = XML_PARSER_EOF; /* TODO: Remove after refactoring */ + ctxt->wellFormed = 0; + ctxt->disableSAX = 2; + + if (ctxt->errorHandler) { + schannel = ctxt->errorHandler; + data = ctxt->errorCtxt; + } else if ((ctxt->sax->initialized == XML_SAX2_MAGIC) && + (ctxt->sax->serror != NULL)) { + schannel = ctxt->sax->serror; + data = ctxt->userData; + } else { + channel = ctxt->sax->error; + data = ctxt->userData; } + + xmlRaiseMemoryError(schannel, channel, data, XML_FROM_PARSER, + &ctxt->lastError); } /** - * xmlErrInternal: - * @ctxt: an XML parser context - * @msg: the error message - * @str: error information + * xmlCtxtErrIO: + * @ctxt: parser context + * @code: xmlParserErrors code + * @uri: filename or URI (optional) * - * Handle an internal error + * If filename is empty, use the one from context input if available. + * + * Report an IO error to the parser context. + */ +void +xmlCtxtErrIO(xmlParserCtxtPtr ctxt, int code, const char *uri) +{ + const char *errstr, *msg, *str1, *str2; + xmlErrorLevel level; + + if (ctxt == NULL) + return; + + /* + * Only report a warning if a file could not be found. This should + * only be done for external entities, but the external entity loader + * of xsltproc can try multiple paths and assumes that ENOENT doesn't + * raise an error and aborts parsing. + */ + if (((code == XML_IO_ENOENT) || + (code == XML_IO_NETWORK_ATTEMPT) || + (code == XML_IO_UNKNOWN))) { + if (ctxt->validate == 0) + level = XML_ERR_WARNING; + else + level = XML_ERR_ERROR; + } else { + level = XML_ERR_FATAL; + } + + errstr = xmlErrString(code); + + if (uri == NULL) { + msg = "%s\n"; + str1 = errstr; + str2 = NULL; + } else { + msg = "failed to load \"%s\": %s\n"; + str1 = uri; + str2 = errstr; + } + + xmlCtxtErr(ctxt, NULL, XML_FROM_IO, code, level, + (const xmlChar *) uri, NULL, NULL, 0, + msg, str1, str2); +} + +/** + * xmlCtxtVErr: + * @ctxt: a parser context + * @node: the current node or NULL + * @domain: the domain for the error + * @code: the code for the error + * @level: the xmlErrorLevel for the error + * @str1: extra string info + * @str2: extra string info + * @str3: extra string info + * @int1: extra int info + * @msg: the message to display/transmit + * @ap: extra parameters for the message display + * + * Raise a parser error. */ -static void LIBXML_ATTR_FORMAT(2,0) -xmlErrInternal(xmlParserCtxtPtr ctxt, const char *msg, const xmlChar * str) +void +xmlCtxtVErr(xmlParserCtxtPtr ctxt, xmlNodePtr node, xmlErrorDomain domain, + xmlParserErrors code, xmlErrorLevel level, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, + int int1, const char *msg, va_list ap) { - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + void *data = NULL; + const char *file = NULL; + int line = 0; + int col = 0; + int res; + + if (code == XML_ERR_NO_MEMORY) { + xmlCtxtErrMemory(ctxt); + return; + } + + if (PARSER_STOPPED(ctxt)) return; - if (ctxt != NULL) - ctxt->errNo = XML_ERR_INTERNAL_ERROR; - __xmlRaiseError(NULL, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, XML_ERR_INTERNAL_ERROR, - XML_ERR_FATAL, NULL, 0, (const char *) str, NULL, NULL, - 0, 0, msg, str); - if (ctxt != NULL) { + + if (level == XML_ERR_WARNING) { + if (ctxt->nbWarnings >= XML_MAX_ERRORS) + return; + ctxt->nbWarnings += 1; + } else { + if (ctxt->nbErrors >= XML_MAX_ERRORS) + return; + ctxt->nbErrors += 1; + } + + if (((ctxt->options & XML_PARSE_NOERROR) == 0) && + ((level != XML_ERR_WARNING) || + ((ctxt->options & XML_PARSE_NOWARNING) == 0))) { + if (ctxt->errorHandler) { + schannel = ctxt->errorHandler; + data = ctxt->errorCtxt; + } else if ((ctxt->sax->initialized == XML_SAX2_MAGIC) && + (ctxt->sax->serror != NULL)) { + schannel = ctxt->sax->serror; + data = ctxt->userData; + } else if ((domain == XML_FROM_VALID) || (domain == XML_FROM_DTD)) { + if (level == XML_ERR_WARNING) + channel = ctxt->vctxt.warning; + else + channel = ctxt->vctxt.error; + data = ctxt->vctxt.userData; + } else { + if (level == XML_ERR_WARNING) + channel = ctxt->sax->warning; + else + channel = ctxt->sax->error; + data = ctxt->userData; + } + } + + if (ctxt->input != NULL) { + xmlParserInputPtr input = ctxt->input; + + if ((input->filename == NULL) && + (ctxt->inputNr > 1)) { + input = ctxt->inputTab[ctxt->inputNr - 2]; + } + file = input->filename; + line = input->line; + col = input->col; + } + + res = xmlVRaiseError(schannel, channel, data, ctxt, node, domain, code, + level, file, line, (const char *) str1, + (const char *) str2, (const char *) str3, int1, col, + msg, ap); + + if (res < 0) { + xmlCtxtErrMemory(ctxt); + return; + } + + if (level >= XML_ERR_ERROR) + ctxt->errNo = code; + if (level == XML_ERR_FATAL) { ctxt->wellFormed = 0; if (ctxt->recovery == 0) ctxt->disableSAX = 1; } + + return; +} + +/** + * xmlCtxtErr: + * @ctxt: a parser context + * @node: the current node or NULL + * @domain: the domain for the error + * @code: the code for the error + * @level: the xmlErrorLevel for the error + * @str1: extra string info + * @str2: extra string info + * @str3: extra string info + * @int1: extra int info + * @msg: the message to display/transmit + * @...: extra parameters for the message display + * + * Raise a parser error. + */ +void +xmlCtxtErr(xmlParserCtxtPtr ctxt, xmlNodePtr node, xmlErrorDomain domain, + xmlParserErrors code, xmlErrorLevel level, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, + int int1, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + xmlCtxtVErr(ctxt, node, domain, code, level, + str1, str2, str3, int1, msg, ap); + va_end(ap); } /** * xmlFatalErr: * @ctxt: an XML parser context - * @error: the error number + * @code: the error number * @info: extra information string * * Handle a fatal parser error, i.e. violating Well-Formedness constraints */ void -xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *info) +xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors code, const char *info) { const char *errmsg; + xmlErrorLevel level; + + if (code == XML_ERR_UNSUPPORTED_ENCODING) + level = XML_ERR_WARNING; + else + level = XML_ERR_FATAL; + + errmsg = xmlErrString(code); - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - switch (error) { - case XML_ERR_INVALID_HEX_CHARREF: - errmsg = "CharRef: invalid hexadecimal value"; - break; - case XML_ERR_INVALID_DEC_CHARREF: - errmsg = "CharRef: invalid decimal value"; - break; - case XML_ERR_INVALID_CHARREF: - errmsg = "CharRef: invalid value"; - break; - case XML_ERR_INTERNAL_ERROR: - errmsg = "internal error"; - break; - case XML_ERR_PEREF_AT_EOF: - errmsg = "PEReference at end of document"; - break; - case XML_ERR_PEREF_IN_PROLOG: - errmsg = "PEReference in prolog"; - break; - case XML_ERR_PEREF_IN_EPILOG: - errmsg = "PEReference in epilog"; - break; - case XML_ERR_PEREF_NO_NAME: - errmsg = "PEReference: no name"; - break; - case XML_ERR_PEREF_SEMICOL_MISSING: - errmsg = "PEReference: expecting ';'"; - break; - case XML_ERR_ENTITY_LOOP: - errmsg = "Detected an entity reference loop"; - break; - case XML_ERR_ENTITY_NOT_STARTED: - errmsg = "EntityValue: \" or ' expected"; - break; - case XML_ERR_ENTITY_PE_INTERNAL: - errmsg = "PEReferences forbidden in internal subset"; - break; - case XML_ERR_ENTITY_NOT_FINISHED: - errmsg = "EntityValue: \" or ' expected"; - break; - case XML_ERR_ATTRIBUTE_NOT_STARTED: - errmsg = "AttValue: \" or ' expected"; - break; - case XML_ERR_LT_IN_ATTRIBUTE: - errmsg = "Unescaped '<' not allowed in attributes values"; - break; - case XML_ERR_LITERAL_NOT_STARTED: - errmsg = "SystemLiteral \" or ' expected"; - break; - case XML_ERR_LITERAL_NOT_FINISHED: - errmsg = "Unfinished System or Public ID \" or ' expected"; - break; - case XML_ERR_MISPLACED_CDATA_END: - errmsg = "Sequence ']]>' not allowed in content"; - break; - case XML_ERR_URI_REQUIRED: - errmsg = "SYSTEM or PUBLIC, the URI is missing"; - break; - case XML_ERR_PUBID_REQUIRED: - errmsg = "PUBLIC, the Public Identifier is missing"; - break; - case XML_ERR_HYPHEN_IN_COMMENT: - errmsg = "Comment must not contain '--' (double-hyphen)"; - break; - case XML_ERR_PI_NOT_STARTED: - errmsg = "xmlParsePI : no target name"; - break; - case XML_ERR_RESERVED_XML_NAME: - errmsg = "Invalid PI name"; - break; - case XML_ERR_NOTATION_NOT_STARTED: - errmsg = "NOTATION: Name expected here"; - break; - case XML_ERR_NOTATION_NOT_FINISHED: - errmsg = "'>' required to close NOTATION declaration"; - break; - case XML_ERR_VALUE_REQUIRED: - errmsg = "Entity value required"; - break; - case XML_ERR_URI_FRAGMENT: - errmsg = "Fragment not allowed"; - break; - case XML_ERR_ATTLIST_NOT_STARTED: - errmsg = "'(' required to start ATTLIST enumeration"; - break; - case XML_ERR_NMTOKEN_REQUIRED: - errmsg = "NmToken expected in ATTLIST enumeration"; - break; - case XML_ERR_ATTLIST_NOT_FINISHED: - errmsg = "')' required to finish ATTLIST enumeration"; - break; - case XML_ERR_MIXED_NOT_STARTED: - errmsg = "MixedContentDecl : '|' or ')*' expected"; - break; - case XML_ERR_PCDATA_REQUIRED: - errmsg = "MixedContentDecl : '#PCDATA' expected"; - break; - case XML_ERR_ELEMCONTENT_NOT_STARTED: - errmsg = "ContentDecl : Name or '(' expected"; - break; - case XML_ERR_ELEMCONTENT_NOT_FINISHED: - errmsg = "ContentDecl : ',' '|' or ')' expected"; - break; - case XML_ERR_PEREF_IN_INT_SUBSET: - errmsg = - "PEReference: forbidden within markup decl in internal subset"; - break; - case XML_ERR_GT_REQUIRED: - errmsg = "expected '>'"; - break; - case XML_ERR_CONDSEC_INVALID: - errmsg = "XML conditional section '[' expected"; - break; - case XML_ERR_EXT_SUBSET_NOT_FINISHED: - errmsg = "Content error in the external subset"; - break; - case XML_ERR_CONDSEC_INVALID_KEYWORD: - errmsg = - "conditional section INCLUDE or IGNORE keyword expected"; - break; - case XML_ERR_CONDSEC_NOT_FINISHED: - errmsg = "XML conditional section not closed"; - break; - case XML_ERR_XMLDECL_NOT_STARTED: - errmsg = "Text declaration '' expected"; - break; - case XML_ERR_EXT_ENTITY_STANDALONE: - errmsg = "external parsed entities cannot be standalone"; - break; - case XML_ERR_ENTITYREF_SEMICOL_MISSING: - errmsg = "EntityRef: expecting ';'"; - break; - case XML_ERR_DOCTYPE_NOT_FINISHED: - errmsg = "DOCTYPE improperly terminated"; - break; - case XML_ERR_LTSLASH_REQUIRED: - errmsg = "EndTag: 'errNo = error; if (info == NULL) { - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_FATAL, NULL, 0, info, NULL, NULL, 0, 0, "%s\n", - errmsg); + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, code, level, + NULL, NULL, NULL, 0, "%s\n", errmsg); } else { - __xmlRaiseError(NULL, NULL, NULL, ctxt, NULL, XML_FROM_PARSER, error, - XML_ERR_FATAL, NULL, 0, info, NULL, NULL, 0, 0, "%s: %s\n", - errmsg, info); - } - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; - } -} - -/** - * xmlErrEncodingInt: - * @ctxt: an XML parser context - * @error: the error number - * @msg: the error message - * @val: an integer value - * - * n encoding error - */ -static void LIBXML_ATTR_FORMAT(3,0) -xmlErrEncodingInt(xmlParserCtxtPtr ctxt, xmlParserErrors error, - const char *msg, int val) -{ - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if (ctxt != NULL) - ctxt->errNo = error; - __xmlRaiseError(NULL, NULL, NULL, - ctxt, NULL, XML_FROM_PARSER, error, XML_ERR_FATAL, - NULL, 0, NULL, NULL, NULL, val, 0, msg, val); - if (ctxt != NULL) { - ctxt->wellFormed = 0; - if (ctxt->recovery == 0) - ctxt->disableSAX = 1; + xmlCtxtErr(ctxt, NULL, XML_FROM_PARSER, code, level, + (const xmlChar *) info, NULL, NULL, 0, + "%s: %s\n", errmsg, info); } } @@ -461,28 +414,8 @@ void xmlHaltParser(xmlParserCtxtPtr ctxt) { if (ctxt == NULL) return; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; - while (ctxt->inputNr > 1) - xmlFreeInputStream(inputPop(ctxt)); - if (ctxt->input != NULL) { - /* - * in case there was a specific allocation deallocate before - * overriding base - */ - if (ctxt->input->free != NULL) { - ctxt->input->free((xmlChar *) ctxt->input->base); - ctxt->input->free = NULL; - } - if (ctxt->input->buf != NULL) { - xmlFreeParserInputBuffer(ctxt->input->buf); - ctxt->input->buf = NULL; - } - ctxt->input->cur = BAD_CAST""; - ctxt->input->length = 0; - ctxt->input->base = ctxt->input->cur; - ctxt->input->end = ctxt->input->cur; - } + ctxt->instate = XML_PARSER_EOF; /* TODO: Remove after refactoring */ + ctxt->disableSAX = 2; } /** @@ -511,14 +444,17 @@ int xmlParserGrow(xmlParserCtxtPtr ctxt) { xmlParserInputPtr in = ctxt->input; xmlParserInputBufferPtr buf = in->buf; - ptrdiff_t curEnd = in->end - in->cur; - ptrdiff_t curBase = in->cur - in->base; + size_t curEnd = in->end - in->cur; + size_t curBase = in->cur - in->base; + size_t maxLength = (ctxt->options & XML_PARSE_HUGE) ? + XML_MAX_HUGE_LENGTH : + XML_MAX_LOOKUP_LIMIT; int ret; if (buf == NULL) return(0); /* Don't grow push parser buffer. */ - if ((ctxt->progressive) && (ctxt->inputNr <= 1)) + if (PARSER_PROGRESSIVE(ctxt)) return(0); /* Don't grow memory buffers. */ if ((buf->encoder == NULL) && (buf->readcallback == NULL)) @@ -526,10 +462,9 @@ xmlParserGrow(xmlParserCtxtPtr ctxt) { if (buf->error != 0) return(-1); - if (((curEnd > XML_MAX_LOOKUP_LIMIT) || - (curBase > XML_MAX_LOOKUP_LIMIT)) && - ((ctxt->options & XML_PARSE_HUGE) == 0)) { - xmlErrMemory(ctxt, "Huge input lookup"); + if (curBase > maxLength) { + xmlFatalErr(ctxt, XML_ERR_RESOURCE_LIMIT, + "Buffer size limit exceeded, try XML_PARSE_HUGE\n"); xmlHaltParser(ctxt); return(-1); } @@ -541,10 +476,7 @@ xmlParserGrow(xmlParserCtxtPtr ctxt) { xmlBufUpdateInput(buf->buffer, in, curBase); if (ret < 0) { - xmlFatalErr(ctxt, buf->error, NULL); - /* Buffer contents may be lost in case of memory errors. */ - if (buf->error == XML_ERR_NO_MEMORY) - xmlHaltParser(ctxt); + xmlCtxtErrIO(ctxt, buf->error, NULL); } return(ret); @@ -612,7 +544,7 @@ xmlParserShrink(xmlParserCtxtPtr ctxt) { if (buf == NULL) return; /* Don't shrink pull parser memory buffers. */ - if (((ctxt->progressive == 0) || (ctxt->inputNr > 1)) && + if ((!PARSER_PROGRESSIVE(ctxt)) && (buf->encoder == NULL) && (buf->readcallback == NULL)) return; @@ -712,16 +644,14 @@ xmlNextChar(xmlParserCtxtPtr ctxt) size_t avail; int c; - if ((ctxt == NULL) || (ctxt->instate == XML_PARSER_EOF) || - (ctxt->input == NULL)) + if ((ctxt == NULL) || (ctxt->input == NULL)) return; avail = ctxt->input->end - ctxt->input->cur; if (avail < INPUT_CHUNK) { xmlParserGrow(ctxt); - if ((ctxt->instate == XML_PARSER_EOF) || - (ctxt->input->cur >= ctxt->input->end)) + if (ctxt->input->cur >= ctxt->input->end) return; avail = ctxt->input->end - ctxt->input->cur; } @@ -788,21 +718,7 @@ xmlNextChar(xmlParserCtxtPtr ctxt) encoding_error: /* Only report the first error */ if ((ctxt->input->flags & XML_INPUT_ENCODING_ERROR) == 0) { - if ((ctxt == NULL) || (ctxt->input == NULL) || - (ctxt->input->end - ctxt->input->cur < 4)) { - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n", - NULL, NULL); - } else { - char buffer[150]; - - snprintf(buffer, 149, "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n", - ctxt->input->cur[0], ctxt->input->cur[1], - ctxt->input->cur[2], ctxt->input->cur[3]); - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n%s", - BAD_CAST buffer, NULL); - } + xmlCtxtErrIO(ctxt, XML_ERR_INVALID_ENCODING, NULL); ctxt->input->flags |= XML_INPUT_ENCODING_ERROR; } ctxt->input->cur++; @@ -836,15 +752,11 @@ xmlCurrentChar(xmlParserCtxtPtr ctxt, int *len) { int c; if ((ctxt == NULL) || (len == NULL) || (ctxt->input == NULL)) return(0); - if (ctxt->instate == XML_PARSER_EOF) - return(0); avail = ctxt->input->end - ctxt->input->cur; if (avail < INPUT_CHUNK) { xmlParserGrow(ctxt); - if (ctxt->instate == XML_PARSER_EOF) - return(0); avail = ctxt->input->end - ctxt->input->cur; } @@ -879,8 +791,8 @@ xmlCurrentChar(xmlParserCtxtPtr ctxt, int *len) { * TODO: Null bytes should be handled by callers, * but this can be tricky. */ - xmlErrEncodingInt(ctxt, XML_ERR_INVALID_CHAR, - "Char 0x0 out of allowed range\n", c); + xmlFatalErr(ctxt, XML_ERR_INVALID_CHAR, + "Char 0x0 out of allowed range\n"); } } else { *len = 1; @@ -942,20 +854,7 @@ xmlCurrentChar(xmlParserCtxtPtr ctxt, int *len) { encoding_error: /* Only report the first error */ if ((ctxt->input->flags & XML_INPUT_ENCODING_ERROR) == 0) { - if (ctxt->input->end - ctxt->input->cur < 4) { - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n", - NULL, NULL); - } else { - char buffer[150]; - - snprintf(&buffer[0], 149, "Bytes: 0x%02X 0x%02X 0x%02X 0x%02X\n", - ctxt->input->cur[0], ctxt->input->cur[1], - ctxt->input->cur[2], ctxt->input->cur[3]); - __xmlErrEncoding(ctxt, XML_ERR_INVALID_CHAR, - "Input is not proper UTF-8, indicate encoding !\n%s", - BAD_CAST buffer, NULL); - } + xmlCtxtErrIO(ctxt, XML_ERR_INVALID_ENCODING, NULL); ctxt->input->flags |= XML_INPUT_ENCODING_ERROR; } *len = 1; @@ -1029,9 +928,10 @@ xmlCopyCharMultiByte(xmlChar *out, int val) { else if (val < 0x10000) { *out++= (val >> 12) | 0xE0; bits= 6;} else if (val < 0x110000) { *out++= (val >> 18) | 0xF0; bits= 12; } else { - xmlErrEncodingInt(NULL, XML_ERR_INVALID_CHAR, - "Internal error, xmlCopyCharMultiByte 0x%X out of bound\n", - val); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + fprintf(stderr, "xmlCopyCharMultiByte: codepoint out of range\n"); + abort(); +#endif return(0); } for ( ; bits >= 0; bits-= 6) @@ -1070,24 +970,30 @@ xmlCopyChar(int len ATTRIBUTE_UNUSED, xmlChar *out, int val) { * * ************************************************************************/ -static xmlCharEncodingHandlerPtr -xmlDetectEBCDIC(xmlParserInputPtr input) { +static int +xmlDetectEBCDIC(xmlParserInputPtr input, xmlCharEncodingHandlerPtr *hout) { xmlChar out[200]; xmlCharEncodingHandlerPtr handler; int inlen, outlen, res, i; + *hout = NULL; + /* * To detect the EBCDIC code page, we convert the first 200 bytes * to EBCDIC-US and try to find the encoding declaration. */ - handler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC); - if (handler == NULL) - return(NULL); + res = xmlLookupCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC, &handler); + if (res != 0) + return(res); outlen = sizeof(out) - 1; inlen = input->end - input->cur; res = xmlEncInputChunk(handler, out, &outlen, input->cur, &inlen); + /* + * Return the EBCDIC handler if decoding failed. The error will + * be reported later. + */ if (res < 0) - return(handler); + goto done; out[outlen] = 0; for (i = 0; i < outlen; i++) { @@ -1119,15 +1025,25 @@ xmlDetectEBCDIC(xmlParserInputPtr input) { break; out[i] = 0; xmlCharEncCloseFunc(handler); - return(xmlFindCharEncodingHandler((char *) out + start)); + res = xmlOpenCharEncodingHandler((char *) out + start, + /* output */ 0, &handler); + if (res != 0) + return(res); + *hout = handler; + return(0); } } +done: /* - * ICU handlers are stateful, so we have to recreate them. + * Encoding handlers are stateful, so we have to recreate them. */ xmlCharEncCloseFunc(handler); - return(xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC)); + res = xmlLookupCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC, &handler); + if (res != 0) + return(res); + *hout = handler; + return(0); } /** @@ -1135,10 +1051,11 @@ xmlDetectEBCDIC(xmlParserInputPtr input) { * @ctxt: the parser context * @enc: the encoding value (number) * - * Use encoding specified by enum to decode input data. + * Use encoding specified by enum to decode input data. This overrides + * the encoding found in the XML declaration. * - * This function can be used to enforce the encoding of chunks passed - * to xmlParseChunk. + * This function can also be used to override the encoding of chunks + * passed to xmlParseChunk. * * Returns 0 in case of success, -1 otherwise */ @@ -1146,8 +1063,8 @@ int xmlSwitchEncoding(xmlParserCtxtPtr ctxt, xmlCharEncoding enc) { xmlCharEncodingHandlerPtr handler = NULL; - int check = 1; int ret; + int res; if ((ctxt == NULL) || (ctxt->input == NULL)) return(-1); @@ -1156,28 +1073,20 @@ xmlSwitchEncoding(xmlParserCtxtPtr ctxt, xmlCharEncoding enc) case XML_CHAR_ENCODING_NONE: case XML_CHAR_ENCODING_UTF8: case XML_CHAR_ENCODING_ASCII: - check = 0; + res = 0; break; case XML_CHAR_ENCODING_EBCDIC: - handler = xmlDetectEBCDIC(ctxt->input); + res = xmlDetectEBCDIC(ctxt->input, &handler); break; default: - handler = xmlGetCharEncodingHandler(enc); + res = xmlLookupCharEncodingHandler(enc, &handler); break; } - if ((check) && (handler == NULL)) { + if (res != 0) { const char *name = xmlGetCharEncodingName(enc); - __xmlErrEncoding(ctxt, XML_ERR_UNSUPPORTED_ENCODING, - "encoding not supported: %s\n", - BAD_CAST (name ? name : ""), NULL); - /* - * TODO: We could recover from errors in external entities - * if we didn't stop the parser. But most callers of this - * function don't check the return value. - */ - xmlStopParser(ctxt); + xmlFatalErr(ctxt, res, (name ? name : "")); return(-1); } @@ -1191,8 +1100,59 @@ xmlSwitchEncoding(xmlParserCtxtPtr ctxt, xmlCharEncoding enc) } /** - * xmlSwitchInputEncoding: + * xmlSwitchEncodingName: + * @ctxt: the parser context, only for error reporting + * @input: the input strea, + * @encoding: the encoding name + * + * Available since 2.13.0. + * + * Returns 0 in case of success, -1 otherwise + */ +static int +xmlSwitchInputEncodingName(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, + const char *encoding) { + xmlCharEncodingHandlerPtr handler; + int res; + + if (encoding == NULL) + return(-1); + + res = xmlOpenCharEncodingHandler(encoding, /* output */ 0, &handler); + if (res != 0) { + xmlFatalErr(ctxt, res, encoding); + return(-1); + } + + return(xmlSwitchInputEncoding(ctxt, input, handler)); +} + +/** + * xmlSwitchEncodingName: * @ctxt: the parser context + * @encoding: the encoding name + * + * Use specified encoding to decode input data. This overrides the + * encoding found in the XML declaration. + * + * This function can also be used to override the encoding of chunks + * passed to xmlParseChunk. + * + * Available since 2.13.0. + * + * Returns 0 in case of success, -1 otherwise + */ +int +xmlSwitchEncodingName(xmlParserCtxtPtr ctxt, const char *encoding) { + if (ctxt == NULL) + return(-1); + + return(xmlSwitchInputEncodingName(ctxt, ctxt->input, encoding)); +} + +/** + * xmlSwitchInputEncoding: + * @ctxt: the parser context, only for error reporting * @input: the input stream * @handler: the encoding handler * @@ -1250,8 +1210,15 @@ xmlSwitchInputEncoding(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, * Is there already some content down the pipe to convert ? */ if (xmlBufIsEmpty(in->buffer) == 0) { + xmlBufPtr buf; size_t processed; + buf = xmlBufCreate(); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + return(-1); + } + /* * Shrink the current input buffer. * Move it as the raw buffer and create a new input buffer @@ -1260,16 +1227,15 @@ xmlSwitchInputEncoding(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, xmlBufShrink(in->buffer, processed); input->consumed += processed; in->raw = in->buffer; - in->buffer = xmlBufCreate(); + in->buffer = buf; in->rawconsumed = processed; nbchars = xmlCharEncInput(in); xmlBufResetInput(in->buffer, input); - if (nbchars < 0) { - /* TODO: This could be an out of memory or an encoding error. */ - xmlErrInternal(ctxt, - "switching encoding: encoder error\n", - NULL); + if (nbchars == XML_ENC_ERR_MEMORY) { + xmlCtxtErrMemory(ctxt); + } else if (nbchars < 0) { + xmlCtxtErrIO(ctxt, in->error, NULL); xmlHaltParser(ctxt); return (-1); } @@ -1412,23 +1378,9 @@ xmlDetectEncoding(xmlParserCtxtPtr ctxt) { */ void xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding) { - if (ctxt->encoding != NULL) - xmlFree((xmlChar *) ctxt->encoding); - ctxt->encoding = encoding; - if (((ctxt->input->flags & XML_INPUT_HAS_ENCODING) == 0) && ((ctxt->options & XML_PARSE_IGNORE_ENC) == 0)) { - xmlCharEncodingHandlerPtr handler; - - handler = xmlFindCharEncodingHandler((const char *) encoding); - if (handler == NULL) { - __xmlErrEncoding(ctxt, XML_ERR_UNSUPPORTED_ENCODING, - "Unsupported encoding: %s\n", - encoding, NULL); - return; - } - - xmlSwitchToEncoding(ctxt, handler); + xmlSwitchEncodingName(ctxt, (const char *) encoding); ctxt->input->flags |= XML_INPUT_USES_ENC_DECL; } else if (ctxt->input->flags & XML_INPUT_AUTO_ENCODING) { static const char *allowedUTF8[] = { @@ -1474,9 +1426,41 @@ xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding) { "Encoding '%s' doesn't match " "auto-detected '%s'\n", encoding, BAD_CAST autoEnc); + xmlFree(encoding); + encoding = xmlStrdup(BAD_CAST autoEnc); + if (encoding == NULL) + xmlCtxtErrMemory(ctxt); } } } + + if (ctxt->encoding != NULL) + xmlFree((xmlChar *) ctxt->encoding); + ctxt->encoding = encoding; +} + +/** + * xmlGetActualEncoding: + * @ctxt: the parser context + * + * Returns the actual used to parse the document. This can differ from + * the declared encoding. + */ +const xmlChar * +xmlGetActualEncoding(xmlParserCtxtPtr ctxt) { + const xmlChar *encoding = NULL; + + if ((ctxt->input->flags & XML_INPUT_USES_ENC_DECL) || + (ctxt->input->flags & XML_INPUT_AUTO_ENCODING)) { + /* Preserve encoding exactly */ + encoding = ctxt->encoding; + } else if ((ctxt->input->buf) && (ctxt->input->buf->encoder)) { + encoding = BAD_CAST ctxt->input->buf->encoder->name; + } else if (ctxt->input->flags & XML_INPUT_HAS_ENCODING) { + encoding = BAD_CAST "UTF-8"; + } + + return(encoding); } /************************************************************************ @@ -1496,7 +1480,6 @@ xmlFreeInputStream(xmlParserInputPtr input) { if (input == NULL) return; if (input->filename != NULL) xmlFree((char *) input->filename); - if (input->directory != NULL) xmlFree((char *) input->directory); if (input->version != NULL) xmlFree((char *) input->version); if ((input->free != NULL) && (input->base != NULL)) input->free((xmlChar *) input->base); @@ -1519,7 +1502,7 @@ xmlNewInputStream(xmlParserCtxtPtr ctxt) { input = (xmlParserInputPtr) xmlMalloc(sizeof(xmlParserInput)); if (input == NULL) { - xmlErrMemory(ctxt, "couldn't allocate a new input stream\n"); + xmlCtxtErrMemory(ctxt); return(NULL); } memset(input, 0, sizeof(xmlParserInput)); @@ -1533,7 +1516,7 @@ xmlNewInputStream(xmlParserCtxtPtr ctxt) { */ if (ctxt != NULL) { if (input->id >= INT_MAX) { - xmlErrMemory(ctxt, "Input ID overflow\n"); + xmlCtxtErrMemory(ctxt); return(NULL); } input->id = ctxt->input_id++; @@ -1543,43 +1526,311 @@ xmlNewInputStream(xmlParserCtxtPtr ctxt) { } /** - * xmlNewIOInputStream: - * @ctxt: an XML parser context - * @input: an I/O Input - * @enc: the charset encoding if known + * xmlNewInputURL: + * @ctxt: parser context + * @url: filename or URL + * @publicId: publid ID from doctype (optional) + * @encoding: character encoding (optional) + * @flags: unused, pass 0 * - * Create a new input stream structure encapsulating the @input into - * a stream suitable for the parser. + * Creates a new parser input from the filesystem, the network or + * a user-defined resource loader. * - * Returns the new input stream or NULL + * Returns a new parser input. */ xmlParserInputPtr -xmlNewIOInputStream(xmlParserCtxtPtr ctxt, xmlParserInputBufferPtr input, - xmlCharEncoding enc) { - xmlParserInputPtr inputStream; +xmlNewInputURL(xmlParserCtxtPtr ctxt, const char *url, const char *publicId, + const char *encoding, int flags ATTRIBUTE_UNUSED) { + xmlParserInputPtr input; - if (input == NULL) return(NULL); - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, "new input from I/O\n"); - inputStream = xmlNewInputStream(ctxt); - if (inputStream == NULL) { + if ((ctxt == NULL) || (url == NULL)) return(NULL); - } - inputStream->filename = NULL; - inputStream->buf = input; - xmlBufResetInput(inputStream->buf->buffer, inputStream); - if (enc != XML_CHAR_ENCODING_NONE) { - xmlSwitchEncoding(ctxt, enc); + input = xmlLoadExternalEntity(url, publicId, ctxt); + if (input == NULL) + return(NULL); + + if (encoding != NULL) + xmlSwitchInputEncodingName(ctxt, input, encoding); + + return(input); +} + +/** + * xmlNewInputInternal: + * @ctxt: parser context + * @buf: parser input buffer + * @filename: filename or URL + * @encoding: character encoding (optional) + * + * Internal helper function. + * + * Returns a new parser input. + */ +static xmlParserInputPtr +xmlNewInputInternal(xmlParserCtxtPtr ctxt, xmlParserInputBufferPtr buf, + const char *filename, const char *encoding) { + xmlParserInputPtr input; + + input = xmlNewInputStream(ctxt); + if (input == NULL) { + xmlFreeParserInputBuffer(buf); + return(NULL); } - return(inputStream); + input->buf = buf; + xmlBufResetInput(input->buf->buffer, input); + + if (filename != NULL) { + input->filename = xmlMemStrdup(filename); + if (input->filename == NULL) { + xmlCtxtErrMemory(ctxt); + xmlFreeInputStream(input); + return(NULL); + } + } + + if (encoding != NULL) + xmlSwitchInputEncodingName(ctxt, input, encoding); + + return(input); +} + +/** + * xmlNewInputMemory: + * @ctxt: parser context + * @url: base URL (optional) + * @mem: pointer to char array + * @size: size of array + * @encoding: character encoding (optional) + * @flags: optimization hints + * + * Creates a new parser input to read from a memory area. + * + * @url is used as base to resolve external entities and for + * error reporting. + * + * If the XML_INPUT_BUF_STATIC flag is set, the memory area must + * stay unchanged until parsing has finished. This can avoid + * temporary copies. + * + * If the XML_INPUT_BUF_ZERO_TERMINATED flag is set, the memory + * area must contain a zero byte after the buffer at position @size. + * This can avoid temporary copies. + * + * Returns a new parser input. + */ +xmlParserInputPtr +xmlNewInputMemory(xmlParserCtxtPtr ctxt, const char *url, + const void *mem, size_t size, + const char *encoding, int flags) { + xmlParserInputBufferPtr buf; + + if ((ctxt == NULL) || (mem == NULL)) + return(NULL); + + buf = xmlNewInputBufferMemory(mem, size, flags, XML_CHAR_ENCODING_NONE); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + return(NULL); + } + + return(xmlNewInputInternal(ctxt, buf, url, encoding)); +} + +/** + * xmlNewInputString: + * @ctxt: parser context + * @url: base URL (optional) + * @str: zero-terminated string + * @encoding: character encoding (optional) + * @flags: optimization hints + * + * Creates a new parser input to read from a zero-terminated string. + * + * @url is used as base to resolve external entities and for + * error reporting. + * + * If the XML_INPUT_BUF_STATIC flag is set, the string must + * stay unchanged until parsing has finished. This can avoid + * temporary copies. + * + * Returns a new parser input. + */ +xmlParserInputPtr +xmlNewInputString(xmlParserCtxtPtr ctxt, const char *url, + const char *str, const char *encoding, int flags) { + xmlParserInputBufferPtr buf; + + if ((ctxt == NULL) || (str == NULL)) + return(NULL); + + buf = xmlNewInputBufferString(str, flags); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + return(NULL); + } + + return(xmlNewInputInternal(ctxt, buf, url, encoding)); +} + +/** + * xmlNewInputFd: + * @ctxt: parser context + * @url: base URL (optional) + * @fd: file descriptor + * @encoding: character encoding (optional) + * @flags: unused, pass 0 + * + * Creates a new parser input to read from a zero-terminated string. + * + * @url is used as base to resolve external entities and for + * error reporting. + * + * @fd is closed after parsing has finished. + * + * Returns a new parser input. + */ +xmlParserInputPtr +xmlNewInputFd(xmlParserCtxtPtr ctxt, const char *url, + int fd, const char *encoding, int flags ATTRIBUTE_UNUSED) { + xmlParserInputBufferPtr buf; + + if ((ctxt == NULL) || (fd < 0)) + return(NULL); + + buf = xmlParserInputBufferCreateFd(fd, XML_CHAR_ENCODING_NONE); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + return(NULL); + } + + return(xmlNewInputInternal(ctxt, buf, url, encoding)); +} + +/** + * xmlNewInputIO: + * @ctxt: parser context + * @url: base URL (optional) + * @ioRead: read callback + * @ioClose: close callback (optional) + * @ioCtxt: IO context + * @encoding: character encoding (optional) + * @flags: unused, pass 0 + * + * Creates a new parser input to read from input callbacks and + * cintext. + * + * @url is used as base to resolve external entities and for + * error reporting. + * + * @ioRead is called to read new data into a provided buffer. + * It must return the number of bytes written into the buffer + * ot a negative xmlParserErrors code on failure. + * + * @ioClose is called after parsing has finished. + * + * @ioCtxt is an opaque pointer passed to the callbacks. + * + * Returns a new parser input. + */ +xmlParserInputPtr +xmlNewInputIO(xmlParserCtxtPtr ctxt, const char *url, + xmlInputReadCallback ioRead, xmlInputCloseCallback ioClose, + void *ioCtxt, + const char *encoding, int flags ATTRIBUTE_UNUSED) { + xmlParserInputBufferPtr buf; + + if ((ctxt == NULL) || (ioRead == NULL)) + return(NULL); + + buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + if (ioClose != NULL) + ioClose(ioCtxt); + return(NULL); + } + + buf->context = ioCtxt; + buf->readcallback = ioRead; + buf->closecallback = ioClose; + + return(xmlNewInputInternal(ctxt, buf, url, encoding)); +} + +/** + * xmlNewInputPush: + * @ctxt: parser context + * @url: base URL (optional) + * @chunk: pointer to char array + * @size: size of array + * @encoding: character encoding (optional) + * + * Creates a new parser input for a push parser. + * + * Returns a new parser input. + */ +xmlParserInputPtr +xmlNewInputPush(xmlParserCtxtPtr ctxt, const char *url, + const char *chunk, int size, const char *encoding) { + xmlParserInputBufferPtr buf; + xmlParserInputPtr input; + + buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); + if (buf == NULL) { + xmlCtxtErrMemory(ctxt); + return(NULL); + } + + input = xmlNewInputInternal(ctxt, buf, url, encoding); + if (input == NULL) + return(NULL); + + input->flags |= XML_INPUT_PROGRESSIVE; + + if ((size > 0) && (chunk != NULL)) { + int res; + + res = xmlParserInputBufferPush(input->buf, size, chunk); + xmlBufResetInput(input->buf->buffer, input); + if (res < 0) { + xmlCtxtErrIO(ctxt, input->buf->error, NULL); + xmlFreeInputStream(input); + return(NULL); + } + } + + return(input); +} + +/** + * xmlNewIOInputStream: + * @ctxt: an XML parser context + * @buf: an input buffer + * @enc: the charset encoding if known + * + * Create a new input stream structure encapsulating the @input into + * a stream suitable for the parser. + * + * Returns the new input stream or NULL + */ +xmlParserInputPtr +xmlNewIOInputStream(xmlParserCtxtPtr ctxt, xmlParserInputBufferPtr buf, + xmlCharEncoding enc) { + const char *encoding; + + if (buf == NULL) + return(NULL); + + encoding = xmlGetCharEncodingName(enc); + return(xmlNewInputInternal(ctxt, buf, NULL, encoding)); } /** * xmlNewEntityInputStream: * @ctxt: an XML parser context - * @entity: an Entity pointer + * @ent: an Entity pointer * * DEPRECATED: Internal function, do not use. * @@ -1588,61 +1839,27 @@ xmlNewIOInputStream(xmlParserCtxtPtr ctxt, xmlParserInputBufferPtr input, * Returns the new input stream or NULL */ xmlParserInputPtr -xmlNewEntityInputStream(xmlParserCtxtPtr ctxt, xmlEntityPtr entity) { +xmlNewEntityInputStream(xmlParserCtxtPtr ctxt, xmlEntityPtr ent) { xmlParserInputPtr input; - if (entity == NULL) { - xmlErrInternal(ctxt, "xmlNewEntityInputStream entity = NULL\n", - NULL); - return(NULL); - } - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "new input from entity: %s\n", entity->name); - if (entity->content == NULL) { - switch (entity->etype) { - case XML_EXTERNAL_GENERAL_UNPARSED_ENTITY: - xmlErrInternal(ctxt, "Cannot parse entity %s\n", - entity->name); - break; - case XML_EXTERNAL_GENERAL_PARSED_ENTITY: - case XML_EXTERNAL_PARAMETER_ENTITY: - input = xmlLoadExternalEntity((char *) entity->URI, - (char *) entity->ExternalID, ctxt); - if (input != NULL) - input->entity = entity; - return(input); - case XML_INTERNAL_GENERAL_ENTITY: - xmlErrInternal(ctxt, - "Internal entity %s without content !\n", - entity->name); - break; - case XML_INTERNAL_PARAMETER_ENTITY: - xmlErrInternal(ctxt, - "Internal parameter entity %s without content !\n", - entity->name); - break; - case XML_INTERNAL_PREDEFINED_ENTITY: - xmlErrInternal(ctxt, - "Predefined entity %s without content !\n", - entity->name); - break; - } - return(NULL); - } - input = xmlNewInputStream(ctxt); - if (input == NULL) { + if ((ctxt == NULL) || (ent == NULL)) return(NULL); + + if (ent->content != NULL) { + input = xmlNewInputString(ctxt, NULL, (const char *) ent->content, + NULL, XML_INPUT_BUF_STATIC); + } else if (ent->URI != NULL) { + input = xmlLoadExternalEntity((char *) ent->URI, + (char *) ent->ExternalID, ctxt); + } else { + return(NULL); } - if (entity->URI != NULL) - input->filename = (char *) xmlStrdup((xmlChar *) entity->URI); - input->base = entity->content; - if (entity->length == 0) - entity->length = xmlStrlen(entity->content); - input->cur = entity->content; - input->length = entity->length; - input->end = &entity->content[input->length]; - input->entity = entity; + + if (input == NULL) + return(NULL); + + input->entity = ent; + return(input); } @@ -1652,35 +1869,159 @@ xmlNewEntityInputStream(xmlParserCtxtPtr ctxt, xmlEntityPtr entity) { * @buffer: an memory buffer * * Create a new input stream based on a memory buffer. + * * Returns the new input stream */ xmlParserInputPtr xmlNewStringInputStream(xmlParserCtxtPtr ctxt, const xmlChar *buffer) { - xmlParserInputPtr input; - xmlParserInputBufferPtr buf; + return(xmlNewInputString(ctxt, NULL, (const char *) buffer, NULL, 0)); +} - if (buffer == NULL) { - xmlErrInternal(ctxt, "xmlNewStringInputStream string = NULL\n", - NULL); - return(NULL); - } - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "new fixed input: %.30s\n", buffer); - buf = xmlParserInputBufferCreateString(buffer); - if (buf == NULL) { - xmlErrMemory(ctxt, NULL); - return(NULL); + +/**************************************************************** + * * + * External entities loading * + * * + ****************************************************************/ + +#ifdef LIBXML_CATALOG_ENABLED + +/** + * xmlResolveResourceFromCatalog: + * @URL: the URL for the entity to load + * @ID: the System ID for the entity to load + * @ctxt: the context in which the entity is called or NULL + * + * Resolves the URL and ID against the appropriate catalog. + * This function is used by xmlDefaultExternalEntityLoader and + * xmlNoNetExternalEntityLoader. + * + * Returns a new allocated URL, or NULL. + */ +static xmlChar * +xmlResolveResourceFromCatalog(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlChar *resource = NULL; + xmlCatalogAllow pref; + + /* + * If the resource doesn't exists as a file, + * try to load it from the resource pointed in the catalogs + */ + pref = xmlCatalogGetDefaults(); + + if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) { + /* + * Do a local lookup + */ + if ((ctxt != NULL) && (ctxt->catalogs != NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_DOCUMENT))) { + resource = xmlCatalogLocalResolve(ctxt->catalogs, + (const xmlChar *)ID, + (const xmlChar *)URL); + } + /* + * Try a global lookup + */ + if ((resource == NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_GLOBAL))) { + resource = xmlCatalogResolve((const xmlChar *)ID, + (const xmlChar *)URL); + } + if ((resource == NULL) && (URL != NULL)) + resource = xmlStrdup((const xmlChar *) URL); + + /* + * TODO: do an URI lookup on the reference + */ + if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) { + xmlChar *tmp = NULL; + + if ((ctxt != NULL) && (ctxt->catalogs != NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_DOCUMENT))) { + tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource); + } + if ((tmp == NULL) && + ((pref == XML_CATA_ALLOW_ALL) || + (pref == XML_CATA_ALLOW_GLOBAL))) { + tmp = xmlCatalogResolveURI(resource); + } + + if (tmp != NULL) { + xmlFree(resource); + resource = tmp; + } + } } - input = xmlNewInputStream(ctxt); - if (input == NULL) { - xmlErrMemory(ctxt, "couldn't allocate a new input stream\n"); - xmlFreeParserInputBuffer(buf); - return(NULL); + + return resource; +} + +#endif + +/** + * xmlCheckHTTPInput: + * @ctxt: an XML parser context + * @ret: an XML parser input + * + * DEPRECATED: Internal function, don't use. + * + * Check an input in case it was created from an HTTP stream, in that + * case it will handle encoding and update of the base URL in case of + * redirection. It also checks for HTTP errors in which case the input + * is cleanly freed up and an appropriate error is raised in context + * + * Returns the input or NULL in case of HTTP error. + */ +xmlParserInputPtr +xmlCheckHTTPInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr ret) { + /* Avoid unused variable warning if features are disabled. */ + (void) ctxt; + +#ifdef LIBXML_HTTP_ENABLED + if ((ret != NULL) && (ret->buf != NULL) && + (ret->buf->readcallback == xmlIOHTTPRead) && + (ret->buf->context != NULL)) { + const char *encoding; + const char *redir; + const char *mime; + int code; + + code = xmlNanoHTTPReturnCode(ret->buf->context); + if (code >= 400) { + /* fatal error */ + if (ret->filename != NULL) + xmlCtxtErrIO(ctxt, XML_IO_LOAD_ERROR, ret->filename); + else + xmlCtxtErrIO(ctxt, XML_IO_LOAD_ERROR, ""); + xmlFreeInputStream(ret); + ret = NULL; + } else { + + mime = xmlNanoHTTPMimeType(ret->buf->context); + if ((xmlStrstr(BAD_CAST mime, BAD_CAST "/xml")) || + (xmlStrstr(BAD_CAST mime, BAD_CAST "+xml"))) { + encoding = xmlNanoHTTPEncoding(ret->buf->context); + if (encoding != NULL) + xmlSwitchEncodingName(ctxt, encoding); +#if 0 + } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) { +#endif + } + redir = xmlNanoHTTPRedir(ret->buf->context); + if (redir != NULL) { + if (ret->filename != NULL) + xmlFree((xmlChar *) ret->filename); + ret->filename = + (char *) xmlStrdup((const xmlChar *) redir); + } + } } - input->buf = buf; - xmlBufResetInput(input->buf->buffer, input); - return(input); +#endif + return(ret); } /** @@ -1696,22 +2037,17 @@ xmlParserInputPtr xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename) { xmlParserInputBufferPtr buf; xmlParserInputPtr inputStream; - char *directory = NULL; - xmlChar *URI = NULL; - - if (xmlParserDebugEntities) - xmlGenericError(xmlGenericErrorContext, - "new input from file: %s\n", filename); - if (ctxt == NULL) return(NULL); - buf = xmlParserInputBufferCreateFilename(filename, XML_CHAR_ENCODING_NONE); + const xmlChar *URI; + xmlChar *canonic; + int code; + + if ((ctxt == NULL) || (filename == NULL)) + return(NULL); + + code = xmlParserInputBufferCreateFilenameSafe(filename, + XML_CHAR_ENCODING_NONE, &buf); if (buf == NULL) { - if (filename == NULL) - __xmlLoaderErr(ctxt, - "failed to load external entity: NULL filename \n", - NULL); - else - __xmlLoaderErr(ctxt, "failed to load external entity \"%s\"\n", - (const char *) filename); + xmlCtxtErrIO(ctxt, code, filename); return(NULL); } @@ -1727,21 +2063,189 @@ xmlNewInputFromFile(xmlParserCtxtPtr ctxt, const char *filename) { return(NULL); if (inputStream->filename == NULL) - URI = xmlStrdup((xmlChar *) filename); + URI = (xmlChar *) filename; else - URI = xmlStrdup((xmlChar *) inputStream->filename); - directory = xmlParserGetDirectory((const char *) URI); - if (inputStream->filename != NULL) xmlFree((char *)inputStream->filename); - inputStream->filename = (char *) xmlCanonicPath((const xmlChar *) URI); - if (URI != NULL) xmlFree((char *) URI); - inputStream->directory = directory; + URI = (xmlChar *) inputStream->filename; + canonic = xmlCanonicPath(URI); + if (canonic == NULL) { + xmlCtxtErrMemory(ctxt); + xmlFreeInputStream(inputStream); + return(NULL); + } + if (inputStream->filename != NULL) + xmlFree((char *) inputStream->filename); + inputStream->filename = (char *) canonic; xmlBufResetInput(inputStream->buf->buffer, inputStream); - if ((ctxt->directory == NULL) && (directory != NULL)) - ctxt->directory = (char *) xmlStrdup((const xmlChar *) directory); + return(inputStream); } +/** + * xmlDefaultExternalEntityLoader: + * @URL: the URL for the entity to load + * @ID: the System ID for the entity to load + * @ctxt: the context in which the entity is called or NULL + * + * By default we don't load external entities, yet. + * + * Returns a new allocated xmlParserInputPtr, or NULL. + */ +static xmlParserInputPtr +xmlDefaultExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) +{ + xmlParserInputPtr ret = NULL; + xmlChar *resource = NULL; + + if (URL == NULL) + return(NULL); + + if ((ctxt != NULL) && (ctxt->options & XML_PARSE_NONET)) { + int options = ctxt->options; + + ctxt->options -= XML_PARSE_NONET; + ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); + ctxt->options = options; + return(ret); + } +#ifdef LIBXML_CATALOG_ENABLED + resource = xmlResolveResourceFromCatalog(URL, ID, ctxt); +#endif + + if (resource == NULL) + resource = (xmlChar *) URL; + + ret = xmlNewInputFromFile(ctxt, (const char *) resource); + if ((resource != NULL) && (resource != (xmlChar *) URL)) + xmlFree(resource); + return (ret); +} + +/** + * xmlNoNetExternalEntityLoader: + * @URL: the URL for the entity to load + * @ID: the System ID for the entity to load + * @ctxt: the context in which the entity is called or NULL + * + * A specific entity loader disabling network accesses, though still + * allowing local catalog accesses for resolution. + * + * Returns a new allocated xmlParserInputPtr, or NULL. + */ +xmlParserInputPtr +xmlNoNetExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlParserInputPtr input = NULL; + xmlChar *resource = NULL; + +#ifdef LIBXML_CATALOG_ENABLED + resource = xmlResolveResourceFromCatalog(URL, ID, ctxt); +#endif + + if (resource == NULL) + resource = (xmlChar *) URL; + + if (resource != NULL) { + if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) || + (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) { + xmlCtxtErrIO(ctxt, XML_IO_NETWORK_ATTEMPT, + (const char *) resource); + /* + * Also forward the error directly to the global error + * handler, which the XML::LibXML test suite expects. + */ + __xmlIOErr(XML_FROM_IO, XML_IO_NETWORK_ATTEMPT, + (const char *) resource); + if (resource != (xmlChar *) URL) + xmlFree(resource); + return(NULL); + } + } + input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt); + if (resource != (xmlChar *) URL) + xmlFree(resource); + return(input); +} + +/* + * This global has to die eventually + */ +static xmlExternalEntityLoader +xmlCurrentExternalEntityLoader = xmlDefaultExternalEntityLoader; + +/** + * xmlSetExternalEntityLoader: + * @f: the new entity resolver function + * + * Changes the defaultexternal entity resolver function for the application + */ +void +xmlSetExternalEntityLoader(xmlExternalEntityLoader f) { + xmlCurrentExternalEntityLoader = f; +} + +/** + * xmlGetExternalEntityLoader: + * + * Get the default external entity resolver function for the application + * + * Returns the xmlExternalEntityLoader function pointer + */ +xmlExternalEntityLoader +xmlGetExternalEntityLoader(void) { + return(xmlCurrentExternalEntityLoader); +} + +/** + * xmlLoadExternalEntity: + * @URL: the URL for the entity to load + * @ID: the Public ID for the entity to load + * @ctxt: the context in which the entity is called or NULL + * + * @URL is a filename or URL. If if contains the substring "://", + * it is assumed to be a Legacy Extended IRI. Otherwise, it is + * treated as a filesystem path. + * + * @ID is an optional XML public ID, typically from a doctype + * declaration. It is used for catalog lookups. + * + * The following resource loaders will be called if they were + * registered (in order of precedence): + * + * - the global external entity loader set with + * xmlSetExternalEntityLoader + * - the per-thread xmlParserInputBufferCreateFilenameFunc set with + * xmlParserInputBufferCreateFilenameDefault + * - the default loader which will return + * - the result from a matching global input callback set with + * xmlRegisterInputCallbacks + * - a HTTP resource if support is compiled in. + * - a file opened from the filesystem, with automatic detection + * of compressed files if support is compiled in. + * + * Returns the xmlParserInputPtr or NULL + */ +xmlParserInputPtr +xmlLoadExternalEntity(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + char *canonicFilename; + xmlParserInputPtr ret; + + if (URL == NULL) + return(NULL); + + canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL); + if (canonicFilename == NULL) { + xmlCtxtErrMemory(ctxt); + return(NULL); + } + + ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt); + xmlFree(canonicFilename); + return(ret); +} + /************************************************************************ * * * Commodity functions to handle parser contexts * @@ -1765,27 +2269,19 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, { xmlParserInputPtr input; - if(ctxt==NULL) { - xmlErrInternal(NULL, "Got NULL parser context\n", NULL); + if (ctxt == NULL) return(-1); - } - - xmlInitParser(); if (ctxt->dict == NULL) ctxt->dict = xmlDictCreate(); - if (ctxt->dict == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); + if (ctxt->dict == NULL) return(-1); - } xmlDictSetLimit(ctxt->dict, XML_MAX_DICTIONARY_LIMIT); if (ctxt->sax == NULL) ctxt->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler)); - if (ctxt->sax == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); + if (ctxt->sax == NULL) return(-1); - } if (sax == NULL) { memset(ctxt->sax, 0, sizeof(xmlSAXHandler)); xmlSAXVersion(ctxt->sax, 2); @@ -1808,13 +2304,8 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, xmlMalloc(5 * sizeof(xmlParserInputPtr)); ctxt->inputMax = 5; } - if (ctxt->inputTab == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); - ctxt->inputNr = 0; - ctxt->inputMax = 0; - ctxt->input = NULL; + if (ctxt->inputTab == NULL) return(-1); - } while ((input = inputPop(ctxt)) != NULL) { /* Non consuming */ xmlFreeInputStream(input); } @@ -1827,26 +2318,15 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, ctxt->hasExternalSubset = 0; ctxt->hasPErefs = 0; ctxt->html = 0; - ctxt->external = 0; ctxt->instate = XML_PARSER_START; - ctxt->token = 0; - ctxt->directory = NULL; /* Allocate the Node stack */ if (ctxt->nodeTab == NULL) { ctxt->nodeTab = (xmlNodePtr *) xmlMalloc(10 * sizeof(xmlNodePtr)); ctxt->nodeMax = 10; } - if (ctxt->nodeTab == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); - ctxt->nodeNr = 0; - ctxt->nodeMax = 0; - ctxt->node = NULL; - ctxt->inputNr = 0; - ctxt->inputMax = 0; - ctxt->input = NULL; + if (ctxt->nodeTab == NULL) return(-1); - } ctxt->nodeNr = 0; ctxt->node = NULL; @@ -1855,19 +2335,8 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, ctxt->nameTab = (const xmlChar **) xmlMalloc(10 * sizeof(xmlChar *)); ctxt->nameMax = 10; } - if (ctxt->nameTab == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); - ctxt->nodeNr = 0; - ctxt->nodeMax = 0; - ctxt->node = NULL; - ctxt->inputNr = 0; - ctxt->inputMax = 0; - ctxt->input = NULL; - ctxt->nameNr = 0; - ctxt->nameMax = 0; - ctxt->name = NULL; + if (ctxt->nameTab == NULL) return(-1); - } ctxt->nameNr = 0; ctxt->name = NULL; @@ -1876,22 +2345,8 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, ctxt->spaceTab = (int *) xmlMalloc(10 * sizeof(int)); ctxt->spaceMax = 10; } - if (ctxt->spaceTab == NULL) { - xmlErrMemory(NULL, "cannot initialize parser context\n"); - ctxt->nodeNr = 0; - ctxt->nodeMax = 0; - ctxt->node = NULL; - ctxt->inputNr = 0; - ctxt->inputMax = 0; - ctxt->input = NULL; - ctxt->nameNr = 0; - ctxt->nameMax = 0; - ctxt->name = NULL; - ctxt->spaceNr = 0; - ctxt->spaceMax = 0; - ctxt->space = NULL; + if (ctxt->spaceTab == NULL) return(-1); - } ctxt->spaceNr = 1; ctxt->spaceMax = 10; ctxt->spaceTab[0] = -1; @@ -1900,11 +2355,23 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, ctxt->wellFormed = 1; ctxt->nsWellFormed = 1; ctxt->valid = 1; + + ctxt->options = XML_PARSE_NODICT; + + /* + * Initialize some parser options from deprecated global variables. + * Note that the "modern" API taking options arguments or + * xmlCtxtSetOptions will ignore these defaults. They're only + * relevant if old API functions like xmlParseFile are used. + */ ctxt->loadsubset = xmlLoadExtDtdDefaultValue; if (ctxt->loadsubset) { ctxt->options |= XML_PARSE_DTDLOAD; } ctxt->validate = xmlDoValidityCheckingDefaultValue; + if (ctxt->validate) { + ctxt->options |= XML_PARSE_DTDVALID; + } ctxt->pedantic = xmlPedanticParserDefaultValue; if (ctxt->pedantic) { ctxt->options |= XML_PARSE_PEDANTIC; @@ -1915,23 +2382,18 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, ctxt->sax->ignorableWhitespace = xmlSAX2IgnorableWhitespace; ctxt->options |= XML_PARSE_NOBLANKS; } + ctxt->replaceEntities = xmlSubstituteEntitiesDefaultValue; + if (ctxt->replaceEntities) { + ctxt->options |= XML_PARSE_NOENT; + } + if (xmlGetWarningsDefaultValue == 0) + ctxt->options |= XML_PARSE_NOWARNING; ctxt->vctxt.flags = XML_VCTXT_USE_PCTXT; ctxt->vctxt.userData = ctxt; ctxt->vctxt.error = xmlParserValidityError; ctxt->vctxt.warning = xmlParserValidityWarning; - if (ctxt->validate) { - if (xmlGetWarningsDefaultValue == 0) - ctxt->vctxt.warning = NULL; - else - ctxt->vctxt.warning = xmlParserValidityWarning; - ctxt->vctxt.nodeMax = 0; - ctxt->options |= XML_PARSE_DTDVALID; - } - ctxt->replaceEntities = xmlSubstituteEntitiesDefaultValue; - if (ctxt->replaceEntities) { - ctxt->options |= XML_PARSE_NOENT; - } + ctxt->record_info = 0; ctxt->checkIndex = 0; ctxt->inSubset = 0; @@ -1947,7 +2409,7 @@ xmlInitSAXParserCtxt(xmlParserCtxtPtr ctxt, const xmlSAXHandler *sax, if (ctxt->nsdb == NULL) { ctxt->nsdb = xmlParserNsCreate(); if (ctxt->nsdb == NULL) { - xmlErrMemory(ctxt, NULL); + xmlCtxtErrMemory(ctxt); return(-1); } } @@ -2007,7 +2469,7 @@ xmlFreeParserCtxt(xmlParserCtxtPtr ctxt) if (ctxt->sax != NULL) #endif /* LIBXML_SAX1_ENABLED */ xmlFree(ctxt->sax); - if (ctxt->directory != NULL) xmlFree((char *) ctxt->directory); + if (ctxt->directory != NULL) xmlFree(ctxt->directory); if (ctxt->vctxt.nodeTab != NULL) xmlFree(ctxt->vctxt.nodeTab); if (ctxt->atts != NULL) xmlFree((xmlChar * *)ctxt->atts); if (ctxt->dict != NULL) xmlDictFree(ctxt->dict); @@ -2083,6 +2545,10 @@ xmlNewParserCtxt(void) * Allocate and initialize a new SAX parser context. If userData is NULL, * the parser context will be passed as user data. * + * Available since 2.11.0. If you want support older versions, + * it's best to invoke xmlNewParserCtxt and set ctxt->sax with + * struct assignment. + * * Returns the xmlParserCtxtPtr or NULL if memory allocation failed. */ @@ -2091,11 +2557,11 @@ xmlNewSAXParserCtxt(const xmlSAXHandler *sax, void *userData) { xmlParserCtxtPtr ctxt; + xmlInitParser(); + ctxt = (xmlParserCtxtPtr) xmlMalloc(sizeof(xmlParserCtxt)); - if (ctxt == NULL) { - xmlErrMemory(NULL, "cannot allocate parser context\n"); + if (ctxt == NULL) return(NULL); - } memset(ctxt, 0, sizeof(xmlParserCtxt)); if (xmlInitSAXParserCtxt(ctxt, sax, userData) < 0) { xmlFreeParserCtxt(ctxt); @@ -2139,7 +2605,7 @@ xmlClearParserCtxt(xmlParserCtxtPtr ctxt) * Returns an xmlParserNodeInfo block pointer or NULL */ const xmlParserNodeInfo * -xmlParserFindNodeInfo(const xmlParserCtxtPtr ctx, const xmlNodePtr node) +xmlParserFindNodeInfo(xmlParserCtxtPtr ctx, xmlNodePtr node) { unsigned long pos; @@ -2205,8 +2671,8 @@ xmlClearNodeInfoSeq(xmlParserNodeInfoSeqPtr seq) * Returns a long indicating the position of the record */ unsigned long -xmlParserFindNodeInfoIndex(const xmlParserNodeInfoSeqPtr seq, - const xmlNodePtr node) +xmlParserFindNodeInfoIndex(xmlParserNodeInfoSeqPtr seq, + xmlNodePtr node) { unsigned long upper, lower, middle; int found = 0; @@ -2247,7 +2713,7 @@ xmlParserFindNodeInfoIndex(const xmlParserNodeInfoSeqPtr seq, */ void xmlParserAddNodeInfo(xmlParserCtxtPtr ctxt, - const xmlParserNodeInfoPtr info) + xmlParserNodeInfoPtr info) { unsigned long pos; @@ -2283,7 +2749,7 @@ xmlParserAddNodeInfo(xmlParserCtxtPtr ctxt, byte_size); if (tmp_buffer == NULL) { - xmlErrMemory(ctxt, "failed to allocate buffer\n"); + xmlCtxtErrMemory(ctxt); return; } ctxt->node_seq.buffer = tmp_buffer; diff --git a/libraries/libxml2/private/buf.h b/libraries/libxml2/private/buf.h index 6fef4ce0..982b9eea 100644 --- a/libraries/libxml2/private/buf.h +++ b/libraries/libxml2/private/buf.h @@ -28,10 +28,6 @@ XML_HIDDEN int xmlBufAdd(xmlBufPtr buf, const xmlChar *str, int len); XML_HIDDEN int xmlBufCat(xmlBufPtr buf, const xmlChar *str); -XML_HIDDEN int -xmlBufCCat(xmlBufPtr buf, const char *str); -XML_HIDDEN int -xmlBufWriteQuotedString(xmlBufPtr buf, const xmlChar *string); XML_HIDDEN size_t xmlBufAvail(const xmlBufPtr buf); @@ -56,8 +52,6 @@ XML_HIDDEN xmlBufPtr xmlBufFromBuffer(xmlBufferPtr buffer); XML_HIDDEN xmlBufferPtr xmlBufBackToBuffer(xmlBufPtr buf); -XML_HIDDEN int -xmlBufMergeBuffer(xmlBufPtr buf, xmlBufferPtr buffer); XML_HIDDEN int xmlBufResetInput(xmlBufPtr buf, xmlParserInputPtr input); diff --git a/libraries/libxml2/private/dict.h b/libraries/libxml2/private/dict.h index 9b0be621..826ac54a 100644 --- a/libraries/libxml2/private/dict.h +++ b/libraries/libxml2/private/dict.h @@ -67,6 +67,8 @@ xmlInitRandom(void); XML_HIDDEN void xmlCleanupRandom(void); XML_HIDDEN unsigned +xmlGlobalRandom(void); +XML_HIDDEN unsigned xmlRandom(void); #endif /* XML_DICT_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/entities.h b/libraries/libxml2/private/entities.h index c3f15e68..d262ef47 100644 --- a/libraries/libxml2/private/entities.h +++ b/libraries/libxml2/private/entities.h @@ -9,13 +9,17 @@ * * XML_ENT_PARSED: The entity was parsed and `children` points to the * content. - * XML_ENT_CHECKED: The entity was checked for loops. + * + * XML_ENT_CHECKED: The entity was checked for loops and amplification. + * expandedSize was set. + * + * XML_ENT_VALIDATED: The entity contains a valid attribute value. + * Only used when entities aren't substituted. */ -#define XML_ENT_PARSED (1<<0) -#define XML_ENT_CHECKED (1<<1) -#define XML_ENT_EXPANDING (1<<2) -#define XML_ENT_CHECKED_LT (1<<3) -#define XML_ENT_CONTAINS_LT (1<<4) +#define XML_ENT_PARSED (1u << 0) +#define XML_ENT_CHECKED (1u << 1) +#define XML_ENT_VALIDATED (1u << 2) +#define XML_ENT_EXPANDING (1u << 3) XML_HIDDEN xmlChar * xmlEncodeAttributeEntities(xmlDocPtr doc, const xmlChar *input); diff --git a/libraries/libxml2/private/error.h b/libraries/libxml2/private/error.h index 165b782b..506405a1 100644 --- a/libraries/libxml2/private/error.h +++ b/libraries/libxml2/private/error.h @@ -4,20 +4,31 @@ #include #include +#define MAX_ERR_MSG_SIZE 64000 + struct _xmlNode; XML_HIDDEN void -__xmlRaiseError(xmlStructuredErrorFunc schannel, - xmlGenericErrorFunc channel, void *data, void *ctx, - void *nod, int domain, int code, xmlErrorLevel level, +xmlRaiseMemoryError(xmlStructuredErrorFunc schannel, xmlGenericErrorFunc channel, + void *data, int domain, xmlError *error); +XML_HIDDEN int +xmlVRaiseError(xmlStructuredErrorFunc schannel, xmlGenericErrorFunc channel, + void *data, void *ctx, struct _xmlNode *node, + int domain, int code, xmlErrorLevel level, + const char *file, int line, const char *str1, + const char *str2, const char *str3, int int1, int col, + const char *msg, va_list ap); +XML_HIDDEN int +__xmlRaiseError(xmlStructuredErrorFunc schannel, xmlGenericErrorFunc channel, + void *data, void *ctx, struct _xmlNode *node, + int domain, int code, xmlErrorLevel level, const char *file, int line, const char *str1, const char *str2, const char *str3, int int1, int col, const char *msg, ...) LIBXML_ATTR_FORMAT(16,17); XML_HIDDEN void -__xmlSimpleError(int domain, int code, struct _xmlNode *node, - const char *msg, const char *extra) LIBXML_ATTR_FORMAT(4,0); -XML_HIDDEN void xmlGenericErrorDefaultFunc(void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); +XML_HIDDEN const char * +xmlErrString(xmlParserErrors code); #endif /* XML_ERROR_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/globals.h b/libraries/libxml2/private/globals.h index 5f3f1122..828b6d50 100644 --- a/libraries/libxml2/private/globals.h +++ b/libraries/libxml2/private/globals.h @@ -6,4 +6,9 @@ xmlInitGlobalsInternal(void); XML_HIDDEN void xmlCleanupGlobalsInternal(void); +#ifdef LIBXML_THREAD_ENABLED +XML_HIDDEN unsigned * +xmlGetLocalRngState(void); +#endif + #endif /* XML_GLOBALS_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/io.h b/libraries/libxml2/private/io.h index 5f4b210a..a2535ae1 100644 --- a/libraries/libxml2/private/io.h +++ b/libraries/libxml2/private/io.h @@ -6,17 +6,30 @@ #include XML_HIDDEN void +xmlInitIOCallbacks(void); + +XML_HIDDEN int __xmlIOErr(int domain, int code, const char *extra); -XML_HIDDEN void -__xmlLoaderErr(void *ctx, const char *msg, - const char *filename) LIBXML_ATTR_FORMAT(2,0); -xmlParserInputBufferPtr -xmlParserInputBufferCreateString(const xmlChar *str); +XML_HIDDEN int +xmlNoNetExists(const char *filename); + +XML_HIDDEN int +xmlParserInputBufferCreateFilenameSafe(const char *URI, xmlCharEncoding enc, + xmlParserInputBufferPtr *out); + +XML_HIDDEN xmlParserInputBufferPtr +xmlNewInputBufferString(const char *str, int flags); +XML_HIDDEN xmlParserInputBufferPtr +xmlNewInputBufferMemory(const void *mem, size_t size, int flags, + xmlCharEncoding enc); #ifdef LIBXML_OUTPUT_ENABLED XML_HIDDEN xmlOutputBufferPtr xmlAllocOutputBufferInternal(xmlCharEncodingHandlerPtr encoder); +XML_HIDDEN void +xmlOutputBufferWriteQuotedString(xmlOutputBufferPtr buf, + const xmlChar *string); #endif #endif /* XML_IO_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/parser.h b/libraries/libxml2/private/parser.h index 40d179fe..b14bebf9 100644 --- a/libraries/libxml2/private/parser.h +++ b/libraries/libxml2/private/parser.h @@ -25,18 +25,41 @@ #define XML_INPUT_AUTO_OTHER (4u << 1) #define XML_INPUT_USES_ENC_DECL (1u << 4) #define XML_INPUT_ENCODING_ERROR (1u << 5) +#define XML_INPUT_PROGRESSIVE (1u << 6) +#define PARSER_STOPPED(ctxt) ((ctxt)->disableSAX > 1) + +#define PARSER_PROGRESSIVE(ctxt) \ + ((ctxt)->input->flags & XML_INPUT_PROGRESSIVE) + +#define PARSER_IN_PE(ctxt) \ + (((ctxt)->input->entity != NULL) && \ + (((ctxt)->input->entity->etype == XML_INTERNAL_PARAMETER_ENTITY) || \ + ((ctxt)->input->entity->etype == XML_EXTERNAL_PARAMETER_ENTITY))) + +#define PARSER_EXTERNAL(ctxt) \ + (((ctxt)->inSubset == 2) || \ + (((ctxt)->input->entity != NULL) && \ + ((ctxt)->input->entity->etype == XML_EXTERNAL_PARAMETER_ENTITY))) + +XML_HIDDEN void +xmlCtxtVErr(xmlParserCtxtPtr ctxt, xmlNodePtr node, xmlErrorDomain domain, + xmlParserErrors code, xmlErrorLevel level, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, + int int1, const char *msg, va_list ap); XML_HIDDEN void -xmlErrMemory(xmlParserCtxtPtr ctxt, const char *extra); +xmlCtxtErr(xmlParserCtxtPtr ctxt, xmlNodePtr node, xmlErrorDomain domain, + xmlParserErrors code, xmlErrorLevel level, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, + int int1, const char *msg, ...); XML_HIDDEN void xmlFatalErr(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *info); XML_HIDDEN void LIBXML_ATTR_FORMAT(3,0) xmlWarningMsg(xmlParserCtxtPtr ctxt, xmlParserErrors error, const char *msg, const xmlChar *str1, const xmlChar *str2); XML_HIDDEN void -__xmlErrEncoding(xmlParserCtxtPtr ctxt, xmlParserErrors xmlerr, - const char *msg, const xmlChar *str1, - const xmlChar *str2) LIBXML_ATTR_FORMAT(3,0); +xmlCtxtErrIO(xmlParserCtxtPtr ctxt, int code, const char *uri); + XML_HIDDEN void xmlHaltParser(xmlParserCtxtPtr ctxt); XML_HIDDEN int @@ -48,6 +71,8 @@ XML_HIDDEN void xmlDetectEncoding(xmlParserCtxtPtr ctxt); XML_HIDDEN void xmlSetDeclaredEncoding(xmlParserCtxtPtr ctxt, xmlChar *encoding); +XML_HIDDEN const xmlChar * +xmlGetActualEncoding(xmlParserCtxtPtr ctxt); XML_HIDDEN xmlParserNsData * xmlParserNsCreate(void); @@ -63,4 +88,34 @@ xmlParserNsUpdateSax(xmlParserCtxtPtr ctxt, const xmlChar *prefix, XML_HIDDEN void * xmlParserNsLookupSax(xmlParserCtxtPtr ctxt, const xmlChar *prefix); +#define XML_INPUT_BUF_STATIC (1u << 1) +#define XML_INPUT_BUF_ZERO_TERMINATED (1u << 2) + +XML_HIDDEN xmlParserInputPtr +xmlNewInputURL(xmlParserCtxtPtr ctxt, const char *url, const char *publicId, + const char *encoding, int flags); +XML_HIDDEN xmlParserInputPtr +xmlNewInputMemory(xmlParserCtxtPtr ctxt, const char *url, + const void *mem, size_t size, + const char *encoding, int flags); +XML_HIDDEN xmlParserInputPtr +xmlNewInputString(xmlParserCtxtPtr ctxt, const char *url, const char *str, + const char *encoding, int flags); +XML_HIDDEN xmlParserInputPtr +xmlNewInputFd(xmlParserCtxtPtr ctxt, const char *filename, int fd, + const char *encoding, int flags); +XML_HIDDEN xmlParserInputPtr +xmlNewInputIO(xmlParserCtxtPtr ctxt, const char *url, + xmlInputReadCallback ioRead, + xmlInputCloseCallback ioClose, + void *ioCtxt, + const char *encoding, int flags); +XML_HIDDEN xmlParserInputPtr +xmlNewInputPush(xmlParserCtxtPtr ctxt, const char *url, + const char *chunk, int size, const char *encoding); + +XML_HIDDEN xmlChar * +xmlExpandEntitiesInAttValue(xmlParserCtxtPtr ctxt, const xmlChar *str, + int normalize); + #endif /* XML_PARSER_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/regexp.h b/libraries/libxml2/private/regexp.h new file mode 100644 index 00000000..b55c932b --- /dev/null +++ b/libraries/libxml2/private/regexp.h @@ -0,0 +1,23 @@ +#ifndef XML_REGEXP_H_PRIVATE__ +#define XML_REGEXP_H_PRIVATE__ + +#include + +#ifdef LIBXML_REGEXP_ENABLED + +/* + * -2 and -3 are used by xmlValidateElementType for other things. + */ +#define XML_REGEXP_OK 0 +#define XML_REGEXP_NOT_FOUND (-1) +#define XML_REGEXP_INTERNAL_ERROR (-4) +#define XML_REGEXP_OUT_OF_MEMORY (-5) +#define XML_REGEXP_INTERNAL_LIMIT (-6) +#define XML_REGEXP_INVALID_UTF8 (-7) + +XML_HIDDEN void +xmlAutomataSetFlags(xmlAutomataPtr am, int flags); + +#endif /* LIBXML_REGEXP_ENABLED */ + +#endif /* XML_REGEXP_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/save.h b/libraries/libxml2/private/save.h index 873aad76..5d4a753f 100644 --- a/libraries/libxml2/private/save.h +++ b/libraries/libxml2/private/save.h @@ -2,13 +2,19 @@ #define XML_SAVE_H_PRIVATE__ #include +#include #include #ifdef LIBXML_OUTPUT_ENABLED +XML_HIDDEN int +xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt, xmlNotationPtr cur); +XML_HIDDEN int +xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur); + XML_HIDDEN void -xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc, - xmlAttrPtr attr, const xmlChar * string); +xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc, + const xmlChar *string); XML_HIDDEN void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur); diff --git a/libraries/libxml2/private/string.h b/libraries/libxml2/private/string.h index 9665fc47..34f4c96c 100644 --- a/libraries/libxml2/private/string.h +++ b/libraries/libxml2/private/string.h @@ -3,6 +3,10 @@ #include +XML_HIDDEN int +xmlStrVASPrintf(xmlChar **out, int maxSize, const char *msg, va_list ap); +XML_HIDDEN int +xmlStrASPrintf(xmlChar **out, int maxSize, const char *msg, ...); XML_HIDDEN xmlChar * xmlEscapeFormatString(xmlChar **msg); diff --git a/libraries/libxml2/private/tree.h b/libraries/libxml2/private/tree.h index fb5e1623..2d651d53 100644 --- a/libraries/libxml2/private/tree.h +++ b/libraries/libxml2/private/tree.h @@ -9,10 +9,19 @@ XML_HIDDEN extern int __xmlRegisterCallbacks; +XML_HIDDEN int +xmlSearchNsSafe(xmlNodePtr node, const xmlChar *href, xmlNsPtr *out); +XML_HIDDEN int +xmlSearchNsByHrefSafe(xmlNodePtr node, const xmlChar *href, xmlNsPtr *out); + +XML_HIDDEN int +xmlNodeParseContent(xmlNodePtr node, const xmlChar *content, int len); XML_HIDDEN xmlNodePtr xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, int extended); XML_HIDDEN xmlNodePtr xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent); +XML_HIDDEN const xmlChar * +xmlSplitQName4(const xmlChar *name, xmlChar **prefixPtr); #endif /* XML_TREE_H_PRIVATE__ */ diff --git a/libraries/libxml2/private/xpath.h b/libraries/libxml2/private/xpath.h index 0e8d7525..72a69720 100644 --- a/libraries/libxml2/private/xpath.h +++ b/libraries/libxml2/private/xpath.h @@ -1,7 +1,16 @@ #ifndef XML_XPATH_H_PRIVATE__ #define XML_XPATH_H_PRIVATE__ +#include + XML_HIDDEN void xmlInitXPathInternal(void); +#ifdef LIBXML_XPATH_ENABLED +XML_HIDDEN void +xmlXPathErrMemory(xmlXPathContextPtr ctxt); +XML_HIDDEN void +xmlXPathPErrMemory(xmlXPathParserContextPtr ctxt); +#endif + #endif /* XML_XPATH_H_PRIVATE__ */ diff --git a/libraries/libxml2/threads.c b/libraries/libxml2/threads.c index 461f4a51..36347c2f 100644 --- a/libraries/libxml2/threads.c +++ b/libraries/libxml2/threads.c @@ -30,6 +30,7 @@ #include "private/dict.h" #include "private/enc.h" #include "private/globals.h" +#include "private/io.h" #include "private/memory.h" #include "private/threads.h" #include "private/xpath.h" @@ -498,9 +499,8 @@ xmlGlobalInitMutexLock(void) { if (global_init_lock == NULL) { cs = malloc(sizeof(CRITICAL_SECTION)); if (cs == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlGlobalInitMutexLock: out of memory\n"); - return; + fprintf(stderr, "libxml2: xmlInitParser: out of memory\n"); + abort(); } InitializeCriticalSection(cs); @@ -583,19 +583,15 @@ xmlInitParser(void) { atexit(xmlCleanupParser); #endif - xmlInitMemoryInternal(); /* Should come second */ + xmlInitRandom(); /* Required by xmlInitGlobalsInternal */ + xmlInitMemoryInternal(); xmlInitGlobalsInternal(); - xmlInitRandom(); xmlInitDictInternal(); xmlInitEncodingInternal(); #if defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) xmlInitXPathInternal(); #endif - - xmlRegisterDefaultInputCallbacks(); -#ifdef LIBXML_OUTPUT_ENABLED - xmlRegisterDefaultOutputCallbacks(); -#endif /* LIBXML_OUTPUT_ENABLED */ + xmlInitIOCallbacks(); xmlParserInnerInitialized = 1; } @@ -644,11 +640,6 @@ xmlCleanupParser(void) { /* These functions should never call xmlFree. */ - xmlCleanupInputCallbacks(); -#ifdef LIBXML_OUTPUT_ENABLED - xmlCleanupOutputCallbacks(); -#endif - xmlCleanupDictInternal(); xmlCleanupRandom(); xmlCleanupGlobalsInternal(); diff --git a/libraries/libxml2/tree.c b/libraries/libxml2/tree.c index dc3ac4f9..254ea5a3 100644 --- a/libraries/libxml2/tree.c +++ b/libraries/libxml2/tree.c @@ -55,58 +55,23 @@ int __xmlRegisterCallbacks = 0; * * ************************************************************************/ +static xmlNodePtr +xmlNewEntityRef(xmlDocPtr doc, xmlChar *name); + static xmlNsPtr -xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns); +xmlNewReconciledNs(xmlNodePtr tree, xmlNsPtr ns); + +static xmlAttrPtr +xmlGetPropNodeInternal(const xmlNode *node, const xmlChar *name, + const xmlChar *nsName, int useDTD); static xmlChar* xmlGetPropNodeValueInternal(const xmlAttr *prop); -/************************************************************************ - * * - * Tree memory error handler * - * * - ************************************************************************/ -/** - * xmlTreeErrMemory: - * @extra: extra information - * - * Handle an out of memory condition - */ static void -xmlTreeErrMemory(const char *extra) -{ - __xmlSimpleError(XML_FROM_TREE, XML_ERR_NO_MEMORY, NULL, NULL, extra); -} +xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree); -/** - * xmlTreeErr: - * @code: the error number - * @extra: extra information - * - * Handle an out of memory condition - */ static void -xmlTreeErr(int code, xmlNodePtr node, const char *extra) -{ - const char *msg = NULL; - - switch(code) { - case XML_TREE_INVALID_HEX: - msg = "invalid hexadecimal character value\n"; - break; - case XML_TREE_INVALID_DEC: - msg = "invalid decimal character value\n"; - break; - case XML_TREE_UNTERMINATED_ENTITY: - msg = "unterminated entity reference %15s\n"; - break; - case XML_TREE_NOT_UTF8: - msg = "string is not in UTF-8\n"; - break; - default: - msg = "unexpected error number\n"; - } - __xmlSimpleError(XML_FROM_TREE, code, node, msg, extra); -} +xmlUnlinkNodeInternal(xmlNodePtr cur); /************************************************************************ * * @@ -122,20 +87,6 @@ const xmlChar xmlStringTextNoenc[] = const xmlChar xmlStringComment[] = { 'c', 'o', 'm', 'm', 'e', 'n', 't', 0 }; static int xmlCompressMode = 0; -static int xmlCheckDTD = 1; - -#define UPDATE_LAST_CHILD_AND_PARENT(n) if ((n) != NULL) { \ - xmlNodePtr ulccur = (n)->children; \ - if (ulccur == NULL) { \ - (n)->last = NULL; \ - } else { \ - while (ulccur->next != NULL) { \ - ulccur->parent = (n); \ - ulccur = ulccur->next; \ - } \ - ulccur->parent = (n); \ - (n)->last = ulccur; \ -}} #define IS_STR_XML(str) ((str != NULL) && (str[0] == 'x') && \ (str[1] == 'm') && (str[2] == 'l') && (str[3] == 0)) @@ -222,15 +173,19 @@ xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix, if (ncname == NULL) return(NULL); if (prefix == NULL) return((xmlChar *) ncname); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Make allocation more likely */ + if (len > 8) + len = 8; +#endif + lenn = strlen((char *) ncname); lenp = strlen((char *) prefix); if ((memory == NULL) || (len < lenn + lenp + 2)) { ret = (xmlChar *) xmlMallocAtomic(lenn + lenp + 2); - if (ret == NULL) { - xmlTreeErrMemory("building QName"); + if (ret == NULL) return(NULL); - } } else { ret = memory; } @@ -246,6 +201,8 @@ xmlBuildQName(const xmlChar *ncname, const xmlChar *prefix, * @name: the full QName * @prefix: a xmlChar ** * + * DEPRECATED: This function doesn't report malloc failures. + * * parse an XML qualified name string * * [NS 5] QName ::= (Prefix ':')? LocalPart @@ -267,13 +224,6 @@ xmlSplitQName2(const xmlChar *name, xmlChar **prefix) { *prefix = NULL; if (name == NULL) return(NULL); -#ifndef XML_XML_NAMESPACE - /* xml: prefix is not really a namespace */ - if ((name[0] == 'x') && (name[1] == 'm') && - (name[2] == 'l') && (name[3] == ':')) - return(NULL); -#endif - /* nasty but valid */ if (name[0] == ':') return(NULL); @@ -285,17 +235,14 @@ xmlSplitQName2(const xmlChar *name, xmlChar **prefix) { while ((name[len] != 0) && (name[len] != ':')) len++; - if (name[len] == 0) + if ((name[len] == 0) || (name[len+1] == 0)) return(NULL); *prefix = xmlStrndup(name, len); - if (*prefix == NULL) { - xmlTreeErrMemory("QName split"); + if (*prefix == NULL) return(NULL); - } ret = xmlStrdup(&name[len + 1]); if (ret == NULL) { - xmlTreeErrMemory("QName split"); if (*prefix != NULL) { xmlFree(*prefix); *prefix = NULL; @@ -336,7 +283,7 @@ xmlSplitQName3(const xmlChar *name, int *len) { while ((name[l] != 0) && (name[l] != ':')) l++; - if (name[l] == 0) + if ((name[l] == 0) || (name[l+1] == 0)) return(NULL); *len = l; @@ -344,6 +291,53 @@ xmlSplitQName3(const xmlChar *name, int *len) { return(&name[l+1]); } +/** + * xmlSplitQName4: + * @name: the full QName + * @prefixPtr: pointer to resulting prefix + * + * Parse a QName. The return value points to the start of the local + * name in the input string. If the QName has a prefix, it will be + * allocated and stored in @prefixPtr. This string must be freed by + * the caller. If there's no prefix, @prefixPtr is set to NULL. + * + * Returns the local name or NULL if a memory allocation failed. + */ +const xmlChar * +xmlSplitQName4(const xmlChar *name, xmlChar **prefixPtr) { + xmlChar *prefix; + int l = 0; + + if ((name == NULL) || (prefixPtr == NULL)) + return(NULL); + + *prefixPtr = NULL; + + /* nasty but valid */ + if (name[0] == ':') + return(name); + + /* + * we are not trying to validate but just to cut, and yes it will + * work even if this is as set of UTF-8 encoded chars + */ + while ((name[l] != 0) && (name[l] != ':')) + l++; + + /* + * TODO: What about names with multiple colons? + */ + if ((name[l] == 0) || (name[l+1] == 0)) + return(name); + + prefix = xmlStrndup(name, l); + if (prefix == NULL) + return(NULL); + + *prefixPtr = prefix; + return(&name[l+1]); +} + /************************************************************************ * * * Check Name, NCName and QName strings * @@ -708,19 +702,21 @@ xmlGetBufferAllocationScheme(void) { /** * xmlNewNs: - * @node: the element carrying the namespace + * @node: the element carrying the namespace (optional) * @href: the URI associated - * @prefix: the prefix for the namespace + * @prefix: the prefix for the namespace (optional) * - * Creation of a new Namespace. This function will refuse to create - * a namespace with a similar prefix than an existing one present on this - * node. - * Note that for a default namespace, @prefix should be NULL. + * Create a new namespace. For a default namespace, @prefix should be + * NULL. The namespace URI in @href is not checked. You should make sure + * to pass a valid URI. * - * We use href==NULL in the case of an element creation where the namespace - * was not defined. + * If @node is provided, it must be an element node. The namespace will + * be appended to the node's namespace declarations. It is an error if + * the node already has a definition for the prefix or default + * namespace. * - * Returns a new namespace pointer or NULL + * Returns a new namespace pointer or NULL if arguments are invalid, + * the prefix is already in use or a memory allocation failed. */ xmlNsPtr xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix) { @@ -729,35 +725,25 @@ xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix) { if ((node != NULL) && (node->type != XML_ELEMENT_NODE)) return(NULL); - if ((prefix != NULL) && (xmlStrEqual(prefix, BAD_CAST "xml"))) { - /* xml namespace is predefined, no need to add it */ - if (xmlStrEqual(href, XML_XML_NAMESPACE)) - return(NULL); - - /* - * Problem, this is an attempt to bind xml prefix to a wrong - * namespace, which breaks - * Namespace constraint: Reserved Prefixes and Namespace Names - * from XML namespace. But documents authors may not care in - * their context so let's proceed. - */ - } - /* * Allocate a new Namespace and fill the fields. */ cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (cur == NULL) { - xmlTreeErrMemory("building namespace"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNs)); cur->type = XML_LOCAL_NAMESPACE; - if (href != NULL) + if (href != NULL) { cur->href = xmlStrdup(href); - if (prefix != NULL) + if (cur->href == NULL) + goto error; + } + if (prefix != NULL) { cur->prefix = xmlStrdup(prefix); + if (cur->prefix == NULL) + goto error; + } /* * Add it at the end to preserve parsing order ... @@ -769,31 +755,32 @@ xmlNewNs(xmlNodePtr node, const xmlChar *href, const xmlChar *prefix) { } else { xmlNsPtr prev = node->nsDef; - if (((prev->prefix == NULL) && (cur->prefix == NULL)) || - (xmlStrEqual(prev->prefix, cur->prefix))) { - xmlFreeNs(cur); - return(NULL); - } + if ((xmlStrEqual(prev->prefix, cur->prefix)) && + (prev->href != NULL)) + goto error; while (prev->next != NULL) { prev = prev->next; - if (((prev->prefix == NULL) && (cur->prefix == NULL)) || - (xmlStrEqual(prev->prefix, cur->prefix))) { - xmlFreeNs(cur); - return(NULL); - } + if ((xmlStrEqual(prev->prefix, cur->prefix)) && + (prev->href != NULL)) + goto error; } prev->next = cur; } } return(cur); + +error: + xmlFreeNs(cur); + return(NULL); } /** * xmlSetNs: * @node: a node in the document - * @ns: a namespace pointer + * @ns: a namespace pointer (optional) * - * Associate a namespace to a node, a posteriori. + * Set the namespace of an element or attribute node. Passing a NULL + * namespace unsets the namespace. */ void xmlSetNs(xmlNodePtr node, xmlNsPtr ns) { @@ -809,7 +796,7 @@ xmlSetNs(xmlNodePtr node, xmlNsPtr ns) { * xmlFreeNs: * @cur: the namespace pointer * - * Free up the structures associated to a namespace + * Free an xmlNs object. */ void xmlFreeNs(xmlNsPtr cur) { @@ -825,7 +812,7 @@ xmlFreeNs(xmlNsPtr cur) { * xmlFreeNsList: * @cur: the first namespace pointer * - * Free up all the structures associated to the chained namespaces. + * Free a list of xmlNs objects. */ void xmlFreeNsList(xmlNsPtr cur) { @@ -842,15 +829,21 @@ xmlFreeNsList(xmlNsPtr cur) { /** * xmlNewDtd: - * @doc: the document pointer - * @name: the DTD name - * @ExternalID: the external ID - * @SystemID: the system ID + * @doc: the document pointer (optional) + * @name: the DTD name (optional) + * @ExternalID: the external ID (optional) + * @SystemID: the system ID (optional) + * + * Create a DTD node. * - * Creation of a new DTD for the external subset. To create an - * internal subset, use xmlCreateIntSubset(). + * If a document is provided, it is an error if it already has an + * external subset. If the document has no external subset, it + * will be set to the created DTD. * - * Returns a pointer to the new DTD structure + * To create an internal subset, use xmlCreateIntSubset(). + * + * Returns a pointer to the new DTD object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlDtdPtr xmlNewDtd(xmlDocPtr doc, const xmlChar *name, @@ -865,19 +858,26 @@ xmlNewDtd(xmlDocPtr doc, const xmlChar *name, * Allocate a new DTD and fill the fields. */ cur = (xmlDtdPtr) xmlMalloc(sizeof(xmlDtd)); - if (cur == NULL) { - xmlTreeErrMemory("building DTD"); + if (cur == NULL) return(NULL); - } memset(cur, 0 , sizeof(xmlDtd)); cur->type = XML_DTD_NODE; - if (name != NULL) + if (name != NULL) { cur->name = xmlStrdup(name); - if (ExternalID != NULL) + if (cur->name == NULL) + goto error; + } + if (ExternalID != NULL) { cur->ExternalID = xmlStrdup(ExternalID); - if (SystemID != NULL) + if (cur->ExternalID == NULL) + goto error; + } + if (SystemID != NULL) { cur->SystemID = xmlStrdup(SystemID); + if (cur->SystemID == NULL) + goto error; + } if (doc != NULL) doc->extSubset = cur; cur->doc = doc; @@ -885,16 +885,20 @@ xmlNewDtd(xmlDocPtr doc, const xmlChar *name, if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)cur); return(cur); + +error: + xmlFreeDtd(cur); + return(NULL); } /** * xmlGetIntSubset: * @doc: the document pointer * - * Get the internal subset of a document - * Returns a pointer to the DTD structure or NULL if not found + * Get the internal subset of a document. + * + * Returns a pointer to the DTD object or NULL if not found. */ - xmlDtdPtr xmlGetIntSubset(const xmlDoc *doc) { xmlNodePtr cur; @@ -912,63 +916,55 @@ xmlGetIntSubset(const xmlDoc *doc) { /** * xmlCreateIntSubset: - * @doc: the document pointer - * @name: the DTD name - * @ExternalID: the external (PUBLIC) ID - * @SystemID: the system ID + * @doc: the document pointer (optional) + * @name: the DTD name (optional) + * @ExternalID: the external (PUBLIC) ID (optional) + * @SystemID: the system ID (optional) * - * Create the internal subset of a document - * Returns a pointer to the new DTD structure + * Create a DTD node. + * + * If a document is provided and it already has an internal subset, + * the existing DTD object is returned without creating a new object. + * If the document has no internal subset, it will be set to the + * created DTD. + * + * Returns a pointer to the new or existing DTD object or NULL if + * arguments are invalid or a memory allocation failed. */ xmlDtdPtr xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) { xmlDtdPtr cur; - if ((doc != NULL) && (xmlGetIntSubset(doc) != NULL)) { - return(NULL); + if (doc != NULL) { + cur = xmlGetIntSubset(doc); + if (cur != NULL) + return(cur); } /* * Allocate a new DTD and fill the fields. */ cur = (xmlDtdPtr) xmlMalloc(sizeof(xmlDtd)); - if (cur == NULL) { - xmlTreeErrMemory("building internal subset"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlDtd)); cur->type = XML_DTD_NODE; if (name != NULL) { cur->name = xmlStrdup(name); - if (cur->name == NULL) { - xmlTreeErrMemory("building internal subset"); - xmlFree(cur); - return(NULL); - } + if (cur->name == NULL) + goto error; } if (ExternalID != NULL) { cur->ExternalID = xmlStrdup(ExternalID); - if (cur->ExternalID == NULL) { - xmlTreeErrMemory("building internal subset"); - if (cur->name != NULL) - xmlFree((char *)cur->name); - xmlFree(cur); - return(NULL); - } + if (cur->ExternalID == NULL) + goto error; } if (SystemID != NULL) { cur->SystemID = xmlStrdup(SystemID); - if (cur->SystemID == NULL) { - xmlTreeErrMemory("building internal subset"); - if (cur->name != NULL) - xmlFree((char *)cur->name); - if (cur->ExternalID != NULL) - xmlFree((char *)cur->ExternalID); - xmlFree(cur); - return(NULL); - } + if (cur->SystemID == NULL) + goto error; } if (doc != NULL) { doc->intSubset = cur; @@ -1012,6 +1008,10 @@ xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name, if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)cur); return(cur); + +error: + xmlFreeDtd(cur); + return(NULL); } /** @@ -1026,42 +1026,6 @@ xmlCreateIntSubset(xmlDocPtr doc, const xmlChar *name, (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \ xmlFree((char *)(str)); - -/** - * DICT_COPY: - * @str: a string - * - * Copy a string using a "dict" dictionary in the current scope, - * if available. - */ -#define DICT_COPY(str, cpy) \ - if (str) { \ - if (dict) { \ - if (xmlDictOwns(dict, (const xmlChar *)(str))) \ - cpy = (xmlChar *) (str); \ - else \ - cpy = (xmlChar *) xmlDictLookup((dict), (const xmlChar *)(str), -1); \ - } else \ - cpy = xmlStrdup((const xmlChar *)(str)); } - -/** - * DICT_CONST_COPY: - * @str: a string - * - * Copy a string using a "dict" dictionary in the current scope, - * if available. - */ -#define DICT_CONST_COPY(str, cpy) \ - if (str) { \ - if (dict) { \ - if (xmlDictOwns(dict, (const xmlChar *)(str))) \ - cpy = (const xmlChar *) (str); \ - else \ - cpy = xmlDictLookup((dict), (const xmlChar *)(str), -1); \ - } else \ - cpy = (const xmlChar *) xmlStrdup((const xmlChar *)(str)); } - - /** * xmlFreeDtd: * @cur: the DTD structure to free up @@ -1089,11 +1053,10 @@ xmlFreeDtd(xmlDtdPtr cur) { */ while (c != NULL) { next = c->next; - if ((c->type != XML_NOTATION_NODE) && - (c->type != XML_ELEMENT_DECL) && + if ((c->type != XML_ELEMENT_DECL) && (c->type != XML_ATTRIBUTE_DECL) && (c->type != XML_ENTITY_DECL)) { - xmlUnlinkNode(c); + xmlUnlinkNodeInternal(c); xmlFreeNode(c); } c = next; @@ -1120,11 +1083,11 @@ xmlFreeDtd(xmlDtdPtr cur) { /** * xmlNewDoc: - * @version: xmlChar string giving the version of XML "1.0" + * @version: XML version string like "1.0" (optional) * - * Creates a new XML document + * Creates a new XML document. If version is NULL, "1.0" is used. * - * Returns a new document + * Returns a new document or NULL if a memory allocation failed. */ xmlDocPtr xmlNewDoc(const xmlChar *version) { @@ -1137,16 +1100,13 @@ xmlNewDoc(const xmlChar *version) { * Allocate a new document and fill the fields. */ cur = (xmlDocPtr) xmlMalloc(sizeof(xmlDoc)); - if (cur == NULL) { - xmlTreeErrMemory("building doc"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlDoc)); cur->type = XML_DOCUMENT_NODE; cur->version = xmlStrdup(version); if (cur->version == NULL) { - xmlTreeErrMemory("building doc"); xmlFree(cur); return(NULL); } @@ -1171,7 +1131,7 @@ xmlNewDoc(const xmlChar *version) { * xmlFreeDoc: * @cur: pointer to the document * - * Free up all the structures used by a document, tree included. + * Free a document including all children and associated DTDs. */ void xmlFreeDoc(xmlDocPtr cur) { @@ -1182,7 +1142,7 @@ xmlFreeDoc(xmlDocPtr cur) { return; } - if (cur != NULL) dict = cur->dict; + dict = cur->dict; if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue((xmlNodePtr)cur); @@ -1199,12 +1159,12 @@ xmlFreeDoc(xmlDocPtr cur) { if (intSubset == extSubset) extSubset = NULL; if (extSubset != NULL) { - xmlUnlinkNode((xmlNodePtr) cur->extSubset); + xmlUnlinkNodeInternal((xmlNodePtr) cur->extSubset); cur->extSubset = NULL; xmlFreeDtd(extSubset); } if (intSubset != NULL) { - xmlUnlinkNode((xmlNodePtr) cur->intSubset); + xmlUnlinkNodeInternal((xmlNodePtr) cur->intSubset); cur->intSubset = NULL; xmlFreeDtd(intSubset); } @@ -1221,38 +1181,52 @@ xmlFreeDoc(xmlDocPtr cur) { } /** - * xmlStringLenGetNodeList: - * @doc: the document - * @value: the value of the text - * @len: the length of the string value + * xmlNodeParseContentInternal: + * @doc: a document (optional) + * @parent: an element or attribute (optional) + * @value: an attribute value + * @len: maximum length of the attribute value + * @listPtr: pointer to the resulting node list (optional) + * + * See xmlNodeParseContent. * - * Parse the value string and build the node list associated. Should - * produce a flat tree with only TEXTs and ENTITY_REFs. - * Returns a pointer to the first child + * Returns 0 on success, -1 if a memory allocation failed. */ -xmlNodePtr -xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) { - xmlNodePtr ret = NULL, last = NULL; +static int +xmlNodeParseContentInternal(const xmlDoc *doc, xmlNodePtr parent, + const xmlChar *value, int len, + xmlNodePtr *listPtr) { + xmlNodePtr head = NULL, last = NULL; xmlNodePtr node; - xmlChar *val; - const xmlChar *cur, *end; + xmlChar *val = NULL; + const xmlChar *cur; const xmlChar *q; xmlEntityPtr ent; xmlBufPtr buf; + int remaining; + + if (listPtr != NULL) + *listPtr = NULL; + + if (len < 0) + remaining = INT_MAX; + else + remaining = len; + + if ((value == NULL) || (value[0] == 0)) + goto done; - if (value == NULL) return(NULL); cur = value; - end = cur + len; - buf = xmlBufCreateSize(0); - if (buf == NULL) return(NULL); + buf = xmlBufCreateSize(64); + if (buf == NULL) + return(-1); xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); q = cur; - while ((cur < end) && (*cur != 0)) { + while ((remaining > 0) && (*cur != 0)) { if (cur[0] == '&') { int charval = 0; - xmlChar tmp; /* * Save the current text. @@ -1260,25 +1234,15 @@ xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) { if (cur != q) { if (xmlBufAdd(buf, q, cur - q)) goto out; + q = cur; } - q = cur; - if ((cur + 2 < end) && (cur[1] == '#') && (cur[2] == 'x')) { + + if ((remaining > 2) && (cur[1] == '#') && (cur[2] == 'x')) { + int tmp = 0; + cur += 3; - if (cur < end) - tmp = *cur; - else - tmp = 0; - while (tmp != ';') { /* Non input consuming loop */ - /* - * If you find an integer overflow here when fuzzing, - * the bug is probably elsewhere. This function should - * only receive entities that were already validated by - * the parser, typically by xmlParseAttValueComplex - * calling xmlStringDecodeEntities. - * - * So it's better *not* to check for overflow to - * potentially discover new bugs. - */ + remaining -= 3; + while ((remaining > 0) && ((tmp = *cur) != ';')) { if ((tmp >= '0') && (tmp <= '9')) charval = charval * 16 + (tmp - '0'); else if ((tmp >= 'a') && (tmp <= 'f')) @@ -1286,142 +1250,147 @@ xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) { else if ((tmp >= 'A') && (tmp <= 'F')) charval = charval * 16 + (tmp - 'A') + 10; else { - xmlTreeErr(XML_TREE_INVALID_HEX, (xmlNodePtr) doc, - NULL); charval = 0; break; } + if (charval > 0x110000) + charval = 0x110000; cur++; - if (cur < end) - tmp = *cur; - else - tmp = 0; + remaining--; } - if (tmp == ';') + if (tmp == ';') { cur++; + remaining--; + } q = cur; - } else if ((cur + 1 < end) && (cur[1] == '#')) { + } else if ((remaining > 1) && (cur[1] == '#')) { + int tmp = 0; + cur += 2; - if (cur < end) - tmp = *cur; - else - tmp = 0; - while (tmp != ';') { /* Non input consuming loops */ - /* Don't check for integer overflow, see above. */ + remaining -= 2; + while ((remaining > 0) && ((tmp = *cur) != ';')) { if ((tmp >= '0') && (tmp <= '9')) charval = charval * 10 + (tmp - '0'); else { - xmlTreeErr(XML_TREE_INVALID_DEC, (xmlNodePtr) doc, - NULL); charval = 0; break; } + if (charval > 0x110000) + charval = 0x110000; cur++; - if (cur < end) - tmp = *cur; - else - tmp = 0; + remaining--; } - if (tmp == ';') + if (tmp == ';') { cur++; + remaining--; + } q = cur; } else { /* * Read the entity string */ cur++; + remaining--; q = cur; - while ((cur < end) && (*cur != 0) && (*cur != ';')) cur++; - if ((cur >= end) || (*cur == 0)) { - xmlTreeErr(XML_TREE_UNTERMINATED_ENTITY, (xmlNodePtr) doc, - (const char *) q); - goto out; - } + while ((remaining > 0) && (*cur != 0) && (*cur != ';')) { + cur++; + remaining--; + } + if ((remaining <= 0) || (*cur == 0)) + break; if (cur != q) { - /* - * Predefined entities don't generate nodes - */ val = xmlStrndup(q, cur - q); + if (val == NULL) + goto out; ent = xmlGetDocEntity(doc, val); if ((ent != NULL) && (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { - + /* + * Predefined entities don't generate nodes + */ if (xmlBufCat(buf, ent->content)) goto out; - } else { /* * Flush buffer so far */ if (!xmlBufIsEmpty(buf)) { node = xmlNewDocText(doc, NULL); - if (node == NULL) { - if (val != NULL) xmlFree(val); + if (node == NULL) goto out; - } node->content = xmlBufDetach(buf); + node->parent = parent; if (last == NULL) { - last = ret = node; + head = node; } else { - last = xmlAddNextSibling(last, node); + last->next = node; + node->prev = last; } + last = node; + } + + if ((ent != NULL) && + ((ent->flags & XML_ENT_PARSED) == 0) && + ((ent->flags & XML_ENT_EXPANDING) == 0) && + (ent->content != NULL)) { + int res; + + ent->flags |= XML_ENT_EXPANDING; + res = xmlNodeParseContentInternal(doc, + (xmlNodePtr) ent, ent->content, -1, NULL); + ent->flags &= ~XML_ENT_EXPANDING; + if (res < 0) + goto out; + ent->flags |= XML_ENT_PARSED; } /* * Create a new REFERENCE_REF node */ - node = xmlNewReference(doc, val); - if (node == NULL) { - if (val != NULL) xmlFree(val); + node = xmlNewEntityRef((xmlDocPtr) doc, val); + val = NULL; + if (node == NULL) goto out; - } - else if ((ent != NULL) && - ((ent->flags & XML_ENT_PARSED) == 0) && - ((ent->flags & XML_ENT_EXPANDING) == 0)) { - xmlNodePtr temp; - - /* - * The entity should have been checked already, - * but set the flag anyway to avoid recursion. - */ - ent->flags |= XML_ENT_EXPANDING; - ent->children = xmlStringGetNodeList(doc, - (const xmlChar*)node->content); - ent->owner = 1; - ent->flags &= ~XML_ENT_EXPANDING; - ent->flags |= XML_ENT_PARSED; - temp = ent->children; - while (temp) { - temp->parent = (xmlNodePtr)ent; - ent->last = temp; - temp = temp->next; - } - } + node->parent = parent; + node->last = (xmlNodePtr) ent; + if (ent != NULL) { + node->children = (xmlNodePtr) ent; + node->content = ent->content; + } + if (last == NULL) { - last = ret = node; + head = node; } else { - last = xmlAddNextSibling(last, node); + last->next = node; + node->prev = last; } + last = node; } xmlFree(val); + val = NULL; } cur++; + remaining--; q = cur; } if (charval != 0) { xmlChar buffer[10]; int l; + if (charval >= 0x110000) + charval = 0xFFFD; /* replacement character */ + l = xmlCopyCharMultiByte(buffer, charval); buffer[l] = 0; if (xmlBufCat(buf, buffer)) goto out; - charval = 0; } - } else + } else { cur++; + remaining--; + } } if (cur != q) { @@ -1434,398 +1403,250 @@ xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) { if (!xmlBufIsEmpty(buf)) { node = xmlNewDocText(doc, NULL); - if (node == NULL) goto out; + if (node == NULL) + goto out; + node->parent = parent; node->content = xmlBufDetach(buf); if (last == NULL) { - ret = node; + head = node; } else { - xmlAddNextSibling(last, node); + last->next = node; + node->prev = last; } - } else if (ret == NULL) { - ret = xmlNewDocText(doc, BAD_CAST ""); + last = node; + } else if (head == NULL) { + head = xmlNewDocText(doc, BAD_CAST ""); + if (head == NULL) + goto out; + head->parent = parent; + last = head; + } + + xmlBufFree(buf); + +done: + if (parent != NULL) { + if (parent->children != NULL) + xmlFreeNodeList(parent->children); + parent->children = head; + parent->last = last; } + if (listPtr != NULL) + *listPtr = head; + + return(0); + out: xmlBufFree(buf); + if (val != NULL) + xmlFree(val); + if (head != NULL) + xmlFreeNodeList(head); + return(-1); +} + +/** + * xmlNodeParseContent: + * @node: an element or attribute + * @content: text content with XML references + * @len: maximum length of content + * + * Parse content and replace the node's children with the resulting + * node list. + * + * @content is expected to be a valid XML attribute value possibly + * containing character and entity references. Syntax errors + * and references to undeclared entities are ignored silently. + * Only references are handled, nested elements, comments or PIs are + * not. + * + * Returns 0 on success, -1 if a memory allocation failed. + */ +int +xmlNodeParseContent(xmlNodePtr node, const xmlChar *content, int len) { + return(xmlNodeParseContentInternal(node->doc, node, content, len, NULL)); +} + +/** + * xmlStringLenGetNodeList: + * @doc: a document (optional) + * @value: an attribute value + * @len: maximum length of the attribute value + * + * DEPRECATED: Use xmlNodeSetContentLen. + * + * See xmlStringGetNodeList. + * + * Returns a pointer to the first child or NULL if a memory + * allocation failed. + */ +xmlNodePtr +xmlStringLenGetNodeList(const xmlDoc *doc, const xmlChar *value, int len) { + xmlNodePtr ret; + + xmlNodeParseContentInternal(doc, NULL, value, len, &ret); return(ret); } /** * xmlStringGetNodeList: - * @doc: the document - * @value: the value of the attribute + * @doc: a document (optional) + * @value: an attribute value + * + * DEPRECATED: Use xmlNodeSetContent. + * + * Parse an attribute value and build a node list containing only + * text and entity reference nodes. The resulting nodes will be + * associated with the document if provided. The document is also + * used to look up entities. + * + * The input is not validated. Syntax errors or references to + * undeclared entities will be ignored silently with unspecified + * results. * - * Parse the value string and build the node list associated. Should - * produce a flat tree with only TEXTs and ENTITY_REFs. - * Returns a pointer to the first child + * Returns a pointer to the first child or NULL if a memory + * allocation failed. */ xmlNodePtr xmlStringGetNodeList(const xmlDoc *doc, const xmlChar *value) { - xmlNodePtr ret = NULL, head = NULL, last = NULL; - xmlNodePtr node; - xmlChar *val = NULL; - const xmlChar *cur = value; - const xmlChar *q; - xmlEntityPtr ent; + xmlNodePtr ret; + + xmlNodeParseContentInternal(doc, NULL, value, -1, &ret); + return(ret); +} + +/** + * xmlNodeListGetStringInternal: + * @doc: a document (optional) + * @node: a node list + * @escMode: escape mode (0 = no, 1 = elem, 2 = attr, 3 = raw) + * + * Returns a pointer to the string. + */ +static xmlChar * +xmlNodeListGetStringInternal(xmlDocPtr doc, const xmlNode *node, int escMode) { xmlBufPtr buf; + xmlChar *ret; - if (value == NULL) return(NULL); + if (node == NULL) + return(xmlStrdup(BAD_CAST "")); - buf = xmlBufCreateSize(0); - if (buf == NULL) return(NULL); - xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); + if ((escMode == 0) && + ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) && + (node->next == NULL)) { + if (node->content == NULL) + return(xmlStrdup(BAD_CAST "")); + return(xmlStrdup(node->content)); + } - q = cur; - while (*cur != 0) { - if (cur[0] == '&') { - int charval = 0; - xmlChar tmp; + buf = xmlBufCreateSize(64); + if (buf == NULL) + return(NULL); - /* - * Save the current text. - */ - if (cur != q) { - if (xmlBufAdd(buf, q, cur - q)) - goto out; - } - q = cur; - if ((cur[1] == '#') && (cur[2] == 'x')) { - cur += 3; - tmp = *cur; - while (tmp != ';') { /* Non input consuming loop */ - /* Don't check for integer overflow, see above. */ - if ((tmp >= '0') && (tmp <= '9')) - charval = charval * 16 + (tmp - '0'); - else if ((tmp >= 'a') && (tmp <= 'f')) - charval = charval * 16 + (tmp - 'a') + 10; - else if ((tmp >= 'A') && (tmp <= 'F')) - charval = charval * 16 + (tmp - 'A') + 10; - else { - xmlTreeErr(XML_TREE_INVALID_HEX, (xmlNodePtr) doc, - NULL); - charval = 0; - break; - } - cur++; - tmp = *cur; - } - if (tmp == ';') - cur++; - q = cur; - } else if (cur[1] == '#') { - cur += 2; - tmp = *cur; - while (tmp != ';') { /* Non input consuming loops */ - /* Don't check for integer overflow, see above. */ - if ((tmp >= '0') && (tmp <= '9')) - charval = charval * 10 + (tmp - '0'); - else { - xmlTreeErr(XML_TREE_INVALID_DEC, (xmlNodePtr) doc, - NULL); - charval = 0; - break; - } - cur++; - tmp = *cur; - } - if (tmp == ';') - cur++; - q = cur; - } else { - /* - * Read the entity string - */ - cur++; - q = cur; - while ((*cur != 0) && (*cur != ';')) cur++; - if (*cur == 0) { - xmlTreeErr(XML_TREE_UNTERMINATED_ENTITY, - (xmlNodePtr) doc, (const char *) q); - goto out; - } - if (cur != q) { - /* - * Predefined entities don't generate nodes - */ - val = xmlStrndup(q, cur - q); - ent = xmlGetDocEntity(doc, val); - if ((ent != NULL) && - (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY)) { - - if (xmlBufCat(buf, ent->content)) - goto out; - - } else { - /* - * Flush buffer so far - */ - if (!xmlBufIsEmpty(buf)) { - node = xmlNewDocText(doc, NULL); - if (node == NULL) - goto out; - node->content = xmlBufDetach(buf); - - if (last == NULL) { - last = head = node; - } else { - last = xmlAddNextSibling(last, node); - } - } - - /* - * Create a new REFERENCE_REF node - */ - node = xmlNewReference(doc, val); - if (node == NULL) - goto out; - if ((ent != NULL) && - ((ent->flags & XML_ENT_PARSED) == 0) && - ((ent->flags & XML_ENT_EXPANDING) == 0)) { - xmlNodePtr temp; - - /* - * The entity should have been checked already, - * but set the flag anyway to avoid recursion. - */ - ent->flags |= XML_ENT_EXPANDING; - ent->children = xmlStringGetNodeList(doc, - (const xmlChar*)node->content); - ent->owner = 1; - ent->flags &= ~XML_ENT_EXPANDING; - ent->flags |= XML_ENT_PARSED; - temp = ent->children; - while (temp) { - temp->parent = (xmlNodePtr)ent; - ent->last = temp; - temp = temp->next; - } - } - if (last == NULL) { - last = head = node; - } else { - last = xmlAddNextSibling(last, node); - } - } - xmlFree(val); - val = NULL; - } - cur++; - q = cur; - } - if (charval != 0) { - xmlChar buffer[10]; - int len; - - len = xmlCopyCharMultiByte(buffer, charval); - buffer[len] = 0; - - if (xmlBufCat(buf, buffer)) - goto out; - charval = 0; - } - } else - cur++; - } - if ((cur != q) || (head == NULL)) { - /* - * Handle the last piece of text. - */ - xmlBufAdd(buf, q, cur - q); - } - - if (!xmlBufIsEmpty(buf)) { - node = xmlNewDocText(doc, NULL); - if (node == NULL) - goto out; - node->content = xmlBufDetach(buf); + while (node != NULL) { + if ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) { + if (node->content != NULL) { + if (escMode == 0) { + xmlBufCat(buf, node->content); + } else { + xmlChar *encoded; + + if (escMode == 1) + encoded = xmlEncodeEntitiesReentrant(doc, + node->content); + else if (escMode == 2) + encoded = xmlEncodeAttributeEntities(doc, + node->content); + else + encoded = xmlEncodeSpecialChars(doc, node->content); + if (encoded == NULL) + goto error; + xmlBufCat(buf, encoded); + xmlFree(encoded); + } + } + } else if (node->type == XML_ENTITY_REF_NODE) { + if (escMode == 0) { + xmlBufGetNodeContent(buf, node); + } else { + xmlBufAdd(buf, BAD_CAST "&", 1); + xmlBufCat(buf, node->name); + xmlBufAdd(buf, BAD_CAST ";", 1); + } + } - if (last == NULL) { - head = node; - } else { - xmlAddNextSibling(last, node); - } + node = node->next; } - ret = head; - head = NULL; - -out: + ret = xmlBufDetach(buf); xmlBufFree(buf); - if (val != NULL) xmlFree(val); - if (head != NULL) xmlFreeNodeList(head); return(ret); + +error: + xmlBufFree(buf); + return(NULL); } /** * xmlNodeListGetString: - * @doc: the document - * @list: a Node list - * @inLine: should we replace entity contents or show their external form + * @doc: a document (optional) + * @list: a node list of attribute children (optional) + * @inLine: whether entity references are substituted + * + * Serializes attribute children (text and entity reference nodes) + * into a string. An empty list produces an empty string. * - * Build the string equivalent to the text contained in the Node list - * made of TEXTs and ENTITY_REFs + * If @inLine is true, entity references will be substituted. + * Otherwise, entity references will be kept and special characters + * like '&' as well as non-ASCII chars will be escaped. See + * xmlNodeListGetRawString for an alternative option. * - * Returns a pointer to the string copy, the caller must free it with xmlFree(). + * Returns a string or NULL if a memory allocation failed. */ xmlChar * xmlNodeListGetString(xmlDocPtr doc, const xmlNode *list, int inLine) { - const xmlNode *node = list; - xmlChar *ret = NULL; - xmlEntityPtr ent; - int attr; - - if (list == NULL) - return (NULL); - if ((list->parent != NULL) && (list->parent->type == XML_ATTRIBUTE_NODE)) - attr = 1; - else - attr = 0; - - while (node != NULL) { - if ((node->type == XML_TEXT_NODE) || - (node->type == XML_CDATA_SECTION_NODE)) { - if (inLine) { - ret = xmlStrcat(ret, node->content); - } else { - xmlChar *buffer; + int escMode; - if (attr) - buffer = xmlEncodeAttributeEntities(doc, node->content); - else - buffer = xmlEncodeEntitiesReentrant(doc, node->content); - if (buffer != NULL) { - ret = xmlStrcat(ret, buffer); - xmlFree(buffer); - } - } - } else if (node->type == XML_ENTITY_REF_NODE) { - if (inLine) { - ent = xmlGetDocEntity(doc, node->name); - if (ent != NULL) { - xmlChar *buffer; - - /* an entity content can be any "well balanced chunk", - * i.e. the result of the content [43] production: - * http://www.w3.org/TR/REC-xml#NT-content. - * So it can contain text, CDATA section or nested - * entity reference nodes (among others). - * -> we recursive call xmlNodeListGetString() - * which handles these types */ - buffer = xmlNodeListGetString(doc, ent->children, 1); - if (buffer != NULL) { - ret = xmlStrcat(ret, buffer); - xmlFree(buffer); - } - } else { - ret = xmlStrcat(ret, node->content); - } - } else { - xmlChar buf[2]; - - buf[0] = '&'; - buf[1] = 0; - ret = xmlStrncat(ret, buf, 1); - ret = xmlStrcat(ret, node->name); - buf[0] = ';'; - buf[1] = 0; - ret = xmlStrncat(ret, buf, 1); - } - } -#if 0 - else { - xmlGenericError(xmlGenericErrorContext, - "xmlGetNodeListString : invalid node type %d\n", - node->type); - } -#endif - node = node->next; + if (inLine) { + escMode = 0; + } else { + if ((list != NULL) && + (list->parent != NULL) && + (list->parent->type == XML_ATTRIBUTE_NODE)) + escMode = 2; + else + escMode = 1; } - return (ret); + + return(xmlNodeListGetStringInternal(doc, list, escMode)); } #ifdef LIBXML_TREE_ENABLED /** * xmlNodeListGetRawString: - * @doc: the document - * @list: a Node list - * @inLine: should we replace entity contents or show their external form + * @doc: a document (optional) + * @list: a node list of attribute children (optional) + * @inLine: whether entity references are substituted * - * Builds the string equivalent to the text contained in the Node list - * made of TEXTs and ENTITY_REFs, contrary to xmlNodeListGetString() - * this function doesn't do any character encoding handling. + * Serializes attribute children (text and entity reference nodes) + * into a string. An empty list produces an empty string. * - * Returns a pointer to the string copy, the caller must free it with xmlFree(). + * If @inLine is true, entity references will be substituted. + * Otherwise, entity references will be kept and special characters + * like '&' will be escaped. + * + * Returns a string or NULL if a memory allocation failed. */ xmlChar * xmlNodeListGetRawString(const xmlDoc *doc, const xmlNode *list, int inLine) { - const xmlNode *node = list; - xmlChar *ret = NULL; - xmlEntityPtr ent; - - if (list == NULL) - return (NULL); - - while (node != NULL) { - if ((node->type == XML_TEXT_NODE) || - (node->type == XML_CDATA_SECTION_NODE)) { - if (inLine) { - ret = xmlStrcat(ret, node->content); - } else { - xmlChar *buffer; - - buffer = xmlEncodeSpecialChars(doc, node->content); - if (buffer != NULL) { - ret = xmlStrcat(ret, buffer); - xmlFree(buffer); - } - } - } else if (node->type == XML_ENTITY_REF_NODE) { - if (inLine) { - ent = xmlGetDocEntity(doc, node->name); - if (ent != NULL) { - xmlChar *buffer; - - /* an entity content can be any "well balanced chunk", - * i.e. the result of the content [43] production: - * http://www.w3.org/TR/REC-xml#NT-content. - * So it can contain text, CDATA section or nested - * entity reference nodes (among others). - * -> we recursive call xmlNodeListGetRawString() - * which handles these types */ - buffer = - xmlNodeListGetRawString(doc, ent->children, 1); - if (buffer != NULL) { - ret = xmlStrcat(ret, buffer); - xmlFree(buffer); - } - } else { - ret = xmlStrcat(ret, node->content); - } - } else { - xmlChar buf[2]; - - buf[0] = '&'; - buf[1] = 0; - ret = xmlStrncat(ret, buf, 1); - ret = xmlStrcat(ret, node->name); - buf[0] = ';'; - buf[1] = 0; - ret = xmlStrncat(ret, buf, 1); - } - } -#if 0 - else { - xmlGenericError(xmlGenericErrorContext, - "xmlGetNodeListString : invalid node type %d\n", - node->type); - } -#endif - node = node->next; - } - return (ret); + int escMode = inLine ? 0 : 3; + return(xmlNodeListGetStringInternal((xmlDocPtr) doc, list, escMode)); } #endif /* LIBXML_TREE_ENABLED */ @@ -1855,7 +1676,6 @@ xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns, (node->doc->dict == NULL) || (!(xmlDictOwns(node->doc->dict, name))))) xmlFree((xmlChar *) name); - xmlTreeErrMemory("building attribute"); return (NULL); } memset(cur, 0, sizeof(xmlAttr)); @@ -1873,6 +1693,8 @@ xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns, cur->name = (xmlChar *) xmlDictLookup(doc->dict, name, -1); else cur->name = xmlStrdup(name); + if (cur->name == NULL) + goto error; } else cur->name = name; @@ -1880,6 +1702,8 @@ xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns, xmlNodePtr tmp; cur->children = xmlNewDocText(doc, value); + if (cur->children == NULL) + goto error; cur->last = NULL; tmp = cur->children; while (tmp != NULL) { @@ -1888,6 +1712,15 @@ xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns, cur->last = tmp; tmp = tmp->next; } + + if (doc != NULL) { + int res = xmlIsID(doc, node, cur); + + if (res < 0) + goto error; + if ((res == 1) && (xmlAddIDSafe(cur, value) < 0)) + goto error; + } } /* @@ -1906,25 +1739,33 @@ xmlNewPropInternal(xmlNodePtr node, xmlNsPtr ns, } } - if ((value != NULL) && (node != NULL) && - (xmlIsID(node->doc, node, cur) == 1)) - xmlAddID(NULL, node->doc, value, cur); - if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr) cur); return (cur); + +error: + xmlFreeProp(cur); + return(NULL); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \ defined(LIBXML_SCHEMAS_ENABLED) /** * xmlNewProp: - * @node: the holding node + * @node: the parent node (optional) * @name: the name of the attribute - * @value: the value of the attribute + * @value: the value of the attribute (optional) * - * Create a new property carried by a node. - * Returns a pointer to the attribute + * Create an attribute node. + * + * If provided, @value should be a raw, unescaped string. + * + * If @node is provided, the created attribute will be appended without + * checking for duplicate names. It is an error if @node is not an + * element. + * + * Returns a pointer to the attribute or NULL if arguments are invalid + * or a memory allocation failed. */ xmlAttrPtr xmlNewProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { @@ -1939,13 +1780,21 @@ xmlNewProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { /** * xmlNewNsProp: - * @node: the holding node - * @ns: the namespace - * @name: the name of the attribute - * @value: the value of the attribute + * @node: the parent node (optional) + * @ns: the namespace (optional) + * @name: the local name of the attribute + * @value: the value of the attribute (optional) * - * Create a new property tagged with a namespace and carried by a node. - * Returns a pointer to the attribute + * Create an attribute object. + * + * If provided, @value should be a raw, unescaped string. + * + * If @node is provided, the created attribute will be appended without + * checking for duplicate names. It is an error if @node is not an + * element. + * + * Returns a pointer to the attribute or NULL if arguments are invalid + * or a memory allocation failed. */ xmlAttrPtr xmlNewNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, @@ -1960,13 +1809,17 @@ xmlNewNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, /** * xmlNewNsPropEatName: - * @node: the holding node - * @ns: the namespace - * @name: the name of the attribute - * @value: the value of the attribute + * @node: the parent node (optional) + * @ns: the namespace (optional) + * @name: the local name of the attribute + * @value: the value of the attribute (optional) * - * Create a new property tagged with a namespace and carried by a node. - * Returns a pointer to the attribute + * Like xmlNewNsProp, but the @name string will be used directly + * without making a copy. Takes ownership of @name which will also + * be freed on error. + * + * Returns a pointer to the attribute or NULL if arguments are invalid + * or a memory allocation failed. */ xmlAttrPtr xmlNewNsPropEatName(xmlNodePtr node, xmlNsPtr ns, xmlChar *name, @@ -1981,17 +1834,19 @@ xmlNewNsPropEatName(xmlNodePtr node, xmlNsPtr ns, xmlChar *name, /** * xmlNewDocProp: - * @doc: the document + * @doc: the target document (optional) * @name: the name of the attribute - * @value: the value of the attribute + * @value: attribute value with XML references (optional) * - * Create a new property carried by a document. - * NOTE: @value is supposed to be a piece of XML CDATA, so it allows entity - * references, but XML special chars need to be escaped first by using - * xmlEncodeEntitiesReentrant(). Use xmlNewProp() if you don't need - * entities support. + * Create an attribute object. * - * Returns a pointer to the attribute + * If provided, @value is expected to be a valid XML attribute value + * possibly containing character and entity references. Syntax errors + * and references to undeclared entities are ignored silently. + * If you want to pass a raw string, see xmlNewProp. + * + * Returns a pointer to the attribute or NULL if arguments are invalid + * or a memory allocation failed. */ xmlAttrPtr xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) { @@ -2005,10 +1860,8 @@ xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) { * Allocate a new property and fill the fields. */ cur = (xmlAttrPtr) xmlMalloc(sizeof(xmlAttr)); - if (cur == NULL) { - xmlTreeErrMemory("building attribute"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlAttr)); cur->type = XML_ATTRIBUTE_NODE; @@ -2016,32 +1869,28 @@ xmlNewDocProp(xmlDocPtr doc, const xmlChar *name, const xmlChar *value) { cur->name = xmlDictLookup(doc->dict, name, -1); else cur->name = xmlStrdup(name); + if (cur->name == NULL) + goto error; cur->doc = doc; if (value != NULL) { - xmlNodePtr tmp; - - cur->children = xmlStringGetNodeList(doc, value); - cur->last = NULL; - - tmp = cur->children; - while (tmp != NULL) { - tmp->parent = (xmlNodePtr) cur; - if (tmp->next == NULL) - cur->last = tmp; - tmp = tmp->next; - } + if (xmlNodeParseContent((xmlNodePtr) cur, value, -1) < 0) + goto error; } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)cur); return(cur); + +error: + xmlFreeProp(cur); + return(NULL); } /** * xmlFreePropList: - * @cur: the first property in the list + * @cur: the first attribute in the list * - * Free a property and all its siblings, all the children are freed too. + * Free an attribute list including all children. */ void xmlFreePropList(xmlAttrPtr cur) { @@ -2058,7 +1907,7 @@ xmlFreePropList(xmlAttrPtr cur) { * xmlFreeProp: * @cur: an attribute * - * Free one attribute, all the content is freed too + * Free an attribute including all children. */ void xmlFreeProp(xmlAttrPtr cur) { @@ -2083,10 +1932,14 @@ xmlFreeProp(xmlAttrPtr cur) { * xmlRemoveProp: * @cur: an attribute * - * Unlink and free one attribute, all the content is freed too - * Note this doesn't work for namespace definition attributes + * Unlink and free an attribute including all children. * - * Returns 0 if success and -1 in case of error. + * Note this doesn't work for namespace declarations. + * + * The attribute must have a non-NULL parent pointer. + * + * Returns 0 on success or -1 if the attribute was not found or + * arguments are invalid. */ int xmlRemoveProp(xmlAttrPtr cur) { @@ -2120,12 +1973,14 @@ xmlRemoveProp(xmlAttrPtr cur) { /** * xmlNewDocPI: - * @doc: the target document - * @name: the processing instruction name - * @content: the PI content + * @doc: the target document (optional) + * @name: the processing instruction target + * @content: the PI content (optional) + * + * Create a processing instruction object. * - * Creation of a processing instruction element. - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewDocPI(xmlDocPtr doc, const xmlChar *name, const xmlChar *content) { @@ -2139,37 +1994,44 @@ xmlNewDocPI(xmlDocPtr doc, const xmlChar *name, const xmlChar *content) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building PI"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_PI_NODE; + cur->doc = doc; if ((doc != NULL) && (doc->dict != NULL)) cur->name = xmlDictLookup(doc->dict, name, -1); else cur->name = xmlStrdup(name); + if (cur->name == NULL) + goto error; if (content != NULL) { cur->content = xmlStrdup(content); + if (cur->content == NULL) + goto error; } - cur->doc = doc; if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)cur); return(cur); + +error: + xmlFreeNode(cur); + return(NULL); } /** * xmlNewPI: - * @name: the processing instruction name - * @content: the PI content + * @name: the processing instruction target + * @content: the PI content (optional) * - * Creation of a processing instruction element. + * Create a processing instruction node. * * Use of this function is DISCOURAGED in favor of xmlNewDocPI. * - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewPI(const xmlChar *name, const xmlChar *content) { @@ -2178,116 +2040,122 @@ xmlNewPI(const xmlChar *name, const xmlChar *content) { /** * xmlNewNode: - * @ns: namespace if any + * @ns: namespace (optional) * @name: the node name * - * Creation of a new node element. @ns is optional (NULL). + * Create an element node. * * Use of this function is DISCOURAGED in favor of xmlNewDocNode. * - * Returns a pointer to the new node object. Uses xmlStrdup() to make - * copy of @name. + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewNode(xmlNsPtr ns, const xmlChar *name) { - xmlNodePtr cur; - - if (name == NULL) { - return(NULL); - } - - /* - * Allocate a new node and fill the fields. - */ - cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building node"); - return(NULL); - } - memset(cur, 0, sizeof(xmlNode)); - cur->type = XML_ELEMENT_NODE; - - cur->name = xmlStrdup(name); - cur->ns = ns; - - if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) - xmlRegisterNodeDefaultValue(cur); - return(cur); + return(xmlNewDocNode(NULL, ns, name, NULL)); } /** * xmlNewNodeEatName: - * @ns: namespace if any + * @ns: namespace (optional) * @name: the node name * - * Creation of a new node element. @ns is optional (NULL). + * Create an element node. * * Use of this function is DISCOURAGED in favor of xmlNewDocNodeEatName. * - * Returns a pointer to the new node object, with pointer @name as - * new node's name. Use xmlNewNode() if a copy of @name string is - * is needed as new node's name. + * Like xmlNewNode, but the @name string will be used directly + * without making a copy. Takes ownership of @name which will also + * be freed on error. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewNodeEatName(xmlNsPtr ns, xmlChar *name) { - xmlNodePtr cur; + return(xmlNewDocNodeEatName(NULL, ns, name, NULL)); +} - if (name == NULL) { - return(NULL); - } +static xmlNodePtr +xmlNewElem(xmlDocPtr doc, xmlNsPtr ns, const xmlChar *name, + const xmlChar *content) { + xmlNodePtr cur; - /* - * Allocate a new node and fill the fields. - */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building node"); - /* we can't check here that name comes from the doc dictionary */ + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); - cur->type = XML_ELEMENT_NODE; + cur->type = XML_ELEMENT_NODE; + cur->doc = doc; cur->name = name; cur->ns = ns; + if (content != NULL) { + if (xmlNodeParseContent(cur, content, -1) < 0) { + /* Don't free name on error */ + xmlFree(cur); + return(NULL); + } + } + if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)cur); + return(cur); } /** * xmlNewDocNode: - * @doc: the document - * @ns: namespace if any + * @doc: the target document + * @ns: namespace (optional) * @name: the node name - * @content: the XML text content if any + * @content: text content with XML references (optional) + * + * Create an element node. + * + * If provided, @content is expected to be a valid XML attribute value + * possibly containing character and entity references. Syntax errors + * and references to undeclared entities are ignored silently. + * Only references are handled, nested elements, comments or PIs are + * not. See xmlNewDocRawNode for an alternative. * - * Creation of a new node element within a document. @ns and @content - * are optional (NULL). - * NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities - * references, but XML special chars need to be escaped first by using - * xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't - * need entities support. + * General notes on object creation: * - * Returns a pointer to the new node object. + * Each node and all its children are associated with the same + * document. The document should be provided when creating nodes to + * avoid a performance penalty when adding the node to a document + * tree. Note that a document only owns nodes reachable from the root + * node. Unlinked subtrees must be freed manually. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewDocNode(xmlDocPtr doc, xmlNsPtr ns, const xmlChar *name, const xmlChar *content) { xmlNodePtr cur; + xmlChar *copy; - if ((doc != NULL) && (doc->dict != NULL)) - cur = xmlNewNodeEatName(ns, (xmlChar *) - xmlDictLookup(doc->dict, name, -1)); - else - cur = xmlNewNode(ns, name); - if (cur != NULL) { - cur->doc = doc; - if (content != NULL) { - cur->children = xmlStringGetNodeList(doc, content); - UPDATE_LAST_CHILD_AND_PARENT(cur) - } + if (name == NULL) + return(NULL); + + if ((doc != NULL) && (doc->dict != NULL)) { + const xmlChar *dictName = xmlDictLookup(doc->dict, name, -1); + + if (dictName == NULL) + return(NULL); + return(xmlNewElem(doc, ns, dictName, content)); + } + + copy = xmlStrdup(name); + if (copy == NULL) + return(NULL); + + cur = xmlNewElem(doc, ns, copy, content); + if (cur == NULL) { + xmlFree(copy); + return(NULL); } return(cur); @@ -2295,54 +2163,55 @@ xmlNewDocNode(xmlDocPtr doc, xmlNsPtr ns, /** * xmlNewDocNodeEatName: - * @doc: the document - * @ns: namespace if any + * @doc: the target document + * @ns: namespace (optional) * @name: the node name - * @content: the XML text content if any + * @content: text content with XML references (optional) * - * Creation of a new node element within a document. @ns and @content - * are optional (NULL). - * NOTE: @content is supposed to be a piece of XML CDATA, so it allow entities - * references, but XML special chars need to be escaped first by using - * xmlEncodeEntitiesReentrant(). Use xmlNewDocRawNode() if you don't - * need entities support. + * Create an element node. * - * Returns a pointer to the new node object. + * Like xmlNewDocNode, but the @name string will be used directly + * without making a copy. Takes ownership of @name which will also + * be freed on error. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewDocNodeEatName(xmlDocPtr doc, xmlNsPtr ns, - xmlChar *name, const xmlChar *content) { + xmlChar *name, const xmlChar *content) { xmlNodePtr cur; - cur = xmlNewNodeEatName(ns, name); - if (cur != NULL) { - cur->doc = doc; - if (content != NULL) { - cur->children = xmlStringGetNodeList(doc, content); - UPDATE_LAST_CHILD_AND_PARENT(cur) - } - } else { - /* if name don't come from the doc dictionary free it here */ - if ((name != NULL) && - ((doc == NULL) || (doc->dict == NULL) || - (!(xmlDictOwns(doc->dict, name))))) - xmlFree(name); + if (name == NULL) + return(NULL); + + cur = xmlNewElem(doc, ns, name, content); + if (cur == NULL) { + /* if name doesn't come from the doc dictionary free it here */ + if ((doc == NULL) || + (doc->dict == NULL) || + (!xmlDictOwns(doc->dict, name))) + xmlFree(name); + return(NULL); } + return(cur); } #ifdef LIBXML_TREE_ENABLED /** * xmlNewDocRawNode: - * @doc: the document - * @ns: namespace if any + * @doc: the target document + * @ns: namespace (optional) * @name: the node name - * @content: the text content if any + * @content: raw text content (optional) * - * Creation of a new node element within a document. @ns and @content - * are optional (NULL). + * Create an element node. * - * Returns a pointer to the new node object. + * If provided, @value should be a raw, unescaped string. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewDocRawNode(xmlDocPtr doc, xmlNsPtr ns, @@ -2353,8 +2222,17 @@ xmlNewDocRawNode(xmlDocPtr doc, xmlNsPtr ns, if (cur != NULL) { cur->doc = doc; if (content != NULL) { - cur->children = xmlNewDocText(doc, content); - UPDATE_LAST_CHILD_AND_PARENT(cur) + xmlNodePtr text; + + text = xmlNewDocText(doc, content); + if (text == NULL) { + xmlFreeNode(cur); + return(NULL); + } + + cur->children = text; + cur->last = text; + text->parent = cur; } } return(cur); @@ -2362,10 +2240,12 @@ xmlNewDocRawNode(xmlDocPtr doc, xmlNsPtr ns, /** * xmlNewDocFragment: - * @doc: the document owning the fragment + * @doc: the target document (optional) + * + * Create a document fragment node. * - * Creation of a new Fragment node. - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewDocFragment(xmlDocPtr doc) { @@ -2375,10 +2255,8 @@ xmlNewDocFragment(xmlDocPtr doc) { * Allocate a new DocumentFragment node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building fragment"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_DOCUMENT_FRAG_NODE; @@ -2392,13 +2270,14 @@ xmlNewDocFragment(xmlDocPtr doc) { /** * xmlNewText: - * @content: the text content + * @content: raw text content (optional) * - * Creation of a new text node. + * Create a text node. * * Use of this function is DISCOURAGED in favor of xmlNewDocText. * - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewText(const xmlChar *content) { @@ -2408,83 +2287,77 @@ xmlNewText(const xmlChar *content) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building text"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_TEXT_NODE; cur->name = xmlStringText; if (content != NULL) { cur->content = xmlStrdup(content); + if (cur->content == NULL) + goto error; } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue(cur); return(cur); + +error: + xmlFreeNode(cur); + return(NULL); } #ifdef LIBXML_TREE_ENABLED /** * xmlNewTextChild: * @parent: the parent node - * @ns: a namespace if any + * @ns: a namespace (optional) * @name: the name of the child - * @content: the text content of the child if any. + * @content: raw text content of the child (optional) + * + * Create a new child element and append it to a parent element. * - * Creation of a new child element, added at the end of @parent children list. - * @ns and @content parameters are optional (NULL). If @ns is NULL, the newly - * created element inherits the namespace of @parent. If @content is non NULL, - * a child TEXT node will be created containing the string @content. - * NOTE: Use xmlNewChild() if @content will contain entities that need to be - * preserved. Use this function, xmlNewTextChild(), if you need to ensure that - * reserved XML chars that might appear in @content, such as the ampersand, - * greater-than or less-than signs, are automatically replaced by their XML - * escaped entity representations. + * If @ns is NULL, the newly created element inherits the namespace + * of the parent. * - * Returns a pointer to the new node object. + * If @content is provided, a text node will be added to the child + * element, see xmlNewDocRawNode. + * + * Returns a pointer to the new node object or NULL if arguments + * are invalid or a memory allocation failed. */ xmlNodePtr xmlNewTextChild(xmlNodePtr parent, xmlNsPtr ns, const xmlChar *name, const xmlChar *content) { xmlNodePtr cur, prev; - if (parent == NULL) { + if ((parent == NULL) || (name == NULL)) return(NULL); - } - if (name == NULL) { - return(NULL); - } + switch (parent->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + break; - /* - * Allocate a new node - */ - if (parent->type == XML_ELEMENT_NODE) { - if (ns == NULL) - cur = xmlNewDocRawNode(parent->doc, parent->ns, name, content); - else - cur = xmlNewDocRawNode(parent->doc, ns, name, content); - } else if ((parent->type == XML_DOCUMENT_NODE) || - (parent->type == XML_HTML_DOCUMENT_NODE)) { - if (ns == NULL) - cur = xmlNewDocRawNode((xmlDocPtr) parent, NULL, name, content); - else - cur = xmlNewDocRawNode((xmlDocPtr) parent, ns, name, content); - } else if (parent->type == XML_DOCUMENT_FRAG_NODE) { - cur = xmlNewDocRawNode( parent->doc, ns, name, content); - } else { - return(NULL); + case XML_ELEMENT_NODE: + if (ns == NULL) + ns = parent->ns; + break; + + default: + return(NULL); } - if (cur == NULL) return(NULL); + + cur = xmlNewDocRawNode(parent->doc, ns, name, content); + if (cur == NULL) + return(NULL); /* * add the new element at the end of the children list. */ - cur->type = XML_ELEMENT_NODE; cur->parent = parent; - cur->doc = parent->doc; if (parent->children == NULL) { parent->children = cur; parent->last = cur; @@ -2500,55 +2373,92 @@ xmlNewTextChild(xmlNodePtr parent, xmlNsPtr ns, #endif /* LIBXML_TREE_ENABLED */ /** - * xmlNewCharRef: - * @doc: the document - * @name: the char ref string, starting with # or "&# ... ;" + * xmlNewEntityRef: + * @doc: the target document (optional) + * @name: the entity name + * + * Create an empty entity reference node. This function doesn't attempt + * to look up the entity in @doc. * - * Creation of a new character reference node. - * Returns a pointer to the new node object. + * @name is consumed. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ -xmlNodePtr -xmlNewCharRef(xmlDocPtr doc, const xmlChar *name) { +static xmlNodePtr +xmlNewEntityRef(xmlDocPtr doc, xmlChar *name) { xmlNodePtr cur; - if (name == NULL) - return(NULL); - /* * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); if (cur == NULL) { - xmlTreeErrMemory("building character reference"); + xmlFree(name); return(NULL); } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_ENTITY_REF_NODE; - cur->doc = doc; + cur->name = name; + + if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) + xmlRegisterNodeDefaultValue(cur); + + return(cur); +} + +/** + * xmlNewCharRef: + * @doc: the target document (optional) + * @name: the entity name + * + * This function is MISNAMED. It doesn't create a character reference + * but an entity reference. + * + * Create an empty entity reference node. This function doesn't attempt + * to look up the entity in @doc. + * + * Entity names like '&entity;' are handled as well. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. + */ +xmlNodePtr +xmlNewCharRef(xmlDocPtr doc, const xmlChar *name) { + xmlChar *copy; + + if (name == NULL) + return(NULL); + if (name[0] == '&') { int len; name++; len = xmlStrlen(name); if (name[len - 1] == ';') - cur->name = xmlStrndup(name, len - 1); + copy = xmlStrndup(name, len - 1); else - cur->name = xmlStrndup(name, len); + copy = xmlStrndup(name, len); } else - cur->name = xmlStrdup(name); + copy = xmlStrdup(name); + if (copy == NULL) + return(NULL); - if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) - xmlRegisterNodeDefaultValue(cur); - return(cur); + return(xmlNewEntityRef(doc, copy)); } /** * xmlNewReference: - * @doc: the document - * @name: the reference name, or the reference string with & and ; + * @doc: the target document (optional) + * @name: the entity name * - * Creation of a new reference node. - * Returns a pointer to the new node object. + * Create a new entity reference node, linking the result with the + * entity in @doc if found. + * + * Entity names like '&entity;' are handled as well. + * + * Returns a pointer to the new node object or NULL if arguments are + * invalid or a memory allocation failed. */ xmlNodePtr xmlNewReference(const xmlDoc *doc, const xmlChar *name) { @@ -2562,10 +2472,8 @@ xmlNewReference(const xmlDoc *doc, const xmlChar *name) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building reference"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_ENTITY_REF_NODE; @@ -2580,6 +2488,8 @@ xmlNewReference(const xmlDoc *doc, const xmlChar *name) { cur->name = xmlStrndup(name, len); } else cur->name = xmlStrdup(name); + if (cur->name == NULL) + goto error; ent = xmlGetDocEntity(doc, cur->name); if (ent != NULL) { @@ -2596,15 +2506,21 @@ xmlNewReference(const xmlDoc *doc, const xmlChar *name) { if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue(cur); return(cur); + +error: + xmlFreeNode(cur); + return(NULL); } /** * xmlNewDocText: - * @doc: the document - * @content: the text content + * @doc: the target document + * @content: raw text content (optional) + * + * Create a new text node. * - * Creation of a new text node within a document. - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewDocText(const xmlDoc *doc, const xmlChar *content) { @@ -2617,13 +2533,13 @@ xmlNewDocText(const xmlDoc *doc, const xmlChar *content) { /** * xmlNewTextLen: - * @content: the text content - * @len: the text len. + * @content: raw text content (optional) + * @len: size of text content * * Use of this function is DISCOURAGED in favor of xmlNewDocTextLen. * - * Creation of a new text node with an extra parameter for the content's length - * Returns a pointer to the new node object. + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewTextLen(const xmlChar *content, int len) { @@ -2633,16 +2549,18 @@ xmlNewTextLen(const xmlChar *content, int len) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building text"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_TEXT_NODE; cur->name = xmlStringText; if (content != NULL) { cur->content = xmlStrndup(content, len); + if (cur->content == NULL) { + xmlFreeNode(cur); + return(NULL); + } } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) @@ -2652,13 +2570,14 @@ xmlNewTextLen(const xmlChar *content, int len) { /** * xmlNewDocTextLen: - * @doc: the document - * @content: the text content - * @len: the text len. + * @doc: the target document + * @content: raw text content (optional) + * @len: size of text content * - * Creation of a new text node with an extra content length parameter. The - * text node pertain to a given document. - * Returns a pointer to the new node object. + * Create a new text node. + * + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewDocTextLen(xmlDocPtr doc, const xmlChar *content, int len) { @@ -2671,12 +2590,14 @@ xmlNewDocTextLen(xmlDocPtr doc, const xmlChar *content, int len) { /** * xmlNewComment: - * @content: the comment content + * @content: the comment content (optional) * * Use of this function is DISCOURAGED in favor of xmlNewDocComment. * - * Creation of a new node containing a comment. - * Returns a pointer to the new node object. + * Create a comment node. + * + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewComment(const xmlChar *content) { @@ -2686,31 +2607,37 @@ xmlNewComment(const xmlChar *content) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building comment"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_COMMENT_NODE; cur->name = xmlStringComment; if (content != NULL) { cur->content = xmlStrdup(content); + if (cur->content == NULL) + goto error; } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue(cur); return(cur); + +error: + xmlFreeNode(cur); + return(NULL); } /** * xmlNewCDataBlock: - * @doc: the document - * @content: the CDATA block content content - * @len: the length of the block + * @doc: the target document (optional) + * @content: raw text content (optional) + * @len: size of text content * - * Creation of a new node containing a CDATA block. - * Returns a pointer to the new node object. + * Create a CDATA section node. + * + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len) { @@ -2720,16 +2647,18 @@ xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len) { * Allocate a new node and fill the fields. */ cur = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (cur == NULL) { - xmlTreeErrMemory("building CDATA"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlNode)); cur->type = XML_CDATA_SECTION_NODE; cur->doc = doc; if (content != NULL) { cur->content = xmlStrndup(content, len); + if (cur->content == NULL) { + xmlFree(cur); + return(NULL); + } } if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) @@ -2742,8 +2671,10 @@ xmlNewCDataBlock(xmlDocPtr doc, const xmlChar *content, int len) { * @doc: the document * @content: the comment content * - * Creation of a new node containing a comment within a document. - * Returns a pointer to the new node object. + * Create a comment node. + * + * Returns a pointer to the new node object or NULL if a memory + * allocation failed. */ xmlNodePtr xmlNewDocComment(xmlDocPtr doc, const xmlChar *content) { @@ -2754,164 +2685,293 @@ xmlNewDocComment(xmlDocPtr doc, const xmlChar *content) { return(cur); } -static const xmlChar *_copyStringForNewDictIfNeeded(xmlDictPtr oldDict, xmlDictPtr newDict, const xmlChar *oldValue) { - const xmlChar *newValue = oldValue; - if (oldValue) { - int oldDictOwnsOldValue = oldDict && (xmlDictOwns(oldDict, oldValue) == 1); - if (oldDictOwnsOldValue) { +static void +xmlRemoveEntity(xmlEntityPtr ent) { + xmlDocPtr doc = ent->doc; + xmlDtdPtr intSubset, extSubset; + + if (doc == NULL) + return; + intSubset = doc->intSubset; + extSubset = doc->extSubset; + + if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) || + (ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) || + (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY)) { + if (intSubset != NULL) { + if (xmlHashLookup(intSubset->entities, ent->name) == ent) + xmlHashRemoveEntry(intSubset->entities, ent->name, NULL); + } + if (extSubset != NULL) { + if (xmlHashLookup(extSubset->entities, ent->name) == ent) + xmlHashRemoveEntry(extSubset->entities, ent->name, NULL); + } + } else if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) || + (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) { + if (intSubset != NULL) { + if (xmlHashLookup(intSubset->pentities, ent->name) == ent) + xmlHashRemoveEntry(intSubset->entities, ent->name, NULL); + } + if (extSubset != NULL) { + if (xmlHashLookup(extSubset->pentities, ent->name) == ent) + xmlHashRemoveEntry(extSubset->entities, ent->name, NULL); + } + } +} + +static int +xmlNodeSetDoc(xmlNodePtr node, xmlDocPtr doc) { + xmlDocPtr oldDoc; + xmlDictPtr oldDict, newDict; + int ret = 0; + + /* + * Remove name and content from old dictionary + */ + + oldDoc = node->doc; + oldDict = oldDoc ? oldDoc->dict : NULL; + newDict = doc ? doc->dict : NULL; + + if ((oldDict != NULL) && (oldDict != newDict)) { + if ((node->name != NULL) && + ((node->type == XML_ELEMENT_NODE) || + (node->type == XML_ATTRIBUTE_NODE) || + (node->type == XML_PI_NODE) || + (node->type == XML_ENTITY_REF_NODE)) && + (xmlDictOwns(oldDict, node->name))) { if (newDict) - newValue = xmlDictLookup(newDict, oldValue, -1); + node->name = xmlDictLookup(newDict, node->name, -1); else - newValue = xmlStrdup(oldValue); + node->name = xmlStrdup(node->name); + if (node->name == NULL) + ret = -1; + } + + if ((node->content != NULL) && + ((node->type == XML_TEXT_NODE) || + (node->type == XML_CDATA_SECTION_NODE)) && + (xmlDictOwns(oldDict, node->content))) { + node->content = xmlStrdup(node->content); + if (node->content == NULL) + ret = -1; + } + } + + switch (node->type) { + case XML_ATTRIBUTE_NODE: { + xmlAttrPtr attr = (xmlAttrPtr) node; + + /* + * Handle IDs + * + * TODO: ID attributes should also be added to the new + * document, but it's not clear how to handle clashes. + */ + if (attr->atype == XML_ATTRIBUTE_ID) + xmlRemoveID(oldDoc, attr); + + break; } + + case XML_ENTITY_REF_NODE: + /* + * Handle entity references + */ + node->children = NULL; + node->last = NULL; + node->content = NULL; + + if ((doc != NULL) && + ((doc->intSubset != NULL) || (doc->extSubset != NULL))) { + xmlEntityPtr ent; + + /* + * Assign new entity node if available + */ + ent = xmlGetDocEntity(doc, node->name); + if (ent != NULL) { + node->children = (xmlNodePtr) ent; + node->last = (xmlNodePtr) ent; + node->content = ent->content; + } + } + + break; + + case XML_DTD_NODE: + if (oldDoc != NULL) { + if (oldDoc->intSubset == (xmlDtdPtr) node) + oldDoc->intSubset = NULL; + if (oldDoc->extSubset == (xmlDtdPtr) node) + oldDoc->extSubset = NULL; + } + + break; + + case XML_ENTITY_DECL: + xmlRemoveEntity((xmlEntityPtr) node); + break; + + /* + * TODO: + * - Remove element decls from doc->elements + * - Remove attribtue decls form doc->attributes + */ + + default: + break; } - return newValue; + + /* + * Set new document + */ + node->doc = doc; + + return(ret); } /** * xmlSetTreeDoc: - * @tree: the top element - * @doc: the document + * @tree: root of a subtree + * @doc: new document * - * update all nodes under the tree to point to the right document + * This is an internal function which shouldn't be used. It is + * invoked by functions like xmlAddChild, xmlAddSibling or + * xmlReplaceNode. @tree must be the root node of an unlinked + * subtree. + * + * Associate all nodes in a tree with a new document. + * + * Also copy strings from the old document's dictionary and + * remove ID attributes from the old ID table. + * + * Returns 0 on success. If a memory allocation fails, returns -1. + * The whole tree will be updated on failure but some strings + * may be lost. */ -void +int xmlSetTreeDoc(xmlNodePtr tree, xmlDocPtr doc) { - xmlAttrPtr prop; + int ret = 0; if ((tree == NULL) || (tree->type == XML_NAMESPACE_DECL)) - return; - if (tree->doc != doc) { - xmlDictPtr oldTreeDict = tree->doc ? tree->doc->dict : NULL; - xmlDictPtr newDict = doc ? doc->dict : NULL; - - if(tree->type == XML_ELEMENT_NODE) { - prop = tree->properties; - while (prop != NULL) { - if (prop->atype == XML_ATTRIBUTE_ID) { - xmlRemoveID(tree->doc, prop); - } + return(0); + if (tree->doc == doc) + return(0); - if (prop->doc != doc) { - xmlDictPtr oldPropDict = prop->doc ? prop->doc->dict : NULL; - prop->name = _copyStringForNewDictIfNeeded(oldPropDict, newDict, prop->name); - prop->doc = doc; - } - xmlSetListDoc(prop->children, doc); + if (tree->type == XML_ELEMENT_NODE) { + xmlAttrPtr prop = tree->properties; - /* - * TODO: ID attributes should be also added to the new - * document, but this breaks things like xmlReplaceNode. - * The underlying problem is that xmlRemoveID is only called - * if a node is destroyed, not if it's unlinked. - */ -#if 0 - if (xmlIsID(doc, tree, prop)) { - xmlChar *idVal = xmlNodeListGetString(doc, prop->children, - 1); - xmlAddID(NULL, doc, idVal, prop); - } -#endif + while (prop != NULL) { + if (prop->children != NULL) { + if (xmlSetListDoc(prop->children, doc) < 0) + ret = -1; + } - prop = prop->next; - } - } - if (tree->type == XML_ENTITY_REF_NODE) { - /* - * Clear 'children' which points to the entity declaration - * from the original document. - */ - tree->children = NULL; - } else if (tree->children != NULL) { - xmlSetListDoc(tree->children, doc); + if (xmlNodeSetDoc((xmlNodePtr) prop, doc) < 0) + ret = -1; + + prop = prop->next; } + } - tree->name = _copyStringForNewDictIfNeeded(oldTreeDict, newDict, tree->name); - tree->content = (xmlChar *)_copyStringForNewDictIfNeeded(oldTreeDict, NULL, tree->content); - /* FIXME: tree->ns should be updated as in xmlStaticCopyNode(). */ - tree->doc = doc; + if ((tree->children != NULL) && + (tree->type != XML_ENTITY_REF_NODE)) { + if (xmlSetListDoc(tree->children, doc) < 0) + ret = -1; } + + if (xmlNodeSetDoc(tree, doc) < 0) + ret = -1; + + return(ret); } /** * xmlSetListDoc: - * @list: the first element - * @doc: the document + * @list: a node list + * @doc: new document * - * update all nodes in the list to point to the right document + * Associate all subtrees in @list with a new document. + * + * Internal function, see xmlSetTreeDoc. + * + * Returns 0 on success. If a memory allocation fails, returns -1. + * All subtrees will be updated on failure but some strings + * may be lost. */ -void +int xmlSetListDoc(xmlNodePtr list, xmlDocPtr doc) { xmlNodePtr cur; + int ret = 0; if ((list == NULL) || (list->type == XML_NAMESPACE_DECL)) - return; + return(0); + cur = list; while (cur != NULL) { - if (cur->doc != doc) - xmlSetTreeDoc(cur, doc); + if (cur->doc != doc) { + if (xmlSetTreeDoc(cur, doc) < 0) + ret = -1; + } cur = cur->next; } + + return(ret); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) /** * xmlNewChild: * @parent: the parent node - * @ns: a namespace if any + * @ns: a namespace (optional) * @name: the name of the child - * @content: the XML content of the child if any. + * @content: text content with XML references (optional) + * + * Create a new child element and append it to a parent element. * - * Creation of a new child element, added at the end of @parent children list. - * @ns and @content parameters are optional (NULL). If @ns is NULL, the newly - * created element inherits the namespace of @parent. If @content is non NULL, - * a child list containing the TEXTs and ENTITY_REFs node will be created. - * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity - * references. XML special chars must be escaped first by using - * xmlEncodeEntitiesReentrant(), or xmlNewTextChild() should be used. + * If @ns is NULL, the newly created element inherits the namespace + * of the parent. * - * Returns a pointer to the new node object. + * If provided, @content is expected to be a valid XML attribute + * value possibly containing character and entity references. Text + * and entity reference node will be added to the child element, + * see xmlNewDocNode. + * + * Returns a pointer to the new node object or NULL if arguments + * are invalid or a memory allocation failed. */ xmlNodePtr xmlNewChild(xmlNodePtr parent, xmlNsPtr ns, const xmlChar *name, const xmlChar *content) { xmlNodePtr cur, prev; - if (parent == NULL) { + if ((parent == NULL) || (name == NULL)) return(NULL); - } - if (name == NULL) { - return(NULL); - } + switch (parent->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_DOCUMENT_FRAG_NODE: + break; - /* - * Allocate a new node - */ - if (parent->type == XML_ELEMENT_NODE) { - if (ns == NULL) - cur = xmlNewDocNode(parent->doc, parent->ns, name, content); - else - cur = xmlNewDocNode(parent->doc, ns, name, content); - } else if ((parent->type == XML_DOCUMENT_NODE) || - (parent->type == XML_HTML_DOCUMENT_NODE)) { - if (ns == NULL) - cur = xmlNewDocNode((xmlDocPtr) parent, NULL, name, content); - else - cur = xmlNewDocNode((xmlDocPtr) parent, ns, name, content); - } else if (parent->type == XML_DOCUMENT_FRAG_NODE) { - cur = xmlNewDocNode( parent->doc, ns, name, content); - } else { - return(NULL); + case XML_ELEMENT_NODE: + if (ns == NULL) + ns = parent->ns; + break; + + default: + return(NULL); } - if (cur == NULL) return(NULL); + + cur = xmlNewDocNode(parent->doc, ns, name, content); + if (cur == NULL) + return(NULL); /* * add the new element at the end of the children list. */ - cur->type = XML_ELEMENT_NODE; cur->parent = parent; - cur->doc = parent->doc; if (parent->children == NULL) { parent->children = cur; parent->last = cur; @@ -2926,261 +2986,274 @@ xmlNewChild(xmlNodePtr parent, xmlNsPtr ns, } #endif /* LIBXML_TREE_ENABLED */ -/** - * xmlAddPropSibling: - * @prev: the attribute to which @prop is added after - * @cur: the base attribute passed to calling function - * @prop: the new attribute - * - * Add a new attribute after @prev using @cur as base attribute. - * When inserting before @cur, @prev is passed as @cur->prev. - * When inserting after @cur, @prev is passed as @cur. - * If an existing attribute is found it is destroyed prior to adding @prop. - * - * See the note regarding namespaces in xmlAddChild. - * - * Returns the attribute being inserted or NULL in case of error. - */ +static void +xmlTextSetContent(xmlNodePtr text, xmlChar *content) { + if ((text->content != NULL) && + (text->content != (xmlChar *) &text->properties)) { + xmlDocPtr doc = text->doc; + + if ((doc == NULL) || + (doc->dict == NULL) || + (!xmlDictOwns(doc->dict, text->content))) + xmlFree(text->content); + } + + text->content = content; + text->properties = NULL; +} + +static int +xmlTextAddContent(xmlNodePtr text, const xmlChar *content, int len) { + xmlChar *merged; + + if (content == NULL) + return(0); + + merged = xmlStrncatNew(text->content, content, len); + if (merged == NULL) + return(-1); + + xmlTextSetContent(text, merged); + return(0); +} + static xmlNodePtr -xmlAddPropSibling(xmlNodePtr prev, xmlNodePtr cur, xmlNodePtr prop) { - xmlAttrPtr attr; +xmlInsertProp(xmlDocPtr doc, xmlNodePtr cur, xmlNodePtr parent, + xmlNodePtr prev, xmlNodePtr next) { + xmlAttrPtr attr; - if ((cur == NULL) || (cur->type != XML_ATTRIBUTE_NODE) || - (prop == NULL) || (prop->type != XML_ATTRIBUTE_NODE) || - ((prev != NULL) && (prev->type != XML_ATTRIBUTE_NODE))) - return(NULL); + if (((prev != NULL) && (prev->type != XML_ATTRIBUTE_NODE)) || + ((next != NULL) && (next->type != XML_ATTRIBUTE_NODE))) + return(NULL); - /* check if an attribute with the same name exists */ - if (prop->ns == NULL) - attr = xmlHasNsProp(cur->parent, prop->name, NULL); - else - attr = xmlHasNsProp(cur->parent, prop->name, prop->ns->href); + /* check if an attribute with the same name exists */ + attr = xmlGetPropNodeInternal(parent, cur->name, + cur->ns ? cur->ns->href : NULL, 0); - if (prop->doc != cur->doc) { - xmlSetTreeDoc(prop, cur->doc); - } - prop->parent = cur->parent; - prop->prev = prev; - if (prev != NULL) { - prop->next = prev->next; - prev->next = prop; - if (prop->next) - prop->next->prev = prop; - } else { - prop->next = cur; - cur->prev = prop; + xmlUnlinkNodeInternal(cur); + + if (cur->doc != doc) { + if (xmlSetTreeDoc(cur, doc) < 0) + return(NULL); + } + + cur->parent = parent; + cur->prev = prev; + cur->next = next; + + if (prev == NULL) { + if (parent != NULL) + parent->properties = (xmlAttrPtr) cur; + } else { + prev->next = cur; + } + if (next != NULL) { + next->prev = cur; + } + + if ((attr != NULL) && (attr != (xmlAttrPtr) cur)) { + /* different instance, destroy it (attributes must be unique) */ + xmlRemoveProp((xmlAttrPtr) attr); + } + + return cur; +} + +static xmlNodePtr +xmlInsertNode(xmlDocPtr doc, xmlNodePtr cur, xmlNodePtr parent, + xmlNodePtr prev, xmlNodePtr next, int coalesce) { + xmlNodePtr oldParent; + + if (cur->type == XML_ATTRIBUTE_NODE) + return xmlInsertProp(doc, cur, parent, prev, next); + + /* + * Coalesce text nodes + */ + if ((coalesce) && (cur->type == XML_TEXT_NODE)) { + if ((prev != NULL) && (prev->type == XML_TEXT_NODE) && + (prev->name == cur->name)) { + if (xmlTextAddContent(prev, cur->content, -1) < 0) + return(NULL); + xmlUnlinkNodeInternal(cur); + xmlFreeNode(cur); + return(prev); } - if (prop->prev == NULL && prop->parent != NULL) - prop->parent->properties = (xmlAttrPtr) prop; - if ((attr != NULL) && (attr->type != XML_ATTRIBUTE_DECL)) { - /* different instance, destroy it (attributes must be unique) */ - xmlRemoveProp((xmlAttrPtr) attr); + + if ((next != NULL) && (next->type == XML_TEXT_NODE) && + (next->name == cur->name)) { + if (cur->content != NULL) { + xmlChar *merged; + + merged = xmlStrncatNew(cur->content, next->content, -1); + if (merged == NULL) + return(NULL); + xmlTextSetContent(next, merged); + } + + xmlUnlinkNodeInternal(cur); + xmlFreeNode(cur); + return(next); } - return prop; + } + + /* Unlink */ + oldParent = cur->parent; + if (oldParent != NULL) { + if (oldParent->children == cur) + oldParent->children = cur->next; + if (oldParent->last == cur) + oldParent->last = cur->prev; + } + if (cur->next != NULL) + cur->next->prev = cur->prev; + if (cur->prev != NULL) + cur->prev->next = cur->next; + + if (cur->doc != doc) { + if (xmlSetTreeDoc(cur, doc) < 0) { + /* + * We shouldn't make any modifications to the inserted + * tree if a memory allocation fails, but that's hard to + * implement. The tree has been moved to the target + * document now but some contents are corrupted. + * Unlinking is the best we can do. + */ + cur->parent = NULL; + cur->prev = NULL; + cur->next = NULL; + return(NULL); + } + } + + cur->parent = parent; + cur->prev = prev; + cur->next = next; + + if (prev == NULL) { + if (parent != NULL) + parent->children = cur; + } else { + prev->next = cur; + } + if (next == NULL) { + if (parent != NULL) + parent->last = cur; + } else { + next->prev = cur; + } + + return(cur); } /** * xmlAddNextSibling: - * @cur: the child node - * @elem: the new node + * @prev: the target node + * @cur: the new node + * + * Unlinks @cur and inserts it as next sibling after @prev. * - * Add a new node @elem as the next sibling of @cur - * If the new node was already inserted in a document it is - * first unlinked from its existing context. - * As a result of text merging @elem may be freed. - * If the new node is ATTRIBUTE, it is added into properties instead of children. - * If there is an attribute with equal name, it is first destroyed. + * Unlike xmlAddChild this function does not merge text nodes. * - * See the note regarding namespaces in xmlAddChild. + * If @cur is an attribute node, it is inserted after attribute + * @prev. If the attribute list contains an attribute with a name + * matching @cur, the old attribute is destroyed. * - * Returns the new node or NULL in case of error. + * See the notes in xmlAddChild. + * + * Returns @cur or a sibling if @cur was merged. Returns NULL + * if arguments are invalid or a memory allocation failed. */ xmlNodePtr -xmlAddNextSibling(xmlNodePtr cur, xmlNodePtr elem) { - if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) { +xmlAddNextSibling(xmlNodePtr prev, xmlNodePtr cur) { + if ((prev == NULL) || (prev->type == XML_NAMESPACE_DECL) || + (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) || + (cur == prev)) return(NULL); - } - - if (cur == elem) { - return(NULL); - } - xmlUnlinkNode(elem); + if (cur == prev->next) + return(cur); - if (elem->type == XML_TEXT_NODE) { - if (cur->type == XML_TEXT_NODE) { - xmlNodeAddContent(cur, elem->content); - xmlFreeNode(elem); - return(cur); - } - if ((cur->next != NULL) && (cur->next->type == XML_TEXT_NODE) && - (cur->name == cur->next->name)) { - xmlChar *tmp; - - tmp = xmlStrdup(elem->content); - tmp = xmlStrcat(tmp, cur->next->content); - xmlNodeSetContent(cur->next, tmp); - xmlFree(tmp); - xmlFreeNode(elem); - return(cur->next); - } - } else if (elem->type == XML_ATTRIBUTE_NODE) { - return xmlAddPropSibling(cur, cur, elem); - } - - if (elem->doc != cur->doc) { - xmlSetTreeDoc(elem, cur->doc); - } - elem->parent = cur->parent; - elem->prev = cur; - elem->next = cur->next; - cur->next = elem; - if (elem->next != NULL) - elem->next->prev = elem; - if ((elem->parent != NULL) && (elem->parent->last == cur)) - elem->parent->last = elem; - return(elem); + return(xmlInsertNode(prev->doc, cur, prev->parent, prev, prev->next, 0)); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_HTML_ENABLED) || \ defined(LIBXML_SCHEMAS_ENABLED) || defined(LIBXML_XINCLUDE_ENABLED) /** * xmlAddPrevSibling: - * @cur: the child node - * @elem: the new node + * @next: the target node + * @cur: the new node + * + * Unlinks @cur and inserts it as previous sibling before @next. + * + * Unlike xmlAddChild this function does not merge text nodes. * - * Add a new node @elem as the previous sibling of @cur - * merging adjacent TEXT nodes (@elem may be freed) - * If the new node was already inserted in a document it is - * first unlinked from its existing context. - * If the new node is ATTRIBUTE, it is added into properties instead of children. - * If there is an attribute with equal name, it is first destroyed. + * If @cur is an attribute node, it is inserted before attribute + * @next. If the attribute list contains an attribute with a name + * matching @cur, the old attribute is destroyed. * - * See the note regarding namespaces in xmlAddChild. + * See the notes in xmlAddChild. * - * Returns the new node or NULL in case of error. + * Returns @cur or a sibling if @cur was merged. Returns NULL + * if arguments are invalid or a memory allocation failed. */ xmlNodePtr -xmlAddPrevSibling(xmlNodePtr cur, xmlNodePtr elem) { - if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - - if (cur == elem) { +xmlAddPrevSibling(xmlNodePtr next, xmlNodePtr cur) { + if ((next == NULL) || (next->type == XML_NAMESPACE_DECL) || + (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) || + (cur == next)) return(NULL); - } - - xmlUnlinkNode(elem); - if (elem->type == XML_TEXT_NODE) { - if (cur->type == XML_TEXT_NODE) { - xmlChar *tmp; + if (cur == next->prev) + return(cur); - tmp = xmlStrdup(elem->content); - tmp = xmlStrcat(tmp, cur->content); - xmlNodeSetContent(cur, tmp); - xmlFree(tmp); - xmlFreeNode(elem); - return(cur); - } - if ((cur->prev != NULL) && (cur->prev->type == XML_TEXT_NODE) && - (cur->name == cur->prev->name)) { - xmlNodeAddContent(cur->prev, elem->content); - xmlFreeNode(elem); - return(cur->prev); - } - } else if (elem->type == XML_ATTRIBUTE_NODE) { - return xmlAddPropSibling(cur->prev, cur, elem); - } - - if (elem->doc != cur->doc) { - xmlSetTreeDoc(elem, cur->doc); - } - elem->parent = cur->parent; - elem->next = cur; - elem->prev = cur->prev; - cur->prev = elem; - if (elem->prev != NULL) - elem->prev->next = elem; - if ((elem->parent != NULL) && (elem->parent->children == cur)) { - elem->parent->children = elem; - } - return(elem); + return(xmlInsertNode(next->doc, cur, next->parent, next->prev, next, 0)); } #endif /* LIBXML_TREE_ENABLED */ /** * xmlAddSibling: - * @cur: the child node - * @elem: the new node + * @node: the target node + * @cur: the new node + * + * Unlinks @cur and inserts it as last sibling of @node. * - * Add a new element @elem to the list of siblings of @cur - * merging adjacent TEXT nodes (@elem may be freed) - * If the new element was already inserted in a document it is - * first unlinked from its existing context. + * If @cur is a text node, it may be merged with an adjacent text + * node and freed. In this case the text node containing the merged + * content is returned. * - * See the note regarding namespaces in xmlAddChild. + * If @cur is an attribute node, it is appended to the attribute + * list containing @node. If the attribute list contains an attribute + * with a name matching @cur, the old attribute is destroyed. * - * Returns the new element or NULL in case of error. + * See the notes in xmlAddChild. + * + * Returns @cur or a sibling if @cur was merged. Returns NULL + * if arguments are invalid or a memory allocation failed. */ xmlNodePtr -xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) { - xmlNodePtr parent; - - if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - - if ((elem == NULL) || (elem->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - - if (cur == elem) { +xmlAddSibling(xmlNodePtr node, xmlNodePtr cur) { + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || + (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) || + (cur == node)) return(NULL); - } /* * Constant time is we can rely on the ->parent->last to find * the last sibling. */ - if ((cur->type != XML_ATTRIBUTE_NODE) && (cur->parent != NULL) && - (cur->parent->children != NULL) && - (cur->parent->last != NULL) && - (cur->parent->last->next == NULL)) { - cur = cur->parent->last; + if ((node->type != XML_ATTRIBUTE_NODE) && (node->parent != NULL)) { + if (node->parent->last != NULL) + node = node->parent->last; } else { - while (cur->next != NULL) cur = cur->next; - } - - xmlUnlinkNode(elem); - - if ((cur->type == XML_TEXT_NODE) && (elem->type == XML_TEXT_NODE) && - (cur->name == elem->name)) { - xmlNodeAddContent(cur, elem->content); - xmlFreeNode(elem); - return(cur); - } else if (elem->type == XML_ATTRIBUTE_NODE) { - return xmlAddPropSibling(cur, cur, elem); + while (node->next != NULL) + node = node->next; } - if (elem->doc != cur->doc) { - xmlSetTreeDoc(elem, cur->doc); - } - parent = cur->parent; - elem->prev = cur; - elem->next = NULL; - elem->parent = parent; - cur->next = elem; - if (parent != NULL) - parent->last = elem; + if (cur == node) + return(cur); - return(elem); + return(xmlInsertNode(node->doc, cur, node->parent, node, NULL, 1)); } /** @@ -3188,16 +3261,17 @@ xmlAddSibling(xmlNodePtr cur, xmlNodePtr elem) { * @parent: the parent node * @cur: the first node in the list * - * Add a list of node at the end of the child list of the parent - * merging adjacent TEXT nodes (@cur may be freed) + * Append a node list to another node. * - * See the note regarding namespaces in xmlAddChild. + * See xmlAddChild. * * Returns the last child or NULL in case of error. */ xmlNodePtr xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) { + xmlNodePtr iter; xmlNodePtr prev; + int oom; if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) { return(NULL); @@ -3207,9 +3281,15 @@ xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) { return(NULL); } - if ((cur->doc != NULL) && (parent->doc != NULL) && - (cur->doc != parent->doc)) { + oom = 0; + for (iter = cur; iter != NULL; iter = iter->next) { + if (iter->doc != parent->doc) { + if (xmlSetTreeDoc(iter, parent->doc) < 0) + oom = 1; + } } + if (oom) + return(NULL); /* * add the first element at the end of the children list. @@ -3218,40 +3298,36 @@ xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) { if (parent->children == NULL) { parent->children = cur; } else { + prev = parent->last; + /* * If cur and parent->last both are TEXT nodes, then merge them. */ if ((cur->type == XML_TEXT_NODE) && - (parent->last->type == XML_TEXT_NODE) && - (cur->name == parent->last->name)) { - xmlNodeAddContent(parent->last, cur->content); + (prev->type == XML_TEXT_NODE) && + (cur->name == prev->name)) { + xmlNodePtr next; + + if (xmlTextAddContent(prev, cur->content, -1) < 0) + return(NULL); + next = cur->next; + xmlFreeNode(cur); /* * if it's the only child, nothing more to be done. */ - if (cur->next == NULL) { - xmlFreeNode(cur); - return(parent->last); - } - prev = cur; - cur = cur->next; - xmlFreeNode(prev); + if (next == NULL) + return(prev); + cur = next; } - prev = parent->last; + prev->next = cur; cur->prev = prev; } while (cur->next != NULL) { cur->parent = parent; - if (cur->doc != parent->doc) { - xmlSetTreeDoc(cur, parent->doc); - } cur = cur->next; } cur->parent = parent; - /* the parent may not be linked to a doc ! */ - if (cur->doc != parent->doc) { - xmlSetTreeDoc(cur, parent->doc); - } parent->last = cur; return(cur); @@ -3262,130 +3338,87 @@ xmlAddChildList(xmlNodePtr parent, xmlNodePtr cur) { * @parent: the parent node * @cur: the child node * - * Add a new node to @parent, at the end of the child (or property) list - * merging adjacent TEXT nodes (in which case @cur is freed) - * If the new node is ATTRIBUTE, it is added into properties instead of children. - * If there is an attribute with equal name, it is first destroyed. + * Unlink @cur and append it to the children of @parent. + * + * If @cur is a text node, it may be merged with an adjacent text + * node and freed. In this case the text node containing the merged + * content is returned. * - * All tree manipulation functions can safely move nodes within a document. - * But when moving nodes from one document to another, references to - * namespaces in element or attribute nodes are NOT fixed. In this case, - * you MUST call xmlReconciliateNs after the move operation to avoid - * memory errors. + * If @cur is an attribute node, it is appended to the attributes of + * @parent. If the attribute list contains an attribute with a name + * matching @elem, the old attribute is destroyed. * - * Returns the child or NULL in case of error. + * General notes: + * + * Move operations like xmlAddChild can cause element or attribute + * nodes to reference namespaces that aren't declared in one of + * their ancestors. This can lead to use-after-free errors if the + * elements containing the declarations are freed later, especially + * when moving nodes from one document to another. You should + * consider calling xmlReconciliateNs after a move operation to + * normalize namespaces. Another option is to call + * xmlDOMWrapAdoptNode with the target parent before moving a node. + * + * For the most part, move operations don't check whether the + * resulting tree structure is valid. Users must make sure that + * parent nodes only receive children of valid types. Inserted + * child nodes must never be an ancestor of the parent node to + * avoid cycles in the tree structure. In general, only + * document, document fragments, elements and attributes + * should be used as parent nodes. + * + * When moving a node between documents and a memory allocation + * fails, the node's content will be corrupted and it will be + * unlinked. In this case, the node must be freed manually. + * + * Moving DTDs between documents isn't supported. + * + * Returns @elem or a sibling if @elem was merged. Returns NULL + * if arguments are invalid or a memory allocation failed. */ xmlNodePtr xmlAddChild(xmlNodePtr parent, xmlNodePtr cur) { xmlNodePtr prev; - if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - - if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) { - return(NULL); - } - - if (parent == cur) { - return(NULL); - } - /* - * If cur is a TEXT node, merge its content with adjacent TEXT nodes - * cur is then freed. - */ - if (cur->type == XML_TEXT_NODE) { - if ((parent->type == XML_TEXT_NODE) && - (parent->content != NULL) && - (parent->name == cur->name)) { - xmlNodeAddContent(parent, cur->content); - xmlFreeNode(cur); - return(parent); - } - if ((parent->last != NULL) && (parent->last->type == XML_TEXT_NODE) && - (parent->last->name == cur->name) && - (parent->last != cur)) { - xmlNodeAddContent(parent->last, cur->content); - xmlFreeNode(cur); - return(parent->last); - } - } + if ((parent == NULL) || (parent->type == XML_NAMESPACE_DECL) || + (cur == NULL) || (cur->type == XML_NAMESPACE_DECL) || + (parent == cur)) + return(NULL); /* - * add the new element at the end of the children list. + * If parent is a text node, call xmlTextAddContent. This + * undocumented quirk should probably be removed. */ - prev = cur->parent; - cur->parent = parent; - if (cur->doc != parent->doc) { - xmlSetTreeDoc(cur, parent->doc); + if (parent->type == XML_TEXT_NODE) { + if (xmlTextAddContent(parent, cur->content, -1) < 0) + return(NULL); + xmlFreeNode(cur); + return(parent); } - /* this check prevents a loop on tree-traversions if a developer - * tries to add a node to its parent multiple times - */ - if (prev == parent) - return(cur); - /* - * Coalescing - */ - if ((parent->type == XML_TEXT_NODE) && - (parent->content != NULL) && - (parent != cur)) { - xmlNodeAddContent(parent, cur->content); - xmlFreeNode(cur); - return(parent); - } if (cur->type == XML_ATTRIBUTE_NODE) { - if (parent->type != XML_ELEMENT_NODE) - return(NULL); - if (parent->properties != NULL) { - /* check if an attribute with the same name exists */ - xmlAttrPtr lastattr; - - if (cur->ns == NULL) - lastattr = xmlHasNsProp(parent, cur->name, NULL); - else - lastattr = xmlHasNsProp(parent, cur->name, cur->ns->href); - if ((lastattr != NULL) && (lastattr != (xmlAttrPtr) cur) && (lastattr->type != XML_ATTRIBUTE_DECL)) { - /* different instance, destroy it (attributes must be unique) */ - xmlUnlinkNode((xmlNodePtr) lastattr); - xmlFreeProp(lastattr); - } - if (lastattr == (xmlAttrPtr) cur) - return(cur); - - } - if (parent->properties == NULL) { - parent->properties = (xmlAttrPtr) cur; - } else { - /* find the end */ - xmlAttrPtr lastattr = parent->properties; - while (lastattr->next != NULL) { - lastattr = lastattr->next; - } - lastattr->next = (xmlAttrPtr) cur; - ((xmlAttrPtr) cur)->prev = lastattr; - } + prev = (xmlNodePtr) parent->properties; + if (prev != NULL) { + while (prev->next != NULL) + prev = prev->next; + } } else { - if (parent->children == NULL) { - parent->children = cur; - parent->last = cur; - } else { - prev = parent->last; - prev->next = cur; - cur->prev = prev; - parent->last = cur; - } + prev = parent->last; } - return(cur); + + if (cur == prev) + return(cur); + + return(xmlInsertNode(parent->doc, cur, parent, prev, NULL, 1)); } /** * xmlGetLastChild: * @parent: the parent node * - * Search the last child of a node. - * Returns the last child or NULL if none. + * Find the last child of a node. + * + * Returns the last child or NULL if parent has no children. */ xmlNodePtr xmlGetLastChild(const xmlNode *parent) { @@ -3404,13 +3437,12 @@ xmlGetLastChild(const xmlNode *parent) { * xmlChildElementCount: * @parent: the parent node * - * Finds the current number of child nodes of that element which are - * element nodes. - * Note the handling of entities references is different than in - * the W3C DOM element traversal spec since we don't have back reference - * from entities content to entities references. + * Count the number of child nodes which are elements. + * + * Note that entity references are not expanded. * - * Returns the count of element child or 0 if not available + * Returns the number of element children or 0 if arguments are + * invalid. */ unsigned long xmlChildElementCount(xmlNodePtr parent) { @@ -3421,10 +3453,10 @@ xmlChildElementCount(xmlNodePtr parent) { return(0); switch (parent->type) { case XML_ELEMENT_NODE: - case XML_ENTITY_NODE: case XML_DOCUMENT_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: + case XML_ENTITY_DECL: cur = parent->children; break; default: @@ -3442,12 +3474,11 @@ xmlChildElementCount(xmlNodePtr parent) { * xmlFirstElementChild: * @parent: the parent node * - * Finds the first child node of that element which is a Element node - * Note the handling of entities references is different than in - * the W3C DOM element traversal spec since we don't have back reference - * from entities content to entities references. + * Find the first child node which is an element. + * + * Note that entity references are not expanded. * - * Returns the first element child or NULL if not available + * Returns the first element or NULL if parent has no children. */ xmlNodePtr xmlFirstElementChild(xmlNodePtr parent) { @@ -3457,10 +3488,10 @@ xmlFirstElementChild(xmlNodePtr parent) { return(NULL); switch (parent->type) { case XML_ELEMENT_NODE: - case XML_ENTITY_NODE: case XML_DOCUMENT_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: + case XML_ENTITY_DECL: cur = parent->children; break; default: @@ -3478,12 +3509,11 @@ xmlFirstElementChild(xmlNodePtr parent) { * xmlLastElementChild: * @parent: the parent node * - * Finds the last child node of that element which is a Element node - * Note the handling of entities references is different than in - * the W3C DOM element traversal spec since we don't have back reference - * from entities content to entities references. + * Find the last child node which is an element. + * + * Note that entity references are not expanded. * - * Returns the last element child or NULL if not available + * Returns the last element or NULL if parent has no children. */ xmlNodePtr xmlLastElementChild(xmlNodePtr parent) { @@ -3493,10 +3523,10 @@ xmlLastElementChild(xmlNodePtr parent) { return(NULL); switch (parent->type) { case XML_ELEMENT_NODE: - case XML_ENTITY_NODE: case XML_DOCUMENT_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_HTML_DOCUMENT_NODE: + case XML_ENTITY_DECL: cur = parent->last; break; default: @@ -3514,13 +3544,11 @@ xmlLastElementChild(xmlNodePtr parent) { * xmlPreviousElementSibling: * @node: the current node * - * Finds the first closest previous sibling of the node which is an - * element node. - * Note the handling of entities references is different than in - * the W3C DOM element traversal spec since we don't have back reference - * from entities content to entities references. + * Find the closest preceding sibling which is a element. + * + * Note that entity references are not expanded. * - * Returns the previous element sibling or NULL if not available + * Returns the sibling or NULL if no sibling was found. */ xmlNodePtr xmlPreviousElementSibling(xmlNodePtr node) { @@ -3531,7 +3559,6 @@ xmlPreviousElementSibling(xmlNodePtr node) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: case XML_XINCLUDE_START: @@ -3553,13 +3580,11 @@ xmlPreviousElementSibling(xmlNodePtr node) { * xmlNextElementSibling: * @node: the current node * - * Finds the first closest next sibling of the node which is an - * element node. - * Note the handling of entities references is different than in - * the W3C DOM element traversal spec since we don't have back reference - * from entities content to entities references. + * Find the closest following sibling which is a element. * - * Returns the next element sibling or NULL if not available + * Note that entity references are not expanded. + * + * Returns the sibling or NULL if no sibling was found. */ xmlNodePtr xmlNextElementSibling(xmlNodePtr node) { @@ -3570,7 +3595,6 @@ xmlNextElementSibling(xmlNodePtr node) { case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: case XML_DTD_NODE: @@ -3595,8 +3619,7 @@ xmlNextElementSibling(xmlNodePtr node) { * xmlFreeNodeList: * @cur: the first node in the list * - * Free a node and all its siblings, this is a recursive behaviour, all - * the children are freed too. + * Free a node list including all children. */ void xmlFreeNodeList(xmlNodePtr cur) { @@ -3626,8 +3649,14 @@ xmlFreeNodeList(xmlNodePtr cur) { if ((cur->type == XML_DOCUMENT_NODE) || (cur->type == XML_HTML_DOCUMENT_NODE)) { xmlFreeDoc((xmlDocPtr) cur); - } else if (cur->type != XML_DTD_NODE) { - + } else if (cur->type == XML_DTD_NODE) { + /* + * TODO: We should consider freeing the DTD if it isn't + * referenced from doc->intSubset or doc->extSubset. + */ + cur->prev = NULL; + cur->next = NULL; + } else { if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue(cur); @@ -3678,8 +3707,10 @@ xmlFreeNodeList(xmlNodePtr cur) { * xmlFreeNode: * @cur: the node * - * Free a node, this is a recursive behaviour, all the children are freed too. - * This doesn't unlink the child from the list, use xmlUnlinkNode() first. + * Free a node including all the children. + * + * This doesn't unlink the node from the tree. Call xmlUnlinkNode first + * unless @cur is a root node. */ void xmlFreeNode(xmlNodePtr cur) { @@ -3700,17 +3731,16 @@ xmlFreeNode(xmlNodePtr cur) { xmlFreeProp((xmlAttrPtr) cur); return; } + if (cur->type == XML_ENTITY_DECL) { + xmlFreeEntity((xmlEntityPtr) cur); + return; + } if ((__xmlRegisterCallbacks) && (xmlDeregisterNodeDefaultValue)) xmlDeregisterNodeDefaultValue(cur); if (cur->doc != NULL) dict = cur->doc->dict; - if (cur->type == XML_ENTITY_DECL) { - xmlEntityPtr ent = (xmlEntityPtr) cur; - DICT_FREE(ent->SystemID); - DICT_FREE(ent->ExternalID); - } if ((cur->children != NULL) && (cur->type != XML_ENTITY_REF_NODE)) xmlFreeNodeList(cur->children); @@ -3742,54 +3772,16 @@ xmlFreeNode(xmlNodePtr cur) { } /** - * xmlUnlinkNode: + * xmlUnlinkNodeInternal: * @cur: the node * - * Unlink a node from it's current context, the node is not freed - * If one need to free the node, use xmlFreeNode() routine after the - * unlink to discard it. - * Note that namespace nodes can't be unlinked as they do not have - * pointer to their parent. + * Unlink a node from its tree. + * + * This function only unlinks the node from the tree. It doesn't + * clear references to DTD nodes. */ -void -xmlUnlinkNode(xmlNodePtr cur) { - if (cur == NULL) { - return; - } - if (cur->type == XML_NAMESPACE_DECL) - return; - if (cur->type == XML_DTD_NODE) { - xmlDocPtr doc; - doc = cur->doc; - if (doc != NULL) { - if (doc->intSubset == (xmlDtdPtr) cur) - doc->intSubset = NULL; - if (doc->extSubset == (xmlDtdPtr) cur) - doc->extSubset = NULL; - } - } - if (cur->type == XML_ENTITY_DECL) { - xmlDocPtr doc; - doc = cur->doc; - if (doc != NULL) { - if (doc->intSubset != NULL) { - if (xmlHashLookup(doc->intSubset->entities, cur->name) == cur) - xmlHashRemoveEntry(doc->intSubset->entities, cur->name, - NULL); - if (xmlHashLookup(doc->intSubset->pentities, cur->name) == cur) - xmlHashRemoveEntry(doc->intSubset->pentities, cur->name, - NULL); - } - if (doc->extSubset != NULL) { - if (xmlHashLookup(doc->extSubset->entities, cur->name) == cur) - xmlHashRemoveEntry(doc->extSubset->entities, cur->name, - NULL); - if (xmlHashLookup(doc->extSubset->pentities, cur->name) == cur) - xmlHashRemoveEntry(doc->extSubset->pentities, cur->name, - NULL); - } - } - } +static void +xmlUnlinkNodeInternal(xmlNodePtr cur) { if (cur->parent != NULL) { xmlNodePtr parent; parent = cur->parent; @@ -3804,26 +3796,67 @@ xmlUnlinkNode(xmlNodePtr cur) { } cur->parent = NULL; } + if (cur->next != NULL) cur->next->prev = cur->prev; if (cur->prev != NULL) cur->prev->next = cur->next; - cur->next = cur->prev = NULL; + cur->next = NULL; + cur->prev = NULL; +} + +/** + * xmlUnlinkNode: + * @cur: the node + * + * Unlink a node from its tree. + * + * The node is not freed. Unless it is reinserted, it must be managed + * manually and freed eventually by calling xmlFreeNode. + */ +void +xmlUnlinkNode(xmlNodePtr cur) { + if (cur == NULL) + return; + + if (cur->type == XML_NAMESPACE_DECL) + return; + + if (cur->type == XML_DTD_NODE) { + xmlDocPtr doc = cur->doc; + + if (doc != NULL) { + if (doc->intSubset == (xmlDtdPtr) cur) + doc->intSubset = NULL; + if (doc->extSubset == (xmlDtdPtr) cur) + doc->extSubset = NULL; + } + } + + if (cur->type == XML_ENTITY_DECL) + xmlRemoveEntity((xmlEntityPtr) cur); + + xmlUnlinkNodeInternal(cur); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_WRITER_ENABLED) /** * xmlReplaceNode: * @old: the old node - * @cur: the node + * @cur: the node (optional) + * + * Unlink the old node. If @cur is provided, it is unlinked and + * inserted in place of @old. + * + * It is an error if @old has no parent. * - * Unlink the old node from its current context, prune the new one - * at the same place. If @cur was already inserted in a document it is - * first unlinked from its existing context. + * Unlike xmlAddChild, this function doesn't merge text nodes or + * delete duplicate attributes. * - * See the note regarding namespaces in xmlAddChild. + * See the notes in xmlAddChild. * - * Returns the @old node + * Returns @old or NULL if arguments are invalid or a memory + * allocation failed. */ xmlNodePtr xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) { @@ -3833,20 +3866,19 @@ xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) { return(NULL); } if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) { + /* Don't call xmlUnlinkNodeInternal to handle DTDs. */ xmlUnlinkNode(old); return(old); } - if (cur == old) { - return(old); - } if ((old->type==XML_ATTRIBUTE_NODE) && (cur->type!=XML_ATTRIBUTE_NODE)) { return(old); } if ((cur->type==XML_ATTRIBUTE_NODE) && (old->type!=XML_ATTRIBUTE_NODE)) { return(old); } - xmlUnlinkNode(cur); - xmlSetTreeDoc(cur, old->doc); + xmlUnlinkNodeInternal(cur); + if (xmlSetTreeDoc(cur, old->doc) < 0) + return(NULL); cur->parent = old->parent; cur->next = old->next; if (cur->next != NULL) @@ -3881,9 +3913,10 @@ xmlReplaceNode(xmlNodePtr old, xmlNodePtr cur) { * xmlCopyNamespace: * @cur: the namespace * - * Do a copy of the namespace. + * Copy a namespace. * - * Returns: a new #xmlNsPtr, or NULL in case of error. + * Returns the copied namespace or NULL if a memory allocation + * failed. */ xmlNsPtr xmlCopyNamespace(xmlNsPtr cur) { @@ -3904,9 +3937,10 @@ xmlCopyNamespace(xmlNsPtr cur) { * xmlCopyNamespaceList: * @cur: the first namespace * - * Do a copy of an namespace list. + * Copy a namespace list. * - * Returns: a new #xmlNsPtr, or NULL in case of error. + * Returns the head of the copied list or NULL if a memory + * allocation failed. */ xmlNsPtr xmlCopyNamespaceList(xmlNsPtr cur) { @@ -3932,7 +3966,7 @@ xmlCopyNamespaceList(xmlNsPtr cur) { static xmlAttrPtr xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { - xmlAttrPtr ret; + xmlAttrPtr ret = NULL; if (cur == NULL) return(NULL); if ((target != NULL) && (target->type != XML_ELEMENT_NODE)) @@ -3952,15 +3986,20 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { if ((cur->ns != NULL) && (target != NULL)) { xmlNsPtr ns; + int res; - ns = xmlSearchNs(target->doc, target, cur->ns->prefix); + res = xmlSearchNsSafe(target, cur->ns->prefix, &ns); + if (res < 0) + goto error; if (ns == NULL) { /* * Humm, we are copying an element whose namespace is defined * out of the new tree scope. Search it in the original tree * and add it at the top of the new tree */ - ns = xmlSearchNs(cur->doc, cur->parent, cur->ns->prefix); + res = xmlSearchNsSafe(cur->parent, cur->ns->prefix, &ns); + if (res < 0) + goto error; if (ns != NULL) { xmlNodePtr root = target; xmlNodePtr pred = NULL; @@ -3974,6 +4013,8 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { root = pred; } ret->ns = xmlNewNs(root, ns->href, ns->prefix); + if (ret->ns == NULL) + goto error; } } else { /* @@ -3989,7 +4030,9 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { * we are in trouble: we need a new reconciled namespace. * This is expensive */ - ret->ns = xmlNewReconciledNs(target->doc, target, cur->ns); + ret->ns = xmlNewReconciledNs(target, cur->ns); + if (ret->ns == NULL) + goto error; } } @@ -4000,6 +4043,8 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { xmlNodePtr tmp; ret->children = xmlStaticCopyNodeList(cur->children, ret->doc, (xmlNodePtr) ret); + if (ret->children == NULL) + goto error; ret->last = NULL; tmp = ret->children; while (tmp != NULL) { @@ -4012,20 +4057,31 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { /* * Try to handle IDs */ - if ((target!= NULL) && (cur!= NULL) && + if ((target != NULL) && (cur != NULL) && (target->doc != NULL) && (cur->doc != NULL) && - (cur->doc->ids != NULL) && (cur->parent != NULL)) { - if (xmlIsID(cur->doc, cur->parent, cur)) { + (cur->parent != NULL) && + (cur->children != NULL)) { + int res = xmlIsID(cur->doc, cur->parent, cur); + + if (res < 0) + goto error; + if (res != 0) { xmlChar *id; - id = xmlNodeListGetString(cur->doc, cur->children, 1); - if (id != NULL) { - xmlAddID(NULL, target->doc, id, ret); - xmlFree(id); - } + id = xmlNodeGetContent((xmlNodePtr) cur); + if (id == NULL) + goto error; + res = xmlAddIDSafe(ret, id); + xmlFree(id); + if (res < 0) + goto error; } } return(ret); + +error: + xmlFreeProp(ret); + return(NULL); } /** @@ -4033,9 +4089,14 @@ xmlCopyPropInternal(xmlDocPtr doc, xmlNodePtr target, xmlAttrPtr cur) { * @target: the element where the attribute will be grafted * @cur: the attribute * - * Do a copy of the attribute. + * Create a copy of the attribute. This function sets the parent + * pointer of the copy to @target but doesn't set the attribute on + * the target element. Users should consider to set the attribute + * by calling xmlAddChild afterwards or reset the parent pointer to + * NULL. * - * Returns: a new #xmlAttrPtr, or NULL in case of error. + * Returns the copied attribute or NULL if a memory allocation + * failed. */ xmlAttrPtr xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) { @@ -4047,9 +4108,12 @@ xmlCopyProp(xmlNodePtr target, xmlAttrPtr cur) { * @target: the element where the attributes will be grafted * @cur: the first attribute * - * Do a copy of an attribute list. + * Create a copy of an attribute list. This function sets the + * parent pointers of the copied attributes to @target but doesn't + * set the attributes on the target element. * - * Returns: a new #xmlAttrPtr, or NULL in case of error. + * Returns the head of the copied list or NULL if a memory + * allocation failed. */ xmlAttrPtr xmlCopyPropList(xmlNodePtr target, xmlAttrPtr cur) { @@ -4095,6 +4159,17 @@ xmlCopyPropList(xmlNodePtr target, xmlAttrPtr cur) { * namespace info, but don't recurse on children. */ +/** + * xmlStaticCopyNode: + * @node: source node + * @doc: target document + * @parent: target parent + * @extended: flags + * + * Copy a node. + * + * Returns the copy or NULL if a memory allocation failed. + */ xmlNodePtr xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, int extended) { @@ -4107,7 +4182,6 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, case XML_ELEMENT_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: case XML_XINCLUDE_START: @@ -4123,12 +4197,7 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, #ifdef LIBXML_TREE_ENABLED return((xmlNodePtr) xmlCopyDoc((xmlDocPtr) node, extended)); #endif /* LIBXML_TREE_ENABLED */ - case XML_DOCUMENT_TYPE_NODE: - case XML_NOTATION_NODE: - case XML_DTD_NODE: - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: + default: return(NULL); } @@ -4136,10 +4205,8 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, * Allocate a new node and fill the fields. */ ret = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (ret == NULL) { - xmlTreeErrMemory("copying node"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlNode)); ret->type = node->type; @@ -4156,6 +4223,8 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, ret->name = xmlDictLookup(doc->dict, node->name, -1); else ret->name = xmlStrdup(node->name); + if (ret->name == NULL) + goto error; } if ((node->type != XML_ELEMENT_NODE) && (node->content != NULL) && @@ -4163,45 +4232,29 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, (node->type != XML_XINCLUDE_END) && (node->type != XML_XINCLUDE_START)) { ret->content = xmlStrdup(node->content); + if (ret->content == NULL) + goto error; }else{ if (node->type == XML_ELEMENT_NODE) ret->line = node->line; } - if (parent != NULL) { - xmlNodePtr tmp; - - /* - * this is a tricky part for the node register thing: - * in case ret does get coalesced in xmlAddChild - * the deregister-node callback is called; so we register ret now already - */ - if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) - xmlRegisterNodeDefaultValue((xmlNodePtr)ret); - - /* - * Note that since ret->parent is already set, xmlAddChild will - * return early and not actually insert the node. It will only - * coalesce text nodes and unnecessarily call xmlSetTreeDoc. - * Assuming that the subtree to be copied always has its text - * nodes coalesced, the somewhat confusing call to xmlAddChild - * could be removed. - */ - tmp = xmlAddChild(parent, ret); - /* node could have coalesced */ - if (tmp != ret) - return(tmp); - } if (!extended) goto out; if (((node->type == XML_ELEMENT_NODE) || - (node->type == XML_XINCLUDE_START)) && (node->nsDef != NULL)) + (node->type == XML_XINCLUDE_START)) && (node->nsDef != NULL)) { ret->nsDef = xmlCopyNamespaceList(node->nsDef); + if (ret->nsDef == NULL) + goto error; + } - if (node->ns != NULL) { - xmlNsPtr ns; + if ((node->type == XML_ELEMENT_NODE) && (node->ns != NULL)) { + xmlNsPtr ns = NULL; + int res; - ns = xmlSearchNs(doc, ret, node->ns->prefix); + res = xmlSearchNsSafe(ret, node->ns->prefix, &ns); + if (res < 0) + goto error; if (ns == NULL) { /* * Humm, we are copying an element whose namespace is defined @@ -4211,15 +4264,19 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, * TODO: Searching the original tree seems unnecessary. We * already have a namespace URI. */ - ns = xmlSearchNs(node->doc, node, node->ns->prefix); + res = xmlSearchNsSafe(node, node->ns->prefix, &ns); + if (res < 0) + goto error; if (ns != NULL) { xmlNodePtr root = ret; while (root->parent != NULL) root = root->parent; ret->ns = xmlNewNs(root, ns->href, ns->prefix); } else { - ret->ns = xmlNewReconciledNs(doc, ret, node->ns); + ret->ns = xmlNewReconciledNs(ret, node->ns); } + if (ret->ns == NULL) + goto error; } else { /* * reference the existing namespace definition in our own tree. @@ -4227,9 +4284,11 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, ret->ns = ns; } } - if (((node->type == XML_ELEMENT_NODE) || - (node->type == XML_XINCLUDE_START)) && (node->properties != NULL)) + if ((node->type == XML_ELEMENT_NODE) && (node->properties != NULL)) { ret->properties = xmlCopyPropList(ret, node->properties); + if (ret->properties == NULL) + goto error; + } if (node->type == XML_ENTITY_REF_NODE) { if ((doc == NULL) || (node->doc != doc)) { /* @@ -4250,10 +4309,8 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, insert = ret; while (cur != NULL) { xmlNodePtr copy = xmlStaticCopyNode(cur, doc, insert, 2); - if (copy == NULL) { - xmlFreeNode(ret); - return(NULL); - } + if (copy == NULL) + goto error; /* Check for coalesced text nodes */ if (insert->last != copy) { @@ -4290,13 +4347,27 @@ xmlStaticCopyNode(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent, } out: - /* if parent != NULL we already registered the node above */ - if ((parent == NULL) && - ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue))) + if ((__xmlRegisterCallbacks) && (xmlRegisterNodeDefaultValue)) xmlRegisterNodeDefaultValue((xmlNodePtr)ret); return(ret); + +error: + xmlFreeNode(ret); + return(NULL); } +/** + * xmlStaticCopyNodeList: + * @node: node to copy + * @doc: target document + * @parent: target node (optional) + * + * Copy a node list. If @parent is provided, sets the parent pointer + * of the copied nodes, but doesn't update the children and last + * pointer of @parent. + * + * Returns a the copy or NULL in case of error. + */ xmlNodePtr xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) { xmlNodePtr ret = NULL; @@ -4305,23 +4376,44 @@ xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) { int linkedSubset = 0; while (node != NULL) { + xmlNodePtr next = node->next; + #ifdef LIBXML_TREE_ENABLED if (node->type == XML_DTD_NODE ) { if (doc == NULL) { - node = node->next; + node = next; continue; } if ((doc->intSubset == NULL) && (newSubset == NULL)) { q = (xmlNodePtr) xmlCopyDtd( (xmlDtdPtr) node ); if (q == NULL) goto error; - q->doc = doc; + /* Can't fail on DTD */ + xmlSetTreeDoc(q, doc); q->parent = parent; newSubset = (xmlDtdPtr) q; - xmlAddChild(parent, q); } else { + /* + * We don't allow multiple internal subsets in a document, + * so we move the DTD instead of creating a copy. + */ linkedSubset = 1; q = (xmlNodePtr) doc->intSubset; - xmlAddChild(parent, q); + /* Unlink */ + if (q->prev == NULL) { + if (q->parent != NULL) + q->parent->children = q->next; + } else { + q->prev->next = q->next; + } + if (q->next == NULL) { + if (q->parent != NULL) + q->parent->last = q->prev; + } else { + q->next->prev = q->prev; + } + q->parent = parent; + q->next = NULL; + q->prev = NULL; } } else #endif /* LIBXML_TREE_ENABLED */ @@ -4336,15 +4428,19 @@ xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) { q->prev = p; p = q; } - node = node->next; + node = next; } if ((doc != NULL) && (newSubset != NULL)) doc->intSubset = newSubset; return(ret); error: - if (linkedSubset != 0) - xmlUnlinkNode((xmlNodePtr) doc->intSubset); xmlFreeNodeList(ret); + if (newSubset != NULL) + xmlFreeDtd(newSubset); + if (linkedSubset != 0) { + doc->intSubset->next = NULL; + doc->intSubset->prev = NULL; + } return(NULL); } @@ -4355,9 +4451,11 @@ xmlStaticCopyNodeList(xmlNodePtr node, xmlDocPtr doc, xmlNodePtr parent) { * when applicable) * if 2 copy properties and namespaces (when applicable) * - * Do a copy of the node. + * Copy a node. * - * Returns: a new #xmlNodePtr, or NULL in case of error. + * Use of this function is DISCOURAGED in favor of xmlDocCopyNode. + * + * Returns the copied node or NULL if a memory allocation failed. */ xmlNodePtr xmlCopyNode(xmlNodePtr node, int extended) { @@ -4375,9 +4473,9 @@ xmlCopyNode(xmlNodePtr node, int extended) { * when applicable) * if 2 copy properties and namespaces (when applicable) * - * Do a copy of the node to a given document. + * Copy a node into another document. * - * Returns: a new #xmlNodePtr, or NULL in case of error. + * Returns the copied node or NULL if a memory allocation failed. */ xmlNodePtr xmlDocCopyNode(xmlNodePtr node, xmlDocPtr doc, int extended) { @@ -4392,9 +4490,10 @@ xmlDocCopyNode(xmlNodePtr node, xmlDocPtr doc, int extended) { * @doc: the target document * @node: the first node in the list. * - * Do a recursive copy of the node list. + * Copy a node list and all children into a new document. * - * Returns: a new #xmlNodePtr, or NULL in case of error. + * Returns the head of the copied list or NULL if a memory + * allocation failed. */ xmlNodePtr xmlDocCopyNodeList(xmlDocPtr doc, xmlNodePtr node) { xmlNodePtr ret = xmlStaticCopyNodeList(node, doc, NULL); @@ -4405,10 +4504,12 @@ xmlNodePtr xmlDocCopyNodeList(xmlDocPtr doc, xmlNodePtr node) { * xmlCopyNodeList: * @node: the first node in the list. * - * Do a recursive copy of the node list. - * Use xmlDocCopyNodeList() if possible to ensure string interning. + * Copy a node list and all children. + * + * Use of this function is DISCOURAGED in favor of xmlDocCopyNodeList. * - * Returns: a new #xmlNodePtr, or NULL in case of error. + * Returns the head of the copied list or NULL if a memory + * allocation failed. */ xmlNodePtr xmlCopyNodeList(xmlNodePtr node) { xmlNodePtr ret = xmlStaticCopyNodeList(node, NULL, NULL); @@ -4418,11 +4519,11 @@ xmlNodePtr xmlCopyNodeList(xmlNodePtr node) { #if defined(LIBXML_TREE_ENABLED) /** * xmlCopyDtd: - * @dtd: the dtd + * @dtd: the DTD * - * Do a copy of the dtd. + * Copy a DTD. * - * Returns: a new #xmlDtdPtr, or NULL in case of error. + * Returns the copied DTD or NULL if a memory allocation failed. */ xmlDtdPtr xmlCopyDtd(xmlDtdPtr dtd) { @@ -4432,21 +4533,36 @@ xmlCopyDtd(xmlDtdPtr dtd) { if (dtd == NULL) return(NULL); ret = xmlNewDtd(NULL, dtd->name, dtd->ExternalID, dtd->SystemID); if (ret == NULL) return(NULL); - if (dtd->entities != NULL) + if (dtd->entities != NULL) { ret->entities = (void *) xmlCopyEntitiesTable( (xmlEntitiesTablePtr) dtd->entities); - if (dtd->notations != NULL) + if (ret->entities == NULL) + goto error; + } + if (dtd->notations != NULL) { ret->notations = (void *) xmlCopyNotationTable( (xmlNotationTablePtr) dtd->notations); - if (dtd->elements != NULL) + if (ret->notations == NULL) + goto error; + } + if (dtd->elements != NULL) { ret->elements = (void *) xmlCopyElementTable( (xmlElementTablePtr) dtd->elements); - if (dtd->attributes != NULL) + if (ret->elements == NULL) + goto error; + } + if (dtd->attributes != NULL) { ret->attributes = (void *) xmlCopyAttributeTable( (xmlAttributeTablePtr) dtd->attributes); - if (dtd->pentities != NULL) + if (ret->attributes == NULL) + goto error; + } + if (dtd->pentities != NULL) { ret->pentities = (void *) xmlCopyEntitiesTable( (xmlEntitiesTablePtr) dtd->pentities); + if (ret->pentities == NULL) + goto error; + } cur = dtd->children; while (cur != NULL) { @@ -4478,6 +4594,8 @@ xmlCopyDtd(xmlDtdPtr dtd) { xmlGetDtdQAttrDesc(ret, tmp->elem, tmp->name, tmp->prefix); } else if (cur->type == XML_COMMENT_NODE) { q = xmlCopyNode(cur, 0); + if (q == NULL) + goto error; } if (q == NULL) { @@ -4499,6 +4617,10 @@ xmlCopyDtd(xmlDtdPtr dtd) { } return(ret); + +error: + xmlFreeDtd(ret); + return(NULL); } #endif @@ -4508,10 +4630,11 @@ xmlCopyDtd(xmlDtdPtr dtd) { * @doc: the document * @recursive: if not zero do a recursive copy. * - * Do a copy of the document info. If recursive, the content tree will + * Copy a document. If recursive, the content tree will * be copied too as well as DTD, namespaces and entities. * - * Returns: a new #xmlDocPtr, or NULL in case of error. + * Returns the copied document or NULL if a memory allocation + * failed. */ xmlDocPtr xmlCopyDoc(xmlDocPtr doc, int recursive) { @@ -4521,12 +4644,21 @@ xmlCopyDoc(xmlDocPtr doc, int recursive) { ret = xmlNewDoc(doc->version); if (ret == NULL) return(NULL); ret->type = doc->type; - if (doc->name != NULL) + if (doc->name != NULL) { ret->name = xmlMemStrdup(doc->name); - if (doc->encoding != NULL) + if (ret->name == NULL) + goto error; + } + if (doc->encoding != NULL) { ret->encoding = xmlStrdup(doc->encoding); - if (doc->URL != NULL) + if (ret->encoding == NULL) + goto error; + } + if (doc->URL != NULL) { ret->URL = xmlStrdup(doc->URL); + if (ret->URL == NULL) + goto error; + } ret->charset = doc->charset; ret->compression = doc->compression; ret->standalone = doc->standalone; @@ -4537,21 +4669,24 @@ xmlCopyDoc(xmlDocPtr doc, int recursive) { #ifdef LIBXML_TREE_ENABLED if (doc->intSubset != NULL) { ret->intSubset = xmlCopyDtd(doc->intSubset); - if (ret->intSubset == NULL) { - xmlFreeDoc(ret); - return(NULL); - } + if (ret->intSubset == NULL) + goto error; + /* Can't fail on DTD */ xmlSetTreeDoc((xmlNodePtr)ret->intSubset, ret); - ret->intSubset->parent = ret; } #endif - if (doc->oldNs != NULL) + if (doc->oldNs != NULL) { ret->oldNs = xmlCopyNamespaceList(doc->oldNs); + if (ret->oldNs == NULL) + goto error; + } if (doc->children != NULL) { xmlNodePtr tmp; ret->children = xmlStaticCopyNodeList(doc->children, ret, (xmlNodePtr)ret); + if (ret->children == NULL) + goto error; ret->last = NULL; tmp = ret->children; while (tmp != NULL) { @@ -4561,6 +4696,10 @@ xmlCopyDoc(xmlDocPtr doc, int recursive) { } } return(ret); + +error: + xmlFreeDoc(ret); + return(NULL); } #endif /* LIBXML_TREE_ENABLED */ @@ -4663,13 +4802,10 @@ xmlGetNodePath(const xmlNode *node) buf_len = 500; buffer = (xmlChar *) xmlMallocAtomic(buf_len); - if (buffer == NULL) { - xmlTreeErrMemory("getting node path"); + if (buffer == NULL) return (NULL); - } buf = (xmlChar *) xmlMallocAtomic(buf_len); if (buf == NULL) { - xmlTreeErrMemory("getting node path"); xmlFree(buffer); return (NULL); } @@ -4856,7 +4992,6 @@ xmlGetNodePath(const xmlNode *node) 2 * buf_len + xmlStrlen(buffer) + sizeof(nametemp) + 20; temp = (xmlChar *) xmlRealloc(buffer, buf_len); if (temp == NULL) { - xmlTreeErrMemory("getting node path"); xmlFree(buf); xmlFree(buffer); return (NULL); @@ -4864,7 +4999,6 @@ xmlGetNodePath(const xmlNode *node) buffer = temp; temp = (xmlChar *) xmlRealloc(buf, buf_len); if (temp == NULL) { - xmlTreeErrMemory("getting node path"); xmlFree(buf); xmlFree(buffer); return (NULL); @@ -4892,7 +5026,7 @@ xmlGetNodePath(const xmlNode *node) * Get the root element of the document (doc->children is a list * containing possibly comments, PIs, etc ...). * - * Returns the #xmlNodePtr for the root or NULL + * Returns the root element or NULL if no element was found. */ xmlNodePtr xmlDocGetRootElement(const xmlDoc *doc) { @@ -4918,7 +5052,10 @@ xmlDocGetRootElement(const xmlDoc *doc) { * Set the root element of the document (doc->children is a list * containing possibly comments, PIs, etc ...). * - * Returns the old root element if any was found, NULL if root was NULL + * @root must be an element node. It is unlinked before insertion. + * + * Returns the unlinked old root element or NULL if the document + * didn't have a root element or a memory allocation failed. */ xmlNodePtr xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root) { @@ -4927,15 +5064,18 @@ xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root) { if (doc == NULL) return(NULL); if ((root == NULL) || (root->type == XML_NAMESPACE_DECL)) return(NULL); - xmlUnlinkNode(root); - xmlSetTreeDoc(root, doc); - root->parent = (xmlNodePtr) doc; old = doc->children; while (old != NULL) { if (old->type == XML_ELEMENT_NODE) break; old = old->next; } + if (old == root) + return(old); + xmlUnlinkNodeInternal(root); + if (xmlSetTreeDoc(root, doc) < 0) + return(NULL); + root->parent = (xmlNodePtr) doc; if (old == NULL) { if (doc->children == NULL) { doc->children = root; @@ -4958,40 +5098,27 @@ xmlDocSetRootElement(xmlDocPtr doc, xmlNodePtr root) { * * Set the language of a node, i.e. the values of the xml:lang * attribute. + * + * Return 0 on success, 1 if arguments are invalid, -1 if a + * memory allocation failed. */ -void +int xmlNodeSetLang(xmlNodePtr cur, const xmlChar *lang) { xmlNsPtr ns; + xmlAttrPtr attr; + int res; - if (cur == NULL) return; - switch(cur->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_DOCUMENT_FRAG_NODE: - case XML_NOTATION_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_DTD_NODE: - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: - case XML_PI_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return; - case XML_ELEMENT_NODE: - case XML_ATTRIBUTE_NODE: - break; - } - ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE); - if (ns == NULL) - return; - xmlSetNsProp(cur, ns, BAD_CAST "lang", lang); + if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) + return(1); + + res = xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns); + if (res != 0) + return(res); + attr = xmlSetNsProp(cur, ns, BAD_CAST "lang", lang); + if (attr == NULL) + return(-1); + + return(0); } #endif /* LIBXML_TREE_ENABLED */ @@ -5008,15 +5135,22 @@ xmlNodeSetLang(xmlNodePtr cur, const xmlChar *lang) { xmlChar * xmlNodeGetLang(const xmlNode *cur) { xmlChar *lang; + int res; if ((cur == NULL) || (cur->type == XML_NAMESPACE_DECL)) return(NULL); + while (cur != NULL) { - lang = xmlGetNsProp(cur, BAD_CAST "lang", XML_XML_NAMESPACE); + res = xmlNodeGetAttrValue(cur, BAD_CAST "lang", XML_XML_NAMESPACE, + &lang); + if (res < 0) + return(NULL); if (lang != NULL) return(lang); + cur = cur->parent; } + return(NULL); } @@ -5029,47 +5163,34 @@ xmlNodeGetLang(const xmlNode *cur) { * * Set (or reset) the space preserving behaviour of a node, i.e. the * value of the xml:space attribute. + * + * Return 0 on success, 1 if arguments are invalid, -1 if a + * memory allocation failed. */ -void +int xmlNodeSetSpacePreserve(xmlNodePtr cur, int val) { xmlNsPtr ns; + xmlAttrPtr attr; + const char *string; + int res; - if (cur == NULL) return; - switch(cur->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_DOCUMENT_FRAG_NODE: - case XML_NOTATION_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_DTD_NODE: - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: - case XML_PI_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return; - case XML_ELEMENT_NODE: - case XML_ATTRIBUTE_NODE: - break; - } - ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE); - if (ns == NULL) - return; - switch (val) { - case 0: - xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "default"); - break; - case 1: - xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST "preserve"); - break; - } + if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) + return(1); + + res = xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns); + if (res != 0) + return(res); + + if (val == 0) + string = "default"; + else + string = "preserve"; + + attr = xmlSetNsProp(cur, ns, BAD_CAST "space", BAD_CAST string); + if (attr == NULL) + return(-1); + + return(0); } #endif /* LIBXML_TREE_ENABLED */ @@ -5086,11 +5207,16 @@ xmlNodeSetSpacePreserve(xmlNodePtr cur, int val) { int xmlNodeGetSpacePreserve(const xmlNode *cur) { xmlChar *space; + int res; if ((cur == NULL) || (cur->type != XML_ELEMENT_NODE)) return(-1); + while (cur != NULL) { - space = xmlGetNsProp(cur, BAD_CAST "space", XML_XML_NAMESPACE); + res = xmlNodeGetAttrValue(cur, BAD_CAST "space", XML_XML_NAMESPACE, + &space); + if (res < 0) + return(-1); if (space != NULL) { if (xmlStrEqual(space, BAD_CAST "preserve")) { xmlFree(space); @@ -5102,8 +5228,10 @@ xmlNodeGetSpacePreserve(const xmlNode *cur) { } xmlFree(space); } + cur = cur->parent; } + return(-1); } @@ -5119,51 +5247,39 @@ void xmlNodeSetName(xmlNodePtr cur, const xmlChar *name) { xmlDocPtr doc; xmlDictPtr dict; - const xmlChar *freeme = NULL; + const xmlChar *copy; + const xmlChar *oldName; if (cur == NULL) return; if (name == NULL) return; switch(cur->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_DOCUMENT_FRAG_NODE: - case XML_NOTATION_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return; case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: case XML_PI_NODE: case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_DTD_NODE: - case XML_DOCUMENT_NODE: - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: break; + default: + return; } + doc = cur->doc; if (doc != NULL) dict = doc->dict; else dict = NULL; - if (dict != NULL) { - if ((cur->name != NULL) && (!xmlDictOwns(dict, cur->name))) - freeme = cur->name; - cur->name = xmlDictLookup(dict, name, -1); - } else { - if (cur->name != NULL) - freeme = cur->name; - cur->name = xmlStrdup(name); - } - if (freeme) - xmlFree((xmlChar *) freeme); + if (dict != NULL) + copy = xmlDictLookup(dict, name, -1); + else + copy = xmlStrdup(name); + if (copy == NULL) + return; + + oldName = cur->name; + cur->name = copy; + if ((oldName != NULL) && + ((dict == NULL) || (!xmlDictOwns(dict, oldName)))) + xmlFree((xmlChar *) oldName); } #endif @@ -5175,31 +5291,17 @@ xmlNodeSetName(xmlNodePtr cur, const xmlChar *name) { * * Set (or reset) the base URI of a node, i.e. the value of the * xml:base attribute. + * + * Returns 0 on success, -1 on error. */ -void +int xmlNodeSetBase(xmlNodePtr cur, const xmlChar* uri) { xmlNsPtr ns; xmlChar* fixed; - if (cur == NULL) return; + if (cur == NULL) + return(-1); switch(cur->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_COMMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_DOCUMENT_FRAG_NODE: - case XML_NOTATION_NODE: - case XML_DTD_NODE: - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: - case XML_PI_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return; case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: break; @@ -5209,31 +5311,40 @@ xmlNodeSetBase(xmlNodePtr cur, const xmlChar* uri) { if (doc->URL != NULL) xmlFree((xmlChar *) doc->URL); - if (uri == NULL) + if (uri == NULL) { doc->URL = NULL; - else + } else { doc->URL = xmlPathToURI(uri); - return; + if (doc->URL == NULL) + return(-1); + } + return(0); } + default: + return(-1); } - ns = xmlSearchNsByHref(cur->doc, cur, XML_XML_NAMESPACE); + xmlSearchNsByHrefSafe(cur, XML_XML_NAMESPACE, &ns); if (ns == NULL) - return; + return(-1); fixed = xmlPathToURI(uri); - if (fixed != NULL) { - xmlSetNsProp(cur, ns, BAD_CAST "base", fixed); - xmlFree(fixed); - } else { - xmlSetNsProp(cur, ns, BAD_CAST "base", uri); + if (fixed == NULL) + return(-1); + if (xmlSetNsProp(cur, ns, BAD_CAST "base", fixed) == NULL) { + xmlFree(fixed); + return(-1); } + xmlFree(fixed); + + return(0); } #endif /* LIBXML_TREE_ENABLED */ /** - * xmlNodeGetBase: + * xmlNodeGetBaseSafe: * @doc: the document the node pertains to * @cur: the node being checked + * @baseOut: pointer to base * * Searches for the BASE URL. The code should work on both XML * and HTML document even if base mechanisms are completely different. @@ -5244,19 +5355,27 @@ xmlNodeSetBase(xmlNodePtr cur, const xmlChar* uri) { * However it does not return the document base (5.1.3), use * doc->URL in this case * - * Returns a pointer to the base URL, or NULL if not found - * It's up to the caller to free the memory with xmlFree(). + * Available since 2.13.0. + * + * Return 0 in case of success, 1 if a URI or argument is invalid, -1 if a + * memory allocation failed. */ -xmlChar * -xmlNodeGetBase(const xmlDoc *doc, const xmlNode *cur) { - xmlChar *oldbase = NULL; +int +xmlNodeGetBaseSafe(const xmlDoc *doc, const xmlNode *cur, xmlChar **baseOut) { + xmlChar *ret = NULL; xmlChar *base, *newbase; + int res; + if (baseOut == NULL) + return(1); + *baseOut = NULL; if ((cur == NULL) && (doc == NULL)) - return(NULL); + return(1); if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL)) - return(NULL); - if (doc == NULL) doc = cur->doc; + return(1); + if (doc == NULL) + doc = cur->doc; + if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) { cur = doc->children; while ((cur != NULL) && (cur->name != NULL)) { @@ -5273,50 +5392,91 @@ xmlNodeGetBase(const xmlDoc *doc, const xmlNode *cur) { continue; } if (!xmlStrcasecmp(cur->name, BAD_CAST "base")) { - return(xmlGetProp(cur, BAD_CAST "href")); + if (xmlNodeGetAttrValue(cur, BAD_CAST "href", NULL, &ret) < 0) + return(-1); + if (ret == NULL) + return(1); + goto found; } cur = cur->next; } - return(NULL); + return(0); } + while (cur != NULL) { if (cur->type == XML_ENTITY_DECL) { xmlEntityPtr ent = (xmlEntityPtr) cur; - return(xmlStrdup(ent->URI)); + + if (ent->URI == NULL) + break; + xmlFree(ret); + ret = xmlStrdup(ent->URI); + if (ret == NULL) + return(-1); + goto found; } if (cur->type == XML_ELEMENT_NODE) { - base = xmlGetNsProp(cur, BAD_CAST "base", XML_XML_NAMESPACE); + if (xmlNodeGetAttrValue(cur, BAD_CAST "base", XML_XML_NAMESPACE, + &base) < 0) { + xmlFree(ret); + return(-1); + } if (base != NULL) { - if (oldbase != NULL) { - newbase = xmlBuildURI(oldbase, base); - if (newbase != NULL) { - xmlFree(oldbase); - xmlFree(base); - oldbase = newbase; - } else { - xmlFree(oldbase); - xmlFree(base); - return(NULL); - } + if (ret != NULL) { + res = xmlBuildURISafe(ret, base, &newbase); + xmlFree(ret); + xmlFree(base); + if (res != 0) + return(res); + ret = newbase; } else { - oldbase = base; + ret = base; } - if ((!xmlStrncmp(oldbase, BAD_CAST "http://", 7)) || - (!xmlStrncmp(oldbase, BAD_CAST "ftp://", 6)) || - (!xmlStrncmp(oldbase, BAD_CAST "urn:", 4))) - return(oldbase); + if ((!xmlStrncmp(ret, BAD_CAST "http://", 7)) || + (!xmlStrncmp(ret, BAD_CAST "ftp://", 6)) || + (!xmlStrncmp(ret, BAD_CAST "urn:", 4))) + goto found; } } cur = cur->parent; } + if ((doc != NULL) && (doc->URL != NULL)) { - if (oldbase == NULL) - return(xmlStrdup(doc->URL)); - newbase = xmlBuildURI(oldbase, doc->URL); - xmlFree(oldbase); - return(newbase); + if (ret == NULL) { + ret = xmlStrdup(doc->URL); + if (ret == NULL) + return(-1); + } else { + res = xmlBuildURISafe(ret, doc->URL, &newbase); + xmlFree(ret); + if (res != 0) + return(res); + ret = newbase; + } } - return(oldbase); + +found: + *baseOut = ret; + return(0); +} + +/** + * xmlNodeGetBase: + * @doc: the document the node pertains to + * @cur: the node being checked + * + * See xmlNodeGetBaseSafe. This function doesn't allow to distinguish + * memory allocation failures from a non-existing base. + * + * Returns a pointer to the base URL, or NULL if not found + * It's up to the caller to free the memory with xmlFree(). + */ +xmlChar * +xmlNodeGetBase(const xmlDoc *doc, const xmlNode *cur) { + xmlChar *base; + + xmlNodeGetBaseSafe(doc, cur, &base); + return(base); } /** @@ -5347,6 +5507,69 @@ xmlNodeBufGetContent(xmlBufferPtr buffer, const xmlNode *cur) return(0); } +static void +xmlBufGetEntityRefContent(xmlBufPtr buf, const xmlNode *ref) { + xmlEntityPtr ent; + + if (ref->children != NULL) { + ent = (xmlEntityPtr) ref->children; + } else { + /* lookup entity declaration */ + ent = xmlGetDocEntity(ref->doc, ref->name); + if (ent == NULL) + return; + } + + /* + * The parser should always expand predefined entities but it's + * possible to create references to predefined entities using + * the tree API. + */ + if (ent->etype == XML_INTERNAL_PREDEFINED_ENTITY) { + xmlBufCat(buf, ent->content); + return; + } + + if (ent->flags & XML_ENT_EXPANDING) + return; + + ent->flags |= XML_ENT_EXPANDING; + xmlBufGetChildContent(buf, (xmlNodePtr) ent); + ent->flags &= ~XML_ENT_EXPANDING; +} + +static void +xmlBufGetChildContent(xmlBufPtr buf, const xmlNode *tree) { + const xmlNode *cur = tree->children; + + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + xmlBufCat(buf, cur->content); + break; + + case XML_ENTITY_REF_NODE: + xmlBufGetEntityRefContent(buf, cur); + break; + + default: + if (cur->children != NULL) { + cur = cur->children; + continue; + } + break; + } + + while (cur->next == NULL) { + cur = cur->parent; + if (cur == tree) + return; + } + cur = cur->next; + } +} + /** * xmlBufGetNodeContent: * @buf: a buffer xmlBufPtr @@ -5363,127 +5586,38 @@ xmlNodeBufGetContent(xmlBufferPtr buffer, const xmlNode *cur) int xmlBufGetNodeContent(xmlBufPtr buf, const xmlNode *cur) { - if ((cur == NULL) || (buf == NULL)) return(-1); + if ((cur == NULL) || (buf == NULL)) + return(-1); + switch (cur->type) { - case XML_CDATA_SECTION_NODE: - case XML_TEXT_NODE: - xmlBufCat(buf, cur->content); - break; + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: case XML_DOCUMENT_FRAG_NODE: - case XML_ELEMENT_NODE:{ - const xmlNode *tmp = cur; - - while (tmp != NULL) { - switch (tmp->type) { - case XML_CDATA_SECTION_NODE: - case XML_TEXT_NODE: - if (tmp->content != NULL) - xmlBufCat(buf, tmp->content); - break; - case XML_ENTITY_REF_NODE: - xmlBufGetNodeContent(buf, tmp); - break; - default: - break; - } - /* - * Skip to next node - */ - if (tmp->children != NULL) { - if (tmp->children->type != XML_ENTITY_DECL) { - tmp = tmp->children; - continue; - } - } - if (tmp == cur) - break; - - if (tmp->next != NULL) { - tmp = tmp->next; - continue; - } - - do { - tmp = tmp->parent; - if (tmp == NULL) - break; - if (tmp == cur) { - tmp = NULL; - break; - } - if (tmp->next != NULL) { - tmp = tmp->next; - break; - } - } while (tmp != NULL); - } - break; - } - case XML_ATTRIBUTE_NODE:{ - xmlAttrPtr attr = (xmlAttrPtr) cur; - xmlNodePtr tmp = attr->children; + case XML_ELEMENT_NODE: + case XML_ATTRIBUTE_NODE: + case XML_ENTITY_DECL: + xmlBufGetChildContent(buf, cur); + break; - while (tmp != NULL) { - if (tmp->type == XML_TEXT_NODE) - xmlBufCat(buf, tmp->content); - else - xmlBufGetNodeContent(buf, tmp); - tmp = tmp->next; - } - break; - } + case XML_CDATA_SECTION_NODE: + case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: xmlBufCat(buf, cur->content); break; - case XML_ENTITY_REF_NODE:{ - xmlEntityPtr ent; - xmlNodePtr tmp; - - /* lookup entity declaration */ - ent = xmlGetDocEntity(cur->doc, cur->name); - if (ent == NULL) - return(-1); - /* an entity content can be any "well balanced chunk", - * i.e. the result of the content [43] production: - * http://www.w3.org/TR/REC-xml#NT-content - * -> we iterate through child nodes and recursive call - * xmlNodeGetContent() which handles all possible node types */ - tmp = ent->children; - while (tmp) { - xmlBufGetNodeContent(buf, tmp); - tmp = tmp->next; - } - break; - } - case XML_ENTITY_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_NOTATION_NODE: - case XML_DTD_NODE: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: + case XML_ENTITY_REF_NODE: + xmlBufGetEntityRefContent(buf, cur); break; - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: - cur = cur->children; - while (cur!= NULL) { - if ((cur->type == XML_ELEMENT_NODE) || - (cur->type == XML_TEXT_NODE) || - (cur->type == XML_CDATA_SECTION_NODE)) { - xmlBufGetNodeContent(buf, cur); - } - cur = cur->next; - } - break; + case XML_NAMESPACE_DECL: xmlBufCat(buf, ((xmlNsPtr) cur)->href); break; - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: + + default: break; } + return(0); } @@ -5501,163 +5635,131 @@ xmlBufGetNodeContent(xmlBufPtr buf, const xmlNode *cur) xmlChar * xmlNodeGetContent(const xmlNode *cur) { + xmlBufPtr buf; + xmlChar *ret; + if (cur == NULL) return (NULL); + switch (cur->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: + case XML_ENTITY_REF_NODE: + break; + case XML_DOCUMENT_FRAG_NODE: - case XML_ELEMENT_NODE:{ - xmlBufPtr buf; - xmlChar *ret; - - buf = xmlBufCreateSize(64); - if (buf == NULL) - return (NULL); - xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); - xmlBufGetNodeContent(buf, cur); - ret = xmlBufDetach(buf); - xmlBufFree(buf); - return (ret); - } + case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: - return(xmlGetPropNodeValueInternal((xmlAttrPtr) cur)); - case XML_COMMENT_NODE: - case XML_PI_NODE: - if (cur->content != NULL) - return (xmlStrdup(cur->content)); - return (NULL); - case XML_ENTITY_REF_NODE:{ - xmlEntityPtr ent; - xmlBufPtr buf; - xmlChar *ret; - - /* lookup entity declaration */ - ent = xmlGetDocEntity(cur->doc, cur->name); - if (ent == NULL) - return (NULL); - - buf = xmlBufCreate(); - if (buf == NULL) - return (NULL); - xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); - - xmlBufGetNodeContent(buf, cur); - - ret = xmlBufDetach(buf); - xmlBufFree(buf); - return (ret); + case XML_ENTITY_DECL: { + xmlNodePtr children = cur->children; + + if (children == NULL) + return(xmlStrdup(BAD_CAST "")); + + /* Optimization for single text children */ + if (((children->type == XML_TEXT_NODE) || + (children->type == XML_CDATA_SECTION_NODE)) && + (children->next == NULL)) { + if (children->content == NULL) + return(xmlStrdup(BAD_CAST "")); + return(xmlStrdup(children->content)); } - case XML_ENTITY_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_NOTATION_NODE: - case XML_DTD_NODE: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return (NULL); - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: { - xmlBufPtr buf; - xmlChar *ret; - - buf = xmlBufCreate(); - if (buf == NULL) - return (NULL); - xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); - - xmlBufGetNodeContent(buf, (xmlNodePtr) cur); - ret = xmlBufDetach(buf); - xmlBufFree(buf); - return (ret); - } - case XML_NAMESPACE_DECL: { - xmlChar *tmp; + break; + } - tmp = xmlStrdup(((xmlNsPtr) cur)->href); - return (tmp); - } - case XML_ELEMENT_DECL: - /* TODO !!! */ - return (NULL); - case XML_ATTRIBUTE_DECL: - /* TODO !!! */ - return (NULL); - case XML_ENTITY_DECL: - /* TODO !!! */ - return (NULL); case XML_CDATA_SECTION_NODE: case XML_TEXT_NODE: + case XML_COMMENT_NODE: + case XML_PI_NODE: if (cur->content != NULL) - return (xmlStrdup(cur->content)); - return (NULL); + return(xmlStrdup(cur->content)); + else + return(xmlStrdup(BAD_CAST "")); + + case XML_NAMESPACE_DECL: + return(xmlStrdup(((xmlNsPtr) cur)->href)); + + default: + return(NULL); } - return (NULL); + + buf = xmlBufCreateSize(64); + if (buf == NULL) + return (NULL); + xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); + xmlBufGetNodeContent(buf, cur); + ret = xmlBufDetach(buf); + xmlBufFree(buf); + + return(ret); } -/** - * xmlNodeSetContent: - * @cur: the node being modified - * @content: the new value of the content - * - * Replace the content of a node. - * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity - * references, but XML special chars need to be escaped first by using - * xmlEncodeEntitiesReentrant() resp. xmlEncodeSpecialChars(). - */ -void -xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) { +static int +xmlNodeSetContentInternal(xmlNodePtr cur, const xmlChar *content, int len) { if (cur == NULL) { - return; + return(1); } switch (cur->type) { case XML_DOCUMENT_FRAG_NODE: case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: - if (cur->children != NULL) xmlFreeNodeList(cur->children); - cur->children = xmlStringGetNodeList(cur->doc, content); - UPDATE_LAST_CHILD_AND_PARENT(cur) + if (xmlNodeParseContent(cur, content, len) < 0) + return(-1); break; + case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: case XML_PI_NODE: - case XML_COMMENT_NODE: - if ((cur->content != NULL) && - (cur->content != (xmlChar *) &(cur->properties))) { - if (!((cur->doc != NULL) && (cur->doc->dict != NULL) && - (xmlDictOwns(cur->doc->dict, cur->content)))) - xmlFree(cur->content); - } - if (cur->children != NULL) xmlFreeNodeList(cur->children); - cur->last = cur->children = NULL; + case XML_COMMENT_NODE: { + xmlChar *copy = NULL; + if (content != NULL) { - cur->content = xmlStrdup(content); - } else - cur->content = NULL; - cur->properties = NULL; - break; - case XML_DOCUMENT_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - break; - case XML_NOTATION_NODE: - break; - case XML_DTD_NODE: - break; - case XML_NAMESPACE_DECL: - break; - case XML_ELEMENT_DECL: - /* TODO !!! */ - break; - case XML_ATTRIBUTE_DECL: - /* TODO !!! */ - break; - case XML_ENTITY_DECL: - /* TODO !!! */ + if (len < 0) + copy = xmlStrdup(content); + else + copy = xmlStrndup(content, len); + if (copy == NULL) + return(-1); + } + + xmlTextSetContent(cur, copy); break; + } + + default: + break; } + + return(0); +} + +/** + * xmlNodeSetContent: + * @cur: the node being modified + * @content: the new value of the content + * + * Replace the text content of a node. + * + * Sets the raw text content of text, CDATA, comment or PI nodes. + * + * For element and attribute nodes, removes all children and + * replaces them by parsing @content which is expected to be a + * valid XML attribute value possibly containing character and + * entity references. Syntax errors and references to undeclared + * entities are ignored silently. Unfortunately, there isn't an + * API to pass raw content directly. An inefficient work-around + * is to escape the content with xmlEncodeSpecialChars before + * passing it. A better trick is clearing the old content + * with xmlNodeSetContent(node, NULL) first and then calling + * xmlNodeAddContent(node, content). Unlike this function, + * xmlNodeAddContent accepts raw text. + * + * Returns 0 on success, 1 on error, -1 if a memory allocation failed. + */ +int +xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) { + return(xmlNodeSetContentInternal(cur, content, -1)); } #ifdef LIBXML_TREE_ENABLED @@ -5667,63 +5769,13 @@ xmlNodeSetContent(xmlNodePtr cur, const xmlChar *content) { * @content: the new value of the content * @len: the size of @content * - * Replace the content of a node. - * NOTE: @content is supposed to be a piece of XML CDATA, so it allows entity - * references, but XML special chars need to be escaped first by using - * xmlEncodeEntitiesReentrant() resp. xmlEncodeSpecialChars(). + * See xmlNodeSetContent. + * + * Returns 0 on success, 1 on error, -1 if a memory allocation failed. */ -void +int xmlNodeSetContentLen(xmlNodePtr cur, const xmlChar *content, int len) { - if (cur == NULL) { - return; - } - switch (cur->type) { - case XML_DOCUMENT_FRAG_NODE: - case XML_ELEMENT_NODE: - case XML_ATTRIBUTE_NODE: - if (cur->children != NULL) xmlFreeNodeList(cur->children); - cur->children = xmlStringLenGetNodeList(cur->doc, content, len); - UPDATE_LAST_CHILD_AND_PARENT(cur) - break; - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_PI_NODE: - case XML_COMMENT_NODE: - case XML_NOTATION_NODE: - if ((cur->content != NULL) && - (cur->content != (xmlChar *) &(cur->properties))) { - if (!((cur->doc != NULL) && (cur->doc->dict != NULL) && - (xmlDictOwns(cur->doc->dict, cur->content)))) - xmlFree(cur->content); - } - if (cur->children != NULL) xmlFreeNodeList(cur->children); - cur->children = cur->last = NULL; - if (content != NULL) { - cur->content = xmlStrndup(content, len); - } else - cur->content = NULL; - cur->properties = NULL; - break; - case XML_DOCUMENT_NODE: - case XML_DTD_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - break; - case XML_ELEMENT_DECL: - /* TODO !!! */ - break; - case XML_ATTRIBUTE_DECL: - /* TODO !!! */ - break; - case XML_ENTITY_DECL: - /* TODO !!! */ - break; - } + return(xmlNodeSetContentInternal(cur, content, len)); } #endif /* LIBXML_TREE_ENABLED */ @@ -5737,63 +5789,43 @@ xmlNodeSetContentLen(xmlNodePtr cur, const xmlChar *content, int len) { * NOTE: In contrast to xmlNodeSetContentLen(), @content is supposed to be * raw text, so unescaped XML special chars are allowed, entity * references are not supported. + * + * Returns 0 on success, 1 on error, -1 if a memory allocation failed. */ -void +int xmlNodeAddContentLen(xmlNodePtr cur, const xmlChar *content, int len) { - if (cur == NULL) { - return; - } - if (len <= 0) return; + if (cur == NULL) + return(1); + if ((content == NULL) || (len <= 0)) + return(0); + switch (cur->type) { case XML_DOCUMENT_FRAG_NODE: case XML_ELEMENT_NODE: { - xmlNodePtr last, newNode, tmp; + xmlNodePtr newNode, tmp; - last = cur->last; newNode = xmlNewDocTextLen(cur->doc, content, len); - if (newNode != NULL) { - tmp = xmlAddChild(cur, newNode); - if (tmp != newNode) - return; - if ((last != NULL) && (last->next == newNode)) { - xmlTextMerge(last, newNode); - } - } + if (newNode == NULL) + return(-1); + tmp = xmlAddChild(cur, newNode); + if (tmp == NULL) { + xmlFreeNode(newNode); + return(-1); + } break; } case XML_ATTRIBUTE_NODE: break; case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: - case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: - case XML_PI_NODE: - case XML_COMMENT_NODE: - case XML_NOTATION_NODE: - if (content != NULL) { - if ((cur->content == (xmlChar *) &(cur->properties)) || - ((cur->doc != NULL) && (cur->doc->dict != NULL) && - xmlDictOwns(cur->doc->dict, cur->content))) { - cur->content = xmlStrncatNew(cur->content, content, len); - cur->properties = NULL; - } else { - cur->content = xmlStrncat(cur->content, content, len); - } - } - break; - case XML_DOCUMENT_NODE: - case XML_DTD_NODE: - case XML_HTML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_NAMESPACE_DECL: - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - break; - case XML_ELEMENT_DECL: - case XML_ATTRIBUTE_DECL: - case XML_ENTITY_DECL: - break; + case XML_PI_NODE: + case XML_COMMENT_NODE: + return(xmlTextAddContent(cur, content, len)); + default: + break; } + + return(0); } /** @@ -5805,17 +5837,12 @@ xmlNodeAddContentLen(xmlNodePtr cur, const xmlChar *content, int len) { * NOTE: In contrast to xmlNodeSetContent(), @content is supposed to be * raw text, so unescaped XML special chars are allowed, entity * references are not supported. + * + * Returns 0 on success, 1 on error, -1 if a memory allocation failed. */ -void +int xmlNodeAddContent(xmlNodePtr cur, const xmlChar *content) { - int len; - - if (cur == NULL) { - return; - } - if (content == NULL) return; - len = xmlStrlen(content); - xmlNodeAddContentLen(cur, content, len); + return(xmlNodeAddContentLen(cur, content, xmlStrlen(content))); } /** @@ -5823,53 +5850,66 @@ xmlNodeAddContent(xmlNodePtr cur, const xmlChar *content) { * @first: the first text node * @second: the second text node being merged * - * Merge two text nodes into one - * Returns the first text node augmented + * Merge the second text node into the first. The second node is + * unlinked and freed. + * + * Returns the first text node augmented or NULL in case of error. */ xmlNodePtr xmlTextMerge(xmlNodePtr first, xmlNodePtr second) { - if (first == NULL) return(second); - if (second == NULL) return(first); - if (first->type != XML_TEXT_NODE) return(first); - if (second->type != XML_TEXT_NODE) return(first); - if (second->name != first->name) - return(first); - xmlNodeAddContent(first, second->content); - xmlUnlinkNode(second); + if ((first == NULL) || (first->type != XML_TEXT_NODE) || + (second == NULL) || (second->type != XML_TEXT_NODE) || + (first == second) || + (first->name != second->name)) + return(NULL); + + if (xmlTextAddContent(first, second->content, -1) < 0) + return(NULL); + + xmlUnlinkNodeInternal(second); xmlFreeNode(second); return(first); } #if defined(LIBXML_TREE_ENABLED) || defined(LIBXML_XPATH_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) /** - * xmlGetNsList: + * xmlGetNsListSafe: * @doc: the document * @node: the current node + * @out: the returned namespace array + * + * Find all in-scope namespaces of a node. @out returns a NULL + * terminated array of namespace pointers that must be freed by + * the caller. * - * Search all the namespace applying to a given element. - * Returns an NULL terminated array of all the #xmlNsPtr found - * that need to be freed by the caller or NULL if no - * namespace if defined + * Available since 2.13.0. + * + * Returns 0 on success, 1 if no namespaces were found, -1 if a + * memory allocation failed. */ -xmlNsPtr * -xmlGetNsList(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlNode *node) +int +xmlGetNsListSafe(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlNode *node, + xmlNsPtr **out) { xmlNsPtr cur; - xmlNsPtr *ret = NULL; + xmlNsPtr *namespaces = NULL; int nbns = 0; int maxns = 0; int i; + if (out == NULL) + return(1); + *out = NULL; if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) - return(NULL); + return(1); while (node != NULL) { if (node->type == XML_ELEMENT_NODE) { cur = node->nsDef; while (cur != NULL) { for (i = 0; i < nbns; i++) { - if ((cur->prefix == ret[i]->prefix) || - (xmlStrEqual(cur->prefix, ret[i]->prefix))) + if ((cur->prefix == namespaces[i]->prefix) || + (xmlStrEqual(cur->prefix, namespaces[i]->prefix))) break; } if (i >= nbns) { @@ -5877,18 +5917,17 @@ xmlGetNsList(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlNode *node) xmlNsPtr *tmp; maxns = maxns ? maxns * 2 : 10; - tmp = (xmlNsPtr *) xmlRealloc(ret, + tmp = (xmlNsPtr *) xmlRealloc(namespaces, (maxns + 1) * sizeof(xmlNsPtr)); if (tmp == NULL) { - xmlTreeErrMemory("getting namespace list"); - xmlFree(ret); - return (NULL); + xmlFree(namespaces); + return(-1); } - ret = tmp; + namespaces = tmp; } - ret[nbns++] = cur; - ret[nbns] = NULL; + namespaces[nbns++] = cur; + namespaces[nbns] = NULL; } cur = cur->next; @@ -5896,40 +5935,161 @@ xmlGetNsList(const xmlDoc *doc ATTRIBUTE_UNUSED, const xmlNode *node) } node = node->parent; } - return (ret); + + *out = namespaces; + return((namespaces == NULL) ? 1 : 0); +} + +/** + * xmlGetNsList: + * @doc: the document + * @node: the current node + * + * Find all in-scope namespaces of a node. + * + * Use xmlGetNsListSafe for better error reporting. + * + * Returns a NULL terminated array of namespace pointers that must + * be freed by the caller or NULL if no namespaces were found or + * a memory allocation failed. + */ +xmlNsPtr * +xmlGetNsList(const xmlDoc *doc, const xmlNode *node) +{ + xmlNsPtr *ret; + + xmlGetNsListSafe(doc, node, &ret); + return(ret); } #endif /* LIBXML_TREE_ENABLED */ +static xmlNsPtr +xmlNewXmlNs(void) { + xmlNsPtr ns; + + ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); + if (ns == NULL) + return(NULL); + memset(ns, 0, sizeof(xmlNs)); + ns->type = XML_LOCAL_NAMESPACE; + ns->href = xmlStrdup(XML_XML_NAMESPACE); + if (ns->href == NULL) { + xmlFreeNs(ns); + return(NULL); + } + ns->prefix = xmlStrdup(BAD_CAST "xml"); + if (ns->prefix == NULL) { + xmlFreeNs(ns); + return(NULL); + } + + return(ns); +} + /* * xmlTreeEnsureXMLDecl: * @doc: the doc * * Ensures that there is an XML namespace declaration on the doc. * -* Returns the XML ns-struct or NULL on API and internal errors. +* Returns the XML ns-struct or NULL if a memory allocation failed. */ static xmlNsPtr xmlTreeEnsureXMLDecl(xmlDocPtr doc) { - if (doc == NULL) - return (NULL); - if (doc->oldNs != NULL) - return (doc->oldNs); - { - xmlNsPtr ns; - ns = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (ns == NULL) { - xmlTreeErrMemory( - "allocating the XML namespace"); - return (NULL); - } - memset(ns, 0, sizeof(xmlNs)); - ns->type = XML_LOCAL_NAMESPACE; - ns->href = xmlStrdup(XML_XML_NAMESPACE); - ns->prefix = xmlStrdup((const xmlChar *)"xml"); - doc->oldNs = ns; + xmlNsPtr ns; + + ns = doc->oldNs; + if (ns != NULL) return (ns); + + ns = xmlNewXmlNs(); + doc->oldNs = ns; + + return(ns); +} + +/** + * xmlSearchNsSafe: + * @node: a node + * @prefix: a namespace prefix + * @out: pointer to resulting namespace + * + * Search a namespace with @prefix in scope of @node. + * + * Returns 0 on success, -1 if a memory allocation failed, 1 on + * other errors. + */ +int +xmlSearchNsSafe(xmlNodePtr node, const xmlChar *prefix, + xmlNsPtr *out) { + xmlNsPtr cur; + xmlDocPtr doc; + xmlNodePtr orig = node; + xmlNodePtr parent; + + if (out == NULL) + return(1); + *out = NULL; + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) + return(1); + + doc = node->doc; + + if ((doc != NULL) && (IS_STR_XML(prefix))) { + cur = xmlTreeEnsureXMLDecl(doc); + if (cur == NULL) + return(-1); + *out = cur; + return(0); + } + + while (node->type != XML_ELEMENT_NODE) { + node = node->parent; + if (node == NULL) + return(0); + } + + parent = node; + + while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) { + cur = node->nsDef; + while (cur != NULL) { + if ((xmlStrEqual(cur->prefix, prefix)) && + (cur->href != NULL)) { + *out = cur; + return(0); + } + cur = cur->next; + } + if (orig != node) { + cur = node->ns; + if ((cur != NULL) && + (xmlStrEqual(cur->prefix, prefix)) && + (cur->href != NULL)) { + *out = cur; + return(0); + } + } + + node = node->parent; + } + + /* + * The XML-1.0 namespace is normally held on the document + * element. In this case exceptionally create it on the + * node element. + */ + if ((doc == NULL) && (IS_STR_XML(prefix))) { + cur = xmlNewXmlNs(); + if (cur == NULL) + return(-1); + cur->next = parent->nsDef; + parent->nsDef = cur; + *out = cur; } + + return(0); } /** @@ -5946,82 +6106,17 @@ xmlTreeEnsureXMLDecl(xmlDocPtr doc) * the namespace within those you will be in troubles !!! A warning * is generated to cover this case. * - * Returns the namespace pointer or NULL. + * Returns the namespace pointer or NULL if no namespace was found or + * a memory allocation failed. Allocations can only fail if the "xml" + * namespace is queried. */ xmlNsPtr -xmlSearchNs(xmlDocPtr doc, xmlNodePtr node, const xmlChar *nameSpace) { - +xmlSearchNs(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node, + const xmlChar *nameSpace) { xmlNsPtr cur; - const xmlNode *orig = node; - if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) return(NULL); - if ((nameSpace != NULL) && - (xmlStrEqual(nameSpace, (const xmlChar *)"xml"))) { - if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) { - /* - * The XML-1.0 namespace is normally held on the root - * element. In this case exceptionally create it on the - * node element. - */ - cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (cur == NULL) { - xmlTreeErrMemory("searching namespace"); - return(NULL); - } - memset(cur, 0, sizeof(xmlNs)); - cur->type = XML_LOCAL_NAMESPACE; - cur->href = xmlStrdup(XML_XML_NAMESPACE); - cur->prefix = xmlStrdup((const xmlChar *)"xml"); - cur->next = node->nsDef; - node->nsDef = cur; - return(cur); - } - if (doc == NULL) { - doc = node->doc; - if (doc == NULL) - return(NULL); - } - /* - * Return the XML namespace declaration held by the doc. - */ - if (doc->oldNs == NULL) - return(xmlTreeEnsureXMLDecl(doc)); - else - return(doc->oldNs); - } - while (node != NULL) { - if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || - (node->type == XML_ENTITY_DECL)) - return(NULL); - if (node->type == XML_ELEMENT_NODE) { - cur = node->nsDef; - while (cur != NULL) { - if ((cur->prefix == NULL) && (nameSpace == NULL) && - (cur->href != NULL)) - return(cur); - if ((cur->prefix != NULL) && (nameSpace != NULL) && - (cur->href != NULL) && - (xmlStrEqual(cur->prefix, nameSpace))) - return(cur); - cur = cur->next; - } - if (orig != node) { - cur = node->ns; - if (cur != NULL) { - if ((cur->prefix == NULL) && (nameSpace == NULL) && - (cur->href != NULL)) - return(cur); - if ((cur->prefix != NULL) && (nameSpace != NULL) && - (cur->href != NULL) && - (xmlStrEqual(cur->prefix, nameSpace))) - return(cur); - } - } - } - node = node->parent; - } - return(NULL); + xmlSearchNsSafe(node, nameSpace, &cur); + return(cur); } /** @@ -6044,7 +6139,6 @@ xmlNsInScope(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node, while ((node != NULL) && (node != ancestor)) { if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || (node->type == XML_ENTITY_DECL)) return (-1); if (node->type == XML_ELEMENT_NODE) { @@ -6068,92 +6162,117 @@ xmlNsInScope(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node, } /** - * xmlSearchNsByHref: - * @doc: the document - * @node: the current node - * @href: the namespace value + * xmlSearchNsByHrefSafe: + * @node: a node + * @href: a namespace URI + * @out: pointer to resulting namespace * - * Search a Ns aliasing a given URI. Recurse on the parents until it finds - * the defined namespace or return NULL otherwise. - * Returns the namespace pointer or NULL. + * Search a namespace matching @URI in scope of @node. + * + * Returns 0 on success, -1 if a memory allocation failed, 1 on + * other errors. */ -xmlNsPtr -xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href) -{ +int +xmlSearchNsByHrefSafe(xmlNodePtr node, const xmlChar *href, + xmlNsPtr *out) { xmlNsPtr cur; + xmlDocPtr doc; xmlNodePtr orig = node; + xmlNodePtr parent; int is_attr; - if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || (href == NULL)) - return (NULL); - if (xmlStrEqual(href, XML_XML_NAMESPACE)) { - /* - * Only the document can hold the XML spec namespace. - */ - if ((doc == NULL) && (node->type == XML_ELEMENT_NODE)) { - /* - * The XML-1.0 namespace is normally held on the root - * element. In this case exceptionally create it on the - * node element. - */ - cur = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (cur == NULL) { - xmlTreeErrMemory("searching namespace"); - return (NULL); - } - memset(cur, 0, sizeof(xmlNs)); - cur->type = XML_LOCAL_NAMESPACE; - cur->href = xmlStrdup(XML_XML_NAMESPACE); - cur->prefix = xmlStrdup((const xmlChar *) "xml"); - cur->next = node->nsDef; - node->nsDef = cur; - return (cur); - } - if (doc == NULL) { - doc = node->doc; - if (doc == NULL) - return(NULL); - } - /* - * Return the XML namespace declaration held by the doc. - */ - if (doc->oldNs == NULL) - return(xmlTreeEnsureXMLDecl(doc)); - else - return(doc->oldNs); + if (out == NULL) + return(1); + *out = NULL; + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) + return(1); + + doc = node->doc; + + if ((doc != NULL) && (xmlStrEqual(href, XML_XML_NAMESPACE))) { + cur = xmlTreeEnsureXMLDecl(doc); + if (cur == NULL) + return(-1); + *out = cur; + return(0); } + is_attr = (node->type == XML_ATTRIBUTE_NODE); - while (node != NULL) { - if ((node->type == XML_ENTITY_REF_NODE) || - (node->type == XML_ENTITY_NODE) || - (node->type == XML_ENTITY_DECL)) - return (NULL); - if (node->type == XML_ELEMENT_NODE) { - cur = node->nsDef; - while (cur != NULL) { - if ((cur->href != NULL) && (href != NULL) && - (xmlStrEqual(cur->href, href))) { - if (((!is_attr) || (cur->prefix != NULL)) && - (xmlNsInScope(doc, orig, node, cur->prefix) == 1)) - return (cur); + + while (node->type != XML_ELEMENT_NODE) { + node = node->parent; + if (node == NULL) + return(0); + } + + parent = node; + + while ((node != NULL) && (node->type == XML_ELEMENT_NODE)) { + cur = node->nsDef; + while (cur != NULL) { + if (xmlStrEqual(cur->href, href)) { + if (((!is_attr) || (cur->prefix != NULL)) && + (xmlNsInScope(doc, orig, node, cur->prefix) == 1)) { + *out = cur; + return(0); } - cur = cur->next; } - if (orig != node) { - cur = node->ns; - if (cur != NULL) { - if ((cur->href != NULL) && (href != NULL) && - (xmlStrEqual(cur->href, href))) { - if (((!is_attr) || (cur->prefix != NULL)) && - (xmlNsInScope(doc, orig, node, cur->prefix) == 1)) - return (cur); + cur = cur->next; + } + if (orig != node) { + cur = node->ns; + if (cur != NULL) { + if (xmlStrEqual(cur->href, href)) { + if (((!is_attr) || (cur->prefix != NULL)) && + (xmlNsInScope(doc, orig, node, + cur->prefix) == 1)) { + *out = cur; + return(0); } } } } + node = node->parent; } - return (NULL); + + /* + * The XML-1.0 namespace is normally held on the document + * element. In this case exceptionally create it on the + * node element. + */ + if ((doc == NULL) && (xmlStrEqual(href, XML_XML_NAMESPACE))) { + cur = xmlNewXmlNs(); + if (cur == NULL) + return(-1); + cur->next = parent->nsDef; + parent->nsDef = cur; + *out = cur; + } + + return(0); +} + +/** + * xmlSearchNsByHref: + * @doc: the document + * @node: the current node + * @href: the namespace value + * + * Search a Ns aliasing a given URI. Recurse on the parents until it finds + * the defined namespace or return NULL otherwise. + * + * Returns the namespace pointer or NULL if no namespace was found or + * a memory allocation failed. Allocations can only fail if the "xml" + * namespace is queried. + */ +xmlNsPtr +xmlSearchNsByHref(xmlDocPtr doc ATTRIBUTE_UNUSED, xmlNodePtr node, + const xmlChar * href) { + xmlNsPtr cur; + + xmlSearchNsByHrefSafe(node, href, &cur); + return(cur); } /** @@ -6170,10 +6289,11 @@ xmlSearchNsByHref(xmlDocPtr doc, xmlNodePtr node, const xmlChar * href) * Returns the (new) namespace definition or NULL in case of error */ static xmlNsPtr -xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { +xmlNewReconciledNs(xmlNodePtr tree, xmlNsPtr ns) { xmlNsPtr def; xmlChar prefix[50]; int counter = 1; + int res; if ((tree == NULL) || (tree->type != XML_ELEMENT_NODE)) { return(NULL); @@ -6184,7 +6304,9 @@ xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { /* * Search an existing namespace definition inherited. */ - def = xmlSearchNsByHref(doc, tree, ns->href); + res = xmlSearchNsByHrefSafe(tree, ns->href, &def); + if (res < 0) + return(NULL); if (def != NULL) return(def); @@ -6197,7 +6319,9 @@ xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { else snprintf((char *) prefix, sizeof(prefix), "%.20s", (char *)ns->prefix); - def = xmlSearchNs(doc, tree, prefix); + res = xmlSearchNsSafe(tree, prefix, &def); + if (res < 0) + return(NULL); while (def != NULL) { if (counter > 1000) return(NULL); if (ns->prefix == NULL) @@ -6205,7 +6329,9 @@ xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { else snprintf((char *) prefix, sizeof(prefix), "%.20s%d", (char *)ns->prefix, counter++); - def = xmlSearchNs(doc, tree, prefix); + res = xmlSearchNsSafe(tree, prefix, &def); + if (res < 0) + return(NULL); } /* @@ -6216,6 +6342,12 @@ xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { } #ifdef LIBXML_TREE_ENABLED + +typedef struct { + xmlNsPtr oldNs; + xmlNsPtr newNs; +} xmlNsCache; + /** * xmlReconciliateNs: * @doc: the document @@ -6228,12 +6360,12 @@ xmlNewReconciledNs(xmlDocPtr doc, xmlNodePtr tree, xmlNsPtr ns) { * as possible the function try to reuse the existing namespaces found in * the new environment. If not possible the new namespaces are redeclared * on @tree at the top of the given subtree. - * Returns the number of namespace declarations created or -1 in case of error. + * + * Returns 0 on success or -1 in case of error. */ int xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { - xmlNsPtr *oldNs = NULL; - xmlNsPtr *newNs = NULL; + xmlNsCache *cache = NULL; int sizeCache = 0; int nbCache = 0; @@ -6243,35 +6375,15 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { int ret = 0, i; if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) return(-1); - if ((doc == NULL) || (doc->type != XML_DOCUMENT_NODE)) return(-1); if (node->doc != doc) return(-1); while (node != NULL) { /* * Reconciliate the node namespace */ if (node->ns != NULL) { - /* - * initialize the cache if needed - */ - if (sizeCache == 0) { - sizeCache = 10; - oldNs = (xmlNsPtr *) xmlMalloc(sizeCache * - sizeof(xmlNsPtr)); - if (oldNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - return(-1); - } - newNs = (xmlNsPtr *) xmlMalloc(sizeCache * - sizeof(xmlNsPtr)); - if (newNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(oldNs); - return(-1); - } - } - for (i = 0;i < nbCache;i++) { - if (oldNs[i] == node->ns) { - node->ns = newNs[i]; + for (i = 0; i < nbCache; i++) { + if (cache[i].oldNs == node->ns) { + node->ns = cache[i].newNs; break; } } @@ -6279,32 +6391,31 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { /* * OK we need to recreate a new namespace definition */ - n = xmlNewReconciledNs(doc, tree, node->ns); - if (n != NULL) { /* :-( what if else ??? */ + n = xmlNewReconciledNs(tree, node->ns); + if (n == NULL) { + ret = -1; + } else { /* * check if we need to grow the cache buffers. */ if (sizeCache <= nbCache) { - sizeCache *= 2; - oldNs = (xmlNsPtr *) xmlRealloc(oldNs, sizeCache * - sizeof(xmlNsPtr)); - if (oldNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(newNs); - return(-1); - } - newNs = (xmlNsPtr *) xmlRealloc(newNs, sizeCache * - sizeof(xmlNsPtr)); - if (newNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(oldNs); - return(-1); - } + xmlNsCache *tmp; + size_t newSize = sizeCache ? sizeCache * 2 : 10; + + tmp = xmlRealloc(cache, newSize * sizeof(tmp[0])); + if (tmp == NULL) { + ret = -1; + } else { + cache = tmp; + sizeCache = newSize; + } } - newNs[nbCache] = n; - oldNs[nbCache++] = node->ns; - node->ns = n; + if (nbCache < sizeCache) { + cache[nbCache].newNs = n; + cache[nbCache++].oldNs = node->ns; + } } + node->ns = n; } } /* @@ -6314,28 +6425,9 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { attr = node->properties; while (attr != NULL) { if (attr->ns != NULL) { - /* - * initialize the cache if needed - */ - if (sizeCache == 0) { - sizeCache = 10; - oldNs = (xmlNsPtr *) xmlMalloc(sizeCache * - sizeof(xmlNsPtr)); - if (oldNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - return(-1); - } - newNs = (xmlNsPtr *) xmlMalloc(sizeCache * - sizeof(xmlNsPtr)); - if (newNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(oldNs); - return(-1); - } - } - for (i = 0;i < nbCache;i++) { - if (oldNs[i] == attr->ns) { - attr->ns = newNs[i]; + for (i = 0; i < nbCache; i++) { + if (cache[i].oldNs == attr->ns) { + attr->ns = cache[i].newNs; break; } } @@ -6343,32 +6435,33 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { /* * OK we need to recreate a new namespace definition */ - n = xmlNewReconciledNs(doc, tree, attr->ns); - if (n != NULL) { /* :-( what if else ??? */ + n = xmlNewReconciledNs(tree, attr->ns); + if (n == NULL) { + ret = -1; + } else { /* * check if we need to grow the cache buffers. */ if (sizeCache <= nbCache) { - sizeCache *= 2; - oldNs = (xmlNsPtr *) xmlRealloc(oldNs, - sizeCache * sizeof(xmlNsPtr)); - if (oldNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(newNs); - return(-1); - } - newNs = (xmlNsPtr *) xmlRealloc(newNs, - sizeCache * sizeof(xmlNsPtr)); - if (newNs == NULL) { - xmlTreeErrMemory("fixing namespaces"); - xmlFree(oldNs); - return(-1); - } + xmlNsCache *tmp; + size_t newSize = sizeCache ? + sizeCache * 2 : 10; + + tmp = xmlRealloc(cache, + newSize * sizeof(tmp[0])); + if (tmp == NULL) { + ret = -1; + } else { + cache = tmp; + sizeCache = newSize; + } + } + if (nbCache < sizeCache) { + cache[nbCache].newNs = n; + cache[nbCache++].oldNs = attr->ns; } - newNs[nbCache] = n; - oldNs[nbCache++] = attr->ns; - attr->ns = n; } + attr->ns = n; } } attr = attr->next; @@ -6404,10 +6497,8 @@ xmlReconciliateNs(xmlDocPtr doc, xmlNodePtr tree) { } else break; } - if (oldNs != NULL) - xmlFree(oldNs); - if (newNs != NULL) - xmlFree(newNs); + if (cache != NULL) + xmlFree(cache); return(ret); } #endif /* LIBXML_TREE_ENABLED */ @@ -6469,7 +6560,11 @@ xmlGetPropNodeInternal(const xmlNode *node, const xmlChar *name, */ if ((node->ns != NULL) && (node->ns->prefix != NULL)) { tmpstr = xmlStrdup(node->ns->prefix); + if (tmpstr == NULL) + return(NULL); tmpstr = xmlStrcat(tmpstr, BAD_CAST ":"); + if (tmpstr == NULL) + return(NULL); tmpstr = xmlStrcat(tmpstr, node->name); if (tmpstr == NULL) return(NULL); @@ -6545,28 +6640,7 @@ xmlGetPropNodeValueInternal(const xmlAttr *prop) if (prop == NULL) return(NULL); if (prop->type == XML_ATTRIBUTE_NODE) { - /* - * Note that we return at least the empty string. - * TODO: Do we really always want that? - */ - if (prop->children != NULL) { - if ((prop->children->next == NULL) && - ((prop->children->type == XML_TEXT_NODE) || - (prop->children->type == XML_CDATA_SECTION_NODE))) - { - /* - * Optimization for the common case: only 1 text node. - */ - return(xmlStrdup(prop->children->content)); - } else { - xmlChar *ret; - - ret = xmlNodeListGetString(prop->doc, prop->children, 1); - if (ret != NULL) - return(ret); - } - } - return(xmlStrdup((xmlChar *)"")); + return(xmlNodeGetContent((xmlNodePtr) prop)); } else if (prop->type == XML_ATTRIBUTE_DECL) { return(xmlStrdup(((xmlAttributePtr)prop)->defaultValue)); } @@ -6580,10 +6654,11 @@ xmlGetPropNodeValueInternal(const xmlAttr *prop) * * Search an attribute associated to a node * This function also looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. + * default declaration values. * * Returns the attribute or the attribute declaration or NULL if - * neither was found. + * neither was found. Also returns NULL if a memory allocation failed + * making this function unreliable. */ xmlAttrPtr xmlHasProp(const xmlNode *node, const xmlChar *name) { @@ -6602,7 +6677,6 @@ xmlHasProp(const xmlNode *node, const xmlChar *name) { } prop = prop->next; } - if (!xmlCheckDTD) return(NULL); /* * Check if there is a default declaration in the internal @@ -6634,16 +6708,53 @@ xmlHasProp(const xmlNode *node, const xmlChar *name) { * This attribute has to be anchored in the namespace specified. * This does the entity substitution. * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. + * default declaration values. * Note that a namespace of NULL indicates to use the default namespace. * - * Returns the attribute or the attribute declaration or NULL - * if neither was found. + * Returns the attribute or the attribute declaration or NULL if + * neither was found. Also returns NULL if a memory allocation failed + * making this function unreliable. */ xmlAttrPtr xmlHasNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace) { - return(xmlGetPropNodeInternal(node, name, nameSpace, xmlCheckDTD)); + return(xmlGetPropNodeInternal(node, name, nameSpace, 1)); +} + +/** + * xmlNodeGetAttrValue: + * @node: the node + * @name: the attribute name + * @nsUri: the URI of the namespace + * @out: the returned string + * + * Search and get the value of an attribute associated to a node + * This attribute has to be anchored in the namespace specified. + * This does the entity substitution. The returned value must be + * freed by the caller. + * + * Available since 2.13.0. + * + * Returns 0 on success, 1 if no attribute was found, -1 if a + * memory allocation failed. + */ +int +xmlNodeGetAttrValue(const xmlNode *node, const xmlChar *name, + const xmlChar *nsUri, xmlChar **out) { + xmlAttrPtr prop; + + if (out == NULL) + return(1); + *out = NULL; + + prop = xmlGetPropNodeInternal(node, name, nsUri, 0); + if (prop == NULL) + return(1); + + *out = xmlGetPropNodeValueInternal(prop); + if (*out == NULL) + return(-1); + return(0); } /** @@ -6654,13 +6765,17 @@ xmlHasNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace) * Search and get the value of an attribute associated to a node * This does the entity substitution. * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. - * NOTE: this function acts independently of namespaces associated + * default declaration values. + * + * NOTE: This function acts independently of namespaces associated * to the attribute. Use xmlGetNsProp() or xmlGetNoNsProp() * for namespace aware processing. * - * Returns the attribute value or NULL if not found. - * It's up to the caller to free the memory with xmlFree(). + * NOTE: This function doesn't allow to distinguish malloc failures from + * missing attributes. It's more robust to use xmlNodeGetAttrValue. + * + * Returns the attribute value or NULL if not found or a memory allocation + * failed. It's up to the caller to free the memory with xmlFree(). */ xmlChar * xmlGetProp(const xmlNode *node, const xmlChar *name) { @@ -6680,18 +6795,21 @@ xmlGetProp(const xmlNode *node, const xmlChar *name) { * Search and get the value of an attribute associated to a node * This does the entity substitution. * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. + * default declaration values. * This function is similar to xmlGetProp except it will accept only * an attribute in no namespace. * - * Returns the attribute value or NULL if not found. - * It's up to the caller to free the memory with xmlFree(). + * NOTE: This function doesn't allow to distinguish malloc failures from + * missing attributes. It's more robust to use xmlNodeGetAttrValue. + * + * Returns the attribute value or NULL if not found or a memory allocation + * failed. It's up to the caller to free the memory with xmlFree(). */ xmlChar * xmlGetNoNsProp(const xmlNode *node, const xmlChar *name) { xmlAttrPtr prop; - prop = xmlGetPropNodeInternal(node, name, NULL, xmlCheckDTD); + prop = xmlGetPropNodeInternal(node, name, NULL, 1); if (prop == NULL) return(NULL); return(xmlGetPropNodeValueInternal(prop)); @@ -6707,16 +6825,19 @@ xmlGetNoNsProp(const xmlNode *node, const xmlChar *name) { * This attribute has to be anchored in the namespace specified. * This does the entity substitution. * This function looks in DTD attribute declaration for #FIXED or - * default declaration values unless DTD use has been turned off. + * default declaration values. * - * Returns the attribute value or NULL if not found. - * It's up to the caller to free the memory with xmlFree(). + * NOTE: This function doesn't allow to distinguish malloc failures from + * missing attributes. It's more robust to use xmlNodeGetAttrValue. + * + * Returns the attribute value or NULL if not found or a memory allocation + * failed. It's up to the caller to free the memory with xmlFree(). */ xmlChar * xmlGetNsProp(const xmlNode *node, const xmlChar *name, const xmlChar *nameSpace) { xmlAttrPtr prop; - prop = xmlGetPropNodeInternal(node, name, nameSpace, xmlCheckDTD); + prop = xmlGetPropNodeInternal(node, name, nameSpace, 1); if (prop == NULL) return(NULL); return(xmlGetPropNodeValueInternal(prop)); @@ -6739,7 +6860,7 @@ xmlUnsetProp(xmlNodePtr node, const xmlChar *name) { prop = xmlGetPropNodeInternal(node, name, NULL, 0); if (prop == NULL) return(-1); - xmlUnlinkNode((xmlNodePtr) prop); + xmlUnlinkNodeInternal((xmlNodePtr) prop); xmlFreeProp(prop); return(0); } @@ -6757,10 +6878,11 @@ int xmlUnsetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name) { xmlAttrPtr prop; - prop = xmlGetPropNodeInternal(node, name, (ns != NULL) ? ns->href : NULL, 0); + prop = xmlGetPropNodeInternal(node, name, + (ns != NULL) ? ns->href : NULL, 0); if (prop == NULL) return(-1); - xmlUnlinkNode((xmlNodePtr) prop); + xmlUnlinkNodeInternal((xmlNodePtr) prop); xmlFreeProp(prop); return(0); } @@ -6783,8 +6905,10 @@ xmlUnsetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name) { */ xmlAttrPtr xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { - int len; - const xmlChar *nqname; + xmlNsPtr ns = NULL; + const xmlChar *localname; + xmlChar *prefix; + int res; if ((node == NULL) || (name == NULL) || (node->type != XML_ELEMENT_NODE)) return(NULL); @@ -6792,16 +6916,19 @@ xmlSetProp(xmlNodePtr node, const xmlChar *name, const xmlChar *value) { /* * handle QNames */ - nqname = xmlSplitQName3(name, &len); - if (nqname != NULL) { - xmlNsPtr ns; - xmlChar *prefix = xmlStrndup(name, len); - ns = xmlSearchNs(node->doc, node, prefix); - if (prefix != NULL) - xmlFree(prefix); - if (ns != NULL) - return(xmlSetNsProp(node, ns, nqname, value)); + localname = xmlSplitQName4(name, &prefix); + if (localname == NULL) + return(NULL); + + if (prefix != NULL) { + res = xmlSearchNsSafe(node, prefix, &ns); + xmlFree(prefix); + if (res < 0) + return(NULL); + if (ns != NULL) + return(xmlSetNsProp(node, ns, localname, value)); } + return(xmlSetNsProp(node, NULL, name, value)); } @@ -6825,11 +6952,22 @@ xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, if (ns && (ns->href == NULL)) return(NULL); - prop = xmlGetPropNodeInternal(node, name, (ns != NULL) ? ns->href : NULL, 0); + if (name == NULL) + return(NULL); + prop = xmlGetPropNodeInternal(node, name, + (ns != NULL) ? ns->href : NULL, 0); if (prop != NULL) { + xmlNodePtr children = NULL; + /* * Modify the attribute's value. */ + if (value != NULL) { + children = xmlNewDocText(node->doc, value); + if (children == NULL) + return(NULL); + } + if (prop->atype == XML_ATTRIBUTE_ID) { xmlRemoveID(node->doc, prop); prop->atype = XML_ATTRIBUTE_ID; @@ -6842,7 +6980,7 @@ xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, if (value != NULL) { xmlNodePtr tmp; - prop->children = xmlNewDocText(node->doc, value); + prop->children = children; prop->last = NULL; tmp = prop->children; while (tmp != NULL) { @@ -6852,8 +6990,10 @@ xmlSetNsProp(xmlNodePtr node, xmlNsPtr ns, const xmlChar *name, tmp = tmp->next; } } - if (prop->atype == XML_ATTRIBUTE_ID) - xmlAddID(NULL, node->doc, value, prop); + if ((prop->atype == XML_ATTRIBUTE_ID) && + (xmlAddIDSafe(prop, value) < 0)) { + return(NULL); + } return(prop); } /* @@ -6912,33 +7052,25 @@ xmlIsBlankNode(const xmlNode *node) { * @content: the content * @len: @content length * - * Concat the given string at the end of the existing node content + * Concat the given string at the end of the existing node content. + * + * If @len is -1, the string length will be calculated. * * Returns -1 in case of error, 0 otherwise */ int xmlTextConcat(xmlNodePtr node, const xmlChar *content, int len) { - if (node == NULL) return(-1); + if (node == NULL) + return(-1); if ((node->type != XML_TEXT_NODE) && (node->type != XML_CDATA_SECTION_NODE) && (node->type != XML_COMMENT_NODE) && - (node->type != XML_PI_NODE)) { + (node->type != XML_PI_NODE)) return(-1); - } - /* need to check if content is currently in the dictionary */ - if ((node->content == (xmlChar *) &(node->properties)) || - ((node->doc != NULL) && (node->doc->dict != NULL) && - xmlDictOwns(node->doc->dict, node->content))) { - node->content = xmlStrncatNew(node->content, content, len); - } else { - node->content = xmlStrncat(node->content, content, len); - } - node->properties = NULL; - if (node->content == NULL) - return(-1); - return(0); + + return(xmlTextAddContent(node, content, len)); } /************************************************************************ @@ -6958,16 +7090,13 @@ xmlBufferCreate(void) { xmlBufferPtr ret; ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer)); - if (ret == NULL) { - xmlTreeErrMemory("creating buffer"); + if (ret == NULL) return(NULL); - } ret->use = 0; ret->size = xmlDefaultBufferSize; ret->alloc = xmlBufferAllocScheme; ret->content = (xmlChar *) xmlMallocAtomic(ret->size); if (ret->content == NULL) { - xmlTreeErrMemory("creating buffer"); xmlFree(ret); return(NULL); } @@ -6990,17 +7119,14 @@ xmlBufferCreateSize(size_t size) { if (size >= UINT_MAX) return(NULL); ret = (xmlBufferPtr) xmlMalloc(sizeof(xmlBuffer)); - if (ret == NULL) { - xmlTreeErrMemory("creating buffer"); + if (ret == NULL) return(NULL); - } ret->use = 0; ret->alloc = xmlBufferAllocScheme; ret->size = (size ? size + 1 : 0); /* +1 for ending null */ if (ret->size){ ret->content = (xmlChar *) xmlMallocAtomic(ret->size); if (ret->content == NULL) { - xmlTreeErrMemory("creating buffer"); xmlFree(ret); return(NULL); } @@ -7178,10 +7304,8 @@ xmlBufferGrow(xmlBufferPtr buf, unsigned int len) { if (len < buf->size - buf->use) return(0); - if (len >= UINT_MAX - buf->use) { - xmlTreeErrMemory("growing buffer past UINT_MAX"); + if (len >= UINT_MAX - buf->use) return(-1); - } if (buf->size > (size_t) len) { size = buf->size > UINT_MAX / 2 ? UINT_MAX : buf->size * 2; @@ -7194,18 +7318,14 @@ xmlBufferGrow(xmlBufferPtr buf, unsigned int len) { size_t start_buf = buf->content - buf->contentIO; newbuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + size); - if (newbuf == NULL) { - xmlTreeErrMemory("growing buffer"); + if (newbuf == NULL) return(-1); - } buf->contentIO = newbuf; buf->content = newbuf + start_buf; } else { newbuf = (xmlChar *) xmlRealloc(buf->content, size); - if (newbuf == NULL) { - xmlTreeErrMemory("growing buffer"); + if (newbuf == NULL) return(-1); - } buf->content = newbuf; } buf->size = size; @@ -7295,10 +7415,8 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size) if (size < buf->size) return 1; - if (size > UINT_MAX - 10) { - xmlTreeErrMemory("growing buffer past UINT_MAX"); + if (size > UINT_MAX - 10) return 0; - } /* figure out new size */ switch (buf->alloc){ @@ -7306,19 +7424,17 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size) case XML_BUFFER_ALLOC_DOUBLEIT: /*take care of empty case*/ if (buf->size == 0) - newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10); + newSize = size + 10; else newSize = buf->size; while (size > newSize) { - if (newSize > UINT_MAX / 2) { - xmlTreeErrMemory("growing buffer"); + if (newSize > UINT_MAX / 2) return 0; - } newSize *= 2; } break; case XML_BUFFER_ALLOC_EXACT: - newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10); + newSize = size + 10; break; case XML_BUFFER_ALLOC_HYBRID: if (buf->use < BASE_BUFFER_SIZE) @@ -7326,17 +7442,15 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size) else { newSize = buf->size; while (size > newSize) { - if (newSize > UINT_MAX / 2) { - xmlTreeErrMemory("growing buffer"); + if (newSize > UINT_MAX / 2) return 0; - } newSize *= 2; } } break; default: - newSize = (size > UINT_MAX - 10 ? UINT_MAX : size + 10); + newSize = size + 10; break; } @@ -7351,10 +7465,8 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size) buf->size += start_buf; } else { rebuf = (xmlChar *) xmlRealloc(buf->contentIO, start_buf + newSize); - if (rebuf == NULL) { - xmlTreeErrMemory("growing buffer"); + if (rebuf == NULL) return 0; - } buf->contentIO = rebuf; buf->content = rebuf + start_buf; } @@ -7378,10 +7490,8 @@ xmlBufferResize(xmlBufferPtr buf, unsigned int size) rebuf[buf->use] = 0; } } - if (rebuf == NULL) { - xmlTreeErrMemory("growing buffer"); + if (rebuf == NULL) return 0; - } buf->content = rebuf; } buf->size = newSize; @@ -7421,15 +7531,11 @@ xmlBufferAdd(xmlBufferPtr buf, const xmlChar *str, int len) { /* Note that both buf->size and buf->use can be zero here. */ if ((unsigned) len >= buf->size - buf->use) { - if ((unsigned) len >= UINT_MAX - buf->use) { - xmlTreeErrMemory("growing buffer past UINT_MAX"); + if ((unsigned) len >= UINT_MAX - buf->use) return XML_ERR_NO_MEMORY; - } needSize = buf->use + len + 1; - if (!xmlBufferResize(buf, needSize)){ - xmlTreeErrMemory("growing buffer"); + if (!xmlBufferResize(buf, needSize)) return XML_ERR_NO_MEMORY; - } } memmove(&buf->content[buf->use], str, len); @@ -7486,15 +7592,11 @@ xmlBufferAddHead(xmlBufferPtr buf, const xmlChar *str, int len) { } /* Note that both buf->size and buf->use can be zero here. */ if ((unsigned) len >= buf->size - buf->use) { - if ((unsigned) len >= UINT_MAX - buf->use) { - xmlTreeErrMemory("growing buffer past UINT_MAX"); + if ((unsigned) len >= UINT_MAX - buf->use) return(-1); - } needSize = buf->use + len + 1; - if (!xmlBufferResize(buf, needSize)){ - xmlTreeErrMemory("growing buffer"); - return XML_ERR_NO_MEMORY; - } + if (!xmlBufferResize(buf, needSize)) + return(-1); } memmove(&buf->content[len], &buf->content[0], buf->use); @@ -7647,6 +7749,8 @@ xmlSetDocCompressMode (xmlDocPtr doc, int mode) { /** * xmlGetCompressMode: * + * DEPRECATED: Use xmlGetDocCompressMode + * * get the default compression mode used, ZLIB based. * Returns 0 (uncompressed) to 9 (max compression) */ @@ -7660,6 +7764,8 @@ xmlGetCompressMode(void) * xmlSetCompressMode: * @mode: the compression ratio * + * DEPRECATED: Use xmlSetDocCompressMode + * * set the default compression mode used, ZLIB based * Correct values: 0 (uncompressed) to 9 (max compression) */ @@ -7767,10 +7873,8 @@ xmlDOMWrapNsMapAddItem(xmlNsMapPtr *nsmap, int position, * Create the ns-map. */ map = (xmlNsMapPtr) xmlMalloc(sizeof(struct xmlNsMap)); - if (map == NULL) { - xmlTreeErrMemory("allocating namespace map"); - return (NULL); - } + if (map == NULL) + return(NULL); memset(map, 0, sizeof(struct xmlNsMap)); *nsmap = map; } @@ -7787,10 +7891,8 @@ xmlDOMWrapNsMapAddItem(xmlNsMapPtr *nsmap, int position, * Create a new item. */ ret = (xmlNsMapItemPtr) xmlMalloc(sizeof(struct xmlNsMapItem)); - if (ret == NULL) { - xmlTreeErrMemory("allocating namespace map item"); - return (NULL); - } + if (ret == NULL) + return(NULL); memset(ret, 0, sizeof(struct xmlNsMapItem)); } @@ -7882,10 +7984,8 @@ xmlDOMWrapNewCtxt(void) xmlDOMWrapCtxtPtr ret; ret = xmlMalloc(sizeof(xmlDOMWrapCtxt)); - if (ret == NULL) { - xmlTreeErrMemory("allocating DOM-wrapper context"); + if (ret == NULL) return (NULL); - } memset(ret, 0, sizeof(xmlDOMWrapCtxt)); return (ret); } @@ -8001,39 +8101,6 @@ xmlDOMWrapNSNormGatherInScopeNs(xmlNsMapPtr *map, return (0); } -/* -* XML_TREE_ADOPT_STR: If we have a dest-dict, put @str in the dict; -* otherwise copy it, when it was in the source-dict. -*/ -#define XML_TREE_ADOPT_STR(str) \ - if (adoptStr && (str != NULL)) { \ - if (destDoc->dict) { \ - const xmlChar *old = str; \ - str = xmlDictLookup(destDoc->dict, str, -1); \ - if ((sourceDoc == NULL) || (sourceDoc->dict == NULL) || \ - (!xmlDictOwns(sourceDoc->dict, old))) \ - xmlFree((char *)old); \ - } else if ((sourceDoc) && (sourceDoc->dict) && \ - xmlDictOwns(sourceDoc->dict, str)) { \ - str = BAD_CAST xmlStrdup(str); \ - } \ - } - -/* -* XML_TREE_ADOPT_STR_2: If @str was in the source-dict, then -* put it in dest-dict or copy it. -*/ -#define XML_TREE_ADOPT_STR_2(str) \ - if (adoptStr && (str != NULL) && (sourceDoc != NULL) && \ - (sourceDoc->dict != NULL) && \ - xmlDictOwns(sourceDoc->dict, cur->content)) { \ - if (destDoc->dict) \ - cur->content = (xmlChar *) \ - xmlDictLookup(destDoc->dict, cur->content, -1); \ - else \ - cur->content = xmlStrdup(BAD_CAST cur->content); \ - } - /* * xmlDOMWrapNSNormAddNsMapItem2: * @@ -8045,23 +8112,18 @@ static int xmlDOMWrapNSNormAddNsMapItem2(xmlNsPtr **list, int *size, int *number, xmlNsPtr oldNs, xmlNsPtr newNs) { - if (*list == NULL) { - *list = (xmlNsPtr *) xmlMalloc(6 * sizeof(xmlNsPtr)); - if (*list == NULL) { - xmlTreeErrMemory("alloc ns map item"); - return(-1); - } - *size = 3; - *number = 0; - } else if ((*number) >= (*size)) { - *size *= 2; - *list = (xmlNsPtr *) xmlRealloc(*list, - (*size) * 2 * sizeof(xmlNsPtr)); - if (*list == NULL) { - xmlTreeErrMemory("realloc ns map item"); - return(-1); - } + if (*number >= *size) { + xmlNsPtr *tmp; + size_t newSize; + + newSize = *size ? *size * 2 : 3; + tmp = xmlRealloc(*list, newSize * 2 * sizeof(tmp[0])); + if (tmp == NULL) + return(-1); + *list = tmp; + *size = newSize; } + (*list)[2 * (*number)] = oldNs; (*list)[2 * (*number) +1] = newNs; (*number)++; @@ -8090,7 +8152,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr node, int options ATTRIBUTE_UNUSED) { xmlNsPtr *list = NULL; - int sizeList, nbList, i, j; + int sizeList = 0, nbList = 0, ret = 0, i, j; xmlNsPtr ns; if ((node == NULL) || (doc == NULL) || (node->doc != doc)) @@ -8106,7 +8168,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, case XML_ENTITY_REF_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: - xmlUnlinkNode(node); + xmlUnlinkNodeInternal(node); return (0); case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: @@ -8114,7 +8176,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, default: return (1); } - xmlUnlinkNode(node); + xmlUnlinkNodeInternal(node); /* * Save out-of-scope ns-references in doc->oldNs. */ @@ -8126,7 +8188,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, do { if (xmlDOMWrapNSNormAddNsMapItem2(&list, &sizeList, &nbList, ns, ns) == -1) - goto internal_error; + ret = -1; ns = ns->next; } while (ns != NULL); } @@ -8156,7 +8218,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, ns = xmlDOMWrapStoreNs(doc, node->ns->href, node->ns->prefix); if (ns == NULL) - goto internal_error; + ret = -1; } if (ns != NULL) { /* @@ -8164,7 +8226,7 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, */ if (xmlDOMWrapNSNormAddNsMapItem2(&list, &sizeList, &nbList, node->ns, ns) == -1) - goto internal_error; + ret = -1; } node->ns = ns; } @@ -8189,19 +8251,22 @@ xmlDOMWrapRemoveNode(xmlDOMWrapCtxtPtr ctxt, xmlDocPtr doc, if (node->next != NULL) node = node->next; else { + int type = node->type; + node = node->parent; - goto next_sibling; + if ((type == XML_ATTRIBUTE_NODE) && + (node != NULL) && + (node->children != NULL)) { + node = node->children; + } else { + goto next_sibling; + } } } while (node != NULL); if (list != NULL) xmlFree(list); - return (0); - -internal_error: - if (list != NULL) - xmlFree(list); - return (-1); + return (ret); } /* @@ -8299,8 +8364,7 @@ xmlSearchNsByNamespaceStrict(xmlDocPtr doc, xmlNodePtr node, out = prev; prev = cur; } - } else if ((cur->type == XML_ENTITY_NODE) || - (cur->type == XML_ENTITY_DECL)) + } else if (cur->type == XML_ENTITY_DECL) return (0); cur = cur->parent; } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur)); @@ -8362,8 +8426,7 @@ xmlSearchNsByPrefixStrict(xmlDocPtr doc, xmlNodePtr node, ns = ns->next; } while (ns != NULL); } - } else if ((cur->type == XML_ENTITY_NODE) || - (cur->type == XML_ENTITY_DECL)) + } else if (cur->type == XML_ENTITY_DECL) return (0); cur = cur->parent; } while ((cur != NULL) && (cur->doc != (xmlDocPtr) cur)); @@ -8547,7 +8610,6 @@ xmlDOMWrapNSNormAcquireNormalizedNs(xmlDocPtr doc, */ if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns, tmpns, XML_TREE_NSMAP_DOC) == NULL) { - xmlFreeNs(tmpns); return (-1); } *retNs = tmpns; @@ -8577,7 +8639,6 @@ xmlDOMWrapNSNormAcquireNormalizedNs(xmlDocPtr doc, } } if (xmlDOMWrapNsMapAddItem(nsMap, -1, ns, tmpns, depth) == NULL) { - xmlFreeNs(tmpns); return (-1); } *retNs = tmpns; @@ -8622,7 +8683,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, int optRemoveRedundantNS = ((xmlDOMReconcileNSOptions) options & XML_DOM_RECONNS_REMOVEREDUND) ? 1 : 0; xmlNsPtr *listRedund = NULL; - int sizeRedund = 0, nbRedund = 0, ret, i, j; + int sizeRedund = 0, nbRedund = 0, ret = 0, i, j; if ((elem == NULL) || (elem->doc == NULL) || (elem->type != XML_ELEMENT_NODE)) @@ -8651,7 +8712,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, */ if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, elem->parent) == -1) - goto internal_error; + ret = -1; } parnsdone = 1; } @@ -8673,16 +8734,18 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, * Add it to the list of redundant ns-decls. */ if (xmlDOMWrapNSNormAddNsMapItem2(&listRedund, - &sizeRedund, &nbRedund, ns, mi->newNs) == -1) - goto internal_error; - /* - * Remove the ns-decl from the element-node. - */ - if (prevns) - prevns->next = ns->next; - else - cur->nsDef = ns->next; - goto next_ns_decl; + &sizeRedund, &nbRedund, ns, mi->newNs) == -1) { + ret = -1; + } else { + /* + * Remove the ns-decl from the element-node. + */ + if (prevns) + prevns->next = ns->next; + else + cur->nsDef = ns->next; + goto next_ns_decl; + } } } } @@ -8712,7 +8775,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, */ if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns, depth) == NULL) - goto internal_error; + ret = -1; prevns = ns; next_ns_decl: @@ -8732,7 +8795,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, ((xmlNodePtr) elem->parent->doc != elem->parent)) { if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, elem->parent) == -1) - goto internal_error; + ret = -1; } parnsdone = 1; } @@ -8771,7 +8834,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, &nsMap, depth, ancestorsOnly, (cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1) - goto internal_error; + ret = -1; cur->ns = ns; ns_end: @@ -8831,11 +8894,6 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, } } while (cur != NULL); - ret = 0; - goto exit; -internal_error: - ret = -1; -exit: if (listRedund) { for (i = 0, j = 0; i < nbRedund; i++, j += 2) { xmlFreeNs(listRedund[j]); @@ -8871,7 +8929,7 @@ xmlDOMWrapReconcileNamespaces(xmlDOMWrapCtxtPtr ctxt ATTRIBUTE_UNUSED, */ static int xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, - xmlDocPtr sourceDoc, + xmlDocPtr sourceDoc ATTRIBUTE_UNUSED, xmlNodePtr node, xmlDocPtr destDoc, xmlNodePtr destParent, @@ -8882,21 +8940,12 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, xmlNsMapPtr nsMap = NULL; xmlNsMapItemPtr mi; xmlNsPtr ns = NULL; - int depth = -1, adoptStr = 1; + int depth = -1; /* gather @parent's ns-decls. */ int parnsdone; /* @ancestorsOnly should be set per option. */ int ancestorsOnly = 0; - /* - * Optimize string adoption for equal or none dicts. - */ - if ((sourceDoc != NULL) && - (sourceDoc->dict == destDoc->dict)) - adoptStr = 0; - else - adoptStr = 1; - /* * Get the ns-map from the context if available. */ @@ -8916,40 +8965,21 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, parnsdone = 0; cur = node; - if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL)) - goto internal_error; while (cur != NULL) { - /* - * Paranoid source-doc sanity check. - */ - if (cur->doc != sourceDoc) { - /* - * We'll assume XIncluded nodes if the doc differs. - * TODO: Do we need to reconciliate XIncluded nodes? - * This here skips XIncluded nodes and tries to handle - * broken sequences. - */ - if (cur->next == NULL) - goto leave_node; - do { - cur = cur->next; - if ((cur->type == XML_XINCLUDE_END) || - (cur->doc == node->doc)) - break; - } while (cur->next != NULL); + if (cur->doc != destDoc) { + if (xmlNodeSetDoc(cur, destDoc) < 0) + ret = -1; + } - if (cur->doc != node->doc) - goto leave_node; - } - cur->doc = destDoc; switch (cur->type) { case XML_XINCLUDE_START: case XML_XINCLUDE_END: /* * TODO */ - return (-1); + ret = -1; + goto leave_node; case XML_ELEMENT_NODE: curElem = cur; depth++; @@ -8970,14 +9000,12 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, */ if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, destParent) == -1) - goto internal_error; + ret = -1; parnsdone = 1; } for (ns = cur->nsDef; ns != NULL; ns = ns->next) { /* * NOTE: ns->prefix and ns->href are never in the dict. - * XML_TREE_ADOPT_STR(ns->prefix) - * XML_TREE_ADOPT_STR(ns->href) */ /* * Does it shadow any ns-decl? @@ -8999,7 +9027,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, */ if (xmlDOMWrapNsMapAddItem(&nsMap, -1, ns, ns, depth) == NULL) - goto internal_error; + ret = -1; } } /* Falls through. */ @@ -9011,7 +9039,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, if (! parnsdone) { if (xmlDOMWrapNSNormGatherInScopeNs(&nsMap, destParent) == -1) - goto internal_error; + ret = -1; parnsdone = 1; } /* @@ -9045,7 +9073,7 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, */ if (xmlDOMWrapNsMapAddItem(&nsMap, -1, cur->ns, ns, XML_TREE_NSMAP_CUSTOM) == NULL) - goto internal_error; + ret = -1; cur->ns = ns; } else { /* @@ -9059,15 +9087,11 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, ancestorsOnly, /* ns-decls must be prefixed for attributes. */ (cur->type == XML_ATTRIBUTE_NODE) ? 1 : 0) == -1) - goto internal_error; + ret = -1; cur->ns = ns; } + ns_end: - /* - * Further node properties. - * TODO: Is this all? - */ - XML_TREE_ADOPT_STR(cur->name) if (cur->type == XML_ELEMENT_NODE) { cur->psvi = NULL; cur->line = 0; @@ -9082,55 +9106,16 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, cur = (xmlNodePtr) cur->properties; continue; } - } else { - /* - * Attributes. - */ - if ((sourceDoc != NULL) && - (((xmlAttrPtr) cur)->atype == XML_ATTRIBUTE_ID)) - { - xmlRemoveID(sourceDoc, (xmlAttrPtr) cur); - } - ((xmlAttrPtr) cur)->atype = 0; - ((xmlAttrPtr) cur)->psvi = NULL; } break; case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: - /* - * This puts the content in the dest dict, only if - * it was previously in the source dict. - */ - XML_TREE_ADOPT_STR_2(cur->content) - goto leave_node; - case XML_ENTITY_REF_NODE: - /* - * Remove reference to the entity-node. - */ - cur->content = NULL; - cur->children = NULL; - cur->last = NULL; - if ((destDoc->intSubset) || (destDoc->extSubset)) { - xmlEntityPtr ent; - /* - * Assign new entity-node if available. - */ - ent = xmlGetDocEntity(destDoc, cur->name); - if (ent != NULL) { - cur->content = ent->content; - cur->children = (xmlNodePtr) ent; - cur->last = (xmlNodePtr) ent; - } - } - goto leave_node; case XML_PI_NODE: - XML_TREE_ADOPT_STR(cur->name) - XML_TREE_ADOPT_STR_2(cur->content) - break; case XML_COMMENT_NODE: - break; + case XML_ENTITY_REF_NODE: + goto leave_node; default: - goto internal_error; + ret = -1; } /* * Walk the tree. @@ -9181,12 +9166,6 @@ xmlDOMWrapAdoptBranch(xmlDOMWrapCtxtPtr ctxt, } } - goto exit; - -internal_error: - ret = -1; - -exit: /* * Cleanup. */ @@ -9248,7 +9227,7 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, int options ATTRIBUTE_UNUSED) { int ret = 0; - xmlNodePtr cur, curElem = NULL; + xmlNodePtr cur, cloneElem = NULL; xmlNsMapPtr nsMap = NULL; xmlNsMapItemPtr mi; xmlNsPtr ns; @@ -9266,7 +9245,8 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, xmlNsPtr cloneNs = NULL, *cloneNsDefSlot = NULL; xmlDictPtr dict; /* The destination dict */ - if ((node == NULL) || (resNode == NULL) || (destDoc == NULL)) + if ((node == NULL) || (resNode == NULL) || (destDoc == NULL) || + ((destParent != NULL) && (destParent->doc != destDoc))) return(-1); /* * TODO: Initially we support only element-nodes. @@ -9298,9 +9278,6 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, *resNode = NULL; cur = node; - if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL)) - return(-1); - while (cur != NULL) { if (cur->doc != sourceDoc) { /* @@ -9328,15 +9305,12 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, case XML_PI_NODE: case XML_DOCUMENT_FRAG_NODE: case XML_ENTITY_REF_NODE: - case XML_ENTITY_NODE: /* * Nodes of xmlNode structure. */ clone = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); - if (clone == NULL) { - xmlTreeErrMemory("xmlDOMWrapCloneNode(): allocating a node"); + if (clone == NULL) goto internal_error; - } memset(clone, 0, sizeof(xmlNode)); /* * Set hierarchical links. @@ -9348,6 +9322,7 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, clone->prev = prevClone; } else parentClone->children = clone; + parentClone->last = clone; } else resultClone = clone; @@ -9358,10 +9333,8 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, */ /* Use xmlRealloc to avoid -Warray-bounds warning */ clone = (xmlNodePtr) xmlRealloc(NULL, sizeof(xmlAttr)); - if (clone == NULL) { - xmlTreeErrMemory("xmlDOMWrapCloneNode(): allocating an attr-node"); + if (clone == NULL) goto internal_error; - } memset(clone, 0, sizeof(xmlAttr)); /* * Set hierarchical links. @@ -9402,7 +9375,12 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, else if (cur->name == xmlStringComment) clone->name = xmlStringComment; else if (cur->name != NULL) { - DICT_CONST_COPY(cur->name, clone->name); + if (dict != NULL) + clone->name = xmlDictLookup(dict, cur->name, -1); + else + clone->name = xmlStrdup(cur->name); + if (clone->name == NULL) + goto internal_error; } switch (cur->type) { @@ -9413,7 +9391,7 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, */ return (-1); case XML_ELEMENT_NODE: - curElem = cur; + cloneElem = clone; depth++; /* * Namespace declarations. @@ -9439,18 +9417,25 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, * Create a new xmlNs. */ cloneNs = (xmlNsPtr) xmlMalloc(sizeof(xmlNs)); - if (cloneNs == NULL) { - xmlTreeErrMemory("xmlDOMWrapCloneNode(): " - "allocating namespace"); - return(-1); - } + if (cloneNs == NULL) + goto internal_error; memset(cloneNs, 0, sizeof(xmlNs)); cloneNs->type = XML_LOCAL_NAMESPACE; - if (ns->href != NULL) + if (ns->href != NULL) { cloneNs->href = xmlStrdup(ns->href); - if (ns->prefix != NULL) + if (cloneNs->href == NULL) { + xmlFreeNs(cloneNs); + goto internal_error; + } + } + if (ns->prefix != NULL) { cloneNs->prefix = xmlStrdup(ns->prefix); + if (cloneNs->prefix == NULL) { + xmlFreeNs(cloneNs); + goto internal_error; + } + } *cloneNsDefSlot = cloneNs; cloneNsDefSlot = &(cloneNs->next); @@ -9496,15 +9481,18 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, /* IDs will be processed further down. */ /* cur->ns will be processed further down. */ break; + case XML_PI_NODE: + case XML_COMMENT_NODE: case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: /* * Note that this will also cover the values of attributes. */ - DICT_COPY(cur->content, clone->content); - goto leave_node; - case XML_ENTITY_NODE: - /* TODO: What to do here? */ + if (cur->content != NULL) { + clone->content = xmlStrdup(cur->content); + if (clone->content == NULL) + goto internal_error; + } goto leave_node; case XML_ENTITY_REF_NODE: if (sourceDoc != destDoc) { @@ -9530,12 +9518,6 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, clone->last = cur->last; } goto leave_node; - case XML_PI_NODE: - DICT_COPY(cur->content, clone->content); - goto leave_node; - case XML_COMMENT_NODE: - DICT_COPY(cur->content, clone->content); - goto leave_node; default: goto internal_error; } @@ -9595,8 +9577,8 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, * Acquire a normalized ns-decl and add it to the map. */ if (xmlDOMWrapNSNormAcquireNormalizedNs(destDoc, - /* ns-decls on curElem or on destDoc->oldNs */ - destParent ? curElem : NULL, + /* ns-decls on cloneElem or on destDoc->oldNs */ + destParent ? cloneElem : NULL, cur->ns, &ns, &nsMap, depth, /* if we need to search only in the ancestor-axis */ @@ -9617,19 +9599,22 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, if ((clone->type == XML_ATTRIBUTE_NODE) && (clone->parent != NULL)) { - if (xmlIsID(destDoc, clone->parent, (xmlAttrPtr) clone)) { + int res; + res = xmlIsID(destDoc, clone->parent, (xmlAttrPtr) clone); + if (res < 0) + goto internal_error; + if (res == 1) { xmlChar *idVal; - idVal = xmlNodeListGetString(cur->doc, cur->children, 1); - if (idVal != NULL) { - if (xmlAddID(NULL, destDoc, idVal, (xmlAttrPtr) cur) == NULL) { - /* TODO: error message. */ - xmlFree(idVal); - goto internal_error; - } - xmlFree(idVal); - } + idVal = xmlNodeGetContent(cur); + if (idVal == NULL) + goto internal_error; + if (xmlAddIDSafe((xmlAttrPtr) cur, idVal) < 0) { + xmlFree(idVal); + goto internal_error; + } + xmlFree(idVal); } } /* @@ -9694,11 +9679,6 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, prevClone = clone; cur = cur->next; } else if (cur->type != XML_ATTRIBUTE_NODE) { - /* - * Set clone->last. - */ - if (clone->parent != NULL) - clone->parent->last = clone; clone = clone->parent; if (clone != NULL) parentClone = clone->parent; @@ -9767,19 +9747,22 @@ xmlDOMWrapCloneNode(xmlDOMWrapCtxtPtr ctxt, */ static int xmlDOMWrapAdoptAttr(xmlDOMWrapCtxtPtr ctxt, - xmlDocPtr sourceDoc, + xmlDocPtr sourceDoc ATTRIBUTE_UNUSED, xmlAttrPtr attr, xmlDocPtr destDoc, xmlNodePtr destParent, int options ATTRIBUTE_UNUSED) { - xmlNodePtr cur; - int adoptStr = 1; + int ret = 0; if ((attr == NULL) || (destDoc == NULL)) return (-1); - attr->doc = destDoc; + if (attr->doc != destDoc) { + if (xmlSetTreeDoc((xmlNodePtr) attr, destDoc) < 0) + ret = -1; + } + if (attr->ns != NULL) { xmlNsPtr ns = NULL; @@ -9800,75 +9783,18 @@ xmlDOMWrapAdoptAttr(xmlDOMWrapCtxtPtr ctxt, */ if (xmlSearchNsByNamespaceStrict(destDoc, destParent, attr->ns->href, &ns, 1) == -1) - goto internal_error; + ret = -1; if (ns == NULL) { ns = xmlDOMWrapNSNormDeclareNsForced(destDoc, destParent, attr->ns->href, attr->ns->prefix, 1); } } if (ns == NULL) - goto internal_error; + ret = -1; attr->ns = ns; } - XML_TREE_ADOPT_STR(attr->name); - attr->atype = 0; - attr->psvi = NULL; - /* - * Walk content. - */ - if (attr->children == NULL) - return (0); - cur = attr->children; - if ((cur != NULL) && (cur->type == XML_NAMESPACE_DECL)) - goto internal_error; - while (cur != NULL) { - cur->doc = destDoc; - switch (cur->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - XML_TREE_ADOPT_STR_2(cur->content) - break; - case XML_ENTITY_REF_NODE: - /* - * Remove reference to the entity-node. - */ - cur->content = NULL; - cur->children = NULL; - cur->last = NULL; - if ((destDoc->intSubset) || (destDoc->extSubset)) { - xmlEntityPtr ent; - /* - * Assign new entity-node if available. - */ - ent = xmlGetDocEntity(destDoc, cur->name); - if (ent != NULL) { - cur->content = ent->content; - cur->children = (xmlNodePtr) ent; - cur->last = (xmlNodePtr) ent; - } - } - break; - default: - break; - } - if (cur->children != NULL) { - cur = cur->children; - continue; - } -next_sibling: - if (cur == (xmlNodePtr) attr) - break; - if (cur->next != NULL) - cur = cur->next; - else { - cur = cur->parent; - goto next_sibling; - } - } - return (0); -internal_error: - return (-1); + return (ret); } /* @@ -9906,6 +9832,8 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, xmlNodePtr destParent, int options) { + int ret = 0; + if ((node == NULL) || (node->type == XML_NAMESPACE_DECL) || (destDoc == NULL) || ((destParent != NULL) && (destParent->doc != destDoc))) @@ -9913,17 +9841,18 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, /* * Check node->doc sanity. */ - if ((node->doc != NULL) && (sourceDoc != NULL) && - (node->doc != sourceDoc)) { - /* - * Might be an XIncluded node. - */ + if (sourceDoc == NULL) { + sourceDoc = node->doc; + } else if (node->doc != sourceDoc) { return (-1); } - if (sourceDoc == NULL) - sourceDoc = node->doc; + + /* + * TODO: Shouldn't this be allowed? + */ if (sourceDoc == destDoc) return (-1); + switch (node->type) { case XML_ELEMENT_NODE: case XML_ATTRIBUTE_NODE: @@ -9943,7 +9872,7 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, * Unlink only if @node was not already added to @destParent. */ if ((node->parent != NULL) && (destParent != node->parent)) - xmlUnlinkNode(node); + xmlUnlinkNodeInternal(node); if (node->type == XML_ELEMENT_NODE) { return (xmlDOMWrapAdoptBranch(ctxt, sourceDoc, node, @@ -9952,52 +9881,12 @@ xmlDOMWrapAdoptNode(xmlDOMWrapCtxtPtr ctxt, return (xmlDOMWrapAdoptAttr(ctxt, sourceDoc, (xmlAttrPtr) node, destDoc, destParent, options)); } else { - xmlNodePtr cur = node; - int adoptStr = 1; - - cur->doc = destDoc; - /* - * Optimize string adoption. - */ - if ((sourceDoc != NULL) && - (sourceDoc->dict == destDoc->dict)) - adoptStr = 0; - switch (node->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - XML_TREE_ADOPT_STR_2(node->content) - break; - case XML_ENTITY_REF_NODE: - /* - * Remove reference to the entity-node. - */ - node->content = NULL; - node->children = NULL; - node->last = NULL; - if ((destDoc->intSubset) || (destDoc->extSubset)) { - xmlEntityPtr ent; - /* - * Assign new entity-node if available. - */ - ent = xmlGetDocEntity(destDoc, node->name); - if (ent != NULL) { - node->content = ent->content; - node->children = (xmlNodePtr) ent; - node->last = (xmlNodePtr) ent; - } - } - XML_TREE_ADOPT_STR(node->name) - break; - case XML_PI_NODE: { - XML_TREE_ADOPT_STR(node->name) - XML_TREE_ADOPT_STR_2(node->content) - break; - } - default: - break; - } + if (node->doc != destDoc) { + if (xmlNodeSetDoc(node, destDoc) < 0) + ret = -1; + } } - return (0); + return (ret); } /************************************************************************ @@ -10055,6 +9944,8 @@ xmlIsXHTML(const xmlChar *systemID, const xmlChar *publicID) { * xmlRegisterNodeDefault: * @func: function pointer to the new RegisterNodeFunc * + * DEPRECATED: don't use + * * Registers a callback for node creation * * Returns the old value of the registration function @@ -10073,6 +9964,8 @@ xmlRegisterNodeDefault(xmlRegisterNodeFunc func) * xmlDeregisterNodeDefault: * @func: function pointer to the new DeregisterNodeFunc * + * DEPRECATED: don't use + * * Registers a callback for node destruction * * Returns the previous value of the deregistration function diff --git a/libraries/libxml2/uri.c b/libraries/libxml2/uri.c index 03b5a31a..3b986532 100644 --- a/libraries/libxml2/uri.c +++ b/libraries/libxml2/uri.c @@ -37,23 +37,6 @@ #define PORT_EMPTY 0 #define PORT_EMPTY_SERVER -1 -static void -xmlURIErrMemory(const char *extra) -{ - if (extra) - __xmlRaiseError(NULL, NULL, NULL, - NULL, NULL, XML_FROM_URI, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, - extra, NULL, NULL, 0, 0, - "Memory allocation failed : %s\n", extra); - else - __xmlRaiseError(NULL, NULL, NULL, - NULL, NULL, XML_FROM_URI, - XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0, - NULL, NULL, NULL, 0, 0, - "Memory allocation failed\n"); -} - static void xmlCleanURI(xmlURIPtr uri); /* @@ -68,7 +51,6 @@ static void xmlCleanURI(xmlURIPtr uri); * "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | * "u" | "v" | "w" | "x" | "y" | "z" */ - #define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z')) /* @@ -89,7 +71,6 @@ static void xmlCleanURI(xmlURIPtr uri); /* * alphanum = alpha | digit */ - #define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x)) /* @@ -103,16 +84,15 @@ static void xmlCleanURI(xmlURIPtr uri); /* * unwise = "{" | "}" | "|" | "\" | "^" | "`" */ - #define IS_UNWISE(p) \ (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) || \ ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) || \ ((*(p) == ']')) || ((*(p) == '`'))) + /* * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," | * "[" | "]" */ - #define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \ ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \ ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \ @@ -121,13 +101,11 @@ static void xmlCleanURI(xmlURIPtr uri); /* * unreserved = alphanum | mark */ - #define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x)) /* * Skip to next pointer char, handle escaped sequences */ - #define NEXT(p) ((*p == '%')? p += 3 : p++) /* @@ -139,7 +117,6 @@ static void xmlCleanURI(xmlURIPtr uri); * * path = [ abs_path | opaque_part ] */ - #define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n)) /************************************************************************ @@ -181,7 +158,7 @@ static void xmlCleanURI(xmlURIPtr uri); /* * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */ -#define ISA_UNRESERVED(p) \ +#define ISA_STRICTLY_UNRESERVED(p) \ ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) || \ ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~'))) @@ -194,10 +171,47 @@ static void xmlCleanURI(xmlURIPtr uri); /* * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" */ -#define ISA_PCHAR(p) \ - (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ +#define ISA_PCHAR(u, p) \ + (ISA_UNRESERVED(u, p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) || \ ((*(p) == ':')) || ((*(p) == '@'))) +/* + * From https://www.w3.org/TR/leiri/ + * + * " " / "<" / ">" / '"' / "{" / "}" / "|" + * / "\" / "^" / "`" / %x0-1F / %x7F-D7FF + * / %xE000-FFFD / %x10000-10FFFF + */ +#define ISA_UCSCHAR(p) \ + ((*(p) <= 0x20) || (*(p) >= 0x7F) || (*(p) == '<') || (*(p) == '>') || \ + (*(p) == '"') || (*(p) == '{') || (*(p) == '}') || (*(p) == '|') || \ + (*(p) == '\\') || (*(p) == '^') || (*(p) == '`')) + +#define ISA_UNRESERVED(u, p) (xmlIsUnreserved(u, p)) + +#define XML_URI_ALLOW_UNWISE 1 +#define XML_URI_NO_UNESCAPE 2 +#define XML_URI_ALLOW_UCSCHAR 4 + +static int +xmlIsUnreserved(xmlURIPtr uri, const char *cur) { + if (uri == NULL) + return(0); + + if (ISA_STRICTLY_UNRESERVED(cur)) + return(1); + + if (uri->cleanup & XML_URI_ALLOW_UNWISE) { + if (IS_UNWISE(cur)) + return(1); + } else if (uri->cleanup & XML_URI_ALLOW_UCSCHAR) { + if (ISA_UCSCHAR(cur)) + return(1); + } + + return(0); +} + /** * xmlParse3986Scheme: * @uri: pointer to an URI structure @@ -213,18 +227,17 @@ static int xmlParse3986Scheme(xmlURIPtr uri, const char **str) { const char *cur; - if (str == NULL) - return(-1); - cur = *str; if (!ISA_ALPHA(cur)) - return(2); + return(1); cur++; while (ISA_ALPHA(cur) || ISA_DIGIT(cur) || (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++; if (uri != NULL) { if (uri->scheme != NULL) xmlFree(uri->scheme); uri->scheme = STRNDUP(*str, cur - *str); + if (uri->scheme == NULL) + return(-1); } *str = cur; return(0); @@ -250,22 +263,20 @@ xmlParse3986Fragment(xmlURIPtr uri, const char **str) { const char *cur; - if (str == NULL) - return (-1); - cur = *str; - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - (*cur == '[') || (*cur == ']') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) + while ((ISA_PCHAR(uri, cur)) || (*cur == '/') || (*cur == '?') || + (*cur == '[') || (*cur == ']')) NEXT(cur); if (uri != NULL) { if (uri->fragment != NULL) xmlFree(uri->fragment); - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->fragment = STRNDUP(*str, cur - *str); else uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->fragment == NULL) + return (-1); } *str = cur; return (0); @@ -287,21 +298,19 @@ xmlParse3986Query(xmlURIPtr uri, const char **str) { const char *cur; - if (str == NULL) - return (-1); - cur = *str; - while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') || - ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur)))) + while ((ISA_PCHAR(uri, cur)) || (*cur == '/') || (*cur == '?')) NEXT(cur); if (uri != NULL) { if (uri->query != NULL) xmlFree(uri->query); - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->query = STRNDUP(*str, cur - *str); else uri->query = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->query == NULL) + return (-1); /* Save the raw bytes of the query as well. * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114 @@ -309,6 +318,8 @@ xmlParse3986Query(xmlURIPtr uri, const char **str) if (uri->query_raw != NULL) xmlFree (uri->query_raw); uri->query_raw = STRNDUP (*str, cur - *str); + if (uri->query_raw == NULL) + return (-1); } *str = cur; return (0); @@ -371,16 +382,18 @@ xmlParse3986Userinfo(xmlURIPtr uri, const char **str) const char *cur; cur = *str; - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || + while (ISA_UNRESERVED(uri, cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur) || (*cur == ':')) NEXT(cur); if (*cur == '@') { if (uri != NULL) { if (uri->user != NULL) xmlFree(uri->user); - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->user = STRNDUP(*str, cur - *str); else uri->user = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->user == NULL) + return(-1); } *str = cur; return(0); @@ -485,7 +498,8 @@ xmlParse3986Host(xmlURIPtr uri, const char **str) /* * then this should be a hostname which can be empty */ - while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) + while (ISA_UNRESERVED(uri, cur) || + ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur)) NEXT(cur); found: if (uri != NULL) { @@ -493,10 +507,12 @@ xmlParse3986Host(xmlURIPtr uri, const char **str) uri->authority = NULL; if (uri->server != NULL) xmlFree(uri->server); if (cur != host) { - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->server = STRNDUP(host, cur - host); else uri->server = xmlURIUnescapeString(host, cur - host, NULL); + if (uri->server == NULL) + return(-1); } else uri->server = NULL; } @@ -527,6 +543,8 @@ xmlParse3986Authority(xmlURIPtr uri, const char **str) * try to parse an userinfo and check for the trailing @ */ ret = xmlParse3986Userinfo(uri, &cur); + if (ret < 0) + return(ret); if ((ret != 0) || (*cur != '@')) cur = *str; else @@ -559,17 +577,17 @@ xmlParse3986Authority(xmlURIPtr uri, const char **str) * Returns 0 or the error code */ static int -xmlParse3986Segment(const char **str, char forbid, int empty) +xmlParse3986Segment(xmlURIPtr uri, const char **str, char forbid, int empty) { const char *cur; cur = *str; - if (!ISA_PCHAR(cur)) { + if (!ISA_PCHAR(uri, cur)) { if (empty) return(0); return(1); } - while (ISA_PCHAR(cur) && (*cur != forbid)) + while (ISA_PCHAR(uri, cur) && (*cur != forbid)) NEXT(cur); *str = cur; return (0); @@ -597,16 +615,18 @@ xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str) while (*cur == '/') { cur++; - ret = xmlParse3986Segment(&cur, 0, 1); + ret = xmlParse3986Segment(uri, &cur, 0, 1); if (ret != 0) return(ret); } if (uri != NULL) { if (uri->path != NULL) xmlFree(uri->path); if (*str != cur) { - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->path = STRNDUP(*str, cur - *str); else uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->path == NULL) + return (-1); } else { uri->path = NULL; } @@ -638,21 +658,23 @@ xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str) if (*cur != '/') return(1); cur++; - ret = xmlParse3986Segment(&cur, 0, 0); + ret = xmlParse3986Segment(uri, &cur, 0, 0); if (ret == 0) { while (*cur == '/') { cur++; - ret = xmlParse3986Segment(&cur, 0, 1); + ret = xmlParse3986Segment(uri, &cur, 0, 1); if (ret != 0) return(ret); } } if (uri != NULL) { if (uri->path != NULL) xmlFree(uri->path); if (cur != *str) { - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->path = STRNDUP(*str, cur - *str); else uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->path == NULL) + return (-1); } else { uri->path = NULL; } @@ -681,20 +703,22 @@ xmlParse3986PathRootless(xmlURIPtr uri, const char **str) cur = *str; - ret = xmlParse3986Segment(&cur, 0, 0); + ret = xmlParse3986Segment(uri, &cur, 0, 0); if (ret != 0) return(ret); while (*cur == '/') { cur++; - ret = xmlParse3986Segment(&cur, 0, 1); + ret = xmlParse3986Segment(uri, &cur, 0, 1); if (ret != 0) return(ret); } if (uri != NULL) { if (uri->path != NULL) xmlFree(uri->path); if (cur != *str) { - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->path = STRNDUP(*str, cur - *str); else uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->path == NULL) + return (-1); } else { uri->path = NULL; } @@ -723,20 +747,22 @@ xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str) cur = *str; - ret = xmlParse3986Segment(&cur, ':', 0); + ret = xmlParse3986Segment(uri, &cur, ':', 0); if (ret != 0) return(ret); while (*cur == '/') { cur++; - ret = xmlParse3986Segment(&cur, 0, 1); + ret = xmlParse3986Segment(uri, &cur, 0, 1); if (ret != 0) return(ret); } if (uri != NULL) { if (uri->path != NULL) xmlFree(uri->path); if (cur != *str) { - if (uri->cleanup & 2) + if (uri->cleanup & XML_URI_NO_UNESCAPE) uri->path = STRNDUP(*str, cur - *str); else uri->path = xmlURIUnescapeString(*str, cur - *str, NULL); + if (uri->path == NULL) + return (-1); } else { uri->path = NULL; } @@ -784,7 +810,7 @@ xmlParse3986HierPart(xmlURIPtr uri, const char **str) } else if (*cur == '/') { ret = xmlParse3986PathAbsolute(uri, &cur); if (ret != 0) return(ret); - } else if (ISA_PCHAR(cur)) { + } else if (ISA_PCHAR(uri, cur)) { ret = xmlParse3986PathRootless(uri, &cur); if (ret != 0) return(ret); } else { @@ -827,7 +853,7 @@ xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) { } else if (*str == '/') { ret = xmlParse3986PathAbsolute(uri, &str); if (ret != 0) return(ret); - } else if (ISA_PCHAR(str)) { + } else if (ISA_PCHAR(uri, str)) { ret = xmlParse3986PathNoScheme(uri, &str); if (ret != 0) return(ret); } else { @@ -922,6 +948,8 @@ xmlParse3986URIReference(xmlURIPtr uri, const char *str) { * it fails. */ ret = xmlParse3986URI(uri, str); + if (ret < 0) + return(ret); if (ret != 0) { xmlCleanURI(uri); ret = xmlParse3986RelativeRef(uri, str); @@ -934,30 +962,58 @@ xmlParse3986URIReference(xmlURIPtr uri, const char *str) { } /** - * xmlParseURI: + * xmlParseURISafe: * @str: the URI string to analyze + * @uriOut: optional pointer to parsed URI * * Parse an URI based on RFC 3986 * * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] * - * Returns a newly built xmlURIPtr or NULL in case of error + * Available since 2.13.0. + * + * Returns 0 on success, an error code (typically 1) if the URI is invalid + * or -1 if a memory allocation failed. */ -xmlURIPtr -xmlParseURI(const char *str) { +int +xmlParseURISafe(const char *str, xmlURIPtr *uriOut) { xmlURIPtr uri; int ret; + if (uriOut == NULL) + return(1); + *uriOut = NULL; if (str == NULL) - return(NULL); + return(1); + uri = xmlCreateURI(); - if (uri != NULL) { - ret = xmlParse3986URIReference(uri, str); - if (ret) { - xmlFreeURI(uri); - return(NULL); - } + if (uri == NULL) + return(-1); + + ret = xmlParse3986URIReference(uri, str); + if (ret) { + xmlFreeURI(uri); + return(ret); } + + *uriOut = uri; + return(0); +} + +/** + * xmlParseURI: + * @str: the URI string to analyze + * + * Parse an URI based on RFC 3986 + * + * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ] + * + * Returns a newly built xmlURIPtr or NULL in case of error + */ +xmlURIPtr +xmlParseURI(const char *str) { + xmlURIPtr uri; + xmlParseURISafe(str, &uri); return(uri); } @@ -999,7 +1055,7 @@ xmlParseURIRaw(const char *str, int raw) { uri = xmlCreateURI(); if (uri != NULL) { if (raw) { - uri->cleanup |= 2; + uri->cleanup |= XML_URI_NO_UNESCAPE; } ret = xmlParseURIReference(uri, str); if (ret) { @@ -1028,10 +1084,8 @@ xmlCreateURI(void) { xmlURIPtr ret; ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI)); - if (ret == NULL) { - xmlURIErrMemory("creating URI structure\n"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlURI)); ret->port = PORT_EMPTY; return(ret); @@ -1048,16 +1102,12 @@ xmlSaveUriRealloc(xmlChar *ret, int *max) { xmlChar *temp; int tmp; - if (*max > MAX_URI_LENGTH) { - xmlURIErrMemory("reaching arbitrary MAX_URI_LENGTH limit\n"); + if (*max > MAX_URI_LENGTH) return(NULL); - } tmp = *max * 2; temp = (xmlChar *) xmlRealloc(ret, (tmp + 1)); - if (temp == NULL) { - xmlURIErrMemory("saving URI\n"); + if (temp == NULL) return(NULL); - } *max = tmp; return(temp); } @@ -1083,10 +1133,8 @@ xmlSaveUri(xmlURIPtr uri) { max = 80; ret = (xmlChar *) xmlMallocAtomic(max + 1); - if (ret == NULL) { - xmlURIErrMemory("saving URI\n"); + if (ret == NULL) return(NULL); - } len = 0; if (uri->scheme != NULL) { @@ -1414,195 +1462,136 @@ xmlFreeURI(xmlURIPtr uri) { * * ************************************************************************/ +static int +xmlIsPathSeparator(int c, int isFile) { + (void) isFile; + + if (c == '/') + return(1); + +#if defined(_WIN32) || defined(__CYGWIN__) + if (isFile && (c == '\\')) + return(1); +#endif + + return(0); +} + /** - * xmlNormalizeURIPath: + * xmlNormalizePath: * @path: pointer to the path string + * @isFile: true for filesystem paths, false for URIs * - * Applies the 5 normalization steps to a path string--that is, RFC 2396 - * Section 5.2, steps 6.c through 6.g. - * - * Normalization occurs directly on the string, no new allocation is done + * Normalize a filesystem path or URI. * * Returns 0 or an error code */ -int -xmlNormalizeURIPath(char *path) { +static int +xmlNormalizePath(char *path, int isFile) { char *cur, *out; + int numSeg = 0; if (path == NULL) return(-1); - /* Skip all initial "/" chars. We want to get to the beginning of the - * first non-empty segment. - */ cur = path; - while (cur[0] == '/') - ++cur; - if (cur[0] == '\0') - return(0); + out = path; - /* Keep everything we've seen so far. */ - out = cur; + if (*cur == 0) + return(0); - /* - * Analyze each segment in sequence for cases (c) and (d). - */ - while (cur[0] != '\0') { - /* - * c) All occurrences of "./", where "." is a complete path segment, - * are removed from the buffer string. - */ - if ((cur[0] == '.') && (cur[1] == '/')) { - cur += 2; - /* '//' normalization should be done at this point too */ - while (cur[0] == '/') - cur++; - continue; - } - - /* - * d) If the buffer string ends with "." as a complete path segment, - * that "." is removed. - */ - if ((cur[0] == '.') && (cur[1] == '\0')) - break; - - /* Otherwise keep the segment. */ - while (cur[0] != '/') { - if (cur[0] == '\0') - goto done_cd; - (out++)[0] = (cur++)[0]; - } - /* normalize // */ - while ((cur[0] == '/') && (cur[1] == '/')) - cur++; - - (out++)[0] = (cur++)[0]; + if (xmlIsPathSeparator(*cur, isFile)) { + cur++; + *out++ = '/'; } - done_cd: - out[0] = '\0'; - /* Reset to the beginning of the first segment for the next sequence. */ - cur = path; - while (cur[0] == '/') - ++cur; - if (cur[0] == '\0') - return(0); - - /* - * Analyze each segment in sequence for cases (e) and (f). - * - * e) All occurrences of "/../", where is a - * complete path segment not equal to "..", are removed from the - * buffer string. Removal of these path segments is performed - * iteratively, removing the leftmost matching pattern on each - * iteration, until no matching pattern remains. - * - * f) If the buffer string ends with "/..", where - * is a complete path segment not equal to "..", that - * "/.." is removed. - * - * To satisfy the "iterative" clause in (e), we need to collapse the - * string every time we find something that needs to be removed. Thus, - * we don't need to keep two pointers into the string: we only need a - * "current position" pointer. - */ - while (1) { - char *segp, *tmp; - - /* At the beginning of each iteration of this loop, "cur" points to - * the first character of the segment we want to examine. - */ - - /* Find the end of the current segment. */ - segp = cur; - while ((segp[0] != '/') && (segp[0] != '\0')) - ++segp; - - /* If this is the last segment, we're done (we need at least two - * segments to meet the criteria for the (e) and (f) cases). - */ - if (segp[0] == '\0') - break; - - /* If the first segment is "..", or if the next segment _isn't_ "..", - * keep this segment and try the next one. + while (*cur != 0) { + /* + * At this point, out is either empty or ends with a separator. + * Collapse multiple separators first. */ - ++segp; - if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3)) - || ((segp[0] != '.') || (segp[1] != '.') - || ((segp[2] != '/') && (segp[2] != '\0')))) { - cur = segp; - continue; + while (xmlIsPathSeparator(*cur, isFile)) { +#if defined(_WIN32) || defined(__CYGWIN__) + /* Allow two separators at start of path */ + if ((isFile) && (out == path + 1)) + *out++ = '/'; +#endif + cur++; } - /* If we get here, remove this segment and the next one and back up - * to the previous segment (if there is one), to implement the - * "iteratively" clause. It's pretty much impossible to back up - * while maintaining two pointers into the buffer, so just compact - * the whole buffer now. - */ + if (*cur == '.') { + if (cur[1] == 0) { + /* Ignore "." at end of path */ + break; + } else if (xmlIsPathSeparator(cur[1], isFile)) { + /* Skip "./" */ + cur += 2; + continue; + } else if ((cur[1] == '.') && + ((cur[2] == 0) || xmlIsPathSeparator(cur[2], isFile))) { + if (numSeg > 0) { + /* Handle ".." by removing last segment */ + do { + out--; + } while ((out > path) && + !xmlIsPathSeparator(out[-1], isFile)); + numSeg--; + + if (cur[2] == 0) + break; + cur += 3; + continue; + } else if (out[0] == '/') { + /* Ignore extraneous ".." in absolute paths */ + if (cur[2] == 0) + break; + cur += 3; + continue; + } else { + /* Keep "../" at start of relative path */ + numSeg--; + } + } + } - /* If this is the end of the buffer, we're done. */ - if (segp[2] == '\0') { - cur[0] = '\0'; - break; + /* Copy segment */ + while ((*cur != 0) && !xmlIsPathSeparator(*cur, isFile)) { + *out++ = *cur++; } - /* Valgrind complained, strcpy(cur, segp + 3); */ - /* string will overlap, do not use strcpy */ - tmp = cur; - segp += 3; - while ((*tmp++ = *segp++) != 0) - ; - - /* If there are no previous segments, then keep going from here. */ - segp = cur; - while ((segp > path) && ((--segp)[0] == '/')) - ; - if (segp == path) - continue; - - /* "segp" is pointing to the end of a previous segment; find it's - * start. We need to back up to the previous segment and start - * over with that to handle things like "foo/bar/../..". If we - * don't do this, then on the first pass we'll remove the "bar/..", - * but be pointing at the second ".." so we won't realize we can also - * remove the "foo/..". - */ - cur = segp; - while ((cur > path) && (cur[-1] != '/')) - --cur; - } - out[0] = '\0'; - /* - * g) If the resulting buffer string still begins with one or more - * complete path segments of "..", then the reference is - * considered to be in error. Implementations may handle this - * error by retaining these components in the resolved path (i.e., - * treating them as part of the final URI), by removing them from - * the resolved path (i.e., discarding relative levels above the - * root), or by avoiding traversal of the reference. - * - * We discard them from the final path. - */ - if (path[0] == '/') { - cur = path; - while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.') - && ((cur[3] == '/') || (cur[3] == '\0'))) - cur += 3; + /* Copy separator */ + if (*cur != 0) { + cur++; + *out++ = '/'; + } - if (cur != path) { - out = path; - while (cur[0] != '\0') - (out++)[0] = (cur++)[0]; - out[0] = 0; - } + numSeg++; } + /* Keep "." if output is empty and it's a file */ + if ((isFile) && (out <= path)) + *out++ = '.'; + *out = 0; + return(0); } +/** + * xmlNormalizeURIPath: + * @path: pointer to the path string + * + * Applies the 5 normalization steps to a path string--that is, RFC 2396 + * Section 5.2, steps 6.c through 6.g. + * + * Normalization occurs directly on the string, no new allocation is done + * + * Returns 0 or an error code + */ +int +xmlNormalizeURIPath(char *path) { + return(xmlNormalizePath(path, 0)); +} + static int is_hex(char c) { if (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || @@ -1637,10 +1626,8 @@ xmlURIUnescapeString(const char *str, int len, char *target) { if (target == NULL) { ret = (char *) xmlMallocAtomic(len + 1); - if (ret == NULL) { - xmlURIErrMemory("unescaping URI value\n"); + if (ret == NULL) return(NULL); - } } else ret = target; in = str; @@ -1680,8 +1667,9 @@ xmlURIUnescapeString(const char *str, int len, char *target) { * @str: string to escape * @list: exception list string of chars not to escape * - * This routine escapes a string to hex, ignoring reserved characters - * (a-z, A-Z, 0-9, "@-_.!~*'()") and the characters in the exception list. + * This routine escapes a string to hex, ignoring unreserved characters + * a-z, A-Z, 0-9, "-._~", a few sub-delims "!*'()", the gen-delim "@" + * (why?) and the characters in the exception list. * * Returns a new escaped string or NULL in case of error. */ @@ -1697,25 +1685,24 @@ xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) { if (str[0] == 0) return(xmlStrdup(str)); len = xmlStrlen(str); - if (!(len > 0)) return(NULL); len += 20; ret = (xmlChar *) xmlMallocAtomic(len); - if (ret == NULL) { - xmlURIErrMemory("escaping URI value\n"); + if (ret == NULL) return(NULL); - } in = (const xmlChar *) str; out = 0; while(*in != 0) { if (len - out <= 3) { - temp = xmlSaveUriRealloc(ret, &len); + if (len > INT_MAX / 2) + return(NULL); + temp = xmlRealloc(ret, len * 2); if (temp == NULL) { - xmlURIErrMemory("escaping URI value\n"); xmlFree(ret); return(NULL); } ret = temp; + len *= 2; } ch = *in; @@ -1773,7 +1760,7 @@ xmlURIEscape(const xmlChar * str) /* * Allow escaping errors in the unescaped form */ - uri->cleanup = 1; + uri->cleanup = XML_URI_ALLOW_UNWISE; ret2 = xmlParseURIReference(uri, (const char *)str); if (ret2) { xmlFreeURI(uri); @@ -1787,7 +1774,6 @@ xmlURIEscape(const xmlChar * str) ret = NULL; #define NULLCHK(p) if(!p) { \ - xmlURIErrMemory("escaping URI value\n"); \ xmlFreeURI(uri); \ xmlFree(ret); \ return NULL; } \ @@ -1883,10 +1869,132 @@ xmlURIEscape(const xmlChar * str) * * ************************************************************************/ +static int +xmlIsAbsolutePath(const xmlChar *path) { + int c = path[0]; + + if (xmlIsPathSeparator(c, 1)) + return(1); + +#if defined(_WIN32) || defined(__CYGWIN__) + if ((((c >= 'A') && (c <= 'Z')) || + ((c >= 'a') && (c <= 'z'))) && + (path[1] == ':')) + return(1); +#endif + + return(0); +} + /** - * xmlBuildURI: + * xmlResolvePath: + * @ref: the filesystem path + * @base: the base value + * @out: pointer to result URI + * + * Resolves a filesystem path from a base path. + * + * Returns 0 on success, -1 if a memory allocation failed or an error + * code if URI or base are invalid. + */ +static int +xmlResolvePath(const xmlChar *escRef, const xmlChar *base, xmlChar **out) { + const xmlChar *fragment; + xmlChar *tmp = NULL; + xmlChar *ref = NULL; + xmlChar *result = NULL; + int ret = -1; + int i; + + if (out == NULL) + return(1); + *out = NULL; + + if ((escRef == NULL) || (escRef[0] == 0)) { + if ((base == NULL) || (base[0] == 0)) + return(1); + ref = xmlStrdup(base); + if (ref == NULL) + goto err_memory; + *out = ref; + return(0); + } + + /* + * If a URI is resolved, we can assume it is a valid URI and not + * a filesystem path. This means we have to unescape the part + * before the fragment. + */ + fragment = xmlStrchr(escRef, '#'); + if (fragment != NULL) { + tmp = xmlStrndup(escRef, fragment - escRef); + if (tmp == NULL) + goto err_memory; + escRef = tmp; + } + + ref = (xmlChar *) xmlURIUnescapeString((char *) escRef, -1, NULL); + if (ref == NULL) + goto err_memory; + + if ((base == NULL) || (base[0] == 0)) + goto done; + + if (xmlIsAbsolutePath(ref)) + goto done; + + /* + * Remove last segment from base + */ + i = xmlStrlen(base); + while ((i > 0) && !xmlIsPathSeparator(base[i-1], 1)) + i--; + + /* + * Concatenate base and ref + */ + if (i > 0) { + int refLen = xmlStrlen(ref); + + result = xmlMalloc(i + refLen + 1); + if (result == NULL) + goto err_memory; + + memcpy(result, base, i); + memcpy(result + i, ref, refLen + 1); + } + + /* + * Normalize + */ + xmlNormalizePath((char *) result, 1); + +done: + if (result == NULL) { + result = ref; + ref = NULL; + } + + if (fragment != NULL) { + result = xmlStrcat(result, fragment); + if (result == NULL) + goto err_memory; + } + + *out = result; + ret = 0; + +err_memory: + xmlFree(tmp); + xmlFree(ref); + return(ret); +} + +/** + * xmlBuildURISafe: * @URI: the URI instance found in the document * @base: the base value + * @valPtr: pointer to result URI * * Computes he final URI of the reference done by checking that * the given URI is valid, and building the final URI using the @@ -1895,17 +2003,34 @@ xmlURIEscape(const xmlChar * str) * * 5.2. Resolving Relative References to Absolute Form * - * Returns a new URI string (to be freed by the caller) or NULL in case - * of error. + * Available since 2.13.0. + * + * Returns 0 on success, -1 if a memory allocation failed or an error + * code if URI or base are invalid. */ -xmlChar * -xmlBuildURI(const xmlChar *URI, const xmlChar *base) { +int +xmlBuildURISafe(const xmlChar *URI, const xmlChar *base, xmlChar **valPtr) { xmlChar *val = NULL; int ret, len, indx, cur, out; xmlURIPtr ref = NULL; xmlURIPtr bas = NULL; xmlURIPtr res = NULL; + if (valPtr == NULL) + return(1); + *valPtr = NULL; + + if (URI == NULL) + return(1); + + if (base == NULL) { + val = xmlStrdup(URI); + if (val == NULL) + return(-1); + *valPtr = val; + return(0); + } + /* * 1) The URI reference is parsed into the potential four components and * fragment identifier, as described in Section 4.3. @@ -1914,18 +2039,10 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * as a reference to "." rather than as a synonym for the current * URI. Should we do that here? */ - if (URI == NULL) - ret = -1; - else { - if (*URI) { - ref = xmlCreateURI(); - if (ref == NULL) - goto done; - ret = xmlParseURIReference(ref, (const char *) URI); - } - else - ret = 0; - } + if (URI[0] != 0) + ret = xmlParseURISafe((const char *) URI, &ref); + else + ret = 0; if (ret != 0) goto done; if ((ref != NULL) && (ref->scheme != NULL)) { @@ -1933,19 +2050,30 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * The URI is absolute don't modify. */ val = xmlStrdup(URI); + if (val == NULL) + ret = -1; goto done; } - if (base == NULL) - ret = -1; - else { - bas = xmlCreateURI(); - if (bas == NULL) - goto done; - ret = xmlParseURIReference(bas, (const char *) base); + + /* + * If base has no scheme or authority, it is assumed to be a + * filesystem path. + */ + if (xmlStrstr(base, BAD_CAST "://") == NULL) { + xmlFreeURI(ref); + return(xmlResolvePath(URI, base, valPtr)); } + + ret = xmlParseURISafe((const char *) base, &bas); + if (ret < 0) + goto done; if (ret != 0) { - if (ref) + if (ref) { + ret = 0; val = xmlSaveUri(ref); + if (val == NULL) + ret = -1; + } goto done; } if (ref == NULL) { @@ -1957,6 +2085,8 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { bas->fragment = NULL; } val = xmlSaveUri(bas); + if (val == NULL) + ret = -1; goto done; } @@ -1972,35 +2102,62 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * defined while still treating this as a reference to the current * document. */ + ret = -1; res = xmlCreateURI(); if (res == NULL) goto done; if ((ref->scheme == NULL) && (ref->path == NULL) && ((ref->authority == NULL) && (ref->server == NULL) && (ref->port == PORT_EMPTY))) { - if (bas->scheme != NULL) + if (bas->scheme != NULL) { res->scheme = xmlMemStrdup(bas->scheme); - if (bas->authority != NULL) + if (res->scheme == NULL) + goto done; + } + if (bas->authority != NULL) { res->authority = xmlMemStrdup(bas->authority); - else { - if (bas->server != NULL) + if (res->authority == NULL) + goto done; + } else { + if (bas->server != NULL) { res->server = xmlMemStrdup(bas->server); - if (bas->user != NULL) + if (res->server == NULL) + goto done; + } + if (bas->user != NULL) { res->user = xmlMemStrdup(bas->user); + if (res->user == NULL) + goto done; + } res->port = bas->port; } - if (bas->path != NULL) + if (bas->path != NULL) { res->path = xmlMemStrdup(bas->path); - if (ref->query_raw != NULL) + if (res->path == NULL) + goto done; + } + if (ref->query_raw != NULL) { res->query_raw = xmlMemStrdup (ref->query_raw); - else if (ref->query != NULL) + if (res->query_raw == NULL) + goto done; + } else if (ref->query != NULL) { res->query = xmlMemStrdup(ref->query); - else if (bas->query_raw != NULL) + if (res->query == NULL) + goto done; + } else if (bas->query_raw != NULL) { res->query_raw = xmlMemStrdup(bas->query_raw); - else if (bas->query != NULL) + if (res->query_raw == NULL) + goto done; + } else if (bas->query != NULL) { res->query = xmlMemStrdup(bas->query); - if (ref->fragment != NULL) + if (res->query == NULL) + goto done; + } + if (ref->fragment != NULL) { res->fragment = xmlMemStrdup(ref->fragment); + if (res->fragment == NULL) + goto done; + } goto step_7; } @@ -2012,17 +2169,30 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { */ if (ref->scheme != NULL) { val = xmlSaveUri(ref); + if (val != NULL) + ret = 0; goto done; } - if (bas->scheme != NULL) + if (bas->scheme != NULL) { res->scheme = xmlMemStrdup(bas->scheme); + if (res->scheme == NULL) + goto done; + } - if (ref->query_raw != NULL) + if (ref->query_raw != NULL) { res->query_raw = xmlMemStrdup(ref->query_raw); - else if (ref->query != NULL) + if (res->query_raw == NULL) + goto done; + } else if (ref->query != NULL) { res->query = xmlMemStrdup(ref->query); - if (ref->fragment != NULL) + if (res->query == NULL) + goto done; + } + if (ref->fragment != NULL) { res->fragment = xmlMemStrdup(ref->fragment); + if (res->fragment == NULL) + goto done; + } /* * 4) If the authority component is defined, then the reference is a @@ -2033,26 +2203,45 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { */ if ((ref->authority != NULL) || (ref->server != NULL) || (ref->port != PORT_EMPTY)) { - if (ref->authority != NULL) + if (ref->authority != NULL) { res->authority = xmlMemStrdup(ref->authority); - else { - if (ref->server != NULL) + if (res->authority == NULL) + goto done; + } else { + if (ref->server != NULL) { res->server = xmlMemStrdup(ref->server); - if (ref->user != NULL) + if (res->server == NULL) + goto done; + } + if (ref->user != NULL) { res->user = xmlMemStrdup(ref->user); + if (res->user == NULL) + goto done; + } res->port = ref->port; } - if (ref->path != NULL) + if (ref->path != NULL) { res->path = xmlMemStrdup(ref->path); + if (res->path == NULL) + goto done; + } goto step_7; } - if (bas->authority != NULL) + if (bas->authority != NULL) { res->authority = xmlMemStrdup(bas->authority); - else if ((bas->server != NULL) || (bas->port != PORT_EMPTY)) { - if (bas->server != NULL) + if (res->authority == NULL) + goto done; + } else if ((bas->server != NULL) || (bas->port != PORT_EMPTY)) { + if (bas->server != NULL) { res->server = xmlMemStrdup(bas->server); - if (bas->user != NULL) + if (res->server == NULL) + goto done; + } + if (bas->user != NULL) { res->user = xmlMemStrdup(bas->user); + if (res->user == NULL) + goto done; + } res->port = bas->port; } @@ -2062,6 +2251,8 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { */ if ((ref->path != NULL) && (ref->path[0] == '/')) { res->path = xmlMemStrdup(ref->path); + if (res->path == NULL) + goto done; goto step_7; } @@ -2080,10 +2271,8 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { if (bas->path != NULL) len += strlen(bas->path); res->path = (char *) xmlMallocAtomic(len); - if (res->path == NULL) { - xmlURIErrMemory("resolving URI against base\n"); + if (res->path == NULL) goto done; - } res->path[0] = 0; /* @@ -2139,6 +2328,8 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * reference. */ val = xmlSaveUri(res); + if (val != NULL) + ret = 0; done: if (ref != NULL) @@ -2147,13 +2338,161 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { xmlFreeURI(bas); if (res != NULL) xmlFreeURI(res); - return(val); + *valPtr = val; + return(ret); } /** - * xmlBuildRelativeURI: + * xmlBuildURI: + * @URI: the URI instance found in the document + * @base: the base value + * + * Computes he final URI of the reference done by checking that + * the given URI is valid, and building the final URI using the + * base URI. This is processed according to section 5.2 of the + * RFC 2396 + * + * 5.2. Resolving Relative References to Absolute Form + * + * Returns a new URI string (to be freed by the caller) or NULL in case + * of error. + */ +xmlChar * +xmlBuildURI(const xmlChar *URI, const xmlChar *base) { + xmlChar *out; + + xmlBuildURISafe(URI, base, &out); + return(out); +} + +static int +xmlParseUriOrPath(const char *str, xmlURIPtr *out, int *drive) { + xmlURIPtr uri; + char *buf = NULL; + int ret; + + *out = NULL; + *drive = 0; + + uri = xmlCreateURI(); + if (uri == NULL) { + ret = -1; + goto done; + } + + if (xmlStrstr(BAD_CAST str, BAD_CAST "://") == NULL) { + const char *path; + size_t pathSize; + int prependSlash = 0; + + buf = xmlMemStrdup(str); + if (buf == NULL) { + ret = -1; + goto done; + } + xmlNormalizePath(buf, /* isFile */ 1); + + path = buf; + + if (xmlIsAbsolutePath(BAD_CAST buf)) { +#if defined(_WIN32) || defined(__CYGWIN__) + const char *server = NULL; + int isFileScheme = 0; +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) + if (strncmp(buf, "//?/UNC/", 8) == 0) { + server = buf + 8; + isFileScheme = 1; + } else if (strncmp(buf, "//?/", 4) == 0) { + path = buf + 3; + isFileScheme = 1; + } else if (strncmp(buf, "//", 2) == 0) { + server = buf + 2; + isFileScheme = 1; + } + + if (server != NULL) { + const char *end = strchr(server, '/'); + + if (end == NULL) { + uri->server = xmlMemStrdup(server); + path = "/"; + } else { + uri->server = (char *) xmlStrndup(BAD_CAST server, + end - server); + path = end; + } + if (uri->server == NULL) { + ret = -1; + goto done; + } + } + + if ((((path[0] >= 'A') && (path[0] <= 'Z')) || + ((path[0] >= 'a') && (path[0] <= 'z'))) && + (path[1] == ':')) { + prependSlash = 1; + isFileScheme = 1; + } + + if (isFileScheme) { + uri->scheme = xmlMemStrdup("file"); + if (uri->scheme == NULL) { + ret = -1; + goto done; + } + + if (uri->server == NULL) + uri->port = PORT_EMPTY_SERVER; + } +#endif + } + + pathSize = strlen(path); + uri->path = xmlMalloc(pathSize + prependSlash + 1); + if (uri->path == NULL) { + ret = -1; + goto done; + } + if (prependSlash) { + uri->path[0] = '/'; + memcpy(uri->path + 1, path, pathSize + 1); + } else { + memcpy(uri->path, path, pathSize + 1); + } + } else { + ret = xmlParseURIReference(uri, str); + if (ret != 0) + goto done; + + xmlNormalizePath(uri->path, /* isFile */ 0); + } + +#if defined(_WIN32) || defined(__CYGWIN__) + if ((uri->path[0] == '/') && + (((uri->path[1] >= 'A') && (uri->path[1] <= 'Z')) || + ((uri->path[1] >= 'a') && (uri->path[1] <= 'z'))) && + (uri->path[2] == ':')) + *drive = uri->path[1]; +#endif + + *out = uri; + uri = NULL; + ret = 0; + +done: + xmlFreeURI(uri); + xmlFree(buf); + + return(ret); +} + +/** + * xmlBuildRelativeURISafe: * @URI: the URI reference under consideration * @base: the base value + * @valPtr: pointer to result URI * * Expresses the URI of the reference in terms relative to the * base. Some examples of this operation include: @@ -2179,106 +2518,112 @@ xmlBuildURI(const xmlChar *URI, const xmlChar *base) { * since this routine (for reasonable efficiency) assumes URI has * already been through some validation. * - * Returns a new URI string (to be freed by the caller) or NULL in case - * error. + * Available since 2.13.0. + * + * Returns 0 on success, -1 if a memory allocation failed or an error + * code if URI or base are invalid. */ -xmlChar * -xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) +int +xmlBuildRelativeURISafe(const xmlChar * URI, const xmlChar * base, + xmlChar **valPtr) { xmlChar *val = NULL; - int ret; + int ret = 0; int ix; int nbslash = 0; int len; xmlURIPtr ref = NULL; xmlURIPtr bas = NULL; - xmlChar *bptr, *uptr, *vptr; + const xmlChar *bptr, *uptr, *rptr; + xmlChar *vptr; int remove_path = 0; + int refDrive, baseDrive; + if (valPtr == NULL) + return(1); + *valPtr = NULL; if ((URI == NULL) || (*URI == 0)) - return NULL; + return(1); - /* - * First parse URI into a standard form - */ - ref = xmlCreateURI (); - if (ref == NULL) - return NULL; - /* If URI not already in "relative" form */ - if (URI[0] != '.') { - ret = xmlParseURIReference (ref, (const char *) URI); - if (ret != 0) - goto done; /* Error in URI, return NULL */ - } else - ref->path = (char *)xmlStrdup(URI); + ret = xmlParseUriOrPath((char *) URI, &ref, &refDrive); + if (ret < 0) + goto done; + if (ret != 0) { + /* Return URI if URI is invalid */ + ret = 0; + val = xmlStrdup(URI); + if (val == NULL) + ret = -1; + goto done; + } - /* - * Next parse base into the same standard form - */ - if ((base == NULL) || (*base == 0)) { - val = xmlStrdup (URI); - goto done; + /* Return URI if base is empty */ + if ((base == NULL) || (*base == 0)) + goto done; + + ret = xmlParseUriOrPath((char *) base, &bas, &baseDrive); + if (ret < 0) + goto done; + if (ret != 0) { + /* Return URI if base is invalid */ + ret = 0; + goto done; } - bas = xmlCreateURI (); - if (bas == NULL) - goto done; - if (base[0] != '.') { - ret = xmlParseURIReference (bas, (const char *) base); - if (ret != 0) - goto done; /* Error in base, return NULL */ - } else - bas->path = (char *)xmlStrdup(base); /* * If the scheme / server on the URI differs from the base, * just return the URI */ - if ((ref->scheme != NULL) && - ((bas->scheme == NULL) || - (xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) || - (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)) || - (bas->port != ref->port))) { - val = xmlStrdup (URI); + if ((xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) || + (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)) || + (bas->port != ref->port) || + (baseDrive != refDrive)) { goto done; } if (xmlStrEqual((xmlChar *)bas->path, (xmlChar *)ref->path)) { val = xmlStrdup(BAD_CAST ""); + if (val == NULL) + ret = -1; goto done; } if (bas->path == NULL) { val = xmlStrdup((xmlChar *)ref->path); - goto done; + if (val == NULL) { + ret = -1; + goto done; + } + goto escape; } if (ref->path == NULL) { ref->path = (char *) "/"; remove_path = 1; } + bptr = (xmlChar *) bas->path; + rptr = (xmlChar *) ref->path; + /* - * At this point (at last!) we can compare the two paths - * - * First we take care of the special case where either of the - * two path components may be missing (bug 316224) + * Return URI if URI and base aren't both absolute or relative. + */ + if ((bptr[0] == '/') != (rptr[0] == '/')) + goto done; + + /* + * At this point we can compare the two paths */ - bptr = (xmlChar *)bas->path; { - xmlChar *rptr = (xmlChar *) ref->path; int pos = 0; /* * Next we compare the two strings and find where they first differ */ - if ((*rptr == '.') && (rptr[1] == '/')) - rptr += 2; - if ((*bptr == '.') && (bptr[1] == '/')) - bptr += 2; - else if ((*bptr == '/') && (*rptr != '/')) - bptr++; while ((bptr[pos] == rptr[pos]) && (bptr[pos] != 0)) pos++; if (bptr[pos] == rptr[pos]) { val = xmlStrdup(BAD_CAST ""); + if (val == NULL) + ret = -1; goto done; /* (I can't imagine why anyone would do this) */ } @@ -2306,6 +2651,8 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) */ if (nbslash == 0 && !uptr[0]) { val = xmlStrdup(BAD_CAST "./"); + if (val == NULL) + ret = -1; goto done; } @@ -2313,9 +2660,12 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) } if (nbslash == 0) { - if (uptr != NULL) + if (uptr != NULL) { /* exception characters from xmlSaveUri */ val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,"); + if (val == NULL) + ret = -1; + } goto done; } @@ -2326,7 +2676,7 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) */ val = (xmlChar *) xmlMalloc (len + 3 * nbslash); if (val == NULL) { - xmlURIErrMemory("building relative URI\n"); + ret = -1; goto done; } vptr = val; @@ -2354,13 +2704,24 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) vptr[len - 1] = 0; } +escape: /* escape the freshly-built path */ vptr = val; - /* exception characters from xmlSaveUri */ + /* exception characters from xmlSaveUri */ val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,"); + if (val == NULL) + ret = -1; + else + ret = 0; xmlFree(vptr); done: + if ((ret == 0) && (val == NULL)) { + val = xmlSaveUri(ref); + if (val == NULL) + ret = -1; + } + /* * Free the working variables */ @@ -2370,153 +2731,75 @@ xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base) xmlFreeURI (ref); if (bas != NULL) xmlFreeURI (bas); + if (ret != 0) { + xmlFree(val); + val = NULL; + } + + *valPtr = val; + return(ret); +} - return val; +/* + * xmlBuildRelativeURI: + * @URI: the URI reference under consideration + * @base: the base value + * + * See xmlBuildRelativeURISafe. + * + * Returns a new URI string (to be freed by the caller) or NULL in case + * error. + */ +xmlChar * +xmlBuildRelativeURI(const xmlChar * URI, const xmlChar * base) +{ + xmlChar *val; + + xmlBuildRelativeURISafe(URI, base, &val); + return(val); } /** * xmlCanonicPath: * @path: the resource locator in a filesystem notation * - * Constructs a canonic path from the specified path. + * Prepares a path. * - * Returns a new canonic path, or a duplicate of the path parameter if the - * construction fails. The caller is responsible for freeing the memory occupied + * If the path contains the substring "://", it is considered a + * Legacy Extended IRI. Characters which aren't allowed in URIs are + * escaped. + * + * Otherwise, the path is considered a filesystem path which is + * copied without modification. + * + * The caller is responsible for freeing the memory occupied * by the returned string. If there is insufficient memory available, or the * argument is NULL, the function returns NULL. + * + * Returns the escaped path. */ -#define IS_WINDOWS_PATH(p) \ - ((p != NULL) && \ - (((p[0] >= 'a') && (p[0] <= 'z')) || \ - ((p[0] >= 'A') && (p[0] <= 'Z'))) && \ - (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\'))) xmlChar * xmlCanonicPath(const xmlChar *path) { -/* - * For Windows implementations, additional work needs to be done to - * replace backslashes in pathnames with "forward slashes" - */ -#if defined(_WIN32) - int len = 0; - char *p = NULL; -#endif - xmlURIPtr uri; xmlChar *ret; - const xmlChar *absuri; if (path == NULL) return(NULL); -#if defined(_WIN32) - /* - * We must not change the backslashes to slashes if the the path - * starts with \\?\ - * Those paths can be up to 32k characters long. - * Was added specifically for OpenOffice, those paths can't be converted - * to URIs anyway. - */ - if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && - (path[3] == '\\') ) - return xmlStrdup((const xmlChar *) path); -#endif - - /* sanitize filename starting with // so it can be used as URI */ - if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/')) - path++; - - if ((uri = xmlParseURI((const char *) path)) != NULL) { - xmlFreeURI(uri); - return xmlStrdup(path); - } - /* Check if this is an "absolute uri" */ - absuri = xmlStrstr(path, BAD_CAST "://"); - if (absuri != NULL) { - int l, j; - unsigned char c; - xmlChar *escURI; - - /* - * this looks like an URI where some parts have not been - * escaped leading to a parsing problem. Check that the first - * part matches a protocol. - */ - l = absuri - path; - /* Bypass if first part (part before the '://') is > 20 chars */ - if ((l <= 0) || (l > 20)) - goto path_processing; - /* Bypass if any non-alpha characters are present in first part */ - for (j = 0;j < l;j++) { - c = path[j]; - if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')))) - goto path_processing; - } - - /* Escape all except the characters specified in the supplied path */ - escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;="); - if (escURI != NULL) { - /* Try parsing the escaped path */ - uri = xmlParseURI((const char *) escURI); - /* If successful, return the escaped string */ - if (uri != NULL) { - xmlFreeURI(uri); - return escURI; - } - xmlFree(escURI); - } - } - -path_processing: -/* For Windows implementations, replace backslashes with 'forward slashes' */ -#if defined(_WIN32) - /* - * Create a URI structure - */ - uri = xmlCreateURI(); - if (uri == NULL) { /* Guard against 'out of memory' */ - return(NULL); - } - - len = xmlStrlen(path); - if ((len > 2) && IS_WINDOWS_PATH(path)) { - /* make the scheme 'file' */ - uri->scheme = (char *) xmlStrdup(BAD_CAST "file"); - /* allocate space for leading '/' + path + string terminator */ - uri->path = xmlMallocAtomic(len + 2); - if (uri->path == NULL) { - xmlFreeURI(uri); /* Guard against 'out of memory' */ - return(NULL); - } - /* Put in leading '/' plus path */ - uri->path[0] = '/'; - p = uri->path + 1; - strncpy(p, (char *) path, len + 1); - } else { - uri->path = (char *) xmlStrdup(path); - if (uri->path == NULL) { - xmlFreeURI(uri); - return(NULL); - } - p = uri->path; - } - /* Now change all occurrences of '\' to '/' */ - while (*p != '\0') { - if (*p == '\\') - *p = '/'; - p++; - } - - if (uri->scheme == NULL) { - ret = xmlStrdup((const xmlChar *) uri->path); + if (xmlStrstr(path, BAD_CAST "://") != NULL) { + /* + * Escape all characters except reserved, unreserved and the + * percent sign. + * + * xmlURIEscapeStr already keeps unreserved characters, so we + * pass gen-delims, sub-delims and "%" to ignore. + */ + ret = xmlURIEscapeStr(path, BAD_CAST ":/?#[]@!$&()*+,;='%"); } else { - ret = xmlSaveUri(uri); + ret = xmlStrdup((const xmlChar *) path); } - xmlFreeURI(uri); -#else - ret = xmlStrdup((const xmlChar *) path); -#endif return(ret); } @@ -2534,41 +2817,5 @@ xmlCanonicPath(const xmlChar *path) xmlChar * xmlPathToURI(const xmlChar *path) { - xmlURIPtr uri; - xmlURI temp; - xmlChar *ret, *cal; - - if (path == NULL) - return(NULL); - - if ((uri = xmlParseURI((const char *) path)) != NULL) { - xmlFreeURI(uri); - return xmlStrdup(path); - } - cal = xmlCanonicPath(path); - if (cal == NULL) - return(NULL); -#if defined(_WIN32) - /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?) - If 'cal' is a valid URI already then we are done here, as continuing would make - it invalid. */ - if ((uri = xmlParseURI((const char *) cal)) != NULL) { - xmlFreeURI(uri); - return cal; - } - /* 'cal' can contain a relative path with backslashes. If that is processed - by xmlSaveURI, they will be escaped and the external entity loader machinery - will fail. So convert them to slashes. Misuse 'ret' for walking. */ - ret = cal; - while (*ret != '\0') { - if (*ret == '\\') - *ret = '/'; - ret++; - } -#endif - memset(&temp, 0, sizeof(temp)); - temp.path = (char *) cal; - ret = xmlSaveUri(&temp); - xmlFree(cal); - return(ret); + return(xmlCanonicPath(path)); } diff --git a/libraries/libxml2/valid.c b/libraries/libxml2/valid.c index 76d657d6..7b9cd0d3 100644 --- a/libraries/libxml2/valid.c +++ b/libraries/libxml2/valid.c @@ -21,18 +21,16 @@ #include #include #include +#include #include "private/error.h" #include "private/parser.h" +#include "private/regexp.h" +#include "private/save.h" +#include "private/tree.h" static xmlElementPtr -xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, - int create); - -#define TODO \ - xmlGenericError(xmlGenericErrorContext, \ - "Unimplemented block at %s:%d\n", \ - __FILE__, __LINE__); +xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name); #ifdef LIBXML_VALID_ENABLED static int @@ -53,31 +51,56 @@ xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type, * Handle an out of memory error */ static void -xmlVErrMemory(xmlValidCtxtPtr ctxt, const char *extra) +xmlVErrMemory(xmlValidCtxtPtr ctxt) { - xmlGenericErrorFunc channel = NULL; + if (ctxt != NULL) { + if (ctxt->flags & XML_VCTXT_USE_PCTXT) { + xmlCtxtErrMemory(ctxt->userData); + } else { + xmlRaiseMemoryError(NULL, ctxt->error, ctxt->userData, + XML_FROM_VALID, NULL); + } + } else { + xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_VALID, NULL); + } +} + +static void +xmlDoErrValid(xmlValidCtxtPtr ctxt, xmlNodePtr node, + xmlParserErrors code, int level, + const xmlChar *str1, const xmlChar *str2, const xmlChar *str3, + int int1, + const char *msg, ...) { xmlParserCtxtPtr pctxt = NULL; - void *data = NULL; + va_list ap; - if (ctxt != NULL) { - channel = ctxt->error; - data = ctxt->userData; - /* Look up flag to detect if it is part of a parsing - context */ - if (ctxt->flags & XML_VCTXT_USE_PCTXT) { - pctxt = ctxt->userData; - } + if (ctxt == NULL) + return; + if (ctxt->flags & XML_VCTXT_USE_PCTXT) + pctxt = ctxt->userData; + + va_start(ap, msg); + if (pctxt != NULL) { + xmlCtxtVErr(pctxt, node, XML_FROM_VALID, code, level, + str1, str2, str3, int1, msg, ap); + } else { + xmlGenericErrorFunc channel = NULL; + void *data = NULL; + int res; + + if (ctxt != NULL) { + channel = ctxt->error; + data = ctxt->userData; + } + res = xmlVRaiseError(NULL, channel, data, NULL, node, + XML_FROM_VALID, code, level, NULL, 0, + (const char *) str1, (const char *) str2, + (const char *) str2, int1, 0, + msg, ap); + if (res < 0) + xmlVErrMemory(ctxt); } - if (extra) - __xmlRaiseError(NULL, channel, data, - pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, - XML_ERR_FATAL, NULL, 0, extra, NULL, NULL, 0, 0, - "Memory allocation failed : %s\n", extra); - else - __xmlRaiseError(NULL, channel, data, - pctxt, NULL, XML_FROM_VALID, XML_ERR_NO_MEMORY, - XML_ERR_FATAL, NULL, 0, NULL, NULL, NULL, 0, 0, - "Memory allocation failed\n"); + va_end(ap); } /** @@ -92,29 +115,8 @@ static void LIBXML_ATTR_FORMAT(3,0) xmlErrValid(xmlValidCtxtPtr ctxt, xmlParserErrors error, const char *msg, const char *extra) { - xmlGenericErrorFunc channel = NULL; - xmlParserCtxtPtr pctxt = NULL; - void *data = NULL; - - if (ctxt != NULL) { - channel = ctxt->error; - data = ctxt->userData; - /* Look up flag to detect if it is part of a parsing - context */ - if (ctxt->flags & XML_VCTXT_USE_PCTXT) { - pctxt = ctxt->userData; - } - } - if (extra) - __xmlRaiseError(NULL, channel, data, - pctxt, NULL, XML_FROM_VALID, error, - XML_ERR_ERROR, NULL, 0, extra, NULL, NULL, 0, 0, - msg, extra); - else - __xmlRaiseError(NULL, channel, data, - pctxt, NULL, XML_FROM_VALID, error, - XML_ERR_ERROR, NULL, 0, NULL, NULL, NULL, 0, 0, - "%s", msg); + xmlDoErrValid(ctxt, NULL, error, XML_ERR_ERROR, (const xmlChar *) extra, + NULL, NULL, 0, msg, extra); } #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_SCHEMAS_ENABLED) @@ -135,25 +137,8 @@ xmlErrValidNode(xmlValidCtxtPtr ctxt, const char *msg, const xmlChar * str1, const xmlChar * str2, const xmlChar * str3) { - xmlStructuredErrorFunc schannel = NULL; - xmlGenericErrorFunc channel = NULL; - xmlParserCtxtPtr pctxt = NULL; - void *data = NULL; - - if (ctxt != NULL) { - channel = ctxt->error; - data = ctxt->userData; - /* Look up flag to detect if it is part of a parsing - context */ - if (ctxt->flags & XML_VCTXT_USE_PCTXT) { - pctxt = ctxt->userData; - } - } - __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, - XML_ERR_ERROR, NULL, 0, - (const char *) str1, - (const char *) str2, - (const char *) str3, 0, 0, msg, str1, str2, str3); + xmlDoErrValid(ctxt, node, error, XML_ERR_ERROR, str1, str2, str3, 0, + msg, str1, str2, str3); } #endif /* LIBXML_VALID_ENABLED or LIBXML_SCHEMAS_ENABLED */ @@ -175,25 +160,8 @@ xmlErrValidNodeNr(xmlValidCtxtPtr ctxt, const char *msg, const xmlChar * str1, int int2, const xmlChar * str3) { - xmlStructuredErrorFunc schannel = NULL; - xmlGenericErrorFunc channel = NULL; - xmlParserCtxtPtr pctxt = NULL; - void *data = NULL; - - if (ctxt != NULL) { - channel = ctxt->error; - data = ctxt->userData; - /* Look up flag to detect if it is part of a parsing - context */ - if (ctxt->flags & XML_VCTXT_USE_PCTXT) { - pctxt = ctxt->userData; - } - } - __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, - XML_ERR_ERROR, NULL, 0, - (const char *) str1, - (const char *) str3, - NULL, int2, 0, msg, str1, int2, str3); + xmlDoErrValid(ctxt, node, error, XML_ERR_ERROR, str1, str3, NULL, int2, + msg, str1, int2, str3); } /** @@ -213,25 +181,8 @@ xmlErrValidWarning(xmlValidCtxtPtr ctxt, const char *msg, const xmlChar * str1, const xmlChar * str2, const xmlChar * str3) { - xmlStructuredErrorFunc schannel = NULL; - xmlGenericErrorFunc channel = NULL; - xmlParserCtxtPtr pctxt = NULL; - void *data = NULL; - - if (ctxt != NULL) { - channel = ctxt->warning; - data = ctxt->userData; - /* Look up flag to detect if it is part of a parsing - context */ - if (ctxt->flags & XML_VCTXT_USE_PCTXT) { - pctxt = ctxt->userData; - } - } - __xmlRaiseError(schannel, channel, data, pctxt, node, XML_FROM_VALID, error, - XML_ERR_WARNING, NULL, 0, - (const char *) str1, - (const char *) str2, - (const char *) str3, 0, 0, msg, str1, str2, str3); + xmlDoErrValid(ctxt, node, error, XML_ERR_WARNING, str1, str2, str3, 0, + msg, str1, str2, str3); } @@ -260,7 +211,7 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) { ctxt->vstateTab = (xmlValidState *) xmlMalloc(ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (ctxt->vstateTab == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); return(-1); } } @@ -271,7 +222,7 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) { tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (tmp == NULL) { - xmlVErrMemory(ctxt, "realloc failed"); + xmlVErrMemory(ctxt); return(-1); } ctxt->vstateMax *= 2; @@ -286,6 +237,10 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementPtr elemDecl, xmlNodePtr node) { if (elemDecl->contModel != NULL) { ctxt->vstateTab[ctxt->vstateNr].exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); + if (ctxt->vstateTab[ctxt->vstateNr].exec == NULL) { + xmlVErrMemory(ctxt); + return(-1); + } } else { ctxt->vstateTab[ctxt->vstateNr].exec = NULL; xmlErrValidNode(ctxt, (xmlNodePtr) elemDecl, @@ -367,7 +322,7 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, ctxt->vstateTab = (xmlValidState *) xmlMalloc( ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (ctxt->vstateTab == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); return(-1); } } @@ -377,7 +332,7 @@ vstateVPush(xmlValidCtxtPtr ctxt, xmlElementContentPtr cont, tmp = (xmlValidState *) xmlRealloc(ctxt->vstateTab, 2 * ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (tmp == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); return(-1); } ctxt->vstateMax *= 2; @@ -425,7 +380,7 @@ nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value) (xmlNodePtr *) xmlMalloc(ctxt->nodeMax * sizeof(ctxt->nodeTab[0])); if (ctxt->nodeTab == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); ctxt->nodeMax = 0; return (0); } @@ -435,7 +390,7 @@ nodeVPush(xmlValidCtxtPtr ctxt, xmlNodePtr value) tmp = (xmlNodePtr *) xmlRealloc(ctxt->nodeTab, ctxt->nodeMax * 2 * sizeof(ctxt->nodeTab[0])); if (tmp == NULL) { - xmlVErrMemory(ctxt, "realloc failed"); + xmlVErrMemory(ctxt); return (0); } ctxt->nodeMax *= 2; @@ -512,7 +467,7 @@ xmlValidBuildAContentModel(xmlElementContentPtr content, fullname = xmlBuildQName(content->name, content->prefix, fn, 50); if (fullname == NULL) { - xmlVErrMemory(ctxt, "Building content model"); + xmlVErrMemory(ctxt); return(0); } @@ -557,11 +512,13 @@ xmlValidBuildAContentModel(xmlElementContentPtr content, oldstate = ctxt->state; } do { - xmlValidBuildAContentModel(content->c1, ctxt, name); + if (xmlValidBuildAContentModel(content->c1, ctxt, name) == 0) + return(0); content = content->c2; } while ((content->type == XML_ELEMENT_CONTENT_SEQ) && (content->ocur == XML_ELEMENT_CONTENT_ONCE)); - xmlValidBuildAContentModel(content, ctxt, name); + if (xmlValidBuildAContentModel(content, ctxt, name) == 0) + return(0); oldend = ctxt->state; ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); switch (ocur) { @@ -599,13 +556,15 @@ xmlValidBuildAContentModel(xmlElementContentPtr content, */ do { ctxt->state = oldstate; - xmlValidBuildAContentModel(content->c1, ctxt, name); + if (xmlValidBuildAContentModel(content->c1, ctxt, name) == 0) + return(0); xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); content = content->c2; } while ((content->type == XML_ELEMENT_CONTENT_OR) && (content->ocur == XML_ELEMENT_CONTENT_ONCE)); ctxt->state = oldstate; - xmlValidBuildAContentModel(content, ctxt, name); + if (xmlValidBuildAContentModel(content, ctxt, name) == 0) + return(0); xmlAutomataNewEpsilon(ctxt->am, ctxt->state, oldend); ctxt->state = xmlAutomataNewEpsilon(ctxt->am, oldend, NULL); switch (ocur) { @@ -637,6 +596,8 @@ xmlValidBuildAContentModel(xmlElementContentPtr content, * @ctxt: a validation context * @elem: an element declaration node * + * DEPRECATED: Internal function, don't use. + * * (Re)Build the automata associated to the content model of this * element * @@ -644,6 +605,7 @@ xmlValidBuildAContentModel(xmlElementContentPtr content, */ int xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { + int ret = 0; if ((ctxt == NULL) || (elem == NULL)) return(0); @@ -662,16 +624,18 @@ xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { ctxt->am = xmlNewAutomata(); if (ctxt->am == NULL) { - xmlErrValidNode(ctxt, (xmlNodePtr) elem, - XML_ERR_INTERNAL_ERROR, - "Cannot create automata for element %s\n", - elem->name, NULL, NULL); + xmlVErrMemory(ctxt); return(0); } ctxt->state = xmlAutomataGetInitState(ctxt->am); - xmlValidBuildAContentModel(elem->content, ctxt, elem->name); + if (xmlValidBuildAContentModel(elem->content, ctxt, elem->name) == 0) + goto done; xmlAutomataSetFinalState(ctxt->am, ctxt->state); elem->contModel = xmlAutomataCompile(ctxt->am); + if (elem->contModel == NULL) { + xmlVErrMemory(ctxt); + goto done; + } if (xmlRegexpIsDeterminist(elem->contModel) != 1) { char expr[5000]; expr[0] = 0; @@ -681,15 +645,16 @@ xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { "Content model of %s is not deterministic: %s\n", elem->name, BAD_CAST expr, NULL); ctxt->valid = 0; - ctxt->state = NULL; - xmlFreeAutomata(ctxt->am); - ctxt->am = NULL; - return(0); + goto done; } + + ret = 1; + +done: ctxt->state = NULL; xmlFreeAutomata(ctxt->am); ctxt->am = NULL; - return(1); + return(ret); } #endif /* LIBXML_REGEXP_ENABLED */ @@ -710,10 +675,8 @@ xmlValidBuildContentModel(xmlValidCtxtPtr ctxt, xmlElementPtr elem) { xmlValidCtxtPtr xmlNewValidCtxt(void) { xmlValidCtxtPtr ret; - if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if ((ret = xmlMalloc(sizeof (xmlValidCtxt))) == NULL) return (NULL); - } (void) memset(ret, 0, sizeof (xmlValidCtxt)); @@ -782,10 +745,8 @@ xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name, return(NULL); } ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); - if (ret == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlElementContent)); ret->type = type; ret->ocur = XML_ELEMENT_CONTENT_ONCE; @@ -807,9 +768,17 @@ xmlNewDocElementContent(xmlDocPtr doc, const xmlChar *name, ret->prefix = xmlDictLookup(dict, name, l); ret->name = xmlDictLookup(dict, tmp, -1); } + if (ret->prefix == NULL) + goto error; } + if (ret->name == NULL) + goto error; } return(ret); + +error: + xmlFreeDocElementContent(doc, ret); + return(NULL); } /** @@ -847,10 +816,8 @@ xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { dict = doc->dict; ret = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); - if (ret == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlElementContent)); ret->type = cur->type; ret->ocur = cur->ocur; @@ -859,6 +826,8 @@ xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { ret->name = xmlDictLookup(dict, cur->name, -1); else ret->name = xmlStrdup(cur->name); + if (ret->name == NULL) + goto error; } if (cur->prefix != NULL) { @@ -866,20 +835,22 @@ xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { ret->prefix = xmlDictLookup(dict, cur->prefix, -1); else ret->prefix = xmlStrdup(cur->prefix); + if (ret->prefix == NULL) + goto error; } - if (cur->c1 != NULL) + if (cur->c1 != NULL) { ret->c1 = xmlCopyDocElementContent(doc, cur->c1); - if (ret->c1 != NULL) + if (ret->c1 == NULL) + goto error; ret->c1->parent = ret; + } if (cur->c2 != NULL) { prev = ret; cur = cur->c2; while (cur != NULL) { tmp = (xmlElementContentPtr) xmlMalloc(sizeof(xmlElementContent)); - if (tmp == NULL) { - xmlVErrMemory(NULL, "malloc failed"); - return(ret); - } + if (tmp == NULL) + goto error; memset(tmp, 0, sizeof(xmlElementContent)); tmp->type = cur->type; tmp->ocur = cur->ocur; @@ -890,6 +861,8 @@ xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { tmp->name = xmlDictLookup(dict, cur->name, -1); else tmp->name = xmlStrdup(cur->name); + if (tmp->name == NULL) + goto error; } if (cur->prefix != NULL) { @@ -897,16 +870,24 @@ xmlCopyDocElementContent(xmlDocPtr doc, xmlElementContentPtr cur) { tmp->prefix = xmlDictLookup(dict, cur->prefix, -1); else tmp->prefix = xmlStrdup(cur->prefix); + if (tmp->prefix == NULL) + goto error; } - if (cur->c1 != NULL) + if (cur->c1 != NULL) { tmp->c1 = xmlCopyDocElementContent(doc,cur->c1); - if (tmp->c1 != NULL) + if (tmp->c1 == NULL) + goto error; tmp->c1->parent = tmp; + } prev = tmp; cur = cur->c2; } } return(ret); + +error: + xmlFreeElementContent(ret); + return(NULL); } /** @@ -1002,105 +983,6 @@ xmlFreeElementContent(xmlElementContentPtr cur) { } #ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlDumpElementOccur: - * @buf: An XML buffer - * @cur: An element table - * - * Dump the occurrence operator of an element. - */ -static void -xmlDumpElementOccur(xmlBufferPtr buf, xmlElementContentPtr cur) { - switch (cur->ocur) { - case XML_ELEMENT_CONTENT_ONCE: - break; - case XML_ELEMENT_CONTENT_OPT: - xmlBufferWriteChar(buf, "?"); - break; - case XML_ELEMENT_CONTENT_MULT: - xmlBufferWriteChar(buf, "*"); - break; - case XML_ELEMENT_CONTENT_PLUS: - xmlBufferWriteChar(buf, "+"); - break; - } -} - -/** - * xmlDumpElementContent: - * @buf: An XML buffer - * @content: An element table - * - * This will dump the content of the element table as an XML DTD definition - */ -static void -xmlDumpElementContent(xmlBufferPtr buf, xmlElementContentPtr content) { - xmlElementContentPtr cur; - - if (content == NULL) return; - - xmlBufferWriteChar(buf, "("); - cur = content; - - do { - if (cur == NULL) return; - - switch (cur->type) { - case XML_ELEMENT_CONTENT_PCDATA: - xmlBufferWriteChar(buf, "#PCDATA"); - break; - case XML_ELEMENT_CONTENT_ELEMENT: - if (cur->prefix != NULL) { - xmlBufferWriteCHAR(buf, cur->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, cur->name); - break; - case XML_ELEMENT_CONTENT_SEQ: - case XML_ELEMENT_CONTENT_OR: - if ((cur != content) && - (cur->parent != NULL) && - ((cur->type != cur->parent->type) || - (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) - xmlBufferWriteChar(buf, "("); - cur = cur->c1; - continue; - default: - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "Internal: ELEMENT cur corrupted invalid type\n", - NULL); - } - - while (cur != content) { - xmlElementContentPtr parent = cur->parent; - - if (parent == NULL) return; - - if (((cur->type == XML_ELEMENT_CONTENT_OR) || - (cur->type == XML_ELEMENT_CONTENT_SEQ)) && - ((cur->type != parent->type) || - (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) - xmlBufferWriteChar(buf, ")"); - xmlDumpElementOccur(buf, cur); - - if (cur == parent->c1) { - if (parent->type == XML_ELEMENT_CONTENT_SEQ) - xmlBufferWriteChar(buf, " , "); - else if (parent->type == XML_ELEMENT_CONTENT_OR) - xmlBufferWriteChar(buf, " | "); - - cur = parent->c2; - break; - } - - cur = parent; - } - } while (cur != content); - - xmlBufferWriteChar(buf, ")"); - xmlDumpElementOccur(buf, content); -} - /** * xmlSprintfElementContent: * @buf: an output buffer @@ -1267,7 +1149,8 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, xmlElementPtr ret; xmlElementTablePtr table; xmlAttributePtr oldAttributes = NULL; - xmlChar *ns, *uqname; + const xmlChar *localName; + xmlChar *prefix = NULL; if (dtd == NULL) { return(NULL); @@ -1279,7 +1162,7 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, switch (type) { case XML_ELEMENT_TYPE_EMPTY: if (content != NULL) { - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR, "xmlAddElementDecl: content != NULL for EMPTY\n", NULL); return(NULL); @@ -1287,7 +1170,7 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, break; case XML_ELEMENT_TYPE_ANY: if (content != NULL) { - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR, "xmlAddElementDecl: content != NULL for ANY\n", NULL); return(NULL); @@ -1295,7 +1178,7 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, break; case XML_ELEMENT_TYPE_MIXED: if (content == NULL) { - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR, "xmlAddElementDecl: content == NULL for MIXED\n", NULL); return(NULL); @@ -1303,25 +1186,24 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, break; case XML_ELEMENT_TYPE_ELEMENT: if (content == NULL) { - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, + xmlErrValid(ctxt, XML_DTD_CONTENT_ERROR, "xmlAddElementDecl: content == NULL for ELEMENT\n", NULL); return(NULL); } break; default: - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, - "Internal: ELEMENT decl corrupted invalid type\n", - NULL); + xmlErrValid(ctxt, XML_ERR_ARGUMENT, + "xmlAddElementDecl: invalid type\n", NULL); return(NULL); } /* * check if name is a QName */ - uqname = xmlSplitQName2(name, &ns); - if (uqname != NULL) - name = uqname; + localName = xmlSplitQName4(name, &prefix); + if (localName == NULL) + goto mem_error; /* * Create the Element table if needed. @@ -1333,28 +1215,22 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, if (dtd->doc != NULL) dict = dtd->doc->dict; table = xmlHashCreateDict(0, dict); + if (table == NULL) + goto mem_error; dtd->elements = (void *) table; } - if (table == NULL) { - xmlVErrMemory(ctxt, - "xmlAddElementDecl: Table creation failed!\n"); - if (uqname != NULL) - xmlFree(uqname); - if (ns != NULL) - xmlFree(ns); - return(NULL); - } /* * lookup old attributes inserted on an undefined element in the * internal subset. */ if ((dtd->doc != NULL) && (dtd->doc->intSubset != NULL)) { - ret = xmlHashLookup2(dtd->doc->intSubset->elements, name, ns); + ret = xmlHashLookup2(dtd->doc->intSubset->elements, localName, prefix); if ((ret != NULL) && (ret->etype == XML_ELEMENT_TYPE_UNDEFINED)) { oldAttributes = ret->attributes; ret->attributes = NULL; - xmlHashRemoveEntry2(dtd->doc->intSubset->elements, name, ns, NULL); + xmlHashRemoveEntry2(dtd->doc->intSubset->elements, localName, prefix, + NULL); xmlFreeElement(ret); } } @@ -1363,7 +1239,7 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, * The element may already be present if one of its attribute * was registered first */ - ret = xmlHashLookup2(table, name, ns); + ret = xmlHashLookup2(table, localName, prefix); if (ret != NULL) { if (ret->etype != XML_ELEMENT_TYPE_UNDEFINED) { #ifdef LIBXML_VALID_ENABLED @@ -1374,61 +1250,42 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, "Redefinition of element %s\n", name, NULL, NULL); #endif /* LIBXML_VALID_ENABLED */ - if (uqname != NULL) - xmlFree(uqname); - if (ns != NULL) - xmlFree(ns); + if (prefix != NULL) + xmlFree(prefix); return(NULL); } - if (ns != NULL) { - xmlFree(ns); - ns = NULL; + if (prefix != NULL) { + xmlFree(prefix); + prefix = NULL; } } else { + int res; + ret = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); - if (ret == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - if (uqname != NULL) - xmlFree(uqname); - if (ns != NULL) - xmlFree(ns); - return(NULL); - } + if (ret == NULL) + goto mem_error; memset(ret, 0, sizeof(xmlElement)); ret->type = XML_ELEMENT_DECL; /* * fill the structure. */ - ret->name = xmlStrdup(name); + ret->name = xmlStrdup(localName); if (ret->name == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - if (uqname != NULL) - xmlFree(uqname); - if (ns != NULL) - xmlFree(ns); xmlFree(ret); - return(NULL); + goto mem_error; } - ret->prefix = ns; + ret->prefix = prefix; + prefix = NULL; /* * Validity Check: * Insertion must not fail */ - if (xmlHashAddEntry2(table, name, ns, ret)) { -#ifdef LIBXML_VALID_ENABLED - /* - * The element is already defined in this DTD. - */ - xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_ELEM_REDEFINED, - "Redefinition of element %s\n", - name, NULL, NULL); -#endif /* LIBXML_VALID_ENABLED */ + res = xmlHashAdd2(table, localName, ret->prefix, ret); + if (res <= 0) { xmlFreeElement(ret); - if (uqname != NULL) - xmlFree(uqname); - return(NULL); + goto mem_error; } /* * For new element, may have attributes from earlier @@ -1446,12 +1303,15 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, * and flag it by setting a special parent value * so the parser doesn't unallocate it. */ - if ((ctxt != NULL) && (ctxt->flags & XML_VCTXT_USE_PCTXT)) { - ret->content = content; - if (content != NULL) - content->parent = (xmlElementContentPtr) 1; - } else { - ret->content = xmlCopyDocElementContent(dtd->doc, content); + if (content != NULL) { + if ((ctxt != NULL) && (ctxt->flags & XML_VCTXT_USE_PCTXT)) { + ret->content = content; + content->parent = (xmlElementContentPtr) 1; + } else if (content != NULL){ + ret->content = xmlCopyDocElementContent(dtd->doc, content); + if (ret->content == NULL) + goto mem_error; + } } /* @@ -1466,9 +1326,15 @@ xmlAddElementDecl(xmlValidCtxtPtr ctxt, ret->prev = dtd->last; dtd->last = (xmlNodePtr) ret; } - if (uqname != NULL) - xmlFree(uqname); + if (prefix != NULL) + xmlFree(prefix); return(ret); + +mem_error: + xmlVErrMemory(ctxt); + if (prefix != NULL) + xmlFree(prefix); + return(NULL); } static void @@ -1502,25 +1368,33 @@ xmlCopyElement(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { xmlElementPtr cur; cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); - if (cur == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlElement)); cur->type = XML_ELEMENT_DECL; cur->etype = elem->etype; - if (elem->name != NULL) + if (elem->name != NULL) { cur->name = xmlStrdup(elem->name); - else - cur->name = NULL; - if (elem->prefix != NULL) + if (cur->name == NULL) + goto error; + } + if (elem->prefix != NULL) { cur->prefix = xmlStrdup(elem->prefix); - else - cur->prefix = NULL; - cur->content = xmlCopyElementContent(elem->content); + if (cur->prefix == NULL) + goto error; + } + if (elem->content != NULL) { + cur->content = xmlCopyElementContent(elem->content); + if (cur->content == NULL) + goto error; + } /* TODO : rebuild the attribute list on the copy */ cur->attributes = NULL; return(cur); + +error: + xmlFreeElement(cur); + return(NULL); } /** @@ -1533,7 +1407,7 @@ xmlCopyElement(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { */ xmlElementTablePtr xmlCopyElementTable(xmlElementTablePtr table) { - return((xmlElementTablePtr) xmlHashCopy(table, xmlCopyElement)); + return(xmlHashCopySafe(table, xmlCopyElement, xmlFreeElementTableEntry)); } #endif /* LIBXML_TREE_ENABLED */ @@ -1543,59 +1417,22 @@ xmlCopyElementTable(xmlElementTablePtr table) { * @buf: the XML buffer output * @elem: An element table * + * DEPRECATED: Use xmlSaveTree. + * * This will dump the content of the element declaration as an XML * DTD definition */ void xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (elem == NULL)) return; - switch (elem->etype) { - case XML_ELEMENT_TYPE_EMPTY: - xmlBufferWriteChar(buf, "prefix != NULL) { - xmlBufferWriteCHAR(buf, elem->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, elem->name); - xmlBufferWriteChar(buf, " EMPTY>\n"); - break; - case XML_ELEMENT_TYPE_ANY: - xmlBufferWriteChar(buf, "prefix != NULL) { - xmlBufferWriteCHAR(buf, elem->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, elem->name); - xmlBufferWriteChar(buf, " ANY>\n"); - break; - case XML_ELEMENT_TYPE_MIXED: - xmlBufferWriteChar(buf, "prefix != NULL) { - xmlBufferWriteCHAR(buf, elem->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, elem->name); - xmlBufferWriteChar(buf, " "); - xmlDumpElementContent(buf, elem->content); - xmlBufferWriteChar(buf, ">\n"); - break; - case XML_ELEMENT_TYPE_ELEMENT: - xmlBufferWriteChar(buf, "prefix != NULL) { - xmlBufferWriteCHAR(buf, elem->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, elem->name); - xmlBufferWriteChar(buf, " "); - xmlDumpElementContent(buf, elem->content); - xmlBufferWriteChar(buf, ">\n"); - break; - default: - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "Internal: ELEMENT struct corrupted invalid type\n", - NULL); - } + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlSaveTree(save, (xmlNodePtr) elem); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } /** @@ -1607,9 +1444,9 @@ xmlDumpElementDecl(xmlBufferPtr buf, xmlElementPtr elem) { * the arguments. */ static void -xmlDumpElementDeclScan(void *elem, void *buf, +xmlDumpElementDeclScan(void *elem, void *save, const xmlChar *name ATTRIBUTE_UNUSED) { - xmlDumpElementDecl((xmlBufferPtr) buf, (xmlElementPtr) elem); + xmlSaveTree(save, elem); } /** @@ -1617,13 +1454,21 @@ xmlDumpElementDeclScan(void *elem, void *buf, * @buf: the XML buffer output * @table: An element table * + * DEPRECATED: Don't use. + * * This will dump the content of the element table as an XML DTD definition */ void xmlDumpElementTable(xmlBufferPtr buf, xmlElementTablePtr table) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (table == NULL)) return; - xmlHashScan(table, xmlDumpElementDeclScan, buf); + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlHashScan(table, xmlDumpElementDeclScan, save); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } #endif /* LIBXML_OUTPUT_ENABLED */ @@ -1641,14 +1486,18 @@ xmlCreateEnumeration(const xmlChar *name) { xmlEnumerationPtr ret; ret = (xmlEnumerationPtr) xmlMalloc(sizeof(xmlEnumeration)); - if (ret == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlEnumeration)); - if (name != NULL) + if (name != NULL) { ret->name = xmlStrdup(name); + if (ret->name == NULL) { + xmlFree(ret); + return(NULL); + } + } + return(ret); } @@ -1660,12 +1509,14 @@ xmlCreateEnumeration(const xmlChar *name) { */ void xmlFreeEnumeration(xmlEnumerationPtr cur) { - if (cur == NULL) return; + while (cur != NULL) { + xmlEnumerationPtr next = cur->next; - if (cur->next != NULL) xmlFreeEnumeration(cur->next); + xmlFree((xmlChar *) cur->name); + xmlFree(cur); - if (cur->name != NULL) xmlFree((xmlChar *) cur->name); - xmlFree(cur); + cur = next; + } } #ifdef LIBXML_TREE_ENABLED @@ -1680,41 +1531,30 @@ xmlFreeEnumeration(xmlEnumerationPtr cur) { */ xmlEnumerationPtr xmlCopyEnumeration(xmlEnumerationPtr cur) { - xmlEnumerationPtr ret; + xmlEnumerationPtr ret = NULL; + xmlEnumerationPtr last = NULL; - if (cur == NULL) return(NULL); - ret = xmlCreateEnumeration((xmlChar *) cur->name); - if (ret == NULL) return(NULL); - - if (cur->next != NULL) ret->next = xmlCopyEnumeration(cur->next); - else ret->next = NULL; + while (cur != NULL) { + xmlEnumerationPtr copy = xmlCreateEnumeration(cur->name); - return(ret); -} -#endif /* LIBXML_TREE_ENABLED */ + if (copy == NULL) { + xmlFreeEnumeration(ret); + return(NULL); + } -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlDumpEnumeration: - * @buf: the XML buffer output - * @enum: An enumeration - * - * This will dump the content of the enumeration - */ -static void -xmlDumpEnumeration(xmlBufferPtr buf, xmlEnumerationPtr cur) { - if ((buf == NULL) || (cur == NULL)) - return; + if (ret == NULL) { + ret = last = copy; + } else { + last->next = copy; + last = copy; + } - xmlBufferWriteCHAR(buf, cur->name); - if (cur->next == NULL) - xmlBufferWriteChar(buf, ")"); - else { - xmlBufferWriteChar(buf, " | "); - xmlDumpEnumeration(buf, cur->next); + cur = cur->next; } + + return(ret); } -#endif /* LIBXML_OUTPUT_ENABLED */ +#endif /* LIBXML_TREE_ENABLED */ #ifdef LIBXML_VALID_ENABLED /** @@ -1814,10 +1654,11 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, const xmlChar *name, const xmlChar *ns, xmlAttributeType type, xmlAttributeDefault def, const xmlChar *defaultValue, xmlEnumerationPtr tree) { - xmlAttributePtr ret; + xmlAttributePtr ret = NULL; xmlAttributeTablePtr table; xmlElementPtr elemDef; xmlDictPtr dict = NULL; + int res; if (dtd == NULL) { xmlFreeEnumeration(tree); @@ -1860,9 +1701,8 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, case XML_ATTRIBUTE_NOTATION: break; default: - xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, - "Internal: ATTRIBUTE struct corrupted invalid type\n", - NULL); + xmlErrValid(ctxt, XML_ERR_ARGUMENT, + "xmlAddAttributeDecl: invalid type\n", NULL); xmlFreeEnumeration(tree); return(NULL); } @@ -1899,20 +1739,12 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, table = xmlHashCreateDict(0, dict); dtd->attributes = (void *) table; } - if (table == NULL) { - xmlVErrMemory(ctxt, - "xmlAddAttributeDecl: Table creation failed!\n"); - xmlFreeEnumeration(tree); - return(NULL); - } - + if (table == NULL) + goto mem_error; ret = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); - if (ret == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - xmlFreeEnumeration(tree); - return(NULL); - } + if (ret == NULL) + goto mem_error; memset(ret, 0, sizeof(xmlAttribute)); ret->type = XML_ATTRIBUTE_DECL; @@ -1928,34 +1760,53 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, ret->doc = dtd->doc; if (dict) { ret->name = xmlDictLookup(dict, name, -1); - ret->prefix = xmlDictLookup(dict, ns, -1); ret->elem = xmlDictLookup(dict, elem, -1); } else { ret->name = xmlStrdup(name); - ret->prefix = xmlStrdup(ns); ret->elem = xmlStrdup(elem); } + if ((ret->name == NULL) || (ret->elem == NULL)) + goto mem_error; + if (ns != NULL) { + if (dict) + ret->prefix = xmlDictLookup(dict, ns, -1); + else + ret->prefix = xmlStrdup(ns); + if (ret->prefix == NULL) + goto mem_error; + } ret->def = def; ret->tree = tree; + tree = NULL; if (defaultValue != NULL) { if (dict) ret->defaultValue = xmlDictLookup(dict, defaultValue, -1); else ret->defaultValue = xmlStrdup(defaultValue); + if (ret->defaultValue == NULL) + goto mem_error; } + elemDef = xmlGetDtdElementDesc2(ctxt, dtd, elem); + if (elemDef == NULL) + goto mem_error; + /* * Validity Check: * Search the DTD for previous declarations of the ATTLIST */ - if (xmlHashAddEntry3(table, ret->name, ret->prefix, ret->elem, ret) < 0) { + res = xmlHashAdd3(table, ret->name, ret->prefix, ret->elem, ret); + if (res <= 0) { + if (res < 0) + goto mem_error; #ifdef LIBXML_VALID_ENABLED - /* - * The attribute is already defined in this DTD. - */ - xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, XML_DTD_ATTRIBUTE_REDEFINED, - "Attribute %s of element %s: already defined\n", - name, elem, NULL); + /* + * The attribute is already defined in this DTD. + */ + xmlErrValidWarning(ctxt, (xmlNodePtr) dtd, + XML_DTD_ATTRIBUTE_REDEFINED, + "Attribute %s of element %s: already defined\n", + name, elem, NULL); #endif /* LIBXML_VALID_ENABLED */ xmlFreeAttribute(ret); return(NULL); @@ -1965,48 +1816,44 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, * Validity Check: * Multiple ID per element */ - elemDef = xmlGetDtdElementDesc2(ctxt, dtd, elem, 1); - if (elemDef != NULL) { - #ifdef LIBXML_VALID_ENABLED - if ((type == XML_ATTRIBUTE_ID) && - (xmlScanIDAttributeDecl(NULL, elemDef, 1) != 0)) { - xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID, - "Element %s has too may ID attributes defined : %s\n", - elem, name, NULL); - if (ctxt != NULL) - ctxt->valid = 0; - } + if ((type == XML_ATTRIBUTE_ID) && + (xmlScanIDAttributeDecl(ctxt, elemDef, 1) != 0)) { + xmlErrValidNode(ctxt, (xmlNodePtr) dtd, XML_DTD_MULTIPLE_ID, + "Element %s has too may ID attributes defined : %s\n", + elem, name, NULL); + if (ctxt != NULL) + ctxt->valid = 0; + } #endif /* LIBXML_VALID_ENABLED */ - /* - * Insert namespace default def first they need to be - * processed first. - */ - if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) || - ((ret->prefix != NULL && - (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) { - ret->nexth = elemDef->attributes; - elemDef->attributes = ret; - } else { - xmlAttributePtr tmp = elemDef->attributes; + /* + * Insert namespace default def first they need to be + * processed first. + */ + if ((xmlStrEqual(ret->name, BAD_CAST "xmlns")) || + ((ret->prefix != NULL && + (xmlStrEqual(ret->prefix, BAD_CAST "xmlns"))))) { + ret->nexth = elemDef->attributes; + elemDef->attributes = ret; + } else { + xmlAttributePtr tmp = elemDef->attributes; - while ((tmp != NULL) && - ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) || - ((ret->prefix != NULL && - (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) { - if (tmp->nexth == NULL) - break; - tmp = tmp->nexth; - } - if (tmp != NULL) { - ret->nexth = tmp->nexth; - tmp->nexth = ret; - } else { - ret->nexth = elemDef->attributes; - elemDef->attributes = ret; - } - } + while ((tmp != NULL) && + ((xmlStrEqual(tmp->name, BAD_CAST "xmlns")) || + ((ret->prefix != NULL && + (xmlStrEqual(ret->prefix, BAD_CAST "xmlns")))))) { + if (tmp->nexth == NULL) + break; + tmp = tmp->nexth; + } + if (tmp != NULL) { + ret->nexth = tmp->nexth; + tmp->nexth = ret; + } else { + ret->nexth = elemDef->attributes; + elemDef->attributes = ret; + } } /* @@ -2021,6 +1868,12 @@ xmlAddAttributeDecl(xmlValidCtxtPtr ctxt, dtd->last = (xmlNodePtr) ret; } return(ret); + +mem_error: + xmlVErrMemory(ctxt); + xmlFreeEnumeration(tree); + xmlFreeAttribute(ret); + return(NULL); } static void @@ -2054,24 +1907,42 @@ xmlCopyAttribute(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { xmlAttributePtr cur; cur = (xmlAttributePtr) xmlMalloc(sizeof(xmlAttribute)); - if (cur == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (cur == NULL) return(NULL); - } memset(cur, 0, sizeof(xmlAttribute)); cur->type = XML_ATTRIBUTE_DECL; cur->atype = attr->atype; cur->def = attr->def; - cur->tree = xmlCopyEnumeration(attr->tree); - if (attr->elem != NULL) + if (attr->tree != NULL) { + cur->tree = xmlCopyEnumeration(attr->tree); + if (cur->tree == NULL) + goto error; + } + if (attr->elem != NULL) { cur->elem = xmlStrdup(attr->elem); - if (attr->name != NULL) + if (cur->elem == NULL) + goto error; + } + if (attr->name != NULL) { cur->name = xmlStrdup(attr->name); - if (attr->prefix != NULL) + if (cur->name == NULL) + goto error; + } + if (attr->prefix != NULL) { cur->prefix = xmlStrdup(attr->prefix); - if (attr->defaultValue != NULL) + if (cur->prefix == NULL) + goto error; + } + if (attr->defaultValue != NULL) { cur->defaultValue = xmlStrdup(attr->defaultValue); + if (cur->defaultValue == NULL) + goto error; + } return(cur); + +error: + xmlFreeAttribute(cur); + return(NULL); } /** @@ -2084,7 +1955,8 @@ xmlCopyAttribute(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { */ xmlAttributeTablePtr xmlCopyAttributeTable(xmlAttributeTablePtr table) { - return((xmlAttributeTablePtr) xmlHashCopy(table, xmlCopyAttribute)); + return(xmlHashCopySafe(table, xmlCopyAttribute, + xmlFreeAttributeTableEntry)); } #endif /* LIBXML_TREE_ENABLED */ @@ -2094,81 +1966,22 @@ xmlCopyAttributeTable(xmlAttributeTablePtr table) { * @buf: the XML buffer output * @attr: An attribute declaration * + * DEPRECATED: Use xmlSaveTree. + * * This will dump the content of the attribute declaration as an XML * DTD definition */ void xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (attr == NULL)) return; - xmlBufferWriteChar(buf, "elem); - xmlBufferWriteChar(buf, " "); - if (attr->prefix != NULL) { - xmlBufferWriteCHAR(buf, attr->prefix); - xmlBufferWriteChar(buf, ":"); - } - xmlBufferWriteCHAR(buf, attr->name); - switch (attr->atype) { - case XML_ATTRIBUTE_CDATA: - xmlBufferWriteChar(buf, " CDATA"); - break; - case XML_ATTRIBUTE_ID: - xmlBufferWriteChar(buf, " ID"); - break; - case XML_ATTRIBUTE_IDREF: - xmlBufferWriteChar(buf, " IDREF"); - break; - case XML_ATTRIBUTE_IDREFS: - xmlBufferWriteChar(buf, " IDREFS"); - break; - case XML_ATTRIBUTE_ENTITY: - xmlBufferWriteChar(buf, " ENTITY"); - break; - case XML_ATTRIBUTE_ENTITIES: - xmlBufferWriteChar(buf, " ENTITIES"); - break; - case XML_ATTRIBUTE_NMTOKEN: - xmlBufferWriteChar(buf, " NMTOKEN"); - break; - case XML_ATTRIBUTE_NMTOKENS: - xmlBufferWriteChar(buf, " NMTOKENS"); - break; - case XML_ATTRIBUTE_ENUMERATION: - xmlBufferWriteChar(buf, " ("); - xmlDumpEnumeration(buf, attr->tree); - break; - case XML_ATTRIBUTE_NOTATION: - xmlBufferWriteChar(buf, " NOTATION ("); - xmlDumpEnumeration(buf, attr->tree); - break; - default: - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "Internal: ATTRIBUTE struct corrupted invalid type\n", - NULL); - } - switch (attr->def) { - case XML_ATTRIBUTE_NONE: - break; - case XML_ATTRIBUTE_REQUIRED: - xmlBufferWriteChar(buf, " #REQUIRED"); - break; - case XML_ATTRIBUTE_IMPLIED: - xmlBufferWriteChar(buf, " #IMPLIED"); - break; - case XML_ATTRIBUTE_FIXED: - xmlBufferWriteChar(buf, " #FIXED"); - break; - default: - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "Internal: ATTRIBUTE struct corrupted invalid def\n", - NULL); - } - if (attr->defaultValue != NULL) { - xmlBufferWriteChar(buf, " "); - xmlBufferWriteQuotedString(buf, attr->defaultValue); - } - xmlBufferWriteChar(buf, ">\n"); + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlSaveTree(save, (xmlNodePtr) attr); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } /** @@ -2179,9 +1992,9 @@ xmlDumpAttributeDecl(xmlBufferPtr buf, xmlAttributePtr attr) { * This is used with the hash scan function - just reverses arguments */ static void -xmlDumpAttributeDeclScan(void *attr, void *buf, +xmlDumpAttributeDeclScan(void *attr, void *save, const xmlChar *name ATTRIBUTE_UNUSED) { - xmlDumpAttributeDecl((xmlBufferPtr) buf, (xmlAttributePtr) attr); + xmlSaveTree(save, attr); } /** @@ -2189,13 +2002,21 @@ xmlDumpAttributeDeclScan(void *attr, void *buf, * @buf: the XML buffer output * @table: An attribute table * + * DEPRECATED: Don't use. + * * This will dump the content of the attribute table as an XML DTD definition */ void xmlDumpAttributeTable(xmlBufferPtr buf, xmlAttributeTablePtr table) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (table == NULL)) return; - xmlHashScan(table, xmlDumpAttributeDeclScan, buf); + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlHashScan(table, xmlDumpAttributeDeclScan, save); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } #endif /* LIBXML_OUTPUT_ENABLED */ @@ -2239,8 +2060,9 @@ xmlNotationPtr xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, const xmlChar *PublicID, const xmlChar *SystemID) { - xmlNotationPtr ret; + xmlNotationPtr ret = NULL; xmlNotationTablePtr table; + int res; if (dtd == NULL) { return(NULL); @@ -2262,43 +2084,54 @@ xmlAddNotationDecl(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, dict = dtd->doc->dict; dtd->notations = table = xmlHashCreateDict(0, dict); - } - if (table == NULL) { - xmlVErrMemory(ctxt, - "xmlAddNotationDecl: Table creation failed!\n"); - return(NULL); + if (table == NULL) + goto mem_error; } ret = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); - if (ret == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - return(NULL); - } + if (ret == NULL) + goto mem_error; memset(ret, 0, sizeof(xmlNotation)); /* * fill the structure. */ ret->name = xmlStrdup(name); - if (SystemID != NULL) + if (ret->name == NULL) + goto mem_error; + if (SystemID != NULL) { ret->SystemID = xmlStrdup(SystemID); - if (PublicID != NULL) + if (ret->SystemID == NULL) + goto mem_error; + } + if (PublicID != NULL) { ret->PublicID = xmlStrdup(PublicID); + if (ret->PublicID == NULL) + goto mem_error; + } /* * Validity Check: * Check the DTD for previous declarations of the ATTLIST */ - if (xmlHashAddEntry(table, name, ret)) { + res = xmlHashAdd(table, name, ret); + if (res <= 0) { + if (res < 0) + goto mem_error; #ifdef LIBXML_VALID_ENABLED - xmlErrValid(NULL, XML_DTD_NOTATION_REDEFINED, - "xmlAddNotationDecl: %s already defined\n", - (const char *) name); + xmlErrValid(ctxt, XML_DTD_NOTATION_REDEFINED, + "xmlAddNotationDecl: %s already defined\n", + (const char *) name); #endif /* LIBXML_VALID_ENABLED */ xmlFreeNotation(ret); return(NULL); } return(ret); + +mem_error: + xmlVErrMemory(ctxt); + xmlFreeNotation(ret); + return(NULL); } static void @@ -2332,23 +2165,29 @@ xmlCopyNotation(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { xmlNotationPtr cur; cur = (xmlNotationPtr) xmlMalloc(sizeof(xmlNotation)); - if (cur == NULL) { - xmlVErrMemory(NULL, "malloc failed"); + if (cur == NULL) return(NULL); - } - if (nota->name != NULL) + memset(cur, 0, sizeof(*cur)); + if (nota->name != NULL) { cur->name = xmlStrdup(nota->name); - else - cur->name = NULL; - if (nota->PublicID != NULL) + if (cur->name == NULL) + goto error; + } + if (nota->PublicID != NULL) { cur->PublicID = xmlStrdup(nota->PublicID); - else - cur->PublicID = NULL; - if (nota->SystemID != NULL) + if (cur->PublicID == NULL) + goto error; + } + if (nota->SystemID != NULL) { cur->SystemID = xmlStrdup(nota->SystemID); - else - cur->SystemID = NULL; + if (cur->SystemID == NULL) + goto error; + } return(cur); + +error: + xmlFreeNotation(cur); + return(NULL); } /** @@ -2361,7 +2200,7 @@ xmlCopyNotation(void *payload, const xmlChar *name ATTRIBUTE_UNUSED) { */ xmlNotationTablePtr xmlCopyNotationTable(xmlNotationTablePtr table) { - return((xmlNotationTablePtr) xmlHashCopy(table, xmlCopyNotation)); + return(xmlHashCopySafe(table, xmlCopyNotation, xmlFreeNotationTableEntry)); } #endif /* LIBXML_TREE_ENABLED */ @@ -2371,39 +2210,21 @@ xmlCopyNotationTable(xmlNotationTablePtr table) { * @buf: the XML buffer output * @nota: A notation declaration * + * DEPRECATED: Don't use. + * * This will dump the content the notation declaration as an XML DTD definition */ void xmlDumpNotationDecl(xmlBufferPtr buf, xmlNotationPtr nota) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (nota == NULL)) return; - xmlBufferWriteChar(buf, "name); - if (nota->PublicID != NULL) { - xmlBufferWriteChar(buf, " PUBLIC "); - xmlBufferWriteQuotedString(buf, nota->PublicID); - if (nota->SystemID != NULL) { - xmlBufferWriteChar(buf, " "); - xmlBufferWriteQuotedString(buf, nota->SystemID); - } - } else { - xmlBufferWriteChar(buf, " SYSTEM "); - xmlBufferWriteQuotedString(buf, nota->SystemID); - } - xmlBufferWriteChar(buf, " >\n"); -} -/** - * xmlDumpNotationDeclScan: - * @nota: A notation declaration - * @buf: the XML buffer output - * - * This is called with the hash scan function, and just reverses args - */ -static void -xmlDumpNotationDeclScan(void *nota, void *buf, - const xmlChar *name ATTRIBUTE_UNUSED) { - xmlDumpNotationDecl((xmlBufferPtr) buf, (xmlNotationPtr) nota); + save = xmlSaveToBuffer(buf, NULL, 0); + xmlSaveNotationDecl(save, nota); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } /** @@ -2411,13 +2232,21 @@ xmlDumpNotationDeclScan(void *nota, void *buf, * @buf: the XML buffer output * @table: A notation table * + * DEPRECATED: Don't use. + * * This will dump the content of the notation table as an XML DTD definition */ void xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) { + xmlSaveCtxtPtr save; + if ((buf == NULL) || (table == NULL)) return; - xmlHashScan(table, xmlDumpNotationDeclScan, buf); + + save = xmlSaveToBuffer(buf, NULL, 0); + xmlSaveNotationTable(save, table); + if (xmlSaveFinish(save) != XML_ERR_OK) + xmlFree(xmlBufferDetach(buf)); } #endif /* LIBXML_OUTPUT_ENABLED */ @@ -2438,35 +2267,6 @@ xmlDumpNotationTable(xmlBufferPtr buf, xmlNotationTablePtr table) { (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \ xmlFree((char *)(str)); -/** - * xmlValidNormalizeString: - * @str: a string - * - * Normalize a string in-place. - */ -static void -xmlValidNormalizeString(xmlChar *str) { - xmlChar *dst; - const xmlChar *src; - - if (str == NULL) - return; - src = str; - dst = str; - - while (*src == 0x20) src++; - while (*src != 0) { - if (*src == 0x20) { - while (*src == 0x20) src++; - if (*src != 0) - *dst++ = 0x20; - } else { - *dst++ = *src++; - } - } - *dst = 0; -} - static int xmlIsStreaming(xmlValidCtxtPtr ctxt) { xmlParserCtxtPtr pctxt; @@ -2498,36 +2298,43 @@ xmlFreeID(xmlIDPtr id) { DICT_FREE(id->value) if (id->name != NULL) DICT_FREE(id->name) + if (id->attr != NULL) { + id->attr->id = NULL; + id->attr->atype = 0; + } + xmlFree(id); } /** - * xmlAddID: - * @ctxt: the validation context - * @doc: pointer to the document - * @value: the value name + * xmlAddIDInternal: * @attr: the attribute holding the ID + * @value: the attribute (ID) value + * @idPtr: pointer to resulting ID * * Register a new id declaration * - * Returns NULL if not, otherwise the new xmlIDPtr + * Returns 1 on success, 0 if the ID already exists, -1 if a memory + * allocation fails. */ -xmlIDPtr -xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, - xmlAttrPtr attr) { - xmlIDPtr ret; +static int +xmlAddIDInternal(xmlAttrPtr attr, const xmlChar *value, xmlIDPtr *idPtr) { + xmlDocPtr doc; + xmlIDPtr id; xmlIDTablePtr table; + int ret; - if (doc == NULL) { - return(NULL); - } - if ((value == NULL) || (value[0] == 0)) { - return(NULL); - } - if (attr == NULL) { - return(NULL); - } + if (idPtr != NULL) + *idPtr = NULL; + if ((value == NULL) || (value[0] == 0)) + return(0); + if (attr == NULL) + return(0); + + doc = attr->doc; + if (doc == NULL) + return(0); /* * Create the ID table if needed. @@ -2535,57 +2342,105 @@ xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, table = (xmlIDTablePtr) doc->ids; if (table == NULL) { doc->ids = table = xmlHashCreateDict(0, doc->dict); - } - if (table == NULL) { - xmlVErrMemory(ctxt, - "xmlAddID: Table creation failed!\n"); - return(NULL); + if (table == NULL) + return(-1); + } else { + id = xmlHashLookup(table, value); + if (id != NULL) + return(0); } - ret = (xmlIDPtr) xmlMalloc(sizeof(xmlID)); - if (ret == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - return(NULL); - } + id = (xmlIDPtr) xmlMalloc(sizeof(xmlID)); + if (id == NULL) + return(-1); + memset(id, 0, sizeof(*id)); /* * fill the structure. */ - ret->value = xmlStrdup(value); - ret->doc = doc; - if (xmlIsStreaming(ctxt)) { - /* - * Operating in streaming mode, attr is gonna disappear - */ - if (doc->dict != NULL) - ret->name = xmlDictLookup(doc->dict, attr->name, -1); - else - ret->name = xmlStrdup(attr->name); - ret->attr = NULL; - } else { - ret->attr = attr; - ret->name = NULL; + id->doc = doc; + id->value = xmlStrdup(value); + if (id->value == NULL) { + xmlFreeID(id); + return(-1); } - ret->lineno = xmlGetLineNo(attr->parent); - if (xmlHashAddEntry(table, value, ret) < 0) { -#ifdef LIBXML_VALID_ENABLED - /* - * The id is already defined in this DTD. - */ - if (ctxt != NULL) { - xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED, - "ID %s already defined\n", value, NULL, NULL); - } -#endif /* LIBXML_VALID_ENABLED */ - xmlFreeID(ret); - return(NULL); + if (attr->id != NULL) + xmlRemoveID(doc, attr); + + if (xmlHashAddEntry(table, value, id) < 0) { + xmlFreeID(id); + return(-1); } - if (attr != NULL) - attr->atype = XML_ATTRIBUTE_ID; + + ret = 1; + if (idPtr != NULL) + *idPtr = id; + + id->attr = attr; + id->lineno = xmlGetLineNo(attr->parent); + attr->atype = XML_ATTRIBUTE_ID; + attr->id = id; + return(ret); } +/** + * xmlAddIDSafe: + * @attr: the attribute holding the ID + * @value: the attribute (ID) value + * + * Register a new id declaration + * + * Available since 2.13.0. + * + * Returns 1 on success, 0 if the ID already exists, -1 if a memory + * allocation fails. + */ +int +xmlAddIDSafe(xmlAttrPtr attr, const xmlChar *value) { + return(xmlAddIDInternal(attr, value, NULL)); +} + +/** + * xmlAddID: + * @ctxt: the validation context + * @doc: pointer to the document + * @value: the value name + * @attr: the attribute holding the ID + * + * Register a new id declaration + * + * Returns NULL if not, otherwise the new xmlIDPtr + */ +xmlIDPtr +xmlAddID(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, + xmlAttrPtr attr) { + xmlIDPtr id; + int res; + + if ((attr == NULL) || (doc != attr->doc)) + return(NULL); + + res = xmlAddIDInternal(attr, value, &id); + if (res < 0) { + xmlVErrMemory(ctxt); + } +#ifdef LIBXML_VALID_ENABLED + else if (res == 0) { + if (ctxt != NULL) { + /* + * The id is already defined in this DTD. + */ + xmlErrValidNode(ctxt, attr->parent, XML_DTD_ID_REDEFINED, + "ID %s already defined\n", value, NULL, NULL); + } + } +#endif /* LIBXML_VALID_ENABLED */ + + return(id); +} + static void xmlFreeIDTableEntry(void *id, const xmlChar *name ATTRIBUTE_UNUSED) { xmlFreeID((xmlIDPtr) id); @@ -2613,57 +2468,67 @@ xmlFreeIDTable(xmlIDTablePtr table) { * of HTML documents parsed with the HTML parser, then ID detection is * done systematically. * - * Returns 0 or 1 depending on the lookup result + * Returns 0 or 1 depending on the lookup result or -1 if a memory allocation + * failed. */ int xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { - if ((attr == NULL) || (attr->name == NULL)) return(0); - if ((attr->ns != NULL) && (attr->ns->prefix != NULL) && - (!strcmp((char *) attr->name, "id")) && - (!strcmp((char *) attr->ns->prefix, "xml"))) - return(1); - if (doc == NULL) return(0); - if ((doc->intSubset == NULL) && (doc->extSubset == NULL) && - (doc->type != XML_HTML_DOCUMENT_NODE)) { - return(0); - } else if (doc->type == XML_HTML_DOCUMENT_NODE) { - if ((xmlStrEqual(BAD_CAST "id", attr->name)) || - ((xmlStrEqual(BAD_CAST "name", attr->name)) && - ((elem == NULL) || (xmlStrEqual(elem->name, BAD_CAST "a"))))) + if ((attr == NULL) || (attr->name == NULL)) + return(0); + + if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) { + if (xmlStrEqual(BAD_CAST "id", attr->name)) + return(1); + + if ((elem == NULL) || (elem->type != XML_ELEMENT_NODE)) + return(0); + + if ((xmlStrEqual(BAD_CAST "name", attr->name)) && + (xmlStrEqual(elem->name, BAD_CAST "a"))) return(1); - return(0); - } else if (elem == NULL) { - return(0); } else { xmlAttributePtr attrDecl = NULL; + xmlChar felem[50]; + xmlChar *fullelemname; + const xmlChar *aprefix; - xmlChar felem[50], fattr[50]; - xmlChar *fullelemname, *fullattrname; + if ((attr->ns != NULL) && (attr->ns->prefix != NULL) && + (!strcmp((char *) attr->name, "id")) && + (!strcmp((char *) attr->ns->prefix, "xml"))) + return(1); + + if ((doc == NULL) || + ((doc->intSubset == NULL) && (doc->extSubset == NULL))) + return(0); + + if ((elem == NULL) || + (elem->type != XML_ELEMENT_NODE) || + (elem->name == NULL)) + return(0); fullelemname = (elem->ns != NULL && elem->ns->prefix != NULL) ? xmlBuildQName(elem->name, elem->ns->prefix, felem, 50) : (xmlChar *)elem->name; + if (fullelemname == NULL) + return(-1); - fullattrname = (attr->ns != NULL && attr->ns->prefix != NULL) ? - xmlBuildQName(attr->name, attr->ns->prefix, fattr, 50) : - (xmlChar *)attr->name; + aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL; - if (fullelemname != NULL && fullattrname != NULL) { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullelemname, - fullattrname); + if (fullelemname != NULL) { + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullelemname, + attr->name, aprefix); if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullelemname, - fullattrname); + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullelemname, + attr->name, aprefix); } - if ((fullattrname != fattr) && (fullattrname != attr->name)) - xmlFree(fullattrname); if ((fullelemname != felem) && (fullelemname != elem->name)) xmlFree(fullelemname); if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_ID)) return(1); } + return(0); } @@ -2678,31 +2543,18 @@ xmlIsID(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { */ int xmlRemoveID(xmlDocPtr doc, xmlAttrPtr attr) { - xmlIDTablePtr table; - xmlIDPtr id; - xmlChar *ID; + xmlIDTablePtr table; if (doc == NULL) return(-1); - if (attr == NULL) return(-1); + if ((attr == NULL) || (attr->id == NULL)) return(-1); table = (xmlIDTablePtr) doc->ids; if (table == NULL) return(-1); - ID = xmlNodeListGetString(doc, attr->children, 1); - if (ID == NULL) - return(-1); - xmlValidNormalizeString(ID); - - id = xmlHashLookup(table, ID); - if (id == NULL || id->attr != attr) { - xmlFree(ID); + if (xmlHashRemoveEntry(table, attr->id->value, xmlFreeIDTableEntry) < 0) return(-1); - } - xmlHashRemoveEntry(table, ID, xmlFreeIDTableEntry); - xmlFree(ID); - attr->atype = 0; return(0); } @@ -2847,7 +2699,7 @@ xmlDummyCompare(const void *data0 ATTRIBUTE_UNUSED, xmlRefPtr xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, xmlAttrPtr attr) { - xmlRefPtr ret; + xmlRefPtr ret = NULL; xmlRefTablePtr table; xmlListPtr ref_list; @@ -2867,28 +2719,28 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, table = (xmlRefTablePtr) doc->refs; if (table == NULL) { doc->refs = table = xmlHashCreateDict(0, doc->dict); - } - if (table == NULL) { - xmlVErrMemory(ctxt, - "xmlAddRef: Table creation failed!\n"); - return(NULL); + if (table == NULL) + goto failed; } ret = (xmlRefPtr) xmlMalloc(sizeof(xmlRef)); - if (ret == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - return(NULL); - } + if (ret == NULL) + goto failed; + memset(ret, 0, sizeof(*ret)); /* * fill the structure. */ ret->value = xmlStrdup(value); + if (ret->value == NULL) + goto failed; if (xmlIsStreaming(ctxt)) { /* * Operating in streaming mode, attr is gonna disappear */ ret->name = xmlStrdup(attr->name); + if (ret->name == NULL) + goto failed; ret->attr = NULL; } else { ret->name = NULL; @@ -2904,28 +2756,22 @@ xmlAddRef(xmlValidCtxtPtr ctxt, xmlDocPtr doc, const xmlChar *value, */ if (NULL == (ref_list = xmlHashLookup(table, value))) { - if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) { - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "xmlAddRef: Reference list creation failed!\n", - NULL); + int res; + + if (NULL == (ref_list = xmlListCreate(xmlFreeRef, xmlDummyCompare))) goto failed; - } - if (xmlHashAddEntry(table, value, ref_list) < 0) { + res = xmlHashAdd(table, value, ref_list); + if (res <= 0) { xmlListDelete(ref_list); - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "xmlAddRef: Reference list insertion failed!\n", - NULL); goto failed; } } - if (xmlListAppend(ref_list, ret) != 0) { - xmlErrValid(NULL, XML_ERR_INTERNAL_ERROR, - "xmlAddRef: Reference list insertion failed!\n", - NULL); + if (xmlListAppend(ref_list, ret) != 0) goto failed; - } return(ret); + failed: + xmlVErrMemory(ctxt); if (ret != NULL) { if (ret->value != NULL) xmlFree((char *)ret->value); @@ -2979,12 +2825,15 @@ xmlIsRef(xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr) { return(0); } else { xmlAttributePtr attrDecl; + const xmlChar *aprefix; if (elem == NULL) return(0); - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, attr->name); + aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL; + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, attr->name, + aprefix); if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, - elem->name, attr->name); + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, attr->name, + aprefix); if ((attrDecl != NULL) && (attrDecl->atype == XML_ATTRIBUTE_IDREF || @@ -3047,7 +2896,7 @@ xmlRemoveRef(xmlDocPtr doc, xmlAttrPtr attr) { /*If the list is empty then remove the list entry in the hash */ if (xmlListEmpty(ref_list)) - xmlHashUpdateEntry(table, ID, NULL, xmlFreeRefTableEntry); + xmlHashRemoveEntry(table, ID, xmlFreeRefTableEntry); xmlFree(ID); return(0); } @@ -3095,6 +2944,8 @@ xmlGetRefs(xmlDocPtr doc, const xmlChar *ID) { * * Search the DTD for the description of this element * + * NOTE: A NULL return value can also mean that a memory allocation failed. + * * returns the xmlElementPtr if found or NULL */ @@ -3102,21 +2953,26 @@ xmlElementPtr xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { xmlElementTablePtr table; xmlElementPtr cur; - xmlChar *uqname = NULL, *prefix = NULL; + const xmlChar *localname; + xmlChar *prefix; + + if ((dtd == NULL) || (dtd->elements == NULL) || + (name == NULL)) + return(NULL); - if ((dtd == NULL) || (name == NULL)) return(NULL); - if (dtd->elements == NULL) - return(NULL); table = (xmlElementTablePtr) dtd->elements; + if (table == NULL) + return(NULL); - uqname = xmlSplitQName2(name, &prefix); - if (uqname != NULL) - name = uqname; - cur = xmlHashLookup2(table, name, prefix); - if (prefix != NULL) xmlFree(prefix); - if (uqname != NULL) xmlFree(uqname); + localname = xmlSplitQName4(name, &prefix); + if (localname == NULL) + return(NULL); + cur = xmlHashLookup2(table, localname, prefix); + if (prefix != NULL) + xmlFree(prefix); return(cur); } + /** * xmlGetDtdElementDesc2: * @dtd: a pointer to the DtD to search @@ -3129,66 +2985,64 @@ xmlGetDtdElementDesc(xmlDtdPtr dtd, const xmlChar *name) { */ static xmlElementPtr -xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name, - int create) { +xmlGetDtdElementDesc2(xmlValidCtxtPtr ctxt, xmlDtdPtr dtd, const xmlChar *name) { xmlElementTablePtr table; - xmlElementPtr cur; - xmlChar *uqname = NULL, *prefix = NULL; + xmlElementPtr cur = NULL; + const xmlChar *localName; + xmlChar *prefix = NULL; if (dtd == NULL) return(NULL); + + /* + * Create the Element table if needed. + */ if (dtd->elements == NULL) { xmlDictPtr dict = NULL; if (dtd->doc != NULL) dict = dtd->doc->dict; - if (!create) - return(NULL); - /* - * Create the Element table if needed. - */ - table = (xmlElementTablePtr) dtd->elements; - if (table == NULL) { - table = xmlHashCreateDict(0, dict); - dtd->elements = (void *) table; - } - if (table == NULL) { - xmlVErrMemory(ctxt, "element table allocation failed"); - return(NULL); - } + dtd->elements = xmlHashCreateDict(0, dict); + if (dtd->elements == NULL) + goto mem_error; } table = (xmlElementTablePtr) dtd->elements; - uqname = xmlSplitQName2(name, &prefix); - if (uqname != NULL) - name = uqname; - cur = xmlHashLookup2(table, name, prefix); - if ((cur == NULL) && (create)) { + localName = xmlSplitQName4(name, &prefix); + if (localName == NULL) + goto mem_error; + cur = xmlHashLookup2(table, localName, prefix); + if (cur == NULL) { cur = (xmlElementPtr) xmlMalloc(sizeof(xmlElement)); - if (cur == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); - goto error; - } + if (cur == NULL) + goto mem_error; memset(cur, 0, sizeof(xmlElement)); cur->type = XML_ELEMENT_DECL; + cur->doc = dtd->doc; /* * fill the structure. */ - cur->name = xmlStrdup(name); - cur->prefix = xmlStrdup(prefix); + cur->name = xmlStrdup(localName); + if (cur->name == NULL) + goto mem_error; + cur->prefix = prefix; + prefix = NULL; cur->etype = XML_ELEMENT_TYPE_UNDEFINED; - if (xmlHashAddEntry2(table, name, prefix, cur) < 0) { - xmlVErrMemory(ctxt, "adding entry failed"); - xmlFreeElement(cur); - cur = NULL; - } + if (xmlHashAdd2(table, localName, cur->prefix, cur) <= 0) + goto mem_error; } -error: - if (prefix != NULL) xmlFree(prefix); - if (uqname != NULL) xmlFree(uqname); + + if (prefix != NULL) + xmlFree(prefix); return(cur); + +mem_error: + xmlVErrMemory(ctxt); + xmlFree(prefix); + xmlFreeElement(cur); + return(NULL); } /** @@ -3230,23 +3084,23 @@ xmlAttributePtr xmlGetDtdAttrDesc(xmlDtdPtr dtd, const xmlChar *elem, const xmlChar *name) { xmlAttributeTablePtr table; xmlAttributePtr cur; - xmlChar *uqname = NULL, *prefix = NULL; + const xmlChar *localname; + xmlChar *prefix = NULL; - if (dtd == NULL) return(NULL); - if (dtd->attributes == NULL) return(NULL); + if ((dtd == NULL) || (dtd->attributes == NULL) || + (elem == NULL) || (name == NULL)) + return(NULL); table = (xmlAttributeTablePtr) dtd->attributes; if (table == NULL) return(NULL); - uqname = xmlSplitQName2(name, &prefix); - - if (uqname != NULL) { - cur = xmlHashLookup3(table, uqname, prefix, elem); - if (prefix != NULL) xmlFree(prefix); - if (uqname != NULL) xmlFree(uqname); - } else - cur = xmlHashLookup3(table, name, NULL, elem); + localname = xmlSplitQName4(name, &prefix); + if (localname == NULL) + return(NULL); + cur = xmlHashLookup3(table, localname, prefix, elem); + if (prefix != NULL) + xmlFree(prefix); return(cur); } @@ -3303,6 +3157,8 @@ xmlGetDtdNotationDesc(xmlDtdPtr dtd, const xmlChar *name) { * @doc: the document * @notationName: the notation name to check * + * DEPRECATED: Internal function, don't use. + * * Validate that the given name match a notation declaration. * - [ VC: Notation Declared ] * @@ -3370,6 +3226,35 @@ xmlIsMixedElement(xmlDocPtr doc, const xmlChar *name) { #ifdef LIBXML_VALID_ENABLED +/** + * xmlValidNormalizeString: + * @str: a string + * + * Normalize a string in-place. + */ +static void +xmlValidNormalizeString(xmlChar *str) { + xmlChar *dst; + const xmlChar *src; + + if (str == NULL) + return; + src = str; + dst = str; + + while (*src == 0x20) src++; + while (*src != 0) { + if (*src == 0x20) { + while (*src == 0x20) src++; + if (*src != 0) + *dst++ = 0x20; + } else { + *dst++ = *src++; + } + } + *dst = 0; +} + static int xmlIsDocNameStartChar(xmlDocPtr doc, int c) { if ((doc == NULL) || (doc->properties & XML_DOC_OLD10) == 0) { @@ -3689,6 +3574,8 @@ xmlValidateNmtokensValue(const xmlChar *value) { * @doc: a document instance * @nota: a notation definition * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single notation definition * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -3745,6 +3632,8 @@ xmlValidateAttributeValueInternal(xmlDocPtr doc, xmlAttributeType type, * @type: an attribute type * @value: an attribute value * + * DEPRECATED: Internal function, don't use. + * * Validate that the given attribute value match the proper production * * [ VC: ID ] @@ -3840,8 +3729,10 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlEntityPtr ent; dup = xmlStrdup(value); - if (dup == NULL) + if (dup == NULL) { + xmlVErrMemory(ctxt); return(0); + } cur = dup; while (*cur != 0) { nam = cur; @@ -3899,6 +3790,8 @@ xmlValidateAttributeValue2(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @value: the attribute value * @ctxt: the validation context or NULL * + * DEPRECATED: Internal function, don't use. + * * Does the validation related extra step of the normalization of attribute * values: * @@ -3919,6 +3812,8 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, const xmlChar *name, const xmlChar *value) { xmlChar *ret; xmlAttributePtr attrDecl = NULL; + const xmlChar *localName; + xmlChar *prefix = NULL; int extsubset = 0; if (doc == NULL) return(NULL); @@ -3926,38 +3821,47 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, if (name == NULL) return(NULL); if (value == NULL) return(NULL); - if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { - xmlChar fn[50]; - xmlChar *fullname; + localName = xmlSplitQName4(name, &prefix); + if (localName == NULL) + goto mem_error; - fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); - if (fullname == NULL) - return(NULL); - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, name); + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { + xmlChar buf[50]; + xmlChar *elemname; + + elemname = xmlBuildQName(elem->name, elem->ns->prefix, buf, 50); + if (elemname == NULL) + goto mem_error; + if (doc->intSubset != NULL) + attrDecl = xmlHashLookup3(doc->intSubset->attributes, localName, + prefix, elemname); if ((attrDecl == NULL) && (doc->extSubset != NULL)) { - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, name); + attrDecl = xmlHashLookup3(doc->extSubset->attributes, localName, + prefix, elemname); if (attrDecl != NULL) extsubset = 1; } - if ((fullname != fn) && (fullname != elem->name)) - xmlFree(fullname); + if ((elemname != buf) && (elemname != elem->name)) + xmlFree(elemname); } if ((attrDecl == NULL) && (doc->intSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, elem->name, name); + attrDecl = xmlHashLookup3(doc->intSubset->attributes, localName, + prefix, elem->name); if ((attrDecl == NULL) && (doc->extSubset != NULL)) { - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, elem->name, name); + attrDecl = xmlHashLookup3(doc->extSubset->attributes, localName, + prefix, elem->name); if (attrDecl != NULL) extsubset = 1; } if (attrDecl == NULL) - return(NULL); + goto done; if (attrDecl->atype == XML_ATTRIBUTE_CDATA) - return(NULL); + goto done; ret = xmlStrdup(value); if (ret == NULL) - return(NULL); + goto mem_error; xmlValidNormalizeString(ret); if ((doc->standalone) && (extsubset == 1) && (!xmlStrEqual(value, ret))) { xmlErrValidNode(ctxt, elem, XML_DTD_NOT_STANDALONE, @@ -3965,7 +3869,16 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, name, elem->name, NULL); ctxt->valid = 0; } + + xmlFree(prefix); return(ret); + +mem_error: + xmlVErrMemory(ctxt); + +done: + xmlFree(prefix); + return(NULL); } /** @@ -3975,6 +3888,8 @@ xmlValidCtxtNormalizeAttributeValue(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @name: the attribute name * @value: the attribute value * + * DEPRECATED: Internal function, don't use. + * * Does the validation related extra step of the normalization of attribute * values: * @@ -4038,6 +3953,8 @@ xmlValidateAttributeIdCallback(void *payload, void *data, * @doc: a document instance * @attr: an attribute definition * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single attribute definition * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -4083,13 +4000,23 @@ xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, /* One ID per Element Type */ if (attr->atype == XML_ATTRIBUTE_ID) { + xmlElementPtr elem = NULL; + const xmlChar *elemLocalName; + xmlChar *elemPrefix; int nbId; + elemLocalName = xmlSplitQName4(attr->elem, &elemPrefix); + if (elemLocalName == NULL) { + xmlVErrMemory(ctxt); + return(0); + } + /* the trick is that we parse DtD as their own internal subset */ - xmlElementPtr elem = xmlGetDtdElementDesc(doc->intSubset, - attr->elem); + if (doc->intSubset != NULL) + elem = xmlHashLookup2(doc->intSubset->elements, + elemLocalName, elemPrefix); if (elem != NULL) { - nbId = xmlScanIDAttributeDecl(NULL, elem, 0); + nbId = xmlScanIDAttributeDecl(ctxt, elem, 0); } else { xmlAttributeTablePtr table; @@ -4109,22 +4036,28 @@ xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has %d ID attribute defined in the internal subset : %s\n", attr->elem, nbId, attr->name); + ret = 0; } else if (doc->extSubset != NULL) { int extId = 0; - elem = xmlGetDtdElementDesc(doc->extSubset, attr->elem); + elem = xmlHashLookup2(doc->extSubset->elements, + elemLocalName, elemPrefix); if (elem != NULL) { - extId = xmlScanIDAttributeDecl(NULL, elem, 0); + extId = xmlScanIDAttributeDecl(ctxt, elem, 0); } if (extId > 1) { xmlErrValidNodeNr(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has %d ID attribute defined in the external subset : %s\n", attr->elem, extId, attr->name); + ret = 0; } else if (extId + nbId > 1) { xmlErrValidNode(ctxt, (xmlNodePtr) attr, XML_DTD_ID_SUBSET, "Element %s has ID attributes defined in the internal and external subset : %s\n", attr->elem, attr->name, NULL); + ret = 0; } } + + xmlFree(elemPrefix); } /* Validity Constraint: Enumeration */ @@ -4151,6 +4084,8 @@ xmlValidateAttributeDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @doc: a document instance * @elem: an element definition * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single element definition * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -4166,6 +4101,8 @@ xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlElementPtr elem) { int ret = 1; xmlElementPtr tst; + const xmlChar *localName; + xmlChar *prefix; CHECK_DTD; @@ -4229,32 +4166,47 @@ xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, } } + localName = xmlSplitQName4(elem->name, &prefix); + if (localName == NULL) { + xmlVErrMemory(ctxt); + return(0); + } + /* VC: Unique Element Type Declaration */ - tst = xmlGetDtdElementDesc(doc->intSubset, elem->name); - if ((tst != NULL ) && (tst != elem) && - ((tst->prefix == elem->prefix) || - (xmlStrEqual(tst->prefix, elem->prefix))) && - (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { - xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, - "Redefinition of element %s\n", - elem->name, NULL, NULL); - ret = 0; + if (doc->intSubset != NULL) { + tst = xmlHashLookup2(doc->intSubset->elements, localName, prefix); + + if ((tst != NULL ) && (tst != elem) && + ((tst->prefix == elem->prefix) || + (xmlStrEqual(tst->prefix, elem->prefix))) && + (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + elem->name, NULL, NULL); + ret = 0; + } } - tst = xmlGetDtdElementDesc(doc->extSubset, elem->name); - if ((tst != NULL ) && (tst != elem) && - ((tst->prefix == elem->prefix) || - (xmlStrEqual(tst->prefix, elem->prefix))) && - (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { - xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, - "Redefinition of element %s\n", - elem->name, NULL, NULL); - ret = 0; + if (doc->extSubset != NULL) { + tst = xmlHashLookup2(doc->extSubset->elements, localName, prefix); + + if ((tst != NULL ) && (tst != elem) && + ((tst->prefix == elem->prefix) || + (xmlStrEqual(tst->prefix, elem->prefix))) && + (tst->etype != XML_ELEMENT_TYPE_UNDEFINED)) { + xmlErrValidNode(ctxt, (xmlNodePtr) elem, XML_DTD_ELEM_REDEFINED, + "Redefinition of element %s\n", + elem->name, NULL, NULL); + ret = 0; + } } + /* One ID per Element Type * already done when registering the attribute if (xmlScanIDAttributeDecl(ctxt, elem) > 1) { ret = 0; } */ + + xmlFree(prefix); return(ret); } @@ -4266,6 +4218,8 @@ xmlValidateElementDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @attr: an attribute instance * @value: the attribute value (without entities processing) * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single attribute for an element * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -4288,6 +4242,7 @@ xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr elem, xmlAttrPtr attr, const xmlChar *value) { xmlAttributePtr attrDecl = NULL; + const xmlChar *aprefix; int val; int ret = 1; @@ -4295,42 +4250,31 @@ xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, if ((elem == NULL) || (elem->name == NULL)) return(0); if ((attr == NULL) || (attr->name == NULL)) return(0); + aprefix = (attr->ns != NULL) ? attr->ns->prefix : NULL; + if ((elem->ns != NULL) && (elem->ns->prefix != NULL)) { xmlChar fn[50]; xmlChar *fullname; fullname = xmlBuildQName(elem->name, elem->ns->prefix, fn, 50); - if (fullname == NULL) + if (fullname == NULL) { + xmlVErrMemory(ctxt); return(0); - if (attr->ns != NULL) { - attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, - attr->name, attr->ns->prefix); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, - attr->name, attr->ns->prefix); - } else { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, attr->name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, - fullname, attr->name); - } + } + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, + attr->name, aprefix); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, + attr->name, aprefix); if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); } if (attrDecl == NULL) { - if (attr->ns != NULL) { - attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, - attr->name, attr->ns->prefix); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, - attr->name, attr->ns->prefix); - } else { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, - elem->name, attr->name); - if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, - elem->name, attr->name); - } + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, + attr->name, aprefix); + if ((attrDecl == NULL) && (doc->extSubset != NULL)) + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, + attr->name, aprefix); } @@ -4341,6 +4285,8 @@ xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, attr->name, elem->name, NULL); return(0); } + if (attr->atype == XML_ATTRIBUTE_ID) + xmlRemoveID(doc, attr); attr->atype = attrDecl->atype; val = xmlValidateAttributeValueInternal(doc, attrDecl->atype, value); @@ -4443,6 +4389,8 @@ xmlValidateOneAttribute(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @ns: an namespace declaration instance * @value: the attribute value (without entities processing) * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single namespace declaration for an element * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -4478,7 +4426,7 @@ xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { fullname = xmlBuildQName(elem->name, prefix, fn, 50); if (fullname == NULL) { - xmlVErrMemory(ctxt, "Validating namespace"); + xmlVErrMemory(ctxt); return(0); } if (ns->prefix != NULL) { @@ -4488,11 +4436,11 @@ xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, ns->prefix, BAD_CAST "xmlns"); } else { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, fullname, - BAD_CAST "xmlns"); + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, fullname, + BAD_CAST "xmlns", NULL); if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, fullname, - BAD_CAST "xmlns"); + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, fullname, + BAD_CAST "xmlns", NULL); } if ((fullname != fn) && (fullname != elem->name)) xmlFree(fullname); @@ -4505,11 +4453,11 @@ xmlNodePtr elem, const xmlChar *prefix, xmlNsPtr ns, const xmlChar *value) { attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, ns->prefix, BAD_CAST "xmlns"); } else { - attrDecl = xmlGetDtdAttrDesc(doc->intSubset, - elem->name, BAD_CAST "xmlns"); + attrDecl = xmlGetDtdQAttrDesc(doc->intSubset, elem->name, + BAD_CAST "xmlns", NULL); if ((attrDecl == NULL) && (doc->extSubset != NULL)) - attrDecl = xmlGetDtdAttrDesc(doc->extSubset, - elem->name, BAD_CAST "xmlns"); + attrDecl = xmlGetDtdQAttrDesc(doc->extSubset, elem->name, + BAD_CAST "xmlns", NULL); } } @@ -5072,7 +5020,8 @@ xmlSnprintfElements(char *buf, int size, xmlNodePtr node, int glob) { strcat(buf, " ..."); return; } - strcat(buf, (char *) cur->name); + if (cur->name != NULL) + strcat(buf, (char *) cur->name); if (cur->next != NULL) strcat(buf, " "); break; @@ -5158,67 +5107,74 @@ xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, ctxt->nodeNr = 0; ctxt->nodeTab = NULL; exec = xmlRegNewExecCtxt(elemDecl->contModel, NULL, NULL); - if (exec != NULL) { - cur = child; - while (cur != NULL) { - switch (cur->type) { - case XML_ENTITY_REF_NODE: - /* - * Push the current node to be able to roll back - * and process within the entity - */ - if ((cur->children != NULL) && - (cur->children->children != NULL)) { - nodeVPush(ctxt, cur); - cur = cur->children->children; - continue; - } - break; - case XML_TEXT_NODE: - if (xmlIsBlankNode(cur)) - break; - ret = 0; - goto fail; - case XML_CDATA_SECTION_NODE: - /* TODO */ - ret = 0; - goto fail; - case XML_ELEMENT_NODE: - if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { - xmlChar fn[50]; - xmlChar *fullname; - - fullname = xmlBuildQName(cur->name, - cur->ns->prefix, fn, 50); - if (fullname == NULL) { - ret = -1; - goto fail; - } - ret = xmlRegExecPushString(exec, fullname, NULL); - if ((fullname != fn) && (fullname != cur->name)) - xmlFree(fullname); - } else { - ret = xmlRegExecPushString(exec, cur->name, NULL); - } - break; - default: - break; - } - /* - * Switch to next element - */ - cur = cur->next; - while (cur == NULL) { - cur = nodeVPop(ctxt); - if (cur == NULL) - break; - cur = cur->next; - } - } - ret = xmlRegExecPushString(exec, NULL, NULL); + if (exec == NULL) { + xmlVErrMemory(ctxt); + return(-1); + } + cur = child; + while (cur != NULL) { + switch (cur->type) { + case XML_ENTITY_REF_NODE: + /* + * Push the current node to be able to roll back + * and process within the entity + */ + if ((cur->children != NULL) && + (cur->children->children != NULL)) { + nodeVPush(ctxt, cur); + cur = cur->children->children; + continue; + } + break; + case XML_TEXT_NODE: + if (xmlIsBlankNode(cur)) + break; + ret = 0; + goto fail; + case XML_CDATA_SECTION_NODE: + /* TODO */ + ret = 0; + goto fail; + case XML_ELEMENT_NODE: + if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { + xmlChar fn[50]; + xmlChar *fullname; + + fullname = xmlBuildQName(cur->name, + cur->ns->prefix, fn, 50); + if (fullname == NULL) { + xmlVErrMemory(ctxt); + ret = -1; + goto fail; + } + ret = xmlRegExecPushString(exec, fullname, NULL); + if ((fullname != fn) && (fullname != cur->name)) + xmlFree(fullname); + } else { + ret = xmlRegExecPushString(exec, cur->name, NULL); + } + break; + default: + break; + } + if (ret == XML_REGEXP_OUT_OF_MEMORY) + xmlVErrMemory(ctxt); + /* + * Switch to next element + */ + cur = cur->next; + while (cur == NULL) { + cur = nodeVPop(ctxt); + if (cur == NULL) + break; + cur = cur->next; + } + } + ret = xmlRegExecPushString(exec, NULL, NULL); + if (ret == XML_REGEXP_OUT_OF_MEMORY) + xmlVErrMemory(ctxt); fail: - xmlRegFreeExecCtxt(exec); - } + xmlRegFreeExecCtxt(exec); } #else /* LIBXML_REGEXP_ENABLED */ /* @@ -5228,7 +5184,7 @@ xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, ctxt->vstateTab = (xmlValidState *) xmlMalloc( ctxt->vstateMax * sizeof(ctxt->vstateTab[0])); if (ctxt->vstateTab == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); return(-1); } /* @@ -5287,7 +5243,7 @@ xmlValidateElementContent(xmlValidCtxtPtr ctxt, xmlNodePtr child, */ tmp = (xmlNodePtr) xmlMalloc(sizeof(xmlNode)); if (tmp == NULL) { - xmlVErrMemory(ctxt, "malloc failed"); + xmlVErrMemory(ctxt); xmlFreeNodeList(repl); ret = -1; goto done; @@ -5587,9 +5543,9 @@ xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * full QName but in that case being flexible makes sense. */ if (elemDecl == NULL) { - elemDecl = xmlGetDtdElementDesc(doc->intSubset, elem->name); + elemDecl = xmlGetDtdQElementDesc(doc->intSubset, elem->name, NULL); if ((elemDecl == NULL) && (doc->extSubset != NULL)) { - elemDecl = xmlGetDtdElementDesc(doc->extSubset, elem->name); + elemDecl = xmlGetDtdQElementDesc(doc->extSubset, elem->name, NULL); if ((elemDecl != NULL) && (extsubset != NULL)) *extsubset = 1; } @@ -5611,6 +5567,8 @@ xmlValidGetElemDecl(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @elem: an element instance * @qname: the qualified name as appearing in the serialization * + * DEPRECATED: Internal function, don't use. + * * Push a new element start on the validation stack. * * returns 1 if no validation problem was found or 0 otherwise @@ -5679,6 +5637,10 @@ xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, */ if (state->exec != NULL) { ret = xmlRegExecPushString(state->exec, qname, NULL); + if (ret == XML_REGEXP_OUT_OF_MEMORY) { + xmlVErrMemory(ctxt); + return(0); + } if (ret < 0) { xmlErrValidNode(ctxt, state->node, XML_DTD_CONTENT_MODEL, @@ -5704,6 +5666,8 @@ xmlValidatePushElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @data: some character data read * @len: the length of the data * + * DEPRECATED: Internal function, don't use. + * * check the CData parsed for validation in the current stack * * returns 1 if no validation problem was found or 0 otherwise @@ -5777,6 +5741,8 @@ xmlValidatePushCData(xmlValidCtxtPtr ctxt, const xmlChar *data, int len) { * @elem: an element instance * @qname: the qualified name as appearing in the serialization * + * DEPRECATED: Internal function, don't use. + * * Pop the element end from the validation stack. * * returns 1 if no validation problem was found or 0 otherwise @@ -5804,8 +5770,11 @@ xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, if (state->exec != NULL) { ret = xmlRegExecPushString(state->exec, NULL, NULL); if (ret <= 0) { - xmlErrValidNode(ctxt, state->node, - XML_DTD_CONTENT_MODEL, + if (ret == XML_REGEXP_OUT_OF_MEMORY) + xmlVErrMemory(ctxt); + else + xmlErrValidNode(ctxt, state->node, + XML_DTD_CONTENT_MODEL, "Element %s content does not follow the DTD, Expecting more children\n", state->node->name, NULL,NULL); ret = 0; @@ -5831,6 +5800,8 @@ xmlValidatePopElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc ATTRIBUTE_UNUSED, * @doc: a document instance * @elem: an element instance * + * DEPRECATED: Internal function, don't use. + * * Try to validate a single element and it's attributes, * basically it does the following checks as described by the * XML-1.0 recommendation: @@ -5858,61 +5829,19 @@ xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, if (elem == NULL) return(0); switch (elem->type) { - case XML_ATTRIBUTE_NODE: - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Attribute element not expected\n", NULL, NULL ,NULL); - return(0); case XML_TEXT_NODE: - if (elem->children != NULL) { - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Text element has children !\n", - NULL,NULL,NULL); - return(0); - } - if (elem->ns != NULL) { - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Text element has namespace !\n", - NULL,NULL,NULL); - return(0); - } - if (elem->content == NULL) { - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Text element has no content !\n", - NULL,NULL,NULL); - return(0); - } - return(1); - case XML_XINCLUDE_START: - case XML_XINCLUDE_END: - return(1); case XML_CDATA_SECTION_NODE: case XML_ENTITY_REF_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: return(1); - case XML_ENTITY_NODE: - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Entity element not expected\n", NULL, NULL ,NULL); - return(0); - case XML_NOTATION_NODE: - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Notation element not expected\n", NULL, NULL ,NULL); - return(0); - case XML_DOCUMENT_NODE: - case XML_DOCUMENT_TYPE_NODE: - case XML_DOCUMENT_FRAG_NODE: - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "Document element not expected\n", NULL, NULL ,NULL); - return(0); - case XML_HTML_DOCUMENT_NODE: - xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "HTML Document not expected\n", NULL, NULL ,NULL); - return(0); case XML_ELEMENT_NODE: break; default: xmlErrValidNode(ctxt, elem, XML_ERR_INTERNAL_ERROR, - "unknown element type\n", NULL, NULL ,NULL); + "unexpected element type\n", NULL, NULL ,NULL); return(0); } @@ -5970,8 +5899,10 @@ xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, fullname = xmlBuildQName(child->name, child->ns->prefix, fn, 50); - if (fullname == NULL) + if (fullname == NULL) { + xmlVErrMemory(ctxt); return(0); + } cont = elemDecl->content; while (cont != NULL) { if (cont->type == XML_ELEMENT_CONTENT_ELEMENT) { @@ -6035,7 +5966,8 @@ xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, */ child = elem->children; while (child != NULL) { - if (child->type == XML_TEXT_NODE) { + if ((child->type == XML_TEXT_NODE) && + (child->content != NULL)) { const xmlChar *content = child->content; while (IS_BLANK_CH(*content)) @@ -6056,7 +5988,7 @@ xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, cont = elemDecl->content; tmp = xmlValidateElementContent(ctxt, child, elemDecl, 1, elem); if (tmp <= 0) - ret = tmp; + ret = 0; break; } } /* not continuous */ @@ -6198,6 +6130,8 @@ xmlValidateOneElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, * @ctxt: the validation context * @doc: a document instance * + * DEPRECATED: Internal function, don't use. + * * Try to validate a the root element * basically it does the following check as described by the * XML-1.0 recommendation: @@ -6237,7 +6171,7 @@ xmlValidateRoot(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { fullname = xmlBuildQName(root->name, root->ns->prefix, fn, 50); if (fullname == NULL) { - xmlVErrMemory(ctxt, NULL); + xmlVErrMemory(ctxt); return(0); } ret = xmlStrEqual(doc->intSubset->name, fullname); @@ -6291,9 +6225,13 @@ xmlValidateElement(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlNodePtr root) { attr = elem->properties; while (attr != NULL) { value = xmlNodeListGetString(doc, attr->children, 0); - ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value); - if (value != NULL) + if (value == NULL) { + xmlVErrMemory(ctxt); + ret = 0; + } else { + ret &= xmlValidateOneAttribute(ctxt, doc, elem, attr, value); xmlFree((char *)value); + } attr= attr->next; } @@ -6352,7 +6290,7 @@ xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt, dup = xmlStrdup(name); if (dup == NULL) { - ctxt->valid = 0; + xmlVErrMemory(ctxt); return; } cur = dup; @@ -6387,7 +6325,7 @@ xmlValidateRef(xmlRefPtr ref, xmlValidCtxtPtr ctxt, dup = xmlStrdup(name); if (dup == NULL) { - xmlVErrMemory(ctxt, "IDREFS split"); + xmlVErrMemory(ctxt); ctxt->valid = 0; return; } @@ -6455,6 +6393,8 @@ xmlValidateCheckRefCallback(void *payload, void *data, const xmlChar *name) { * @ctxt: the validation context * @doc: a document instance * + * DEPRECATED: Internal function, don't use. + * * Does the final step for the document validation once all the * incremental validation steps have been completed * @@ -6468,7 +6408,8 @@ xmlValidateCheckRefCallback(void *payload, void *data, const xmlChar *name) { int xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { xmlRefTablePtr table; - unsigned int save; + xmlParserCtxtPtr pctxt = NULL; + xmlParserInputPtr oldInput = NULL; if (ctxt == NULL) return(0); @@ -6478,10 +6419,6 @@ xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { return(0); } - /* trick to get correct line id report */ - save = ctxt->flags; - ctxt->flags &= ~XML_VCTXT_USE_PCTXT; - /* * Check all the NOTATION/NOTATIONS attributes */ @@ -6491,12 +6428,24 @@ xmlValidateDocumentFinal(xmlValidCtxtPtr ctxt, xmlDocPtr doc) { /* * Check all the IDREF/IDREFS attributes definition for validity */ + + /* + * Don't print line numbers. + */ + if (ctxt->flags & XML_VCTXT_USE_PCTXT) { + pctxt = ctxt->userData; + oldInput = pctxt->input; + pctxt->input = NULL; + } + table = (xmlRefTablePtr) doc->refs; ctxt->doc = doc; ctxt->valid = 1; xmlHashScan(table, xmlValidateCheckRefCallback, ctxt); - ctxt->flags = save; + if (ctxt->flags & XML_VCTXT_USE_PCTXT) + pctxt->input = oldInput; + return(ctxt->valid); } @@ -6522,31 +6471,42 @@ xmlValidateDtd(xmlValidCtxtPtr ctxt, xmlDocPtr doc, xmlDtdPtr dtd) { xmlDtdPtr oldExt, oldInt; xmlNodePtr root; - if (dtd == NULL) return(0); - if (doc == NULL) return(0); + if (dtd == NULL) + return(0); + if (doc == NULL) + return(0); + oldExt = doc->extSubset; oldInt = doc->intSubset; doc->extSubset = dtd; doc->intSubset = NULL; - ret = xmlValidateRoot(ctxt, doc); - if (ret == 0) { - doc->extSubset = oldExt; - doc->intSubset = oldInt; - return(ret); - } if (doc->ids != NULL) { - xmlFreeIDTable(doc->ids); - doc->ids = NULL; + xmlFreeIDTable(doc->ids); + doc->ids = NULL; } if (doc->refs != NULL) { - xmlFreeRefTable(doc->refs); - doc->refs = NULL; + xmlFreeRefTable(doc->refs); + doc->refs = NULL; } - root = xmlDocGetRootElement(doc); - ret = xmlValidateElement(ctxt, doc, root); - ret &= xmlValidateDocumentFinal(ctxt, doc); + + ret = xmlValidateRoot(ctxt, doc); + if (ret != 0) { + root = xmlDocGetRootElement(doc); + ret = xmlValidateElement(ctxt, doc, root); + ret &= xmlValidateDocumentFinal(ctxt, doc); + } + doc->extSubset = oldExt; doc->intSubset = oldInt; + if (doc->ids != NULL) { + xmlFreeIDTable(doc->ids); + doc->ids = NULL; + } + if (doc->refs != NULL) { + xmlFreeRefTable(doc->refs); + doc->refs = NULL; + } + return(ret); } @@ -6613,6 +6573,9 @@ xmlValidateAttributeCallback(void *payload, void *data, } } if (cur->atype == XML_ATTRIBUTE_NOTATION) { + const xmlChar *elemLocalName; + xmlChar *elemPrefix; + doc = cur->doc; if (cur->elem == NULL) { xmlErrValid(ctxt, XML_ERR_INTERNAL_ERROR, @@ -6621,13 +6584,25 @@ xmlValidateAttributeCallback(void *payload, void *data, return; } - if (doc != NULL) - elem = xmlGetDtdElementDesc(doc->intSubset, cur->elem); - if ((elem == NULL) && (doc != NULL)) - elem = xmlGetDtdElementDesc(doc->extSubset, cur->elem); + elemLocalName = xmlSplitQName4(cur->elem, &elemPrefix); + if (elemLocalName == NULL) { + xmlVErrMemory(ctxt); + return; + } + + if ((doc != NULL) && (doc->intSubset != NULL)) + elem = xmlHashLookup2(doc->intSubset->elements, + elemLocalName, elemPrefix); + if ((elem == NULL) && (doc != NULL) && (doc->extSubset != NULL)) + elem = xmlHashLookup2(doc->extSubset->elements, + elemLocalName, elemPrefix); if ((elem == NULL) && (cur->parent != NULL) && (cur->parent->type == XML_DTD_NODE)) - elem = xmlGetDtdElementDesc((xmlDtdPtr) cur->parent, cur->elem); + elem = xmlHashLookup2(((xmlDtdPtr) cur->parent)->elements, + elemLocalName, elemPrefix); + + xmlFree(elemPrefix); + if (elem == NULL) { xmlErrValidNode(ctxt, NULL, XML_DTD_UNKNOWN_ELEM, "attribute %s: could not find decl for element %s\n", @@ -6648,6 +6623,8 @@ xmlValidateAttributeCallback(void *payload, void *data, * @ctxt: the validation context * @doc: a document instance * + * DEPRECATED: Internal function, don't use. + * * Does the final step for the dtds validation once all the * subsets have been parsed * diff --git a/libraries/libxml2/xmlIO.c b/libraries/libxml2/xmlIO.c index 95d27157..421e1259 100644 --- a/libraries/libxml2/xmlIO.c +++ b/libraries/libxml2/xmlIO.c @@ -4,8 +4,6 @@ * See Copyright for the status of this software. * * daniel@veillard.com - * - * 14 Nov 2000 ht - for VMS, truncated name of long functions to under 32 char */ #define IN_LIBXML @@ -38,22 +36,8 @@ #include #endif -#ifndef S_ISDIR -# ifdef _S_ISDIR -# define S_ISDIR(x) _S_ISDIR(x) -# elif defined(S_IFDIR) -# ifdef S_IFMT -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -# elif defined(_S_IFMT) -# define S_ISDIR(m) (((m) & _S_IFMT) == S_IFDIR) -# endif -# endif -#endif - #include #include -#include -#include #include #include #include @@ -72,6 +56,22 @@ #define MINLEN 4000 +#ifndef STDOUT_FILENO + #define STDOUT_FILENO 1 +#endif + +#ifndef S_ISDIR +# ifdef _S_ISDIR +# define S_ISDIR(x) _S_ISDIR(x) +# elif defined(S_IFDIR) +# ifdef S_IFMT +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# elif defined(_S_IFMT) +# define S_ISDIR(m) (((m) & _S_IFMT) == S_IFDIR) +# endif +# endif +#endif + /* * Input I/O callback sets */ @@ -82,11 +82,14 @@ typedef struct _xmlInputCallback { xmlInputCloseCallback closecallback; } xmlInputCallback; -#define MAX_INPUT_CALLBACK 15 +/* This dummy function only marks default IO in the callback table */ +static int +xmlIODefaultMatch(const char *filename); + +#define MAX_INPUT_CALLBACK 10 static xmlInputCallback xmlInputCallbackTable[MAX_INPUT_CALLBACK]; -static int xmlInputCallbackNr = 0; -static int xmlInputCallbackInitialized = 0; +static int xmlInputCallbackNr; #ifdef LIBXML_OUTPUT_ENABLED /* @@ -99,112 +102,18 @@ typedef struct _xmlOutputCallback { xmlOutputCloseCallback closecallback; } xmlOutputCallback; -#define MAX_OUTPUT_CALLBACK 15 +#define MAX_OUTPUT_CALLBACK 10 static xmlOutputCallback xmlOutputCallbackTable[MAX_OUTPUT_CALLBACK]; -static int xmlOutputCallbackNr = 0; -static int xmlOutputCallbackInitialized = 0; +static int xmlOutputCallbackNr; #endif /* LIBXML_OUTPUT_ENABLED */ /************************************************************************ * * - * Tree memory error handler * + * Error handling * * * ************************************************************************/ -static const char* const IOerr[] = { - "Unknown IO error", /* UNKNOWN */ - "Permission denied", /* EACCES */ - "Resource temporarily unavailable",/* EAGAIN */ - "Bad file descriptor", /* EBADF */ - "Bad message", /* EBADMSG */ - "Resource busy", /* EBUSY */ - "Operation canceled", /* ECANCELED */ - "No child processes", /* ECHILD */ - "Resource deadlock avoided",/* EDEADLK */ - "Domain error", /* EDOM */ - "File exists", /* EEXIST */ - "Bad address", /* EFAULT */ - "File too large", /* EFBIG */ - "Operation in progress", /* EINPROGRESS */ - "Interrupted function call",/* EINTR */ - "Invalid argument", /* EINVAL */ - "Input/output error", /* EIO */ - "Is a directory", /* EISDIR */ - "Too many open files", /* EMFILE */ - "Too many links", /* EMLINK */ - "Inappropriate message buffer length",/* EMSGSIZE */ - "Filename too long", /* ENAMETOOLONG */ - "Too many open files in system",/* ENFILE */ - "No such device", /* ENODEV */ - "No such file or directory",/* ENOENT */ - "Exec format error", /* ENOEXEC */ - "No locks available", /* ENOLCK */ - "Not enough space", /* ENOMEM */ - "No space left on device", /* ENOSPC */ - "Function not implemented", /* ENOSYS */ - "Not a directory", /* ENOTDIR */ - "Directory not empty", /* ENOTEMPTY */ - "Not supported", /* ENOTSUP */ - "Inappropriate I/O control operation",/* ENOTTY */ - "No such device or address",/* ENXIO */ - "Operation not permitted", /* EPERM */ - "Broken pipe", /* EPIPE */ - "Result too large", /* ERANGE */ - "Read-only file system", /* EROFS */ - "Invalid seek", /* ESPIPE */ - "No such process", /* ESRCH */ - "Operation timed out", /* ETIMEDOUT */ - "Improper link", /* EXDEV */ - "Attempt to load network entity %s", /* XML_IO_NETWORK_ATTEMPT */ - "encoder error", /* XML_IO_ENCODER */ - "flush error", - "write error", - "no input", - "buffer full", - "loading error", - "not a socket", /* ENOTSOCK */ - "already connected", /* EISCONN */ - "connection refused", /* ECONNREFUSED */ - "unreachable network", /* ENETUNREACH */ - "address in use", /* EADDRINUSE */ - "already in use", /* EALREADY */ - "unknown address family", /* EAFNOSUPPORT */ -}; - -#if defined(_WIN32) -/** - * __xmlIOWin32UTF8ToWChar: - * @u8String: uft-8 string - * - * Convert a string from utf-8 to wchar (WINDOWS ONLY!) - */ -static wchar_t * -__xmlIOWin32UTF8ToWChar(const char *u8String) -{ - wchar_t *wString = NULL; - - if (u8String) { - int wLen = - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8String, - -1, NULL, 0); - if (wLen) { - wString = xmlMalloc(wLen * sizeof(wchar_t)); - if (wString) { - if (MultiByteToWideChar - (CP_UTF8, 0, u8String, -1, wString, wLen) == 0) { - xmlFree(wString); - wString = NULL; - } - } - } - } - - return wString; -} -#endif - -#if defined(LIBXML_HTTP_ENABLED) && defined(LIBXML_OUTPUT_ENABLED) /** * xmlIOErrMemory: * @extra: extra information @@ -212,11 +121,10 @@ __xmlIOWin32UTF8ToWChar(const char *u8String) * Handle an out of memory condition */ static void -xmlIOErrMemory(const char *extra) +xmlIOErrMemory(void) { - __xmlSimpleError(XML_FROM_IO, XML_ERR_NO_MEMORY, NULL, NULL, extra); + xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_IO, NULL); } -#endif /** * __xmlIOErr: @@ -226,13 +134,17 @@ xmlIOErrMemory(const char *extra) * * Handle an I/O error */ -void +int __xmlIOErr(int domain, int code, const char *extra) { - unsigned int idx; + xmlStructuredErrorFunc schannel = NULL; + xmlGenericErrorFunc channel = NULL; + void *data = NULL; + const char *fmt, *arg1, *arg2; + int res; if (code == 0) { - if (errno == 0) code = 0; + if (errno == 0) code = XML_IO_UNKNOWN; #ifdef EACCES else if (errno == EACCES) code = XML_IO_EACCES; #endif @@ -388,189 +300,49 @@ __xmlIOErr(int domain, int code, const char *extra) #endif else code = XML_IO_UNKNOWN; } - idx = 0; - if (code >= XML_IO_UNKNOWN) idx = code - XML_IO_UNKNOWN; - if (idx >= (sizeof(IOerr) / sizeof(IOerr[0]))) idx = 0; - - __xmlSimpleError(domain, code, NULL, IOerr[idx], extra); -} - -/** - * xmlIOErr: - * @code: the error number - * @extra: extra information - * - * Handle an I/O error - */ -static void -xmlIOErr(int code, const char *extra) -{ - __xmlIOErr(XML_FROM_IO, code, extra); -} -/** - * __xmlLoaderErr: - * @ctx: the parser context - * @extra: extra information - * - * Handle a resource access error - */ -void -__xmlLoaderErr(void *ctx, const char *msg, const char *filename) -{ - xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; - xmlStructuredErrorFunc schannel = NULL; - xmlGenericErrorFunc channel = NULL; - void *data = NULL; - xmlErrorLevel level = XML_ERR_ERROR; - - if ((ctxt != NULL) && (ctxt->disableSAX != 0) && - (ctxt->instate == XML_PARSER_EOF)) - return; - if ((ctxt != NULL) && (ctxt->sax != NULL)) { - if (ctxt->validate) { - channel = ctxt->sax->error; - level = XML_ERR_ERROR; - } else { - channel = ctxt->sax->warning; - level = XML_ERR_WARNING; - } - if (ctxt->sax->initialized == XML_SAX2_MAGIC) - schannel = ctxt->sax->serror; - data = ctxt->userData; + if (xmlStructuredError) { + schannel = xmlStructuredError; + data = xmlStructuredErrorContext; + } else { + channel = xmlGenericError; + data = xmlGenericErrorContext; } - __xmlRaiseError(schannel, channel, data, ctxt, NULL, XML_FROM_IO, - XML_IO_LOAD_ERROR, level, NULL, 0, - filename, NULL, NULL, 0, 0, - msg, filename); - -} -/************************************************************************ - * * - * Tree memory error handler * - * * - ************************************************************************/ -/** - * xmlNormalizeWindowsPath: - * @path: the input file path - * - * This function is obsolete. Please see xmlURIFromPath in uri.c for - * a better solution. - * - * Returns a canonicalized version of the path - */ -xmlChar * -xmlNormalizeWindowsPath(const xmlChar *path) -{ - return xmlCanonicPath(path); -} - -/** - * xmlCleanupInputCallbacks: - * - * clears the entire input callback table. this includes the - * compiled-in I/O. - */ -void -xmlCleanupInputCallbacks(void) -{ - int i; - - if (!xmlInputCallbackInitialized) - return; - - for (i = xmlInputCallbackNr - 1; i >= 0; i--) { - xmlInputCallbackTable[i].matchcallback = NULL; - xmlInputCallbackTable[i].opencallback = NULL; - xmlInputCallbackTable[i].readcallback = NULL; - xmlInputCallbackTable[i].closecallback = NULL; + if (extra != NULL) { + fmt = "%s: %s"; + } else { + fmt = "%s"; } - xmlInputCallbackNr = 0; - xmlInputCallbackInitialized = 0; -} - -/** - * xmlPopInputCallbacks: - * - * Clear the top input callback from the input stack. this includes the - * compiled-in I/O. - * - * Returns the number of input callback registered or -1 in case of error. - */ -int -xmlPopInputCallbacks(void) -{ - if (!xmlInputCallbackInitialized) - return(-1); - - if (xmlInputCallbackNr <= 0) - return(-1); - - xmlInputCallbackNr--; - xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = NULL; - xmlInputCallbackTable[xmlInputCallbackNr].opencallback = NULL; - xmlInputCallbackTable[xmlInputCallbackNr].readcallback = NULL; - xmlInputCallbackTable[xmlInputCallbackNr].closecallback = NULL; - - return(xmlInputCallbackNr); -} - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlCleanupOutputCallbacks: - * - * clears the entire output callback table. this includes the - * compiled-in I/O callbacks. - */ -void -xmlCleanupOutputCallbacks(void) -{ - int i; - - if (!xmlOutputCallbackInitialized) - return; + arg1 = xmlErrString(code); + arg2 = extra; - for (i = xmlOutputCallbackNr - 1; i >= 0; i--) { - xmlOutputCallbackTable[i].matchcallback = NULL; - xmlOutputCallbackTable[i].opencallback = NULL; - xmlOutputCallbackTable[i].writecallback = NULL; - xmlOutputCallbackTable[i].closecallback = NULL; + res = __xmlRaiseError(schannel, channel, data, NULL, NULL, + domain, code, XML_ERR_ERROR, NULL, 0, + extra, NULL, NULL, 0, 0, + fmt, arg1, arg2); + if (res < 0) { + xmlIOErrMemory(); + return(XML_ERR_NO_MEMORY); } - xmlOutputCallbackNr = 0; - xmlOutputCallbackInitialized = 0; + return(code); } /** - * xmlPopOutputCallbacks: - * - * Remove the top output callbacks from the output stack. This includes the - * compiled-in I/O. + * xmlIOErr: + * @code: the error number + * @extra: extra information * - * Returns the number of output callback registered or -1 in case of error. + * Handle an I/O error */ -int -xmlPopOutputCallbacks(void) +static int +xmlIOErr(int code, const char *extra) { - if (!xmlOutputCallbackInitialized) - return(-1); - - if (xmlOutputCallbackNr <= 0) - return(-1); - - xmlOutputCallbackNr--; - xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = NULL; - xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = NULL; - xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = NULL; - xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = NULL; - - return(xmlOutputCallbackNr); + return(__xmlIOErr(XML_FROM_IO, code, extra)); } -#endif /* LIBXML_OUTPUT_ENABLED */ - /************************************************************************ * * * Standard I/O for file accesses * @@ -580,92 +352,65 @@ xmlPopOutputCallbacks(void) #if defined(_WIN32) /** - * xmlWrapOpenUtf8: - * @path: the path in utf-8 encoding - * @mode: type of access (0 - read, 1 - write) - * - * function opens the file specified by @path + * __xmlIOWin32UTF8ToWChar: + * @u8String: uft-8 string * + * Convert a string from utf-8 to wchar (WINDOWS ONLY!) */ -static FILE* -xmlWrapOpenUtf8(const char *path,int mode) -{ - FILE *fd = NULL; - wchar_t *wPath; - - wPath = __xmlIOWin32UTF8ToWChar(path); - if(wPath) - { - fd = _wfopen(wPath, mode ? L"wb" : L"rb"); - xmlFree(wPath); - } - /* maybe path in native encoding */ - if(fd == NULL) - fd = fopen(path, mode ? "wb" : "rb"); - - return fd; -} - -#ifdef LIBXML_ZLIB_ENABLED -static gzFile -xmlWrapGzOpenUtf8(const char *path, const char *mode) +static wchar_t * +__xmlIOWin32UTF8ToWChar(const char *u8String) { - gzFile fd; - wchar_t *wPath; + wchar_t *wString = NULL; + int i; - fd = gzopen (path, mode); - if (fd) - return fd; + if (u8String) { + int wLen = + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, u8String, + -1, NULL, 0); + if (wLen) { + wString = xmlMalloc(wLen * sizeof(wchar_t)); + if (wString) { + if (MultiByteToWideChar + (CP_UTF8, 0, u8String, -1, wString, wLen) == 0) { + xmlFree(wString); + wString = NULL; + } + } - wPath = __xmlIOWin32UTF8ToWChar(path); - if(wPath) - { - int d, m = (strstr(mode, "r") ? O_RDONLY : O_RDWR); -#ifdef _O_BINARY - m |= (strstr(mode, "b") ? _O_BINARY : 0); -#endif - d = _wopen(wPath, m); - if (d >= 0) - fd = gzdopen(d, mode); - xmlFree(wPath); + /* + * Convert to backward slash + */ + for (i = 0; wString[i] != 0; i++) { + if (wString[i] == '/') + wString[i] = '\\'; + } + } } - return fd; + return wString; } + #endif /** - * xmlWrapStatUtf8: - * @path: the path in utf-8 encoding - * @info: structure that stores results + * xmlNormalizeWindowsPath: + * @path: the input file path * - * function obtains information about the file or directory + * DEPRECATED: This never really worked. * + * Returns a copy of path. */ -static int -xmlWrapStatUtf8(const char *path, struct _stat *info) { - int retval = -1; - wchar_t *wPath; - - wPath = __xmlIOWin32UTF8ToWChar(path); - if (wPath) { - retval = _wstat(wPath, info); - xmlFree(wPath); - } - /* maybe path in native encoding */ - if(retval < 0) - retval = _stat(path, info); - return retval; +xmlChar * +xmlNormalizeWindowsPath(const xmlChar *path) +{ + return xmlStrdup(path); } -#endif - /** * xmlCheckFilename: * @path: the path to check * - * function checks to see if @path is a valid source - * (file, socket...) for XML. + * DEPRECATED: Internal function, don't use. * * if stat is not available on the target machine, * returns 1. if stat fails, returns 0 (if calling @@ -673,9 +418,8 @@ xmlWrapStatUtf8(const char *path, struct _stat *info) { * if stat succeeds and the file is a directory, * returns 2. otherwise returns 1. */ - int -xmlCheckFilename (const char *path) +xmlCheckFilename(const char *path) { #ifdef HAVE_STAT #if defined(_WIN32) @@ -683,50 +427,183 @@ xmlCheckFilename (const char *path) #else struct stat stat_buffer; #endif + int res; #endif + if (path == NULL) return(0); #ifdef HAVE_STAT #if defined(_WIN32) - /* - * On Windows stat and wstat do not work with long pathname, - * which start with '\\?\' - */ - if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && - (path[3] == '\\') ) - return 1; + { + wchar_t *wpath; - if (xmlWrapStatUtf8(path, &stat_buffer) == -1) - return 0; + /* + * On Windows stat and wstat do not work with long pathname, + * which start with '\\?\' + */ + if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') && + (path[3] == '\\') ) + return 1; + + wpath = __xmlIOWin32UTF8ToWChar(path); + if (wpath == NULL) + return(0); + res = _wstat(wpath, &stat_buffer); + xmlFree(wpath); + } #else - if (stat(path, &stat_buffer) == -1) - return 0; + res = stat(path, &stat_buffer); #endif + + if (res < 0) + return 0; + #ifdef S_ISDIR if (S_ISDIR(stat_buffer.st_mode)) return 2; #endif #endif /* HAVE_STAT */ + return 1; } -/** - * xmlFdRead: - * @context: the I/O context - * @buffer: where to drop data - * @len: number of bytes to read - * - * Read @len bytes to @buffer from the I/O channel. - * - * Returns the number of bytes written +static int +xmlConvertUriToPath(const char *uri, char **out) { + const char *escaped; + char *unescaped; + + *out = NULL; + + if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file://localhost/", 17)) { + escaped = &uri[16]; + } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:///", 8)) { + escaped = &uri[7]; + } else if (!xmlStrncasecmp(BAD_CAST uri, BAD_CAST "file:/", 6)) { + /* lots of generators seems to lazy to read RFC 1738 */ + escaped = &uri[5]; + } else { + return(1); + } + +#ifdef _WIN32 + /* Ignore slash like in file:///C:/file.txt */ + escaped += 1; +#endif + + unescaped = xmlURIUnescapeString(escaped, 0, NULL); + if (unescaped == NULL) + return(-1); + + *out = unescaped; + return(0); +} + +/** + * xmlFdOpen: + * @filename: the URI for matching + * @out: pointer to resulting context + * + * Returns an xmlParserErrors code */ static int -xmlFdRead (void * context, char * buffer, int len) { +xmlFdOpen(const char *filename, int write, int *out) { + char *fromUri = NULL; + int flags; + int fd; int ret; - ret = read((int) (ptrdiff_t) context, &buffer[0], len); - if (ret < 0) xmlIOErr(0, "read()"); + *out = -1; + if (filename == NULL) + return(XML_ERR_ARGUMENT); + + if (xmlConvertUriToPath(filename, &fromUri) < 0) + return(XML_ERR_NO_MEMORY); + + if (fromUri != NULL) + filename = fromUri; + +#if defined(_WIN32) + { + wchar_t *wpath; + + wpath = __xmlIOWin32UTF8ToWChar(filename); + if (wpath == NULL) { + xmlFree(fromUri); + return(XML_ERR_NO_MEMORY); + } + if (write) + flags = _O_WRONLY | _O_CREAT | _O_TRUNC; + else + flags = _O_RDONLY; + fd = _wopen(wpath, flags | _O_BINARY, 0777); + xmlFree(wpath); + } +#else + if (write) + flags = O_WRONLY | O_CREAT | O_TRUNC; + else + flags = O_RDONLY; + fd = open(filename, flags, 0777); +#endif /* WIN32 */ + + if (fd < 0) { + /* + * Windows and possibly other platforms return EINVAL + * for invalid filenames. + */ + if ((errno == ENOENT) || (errno == EINVAL)) { + ret = XML_IO_ENOENT; + } else { + /* + * This error won't be forwarded to the parser context + * which will report it a second time. + */ + ret = xmlIOErr(0, filename); + } + } else { + *out = fd; + ret = XML_ERR_OK; + } + + xmlFree(fromUri); + return(ret); +} + +/** + * xmlFdRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to read + * + * Read @len bytes to @buffer from the I/O channel. + * + * Returns the number of bytes read + */ +static int +xmlFdRead(void *context, char *buffer, int len) { + int fd = (int) (ptrdiff_t) context; + int ret = 0; + int bytes; + + while (len > 0) { + bytes = read(fd, buffer, len); + if (bytes < 0) { + /* + * If we already got some bytes, return them without + * raising an error. + */ + if (ret > 0) + break; + return(-xmlIOErr(0, "read()")); + } + if (bytes == 0) + break; + ret += bytes; + buffer += bytes; + len -= bytes; + } + return(ret); } @@ -742,13 +619,20 @@ xmlFdRead (void * context, char * buffer, int len) { * Returns the number of bytes written */ static int -xmlFdWrite (void * context, const char * buffer, int len) { +xmlFdWrite(void *context, const char *buffer, int len) { + int fd = (int) (ptrdiff_t) context; int ret = 0; + int bytes; - if (len > 0) { - ret = write((int) (ptrdiff_t) context, &buffer[0], len); - if (ret < 0) xmlIOErr(0, "write()"); + while (len > 0) { + bytes = write(fd, buffer, len); + if (bytes < 0) + return(-xmlIOErr(0, "write()")); + ret += bytes; + buffer += bytes; + len -= bytes; } + return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ @@ -764,16 +648,19 @@ xmlFdWrite (void * context, const char * buffer, int len) { static int xmlFdClose (void * context) { int ret; + ret = close((int) (ptrdiff_t) context); - if (ret < 0) xmlIOErr(0, "close()"); - return(ret); + if (ret < 0) + return(xmlIOErr(0, "close()")); + + return(XML_ERR_OK); } /** * xmlFileMatch: * @filename: the URI for matching * - * input from FILE * + * DEPRECATED: Internal function, don't use. * * Returns 1 if matches, 0 otherwise */ @@ -783,139 +670,82 @@ xmlFileMatch (const char *filename ATTRIBUTE_UNUSED) { } /** - * xmlFileOpen_real: + * xmlFileOpenSafe: * @filename: the URI for matching + * @out: pointer to resulting context * - * input from FILE *, supports compressed input - * if @filename is " " then the standard input is used + * input from FILE * * * Returns an I/O context or NULL in case of error */ -static void * -xmlFileOpen_real (const char *filename) { - const char *path = filename; +static int +xmlFileOpenSafe(const char *filename, int write, void **out) { + char *fromUri = NULL; FILE *fd; + int ret = XML_ERR_OK; + *out = NULL; if (filename == NULL) - return(NULL); - - if (!strcmp(filename, "-")) { - fd = stdin; - return((void *) fd); - } + return(XML_ERR_ARGUMENT); - if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) { -#if defined (_WIN32) - path = &filename[17]; -#else - path = &filename[16]; -#endif - } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { -#if defined (_WIN32) - path = &filename[8]; -#else - path = &filename[7]; -#endif - } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) { - /* lots of generators seems to lazy to read RFC 1738 */ -#if defined (_WIN32) - path = &filename[6]; -#else - path = &filename[5]; -#endif - } + if (xmlConvertUriToPath(filename, &fromUri) < 0) + return(XML_ERR_NO_MEMORY); - /* Do not check DDNAME on zOS ! */ -#if !defined(__MVS__) - if (!xmlCheckFilename(path)) - return(NULL); -#endif + if (fromUri != NULL) + filename = fromUri; #if defined(_WIN32) - fd = xmlWrapOpenUtf8(path, 0); + { + wchar_t *wpath; + + wpath = __xmlIOWin32UTF8ToWChar(filename); + if (wpath == NULL) { + xmlFree(fromUri); + return(XML_ERR_NO_MEMORY); + } + fd = _wfopen(wpath, write ? L"wb" : L"rb"); + xmlFree(wpath); + } #else - fd = fopen(path, "rb"); + fd = fopen(filename, write ? "wb" : "rb"); #endif /* WIN32 */ - if (fd == NULL) xmlIOErr(0, path); - return((void *) fd); -} -/** - * xmlFileOpen: - * @filename: the URI for matching - * - * Wrapper around xmlFileOpen_real that try it with an unescaped - * version of @filename, if this fails fallback to @filename - * - * Returns a handler or NULL in case or failure - */ -void * -xmlFileOpen (const char *filename) { - char *unescaped; - void *retval; - - retval = xmlFileOpen_real(filename); - if (retval == NULL) { - unescaped = xmlURIUnescapeString(filename, 0, NULL); - if (unescaped != NULL) { - retval = xmlFileOpen_real(unescaped); - xmlFree(unescaped); - } + if (fd == NULL) { + /* + * Windows and possibly other platforms return EINVAL + * for invalid filenames. + */ + if ((errno == ENOENT) || (errno == EINVAL)) { + ret = XML_IO_ENOENT; + } else { + /* + * This error won't be forwarded to the parser context + * which will report it a second time. + */ + ret = xmlIOErr(0, filename); + } } - return retval; + *out = fd; + xmlFree(fromUri); + return(ret); } -#ifdef LIBXML_OUTPUT_ENABLED /** - * xmlFileOpenW: + * xmlFileOpen: * @filename: the URI for matching * - * output to from FILE *, - * if @filename is "-" then the standard output is used + * DEPRECATED: Internal function, don't use. * - * Returns an I/O context or NULL in case of error + * Returns an IO context or NULL in case or failure */ -static void * -xmlFileOpenW (const char *filename) { - const char *path = NULL; - FILE *fd; - - if (!strcmp(filename, "-")) { - fd = stdout; - return((void *) fd); - } - - if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) -#if defined (_WIN32) - path = &filename[17]; -#else - path = &filename[16]; -#endif - else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { -#if defined (_WIN32) - path = &filename[8]; -#else - path = &filename[7]; -#endif - } else - path = filename; - - if (path == NULL) - return(NULL); - -#if defined(_WIN32) - fd = xmlWrapOpenUtf8(path, 1); -#elif(__MVS__) - fd = fopen(path, "w"); -#else - fd = fopen(path, "wb"); -#endif /* WIN32 */ +void * +xmlFileOpen(const char *filename) { + void *context; - if (fd == NULL) xmlIOErr(0, path); - return((void *) fd); + xmlFileOpenSafe(filename, 0, &context); + return(context); } -#endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlFileRead: @@ -923,18 +753,30 @@ xmlFileOpenW (const char *filename) { * @buffer: where to drop data * @len: number of bytes to write * - * Read @len bytes to @buffer from the I/O channel. + * DEPRECATED: Internal function, don't use. * - * Returns the number of bytes written or < 0 in case of failure + * Returns the number of bytes read or < 0 in case of failure */ int -xmlFileRead (void * context, char * buffer, int len) { - int ret; +xmlFileRead(void * context, char * buffer, int len) { + FILE *file = context; + size_t bytes; + if ((context == NULL) || (buffer == NULL)) return(-1); - ret = fread(&buffer[0], 1, len, (FILE *) context); - if (ret < 0) xmlIOErr(0, "fread()"); - return(ret); + + /* + * The C standard doesn't mandate that fread sets errno, only + * POSIX does. The Windows documentation isn't really clear. + * Set errno to zero which will be reported as unknown error + * if fread fails without setting errno. + */ + errno = 0; + bytes = fread(buffer, 1, len, file); + if ((bytes < (size_t) len) && (ferror(file))) + return(-xmlIOErr(0, "fread()")); + + return(bytes); } #ifdef LIBXML_OUTPUT_ENABLED @@ -949,66 +791,65 @@ xmlFileRead (void * context, char * buffer, int len) { * Returns the number of bytes written */ static int -xmlFileWrite (void * context, const char * buffer, int len) { - int items; +xmlFileWrite(void *context, const char *buffer, int len) { + FILE *file = context; + size_t bytes; if ((context == NULL) || (buffer == NULL)) return(-1); - items = fwrite(&buffer[0], len, 1, (FILE *) context); - if ((items == 0) && (ferror((FILE *) context))) { - xmlIOErr(0, "fwrite()"); - return(-1); - } - return(items * len); + + errno = 0; + bytes = fwrite(buffer, 1, len, file); + if (bytes < (size_t) len) + return(-xmlIOErr(0, "fwrite()")); + + return(len); } #endif /* LIBXML_OUTPUT_ENABLED */ /** - * xmlFileClose: + * xmlFileFlush: * @context: the I/O context * - * Close an I/O channel - * - * Returns 0 or -1 in case of error + * Flush an I/O channel */ -int -xmlFileClose (void * context) { - FILE *fil; - int ret; +static int +xmlFileFlush (void * context) { + FILE *file = context; - if (context == NULL) + if (file == NULL) return(-1); - fil = (FILE *) context; - if ((fil == stdout) || (fil == stderr)) { - ret = fflush(fil); - if (ret < 0) - xmlIOErr(0, "fflush()"); - return(0); - } - if (fil == stdin) - return(0); - ret = ( fclose((FILE *) context) == EOF ) ? -1 : 0; - if (ret < 0) - xmlIOErr(0, "fclose()"); - return(ret); + + if (fflush(file) != 0) + return(xmlIOErr(0, "fflush()")); + + return(XML_ERR_OK); } /** - * xmlFileFlush: + * xmlFileClose: * @context: the I/O context * - * Flush an I/O channel + * DEPRECATED: Internal function, don't use. + * + * Returns 0 or -1 an error code case of error */ -static int -xmlFileFlush (void * context) { - int ret; +int +xmlFileClose (void * context) { + FILE *file = context; if (context == NULL) return(-1); - ret = ( fflush((FILE *) context) == EOF ) ? -1 : 0; - if (ret < 0) - xmlIOErr(0, "fflush()"); - return(ret); + + if (file == stdin) + return(0); + if ((file == stdout) || (file == stderr)) + return(xmlFileFlush(file)); + + if (fclose(file) != 0) + return(xmlIOErr(0, "fclose()")); + + return(0); } #ifdef LIBXML_OUTPUT_ENABLED @@ -1020,7 +861,8 @@ xmlFileFlush (void * context) { * * Write @len bytes from @buffer to the xml buffer * - * Returns the number of bytes written + * Returns the number of bytes written or a negative xmlParserErrors + * value. */ static int xmlBufferWrite (void * context, const char * buffer, int len) { @@ -1028,7 +870,7 @@ xmlBufferWrite (void * context, const char * buffer, int len) { ret = xmlBufferAdd((xmlBufferPtr) context, (const xmlChar *) buffer, len); if (ret != 0) - return(-1); + return(-XML_ERR_NO_MEMORY); return(len); } #endif @@ -1039,148 +881,6 @@ xmlBufferWrite (void * context, const char * buffer, int len) { * I/O for compressed file accesses * * * ************************************************************************/ -/** - * xmlGzfileMatch: - * @filename: the URI for matching - * - * input from compressed file test - * - * Returns 1 if matches, 0 otherwise - */ -static int -xmlGzfileMatch (const char *filename ATTRIBUTE_UNUSED) { - return(1); -} - -/** - * xmlGzfileOpen_real: - * @filename: the URI for matching - * - * input from compressed file open - * if @filename is " " then the standard input is used - * - * Returns an I/O context or NULL in case of error - */ -static void * -xmlGzfileOpen_real (const char *filename) { - const char *path = NULL; - gzFile fd; - - if (!strcmp(filename, "-")) { - int duped_fd = dup(fileno(stdin)); - fd = gzdopen(duped_fd, "rb"); - if (fd == Z_NULL && duped_fd >= 0) { - close(duped_fd); /* gzdOpen() does not close on failure */ - } - - return((void *) fd); - } - - if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) -#if defined (_WIN32) - path = &filename[17]; -#else - path = &filename[16]; -#endif - else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { -#if defined (_WIN32) - path = &filename[8]; -#else - path = &filename[7]; -#endif - } else - path = filename; - - if (path == NULL) - return(NULL); - if (!xmlCheckFilename(path)) - return(NULL); - -#if defined(_WIN32) - fd = xmlWrapGzOpenUtf8(path, "rb"); -#else - fd = gzopen(path, "rb"); -#endif - return((void *) fd); -} - -/** - * xmlGzfileOpen: - * @filename: the URI for matching - * - * Wrapper around xmlGzfileOpen_real if the open fails, it will - * try to unescape @filename - */ -static void * -xmlGzfileOpen (const char *filename) { - char *unescaped; - void *retval; - - retval = xmlGzfileOpen_real(filename); - if (retval == NULL) { - unescaped = xmlURIUnescapeString(filename, 0, NULL); - if (unescaped != NULL) { - retval = xmlGzfileOpen_real(unescaped); - } - xmlFree(unescaped); - } - return retval; -} - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlGzfileOpenW: - * @filename: the URI for matching - * @compression: the compression factor (0 - 9 included) - * - * input from compressed file open - * if @filename is " " then the standard input is used - * - * Returns an I/O context or NULL in case of error - */ -static void * -xmlGzfileOpenW (const char *filename, int compression) { - const char *path = NULL; - char mode[15]; - gzFile fd; - - snprintf(mode, sizeof(mode), "wb%d", compression); - if (!strcmp(filename, "-")) { - int duped_fd = dup(fileno(stdout)); - fd = gzdopen(duped_fd, "rb"); - if (fd == Z_NULL && duped_fd >= 0) { - close(duped_fd); /* gzdOpen() does not close on failure */ - } - - return((void *) fd); - } - - if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) -#if defined (_WIN32) - path = &filename[17]; -#else - path = &filename[16]; -#endif - else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { -#if defined (_WIN32) - path = &filename[8]; -#else - path = &filename[7]; -#endif - } else - path = filename; - - if (path == NULL) - return(NULL); - -#if defined(_WIN32) - fd = xmlWrapGzOpenUtf8(path, mode); -#else - fd = gzopen(path, mode); -#endif - return((void *) fd); -} -#endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlGzfileRead: @@ -1238,103 +938,29 @@ xmlGzfileClose (void * context) { } #endif /* LIBXML_ZLIB_ENABLED */ -#ifdef LIBXML_LZMA_ENABLED /************************************************************************ * * * I/O for compressed file accesses * * * ************************************************************************/ + +#ifdef LIBXML_LZMA_ENABLED + #include "private/xzlib.h" + /** - * xmlXzfileMatch: - * @filename: the URI for matching + * xmlXzfileRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write * - * input from compressed file test + * Read @len bytes to @buffer from the compressed I/O channel. * - * Returns 1 if matches, 0 otherwise + * Returns the number of bytes written */ static int -xmlXzfileMatch (const char *filename ATTRIBUTE_UNUSED) { - return(1); -} - -/** - * xmlXzFileOpen_real: - * @filename: the URI for matching - * - * input from compressed file open - * if @filename is " " then the standard input is used - * - * Returns an I/O context or NULL in case of error - */ -static void * -xmlXzfileOpen_real (const char *filename) { - const char *path = NULL; - xzFile fd; - - if (!strcmp(filename, "-")) { - fd = __libxml2_xzdopen(dup(fileno(stdin)), "rb"); - return((void *) fd); - } - - if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file://localhost/", 17)) { - path = &filename[16]; - } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:///", 8)) { - path = &filename[7]; - } else if (!xmlStrncasecmp(BAD_CAST filename, BAD_CAST "file:/", 6)) { - /* lots of generators seems to lazy to read RFC 1738 */ - path = &filename[5]; - } else - path = filename; - - if (path == NULL) - return(NULL); - if (!xmlCheckFilename(path)) - return(NULL); - - fd = __libxml2_xzopen(path, "rb"); - return((void *) fd); -} - -/** - * xmlXzfileOpen: - * @filename: the URI for matching - * - * Wrapper around xmlXzfileOpen_real that try it with an unescaped - * version of @filename, if this fails fallback to @filename - * - * Returns a handler or NULL in case or failure - */ -static void * -xmlXzfileOpen (const char *filename) { - char *unescaped; - void *retval; - - retval = xmlXzfileOpen_real(filename); - if (retval == NULL) { - unescaped = xmlURIUnescapeString(filename, 0, NULL); - if (unescaped != NULL) { - retval = xmlXzfileOpen_real(unescaped); - } - xmlFree(unescaped); - } - - return retval; -} - -/** - * xmlXzfileRead: - * @context: the I/O context - * @buffer: where to drop data - * @len: number of bytes to write - * - * Read @len bytes to @buffer from the compressed I/O channel. - * - * Returns the number of bytes written - */ -static int -xmlXzfileRead (void * context, char * buffer, int len) { - int ret; +xmlXzfileRead (void * context, char * buffer, int len) { + int ret; ret = __libxml2_xzread((xzFile) context, &buffer[0], len); if (ret < 0) xmlIOErr(0, "xzread()"); @@ -1357,371 +983,19 @@ xmlXzfileClose (void * context) { } #endif /* LIBXML_LZMA_ENABLED */ -#ifdef LIBXML_HTTP_ENABLED /************************************************************************ * * * I/O for HTTP file accesses * * * ************************************************************************/ -#ifdef LIBXML_OUTPUT_ENABLED -typedef struct xmlIOHTTPWriteCtxt_ -{ - int compression; - - char * uri; - - void * doc_buff; - -} xmlIOHTTPWriteCtxt, *xmlIOHTTPWriteCtxtPtr; - -#ifdef LIBXML_ZLIB_ENABLED - -#define DFLT_WBITS ( -15 ) -#define DFLT_MEM_LVL ( 8 ) -#define GZ_MAGIC1 ( 0x1f ) -#define GZ_MAGIC2 ( 0x8b ) -#define LXML_ZLIB_OS_CODE ( 0x03 ) -#define INIT_HTTP_BUFF_SIZE ( 32768 ) -#define DFLT_ZLIB_RATIO ( 5 ) - -/* -** Data structure and functions to work with sending compressed data -** via HTTP. -*/ - -typedef struct xmlZMemBuff_ -{ - unsigned long size; - unsigned long crc; - - unsigned char * zbuff; - z_stream zctrl; - -} xmlZMemBuff, *xmlZMemBuffPtr; - -/** - * append_reverse_ulong - * @buff: Compressed memory buffer - * @data: Unsigned long to append - * - * Append a unsigned long in reverse byte order to the end of the - * memory buffer. - */ -static void -append_reverse_ulong( xmlZMemBuff * buff, unsigned long data ) { - - int idx; - - if ( buff == NULL ) - return; - - /* - ** This is plagiarized from putLong in gzio.c (zlib source) where - ** the number "4" is hardcoded. If zlib is ever patched to - ** support 64 bit file sizes, this code would need to be patched - ** as well. - */ - - for ( idx = 0; idx < 4; idx++ ) { - *buff->zctrl.next_out = ( data & 0xff ); - data >>= 8; - buff->zctrl.next_out++; - } - - return; -} - -/** - * - * xmlFreeZMemBuff - * @buff: The memory buffer context to clear - * - * Release all the resources associated with the compressed memory buffer. - */ -static void -xmlFreeZMemBuff( xmlZMemBuffPtr buff ) { - - if ( buff == NULL ) - return; - - xmlFree( buff->zbuff ); - deflateEnd( &buff->zctrl ); - - xmlFree( buff ); - return; -} - -/** - * xmlCreateZMemBuff - *@compression: Compression value to use - * - * Create a memory buffer to hold the compressed XML document. The - * compressed document in memory will end up being identical to what - * would be created if gzopen/gzwrite/gzclose were being used to - * write the document to disk. The code for the header/trailer data to - * the compression is plagiarized from the zlib source files. - */ -static void * -xmlCreateZMemBuff( int compression ) { - - int z_err; - int hdr_lgth; - xmlZMemBuffPtr buff = NULL; - - if ( ( compression < 1 ) || ( compression > 9 ) ) - return ( NULL ); - - /* Create the control and data areas */ - - buff = xmlMalloc( sizeof( xmlZMemBuff ) ); - if ( buff == NULL ) { - xmlIOErrMemory("creating buffer context"); - return ( NULL ); - } - - (void)memset( buff, 0, sizeof( xmlZMemBuff ) ); - buff->size = INIT_HTTP_BUFF_SIZE; - buff->zbuff = xmlMalloc( buff->size ); - if ( buff->zbuff == NULL ) { - xmlFreeZMemBuff( buff ); - xmlIOErrMemory("creating buffer"); - return ( NULL ); - } - - z_err = deflateInit2( &buff->zctrl, compression, Z_DEFLATED, - DFLT_WBITS, DFLT_MEM_LVL, Z_DEFAULT_STRATEGY ); - if ( z_err != Z_OK ) { - xmlChar msg[500]; - xmlFreeZMemBuff( buff ); - buff = NULL; - xmlStrPrintf(msg, 500, - "xmlCreateZMemBuff: %s %d\n", - "Error initializing compression context. ZLIB error:", - z_err ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - return ( NULL ); - } - - /* Set the header data. The CRC will be needed for the trailer */ - buff->crc = crc32( 0L, NULL, 0 ); - hdr_lgth = snprintf( (char *)buff->zbuff, buff->size, - "%c%c%c%c%c%c%c%c%c%c", - GZ_MAGIC1, GZ_MAGIC2, Z_DEFLATED, - 0, 0, 0, 0, 0, 0, LXML_ZLIB_OS_CODE ); - buff->zctrl.next_out = buff->zbuff + hdr_lgth; - buff->zctrl.avail_out = buff->size - hdr_lgth; - - return ( buff ); -} - -/** - * xmlZMemBuffExtend - * @buff: Buffer used to compress and consolidate data. - * @ext_amt: Number of bytes to extend the buffer. - * - * Extend the internal buffer used to store the compressed data by the - * specified amount. - * - * Returns 0 on success or -1 on failure to extend the buffer. On failure - * the original buffer still exists at the original size. - */ -static int -xmlZMemBuffExtend( xmlZMemBuffPtr buff, size_t ext_amt ) { - - int rc = -1; - size_t new_size; - size_t cur_used; - - unsigned char * tmp_ptr = NULL; - - if ( buff == NULL ) - return ( -1 ); - - else if ( ext_amt == 0 ) - return ( 0 ); - - cur_used = buff->zctrl.next_out - buff->zbuff; - new_size = buff->size + ext_amt; - - tmp_ptr = xmlRealloc( buff->zbuff, new_size ); - if ( tmp_ptr != NULL ) { - rc = 0; - buff->size = new_size; - buff->zbuff = tmp_ptr; - buff->zctrl.next_out = tmp_ptr + cur_used; - buff->zctrl.avail_out = new_size - cur_used; - } - else { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlZMemBuffExtend: %s %lu bytes.\n", - "Allocation failure extending output buffer to", - (unsigned long) new_size ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - } - - return ( rc ); -} - -/** - * xmlZMemBuffAppend - * @buff: Buffer used to compress and consolidate data - * @src: Uncompressed source content to append to buffer - * @len: Length of source data to append to buffer - * - * Compress and append data to the internal buffer. The data buffer - * will be expanded if needed to store the additional data. - * - * Returns the number of bytes appended to the buffer or -1 on error. - */ -static int -xmlZMemBuffAppend( xmlZMemBuffPtr buff, const char * src, int len ) { - - int z_err; - size_t min_accept; - - if ( ( buff == NULL ) || ( src == NULL ) ) - return ( -1 ); - - buff->zctrl.avail_in = len; - buff->zctrl.next_in = (unsigned char *)src; - while ( buff->zctrl.avail_in > 0 ) { - /* - ** Extend the buffer prior to deflate call if a reasonable amount - ** of output buffer space is not available. - */ - min_accept = buff->zctrl.avail_in / DFLT_ZLIB_RATIO; - if ( buff->zctrl.avail_out <= min_accept ) { - if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) - return ( -1 ); - } - - z_err = deflate( &buff->zctrl, Z_NO_FLUSH ); - if ( z_err != Z_OK ) { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlZMemBuffAppend: %s %d %s - %d", - "Compression error while appending", - len, "bytes to buffer. ZLIB error", z_err ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - return ( -1 ); - } - } - - buff->crc = crc32( buff->crc, (unsigned char *)src, len ); - - return ( len ); -} - -/** - * xmlZMemBuffGetContent - * @buff: Compressed memory content buffer - * @data_ref: Pointer reference to point to compressed content - * - * Flushes the compression buffers, appends gzip file trailers and - * returns the compressed content and length of the compressed data. - * NOTE: The gzip trailer code here is plagiarized from zlib source. - * - * Returns the length of the compressed data or -1 on error. - */ -static int -xmlZMemBuffGetContent( xmlZMemBuffPtr buff, char ** data_ref ) { - - int zlgth = -1; - int z_err; - - if ( ( buff == NULL ) || ( data_ref == NULL ) ) - return ( -1 ); - - /* Need to loop until compression output buffers are flushed */ - - do - { - z_err = deflate( &buff->zctrl, Z_FINISH ); - if ( z_err == Z_OK ) { - /* In this case Z_OK means more buffer space needed */ - - if ( xmlZMemBuffExtend( buff, buff->size ) == -1 ) - return ( -1 ); - } - } - while ( z_err == Z_OK ); - - /* If the compression state is not Z_STREAM_END, some error occurred */ - - if ( z_err == Z_STREAM_END ) { - - /* Need to append the gzip data trailer */ - - if ( buff->zctrl.avail_out < ( 2 * sizeof( unsigned long ) ) ) { - if ( xmlZMemBuffExtend(buff, (2 * sizeof(unsigned long))) == -1 ) - return ( -1 ); - } - - /* - ** For whatever reason, the CRC and length data are pushed out - ** in reverse byte order. So a memcpy can't be used here. - */ - - append_reverse_ulong( buff, buff->crc ); - append_reverse_ulong( buff, buff->zctrl.total_in ); - - zlgth = buff->zctrl.next_out - buff->zbuff; - *data_ref = (char *)buff->zbuff; - } - - else { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlZMemBuffGetContent: %s - %d\n", - "Error flushing zlib buffers. Error code", z_err ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - } - - return ( zlgth ); -} -#endif /* LIBXML_OUTPUT_ENABLED */ -#endif /* LIBXML_ZLIB_ENABLED */ - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlFreeHTTPWriteCtxt - * @ctxt: Context to cleanup - * - * Free allocated memory and reclaim system resources. - * - * No return value. - */ -static void -xmlFreeHTTPWriteCtxt( xmlIOHTTPWriteCtxtPtr ctxt ) -{ - if ( ctxt->uri != NULL ) - xmlFree( ctxt->uri ); - - if ( ctxt->doc_buff != NULL ) { - -#ifdef LIBXML_ZLIB_ENABLED - if ( ctxt->compression > 0 ) { - xmlFreeZMemBuff( ctxt->doc_buff ); - } - else -#endif - { - xmlOutputBufferClose( ctxt->doc_buff ); - } - } - - xmlFree( ctxt ); - return; -} -#endif /* LIBXML_OUTPUT_ENABLED */ - - +#ifdef LIBXML_HTTP_ENABLED /** * xmlIOHTTPMatch: * @filename: the URI for matching * + * DEPRECATED: Internal function, don't use. + * * check if the URI matches an HTTP one * * Returns 1 if matches, 0 otherwise @@ -1737,6 +1011,8 @@ xmlIOHTTPMatch (const char *filename) { * xmlIOHTTPOpen: * @filename: the URI for matching * + * DEPRECATED: Internal function, don't use. + * * open an HTTP I/O channel * * Returns an I/O context or NULL in case of error @@ -1752,78 +1028,15 @@ xmlIOHTTPOpen (const char *filename) { * @post_uri: The destination URI for the document * @compression: The compression desired for the document. * - * Open a temporary buffer to collect the document for a subsequent HTTP POST - * request. Non-static as is called from the output buffer creation routine. + * DEPRECATED: Support for HTTP POST has been removed. * - * Returns an I/O context or NULL in case of error. + * Returns NULL. */ - void * -xmlIOHTTPOpenW(const char *post_uri, int compression ATTRIBUTE_UNUSED) +xmlIOHTTPOpenW(const char *post_uri ATTRIBUTE_UNUSED, + int compression ATTRIBUTE_UNUSED) { - - xmlIOHTTPWriteCtxtPtr ctxt = NULL; - - if (post_uri == NULL) - return (NULL); - - ctxt = xmlMalloc(sizeof(xmlIOHTTPWriteCtxt)); - if (ctxt == NULL) { - xmlIOErrMemory("creating HTTP output context"); - return (NULL); - } - - (void) memset(ctxt, 0, sizeof(xmlIOHTTPWriteCtxt)); - - ctxt->uri = (char *) xmlStrdup((const xmlChar *)post_uri); - if (ctxt->uri == NULL) { - xmlIOErrMemory("copying URI"); - xmlFreeHTTPWriteCtxt(ctxt); - return (NULL); - } - - /* - * ** Since the document length is required for an HTTP post, - * ** need to put the document into a buffer. A memory buffer - * ** is being used to avoid pushing the data to disk and back. - */ - -#ifdef LIBXML_ZLIB_ENABLED - if ((compression > 0) && (compression <= 9)) { - - ctxt->compression = compression; - ctxt->doc_buff = xmlCreateZMemBuff(compression); - } else -#endif - { - /* Any character conversions should have been done before this */ - - ctxt->doc_buff = xmlAllocOutputBufferInternal(NULL); - } - - if (ctxt->doc_buff == NULL) { - xmlFreeHTTPWriteCtxt(ctxt); - ctxt = NULL; - } - - return (ctxt); -} -#endif /* LIBXML_OUTPUT_ENABLED */ - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlIOHTTPDfltOpenW - * @post_uri: The destination URI for this document. - * - * Calls xmlIOHTTPOpenW with no compression to set up for a subsequent - * HTTP post command. This function should generally not be used as - * the open callback is short circuited in xmlOutputBufferCreateFile. - * - * Returns a pointer to the new IO context. - */ -static void * -xmlIOHTTPDfltOpenW( const char * post_uri ) { - return ( xmlIOHTTPOpenW( post_uri, 0 ) ); + return(NULL); } #endif /* LIBXML_OUTPUT_ENABLED */ @@ -1833,6 +1046,8 @@ xmlIOHTTPDfltOpenW( const char * post_uri ) { * @buffer: where to drop data * @len: number of bytes to write * + * DEPRECATED: Internal function, don't use. + * * Read @len bytes to @buffer from the I/O channel. * * Returns the number of bytes written @@ -1843,59 +1058,12 @@ xmlIOHTTPRead(void * context, char * buffer, int len) { return(xmlNanoHTTPRead(context, &buffer[0], len)); } -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlIOHTTPWrite - * @context: previously opened writing context - * @buffer: data to output to temporary buffer - * @len: bytes to output - * - * Collect data from memory buffer into a temporary file for later - * processing. - * - * Returns number of bytes written. - */ - -static int -xmlIOHTTPWrite( void * context, const char * buffer, int len ) { - - xmlIOHTTPWriteCtxtPtr ctxt = context; - - if ( ( ctxt == NULL ) || ( ctxt->doc_buff == NULL ) || ( buffer == NULL ) ) - return ( -1 ); - - if ( len > 0 ) { - - /* Use gzwrite or fwrite as previously setup in the open call */ - -#ifdef LIBXML_ZLIB_ENABLED - if ( ctxt->compression > 0 ) - len = xmlZMemBuffAppend( ctxt->doc_buff, buffer, len ); - - else -#endif - len = xmlOutputBufferWrite( ctxt->doc_buff, len, buffer ); - - if ( len < 0 ) { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlIOHTTPWrite: %s\n%s '%s'.\n", - "Error appending to internal buffer.", - "Error sending document to URI", - ctxt->uri ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - } - } - - return ( len ); -} -#endif /* LIBXML_OUTPUT_ENABLED */ - - /** * xmlIOHTTPClose: * @context: the I/O context * + * DEPRECATED: Internal function, don't use. + * * Close an HTTP I/O channel * * Returns 0 @@ -1905,119 +1073,6 @@ xmlIOHTTPClose (void * context) { xmlNanoHTTPClose(context); return 0; } - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlIOHTTCloseWrite - * @context: The I/O context - * @http_mthd: The HTTP method to be used when sending the data - * - * Close the transmit HTTP I/O channel and actually send the data. - */ -static int -xmlIOHTTPCloseWrite( void * context, const char * http_mthd ) { - - int close_rc = -1; - int http_rtn = 0; - int content_lgth = 0; - xmlIOHTTPWriteCtxtPtr ctxt = context; - - char * http_content = NULL; - char * content_encoding = NULL; - char * content_type = (char *) "text/xml"; - void * http_ctxt = NULL; - - if ( ( ctxt == NULL ) || ( http_mthd == NULL ) ) - return ( -1 ); - - /* Retrieve the content from the appropriate buffer */ - -#ifdef LIBXML_ZLIB_ENABLED - - if ( ctxt->compression > 0 ) { - content_lgth = xmlZMemBuffGetContent( ctxt->doc_buff, &http_content ); - content_encoding = (char *) "Content-Encoding: gzip"; - } - else -#endif - { - /* Pull the data out of the memory output buffer */ - - xmlOutputBufferPtr dctxt = ctxt->doc_buff; - http_content = (char *) xmlBufContent(dctxt->buffer); - content_lgth = xmlBufUse(dctxt->buffer); - } - - if ( http_content == NULL ) { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlIOHTTPCloseWrite: %s '%s' %s '%s'.\n", - "Error retrieving content.\nUnable to", - http_mthd, "data to URI", ctxt->uri ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - } - - else { - - http_ctxt = xmlNanoHTTPMethod( ctxt->uri, http_mthd, http_content, - &content_type, content_encoding, - content_lgth ); - - if ( http_ctxt != NULL ) { - - http_rtn = xmlNanoHTTPReturnCode( http_ctxt ); - if ( ( http_rtn >= 200 ) && ( http_rtn < 300 ) ) - close_rc = 0; - else { - xmlChar msg[500]; - xmlStrPrintf(msg, 500, - "xmlIOHTTPCloseWrite: HTTP '%s' of %d %s\n'%s' %s %d\n", - http_mthd, content_lgth, - "bytes to URI", ctxt->uri, - "failed. HTTP return code:", http_rtn ); - xmlIOErr(XML_IO_WRITE, (const char *) msg); - } - - xmlNanoHTTPClose( http_ctxt ); - xmlFree( content_type ); - } - } - - /* Final cleanups */ - - xmlFreeHTTPWriteCtxt( ctxt ); - - return ( close_rc ); -} - -/** - * xmlIOHTTPClosePut - * - * @context: The I/O context - * - * Close the transmit HTTP I/O channel and actually send data using a PUT - * HTTP method. - */ -static int -xmlIOHTTPClosePut( void * ctxt ) { - return ( xmlIOHTTPCloseWrite( ctxt, "PUT" ) ); -} - - -/** - * xmlIOHTTPClosePost - * - * @context: The I/O context - * - * Close the transmit HTTP I/O channel and actually send data using a POST - * HTTP method. - */ -static int -xmlIOHTTPClosePost( void * ctxt ) { - return ( xmlIOHTTPCloseWrite( ctxt, "POST" ) ); -} -#endif /* LIBXML_OUTPUT_ENABLED */ - #endif /* LIBXML_HTTP_ENABLED */ #ifdef LIBXML_FTP_ENABLED @@ -2030,6 +1085,8 @@ xmlIOHTTPClosePost( void * ctxt ) { * xmlIOFTPMatch: * @filename: the URI for matching * + * DEPRECATED: Internal function, don't use. + * * check if the URI matches an FTP one * * Returns 1 if matches, 0 otherwise @@ -2045,6 +1102,8 @@ xmlIOFTPMatch (const char *filename) { * xmlIOFTPOpen: * @filename: the URI for matching * + * DEPRECATED: Internal function, don't use. + * * open an FTP I/O channel * * Returns an I/O context or NULL in case of error @@ -2060,6 +1119,8 @@ xmlIOFTPOpen (const char *filename) { * @buffer: where to drop data * @len: number of bytes to write * + * DEPRECATED: Internal function, don't use. + * * Read @len bytes to @buffer from the I/O channel. * * Returns the number of bytes written @@ -2074,6 +1135,8 @@ xmlIOFTPRead(void * context, char * buffer, int len) { * xmlIOFTPClose: * @context: the I/O context * + * DEPRECATED: Internal function, don't use. + * * Close an FTP I/O channel * * Returns 0 @@ -2084,161 +1147,192 @@ xmlIOFTPClose (void * context) { } #endif /* LIBXML_FTP_ENABLED */ +/************************************************************************ + * * + * Input/output buffers * + * * + ************************************************************************/ -/** - * xmlRegisterInputCallbacks: - * @matchFunc: the xmlInputMatchCallback - * @openFunc: the xmlInputOpenCallback - * @readFunc: the xmlInputReadCallback - * @closeFunc: the xmlInputCloseCallback - * - * Register a new set of I/O callback for handling parser input. - * - * Returns the registered handler number or -1 in case of error - */ -int -xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc, - xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc, - xmlInputCloseCallback closeFunc) { - if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) { - return(-1); - } - xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc; - xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc; - xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc; - xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc; - xmlInputCallbackInitialized = 1; - return(xmlInputCallbackNr++); -} - -#ifdef LIBXML_OUTPUT_ENABLED -/** - * xmlRegisterOutputCallbacks: - * @matchFunc: the xmlOutputMatchCallback - * @openFunc: the xmlOutputOpenCallback - * @writeFunc: the xmlOutputWriteCallback - * @closeFunc: the xmlOutputCloseCallback - * - * Register a new set of I/O callback for handling output. - * - * Returns the registered handler number or -1 in case of error - */ -int -xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc, - xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc, - xmlOutputCloseCallback closeFunc) { - if (xmlOutputCallbackNr >= MAX_OUTPUT_CALLBACK) { - return(-1); - } - xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc; - xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc; - xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc; - xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc; - xmlOutputCallbackInitialized = 1; - return(xmlOutputCallbackNr++); +static int +xmlIODefaultMatch(const char *filename ATTRIBUTE_UNUSED) { + return(1); } -#endif /* LIBXML_OUTPUT_ENABLED */ /** - * xmlRegisterDefaultInputCallbacks: + * xmlInputDefaultOpen: + * @buf: input buffer to be filled + * @filename: filename or URI * - * Registers the default compiled-in I/O handlers. + * Returns an xmlParserErrors code. */ -void -xmlRegisterDefaultInputCallbacks(void) { - if (xmlInputCallbackInitialized) - return; +static int +xmlInputDefaultOpen(xmlParserInputBufferPtr buf, const char *filename) { + int ret; + int fd; - xmlRegisterInputCallbacks(xmlFileMatch, xmlFileOpen, - xmlFileRead, xmlFileClose); -#ifdef LIBXML_ZLIB_ENABLED - xmlRegisterInputCallbacks(xmlGzfileMatch, xmlGzfileOpen, - xmlGzfileRead, xmlGzfileClose); -#endif /* LIBXML_ZLIB_ENABLED */ -#ifdef LIBXML_LZMA_ENABLED - xmlRegisterInputCallbacks(xmlXzfileMatch, xmlXzfileOpen, - xmlXzfileRead, xmlXzfileClose); -#endif /* LIBXML_LZMA_ENABLED */ +#ifdef LIBXML_FTP_ENABLED + if (xmlIOFTPMatch(filename)) { + buf->context = xmlIOFTPOpen(filename); + + if (buf->context != NULL) { + buf->readcallback = xmlIOFTPRead; + buf->closecallback = xmlIOFTPClose; + return(XML_ERR_OK); + } + } +#endif /* LIBXML_FTP_ENABLED */ #ifdef LIBXML_HTTP_ENABLED - xmlRegisterInputCallbacks(xmlIOHTTPMatch, xmlIOHTTPOpen, - xmlIOHTTPRead, xmlIOHTTPClose); + if (xmlIOHTTPMatch(filename)) { + buf->context = xmlIOHTTPOpen(filename); + + if (buf->context != NULL) { + buf->readcallback = xmlIOHTTPRead; + buf->closecallback = xmlIOHTTPClose; + return(XML_ERR_OK); + } + } #endif /* LIBXML_HTTP_ENABLED */ -#ifdef LIBXML_FTP_ENABLED - xmlRegisterInputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, - xmlIOFTPRead, xmlIOFTPClose); -#endif /* LIBXML_FTP_ENABLED */ - xmlInputCallbackInitialized = 1; + if (!xmlFileMatch(filename)) + return(XML_IO_ENOENT); + +#ifdef LIBXML_LZMA_ENABLED + { + xzFile xzStream; + + ret = xmlFdOpen(filename, 0, &fd); + if (ret != XML_ERR_OK) + return(ret); + + xzStream = __libxml2_xzdopen(filename, fd, "rb"); + + if (xzStream == NULL) { + close(fd); + } else { + if (__libxml2_xzcompressed(xzStream) > 0) { + buf->context = xzStream; + buf->readcallback = xmlXzfileRead; + buf->closecallback = xmlXzfileClose; + buf->compressed = 1; + + return(XML_ERR_OK); + } + + xmlXzfileClose(xzStream); + } + } +#endif /* LIBXML_LZMA_ENABLED */ + +#ifdef LIBXML_ZLIB_ENABLED + { + gzFile gzStream; + + ret = xmlFdOpen(filename, 0, &fd); + if (ret != XML_ERR_OK) + return(ret); + + gzStream = gzdopen(fd, "rb"); + + if (gzStream == NULL) { + close(fd); + } else { + char buff4[4]; + + if ((gzread(gzStream, buff4, 4) > 0) && + (gzdirect(gzStream) == 0)) { + gzrewind(gzStream); + + buf->context = gzStream; + buf->readcallback = xmlGzfileRead; + buf->closecallback = xmlGzfileClose; + buf->compressed = 1; + + return(XML_ERR_OK); + } + + xmlGzfileClose(gzStream); + } + } +#endif /* LIBXML_ZLIB_ENABLED */ + + ret = xmlFdOpen(filename, 0, &fd); + if (ret != XML_ERR_OK) + return(ret); + + buf->context = (void *) (ptrdiff_t) fd; + buf->readcallback = xmlFdRead; + buf->closecallback = xmlFdClose; + return(XML_ERR_OK); } #ifdef LIBXML_OUTPUT_ENABLED /** - * xmlRegisterDefaultOutputCallbacks: + * xmlOutputDefaultOpen: + * @buf: input buffer to be filled + * @filename: filename or URI + * @compression: compression level or 0 + * @is_file_uri: whether filename is a file URI * - * Registers the default compiled-in I/O handlers. + * Returns an xmlParserErrors code. */ -void -xmlRegisterDefaultOutputCallbacks (void) { - if (xmlOutputCallbackInitialized) - return; +static int +xmlOutputDefaultOpen(xmlOutputBufferPtr buf, const char *filename, + int compression) { + int fd; - xmlRegisterOutputCallbacks(xmlFileMatch, xmlFileOpenW, - xmlFileWrite, xmlFileClose); + (void) compression; -#ifdef LIBXML_HTTP_ENABLED - xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW, - xmlIOHTTPWrite, xmlIOHTTPClosePut); -#endif + if (!strcmp(filename, "-")) { + fd = dup(STDOUT_FILENO); + + if (fd < 0) + return(xmlIOErr(0, "dup()")); + } else { + int ret; -/********************************* - No way a-priori to distinguish between gzipped files from - uncompressed ones except opening if existing then closing - and saving with same compression ratio ... a pain. + ret = xmlFdOpen(filename, /* write */ 1, &fd); + if (ret != XML_ERR_OK) + return(ret); + } #ifdef LIBXML_ZLIB_ENABLED - xmlRegisterOutputCallbacks(xmlGzfileMatch, xmlGzfileOpen, - xmlGzfileWrite, xmlGzfileClose); -#endif + if ((compression > 0) && (compression <= 9)) { + gzFile gzStream; + char mode[15]; - Nor FTP PUT .... -#ifdef LIBXML_FTP_ENABLED - xmlRegisterOutputCallbacks(xmlIOFTPMatch, xmlIOFTPOpen, - xmlIOFTPWrite, xmlIOFTPClose); -#endif - **********************************/ - xmlOutputCallbackInitialized = 1; -} + snprintf(mode, sizeof(mode), "wb%d", compression); + gzStream = gzdopen(fd, mode); -#ifdef LIBXML_HTTP_ENABLED -/** - * xmlRegisterHTTPPostCallbacks: - * - * By default, libxml submits HTTP output requests using the "PUT" method. - * Calling this method changes the HTTP output method to use the "POST" - * method instead. - * - */ -void -xmlRegisterHTTPPostCallbacks( void ) { + if (gzStream == NULL) { + close(fd); + return(xmlIOErr(XML_IO_UNKNOWN, "gzdopen()")); + } - /* Register defaults if not done previously */ + buf->context = gzStream; + buf->writecallback = xmlGzfileWrite; + buf->closecallback = xmlGzfileClose; - if ( xmlOutputCallbackInitialized == 0 ) - xmlRegisterDefaultOutputCallbacks( ); + return(XML_ERR_OK); + } +#endif /* LIBXML_ZLIB_ENABLED */ - xmlRegisterOutputCallbacks(xmlIOHTTPMatch, xmlIOHTTPDfltOpenW, - xmlIOHTTPWrite, xmlIOHTTPClosePost); - return; + buf->context = (void *) (ptrdiff_t) fd; + buf->writecallback = xmlFdWrite; + buf->closecallback = xmlFdClose; + return(XML_ERR_OK); } #endif -#endif /* LIBXML_OUTPUT_ENABLED */ /** * xmlAllocParserInputBuffer: - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) + * + * Create a buffered parser input for progressive parsing. * - * Create a buffered parser input for progressive parsing + * The encoding argument is deprecated and should be set to + * XML_CHAR_ENCODING_NONE. The encoding can be changed with + * xmlSwitchEncoding or xmlSwitchEncodingName later on. * * Returns the new parser input or NULL */ @@ -2257,7 +1351,13 @@ xmlAllocParserInputBuffer(xmlCharEncoding enc) { return(NULL); } xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT); - ret->encoder = xmlGetCharEncodingHandler(enc); + if (enc != XML_CHAR_ENCODING_NONE) { + if (xmlLookupCharEncodingHandler(enc, &ret->encoder) != 0) { + /* We can't handle errors properly here. */ + xmlFreeParserInputBuffer(ret); + return(NULL); + } + } if (ret->encoder != NULL) ret->raw = xmlBufCreateSize(2 * xmlDefaultBufferSize); else @@ -2294,7 +1394,7 @@ xmlAllocOutputBuffer(xmlCharEncodingHandlerPtr encoder) { xmlFree(ret); return(NULL); } - xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_DOUBLEIT); + xmlBufSetAllocationScheme(ret->buffer, XML_BUFFER_ALLOC_IO); ret->encoder = encoder; if (encoder != NULL) { @@ -2409,22 +1509,36 @@ xmlFreeParserInputBuffer(xmlParserInputBufferPtr in) { * flushes and close the output I/O channel * and free up all the associated resources * - * Returns the number of byte written or -1 in case of error. + * Returns the number of byte written or a negative xmlParserErrors + * code in case of error. */ int xmlOutputBufferClose(xmlOutputBufferPtr out) { - int written; - int err_rc = 0; + int ret; if (out == NULL) return (-1); + if (out->writecallback != NULL) xmlOutputBufferFlush(out); + if (out->closecallback != NULL) { - err_rc = out->closecallback(out->context); + int code = out->closecallback(out->context); + + if ((code != XML_ERR_OK) && (out->error == XML_ERR_OK)) { + if (code < 0) + out->error = XML_IO_UNKNOWN; + else + out->error = code; + } } - written = out->written; + + if (out->error != XML_ERR_OK) + ret = -out->error; + else + ret = out->written; + if (out->conv) { xmlBufFree(out->conv); out->conv = NULL; @@ -2437,81 +1551,77 @@ xmlOutputBufferClose(xmlOutputBufferPtr out) out->buffer = NULL; } - if (out->error) - err_rc = -1; xmlFree(out); - return ((err_rc == 0) ? written : err_rc); + + return(ret); } #endif /* LIBXML_OUTPUT_ENABLED */ -xmlParserInputBufferPtr -__xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { - xmlParserInputBufferPtr ret; - int i = 0; - void *context = NULL; +/** + * xmlParserInputBufferCreateFilenameInt: + * @URI: the filename or URI + * @enc: encoding enum (deprecated) + * @out: pointer to resulting input buffer + * + * Returns an xmlParserErrors code. + */ +static int +xmlParserInputBufferCreateFilenameInt(const char *URI, xmlCharEncoding enc, + xmlParserInputBufferPtr *out) { + xmlParserInputBufferPtr buf; + int ret; + int i; - if (xmlInputCallbackInitialized == 0) - xmlRegisterDefaultInputCallbacks(); + *out = NULL; + if (URI == NULL) + return(XML_ERR_ARGUMENT); - if (URI == NULL) return(NULL); + /* + * Allocate the Input buffer front-end. + */ + buf = xmlAllocParserInputBuffer(enc); + if (buf == NULL) + return(XML_ERR_NO_MEMORY); /* * Try to find one of the input accept method accepting that scheme * Go in reverse to give precedence to user defined handlers. */ - if (context == NULL) { - for (i = xmlInputCallbackNr - 1;i >= 0;i--) { - if ((xmlInputCallbackTable[i].matchcallback != NULL) && - (xmlInputCallbackTable[i].matchcallback(URI) != 0)) { - context = xmlInputCallbackTable[i].opencallback(URI); - if (context != NULL) { - break; - } - } - } + ret = XML_IO_ENOENT; + for (i = xmlInputCallbackNr - 1; i >= 0; i--) { + xmlInputCallback *cb = &xmlInputCallbackTable[i]; + + if (cb->matchcallback == xmlIODefaultMatch) { + ret = xmlInputDefaultOpen(buf, URI); + + if ((ret == XML_ERR_OK) || (ret != XML_IO_ENOENT)) + break; + } else if ((cb->matchcallback != NULL) && + (cb->matchcallback(URI) != 0)) { + buf->context = cb->opencallback(URI); + if (buf->context != NULL) { + buf->readcallback = cb->readcallback; + buf->closecallback = cb->closecallback; + ret = XML_ERR_OK; + break; + } + } } - if (context == NULL) { - return(NULL); + if (ret != XML_ERR_OK) { + xmlFreeParserInputBuffer(buf); + *out = NULL; + return(ret); } - /* - * Allocate the Input buffer front-end. - */ - ret = xmlAllocParserInputBuffer(enc); - if (ret != NULL) { - ret->context = context; - ret->readcallback = xmlInputCallbackTable[i].readcallback; - ret->closecallback = xmlInputCallbackTable[i].closecallback; -#ifdef LIBXML_ZLIB_ENABLED - if ((xmlInputCallbackTable[i].opencallback == xmlGzfileOpen) && - (strcmp(URI, "-") != 0)) { -#if defined(ZLIB_VERNUM) && ZLIB_VERNUM >= 0x1230 - ret->compressed = !gzdirect(context); -#else - if (((z_stream *)context)->avail_in > 4) { - char *cptr, buff4[4]; - cptr = (char *) ((z_stream *)context)->next_in; - if (gzread(context, buff4, 4) == 4) { - if (strncmp(buff4, cptr, 4) == 0) - ret->compressed = 0; - else - ret->compressed = 1; - gzrewind(context); - } - } -#endif - } -#endif -#ifdef LIBXML_LZMA_ENABLED - if ((xmlInputCallbackTable[i].opencallback == xmlXzfileOpen) && - (strcmp(URI, "-") != 0)) { - ret->compressed = __libxml2_xzcompressed(context); - } -#endif - } - else - xmlInputCallbackTable[i].closecallback (context); + *out = buf; + return(ret); +} +xmlParserInputBufferPtr +__xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + + xmlParserInputBufferCreateFilenameInt(URI, enc, &ret); return(ret); } @@ -2521,7 +1631,6 @@ __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { * @enc: the charset encoding if known * * Create a buffered parser input for the progressive parsing of a file - * If filename is "-' then we use stdin as the input. * Automatic support for ZLIB/Compress compressed document is provided * by default if found at compile-time. * Do an encoding check if enc == XML_CHAR_ENCODING_NONE @@ -2530,135 +1639,104 @@ __xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { */ xmlParserInputBufferPtr xmlParserInputBufferCreateFilename(const char *URI, xmlCharEncoding enc) { - if ((xmlParserInputBufferCreateFilenameValue)) { - return xmlParserInputBufferCreateFilenameValue(URI, enc); - } - return __xmlParserInputBufferCreateFilename(URI, enc); + if (xmlParserInputBufferCreateFilenameValue != NULL) + return(xmlParserInputBufferCreateFilenameValue(URI, enc)); + + return(__xmlParserInputBufferCreateFilename(URI, enc)); +} + +/** + * xmlParserInputBufferCreateFilenameSafe: + * @URI: the filename or URI + * @enc: encoding enum (deprecated) + * @out: pointer to resulting input buffer + * + * Create an input buffer for a filename or URI. + * + * Returns an xmlParserErrors code. + */ +int +xmlParserInputBufferCreateFilenameSafe(const char *URI, xmlCharEncoding enc, + xmlParserInputBufferPtr *out) { + if (xmlParserInputBufferCreateFilenameValue != NULL) { + *out = xmlParserInputBufferCreateFilenameValue(URI, enc); + + if (*out == NULL) + return(XML_IO_ENOENT); + return(XML_ERR_OK); + } + + return(xmlParserInputBufferCreateFilenameInt(URI, enc, out)); } #ifdef LIBXML_OUTPUT_ENABLED xmlOutputBufferPtr __xmlOutputBufferCreateFilename(const char *URI, xmlCharEncodingHandlerPtr encoder, - int compression ATTRIBUTE_UNUSED) { + int compression) { xmlOutputBufferPtr ret; xmlURIPtr puri; int i = 0; - void *context = NULL; char *unescaped = NULL; -#ifdef LIBXML_ZLIB_ENABLED - int is_file_uri = 1; -#endif - - if (xmlOutputCallbackInitialized == 0) - xmlRegisterDefaultOutputCallbacks(); - if (URI == NULL) return(NULL); + if (URI == NULL) + return(NULL); puri = xmlParseURI(URI); if (puri != NULL) { -#ifdef LIBXML_ZLIB_ENABLED - if ((puri->scheme != NULL) && - (!xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file"))) - is_file_uri = 0; -#endif - /* - * try to limit the damages of the URI unescaping code. - */ - if ((puri->scheme == NULL) || - (xmlStrEqual(BAD_CAST puri->scheme, BAD_CAST "file"))) - unescaped = xmlURIUnescapeString(URI, 0, NULL); - xmlFreeURI(puri); + /* + * try to limit the damages of the URI unescaping code. + */ + if (puri->scheme == NULL) { + unescaped = xmlURIUnescapeString(URI, 0, NULL); + if (unescaped == NULL) { + xmlFreeURI(puri); + return(NULL); + } + URI = unescaped; + } + xmlFreeURI(puri); } /* - * Try to find one of the output accept method accepting that scheme - * Go in reverse to give precedence to user defined handlers. - * try with an unescaped version of the URI + * Allocate the Output buffer front-end. */ - if (unescaped != NULL) { -#ifdef LIBXML_ZLIB_ENABLED - if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) { - context = xmlGzfileOpenW(unescaped, compression); - if (context != NULL) { - ret = xmlAllocOutputBufferInternal(encoder); - if (ret != NULL) { - ret->context = context; - ret->writecallback = xmlGzfileWrite; - ret->closecallback = xmlGzfileClose; - } - xmlFree(unescaped); - return(ret); - } - } -#endif - for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { - if ((xmlOutputCallbackTable[i].matchcallback != NULL) && - (xmlOutputCallbackTable[i].matchcallback(unescaped) != 0)) { -#if defined(LIBXML_HTTP_ENABLED) && defined(LIBXML_ZLIB_ENABLED) - /* Need to pass compression parameter into HTTP open calls */ - if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch) - context = xmlIOHTTPOpenW(unescaped, compression); - else -#endif - context = xmlOutputCallbackTable[i].opencallback(unescaped); - if (context != NULL) - break; - } - } - xmlFree(unescaped); + ret = xmlAllocOutputBufferInternal(encoder); + if (ret == NULL) { + xmlFree(unescaped); + return(NULL); } /* - * If this failed try with a non-escaped URI this may be a strange - * filename + * Try to find one of the output accept method accepting that scheme + * Go in reverse to give precedence to user defined handlers. */ - if (context == NULL) { -#ifdef LIBXML_ZLIB_ENABLED - if ((compression > 0) && (compression <= 9) && (is_file_uri == 1)) { - context = xmlGzfileOpenW(URI, compression); - if (context != NULL) { - ret = xmlAllocOutputBufferInternal(encoder); - if (ret != NULL) { - ret->context = context; - ret->writecallback = xmlGzfileWrite; - ret->closecallback = xmlGzfileClose; - } - else - xmlGzfileClose(context); - return(ret); - } - } -#endif - for (i = xmlOutputCallbackNr - 1;i >= 0;i--) { - if ((xmlOutputCallbackTable[i].matchcallback != NULL) && - (xmlOutputCallbackTable[i].matchcallback(URI) != 0)) { -#if defined(LIBXML_HTTP_ENABLED) && defined(LIBXML_ZLIB_ENABLED) - /* Need to pass compression parameter into HTTP open calls */ - if (xmlOutputCallbackTable[i].matchcallback == xmlIOHTTPMatch) - context = xmlIOHTTPOpenW(URI, compression); - else -#endif - context = xmlOutputCallbackTable[i].opencallback(URI); - if (context != NULL) - break; - } - } - } + for (i = xmlOutputCallbackNr - 1; i >= 0; i--) { + xmlOutputCallback *cb = &xmlOutputCallbackTable[i]; + int code; - if (context == NULL) { - return(NULL); + if (cb->matchcallback == xmlIODefaultMatch) { + code = xmlOutputDefaultOpen(ret, URI, compression); + /* TODO: Handle other errors */ + if (code == XML_ERR_OK) + break; + } else if ((cb->matchcallback != NULL) && + (cb->matchcallback(URI) != 0)) { + ret->context = cb->opencallback(URI); + if (ret->context != NULL) { + ret->writecallback = cb->writecallback; + ret->closecallback = cb->closecallback; + break; + } + } } - /* - * Allocate the Output buffer front-end. - */ - ret = xmlAllocOutputBufferInternal(encoder); - if (ret != NULL) { - ret->context = context; - ret->writecallback = xmlOutputCallbackTable[i].writecallback; - ret->closecallback = xmlOutputCallbackTable[i].closecallback; + if (ret->context == NULL) { + xmlOutputBufferClose(ret); + ret = NULL; } + + xmlFree(unescaped); return(ret); } @@ -2691,27 +1769,28 @@ xmlOutputBufferCreateFilename(const char *URI, /** * xmlParserInputBufferCreateFile: * @file: a FILE* - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for the progressive parsing of a FILE * * buffered C I/O * + * The encoding argument is deprecated and should be set to + * XML_CHAR_ENCODING_NONE. The encoding can be changed with + * xmlSwitchEncoding or xmlSwitchEncodingName later on. + * * Returns the new parser input or NULL */ xmlParserInputBufferPtr xmlParserInputBufferCreateFile(FILE *file, xmlCharEncoding enc) { xmlParserInputBufferPtr ret; - if (xmlInputCallbackInitialized == 0) - xmlRegisterDefaultInputCallbacks(); - if (file == NULL) return(NULL); ret = xmlAllocParserInputBuffer(enc); if (ret != NULL) { ret->context = file; ret->readcallback = xmlFileRead; - ret->closecallback = xmlFileFlush; + ret->closecallback = NULL; } return(ret); @@ -2732,9 +1811,6 @@ xmlOutputBufferPtr xmlOutputBufferCreateFile(FILE *file, xmlCharEncodingHandlerPtr encoder) { xmlOutputBufferPtr ret; - if (xmlOutputCallbackInitialized == 0) - xmlRegisterDefaultOutputCallbacks(); - if (file == NULL) return(NULL); ret = xmlAllocOutputBufferInternal(encoder); @@ -2779,7 +1855,7 @@ xmlOutputBufferCreateBuffer(xmlBufferPtr buffer, */ const xmlChar * xmlOutputBufferGetContent(xmlOutputBufferPtr out) { - if ((out == NULL) || (out->buffer == NULL)) + if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) return(NULL); return(xmlBufContent(out->buffer)); @@ -2795,7 +1871,7 @@ xmlOutputBufferGetContent(xmlOutputBufferPtr out) { */ size_t xmlOutputBufferGetSize(xmlOutputBufferPtr out) { - if ((out == NULL) || (out->buffer == NULL)) + if ((out == NULL) || (out->buffer == NULL) || (out->error != 0)) return(0); return(xmlBufUse(out->buffer)); @@ -2807,11 +1883,15 @@ xmlOutputBufferGetSize(xmlOutputBufferPtr out) { /** * xmlParserInputBufferCreateFd: * @fd: a file descriptor number - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for the progressive parsing for the input * from a file descriptor * + * The encoding argument is deprecated and should be set to + * XML_CHAR_ENCODING_NONE. The encoding can be changed with + * xmlSwitchEncoding or xmlSwitchEncodingName later on. + * * Returns the new parser input or NULL */ xmlParserInputBufferPtr @@ -2824,7 +1904,6 @@ xmlParserInputBufferCreateFd(int fd, xmlCharEncoding enc) { if (ret != NULL) { ret->context = (void *) (ptrdiff_t) fd; ret->readcallback = xmlFdRead; - ret->closecallback = xmlFdClose; } return(ret); @@ -2854,17 +1933,71 @@ static int xmlMemClose(void *vctxt) { xmlMemIOCtxt *ctxt = vctxt; - if (ctxt->mem != 0) + if (ctxt->mem != NULL) xmlFree(ctxt->mem); xmlFree(ctxt); return(0); } +/** + * xmlNewInputBufferMemory: + * @mem: memory buffer + * @size: size of buffer + * @flags: flags + * @enc: the charset encoding if known (deprecated) + * + * Create an input buffer for memory. + * + * Returns the new input buffer or NULL. + */ +xmlParserInputBufferPtr +xmlNewInputBufferMemory(const void *mem, size_t size, int flags, + xmlCharEncoding enc) { + xmlParserInputBufferPtr ret; + xmlMemIOCtxt *ctxt; + char *copy = NULL; + + if ((flags & XML_INPUT_BUF_STATIC) == 0) { + if (size + 1 == 0) + return(NULL); + copy = xmlMalloc(size + 1); + if (copy == NULL) + return(NULL); + memcpy(copy, mem, size); + copy[size] = 0; + + mem = copy; + } + + ret = xmlAllocParserInputBuffer(enc); + if (ret == NULL) { + xmlFree(copy); + return(NULL); + } + + ctxt = xmlMalloc(sizeof(*ctxt)); + if (ctxt == NULL) { + xmlFreeParserInputBuffer(ret); + xmlFree(copy); + return(NULL); + } + + ctxt->mem = copy; + ctxt->cur = mem; + ctxt->size = size; + + ret->context = ctxt; + ret->readcallback = xmlMemRead; + ret->closecallback = xmlMemClose; + + return(ret); +} + /** * xmlParserInputBufferCreateMem: * @mem: the memory input * @size: the length of the memory block - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) * * Create a parser input buffer for parsing from a memory area. * @@ -2880,28 +2013,11 @@ xmlMemClose(void *vctxt) { * Returns the new parser input or NULL in case of error. */ xmlParserInputBufferPtr -xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { - xmlParserInputBufferPtr buf; - xmlMemIOCtxt *ctxt; - char *copy; - - if ((size < 0) || (mem == NULL)) - return(NULL); - - copy = (char *) xmlStrndup((const xmlChar *) mem, size); - if (copy == NULL) - return(NULL); - - buf = xmlParserInputBufferCreateStatic(copy, size, enc); - if (buf == NULL) { - xmlFree(copy); +xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { + if ((mem == NULL) || (size < 0)) return(NULL); - } - - ctxt = buf->context; - ctxt->mem = copy; - return(buf); + return(xmlNewInputBufferMemory(mem, size, 0, enc)); } /** @@ -2925,40 +2041,20 @@ xmlParserInputBufferCreateMem(const char *mem, int size, xmlCharEncoding enc) { xmlParserInputBufferPtr xmlParserInputBufferCreateStatic(const char *mem, int size, xmlCharEncoding enc) { - xmlParserInputBufferPtr ret; - xmlMemIOCtxt *ctxt; - - if ((size < 0) || (mem == NULL)) - return(NULL); - - ret = xmlAllocParserInputBuffer(enc); - if (ret == NULL) - return(NULL); - - ctxt = xmlMalloc(sizeof(*ctxt)); - if (ctxt == NULL) { - xmlFreeParserInputBuffer(ret); + if ((mem == NULL) || (size < 0)) return(NULL); - } - ctxt->mem = NULL; - ctxt->cur = mem; - ctxt->size = size; - - ret->context = ctxt; - ret->readcallback = xmlMemRead; - ret->closecallback = xmlMemClose; - return(ret); + return(xmlNewInputBufferMemory(mem, size, XML_INPUT_BUF_STATIC, enc)); } typedef struct { - const xmlChar *str; + const char *str; } xmlStringIOCtxt; static int xmlStringRead(void *vctxt, char *buf, int size) { xmlStringIOCtxt *ctxt = vctxt; - const xmlChar *zero; + const char *zero; size_t len; zero = memchr(ctxt->str, 0, size); @@ -2977,20 +2073,22 @@ xmlStringClose(void *vctxt) { } /** - * xmlParserInputBufferCreateString: - * @str: a null-terminated string + * xmlNewInputBufferString: + * @str: C string + * @flags: flags * - * Create a buffered parser input for the progressive parsing for the input - * from a null-terminated C string. + * Create an input buffer for a null-teriminated C string. * - * Returns the new parser input or NULL + * Returns the new input buffer or NULL. */ xmlParserInputBufferPtr -xmlParserInputBufferCreateString(const xmlChar *str) { +xmlNewInputBufferString(const char *str, int flags) { xmlParserInputBufferPtr ret; xmlStringIOCtxt *ctxt; - if (str == NULL) return(NULL); + if ((flags & XML_INPUT_BUF_STATIC) == 0) + return(xmlNewInputBufferMemory(str, strlen(str), flags, + XML_CHAR_ENCODING_NONE)); ret = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); if (ret == NULL) @@ -3001,6 +2099,7 @@ xmlParserInputBufferCreateString(const xmlChar *str) { xmlFreeParserInputBuffer(ret); return(NULL); } + ctxt->str = str; ret->context = ctxt; @@ -3043,11 +2142,15 @@ xmlOutputBufferCreateFd(int fd, xmlCharEncodingHandlerPtr encoder) { * @ioread: an I/O read function * @ioclose: an I/O close function * @ioctx: an I/O handler - * @enc: the charset encoding if known + * @enc: the charset encoding if known (deprecated) * * Create a buffered parser input for the progressive parsing for the input * from an I/O handler * + * The encoding argument is deprecated and should be set to + * XML_CHAR_ENCODING_NONE. The encoding can be changed with + * xmlSwitchEncoding or xmlSwitchEncodingName later on. + * * Returns the new parser input or NULL */ xmlParserInputBufferPtr @@ -3108,13 +2211,17 @@ xmlOutputBufferCreateIO(xmlOutputWriteCallback iowrite, * Returns the old value of the registration function */ xmlParserInputBufferCreateFilenameFunc -xmlParserInputBufferCreateFilenameDefault(xmlParserInputBufferCreateFilenameFunc func) +xmlParserInputBufferCreateFilenameDefault( + xmlParserInputBufferCreateFilenameFunc func) { - xmlParserInputBufferCreateFilenameFunc old = xmlParserInputBufferCreateFilenameValue; - if (old == NULL) { - old = __xmlParserInputBufferCreateFilename; - } + xmlParserInputBufferCreateFilenameFunc old; + old = xmlParserInputBufferCreateFilenameValue; + if (old == NULL) + old = __xmlParserInputBufferCreateFilename; + + if (func == __xmlParserInputBufferCreateFilename) + func = NULL; xmlParserInputBufferCreateFilenameValue = func; return(old); } @@ -3256,7 +2363,10 @@ xmlParserInputBufferGrow(xmlParserInputBufferPtr in, int len) { if (res <= 0) in->readcallback = endOfInput; if (res < 0) { - in->error = XML_IO_UNKNOWN; + if (res == -1) + in->error = XML_IO_UNKNOWN; + else + in->error = -res; return(-1); } @@ -3341,10 +2451,16 @@ xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { */ if (out->conv == NULL) { out->conv = xmlBufCreate(); + if (out->conv == NULL) { + out->error = XML_ERR_NO_MEMORY; + return(-1); + } } ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk); - if (ret != 0) + if (ret != 0) { + out->error = XML_ERR_NO_MEMORY; return(-1); + } if ((xmlBufUse(out->buffer) < MINLEN) && (chunk == len)) goto done; @@ -3353,19 +2469,18 @@ xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { * convert as much as possible to the parser reading buffer. */ ret = xmlCharEncOutput(out, 0); - if ((ret < 0) && (ret != -3)) { - xmlIOErr(XML_IO_ENCODER, NULL); - out->error = XML_IO_ENCODER; + if (ret < 0) return(-1); - } if (out->writecallback) nbchars = xmlBufUse(out->conv); else nbchars = ret >= 0 ? ret : 0; } else { ret = xmlBufAdd(out->buffer, (const xmlChar *) buf, chunk); - if (ret != 0) + if (ret != 0) { + out->error = XML_ERR_NO_MEMORY; return(-1); + } if (out->writecallback) nbchars = xmlBufUse(out->buffer); else @@ -3393,8 +2508,10 @@ xmlOutputBufferWrite(xmlOutputBufferPtr out, int len, const char *buf) { xmlBufShrink(out->buffer, ret); } if (ret < 0) { - xmlIOErr(XML_IO_WRITE, NULL); - out->error = XML_IO_WRITE; + int errNo = (ret == -1) ? XML_IO_WRITE : -ret; + + xmlIOErr(errNo, NULL); + out->error = errNo; return(ret); } if (out->written > INT_MAX - ret) @@ -3517,8 +2634,10 @@ xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str, * not the case force a flush, but make sure we stay in the loop */ if (chunk < 40) { - if (xmlBufGrow(out->buffer, 100) < 0) + if (xmlBufGrow(out->buffer, 100) < 0) { + out->error = XML_ERR_NO_MEMORY; return(-1); + } oldwritten = -1; continue; } @@ -3532,11 +2651,17 @@ xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str, */ if (out->conv == NULL) { out->conv = xmlBufCreate(); + if (out->conv == NULL) { + out->error = XML_ERR_NO_MEMORY; + return(-1); + } } ret = escaping(xmlBufEnd(out->buffer) , &chunk, str, &cons); - if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */ - return(-1); + if (ret < 0) { + out->error = XML_ERR_NO_MEMORY; + return(-1); + } xmlBufAddLen(out->buffer, chunk); if ((xmlBufUse(out->buffer) < MINLEN) && (cons == len)) @@ -3546,19 +2671,18 @@ xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str, * convert as much as possible to the output buffer. */ ret = xmlCharEncOutput(out, 0); - if ((ret < 0) && (ret != -3)) { - xmlIOErr(XML_IO_ENCODER, NULL); - out->error = XML_IO_ENCODER; + if (ret < 0) return(-1); - } if (out->writecallback) nbchars = xmlBufUse(out->conv); else nbchars = ret >= 0 ? ret : 0; } else { ret = escaping(xmlBufEnd(out->buffer), &chunk, str, &cons); - if ((ret < 0) || (chunk == 0)) /* chunk==0 => nothing done */ - return(-1); + if (ret < 0) { + out->error = XML_ERR_NO_MEMORY; + return(-1); + } xmlBufAddLen(out->buffer, chunk); if (out->writecallback) nbchars = xmlBufUse(out->buffer); @@ -3587,16 +2711,20 @@ xmlOutputBufferWriteEscape(xmlOutputBufferPtr out, const xmlChar *str, xmlBufShrink(out->buffer, ret); } if (ret < 0) { - xmlIOErr(XML_IO_WRITE, NULL); - out->error = XML_IO_WRITE; - return(ret); + int errNo = (ret == -1) ? XML_IO_WRITE : -ret; + xmlIOErr(errNo, NULL); + out->error = errNo; + return(-1); } if (out->written > INT_MAX - ret) out->written = INT_MAX; else out->written += ret; } else if (xmlBufAvail(out->buffer) < MINLEN) { - xmlBufGrow(out->buffer, MINLEN); + if (xmlBufGrow(out->buffer, MINLEN) < 0) { + out->error = XML_ERR_NO_MEMORY; + return(-1); + } } written += nbchars; } while ((len > 0) && (oldwritten != written)); @@ -3632,6 +2760,56 @@ xmlOutputBufferWriteString(xmlOutputBufferPtr out, const char *str) { return(len); } +/** + * xmlOutputBufferWriteQuotedString: + * @buf: output buffer + * @string: the string to add + * + * routine which manage and grows an output buffer. This one writes + * a quoted or double quoted #xmlChar string, checking first if it holds + * quote or double-quotes internally + */ +void +xmlOutputBufferWriteQuotedString(xmlOutputBufferPtr buf, + const xmlChar *string) { + const xmlChar *cur, *base; + + if ((buf == NULL) || (buf->error)) + return; + + if (xmlStrchr(string, '\"')) { + if (xmlStrchr(string, '\'')) { + xmlOutputBufferWrite(buf, 1, "\""); + base = cur = string; + while(*cur != 0){ + if(*cur == '"'){ + if (base != cur) + xmlOutputBufferWrite(buf, cur - base, + (const char *) base); + xmlOutputBufferWrite(buf, 6, """); + cur++; + base = cur; + } + else { + cur++; + } + } + if (base != cur) + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 1, "\""); + } + else{ + xmlOutputBufferWrite(buf, 1, "'"); + xmlOutputBufferWriteString(buf, (const char *) string); + xmlOutputBufferWrite(buf, 1, "'"); + } + } else { + xmlOutputBufferWrite(buf, 1, "\""); + xmlOutputBufferWriteString(buf, (const char *) string); + xmlOutputBufferWrite(buf, 1, "\""); + } +} + /** * xmlOutputBufferFlush: * @out: a buffered output @@ -3654,11 +2832,8 @@ xmlOutputBufferFlush(xmlOutputBufferPtr out) { */ do { nbchars = xmlCharEncOutput(out, 0); - if (nbchars < 0) { - xmlIOErr(XML_IO_ENCODER, NULL); - out->error = XML_IO_ENCODER; + if (nbchars < 0) return(-1); - } } while (nbchars); } @@ -3680,8 +2855,10 @@ xmlOutputBufferFlush(xmlOutputBufferPtr out) { xmlBufShrink(out->buffer, ret); } if (ret < 0) { - xmlIOErr(XML_IO_FLUSH, NULL); - out->error = XML_IO_FLUSH; + int errNo = (ret == -1) ? XML_IO_WRITE : -ret; + + xmlIOErr(errNo, NULL); + out->error = errNo; return(ret); } if (out->written > INT_MAX - ret) @@ -3707,9 +2884,6 @@ xmlParserGetDirectory(const char *filename) { char dir[1024]; char *cur; - if (xmlInputCallbackInitialized == 0) - xmlRegisterDefaultInputCallbacks(); - if (filename == NULL) return(NULL); #if defined(_WIN32) @@ -3730,341 +2904,208 @@ xmlParserGetDirectory(const char *filename) { else *cur = 0; ret = xmlMemStrdup(dir); } else { - if (getcwd(dir, 1024) != NULL) { - dir[1023] = 0; - ret = xmlMemStrdup(dir); - } + ret = xmlMemStrdup("."); } return(ret); #undef IS_XMLPGD_SEP } -/**************************************************************** - * * - * External entities loading * - * * - ****************************************************************/ - /** - * xmlCheckHTTPInput: - * @ctxt: an XML parser context - * @ret: an XML parser input + * xmlNoNetExists: + * @filename: the path to check + * + * DEPRECATED: Internal function, don't use. * - * Check an input in case it was created from an HTTP stream, in that - * case it will handle encoding and update of the base URL in case of - * redirection. It also checks for HTTP errors in which case the input - * is cleanly freed up and an appropriate error is raised in context + * Like xmlCheckFilename but handles file URIs. * - * Returns the input or NULL in case of HTTP error. + * Returns 0, 1, or 2. */ -xmlParserInputPtr -xmlCheckHTTPInput(xmlParserCtxtPtr ctxt, xmlParserInputPtr ret) { - /* Avoid unused variable warning if features are disabled. */ - (void) ctxt; +int +xmlNoNetExists(const char *filename) { + char *fromUri; + int ret; -#ifdef LIBXML_HTTP_ENABLED - if ((ret != NULL) && (ret->buf != NULL) && - (ret->buf->readcallback == xmlIOHTTPRead) && - (ret->buf->context != NULL)) { - const char *encoding; - const char *redir; - const char *mime; - int code; + if (filename == NULL) + return(0); - code = xmlNanoHTTPReturnCode(ret->buf->context); - if (code >= 400) { - /* fatal error */ - if (ret->filename != NULL) - __xmlLoaderErr(ctxt, "failed to load HTTP resource \"%s\"\n", - (const char *) ret->filename); - else - __xmlLoaderErr(ctxt, "failed to load HTTP resource\n", NULL); - xmlFreeInputStream(ret); - ret = NULL; - } else { + if (xmlConvertUriToPath(filename, &fromUri) < 0) + return(0); - mime = xmlNanoHTTPMimeType(ret->buf->context); - if ((xmlStrstr(BAD_CAST mime, BAD_CAST "/xml")) || - (xmlStrstr(BAD_CAST mime, BAD_CAST "+xml"))) { - encoding = xmlNanoHTTPEncoding(ret->buf->context); - if (encoding != NULL) { - xmlCharEncodingHandlerPtr handler; - - handler = xmlFindCharEncodingHandler(encoding); - if (handler != NULL) { - xmlSwitchInputEncoding(ctxt, ret, handler); - } else { - __xmlErrEncoding(ctxt, XML_ERR_UNKNOWN_ENCODING, - "Unknown encoding %s", - BAD_CAST encoding, NULL); - } - } -#if 0 - } else if (xmlStrstr(BAD_CAST mime, BAD_CAST "html")) { -#endif - } - redir = xmlNanoHTTPRedir(ret->buf->context); - if (redir != NULL) { - if (ret->filename != NULL) - xmlFree((xmlChar *) ret->filename); - if (ret->directory != NULL) { - xmlFree((xmlChar *) ret->directory); - ret->directory = NULL; - } - ret->filename = - (char *) xmlStrdup((const xmlChar *) redir); - } - } - } -#endif + if (fromUri != NULL) + filename = fromUri; + + ret = xmlCheckFilename(filename); + + xmlFree(fromUri); return(ret); } -static int xmlNoNetExists(const char *URL) { - const char *path; +/************************************************************************ + * * + * Input/output callbacks * + * * + ************************************************************************/ - if (URL == NULL) - return(0); +/** + * xmlInitIOCallbacks: + * + * Initialize callback tables. + */ +void +xmlInitIOCallbacks(void) +{ + xmlInputCallbackNr = 1; + xmlInputCallbackTable[0].matchcallback = xmlIODefaultMatch; - if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file://localhost/", 17)) -#if defined (_WIN32) - path = &URL[17]; -#else - path = &URL[16]; -#endif - else if (!xmlStrncasecmp(BAD_CAST URL, BAD_CAST "file:///", 8)) { -#if defined (_WIN32) - path = &URL[8]; -#else - path = &URL[7]; +#ifdef LIBXML_OUTPUT_ENABLED + xmlOutputCallbackNr = 1; + xmlOutputCallbackTable[0].matchcallback = xmlIODefaultMatch; #endif - } else - path = URL; - - return xmlCheckFilename(path); } -#ifdef LIBXML_CATALOG_ENABLED - /** - * xmlResolveResourceFromCatalog: - * @URL: the URL for the entity to load - * @ID: the System ID for the entity to load - * @ctxt: the context in which the entity is called or NULL + * xmlRegisterInputCallbacks: + * @matchFunc: the xmlInputMatchCallback + * @openFunc: the xmlInputOpenCallback + * @readFunc: the xmlInputReadCallback + * @closeFunc: the xmlInputCloseCallback * - * Resolves the URL and ID against the appropriate catalog. - * This function is used by xmlDefaultExternalEntityLoader and - * xmlNoNetExternalEntityLoader. + * Register a new set of I/O callback for handling parser input. * - * Returns a new allocated URL, or NULL. + * Returns the registered handler number or -1 in case of error */ -static xmlChar * -xmlResolveResourceFromCatalog(const char *URL, const char *ID, - xmlParserCtxtPtr ctxt) { - xmlChar *resource = NULL; - xmlCatalogAllow pref; - - /* - * If the resource doesn't exists as a file, - * try to load it from the resource pointed in the catalogs - */ - pref = xmlCatalogGetDefaults(); - - if ((pref != XML_CATA_ALLOW_NONE) && (!xmlNoNetExists(URL))) { - /* - * Do a local lookup - */ - if ((ctxt != NULL) && (ctxt->catalogs != NULL) && - ((pref == XML_CATA_ALLOW_ALL) || - (pref == XML_CATA_ALLOW_DOCUMENT))) { - resource = xmlCatalogLocalResolve(ctxt->catalogs, - (const xmlChar *)ID, - (const xmlChar *)URL); - } - /* - * Try a global lookup - */ - if ((resource == NULL) && - ((pref == XML_CATA_ALLOW_ALL) || - (pref == XML_CATA_ALLOW_GLOBAL))) { - resource = xmlCatalogResolve((const xmlChar *)ID, - (const xmlChar *)URL); - } - if ((resource == NULL) && (URL != NULL)) - resource = xmlStrdup((const xmlChar *) URL); - - /* - * TODO: do an URI lookup on the reference - */ - if ((resource != NULL) && (!xmlNoNetExists((const char *)resource))) { - xmlChar *tmp = NULL; - - if ((ctxt != NULL) && (ctxt->catalogs != NULL) && - ((pref == XML_CATA_ALLOW_ALL) || - (pref == XML_CATA_ALLOW_DOCUMENT))) { - tmp = xmlCatalogLocalResolveURI(ctxt->catalogs, resource); - } - if ((tmp == NULL) && - ((pref == XML_CATA_ALLOW_ALL) || - (pref == XML_CATA_ALLOW_GLOBAL))) { - tmp = xmlCatalogResolveURI(resource); - } - - if (tmp != NULL) { - xmlFree(resource); - resource = tmp; - } - } +int +xmlRegisterInputCallbacks(xmlInputMatchCallback matchFunc, + xmlInputOpenCallback openFunc, xmlInputReadCallback readFunc, + xmlInputCloseCallback closeFunc) { + if (xmlInputCallbackNr >= MAX_INPUT_CALLBACK) { + return(-1); } - - return resource; + xmlInputCallbackTable[xmlInputCallbackNr].matchcallback = matchFunc; + xmlInputCallbackTable[xmlInputCallbackNr].opencallback = openFunc; + xmlInputCallbackTable[xmlInputCallbackNr].readcallback = readFunc; + xmlInputCallbackTable[xmlInputCallbackNr].closecallback = closeFunc; + return(xmlInputCallbackNr++); } -#endif +/** + * xmlRegisterDefaultInputCallbacks: + * + * Registers the default compiled-in I/O handlers. + */ +void +xmlRegisterDefaultInputCallbacks(void) { + xmlRegisterInputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); +} /** - * xmlDefaultExternalEntityLoader: - * @URL: the URL for the entity to load - * @ID: the System ID for the entity to load - * @ctxt: the context in which the entity is called or NULL + * xmlPopInputCallbacks: * - * By default we don't load external entities, yet. + * Clear the top input callback from the input stack. this includes the + * compiled-in I/O. * - * Returns a new allocated xmlParserInputPtr, or NULL. + * Returns the number of input callback registered or -1 in case of error. */ -static xmlParserInputPtr -xmlDefaultExternalEntityLoader(const char *URL, const char *ID, - xmlParserCtxtPtr ctxt) +int +xmlPopInputCallbacks(void) { - xmlParserInputPtr ret = NULL; - xmlChar *resource = NULL; - - if ((ctxt != NULL) && (ctxt->options & XML_PARSE_NONET)) { - int options = ctxt->options; - - ctxt->options -= XML_PARSE_NONET; - ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); - ctxt->options = options; - return(ret); - } -#ifdef LIBXML_CATALOG_ENABLED - resource = xmlResolveResourceFromCatalog(URL, ID, ctxt); -#endif + if (xmlInputCallbackNr <= 0) + return(-1); - if (resource == NULL) - resource = (xmlChar *) URL; + xmlInputCallbackNr--; - if (resource == NULL) { - if (ID == NULL) - ID = "NULL"; - __xmlLoaderErr(ctxt, "failed to load external entity \"%s\"\n", ID); - return (NULL); - } - ret = xmlNewInputFromFile(ctxt, (const char *) resource); - if ((resource != NULL) && (resource != (xmlChar *) URL)) - xmlFree(resource); - return (ret); + return(xmlInputCallbackNr); } -static xmlExternalEntityLoader xmlCurrentExternalEntityLoader = - xmlDefaultExternalEntityLoader; - /** - * xmlSetExternalEntityLoader: - * @f: the new entity resolver function + * xmlCleanupInputCallbacks: * - * Changes the defaultexternal entity resolver function for the application + * clears the entire input callback table. this includes the + * compiled-in I/O. */ void -xmlSetExternalEntityLoader(xmlExternalEntityLoader f) { - xmlCurrentExternalEntityLoader = f; +xmlCleanupInputCallbacks(void) +{ + xmlInputCallbackNr = 0; } +#ifdef LIBXML_OUTPUT_ENABLED /** - * xmlGetExternalEntityLoader: + * xmlRegisterOutputCallbacks: + * @matchFunc: the xmlOutputMatchCallback + * @openFunc: the xmlOutputOpenCallback + * @writeFunc: the xmlOutputWriteCallback + * @closeFunc: the xmlOutputCloseCallback + * + * Register a new set of I/O callback for handling output. * - * Get the default external entity resolver function for the application + * Returns the registered handler number or -1 in case of error + */ +int +xmlRegisterOutputCallbacks(xmlOutputMatchCallback matchFunc, + xmlOutputOpenCallback openFunc, xmlOutputWriteCallback writeFunc, + xmlOutputCloseCallback closeFunc) { + if (xmlOutputCallbackNr >= MAX_OUTPUT_CALLBACK) { + return(-1); + } + xmlOutputCallbackTable[xmlOutputCallbackNr].matchcallback = matchFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].opencallback = openFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].writecallback = writeFunc; + xmlOutputCallbackTable[xmlOutputCallbackNr].closecallback = closeFunc; + return(xmlOutputCallbackNr++); +} + +/** + * xmlRegisterDefaultOutputCallbacks: * - * Returns the xmlExternalEntityLoader function pointer + * Registers the default compiled-in I/O handlers. */ -xmlExternalEntityLoader -xmlGetExternalEntityLoader(void) { - return(xmlCurrentExternalEntityLoader); +void +xmlRegisterDefaultOutputCallbacks (void) { + xmlRegisterOutputCallbacks(xmlIODefaultMatch, NULL, NULL, NULL); } /** - * xmlLoadExternalEntity: - * @URL: the URL for the entity to load - * @ID: the Public ID for the entity to load - * @ctxt: the context in which the entity is called or NULL + * xmlPopOutputCallbacks: * - * Load an external entity, note that the use of this function for - * unparsed entities may generate problems + * Remove the top output callbacks from the output stack. This includes the + * compiled-in I/O. * - * Returns the xmlParserInputPtr or NULL + * Returns the number of output callback registered or -1 in case of error. */ -xmlParserInputPtr -xmlLoadExternalEntity(const char *URL, const char *ID, - xmlParserCtxtPtr ctxt) { - if ((URL != NULL) && (xmlNoNetExists(URL) == 0)) { - char *canonicFilename; - xmlParserInputPtr ret; +int +xmlPopOutputCallbacks(void) +{ + if (xmlOutputCallbackNr <= 0) + return(-1); - canonicFilename = (char *) xmlCanonicPath((const xmlChar *) URL); - if (canonicFilename == NULL) { - xmlErrMemory(ctxt, "building canonical path\n"); - return(NULL); - } + xmlOutputCallbackNr--; - ret = xmlCurrentExternalEntityLoader(canonicFilename, ID, ctxt); - xmlFree(canonicFilename); - return(ret); - } - return(xmlCurrentExternalEntityLoader(URL, ID, ctxt)); + return(xmlOutputCallbackNr); } -/************************************************************************ - * * - * Disabling Network access * - * * - ************************************************************************/ - /** - * xmlNoNetExternalEntityLoader: - * @URL: the URL for the entity to load - * @ID: the System ID for the entity to load - * @ctxt: the context in which the entity is called or NULL - * - * A specific entity loader disabling network accesses, though still - * allowing local catalog accesses for resolution. + * xmlCleanupOutputCallbacks: * - * Returns a new allocated xmlParserInputPtr, or NULL. + * clears the entire output callback table. this includes the + * compiled-in I/O callbacks. */ -xmlParserInputPtr -xmlNoNetExternalEntityLoader(const char *URL, const char *ID, - xmlParserCtxtPtr ctxt) { - xmlParserInputPtr input = NULL; - xmlChar *resource = NULL; - -#ifdef LIBXML_CATALOG_ENABLED - resource = xmlResolveResourceFromCatalog(URL, ID, ctxt); -#endif - - if (resource == NULL) - resource = (xmlChar *) URL; +void +xmlCleanupOutputCallbacks(void) +{ + xmlOutputCallbackNr = 0; +} - if (resource != NULL) { - if ((!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "ftp://", 6)) || - (!xmlStrncasecmp(BAD_CAST resource, BAD_CAST "http://", 7))) { - xmlIOErr(XML_IO_NETWORK_ATTEMPT, (const char *) resource); - if (resource != (xmlChar *) URL) - xmlFree(resource); - return(NULL); - } - } - input = xmlDefaultExternalEntityLoader((const char *) resource, ID, ctxt); - if (resource != (xmlChar *) URL) - xmlFree(resource); - return(input); +#ifdef LIBXML_HTTP_ENABLED +/** + * xmlRegisterHTTPPostCallbacks: + * + * DEPRECATED: Support for HTTP POST has been removed. + */ +void +xmlRegisterHTTPPostCallbacks(void) { + xmlRegisterDefaultOutputCallbacks(); } +#endif +#endif /* LIBXML_OUTPUT_ENABLED */ diff --git a/libraries/libxml2/xmlmemory.c b/libraries/libxml2/xmlmemory.c index 1e999b11..f0628e2a 100644 --- a/libraries/libxml2/xmlmemory.c +++ b/libraries/libxml2/xmlmemory.c @@ -12,18 +12,6 @@ #include #include -/** - * MEM_LIST: - * - * keep track of all allocated blocks for error reporting - * Always build the memory list ! - */ -#ifdef DEBUG_MEMORY_LOCATION -#ifndef MEM_LIST -#define MEM_LIST /* keep a list of all the allocated memory blocks */ -#endif -#endif - #include #include #include @@ -34,62 +22,31 @@ static unsigned long debugMemSize = 0; static unsigned long debugMemBlocks = 0; -static unsigned long debugMaxMemSize = 0; static xmlMutex xmlMemMutex; -void xmlMallocBreakpoint(void); - /************************************************************************ * * * Macros, variables and associated types * * * ************************************************************************/ -#if !defined(LIBXML_THREAD_ENABLED) && !defined(LIBXML_THREAD_ALLOC_ENABLED) -#ifdef xmlMalloc -#undef xmlMalloc -#endif -#ifdef xmlRealloc -#undef xmlRealloc -#endif -#ifdef xmlMemStrdup -#undef xmlMemStrdup -#endif -#endif - /* * Each of the blocks allocated begin with a header containing information */ #define MEMTAG 0x5aa5U -#define MALLOC_TYPE 1 -#define REALLOC_TYPE 2 -#define STRDUP_TYPE 3 -#define MALLOC_ATOMIC_TYPE 4 -#define REALLOC_ATOMIC_TYPE 5 - typedef struct memnod { unsigned int mh_tag; - unsigned int mh_type; - unsigned long mh_number; size_t mh_size; -#ifdef MEM_LIST - struct memnod *mh_next; - struct memnod *mh_prev; -#endif - const char *mh_file; - unsigned int mh_line; -} MEMHDR; - +} MEMHDR; #ifdef SUN4 #define ALIGN_SIZE 16 #else #define ALIGN_SIZE sizeof(double) #endif -#define HDR_SIZE sizeof(MEMHDR) -#define RESERVE_SIZE (((HDR_SIZE + (ALIGN_SIZE-1)) \ +#define RESERVE_SIZE (((sizeof(MEMHDR) + ALIGN_SIZE - 1) \ / ALIGN_SIZE ) * ALIGN_SIZE) #define MAX_SIZE_T ((size_t)-1) @@ -97,102 +54,21 @@ typedef struct memnod { #define CLIENT_2_HDR(a) ((void *) (((char *) (a)) - RESERVE_SIZE)) #define HDR_2_CLIENT(a) ((void *) (((char *) (a)) + RESERVE_SIZE)) - -static unsigned int block=0; -static unsigned int xmlMemStopAtBlock = 0; -static void *xmlMemTraceBlockAt = NULL; -#ifdef MEM_LIST -static MEMHDR *memlist = NULL; -#endif - -static void debugmem_tag_error(void *addr); -#ifdef MEM_LIST -static void debugmem_list_add(MEMHDR *); -static void debugmem_list_delete(MEMHDR *); -#endif -#define Mem_Tag_Err(a) debugmem_tag_error(a); - -#ifndef TEST_POINT -#define TEST_POINT -#endif - -/** - * xmlMallocBreakpoint: - * - * Breakpoint to use in conjunction with xmlMemStopAtBlock. When the block - * number reaches the specified value this function is called. One need to add a breakpoint - * to it to get the context in which the given block is allocated. - */ - -void -xmlMallocBreakpoint(void) { - xmlGenericError(xmlGenericErrorContext, - "xmlMallocBreakpoint reached on block %d\n", xmlMemStopAtBlock); -} - /** * xmlMallocLoc: * @size: an int specifying the size in byte to allocate. * @file: the file name or NULL * @line: the line number * - * a malloc() equivalent, with logging of the allocation info. + * DEPRECATED: don't use * * Returns a pointer to the allocated area or NULL in case of lack of memory. */ - void * -xmlMallocLoc(size_t size, const char * file, int line) +xmlMallocLoc(size_t size, const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED) { - MEMHDR *p; - void *ret; - - xmlInitParser(); - - TEST_POINT - - if (size > (MAX_SIZE_T - RESERVE_SIZE)) { - xmlGenericError(xmlGenericErrorContext, - "xmlMallocLoc : Unsigned overflow\n"); - return(NULL); - } - - p = (MEMHDR *) malloc(RESERVE_SIZE+size); - - if (!p) { - xmlGenericError(xmlGenericErrorContext, - "xmlMallocLoc : Out of free space\n"); - return(NULL); - } - p->mh_tag = MEMTAG; - p->mh_size = size; - p->mh_type = MALLOC_TYPE; - p->mh_file = file; - p->mh_line = line; - xmlMutexLock(&xmlMemMutex); - p->mh_number = ++block; - debugMemSize += size; - debugMemBlocks++; - if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; -#ifdef MEM_LIST - debugmem_list_add(p); -#endif - xmlMutexUnlock(&xmlMemMutex); - - if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); - - ret = HDR_2_CLIENT(p); - - if (xmlMemTraceBlockAt == ret) { - xmlGenericError(xmlGenericErrorContext, - "%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt, - (long unsigned)size); - xmlMallocBreakpoint(); - } - - TEST_POINT - - return(ret); + return(xmlMemMalloc(size)); } /** @@ -201,174 +77,119 @@ xmlMallocLoc(size_t size, const char * file, int line) * @file: the file name or NULL * @line: the line number * - * a malloc() equivalent, with logging of the allocation info. + * DEPRECATED: don't use * * Returns a pointer to the allocated area or NULL in case of lack of memory. */ +void * +xmlMallocAtomicLoc(size_t size, const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED) +{ + return(xmlMemMalloc(size)); +} +/** + * xmlMemMalloc: + * @size: an int specifying the size in byte to allocate. + * + * a malloc() equivalent, with logging of the allocation info. + * + * Returns a pointer to the allocated area or NULL in case of lack of memory. + */ void * -xmlMallocAtomicLoc(size_t size, const char * file, int line) +xmlMemMalloc(size_t size) { MEMHDR *p; - void *ret; xmlInitParser(); - TEST_POINT - if (size > (MAX_SIZE_T - RESERVE_SIZE)) { - xmlGenericError(xmlGenericErrorContext, - "xmlMallocAtomicLoc : Unsigned overflow\n"); - return(NULL); + fprintf(stderr, "xmlMemMalloc: Unsigned overflow\n"); + return(NULL); } - p = (MEMHDR *) malloc(RESERVE_SIZE+size); - + p = (MEMHDR *) malloc(RESERVE_SIZE + size); if (!p) { - xmlGenericError(xmlGenericErrorContext, - "xmlMallocAtomicLoc : Out of free space\n"); - return(NULL); + fprintf(stderr, "xmlMemMalloc: Out of memory\n"); + return(NULL); } p->mh_tag = MEMTAG; p->mh_size = size; - p->mh_type = MALLOC_ATOMIC_TYPE; - p->mh_file = file; - p->mh_line = line; + xmlMutexLock(&xmlMemMutex); - p->mh_number = ++block; debugMemSize += size; debugMemBlocks++; - if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; -#ifdef MEM_LIST - debugmem_list_add(p); -#endif xmlMutexUnlock(&xmlMemMutex); - if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); - - ret = HDR_2_CLIENT(p); - - if (xmlMemTraceBlockAt == ret) { - xmlGenericError(xmlGenericErrorContext, - "%p : Malloc(%lu) Ok\n", xmlMemTraceBlockAt, - (long unsigned)size); - xmlMallocBreakpoint(); - } - - TEST_POINT - - return(ret); + return(HDR_2_CLIENT(p)); } + /** - * xmlMemMalloc: + * xmlReallocLoc: + * @ptr: the initial memory block pointer * @size: an int specifying the size in byte to allocate. + * @file: the file name or NULL + * @line: the line number * - * a malloc() equivalent, with logging of the allocation info. + * DEPRECATED: don't use * * Returns a pointer to the allocated area or NULL in case of lack of memory. */ - void * -xmlMemMalloc(size_t size) +xmlReallocLoc(void *ptr, size_t size, const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED) { - return(xmlMallocLoc(size, "none", 0)); + return(xmlMemRealloc(ptr, size)); } /** - * xmlReallocLoc: + * xmlMemRealloc: * @ptr: the initial memory block pointer * @size: an int specifying the size in byte to allocate. - * @file: the file name or NULL - * @line: the line number * * a realloc() equivalent, with logging of the allocation info. * * Returns a pointer to the allocated area or NULL in case of lack of memory. */ - void * -xmlReallocLoc(void *ptr,size_t size, const char * file, int line) -{ +xmlMemRealloc(void *ptr, size_t size) { MEMHDR *p, *tmp; - unsigned long number; + size_t oldSize; if (ptr == NULL) - return(xmlMallocLoc(size, file, line)); + return(xmlMemMalloc(size)); xmlInitParser(); - TEST_POINT + + if (size > (MAX_SIZE_T - RESERVE_SIZE)) { + fprintf(stderr, "xmlMemRealloc: Unsigned overflow\n"); + return(NULL); + } p = CLIENT_2_HDR(ptr); - number = p->mh_number; - if (xmlMemStopAtBlock == number) xmlMallocBreakpoint(); if (p->mh_tag != MEMTAG) { - Mem_Tag_Err(p); - goto error; + fprintf(stderr, "xmlMemRealloc: Tag error\n"); + return(NULL); } + oldSize = p->mh_size; p->mh_tag = ~MEMTAG; - xmlMutexLock(&xmlMemMutex); - debugMemSize -= p->mh_size; - debugMemBlocks--; -#ifdef MEM_LIST - debugmem_list_delete(p); -#endif - xmlMutexUnlock(&xmlMemMutex); - if (size > (MAX_SIZE_T - RESERVE_SIZE)) { - xmlGenericError(xmlGenericErrorContext, - "xmlReallocLoc : Unsigned overflow\n"); - return(NULL); - } - - tmp = (MEMHDR *) realloc(p,RESERVE_SIZE+size); + tmp = (MEMHDR *) realloc(p, RESERVE_SIZE + size); if (!tmp) { - free(p); - goto error; + p->mh_tag = MEMTAG; + fprintf(stderr, "xmlMemRealloc: Out of memory\n"); + return(NULL); } p = tmp; - if (xmlMemTraceBlockAt == ptr) { - xmlGenericError(xmlGenericErrorContext, - "%p : Realloced(%lu -> %lu) Ok\n", - xmlMemTraceBlockAt, (long unsigned)p->mh_size, - (long unsigned)size); - xmlMallocBreakpoint(); - } p->mh_tag = MEMTAG; - p->mh_number = number; - p->mh_type = REALLOC_TYPE; p->mh_size = size; - p->mh_file = file; - p->mh_line = line; + xmlMutexLock(&xmlMemMutex); + debugMemSize -= oldSize; debugMemSize += size; - debugMemBlocks++; - if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; -#ifdef MEM_LIST - debugmem_list_add(p); -#endif xmlMutexUnlock(&xmlMemMutex); - TEST_POINT - return(HDR_2_CLIENT(p)); - -error: - return(NULL); -} - -/** - * xmlMemRealloc: - * @ptr: the initial memory block pointer - * @size: an int specifying the size in byte to allocate. - * - * a realloc() equivalent, with logging of the allocation info. - * - * Returns a pointer to the allocated area or NULL in case of lack of memory. - */ - -void * -xmlMemRealloc(void *ptr,size_t size) { - return(xmlReallocLoc(ptr, size, "none", 0)); } /** @@ -381,53 +202,30 @@ void xmlMemFree(void *ptr) { MEMHDR *p; - char *target; if (ptr == NULL) - return; + return; if (ptr == (void *) -1) { - xmlGenericError(xmlGenericErrorContext, - "trying to free pointer from freed area\n"); - goto error; - } - - if (xmlMemTraceBlockAt == ptr) { - xmlGenericError(xmlGenericErrorContext, - "%p : Freed()\n", xmlMemTraceBlockAt); - xmlMallocBreakpoint(); + fprintf(stderr, "xmlMemFree: Pointer from freed area\n"); + return; } - TEST_POINT - - target = (char *) ptr; - p = CLIENT_2_HDR(ptr); if (p->mh_tag != MEMTAG) { - Mem_Tag_Err(p); - goto error; + fprintf(stderr, "xmlMemFree: Tag error\n"); + return; } - if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); p->mh_tag = ~MEMTAG; - memset(target, -1, p->mh_size); + memset(ptr, -1, p->mh_size); + xmlMutexLock(&xmlMemMutex); debugMemSize -= p->mh_size; debugMemBlocks--; -#ifdef MEM_LIST - debugmem_list_delete(p); -#endif xmlMutexUnlock(&xmlMemMutex); free(p); - TEST_POINT - - return; - -error: - xmlGenericError(xmlGenericErrorContext, - "xmlMemFree(%p) error\n", ptr); - xmlMallocBreakpoint(); return; } @@ -437,78 +235,56 @@ xmlMemFree(void *ptr) * @file: the file name or NULL * @line: the line number * - * a strdup() equivalent, with logging of the allocation info. + * DEPRECATED: don't use * * Returns a pointer to the new string or NULL if allocation error occurred. */ - char * -xmlMemStrdupLoc(const char *str, const char *file, int line) +xmlMemStrdupLoc(const char *str, const char *file ATTRIBUTE_UNUSED, + int line ATTRIBUTE_UNUSED) { + return(xmlMemoryStrdup(str)); +} + +/** + * xmlMemoryStrdup: + * @str: the initial string pointer + * + * a strdup() equivalent, with logging of the allocation info. + * + * Returns a pointer to the new string or NULL if allocation error occurred. + */ +char * +xmlMemoryStrdup(const char *str) { char *s; size_t size = strlen(str) + 1; MEMHDR *p; xmlInitParser(); - TEST_POINT if (size > (MAX_SIZE_T - RESERVE_SIZE)) { - xmlGenericError(xmlGenericErrorContext, - "xmlMemStrdupLoc : Unsigned overflow\n"); - return(NULL); + fprintf(stderr, "xmlMemoryStrdup: Unsigned overflow\n"); + return(NULL); } - p = (MEMHDR *) malloc(RESERVE_SIZE+size); + p = (MEMHDR *) malloc(RESERVE_SIZE + size); if (!p) { - goto error; + fprintf(stderr, "xmlMemoryStrdup: Out of memory\n"); + return(NULL); } p->mh_tag = MEMTAG; p->mh_size = size; - p->mh_type = STRDUP_TYPE; - p->mh_file = file; - p->mh_line = line; + xmlMutexLock(&xmlMemMutex); - p->mh_number = ++block; debugMemSize += size; debugMemBlocks++; - if (debugMemSize > debugMaxMemSize) debugMaxMemSize = debugMemSize; -#ifdef MEM_LIST - debugmem_list_add(p); -#endif xmlMutexUnlock(&xmlMemMutex); s = (char *) HDR_2_CLIENT(p); - if (xmlMemStopAtBlock == p->mh_number) xmlMallocBreakpoint(); - - strcpy(s,str); - - TEST_POINT - - if (xmlMemTraceBlockAt == s) { - xmlGenericError(xmlGenericErrorContext, - "%p : Strdup() Ok\n", xmlMemTraceBlockAt); - xmlMallocBreakpoint(); - } + memcpy(s, str, size); return(s); - -error: - return(NULL); -} - -/** - * xmlMemoryStrdup: - * @str: the initial string pointer - * - * a strdup() equivalent, with logging of the allocation info. - * - * Returns a pointer to the new string or NULL if allocation error occurred. - */ - -char * -xmlMemoryStrdup(const char *str) { - return(xmlMemStrdupLoc(str, "none", 0)); } /** @@ -565,256 +341,47 @@ xmlMemBlocks(void) { /** * xmlMemDisplayLast: - * @fp: a FILE descriptor used as the output file, if NULL, the result is - * written to the file .memorylist + * @fp: a FILE descriptor * @nbBytes: the amount of memory to dump * - * the last nbBytes of memory allocated and not freed, useful for dumping - * the memory left allocated between two places at runtime. + * DEPRECATED: This feature was removed. */ - void -xmlMemDisplayLast(FILE *fp, long nbBytes) +xmlMemDisplayLast(FILE *fp ATTRIBUTE_UNUSED, long nbBytes ATTRIBUTE_UNUSED) { -#ifdef MEM_LIST - MEMHDR *p; - unsigned idx; - int nb = 0; -#endif - FILE *old_fp = fp; - - if (nbBytes <= 0) - return; - - if (fp == NULL) { - fp = fopen(".memorylist", "w"); - if (fp == NULL) - return; - } - -#ifdef MEM_LIST - fprintf(fp," Last %li MEMORY ALLOCATED : %lu, MAX was %lu\n", - nbBytes, debugMemSize, debugMaxMemSize); - fprintf(fp,"BLOCK NUMBER SIZE TYPE\n"); - idx = 0; - xmlMutexLock(&xmlMemMutex); - p = memlist; - while ((p) && (nbBytes > 0)) { - fprintf(fp,"%-5u %6lu %6lu ",idx++,p->mh_number, - (unsigned long)p->mh_size); - switch (p->mh_type) { - case STRDUP_TYPE:fprintf(fp,"strdup() in ");break; - case MALLOC_TYPE:fprintf(fp,"malloc() in ");break; - case REALLOC_TYPE:fprintf(fp,"realloc() in ");break; - case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc() in ");break; - case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break; - default: - fprintf(fp,"Unknown memory block, may be corrupted"); - xmlMutexUnlock(&xmlMemMutex); - if (old_fp == NULL) - fclose(fp); - return; - } - if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line); - if (p->mh_tag != MEMTAG) - fprintf(fp," INVALID"); - nb++; - - fprintf(fp,"\n"); - nbBytes -= (unsigned long)p->mh_size; - p = p->mh_next; - } - xmlMutexUnlock(&xmlMemMutex); -#else - fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n"); -#endif - if (old_fp == NULL) - fclose(fp); } /** * xmlMemDisplay: - * @fp: a FILE descriptor used as the output file, if NULL, the result is - * written to the file .memorylist + * @fp: a FILE descriptor * - * show in-extenso the memory blocks allocated + * DEPRECATED: This feature was removed. */ - void -xmlMemDisplay(FILE *fp) +xmlMemDisplay(FILE *fp ATTRIBUTE_UNUSED) { -#ifdef MEM_LIST - MEMHDR *p; - unsigned idx; - int nb = 0; - time_t currentTime; - char buf[500]; - struct tm * tstruct; -#endif - FILE *old_fp = fp; - - if (fp == NULL) { - fp = fopen(".memorylist", "w"); - if (fp == NULL) - return; - } - -#ifdef MEM_LIST - currentTime = time(NULL); - tstruct = localtime(¤tTime); - strftime(buf, sizeof(buf) - 1, "%I:%M:%S %p", tstruct); - fprintf(fp," %s\n\n", buf); - - - fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n", - debugMemSize, debugMaxMemSize); - fprintf(fp,"BLOCK NUMBER SIZE TYPE\n"); - idx = 0; - xmlMutexLock(&xmlMemMutex); - p = memlist; - while (p) { - fprintf(fp,"%-5u %6lu %6lu ",idx++,p->mh_number, - (unsigned long)p->mh_size); - switch (p->mh_type) { - case STRDUP_TYPE:fprintf(fp,"strdup() in ");break; - case MALLOC_TYPE:fprintf(fp,"malloc() in ");break; - case REALLOC_TYPE:fprintf(fp,"realloc() in ");break; - case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc() in ");break; - case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break; - default: - fprintf(fp,"Unknown memory block, may be corrupted"); - xmlMutexUnlock(&xmlMemMutex); - if (old_fp == NULL) - fclose(fp); - return; - } - if (p->mh_file != NULL) fprintf(fp,"%s(%u)", p->mh_file, p->mh_line); - if (p->mh_tag != MEMTAG) - fprintf(fp," INVALID"); - nb++; - - fprintf(fp,"\n"); - p = p->mh_next; - } - xmlMutexUnlock(&xmlMemMutex); -#else - fprintf(fp,"Memory list not compiled (MEM_LIST not defined !)\n"); -#endif - if (old_fp == NULL) - fclose(fp); } -#ifdef MEM_LIST - -static void debugmem_list_add(MEMHDR *p) -{ - p->mh_next = memlist; - p->mh_prev = NULL; - if (memlist) memlist->mh_prev = p; - memlist = p; -} - -static void debugmem_list_delete(MEMHDR *p) -{ - if (p->mh_next) - p->mh_next->mh_prev = p->mh_prev; - if (p->mh_prev) - p->mh_prev->mh_next = p->mh_next; - else memlist = p->mh_next; -} - -#endif - -/* - * debugmem_tag_error: - * - * internal error function. - */ - -static void debugmem_tag_error(void *p) -{ - xmlGenericError(xmlGenericErrorContext, - "Memory tag error occurs :%p \n\t bye\n", p); -#ifdef MEM_LIST - if (stderr) - xmlMemDisplay(stderr); -#endif -} - -#ifdef MEM_LIST -static FILE *xmlMemoryDumpFile = NULL; -#endif - /** * xmlMemShow: - * @fp: a FILE descriptor used as the output file + * @fp: a FILE descriptor * @nr: number of entries to dump * - * show a show display of the memory allocated, and dump - * the @nr last allocated areas which were not freed + * DEPRECATED: This feature was removed. */ - void -xmlMemShow(FILE *fp, int nr ATTRIBUTE_UNUSED) +xmlMemShow(FILE *fp ATTRIBUTE_UNUSED, int nr ATTRIBUTE_UNUSED) { -#ifdef MEM_LIST - MEMHDR *p; -#endif - - if (fp != NULL) - fprintf(fp," MEMORY ALLOCATED : %lu, MAX was %lu\n", - debugMemSize, debugMaxMemSize); -#ifdef MEM_LIST - xmlMutexLock(&xmlMemMutex); - if (nr > 0) { - fprintf(fp,"NUMBER SIZE TYPE WHERE\n"); - p = memlist; - while ((p) && nr > 0) { - fprintf(fp,"%6lu %6lu ",p->mh_number,(unsigned long)p->mh_size); - switch (p->mh_type) { - case STRDUP_TYPE:fprintf(fp,"strdup() in ");break; - case MALLOC_TYPE:fprintf(fp,"malloc() in ");break; - case MALLOC_ATOMIC_TYPE:fprintf(fp,"atomicmalloc() in ");break; - case REALLOC_TYPE:fprintf(fp,"realloc() in ");break; - case REALLOC_ATOMIC_TYPE:fprintf(fp,"atomicrealloc() in ");break; - default:fprintf(fp," ??? in ");break; - } - if (p->mh_file != NULL) - fprintf(fp,"%s(%u)", p->mh_file, p->mh_line); - if (p->mh_tag != MEMTAG) - fprintf(fp," INVALID"); - fprintf(fp,"\n"); - nr--; - p = p->mh_next; - } - } - xmlMutexUnlock(&xmlMemMutex); -#endif /* MEM_LIST */ } /** * xmlMemoryDump: * - * Dump in-extenso the memory blocks allocated to the file .memorylist + * DEPRECATED: This feature was removed. */ - void xmlMemoryDump(void) { -#ifdef MEM_LIST - FILE *dump; - - if (debugMaxMemSize == 0) - return; - dump = fopen(".memdump", "w"); - if (dump == NULL) - xmlMemoryDumpFile = stderr; - else xmlMemoryDumpFile = dump; - - xmlMemDisplay(xmlMemoryDumpFile); - - if (dump != NULL) fclose(dump); -#endif /* MEM_LIST */ } @@ -828,6 +395,8 @@ xmlMemoryDump(void) * xmlInitMemory: * * DEPRECATED: Alias for xmlInitParser. + * + * Returns 0. */ int xmlInitMemory(void) { @@ -839,23 +408,10 @@ xmlInitMemory(void) { * xmlInitMemoryInternal: * * Initialize the memory layer. - * - * Returns 0 on success */ void xmlInitMemoryInternal(void) { - char *breakpoint; - xmlInitMutex(&xmlMemMutex); - - breakpoint = getenv("XML_MEM_BREAKPOINT"); - if (breakpoint != NULL) { - sscanf(breakpoint, "%ud", &xmlMemStopAtBlock); - } - breakpoint = getenv("XML_MEM_TRACE"); - if (breakpoint != NULL) { - sscanf(breakpoint, "%p", &xmlMemTraceBlockAt); - } - + xmlInitMutex(&xmlMemMutex); } /** diff --git a/libraries/libxml2/xmlreader.c b/libraries/libxml2/xmlreader.c index 1f903306..a7328e18 100644 --- a/libraries/libxml2/xmlreader.c +++ b/libraries/libxml2/xmlreader.c @@ -40,13 +40,13 @@ #endif #include "private/buf.h" +#include "private/error.h" #include "private/tree.h" +#include "private/parser.h" #ifdef LIBXML_XINCLUDE_ENABLED #include "private/xinclude.h" #endif -#define MAX_ERR_MSG_SIZE 64000 - #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION /* Keeping free objects can hide memory errors. */ #define MAX_FREE_NODES 1 @@ -54,39 +54,14 @@ #define MAX_FREE_NODES 100 #endif -/* - * The following VA_COPY was coded following an example in - * the Samba project. It may not be sufficient for some - * esoteric implementations of va_list but (hopefully) will - * be sufficient for libxml2. - */ -#ifndef VA_COPY - #ifdef HAVE_VA_COPY - #define VA_COPY(dest, src) va_copy(dest, src) +#ifndef va_copy + #ifdef __va_copy + #define va_copy(dest, src) __va_copy(dest, src) #else - #ifdef HAVE___VA_COPY - #define VA_COPY(dest,src) __va_copy(dest, src) - #else - #ifndef VA_LIST_IS_ARRAY - #define VA_COPY(dest,src) (dest) = (src) - #else - #include - #define VA_COPY(dest,src) memcpy((char *)(dest),(char *)(src),sizeof(va_list)) - #endif - #endif + #define va_copy(dest, src) memcpy(dest, src, sizeof(va_list)) #endif #endif -/** - * TODO: - * - * macro to flag unimplemented blocks - */ -#define TODO \ - xmlGenericError(xmlGenericErrorContext, \ - "Unimplemented block at %s:%d\n", \ - __FILE__, __LINE__); - #define CHUNK_SIZE 512 /************************************************************************ * * @@ -187,22 +162,9 @@ struct _xmlTextReader { #define NODE_IS_PRESERVED 0x2 #define NODE_IS_SPRESERVED 0x4 -/** - * CONSTSTR: - * - * Macro used to return an interned string - */ -#define CONSTSTR(str) xmlDictLookup(reader->dict, (str), -1) -#define CONSTQSTR(p, str) xmlDictQLookup(reader->dict, (p), (str)) - static int xmlTextReaderReadTree(xmlTextReaderPtr reader); static int xmlTextReaderNextTree(xmlTextReaderPtr reader); -/************************************************************************ - * * - * Our own version of the freeing routines as we recycle nodes * - * * - ************************************************************************/ /** * DICT_FREE: * @str: a string @@ -218,6 +180,65 @@ static int xmlTextReaderNextTree(xmlTextReaderPtr reader); static void xmlTextReaderFreeNode(xmlTextReaderPtr reader, xmlNodePtr cur); static void xmlTextReaderFreeNodeList(xmlTextReaderPtr reader, xmlNodePtr cur); +static void +xmlTextReaderErrMemory(xmlTextReaderPtr reader) { + if (reader->ctxt != NULL) + xmlCtxtErrMemory(reader->ctxt); + else + xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_PARSER, NULL); + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; +} + +static xmlChar * +readerStrdup(xmlTextReaderPtr reader, const xmlChar *string) { + xmlChar *copy; + + if (string == NULL) + return(NULL); + + copy = xmlStrdup(string); + if (copy == NULL) + xmlTextReaderErrMemory(reader); + + return(copy); +} + +static const xmlChar * +constString(xmlTextReaderPtr reader, const xmlChar *string) { + const xmlChar *dictString; + + if (string == NULL) + return(NULL); + + dictString = xmlDictLookup(reader->dict, string, -1); + if (dictString == NULL) + xmlTextReaderErrMemory(reader); + + return(dictString); +} + +static const xmlChar * +constQString(xmlTextReaderPtr reader, const xmlChar *prefix, + const xmlChar *name) { + const xmlChar *dictString; + + if (name == NULL) + return(NULL); + + dictString = xmlDictQLookup(reader->dict, prefix, name); + if (dictString == NULL) + xmlTextReaderErrMemory(reader); + + return(dictString); +} + +/************************************************************************ + * * + * Our own version of the freeing routines as we recycle nodes * + * * + ************************************************************************/ + /** * xmlTextReaderFreeProp: * @reader: the xmlTextReaderPtr used @@ -241,7 +262,19 @@ xmlTextReaderFreeProp(xmlTextReaderPtr reader, xmlAttrPtr cur) { if (cur->children != NULL) xmlTextReaderFreeNodeList(reader, cur->children); - DICT_FREE(cur->name); + if (cur->id != NULL) { + /* + * Operating in streaming mode, attr is gonna disappear + */ + cur->id->attr = NULL; + if (cur->id->name != NULL) + DICT_FREE(cur->id->name); + cur->id->name = cur->name; + cur->name = NULL; + } else { + DICT_FREE(cur->name); + } + if ((reader != NULL) && (reader->ctxt != NULL) && (reader->ctxt->freeAttrsNr < MAX_FREE_NODES)) { cur->next = reader->ctxt->freeAttrs; @@ -497,6 +530,34 @@ xmlTextReaderFreeDoc(xmlTextReaderPtr reader, xmlDocPtr cur) { * * ************************************************************************/ +static void +xmlTextReaderStructuredRelay(void *userData, const xmlError *error) +{ + xmlTextReaderPtr reader = (xmlTextReaderPtr) userData; + + if (reader->sErrorFunc != NULL) { + reader->sErrorFunc(reader->errorFuncArg, error); + } else if (reader->errorFunc != NULL) { + xmlParserSeverities severity; + + if ((error->domain == XML_FROM_VALID) || + (error->domain == XML_FROM_DTD)) { + if (error->level == XML_ERR_WARNING) + severity = XML_PARSER_SEVERITY_VALIDITY_WARNING; + else + severity = XML_PARSER_SEVERITY_VALIDITY_ERROR; + } else { + if (error->level == XML_ERR_WARNING) + severity = XML_PARSER_SEVERITY_WARNING; + else + severity = XML_PARSER_SEVERITY_ERROR; + } + + reader->errorFunc(reader->errorFuncArg, error->message, severity, + reader->ctxt); + } +} + /** * xmlTextReaderEntPush: * @reader: the xmlTextReaderPtr used @@ -516,7 +577,7 @@ xmlTextReaderEntPush(xmlTextReaderPtr reader, xmlNodePtr value) tmp = (xmlNodePtr *) xmlRealloc(reader->entTab, newSize * sizeof(*tmp)); if (tmp == NULL) { - xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); + xmlTextReaderErrMemory(reader); return (-1); } reader->entTab = tmp; @@ -733,11 +794,10 @@ xmlTextReaderPushData(xmlTextReaderPtr reader) { break; } } else if (val < 0) { - xmlGenericError(xmlGenericErrorContext, - "xmlParserInputBufferRead failed\n"); - reader->mode = XML_TEXTREADER_MODE_EOF; - reader->state = oldstate; - return(val); + xmlCtxtErrIO(reader->ctxt, reader->input->error, NULL); + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; + return(-1); } } else @@ -819,8 +879,8 @@ xmlTextReaderPushData(xmlTextReaderPtr reader) { * * Push the current node for validation */ -static void -xmlTextReaderValidatePush(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { +static int +xmlTextReaderValidatePush(xmlTextReaderPtr reader) { xmlNodePtr node = reader->node; #ifdef LIBXML_VALID_ENABLED @@ -830,17 +890,24 @@ xmlTextReaderValidatePush(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, node->name); } else { - /* TODO use the BuildQName interface */ + xmlChar buf[50]; xmlChar *qname; - qname = xmlStrdup(node->ns->prefix); - qname = xmlStrcat(qname, BAD_CAST ":"); - qname = xmlStrcat(qname, node->name); + qname = xmlBuildQName(node->name, node->ns->prefix, buf, 50); + if (qname == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } reader->ctxt->valid &= xmlValidatePushElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, qname); - if (qname != NULL) - xmlFree(qname); + if (qname != buf) + xmlFree(qname); } + /*if (reader->ctxt->errNo == XML_ERR_NO_MEMORY) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; + return(-1); + }*/ } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED @@ -848,7 +915,7 @@ xmlTextReaderValidatePush(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { (reader->rngValidCtxt != NULL)) { int ret; - if (reader->rngFullNode != NULL) return; + if (reader->rngFullNode != NULL) return(0); ret = xmlRelaxNGValidatePushElement(reader->rngValidCtxt, reader->ctxt->myDoc, node); @@ -870,6 +937,8 @@ xmlTextReaderValidatePush(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) { reader->rngValidErrors++; } #endif + + return(0); } /** @@ -909,7 +978,7 @@ xmlTextReaderValidateCData(xmlTextReaderPtr reader, * * Pop the current node from validation */ -static void +static int xmlTextReaderValidatePop(xmlTextReaderPtr reader) { xmlNodePtr node = reader->node; @@ -920,17 +989,24 @@ xmlTextReaderValidatePop(xmlTextReaderPtr reader) { reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, node->name); } else { - /* TODO use the BuildQName interface */ + xmlChar buf[50]; xmlChar *qname; - qname = xmlStrdup(node->ns->prefix); - qname = xmlStrcat(qname, BAD_CAST ":"); - qname = xmlStrcat(qname, node->name); + qname = xmlBuildQName(node->name, node->ns->prefix, buf, 50); + if (qname == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } reader->ctxt->valid &= xmlValidatePopElement(&reader->ctxt->vctxt, reader->ctxt->myDoc, node, qname); - if (qname != NULL) - xmlFree(qname); + if (qname != buf) + xmlFree(qname); } + /*if (reader->ctxt->errNo == XML_ERR_NO_MEMORY) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; + return(-1); + }*/ } #endif /* LIBXML_VALID_ENABLED */ #ifdef LIBXML_SCHEMAS_ENABLED @@ -941,7 +1017,7 @@ xmlTextReaderValidatePop(xmlTextReaderPtr reader) { if (reader->rngFullNode != NULL) { if (node == reader->rngFullNode) reader->rngFullNode = NULL; - return; + return(0); } ret = xmlRelaxNGValidatePopElement(reader->rngValidCtxt, reader->ctxt->myDoc, @@ -950,6 +1026,8 @@ xmlTextReaderValidatePop(xmlTextReaderPtr reader) { reader->rngValidErrors++; } #endif + + return(0); } /** @@ -960,7 +1038,7 @@ xmlTextReaderValidatePop(xmlTextReaderPtr reader) { * entity substitution is not activated. As a result the parser interface * must walk through the entity and do the validation calls */ -static void +static int xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { xmlNodePtr oldnode = reader->node; xmlNodePtr node = reader->node; @@ -988,7 +1066,8 @@ xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { #ifdef LIBXML_REGEXP_ENABLED } else if (node->type == XML_ELEMENT_NODE) { reader->node = node; - xmlTextReaderValidatePush(reader); + if (xmlTextReaderValidatePush(reader) < 0) + return(-1); } else if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { xmlTextReaderValidateCData(reader, node->content, @@ -1003,7 +1082,8 @@ xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { node = node->children; continue; } else if (node->type == XML_ELEMENT_NODE) { - xmlTextReaderValidatePop(reader); + if (xmlTextReaderValidatePop(reader) < 0) + return(-1); } skip_children: if (node->next != NULL) { @@ -1024,7 +1104,8 @@ xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { } } reader->node = node; - xmlTextReaderValidatePop(reader); + if (xmlTextReaderValidatePop(reader) < 0) + return(-1); } if ((node->type == XML_ENTITY_DECL) && (reader->ent != NULL) && (reader->ent->children == node)) { @@ -1039,6 +1120,8 @@ xmlTextReaderValidateEntity(xmlTextReaderPtr reader) { } while ((node != NULL) && (node != oldnode)); } while ((node != NULL) && (node != oldnode)); reader->node = oldnode; + + return(0); } #endif /* LIBXML_REGEXP_ENABLED */ @@ -1081,7 +1164,8 @@ xmlTextReaderDoExpand(xmlTextReaderPtr reader) { if ((reader == NULL) || (reader->node == NULL) || (reader->ctxt == NULL)) return(-1); do { - if (reader->ctxt->instate == XML_PARSER_EOF) return(1); + if (PARSER_STOPPED(reader->ctxt)) + return(1); if (xmlTextReaderGetSuccessor(reader->node) != NULL) return(1); @@ -1092,60 +1176,13 @@ xmlTextReaderDoExpand(xmlTextReaderPtr reader) { val = xmlTextReaderPushData(reader); if (val < 0){ reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; return(-1); } } while(reader->mode != XML_TEXTREADER_MODE_EOF); return(1); } -/** - * xmlTextReaderCollectSiblings: - * @node: the first child - * - * Traverse depth-first through all sibling nodes and their children - * nodes and concatenate their content. This is an auxiliary function - * to xmlTextReaderReadString. - * - * Returns a string containing the content, or NULL in case of error. - */ -static xmlChar * -xmlTextReaderCollectSiblings(xmlNodePtr node) -{ - xmlBufferPtr buffer; - xmlChar *ret; - - if ((node == NULL) || (node->type == XML_NAMESPACE_DECL)) - return(NULL); - - buffer = xmlBufferCreate(); - if (buffer == NULL) - return NULL; - xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT); - - for ( ; node != NULL; node = node->next) { - switch (node->type) { - case XML_TEXT_NODE: - case XML_CDATA_SECTION_NODE: - xmlBufferCat(buffer, node->content); - break; - case XML_ELEMENT_NODE: { - xmlChar *tmp; - - tmp = xmlTextReaderCollectSiblings(node->children); - xmlBufferCat(buffer, tmp); - xmlFree(tmp); - break; - } - default: - break; - } - } - ret = buffer->content; - buffer->content = NULL; - xmlBufferFree(buffer); - return(ret); -} - /** * xmlTextReaderRead: * @reader: the xmlTextReaderPtr used @@ -1162,9 +1199,11 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { xmlTextReaderState oldstate = XML_TEXTREADER_START; xmlNodePtr oldnode = NULL; - if (reader == NULL) return(-1); + if (reader->state == XML_TEXTREADER_ERROR) + return(-1); + reader->curnode = NULL; if (reader->doc != NULL) return(xmlTextReaderReadTree(reader)); @@ -1178,11 +1217,11 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { */ do { val = xmlTextReaderPushData(reader); - if (val < 0){ - reader->mode = XML_TEXTREADER_MODE_ERROR; - reader->state = XML_TEXTREADER_ERROR; - return(-1); - } + if (val < 0) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; + return(-1); + } } while ((reader->ctxt->node == NULL) && ((reader->mode != XML_TEXTREADER_MODE_EOF) && (reader->state != XML_TEXTREADER_DONE))); @@ -1190,11 +1229,11 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { if (reader->ctxt->myDoc != NULL) { reader->node = reader->ctxt->myDoc->children; } - if (reader->node == NULL){ - reader->mode = XML_TEXTREADER_MODE_ERROR; - reader->state = XML_TEXTREADER_ERROR; + if (reader->node == NULL) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; return(-1); - } + } reader->state = XML_TEXTREADER_ELEMENT; } else { if (reader->ctxt->myDoc != NULL) { @@ -1214,10 +1253,13 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { get_next_node: if (reader->node == NULL) { - if (reader->mode == XML_TEXTREADER_MODE_EOF) + if (reader->mode == XML_TEXTREADER_MODE_EOF) { return(0); - else + } else { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; return(-1); + } } /* @@ -1239,13 +1281,14 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { ((reader->ctxt->node == NULL) || (reader->ctxt->node == reader->node) || (reader->ctxt->node == reader->node->parent)) && - (reader->ctxt->instate != XML_PARSER_EOF)) { + (reader->ctxt->instate != XML_PARSER_EOF) && + (PARSER_STOPPED(reader->ctxt) == 0)) { val = xmlTextReaderPushData(reader); - if (val < 0){ - reader->mode = XML_TEXTREADER_MODE_ERROR; - reader->state = XML_TEXTREADER_ERROR; + if (val < 0) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; return(-1); - } + } if (reader->node == NULL) goto node_end; } @@ -1275,7 +1318,8 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { #ifdef LIBXML_REGEXP_ENABLED if ((reader->validate) && (reader->node->type == XML_ELEMENT_NODE)) - xmlTextReaderValidatePop(reader); + if (xmlTextReaderValidatePop(reader) < 0) + return(-1); #endif /* LIBXML_REGEXP_ENABLED */ if ((reader->preserves > 0) && (reader->node->extra & NODE_IS_SPRESERVED)) @@ -1312,8 +1356,11 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { goto node_found; } #ifdef LIBXML_REGEXP_ENABLED - if ((reader->validate != XML_TEXTREADER_NOT_VALIDATE) && (reader->node->type == XML_ELEMENT_NODE)) - xmlTextReaderValidatePop(reader); + if ((reader->validate != XML_TEXTREADER_NOT_VALIDATE) && + (reader->node->type == XML_ELEMENT_NODE)) { + if (xmlTextReaderValidatePop(reader) < 0) + return(-1); + } #endif /* LIBXML_REGEXP_ENABLED */ if ((reader->preserves > 0) && (reader->node->extra & NODE_IS_SPRESERVED)) @@ -1325,8 +1372,11 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { if (reader->mode != XML_TEXTREADER_MODE_EOF) { val = xmlParseChunk(reader->ctxt, "", 0, 1); reader->state = XML_TEXTREADER_DONE; - if (val != 0) + if (val != 0) { + reader->mode = XML_TEXTREADER_MODE_ERROR; + reader->state = XML_TEXTREADER_ERROR; return(-1); + } } reader->node = NULL; reader->depth = -1; @@ -1386,16 +1436,29 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { (xmlStrEqual(reader->node->ns->href, XINCLUDE_OLD_NS)))) { if (reader->xincctxt == NULL) { reader->xincctxt = xmlXIncludeNewContext(reader->ctxt->myDoc); + if (reader->xincctxt == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } xmlXIncludeSetFlags(reader->xincctxt, reader->parserFlags & (~XML_PARSE_NOXINCNODE)); xmlXIncludeSetStreamingMode(reader->xincctxt, 1); + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlXIncludeSetErrorHandler(reader->xincctxt, + xmlTextReaderStructuredRelay, reader); } /* * expand that node and process it */ if (xmlTextReaderExpand(reader) == NULL) - return -1; - xmlXIncludeProcessNode(reader->xincctxt, reader->node); + return(-1); + if (xmlXIncludeProcessNode(reader->xincctxt, reader->node) < 0) { + int err = xmlXIncludeGetLastError(reader->xincctxt); + + if (err == XML_ERR_NO_MEMORY) + xmlTextReaderErrMemory(reader); + return(-1); + } } if ((reader->node != NULL) && (reader->node->type == XML_XINCLUDE_START)) { reader->in_xinclude++; @@ -1423,7 +1486,8 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { } else if ((reader->node != NULL) && (reader->node->type == XML_ENTITY_REF_NODE) && (reader->ctxt != NULL) && (reader->validate)) { - xmlTextReaderValidateEntity(reader); + if (xmlTextReaderValidateEntity(reader) < 0) + return(-1); #endif /* LIBXML_REGEXP_ENABLED */ } if ((reader->node != NULL) && @@ -1440,7 +1504,8 @@ xmlTextReaderRead(xmlTextReaderPtr reader) { if ((node->type == XML_ELEMENT_NODE) && ((reader->state != XML_TEXTREADER_END) && (reader->state != XML_TEXTREADER_BACKTRACK))) { - xmlTextReaderValidatePush(reader); + if (xmlTextReaderValidatePush(reader) < 0) + return(-1); } else if ((node->type == XML_TEXT_NODE) || (node->type == XML_CDATA_SECTION_NODE)) { xmlTextReaderValidateCData(reader, node->content, @@ -1546,6 +1611,37 @@ xmlTextReaderNext(xmlTextReaderPtr reader) { } #ifdef LIBXML_WRITER_ENABLED +static void +xmlTextReaderDumpCopy(xmlTextReaderPtr reader, xmlOutputBufferPtr output, + xmlNodePtr node) { + if ((node->type == XML_DTD_NODE) || + (node->type == XML_ELEMENT_DECL) || + (node->type == XML_ATTRIBUTE_DECL) || + (node->type == XML_ENTITY_DECL)) + return; + + if ((node->type == XML_DOCUMENT_NODE) || + (node->type == XML_HTML_DOCUMENT_NODE)) { + xmlNodeDumpOutput(output, node->doc, node, 0, 0, NULL); + } else { + xmlNodePtr copy; + + /* + * Create a copy to make sure that namespace declarations from + * ancestors are added. + */ + copy = xmlDocCopyNode(node, node->doc, 1); + if (copy == NULL) { + xmlTextReaderErrMemory(reader); + return; + } + + xmlNodeDumpOutput(output, copy->doc, copy, 0, 0, NULL); + + xmlFreeNode(copy); + } +} + /** * xmlTextReaderReadInnerXml: * @reader: the xmlTextReaderPtr used @@ -1557,47 +1653,36 @@ xmlTextReaderNext(xmlTextReaderPtr reader) { * string must be deallocated by the caller. */ xmlChar * -xmlTextReaderReadInnerXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) +xmlTextReaderReadInnerXml(xmlTextReaderPtr reader) { - xmlChar *resbuf; - xmlNodePtr node, cur_node; - xmlBufferPtr buff, buff2; - xmlDocPtr doc; + xmlOutputBufferPtr output; + xmlNodePtr cur; + xmlChar *ret; - if (xmlTextReaderExpand(reader) == NULL) { - return NULL; - } - doc = reader->node->doc; - buff = xmlBufferCreate(); - if (buff == NULL) - return NULL; - xmlBufferSetAllocationScheme(buff, XML_BUFFER_ALLOC_DOUBLEIT); - for (cur_node = reader->node->children; cur_node != NULL; - cur_node = cur_node->next) { - /* XXX: Why is the node copied? */ - node = xmlDocCopyNode(cur_node, doc, 1); - /* XXX: Why do we need a second buffer? */ - buff2 = xmlBufferCreate(); - xmlBufferSetAllocationScheme(buff2, XML_BUFFER_ALLOC_DOUBLEIT); - if (xmlNodeDump(buff2, doc, node, 0, 0) == -1) { - xmlFreeNode(node); - xmlBufferFree(buff2); - xmlBufferFree(buff); - return NULL; - } - xmlBufferCat(buff, buff2->content); - xmlFreeNode(node); - xmlBufferFree(buff2); + if (xmlTextReaderExpand(reader) == NULL) + return(NULL); + + if (reader->node == NULL) + return(NULL); + + output = xmlAllocOutputBuffer(NULL); + if (output == NULL) { + xmlTextReaderErrMemory(reader); + return(NULL); } - resbuf = buff->content; - buff->content = NULL; - xmlBufferFree(buff); - return resbuf; + for (cur = reader->node->children; cur != NULL; cur = cur->next) + xmlTextReaderDumpCopy(reader, output, cur); + + if (output->error) + xmlCtxtErrIO(reader->ctxt, output->error, NULL); + + ret = xmlBufDetach(output->buffer); + xmlOutputBufferClose(output); + + return(ret); } -#endif -#ifdef LIBXML_WRITER_ENABLED /** * xmlTextReaderReadOuterXml: * @reader: the xmlTextReaderPtr used @@ -1609,38 +1694,33 @@ xmlTextReaderReadInnerXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) * by the caller. */ xmlChar * -xmlTextReaderReadOuterXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) +xmlTextReaderReadOuterXml(xmlTextReaderPtr reader) { - xmlChar *resbuf; + xmlOutputBufferPtr output; xmlNodePtr node; - xmlBufferPtr buff; - xmlDocPtr doc; + xmlChar *ret; + + if (xmlTextReaderExpand(reader) == NULL) + return(NULL); - if (xmlTextReaderExpand(reader) == NULL) { - return NULL; - } node = reader->node; - doc = node->doc; - /* XXX: Why is the node copied? */ - if (node->type == XML_DTD_NODE) { - node = (xmlNodePtr) xmlCopyDtd((xmlDtdPtr) node); - } else { - node = xmlDocCopyNode(node, doc, 1); - } - buff = xmlBufferCreate(); - xmlBufferSetAllocationScheme(buff, XML_BUFFER_ALLOC_DOUBLEIT); - if (xmlNodeDump(buff, doc, node, 0, 0) == -1) { - xmlFreeNode(node); - xmlBufferFree(buff); - return NULL; + if (node == NULL) + return(NULL); + + output = xmlAllocOutputBuffer(NULL); + if (output == NULL) { + xmlTextReaderErrMemory(reader); + return(NULL); } - resbuf = buff->content; - buff->content = NULL; + xmlTextReaderDumpCopy(reader, output, node); + if (output->error) + xmlCtxtErrIO(reader->ctxt, output->error, NULL); - xmlFreeNode(node); - xmlBufferFree(buff); - return resbuf; + ret = xmlBufDetach(output->buffer); + xmlOutputBufferClose(output); + + return(ret); } #endif @@ -1657,29 +1737,73 @@ xmlTextReaderReadOuterXml(xmlTextReaderPtr reader ATTRIBUTE_UNUSED) xmlChar * xmlTextReaderReadString(xmlTextReaderPtr reader) { - xmlNodePtr node; + xmlNodePtr node, cur; + xmlBufPtr buf; + xmlChar *ret; if ((reader == NULL) || (reader->node == NULL)) return(NULL); node = (reader->curnode != NULL) ? reader->curnode : reader->node; switch (node->type) { - case XML_TEXT_NODE: - if (node->content != NULL) - return(xmlStrdup(node->content)); - break; - case XML_ELEMENT_NODE: - if (xmlTextReaderDoExpand(reader) != -1) { - return xmlTextReaderCollectSiblings(node->children); - } - break; - case XML_ATTRIBUTE_NODE: - TODO - break; - default: - break; + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + break; + case XML_ELEMENT_NODE: + if (xmlTextReaderDoExpand(reader) == -1) + return(NULL); + break; + case XML_ATTRIBUTE_NODE: + /* TODO */ + break; + default: + break; } - return(NULL); + + buf = xmlBufCreateSize(30); + if (buf == NULL) { + xmlTextReaderErrMemory(reader); + return(NULL); + } + xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); + + cur = node; + while (cur != NULL) { + switch (cur->type) { + case XML_TEXT_NODE: + case XML_CDATA_SECTION_NODE: + xmlBufCat(buf, cur->content); + break; + + case XML_ELEMENT_NODE: + if (cur->children != NULL) { + cur = cur->children; + continue; + } + break; + + default: + break; + } + + if (cur == node) + goto done; + + while (cur->next == NULL) { + cur = cur->parent; + if (cur == node) + goto done; + } + cur = cur->next; + } + +done: + ret = xmlBufDetach(buf); + if (ret == NULL) + xmlTextReaderErrMemory(reader); + + xmlBufFree(buf); + return(ret); } #if 0 @@ -1709,7 +1833,6 @@ xmlTextReaderReadBase64(xmlTextReaderPtr reader, if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE)) return(0); - TODO return(0); } @@ -1739,7 +1862,6 @@ xmlTextReaderReadBinHex(xmlTextReaderPtr reader, if ((reader->node == NULL) || (reader->node->type == XML_ELEMENT_NODE)) return(0); - TODO return(0); } #endif @@ -1939,11 +2061,8 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { if (input == NULL) return(NULL); ret = xmlMalloc(sizeof(xmlTextReader)); - if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlNewTextReader : malloc failed\n"); + if (ret == NULL) return(NULL); - } memset(ret, 0, sizeof(xmlTextReader)); ret->doc = NULL; ret->entTab = NULL; @@ -1953,8 +2072,6 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { ret->buffer = xmlBufCreateSize(100); if (ret->buffer == NULL) { xmlFree(ret); - xmlGenericError(xmlGenericErrorContext, - "xmlNewTextReader : malloc failed\n"); return(NULL); } /* no operation on a reader should require a huge buffer */ @@ -1964,8 +2081,6 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { if (ret->sax == NULL) { xmlBufFree(ret->buffer); xmlFree(ret); - xmlGenericError(xmlGenericErrorContext, - "xmlNewTextReader : malloc failed\n"); return(NULL); } xmlSAXVersion(ret->sax, 2); @@ -2011,8 +2126,6 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { } if (ret->ctxt == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlNewTextReader : malloc failed\n"); xmlBufFree(ret->buffer); xmlFree(ret->sax); xmlFree(ret); @@ -2026,7 +2139,6 @@ xmlNewTextReader(xmlParserInputBufferPtr input, const char *URI) { /* * use the parser dictionary to allocate all elements and attributes names */ - ret->ctxt->docdict = 1; ret->dict = ret->ctxt->dict; #ifdef LIBXML_XINCLUDE_ENABLED ret->xinclude = 0; @@ -2050,7 +2162,6 @@ xmlTextReaderPtr xmlNewTextReaderFilename(const char *URI) { xmlParserInputBufferPtr input; xmlTextReaderPtr ret; - char *directory = NULL; input = xmlParserInputBufferCreateFilename(URI, XML_CHAR_ENCODING_NONE); if (input == NULL) @@ -2061,12 +2172,6 @@ xmlNewTextReaderFilename(const char *URI) { return(NULL); } ret->allocs |= XML_TEXTREADER_INPUT; - if (ret->ctxt->directory == NULL) - directory = xmlParserGetDirectory(URI); - if ((ret->ctxt->directory == NULL) && (directory != NULL)) - ret->ctxt->directory = (char *) xmlStrdup((xmlChar *) directory); - if (directory != NULL) - xmlFree(directory); return(ret); } @@ -2142,6 +2247,7 @@ xmlFreeTextReader(xmlTextReaderPtr reader) { * Methods for XmlTextReader * * * ************************************************************************/ + /** * xmlTextReaderClose: * @reader: the xmlTextReaderPtr used @@ -2222,7 +2328,7 @@ xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) { ns = ns->next; } if (ns != NULL) - return(xmlStrdup(ns->href)); + return(readerStrdup(reader, ns->href)); cur = reader->node->properties; if (cur == NULL) @@ -2235,7 +2341,8 @@ xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) { /* TODO walk the DTD if present */ ret = xmlNodeListGetString(reader->node->doc, cur->children, 1); - if (ret == NULL) return(xmlStrdup((xmlChar *)"")); + if (ret == NULL) + xmlTextReaderErrMemory(reader); return(ret); } @@ -2252,9 +2359,10 @@ xmlTextReaderGetAttributeNo(xmlTextReaderPtr reader, int no) { xmlChar * xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) { xmlChar *prefix = NULL; - xmlChar *localname; + const xmlChar *localname; xmlNsPtr ns; xmlChar *ret = NULL; + int result; if ((reader == NULL) || (name == NULL)) return(NULL); @@ -2267,43 +2375,56 @@ xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) { if (reader->node->type != XML_ELEMENT_NODE) return(NULL); - localname = xmlSplitQName2(name, &prefix); + localname = xmlSplitQName4(name, &prefix); if (localname == NULL) { - /* - * Namespace default decl - */ - if (xmlStrEqual(name, BAD_CAST "xmlns")) { - ns = reader->node->nsDef; - while (ns != NULL) { - if (ns->prefix == NULL) { - return(xmlStrdup(ns->href)); - } - ns = ns->next; - } - return NULL; - } - return(xmlGetNoNsProp(reader->node, name)); - } + xmlTextReaderErrMemory(reader); + return(NULL); + } + if (prefix == NULL) { + /* + * Namespace default decl + */ + if (xmlStrEqual(name, BAD_CAST "xmlns")) { + ns = reader->node->nsDef; + while (ns != NULL) { + if (ns->prefix == NULL) { + return(readerStrdup(reader, ns->href)); + } + ns = ns->next; + } + return NULL; + } + + result = xmlNodeGetAttrValue(reader->node, name, NULL, &ret); + if (result < 0) + xmlTextReaderErrMemory(reader); + return(ret); + } /* * Namespace default decl */ if (xmlStrEqual(prefix, BAD_CAST "xmlns")) { - ns = reader->node->nsDef; - while (ns != NULL) { - if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) { - ret = xmlStrdup(ns->href); - break; - } - ns = ns->next; - } + ns = reader->node->nsDef; + while (ns != NULL) { + if ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localname))) { + ret = readerStrdup(reader, ns->href); + break; + } + ns = ns->next; + } } else { - ns = xmlSearchNs(reader->node->doc, reader->node, prefix); - if (ns != NULL) - ret = xmlGetNsProp(reader->node, localname, ns->href); - } + result = xmlSearchNsSafe(reader->node, prefix, &ns); + if (result < 0) + xmlTextReaderErrMemory(reader); + if (ns != NULL) { + result = xmlNodeGetAttrValue(reader->node, localname, ns->href, + &ret); + if (result < 0) + xmlTextReaderErrMemory(reader); + } + } - xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(ret); @@ -2324,8 +2445,10 @@ xmlTextReaderGetAttribute(xmlTextReaderPtr reader, const xmlChar *name) { xmlChar * xmlTextReaderGetAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName, const xmlChar *namespaceURI) { + xmlChar *ret = NULL; xmlChar *prefix = NULL; xmlNsPtr ns; + int result; if ((reader == NULL) || (localName == NULL)) return(NULL); @@ -2339,21 +2462,25 @@ xmlTextReaderGetAttributeNs(xmlTextReaderPtr reader, const xmlChar *localName, return(NULL); if (xmlStrEqual(namespaceURI, BAD_CAST "http://www.w3.org/2000/xmlns/")) { - if (! xmlStrEqual(localName, BAD_CAST "xmlns")) { - prefix = BAD_CAST localName; - } - ns = reader->node->nsDef; - while (ns != NULL) { - if ((prefix == NULL && ns->prefix == NULL) || - ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localName)))) { - return xmlStrdup(ns->href); - } - ns = ns->next; - } - return NULL; + if (! xmlStrEqual(localName, BAD_CAST "xmlns")) { + prefix = BAD_CAST localName; + } + ns = reader->node->nsDef; + while (ns != NULL) { + if ((prefix == NULL && ns->prefix == NULL) || + ((ns->prefix != NULL) && (xmlStrEqual(ns->prefix, localName)))) { + return readerStrdup(reader, ns->href); + } + ns = ns->next; + } + return NULL; } - return(xmlGetNsProp(reader->node, localName, namespaceURI)); + result = xmlNodeGetAttrValue(reader->node, localName, namespaceURI, &ret); + if (result < 0) + xmlTextReaderErrMemory(reader); + + return(ret); } /** @@ -2402,7 +2529,6 @@ xmlTextReaderGetRemainder(xmlTextReaderPtr reader) { * - by the layer which allocated it. * - by the layer to which would have been returned to. */ - TODO return(NULL); } return(ret); @@ -2422,16 +2548,21 @@ xmlTextReaderGetRemainder(xmlTextReaderPtr reader) { xmlChar * xmlTextReaderLookupNamespace(xmlTextReaderPtr reader, const xmlChar *prefix) { xmlNsPtr ns; + int result; if (reader == NULL) return(NULL); if (reader->node == NULL) return(NULL); - ns = xmlSearchNs(reader->node->doc, reader->node, prefix); + result = xmlSearchNsSafe(reader->node, prefix, &ns); + if (result < 0) { + xmlTextReaderErrMemory(reader); + return(NULL); + } if (ns == NULL) return(NULL); - return(xmlStrdup(ns->href)); + return(readerStrdup(reader, ns->href)); } /** @@ -2497,7 +2628,7 @@ xmlTextReaderMoveToAttributeNo(xmlTextReaderPtr reader, int no) { int xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) { xmlChar *prefix = NULL; - xmlChar *localname; + const xmlChar *localname; xmlNsPtr ns; xmlAttrPtr prop; @@ -2510,8 +2641,12 @@ xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) { if (reader->node->type != XML_ELEMENT_NODE) return(0); - localname = xmlSplitQName2(name, &prefix); + localname = xmlSplitQName4(name, &prefix); if (localname == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } + if (prefix == NULL) { /* * Namespace default decl */ @@ -2573,15 +2708,11 @@ xmlTextReaderMoveToAttribute(xmlTextReaderPtr reader, const xmlChar *name) { prop = prop->next; } not_found: - if (localname != NULL) - xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(0); found: - if (localname != NULL) - xmlFree(localname); if (prefix != NULL) xmlFree(prefix); return(1); @@ -2768,12 +2899,24 @@ xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader) { if (reader->faketext == NULL) { reader->faketext = xmlNewDocText(reader->node->doc, ns->href); + if (reader->faketext == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } } else { if ((reader->faketext->content != NULL) && (reader->faketext->content != (xmlChar *) &(reader->faketext->properties))) xmlFree(reader->faketext->content); - reader->faketext->content = xmlStrdup(ns->href); + if (ns->href == NULL) { + reader->faketext->content = NULL; + } else { + reader->faketext->content = xmlStrdup(ns->href); + if (reader->faketext->content == NULL) { + xmlTextReaderErrMemory(reader); + return(-1); + } + } } reader->curnode = reader->faketext; } else { @@ -2795,20 +2938,17 @@ xmlTextReaderReadAttributeValue(xmlTextReaderPtr reader) { */ const xmlChar * xmlTextReaderConstEncoding(xmlTextReaderPtr reader) { - xmlDocPtr doc = NULL; + const xmlChar *encoding = NULL; + if (reader == NULL) - return(NULL); - if (reader->doc != NULL) - doc = reader->doc; - else if (reader->ctxt != NULL) - doc = reader->ctxt->myDoc; - if (doc == NULL) - return(NULL); + return(NULL); - if (doc->encoding == NULL) - return(NULL); - else - return(CONSTSTR(doc->encoding)); + if (reader->ctxt != NULL) + encoding = xmlGetActualEncoding(reader->ctxt); + else if (reader->doc != NULL) + encoding = reader->doc->encoding; + + return(constString(reader, encoding)); } @@ -2982,14 +3122,14 @@ xmlTextReaderLocalName(xmlTextReaderPtr reader) { if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) - return(xmlStrdup(BAD_CAST "xmlns")); + return(readerStrdup(reader, BAD_CAST "xmlns")); else - return(xmlStrdup(ns->prefix)); + return(readerStrdup(reader, ns->prefix)); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(xmlTextReaderName(reader)); - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); } /** @@ -3013,7 +3153,7 @@ xmlTextReaderConstLocalName(xmlTextReaderPtr reader) { if (node->type == XML_NAMESPACE_DECL) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) - return(CONSTSTR(BAD_CAST "xmlns")); + return(constString(reader, BAD_CAST "xmlns")); else return(ns->prefix); } @@ -3048,41 +3188,41 @@ xmlTextReaderName(xmlTextReaderPtr reader) { case XML_ATTRIBUTE_NODE: if ((node->ns == NULL) || (node->ns->prefix == NULL)) - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); - ret = xmlStrdup(node->ns->prefix); - ret = xmlStrcat(ret, BAD_CAST ":"); - ret = xmlStrcat(ret, node->name); + ret = xmlBuildQName(node->name, node->ns->prefix, NULL, 0); + if (ret == NULL) + xmlTextReaderErrMemory(reader); return(ret); case XML_TEXT_NODE: - return(xmlStrdup(BAD_CAST "#text")); + return(readerStrdup(reader, BAD_CAST "#text")); case XML_CDATA_SECTION_NODE: - return(xmlStrdup(BAD_CAST "#cdata-section")); + return(readerStrdup(reader, BAD_CAST "#cdata-section")); case XML_ENTITY_NODE: case XML_ENTITY_REF_NODE: - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); case XML_PI_NODE: - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); case XML_COMMENT_NODE: - return(xmlStrdup(BAD_CAST "#comment")); + return(readerStrdup(reader, BAD_CAST "#comment")); case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: - return(xmlStrdup(BAD_CAST "#document")); + return(readerStrdup(reader, BAD_CAST "#document")); case XML_DOCUMENT_FRAG_NODE: - return(xmlStrdup(BAD_CAST "#document-fragment")); + return(readerStrdup(reader, BAD_CAST "#document-fragment")); case XML_NOTATION_NODE: - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: - return(xmlStrdup(node->name)); + return(readerStrdup(reader, node->name)); case XML_NAMESPACE_DECL: { xmlNsPtr ns = (xmlNsPtr) node; - ret = xmlStrdup(BAD_CAST "xmlns"); if (ns->prefix == NULL) - return(ret); - ret = xmlStrcat(ret, BAD_CAST ":"); - ret = xmlStrcat(ret, ns->prefix); + return(readerStrdup(reader, BAD_CAST "xmlns")); + ret = xmlBuildQName(ns->prefix, BAD_CAST "xmlns", NULL, 0); + if (ret == NULL) + xmlTextReaderErrMemory(reader); return(ret); } @@ -3121,34 +3261,34 @@ xmlTextReaderConstName(xmlTextReaderPtr reader) { if ((node->ns == NULL) || (node->ns->prefix == NULL)) return(node->name); - return(CONSTQSTR(node->ns->prefix, node->name)); + return(constQString(reader, node->ns->prefix, node->name)); case XML_TEXT_NODE: - return(CONSTSTR(BAD_CAST "#text")); + return(constString(reader, BAD_CAST "#text")); case XML_CDATA_SECTION_NODE: - return(CONSTSTR(BAD_CAST "#cdata-section")); + return(constString(reader, BAD_CAST "#cdata-section")); case XML_ENTITY_NODE: case XML_ENTITY_REF_NODE: - return(CONSTSTR(node->name)); + return(constString(reader, node->name)); case XML_PI_NODE: - return(CONSTSTR(node->name)); + return(constString(reader, node->name)); case XML_COMMENT_NODE: - return(CONSTSTR(BAD_CAST "#comment")); + return(constString(reader, BAD_CAST "#comment")); case XML_DOCUMENT_NODE: case XML_HTML_DOCUMENT_NODE: - return(CONSTSTR(BAD_CAST "#document")); + return(constString(reader, BAD_CAST "#document")); case XML_DOCUMENT_FRAG_NODE: - return(CONSTSTR(BAD_CAST "#document-fragment")); + return(constString(reader, BAD_CAST "#document-fragment")); case XML_NOTATION_NODE: - return(CONSTSTR(node->name)); + return(constString(reader, node->name)); case XML_DOCUMENT_TYPE_NODE: case XML_DTD_NODE: - return(CONSTSTR(node->name)); + return(constString(reader, node->name)); case XML_NAMESPACE_DECL: { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) - return(CONSTSTR(BAD_CAST "xmlns")); - return(CONSTQSTR(BAD_CAST "xmlns", ns->prefix)); + return(constString(reader, BAD_CAST "xmlns")); + return(constQString(reader, BAD_CAST "xmlns", ns->prefix)); } case XML_ELEMENT_DECL: @@ -3183,13 +3323,13 @@ xmlTextReaderPrefix(xmlTextReaderPtr reader) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(NULL); - return(xmlStrdup(BAD_CAST "xmlns")); + return(readerStrdup(reader, BAD_CAST "xmlns")); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if ((node->ns != NULL) && (node->ns->prefix != NULL)) - return(xmlStrdup(node->ns->prefix)); + return(readerStrdup(reader, node->ns->prefix)); return(NULL); } @@ -3215,13 +3355,13 @@ xmlTextReaderConstPrefix(xmlTextReaderPtr reader) { xmlNsPtr ns = (xmlNsPtr) node; if (ns->prefix == NULL) return(NULL); - return(CONSTSTR(BAD_CAST "xmlns")); + return(constString(reader, BAD_CAST "xmlns")); } if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if ((node->ns != NULL) && (node->ns->prefix != NULL)) - return(CONSTSTR(node->ns->prefix)); + return(constString(reader, node->ns->prefix)); return(NULL); } @@ -3244,12 +3384,12 @@ xmlTextReaderNamespaceUri(xmlTextReaderPtr reader) { else node = reader->node; if (node->type == XML_NAMESPACE_DECL) - return(xmlStrdup(BAD_CAST "http://www.w3.org/2000/xmlns/")); + return(readerStrdup(reader, BAD_CAST "http://www.w3.org/2000/xmlns/")); if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if (node->ns != NULL) - return(xmlStrdup(node->ns->href)); + return(readerStrdup(reader, node->ns->href)); return(NULL); } @@ -3272,12 +3412,12 @@ xmlTextReaderConstNamespaceUri(xmlTextReaderPtr reader) { else node = reader->node; if (node->type == XML_NAMESPACE_DECL) - return(CONSTSTR(BAD_CAST "http://www.w3.org/2000/xmlns/")); + return(constString(reader, BAD_CAST "http://www.w3.org/2000/xmlns/")); if ((node->type != XML_ELEMENT_NODE) && (node->type != XML_ATTRIBUTE_NODE)) return(NULL); if (node->ns != NULL) - return(CONSTSTR(node->ns->href)); + return(constString(reader, node->ns->href)); return(NULL); } @@ -3292,9 +3432,16 @@ xmlTextReaderConstNamespaceUri(xmlTextReaderPtr reader) { */ xmlChar * xmlTextReaderBaseUri(xmlTextReaderPtr reader) { + xmlChar *ret = NULL; + int result; + if ((reader == NULL) || (reader->node == NULL)) return(NULL); - return(xmlNodeGetBase(NULL, reader->node)); + result = xmlNodeGetBaseSafe(NULL, reader->node, &ret); + if (result < 0) + xmlTextReaderErrMemory(reader); + + return(ret); } /** @@ -3310,13 +3457,16 @@ const xmlChar * xmlTextReaderConstBaseUri(xmlTextReaderPtr reader) { xmlChar *tmp; const xmlChar *ret; + int result; if ((reader == NULL) || (reader->node == NULL)) return(NULL); - tmp = xmlNodeGetBase(NULL, reader->node); + result = xmlNodeGetBaseSafe(NULL, reader->node, &tmp); + if (result < 0) + xmlTextReaderErrMemory(reader); if (tmp == NULL) return(NULL); - ret = CONSTSTR(tmp); + ret = constString(reader, tmp); xmlFree(tmp); return(ret); } @@ -3429,23 +3579,24 @@ xmlTextReaderValue(xmlTextReaderPtr reader) { switch (node->type) { case XML_NAMESPACE_DECL: - return(xmlStrdup(((xmlNsPtr) node)->href)); + return(readerStrdup(reader, ((xmlNsPtr) node)->href)); case XML_ATTRIBUTE_NODE:{ xmlAttrPtr attr = (xmlAttrPtr) node; + xmlDocPtr doc = NULL; + xmlChar *ret; if (attr->parent != NULL) - return (xmlNodeListGetString - (attr->parent->doc, attr->children, 1)); - else - return (xmlNodeListGetString(NULL, attr->children, 1)); - break; + doc = attr->parent->doc; + ret = xmlNodeListGetString(doc, attr->children, 1); + if (ret == NULL) + xmlTextReaderErrMemory(reader); + return(ret); } case XML_TEXT_NODE: case XML_CDATA_SECTION_NODE: case XML_PI_NODE: case XML_COMMENT_NODE: - if (node->content != NULL) - return (xmlStrdup(node->content)); + return(readerStrdup(reader, node->content)); default: break; } @@ -3487,11 +3638,8 @@ xmlTextReaderConstValue(xmlTextReaderPtr reader) { else { if (reader->buffer == NULL) { reader->buffer = xmlBufCreateSize(100); - if (reader->buffer == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlTextReaderSetup : malloc failed\n"); + if (reader->buffer == NULL) return (NULL); - } xmlBufSetAllocationScheme(reader->buffer, XML_BUFFER_ALLOC_DOUBLEIT); } else @@ -3499,12 +3647,12 @@ xmlTextReaderConstValue(xmlTextReaderPtr reader) { xmlBufGetNodeContent(reader->buffer, node); ret = xmlBufContent(reader->buffer); if (ret == NULL) { + xmlTextReaderErrMemory(reader); /* error on the buffer best to reallocate */ xmlBufFree(reader->buffer); reader->buffer = xmlBufCreateSize(100); xmlBufSetAllocationScheme(reader->buffer, XML_BUFFER_ALLOC_DOUBLEIT); - ret = BAD_CAST ""; } return(ret); } @@ -3591,7 +3739,7 @@ xmlTextReaderConstXmlLang(xmlTextReaderPtr reader) { tmp = xmlNodeGetLang(reader->node); if (tmp == NULL) return(NULL); - ret = CONSTSTR(tmp); + ret = constString(reader, tmp); xmlFree(tmp); return(ret); } @@ -3611,7 +3759,7 @@ const xmlChar * xmlTextReaderConstString(xmlTextReaderPtr reader, const xmlChar *str) { if (reader == NULL) return(NULL); - return(CONSTSTR(str)); + return(constString(reader, str)); } /** @@ -3666,18 +3814,21 @@ xmlTextReaderSetParserProp(xmlTextReaderPtr reader, int prop, int value) { if (ctxt->loadsubset == 0) { if (reader->mode != XML_TEXTREADER_MODE_INITIAL) return(-1); - ctxt->loadsubset = XML_DETECT_IDS; + ctxt->options |= XML_PARSE_DTDLOAD; + ctxt->loadsubset |= XML_DETECT_IDS; } } else { - ctxt->loadsubset = 0; + ctxt->options &= ~XML_PARSE_DTDLOAD; + ctxt->loadsubset &= ~XML_DETECT_IDS; } return(0); case XML_PARSER_DEFAULTATTRS: if (value != 0) { + ctxt->options |= XML_PARSE_DTDATTR; ctxt->loadsubset |= XML_COMPLETE_ATTRS; } else { - if (ctxt->loadsubset & XML_COMPLETE_ATTRS) - ctxt->loadsubset -= XML_COMPLETE_ATTRS; + ctxt->options &= ~XML_PARSE_DTDATTR; + ctxt->loadsubset &= ~XML_COMPLETE_ATTRS; } return(0); case XML_PARSER_VALIDATE: @@ -3812,10 +3963,7 @@ xmlTextReaderPreserve(xmlTextReaderPtr reader) { if (reader == NULL) return(NULL); - if (reader->curnode != NULL) - cur = reader->curnode; - else - cur = reader->node; + cur = reader->node; if (cur == NULL) return(NULL); @@ -3865,7 +4013,7 @@ xmlTextReaderPreservePattern(xmlTextReaderPtr reader, const xmlChar *pattern, reader->patternTab = (xmlPatternPtr *) xmlMalloc(reader->patternMax * sizeof(reader->patternTab[0])); if (reader->patternTab == NULL) { - xmlGenericError(xmlGenericErrorContext, "xmlMalloc failed !\n"); + xmlTextReaderErrMemory(reader); return (-1); } } @@ -3876,7 +4024,7 @@ xmlTextReaderPreservePattern(xmlTextReaderPtr reader, const xmlChar *pattern, reader->patternMax * sizeof(reader->patternTab[0])); if (tmp == NULL) { - xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); + xmlTextReaderErrMemory(reader); reader->patternMax /= 2; return (-1); } @@ -3913,80 +4061,6 @@ xmlTextReaderCurrentDoc(xmlTextReaderPtr reader) { } #ifdef LIBXML_SCHEMAS_ENABLED -static char *xmlTextReaderBuildMessage(const char *msg, va_list ap) LIBXML_ATTR_FORMAT(1,0); - -static void -xmlTextReaderValidityError(void *ctxt, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); - -static void -xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); - -static void -xmlTextReaderValidityErrorRelay(void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); - -static void -xmlTextReaderValidityWarningRelay(void *ctx, const char *msg, ...) LIBXML_ATTR_FORMAT(2,3); - -static void -xmlTextReaderValidityErrorRelay(void *ctx, const char *msg, ...) -{ - xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx; - - char *str; - - va_list ap; - - va_start(ap, msg); - str = xmlTextReaderBuildMessage(msg, ap); - if (!reader->errorFunc) { - xmlTextReaderValidityError(ctx, "%s", str); - } else { - reader->errorFunc(reader->errorFuncArg, str, - XML_PARSER_SEVERITY_VALIDITY_ERROR, - NULL /* locator */ ); - } - if (str != NULL) - xmlFree(str); - va_end(ap); -} - -static void -xmlTextReaderValidityWarningRelay(void *ctx, const char *msg, ...) -{ - xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx; - - char *str; - - va_list ap; - - va_start(ap, msg); - str = xmlTextReaderBuildMessage(msg, ap); - if (!reader->errorFunc) { - xmlTextReaderValidityWarning(ctx, "%s", str); - } else { - reader->errorFunc(reader->errorFuncArg, str, - XML_PARSER_SEVERITY_VALIDITY_WARNING, - NULL /* locator */ ); - } - if (str != NULL) - xmlFree(str); - va_end(ap); -} - -static void -xmlTextReaderStructuredError(void *ctxt, const xmlError *error); - -static void -xmlTextReaderValidityStructuredRelay(void *userData, const xmlError *error) -{ - xmlTextReaderPtr reader = (xmlTextReaderPtr) userData; - - if (reader->sErrorFunc) { - reader->sErrorFunc(reader->errorFuncArg, error); - } else { - xmlTextReaderStructuredError(reader, error); - } -} /** * xmlTextReaderRelaxNGSetSchema: * @reader: the xmlTextReaderPtr used @@ -4033,17 +4107,9 @@ xmlTextReaderRelaxNGSetSchema(xmlTextReaderPtr reader, xmlRelaxNGPtr schema) { reader->rngValidCtxt = xmlRelaxNGNewValidCtxt(schema); if (reader->rngValidCtxt == NULL) return(-1); - if (reader->errorFunc != NULL) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } - if (reader->sErrorFunc != NULL) { - xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, + xmlTextReaderStructuredRelay, reader); reader->rngValidErrors = 0; reader->rngFullNode = NULL; reader->validate = XML_TEXTREADER_VALIDATE_RNG; @@ -4175,17 +4241,9 @@ xmlTextReaderSetSchema(xmlTextReaderPtr reader, xmlSchemaPtr schema) { xmlTextReaderLocator, (void *) reader); - if (reader->errorFunc != NULL) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } - if (reader->sErrorFunc != NULL) { - xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, + xmlTextReaderStructuredRelay, reader); reader->xsdValidErrors = 0; reader->validate = XML_TEXTREADER_VALIDATE_XSD; return(0); @@ -4245,21 +4303,14 @@ xmlTextReaderRelaxNGValidateInternal(xmlTextReaderPtr reader, /* Parse the schema and create validation environment. */ pctxt = xmlRelaxNGNewParserCtxt(rng); - if (reader->errorFunc != NULL) { - xmlRelaxNGSetParserErrors(pctxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } - if (reader->sErrorFunc != NULL) { - xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlRelaxNGSetParserStructuredErrors(pctxt, + xmlTextReaderStructuredRelay, reader); reader->rngSchemas = xmlRelaxNGParse(pctxt); xmlRelaxNGFreeParserCtxt(pctxt); if (reader->rngSchemas == NULL) return(-1); + reader->rngValidCtxt = xmlRelaxNGNewValidCtxt(reader->rngSchemas); if (reader->rngValidCtxt == NULL) { xmlRelaxNGFree(reader->rngSchemas); @@ -4277,17 +4328,9 @@ xmlTextReaderRelaxNGValidateInternal(xmlTextReaderPtr reader, * TODO: In case the user provides the validation context we * could make this redirection optional. */ - if (reader->errorFunc != NULL) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } - if (reader->sErrorFunc != NULL) { - xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, + xmlTextReaderStructuredRelay, reader); reader->rngValidErrors = 0; reader->rngFullNode = NULL; reader->validate = XML_TEXTREADER_VALIDATE_RNG; @@ -4350,12 +4393,9 @@ xmlTextReaderSchemaValidateInternal(xmlTextReaderPtr reader, xmlSchemaParserCtxtPtr pctxt; /* Parse the schema and create validation environment. */ pctxt = xmlSchemaNewParserCtxt(xsd); - if (reader->errorFunc != NULL) { - xmlSchemaSetParserErrors(pctxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlSchemaSetParserStructuredErrors(pctxt, + xmlTextReaderStructuredRelay, reader); reader->xsdSchemas = xmlSchemaParse(pctxt); xmlSchemaFreeParserCtxt(pctxt); if (reader->xsdSchemas == NULL) @@ -4398,17 +4438,9 @@ xmlTextReaderSchemaValidateInternal(xmlTextReaderPtr reader, * TODO: In case the user provides the validation context we * could make this redirection optional. */ - if (reader->errorFunc != NULL) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - } - if (reader->sErrorFunc != NULL) { - xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); - } + if ((reader->errorFunc != NULL) || (reader->sErrorFunc != NULL)) + xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, + xmlTextReaderStructuredRelay, reader); reader->xsdValidErrors = 0; reader->validate = XML_TEXTREADER_VALIDATE_XSD; return(0); @@ -4547,7 +4579,7 @@ xmlTextReaderConstXmlVersion(xmlTextReaderPtr reader) { if (doc->version == NULL) return(NULL); else - return(CONSTSTR(doc->version)); + return(constString(reader, doc->version)); } /** @@ -4581,43 +4613,6 @@ xmlTextReaderStandalone(xmlTextReaderPtr reader) { * * ************************************************************************/ -/* helper to build a xmlMalloc'ed string from a format and va_list */ -static char * -xmlTextReaderBuildMessage(const char *msg, va_list ap) { - int size = 0; - int chars; - char *larger; - char *str = NULL; - va_list aq; - - while (1) { - VA_COPY(aq, ap); - chars = vsnprintf(str, size, msg, aq); - va_end(aq); - if (chars < 0) { - xmlGenericError(xmlGenericErrorContext, "vsnprintf failed !\n"); - if (str) - xmlFree(str); - return NULL; - } - if ((chars < size) || (size == MAX_ERR_MSG_SIZE)) - break; - if (chars < MAX_ERR_MSG_SIZE) - size = chars + 1; - else - size = MAX_ERR_MSG_SIZE; - if ((larger = (char *) xmlRealloc(str, size)) == NULL) { - xmlGenericError(xmlGenericErrorContext, "xmlRealloc failed !\n"); - if (str) - xmlFree(str); - return NULL; - } - str = larger; - } - - return str; -} - /** * xmlTextReaderLocatorLineNumber: * @locator: the xmlTextReaderLocatorPtr used @@ -4691,105 +4686,14 @@ xmlTextReaderLocatorBaseURI(xmlTextReaderLocatorPtr locator) { return ret; } -static void -xmlTextReaderGenericError(void *ctxt, xmlParserSeverities severity, - char *str) -{ - xmlParserCtxtPtr ctx = (xmlParserCtxtPtr) ctxt; - - xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx->_private; - - if (str != NULL) { - if (reader->errorFunc) - reader->errorFunc(reader->errorFuncArg, str, severity, - (xmlTextReaderLocatorPtr) ctx); - xmlFree(str); - } -} - -static void -xmlTextReaderStructuredError(void *ctxt, const xmlError *error) -{ - xmlParserCtxtPtr ctx = (xmlParserCtxtPtr) ctxt; - - xmlTextReaderPtr reader = (xmlTextReaderPtr) ctx->_private; - - if (error && reader->sErrorFunc) { - reader->sErrorFunc(reader->errorFuncArg, (xmlErrorPtr) error); - } -} - -static void LIBXML_ATTR_FORMAT(2,3) -xmlTextReaderError(void *ctxt, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - xmlTextReaderGenericError(ctxt, - XML_PARSER_SEVERITY_ERROR, - xmlTextReaderBuildMessage(msg, ap)); - va_end(ap); - -} - -static void LIBXML_ATTR_FORMAT(2,3) -xmlTextReaderWarning(void *ctxt, const char *msg, ...) -{ - va_list ap; - - va_start(ap, msg); - xmlTextReaderGenericError(ctxt, - XML_PARSER_SEVERITY_WARNING, - xmlTextReaderBuildMessage(msg, ap)); - va_end(ap); -} - -static void -xmlTextReaderValidityError(void *ctxt, const char *msg, ...) -{ - va_list ap; - - int len = xmlStrlen((const xmlChar *) msg); - - if ((len > 1) && (msg[len - 2] != ':')) { - /* - * some callbacks only report locator information: - * skip them (mimicking behaviour in error.c) - */ - va_start(ap, msg); - xmlTextReaderGenericError(ctxt, - XML_PARSER_SEVERITY_VALIDITY_ERROR, - xmlTextReaderBuildMessage(msg, ap)); - va_end(ap); - } -} - -static void -xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) -{ - va_list ap; - - int len = xmlStrlen((const xmlChar *) msg); - - if ((len != 0) && (msg[len - 1] != ':')) { - /* - * some callbacks only report locator information: - * skip them (mimicking behaviour in error.c) - */ - va_start(ap, msg); - xmlTextReaderGenericError(ctxt, - XML_PARSER_SEVERITY_VALIDITY_WARNING, - xmlTextReaderBuildMessage(msg, ap)); - va_end(ap); - } -} - /** * xmlTextReaderSetErrorHandler: * @reader: the xmlTextReaderPtr used * @f: the callback function to call on error and warnings * @arg: a user argument to pass to the callback function * + * DEPRECATED: Use xmlTextReaderSetStructuredErrorHandler. + * * Register a callback function that will be called on error and warnings. * * If @f is NULL, the default error and warning handlers are restored. @@ -4799,53 +4703,35 @@ xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader, xmlTextReaderErrorFunc f, void *arg) { if (f != NULL) { - reader->ctxt->sax->error = xmlTextReaderError; - reader->ctxt->sax->serror = NULL; - reader->ctxt->vctxt.error = xmlTextReaderValidityError; - reader->ctxt->sax->warning = xmlTextReaderWarning; - reader->ctxt->vctxt.warning = xmlTextReaderValidityWarning; reader->errorFunc = f; reader->sErrorFunc = NULL; reader->errorFuncArg = arg; + xmlCtxtSetErrorHandler(reader->ctxt, + xmlTextReaderStructuredRelay, reader); #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, - reader); + xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, + xmlTextReaderStructuredRelay, reader); } if (reader->xsdValidCtxt) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, - xmlTextReaderValidityErrorRelay, - xmlTextReaderValidityWarningRelay, - reader); - xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, - reader); + xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, + xmlTextReaderStructuredRelay, reader); } #endif } else { /* restore defaults */ - reader->ctxt->sax->error = xmlParserError; - reader->ctxt->vctxt.error = xmlParserValidityError; - reader->ctxt->sax->warning = xmlParserWarning; - reader->ctxt->vctxt.warning = xmlParserValidityWarning; reader->errorFunc = NULL; reader->sErrorFunc = NULL; reader->errorFuncArg = NULL; + xmlCtxtSetErrorHandler(reader->ctxt, NULL, NULL); #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, - reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, - reader); + NULL); } if (reader->xsdValidCtxt) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, - reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, - reader); + NULL); } #endif } @@ -4866,52 +4752,35 @@ xmlTextReaderSetStructuredErrorHandler(xmlTextReaderPtr reader, xmlStructuredErrorFunc f, void *arg) { if (f != NULL) { - reader->ctxt->sax->error = NULL; - reader->ctxt->sax->serror = xmlTextReaderStructuredError; - reader->ctxt->vctxt.error = xmlTextReaderValidityError; - reader->ctxt->sax->warning = xmlTextReaderWarning; - reader->ctxt->vctxt.warning = xmlTextReaderValidityWarning; reader->sErrorFunc = f; reader->errorFunc = NULL; reader->errorFuncArg = arg; + xmlCtxtSetErrorHandler(reader->ctxt, + xmlTextReaderStructuredRelay, reader); #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, - reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); + xmlTextReaderStructuredRelay, reader); } if (reader->xsdValidCtxt) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, - reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, - xmlTextReaderValidityStructuredRelay, - reader); + xmlTextReaderStructuredRelay, reader); } #endif } else { /* restore defaults */ - reader->ctxt->sax->error = xmlParserError; - reader->ctxt->sax->serror = NULL; - reader->ctxt->vctxt.error = xmlParserValidityError; - reader->ctxt->sax->warning = xmlParserWarning; - reader->ctxt->vctxt.warning = xmlParserValidityWarning; reader->errorFunc = NULL; reader->sErrorFunc = NULL; reader->errorFuncArg = NULL; + xmlCtxtSetErrorHandler(reader->ctxt, NULL, NULL); #ifdef LIBXML_SCHEMAS_ENABLED if (reader->rngValidCtxt) { - xmlRelaxNGSetValidErrors(reader->rngValidCtxt, NULL, NULL, - reader); xmlRelaxNGSetValidStructuredErrors(reader->rngValidCtxt, NULL, - reader); + NULL); } if (reader->xsdValidCtxt) { - xmlSchemaSetValidErrors(reader->xsdValidCtxt, NULL, NULL, - reader); xmlSchemaSetValidStructuredErrors(reader->xsdValidCtxt, NULL, - reader); + NULL); } #endif } @@ -5011,8 +4880,6 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, if (reader->buffer == NULL) reader->buffer = xmlBufCreateSize(100); if (reader->buffer == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlTextReaderSetup : malloc failed\n"); return (-1); } /* no operation on a reader should require a huge buffer */ @@ -5021,8 +4888,6 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, if (reader->sax == NULL) reader->sax = (xmlSAXHandler *) xmlMalloc(sizeof(xmlSAXHandler)); if (reader->sax == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlTextReaderSetup : malloc failed\n"); return (-1); } xmlSAXVersion(reader->sax, 2); @@ -5069,13 +4934,15 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, reader->base = 0; reader->cur = 0; } + if (reader->ctxt == NULL) { + return (-1); + } } else { xmlParserInputPtr inputStream; xmlParserInputBufferPtr buf; - xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; xmlCtxtReset(reader->ctxt); - buf = xmlAllocParserInputBuffer(enc); + buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); if (buf == NULL) return(-1); inputStream = xmlNewInputStream(reader->ctxt); if (inputStream == NULL) { @@ -5094,11 +4961,6 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, inputPush(reader->ctxt, inputStream); reader->cur = 0; } - if (reader->ctxt == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlTextReaderSetup : malloc failed\n"); - return (-1); - } } if (reader->dict != NULL) { if (reader->ctxt->dict != NULL) { @@ -5120,7 +4982,6 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, /* * use the parser dictionary to allocate all elements and attributes names */ - reader->ctxt->docdict = 1; reader->ctxt->parseMode = XML_PARSE_READER; #ifdef LIBXML_XINCLUDE_ENABLED @@ -5131,6 +4992,8 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, if (options & XML_PARSE_XINCLUDE) { reader->xinclude = 1; reader->xinclude_name = xmlDictLookup(reader->dict, XINCLUDE_NODE, -1); + if (reader->xinclude_name == NULL) + return(-1); options -= XML_PARSE_XINCLUDE; } else reader->xinclude = 0; @@ -5154,17 +5017,15 @@ xmlTextReaderSetup(xmlTextReaderPtr reader, reader->validate = XML_TEXTREADER_VALIDATE_DTD; xmlCtxtUseOptions(reader->ctxt, options); - if (encoding != NULL) { - xmlCharEncodingHandlerPtr hdlr; - - hdlr = xmlFindCharEncodingHandler(encoding); - if (hdlr != NULL) - xmlSwitchToEncoding(reader->ctxt, hdlr); - } + if (encoding != NULL) + xmlSwitchEncodingName(reader->ctxt, encoding); if ((URL != NULL) && (reader->ctxt->input != NULL) && - (reader->ctxt->input->filename == NULL)) + (reader->ctxt->input->filename == NULL)) { reader->ctxt->input->filename = (char *) xmlStrdup((const xmlChar *) URL); + if (reader->ctxt->input->filename == NULL) + return(-1); + } reader->doc = NULL; @@ -5184,6 +5045,22 @@ xmlTextReaderSetMaxAmplification(xmlTextReaderPtr reader, unsigned maxAmpl) xmlCtxtSetMaxAmplification(reader->ctxt, maxAmpl); } +/** + * xmlTextReaderGetLastError: + * @reader: an XML reader + * + * Available since 2.13.0. + * + * Returns the last error. + */ +const xmlError * +xmlTextReaderGetLastError(xmlTextReaderPtr reader) +{ + if (reader == NULL) + return(NULL); + return(&reader->ctxt->lastError); +} + /** * xmlTextReaderByteConsumed: * @reader: an XML reader @@ -5223,8 +5100,6 @@ xmlReaderWalker(xmlDocPtr doc) ret = xmlMalloc(sizeof(xmlTextReader)); if (ret == NULL) { - xmlGenericError(xmlGenericErrorContext, - "xmlNewTextReader : malloc failed\n"); return(NULL); } memset(ret, 0, sizeof(xmlTextReader)); @@ -5287,7 +5162,10 @@ xmlReaderForFile(const char *filename, const char *encoding, int options) reader = xmlNewTextReaderFilename(filename); if (reader == NULL) return (NULL); - xmlTextReaderSetup(reader, NULL, NULL, encoding, options); + if (xmlTextReaderSetup(reader, NULL, NULL, encoding, options) < 0) { + xmlFreeTextReader(reader); + return (NULL); + } return (reader); } @@ -5321,7 +5199,10 @@ xmlReaderForMemory(const char *buffer, int size, const char *URL, return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; - xmlTextReaderSetup(reader, NULL, URL, encoding, options); + if (xmlTextReaderSetup(reader, NULL, URL, encoding, options) < 0) { + xmlFreeTextReader(reader); + return (NULL); + } return (reader); } @@ -5358,7 +5239,10 @@ xmlReaderForFd(int fd, const char *URL, const char *encoding, int options) return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; - xmlTextReaderSetup(reader, NULL, URL, encoding, options); + if (xmlTextReaderSetup(reader, NULL, URL, encoding, options) < 0) { + xmlFreeTextReader(reader); + return (NULL); + } return (reader); } @@ -5400,7 +5284,10 @@ xmlReaderForIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose, return (NULL); } reader->allocs |= XML_TEXTREADER_INPUT; - xmlTextReaderSetup(reader, NULL, URL, encoding, options); + if (xmlTextReaderSetup(reader, NULL, URL, encoding, options) < 0) { + xmlFreeTextReader(reader); + return (NULL); + } return (reader); } diff --git a/libraries/libxml2/xmlsave.c b/libraries/libxml2/xmlsave.c index 125853ff..16b71d0a 100644 --- a/libraries/libxml2/xmlsave.c +++ b/libraries/libxml2/xmlsave.c @@ -9,6 +9,8 @@ #define IN_LIBXML #include "libxml.h" +#include +#include #include #include #include @@ -22,17 +24,13 @@ #include "private/buf.h" #include "private/enc.h" #include "private/error.h" +#include "private/io.h" #include "private/save.h" #ifdef LIBXML_OUTPUT_ENABLED #define XHTML_NS_NAME BAD_CAST "http://www.w3.org/1999/xhtml" -#define TODO \ - xmlGenericError(xmlGenericErrorContext, \ - "Unimplemented block at %s:%d\n", \ - __FILE__, __LINE__); - struct _xmlSaveCtxt { void *_private; int type; @@ -63,9 +61,11 @@ struct _xmlSaveCtxt { * Handle an out of memory condition */ static void -xmlSaveErrMemory(const char *extra) +xmlSaveErrMemory(xmlOutputBufferPtr out) { - __xmlSimpleError(XML_FROM_OUTPUT, XML_ERR_NO_MEMORY, NULL, NULL, extra); + if (out != NULL) + out->error = XML_ERR_NO_MEMORY; + xmlRaiseMemoryError(NULL, NULL, NULL, XML_FROM_OUTPUT, NULL); } /** @@ -77,9 +77,23 @@ xmlSaveErrMemory(const char *extra) * Handle an out of memory condition */ static void -xmlSaveErr(int code, xmlNodePtr node, const char *extra) +xmlSaveErr(xmlOutputBufferPtr out, int code, xmlNodePtr node, + const char *extra) { const char *msg = NULL; + int res; + + /* Don't overwrite memory errors */ + if ((out != NULL) && (out->error == XML_ERR_NO_MEMORY)) + return; + + if (code == XML_ERR_NO_MEMORY) { + xmlSaveErrMemory(out); + return; + } + + if (out != NULL) + out->error = code; switch(code) { case XML_SAVE_NOT_UTF8: @@ -97,7 +111,13 @@ xmlSaveErr(int code, xmlNodePtr node, const char *extra) default: msg = "unexpected error number\n"; } - __xmlSimpleError(XML_FROM_OUTPUT, code, node, msg, extra); + + res = __xmlRaiseError(NULL, NULL, NULL, NULL, node, + XML_FROM_OUTPUT, code, XML_ERR_ERROR, NULL, 0, + extra, NULL, NULL, 0, 0, + msg, extra); + if (res < 0) + xmlSaveErrMemory(out); } /************************************************************************ @@ -198,79 +218,52 @@ xmlEscapeEntities(unsigned char* out, int *outlen, *out++ = ';'; in++; continue; + } else if (*in == 0xD) { + if (outend - out < 5) break; + *out++ = '&'; + *out++ = '#'; + *out++ = 'x'; + *out++ = 'D'; + *out++ = ';'; + in++; } else if (((*in >= 0x20) && (*in < 0x80)) || - (*in == '\n') || (*in == '\t')) { + (*in == 0xA) || (*in == 0x9)) { /* * default case, just copy ! */ *out++ = *in++; continue; - } else if (*in >= 0x80) { - /* - * We assume we have UTF-8 input. - */ + } else if (*in < 0x80) { + /* invalid control char */ + if (outend - out < 8) break; + out = xmlSerializeHexCharRef(out, 0xFFFD); + in++; + } else { + int len; + if (outend - out < 11) break; - if (*in < 0xC0) { - xmlSaveErr(XML_SAVE_NOT_UTF8, NULL, NULL); - in++; - goto error; - } else if (*in < 0xE0) { - if (inend - in < 2) break; - val = (in[0]) & 0x1F; - val <<= 6; - val |= (in[1]) & 0x3F; - in += 2; - } else if (*in < 0xF0) { - if (inend - in < 3) break; - val = (in[0]) & 0x0F; - val <<= 6; - val |= (in[1]) & 0x3F; - val <<= 6; - val |= (in[2]) & 0x3F; - in += 3; - } else if (*in < 0xF8) { - if (inend - in < 4) break; - val = (in[0]) & 0x07; - val <<= 6; - val |= (in[1]) & 0x3F; - val <<= 6; - val |= (in[2]) & 0x3F; - val <<= 6; - val |= (in[3]) & 0x3F; - in += 4; - } else { - xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); - in++; - goto error; - } - if (!IS_CHAR(val)) { - xmlSaveErr(XML_SAVE_CHAR_INVALID, NULL, NULL); - in++; - goto error; - } + len = inend - in; + val = xmlGetUTF8Char(in, &len); + + if (val < 0) { + val = 0xFFFD; + in++; + } else { + if (!IS_CHAR(val)) + val = 0xFFFD; + in += len; + } /* * We could do multiple things here. Just save as a char ref */ out = xmlSerializeHexCharRef(out, val); - } else if (IS_BYTE_CHAR(*in)) { - if (outend - out < 6) break; - out = xmlSerializeHexCharRef(out, *in++); - } else { - xmlGenericError(xmlGenericErrorContext, - "xmlEscapeEntities : char out of range\n"); - in++; - goto error; } } *outlen = out - outstart; *inlen = in - base; return(0); -error: - *outlen = out - outstart; - *inlen = in - base; - return(-1); } /************************************************************************ @@ -340,15 +333,18 @@ xmlNewSaveCtxt(const char *encoding, int options) ret = (xmlSaveCtxtPtr) xmlMalloc(sizeof(xmlSaveCtxt)); if (ret == NULL) { - xmlSaveErrMemory("creating saving context"); + xmlSaveErrMemory(NULL); return ( NULL ); } memset(ret, 0, sizeof(xmlSaveCtxt)); if (encoding != NULL) { - ret->handler = xmlFindCharEncodingHandler(encoding); - if (ret->handler == NULL) { - xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, encoding); + int res; + + res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, + &ret->handler); + if (res != XML_ERR_OK) { + xmlSaveErr(NULL, res, NULL, encoding); xmlFreeSaveCtxt(ret); return(NULL); } @@ -397,14 +393,13 @@ xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr) while (children != NULL) { switch (children->type) { case XML_TEXT_NODE: - xmlBufAttrSerializeTxtContent(buf->buffer, attr->doc, - attr, children->content); + xmlBufAttrSerializeTxtContent(buf, attr->doc, + children->content); break; case XML_ENTITY_REF_NODE: - xmlBufAdd(buf->buffer, BAD_CAST "&", 1); - xmlBufAdd(buf->buffer, children->name, - xmlStrlen(children->name)); - xmlBufAdd(buf->buffer, BAD_CAST ";", 1); + xmlOutputBufferWrite(buf, 1, "&"); + xmlOutputBufferWriteString(buf, (const char *) children->name); + xmlOutputBufferWrite(buf, 1, ";"); break; default: /* should not happen unless we have a badly built tree */ @@ -414,6 +409,46 @@ xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr) } } +/** + * xmlBufDumpNotationDecl: + * @buf: the XML buffer output + * @nota: A notation declaration + * + * This will dump the content the notation declaration as an XML DTD definition + */ +static void +xmlBufDumpNotationDecl(xmlOutputBufferPtr buf, xmlNotationPtr nota) { + xmlOutputBufferWrite(buf, 11, "name); + + if (nota->PublicID != NULL) { + xmlOutputBufferWrite(buf, 8, " PUBLIC "); + xmlOutputBufferWriteQuotedString(buf, nota->PublicID); + if (nota->SystemID != NULL) { + xmlOutputBufferWrite(buf, 1, " "); + xmlOutputBufferWriteQuotedString(buf, nota->SystemID); + } + } else { + xmlOutputBufferWrite(buf, 8, " SYSTEM "); + xmlOutputBufferWriteQuotedString(buf, nota->SystemID); + } + + xmlOutputBufferWrite(buf, 3, " >\n"); +} + +/** + * xmlBufDumpNotationDeclScan: + * @nota: A notation declaration + * @buf: the XML buffer output + * + * This is called with the hash scan function, and just reverses args + */ +static void +xmlBufDumpNotationDeclScan(void *nota, void *buf, + const xmlChar *name ATTRIBUTE_UNUSED) { + xmlBufDumpNotationDecl((xmlOutputBufferPtr) buf, (xmlNotationPtr) nota); +} + /** * xmlBufDumpNotationTable: * @buf: an xmlBufPtr output @@ -422,19 +457,105 @@ xmlAttrSerializeContent(xmlOutputBufferPtr buf, xmlAttrPtr attr) * This will dump the content of the notation table as an XML DTD definition */ static void -xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) { - xmlBufferPtr buffer; - - buffer = xmlBufferCreate(); - if (buffer == NULL) { - /* - * TODO set the error in buf - */ - return; +xmlBufDumpNotationTable(xmlOutputBufferPtr buf, xmlNotationTablePtr table) { + xmlHashScan(table, xmlBufDumpNotationDeclScan, buf); +} + +/** + * xmlBufDumpElementOccur: + * @buf: output buffer + * @cur: element table + * + * Dump the occurrence operator of an element. + */ +static void +xmlBufDumpElementOccur(xmlOutputBufferPtr buf, xmlElementContentPtr cur) { + switch (cur->ocur) { + case XML_ELEMENT_CONTENT_ONCE: + break; + case XML_ELEMENT_CONTENT_OPT: + xmlOutputBufferWrite(buf, 1, "?"); + break; + case XML_ELEMENT_CONTENT_MULT: + xmlOutputBufferWrite(buf, 1, "*"); + break; + case XML_ELEMENT_CONTENT_PLUS: + xmlOutputBufferWrite(buf, 1, "+"); + break; } - xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT); - xmlDumpNotationTable(buffer, table); - xmlBufMergeBuffer(buf, buffer); +} + +/** + * xmlBufDumpElementContent: + * @buf: output buffer + * @content: element table + * + * This will dump the content of the element table as an XML DTD definition + */ +static void +xmlBufDumpElementContent(xmlOutputBufferPtr buf, + xmlElementContentPtr content) { + xmlElementContentPtr cur; + + if (content == NULL) return; + + xmlOutputBufferWrite(buf, 1, "("); + cur = content; + + do { + if (cur == NULL) return; + + switch (cur->type) { + case XML_ELEMENT_CONTENT_PCDATA: + xmlOutputBufferWrite(buf, 7, "#PCDATA"); + break; + case XML_ELEMENT_CONTENT_ELEMENT: + if (cur->prefix != NULL) { + xmlOutputBufferWriteString(buf, + (const char *) cur->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + xmlOutputBufferWriteString(buf, (const char *) cur->name); + break; + case XML_ELEMENT_CONTENT_SEQ: + case XML_ELEMENT_CONTENT_OR: + if ((cur != content) && + (cur->parent != NULL) && + ((cur->type != cur->parent->type) || + (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) + xmlOutputBufferWrite(buf, 1, "("); + cur = cur->c1; + continue; + } + + while (cur != content) { + xmlElementContentPtr parent = cur->parent; + + if (parent == NULL) return; + + if (((cur->type == XML_ELEMENT_CONTENT_OR) || + (cur->type == XML_ELEMENT_CONTENT_SEQ)) && + ((cur->type != parent->type) || + (cur->ocur != XML_ELEMENT_CONTENT_ONCE))) + xmlOutputBufferWrite(buf, 1, ")"); + xmlBufDumpElementOccur(buf, cur); + + if (cur == parent->c1) { + if (parent->type == XML_ELEMENT_CONTENT_SEQ) + xmlOutputBufferWrite(buf, 3, " , "); + else if (parent->type == XML_ELEMENT_CONTENT_OR) + xmlOutputBufferWrite(buf, 3, " | "); + + cur = parent->c2; + break; + } + + cur = parent; + } + } while (cur != content); + + xmlOutputBufferWrite(buf, 1, ")"); + xmlBufDumpElementOccur(buf, content); } /** @@ -446,43 +567,173 @@ xmlBufDumpNotationTable(xmlBufPtr buf, xmlNotationTablePtr table) { * DTD definition */ static void -xmlBufDumpElementDecl(xmlBufPtr buf, xmlElementPtr elem) { - xmlBufferPtr buffer; - - buffer = xmlBufferCreate(); - if (buffer == NULL) { - /* - * TODO set the error in buf - */ - return; +xmlBufDumpElementDecl(xmlOutputBufferPtr buf, xmlElementPtr elem) { + xmlOutputBufferWrite(buf, 10, "prefix != NULL) { + xmlOutputBufferWriteString(buf, (const char *) elem->prefix); + xmlOutputBufferWrite(buf, 1, ":"); } - xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT); - xmlDumpElementDecl(buffer, elem); - xmlBufMergeBuffer(buf, buffer); + xmlOutputBufferWriteString(buf, (const char *) elem->name); + xmlOutputBufferWrite(buf, 1, " "); + + switch (elem->etype) { + case XML_ELEMENT_TYPE_EMPTY: + xmlOutputBufferWrite(buf, 5, "EMPTY"); + break; + case XML_ELEMENT_TYPE_ANY: + xmlOutputBufferWrite(buf, 3, "ANY"); + break; + case XML_ELEMENT_TYPE_MIXED: + case XML_ELEMENT_TYPE_ELEMENT: + xmlBufDumpElementContent(buf, elem->content); + break; + default: + /* assert(0); */ + break; + } + + xmlOutputBufferWrite(buf, 2, ">\n"); } +/** + * xmlBufDumpEnumeration: + * @buf: output buffer + * @enum: An enumeration + * + * This will dump the content of the enumeration + */ +static void +xmlBufDumpEnumeration(xmlOutputBufferPtr buf, xmlEnumerationPtr cur) { + while (cur != NULL) { + xmlOutputBufferWriteString(buf, (const char *) cur->name); + if (cur->next != NULL) + xmlOutputBufferWrite(buf, 3, " | "); + + cur = cur->next; + } + + xmlOutputBufferWrite(buf, 1, ")"); +} /** * xmlBufDumpAttributeDecl: - * @buf: an xmlBufPtr output + * @buf: output buffer * @attr: An attribute declaration * * This will dump the content of the attribute declaration as an XML * DTD definition */ static void -xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) { - xmlBufferPtr buffer; - - buffer = xmlBufferCreate(); - if (buffer == NULL) { - /* - * TODO set the error in buf - */ - return; +xmlBufDumpAttributeDecl(xmlOutputBufferPtr buf, xmlAttributePtr attr) { + xmlOutputBufferWrite(buf, 10, "elem); + xmlOutputBufferWrite(buf, 1, " "); + if (attr->prefix != NULL) { + xmlOutputBufferWriteString(buf, (const char *) attr->prefix); + xmlOutputBufferWrite(buf, 1, ":"); + } + xmlOutputBufferWriteString(buf, (const char *) attr->name); + + switch (attr->atype) { + case XML_ATTRIBUTE_CDATA: + xmlOutputBufferWrite(buf, 6, " CDATA"); + break; + case XML_ATTRIBUTE_ID: + xmlOutputBufferWrite(buf, 3, " ID"); + break; + case XML_ATTRIBUTE_IDREF: + xmlOutputBufferWrite(buf, 6, " IDREF"); + break; + case XML_ATTRIBUTE_IDREFS: + xmlOutputBufferWrite(buf, 7, " IDREFS"); + break; + case XML_ATTRIBUTE_ENTITY: + xmlOutputBufferWrite(buf, 7, " ENTITY"); + break; + case XML_ATTRIBUTE_ENTITIES: + xmlOutputBufferWrite(buf, 9, " ENTITIES"); + break; + case XML_ATTRIBUTE_NMTOKEN: + xmlOutputBufferWrite(buf, 8, " NMTOKEN"); + break; + case XML_ATTRIBUTE_NMTOKENS: + xmlOutputBufferWrite(buf, 9, " NMTOKENS"); + break; + case XML_ATTRIBUTE_ENUMERATION: + xmlOutputBufferWrite(buf, 2, " ("); + xmlBufDumpEnumeration(buf, attr->tree); + break; + case XML_ATTRIBUTE_NOTATION: + xmlOutputBufferWrite(buf, 11, " NOTATION ("); + xmlBufDumpEnumeration(buf, attr->tree); + break; + default: + /* assert(0); */ + break; + } + + switch (attr->def) { + case XML_ATTRIBUTE_NONE: + break; + case XML_ATTRIBUTE_REQUIRED: + xmlOutputBufferWrite(buf, 10, " #REQUIRED"); + break; + case XML_ATTRIBUTE_IMPLIED: + xmlOutputBufferWrite(buf, 9, " #IMPLIED"); + break; + case XML_ATTRIBUTE_FIXED: + xmlOutputBufferWrite(buf, 7, " #FIXED"); + break; + default: + /* assert(0); */ + break; + } + + if (attr->defaultValue != NULL) { + xmlOutputBufferWrite(buf, 1, " "); + xmlOutputBufferWriteQuotedString(buf, attr->defaultValue); + } + + xmlOutputBufferWrite(buf, 2, ">\n"); +} + +/** + * xmlBufDumpEntityContent: + * @buf: output buffer + * @content: entity content. + * + * This will dump the quoted string value, taking care of the special + * treatment required by % + */ +static void +xmlBufDumpEntityContent(xmlOutputBufferPtr buf, const xmlChar *content) { + if (xmlStrchr(content, '%')) { + const char * base, *cur; + + xmlOutputBufferWrite(buf, 1, "\""); + base = cur = (const char *) content; + while (*cur != 0) { + if (*cur == '"') { + if (base != cur) + xmlOutputBufferWrite(buf, cur - base, base); + xmlOutputBufferWrite(buf, 6, """); + cur++; + base = cur; + } else if (*cur == '%') { + if (base != cur) + xmlOutputBufferWrite(buf, cur - base, base); + xmlOutputBufferWrite(buf, 6, "%"); + cur++; + base = cur; + } else { + cur++; + } + } + if (base != cur) + xmlOutputBufferWrite(buf, cur - base, base); + xmlOutputBufferWrite(buf, 1, "\""); + } else { + xmlOutputBufferWriteQuotedString(buf, content); } - xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT); - xmlDumpAttributeDecl(buffer, attr); - xmlBufMergeBuffer(buf, buffer); } /** @@ -493,19 +744,47 @@ xmlBufDumpAttributeDecl(xmlBufPtr buf, xmlAttributePtr attr) { * This will dump the content of the entity table as an XML DTD definition */ static void -xmlBufDumpEntityDecl(xmlBufPtr buf, xmlEntityPtr ent) { - xmlBufferPtr buffer; - - buffer = xmlBufferCreate(); - if (buffer == NULL) { - /* - * TODO set the error in buf - */ - return; +xmlBufDumpEntityDecl(xmlOutputBufferPtr buf, xmlEntityPtr ent) { + if ((ent->etype == XML_INTERNAL_PARAMETER_ENTITY) || + (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) + xmlOutputBufferWrite(buf, 11, "name); + xmlOutputBufferWrite(buf, 1, " "); + + if ((ent->etype == XML_EXTERNAL_GENERAL_PARSED_ENTITY) || + (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) || + (ent->etype == XML_EXTERNAL_PARAMETER_ENTITY)) { + if (ent->ExternalID != NULL) { + xmlOutputBufferWrite(buf, 7, "PUBLIC "); + xmlOutputBufferWriteQuotedString(buf, ent->ExternalID); + xmlOutputBufferWrite(buf, 1, " "); + } else { + xmlOutputBufferWrite(buf, 7, "SYSTEM "); + } + xmlOutputBufferWriteQuotedString(buf, ent->SystemID); + } + + if (ent->etype == XML_EXTERNAL_GENERAL_UNPARSED_ENTITY) { + if (ent->content != NULL) { /* Should be true ! */ + xmlOutputBufferWrite(buf, 7, " NDATA "); + if (ent->orig != NULL) + xmlOutputBufferWriteString(buf, (const char *) ent->orig); + else + xmlOutputBufferWriteString(buf, (const char *) ent->content); + } + } + + if ((ent->etype == XML_INTERNAL_GENERAL_ENTITY) || + (ent->etype == XML_INTERNAL_PARAMETER_ENTITY)) { + if (ent->orig != NULL) + xmlOutputBufferWriteQuotedString(buf, ent->orig); + else + xmlBufDumpEntityContent(buf, ent->content); } - xmlBufferSetAllocationScheme(buffer, XML_BUFFER_ALLOC_DOUBLEIT); - xmlDumpEntityDecl(buffer, ent); - xmlBufMergeBuffer(buf, buffer); + + xmlOutputBufferWrite(buf, 2, ">\n"); } /************************************************************************ @@ -518,18 +797,21 @@ static int xmlSaveSwitchEncoding(xmlSaveCtxtPtr ctxt, const char *encoding) { xmlOutputBufferPtr buf = ctxt->buf; if ((encoding != NULL) && (buf->encoder == NULL) && (buf->conv == NULL)) { - buf->encoder = xmlFindCharEncodingHandler((const char *)encoding); - if (buf->encoder == NULL) { - xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, NULL, - (const char *)encoding); - return(-1); - } + xmlCharEncodingHandler *handler; + int res; + + res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler); + if (res != XML_ERR_OK) { + xmlSaveErr(buf, res, NULL, encoding); + return(-1); + } buf->conv = xmlBufCreate(); if (buf->conv == NULL) { - xmlCharEncCloseFunc(buf->encoder); - xmlSaveErrMemory("creating encoding buffer"); + xmlCharEncCloseFunc(handler); + xmlSaveErrMemory(buf); return(-1); } + buf->encoder = handler; /* * initialize the state, e.g. if outputting a BOM */ @@ -588,7 +870,8 @@ xmlOutputBufferWriteWSNonSig(xmlSaveCtxtPtr ctxt, int extra) * If @ctxt is supplied, @buf should be its buffer. */ static void -xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) { +xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNsPtr cur, + xmlSaveCtxtPtr ctxt) { if ((cur == NULL) || (buf == NULL)) return; if ((cur->type == XML_LOCAL_NAMESPACE) && (cur->href != NULL)) { if (xmlStrEqual(cur->prefix, BAD_CAST "xml")) @@ -605,24 +888,12 @@ xmlNsDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur, xmlSaveCtxtPtr ctxt) { xmlOutputBufferWriteString(buf, (const char *)cur->prefix); } else xmlOutputBufferWrite(buf, 5, "xmlns"); - xmlOutputBufferWrite(buf, 1, "="); - xmlBufWriteQuotedString(buf->buffer, cur->href); + xmlOutputBufferWrite(buf, 2, "=\""); + xmlBufAttrSerializeTxtContent(buf, doc, cur->href); + xmlOutputBufferWrite(buf, 1, "\""); } } -/** - * xmlNsDumpOutputCtxt - * @ctxt: the save context - * @cur: a namespace - * - * Dump a local Namespace definition to a save context. - * Should be called in the context of attribute dumps. - */ -static void -xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) { - xmlNsDumpOutput(ctxt->buf, cur, ctxt); -} - /** * xmlNsListDumpOutputCtxt * @ctxt: the save context @@ -632,9 +903,9 @@ xmlNsDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) { * Should be called in the context of attribute dumps. */ static void -xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) { +xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlDocPtr doc, xmlNsPtr cur) { while (cur != NULL) { - xmlNsDumpOutput(ctxt->buf, cur, ctxt); + xmlNsDumpOutput(ctxt->buf, doc, cur, ctxt); cur = cur->next; } } @@ -650,7 +921,7 @@ xmlNsListDumpOutputCtxt(xmlSaveCtxtPtr ctxt, xmlNsPtr cur) { void xmlNsListDumpOutput(xmlOutputBufferPtr buf, xmlNsPtr cur) { while (cur != NULL) { - xmlNsDumpOutput(buf, cur, NULL); + xmlNsDumpOutput(buf, NULL, cur, NULL); cur = cur->next; } } @@ -676,12 +947,12 @@ xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) { xmlOutputBufferWriteString(buf, (const char *)dtd->name); if (dtd->ExternalID != NULL) { xmlOutputBufferWrite(buf, 8, " PUBLIC "); - xmlBufWriteQuotedString(buf->buffer, dtd->ExternalID); + xmlOutputBufferWriteQuotedString(buf, dtd->ExternalID); xmlOutputBufferWrite(buf, 1, " "); - xmlBufWriteQuotedString(buf->buffer, dtd->SystemID); + xmlOutputBufferWriteQuotedString(buf, dtd->SystemID); } else if (dtd->SystemID != NULL) { xmlOutputBufferWrite(buf, 8, " SYSTEM "); - xmlBufWriteQuotedString(buf->buffer, dtd->SystemID); + xmlOutputBufferWriteQuotedString(buf, dtd->SystemID); } if ((dtd->entities == NULL) && (dtd->elements == NULL) && (dtd->attributes == NULL) && (dtd->notations == NULL) && @@ -696,8 +967,7 @@ xmlDtdDumpOutput(xmlSaveCtxtPtr ctxt, xmlDtdPtr dtd) { */ if ((dtd->notations != NULL) && ((dtd->doc == NULL) || (dtd->doc->intSubset == dtd))) { - xmlBufDumpNotationTable(buf->buffer, - (xmlNotationTablePtr) dtd->notations); + xmlBufDumpNotationTable(buf, (xmlNotationTablePtr) dtd->notations); } format = ctxt->format; level = ctxt->level; @@ -735,7 +1005,19 @@ xmlAttrDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { } xmlOutputBufferWriteString(buf, (const char *)cur->name); xmlOutputBufferWrite(buf, 2, "=\""); - xmlAttrSerializeContent(buf, cur); +#ifdef LIBXML_HTML_ENABLED + if ((ctxt->options & XML_SAVE_XHTML) && + (cur->ns == NULL) && + ((cur->children == NULL) || + (cur->children->content == NULL) || + (cur->children->content[0] == 0)) && + (htmlIsBooleanAttr(cur->name))) { + xmlOutputBufferWriteString(buf, (const char *) cur->name); + } else +#endif + { + xmlAttrSerializeContent(buf, cur); + } xmlOutputBufferWrite(buf, 1, "\""); } @@ -839,15 +1121,15 @@ xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { break; case XML_ELEMENT_DECL: - xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur); + xmlBufDumpElementDecl(buf, (xmlElementPtr) cur); break; case XML_ATTRIBUTE_DECL: - xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur); + xmlBufDumpAttributeDecl(buf, (xmlAttributePtr) cur); break; case XML_ENTITY_DECL: - xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur); + xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur); break; case XML_ELEMENT_NODE: @@ -875,7 +1157,7 @@ xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { } xmlOutputBufferWriteString(buf, (const char *)cur->name); if (cur->nsDef) - xmlNsListDumpOutputCtxt(ctxt, cur->nsDef); + xmlNsListDumpOutputCtxt(ctxt, cur->doc, cur->nsDef); for (attr = cur->properties; attr != NULL; attr = attr->next) xmlAttrDumpOutput(ctxt, attr); @@ -1015,7 +1297,7 @@ xmlNodeDumpOutputInternal(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { break; case XML_NAMESPACE_DECL: - xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur); + xmlNsDumpOutput(buf, NULL, (xmlNsPtr) cur, ctxt); break; default: @@ -1164,12 +1446,12 @@ xmlDocContentDumpOutput(xmlSaveCtxtPtr ctxt, xmlDocPtr cur) { if ((ctxt->options & XML_SAVE_NO_DECL) == 0) { xmlOutputBufferWrite(buf, 14, "version != NULL) - xmlBufWriteQuotedString(buf->buffer, cur->version); + xmlOutputBufferWriteQuotedString(buf, cur->version); else xmlOutputBufferWrite(buf, 5, "\"1.0\""); if (encoding != NULL) { xmlOutputBufferWrite(buf, 10, " encoding="); - xmlBufWriteQuotedString(buf->buffer, (xmlChar *) encoding); + xmlOutputBufferWriteQuotedString(buf, (xmlChar *) encoding); } switch (cur->standalone) { case 0: @@ -1249,7 +1531,7 @@ xhtmlIsEmpty(xmlNodePtr node) { return(0); if (node->children != NULL) return(0); - switch (node->name[0]) { + switch (node->name ? node->name[0] : 0) { case 'a': if (xmlStrEqual(node->name, BAD_CAST "area")) return(1); @@ -1329,17 +1611,6 @@ xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { if ((cur->ns != NULL) && (xmlStrEqual(cur->name, BAD_CAST "lang")) && (xmlStrEqual(cur->ns->prefix, BAD_CAST "xml"))) xml_lang = cur; - else if ((cur->ns == NULL) && - ((cur->children == NULL) || - (cur->children->content == NULL) || - (cur->children->content[0] == 0)) && - (htmlIsBooleanAttr(cur->name))) { - if (cur->children != NULL) - xmlFreeNode(cur->children); - cur->children = xmlNewDocText(cur->doc, cur->name); - if (cur->children != NULL) - cur->children->parent = (xmlNodePtr) cur; - } xmlAttrDumpOutput(ctxt, cur); cur = cur->next; } @@ -1390,14 +1661,18 @@ xhtmlAttrListDumpOutput(xmlSaveCtxtPtr ctxt, xmlAttrPtr cur) { */ static void xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { - int format = ctxt->format, addmeta; - xmlNodePtr tmp, root, unformattedNode = NULL; + int format = ctxt->format, addmeta, oldoptions; + xmlNodePtr tmp, root, unformattedNode = NULL, parent; xmlChar *start, *end; xmlOutputBufferPtr buf = ctxt->buf; if (cur == NULL) return; + oldoptions = ctxt->options; + ctxt->options |= XML_SAVE_XHTML; + root = cur; + parent = cur->parent; while (1) { switch (cur->type) { case XML_DOCUMENT_NODE: @@ -1406,7 +1681,7 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { break; case XML_NAMESPACE_DECL: - xmlNsDumpOutputCtxt(ctxt, (xmlNsPtr) cur); + xmlNsDumpOutput(buf, NULL, (xmlNsPtr) cur, ctxt); break; case XML_DTD_NODE: @@ -1414,22 +1689,24 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { break; case XML_DOCUMENT_FRAG_NODE: - if (cur->children) { + /* Always validate cur->parent when descending. */ + if ((cur->parent == parent) && (cur->children != NULL)) { + parent = cur; cur = cur->children; continue; } break; case XML_ELEMENT_DECL: - xmlBufDumpElementDecl(buf->buffer, (xmlElementPtr) cur); + xmlBufDumpElementDecl(buf, (xmlElementPtr) cur); break; case XML_ATTRIBUTE_DECL: - xmlBufDumpAttributeDecl(buf->buffer, (xmlAttributePtr) cur); + xmlBufDumpAttributeDecl(buf, (xmlAttributePtr) cur); break; case XML_ENTITY_DECL: - xmlBufDumpEntityDecl(buf->buffer, (xmlEntityPtr) cur); + xmlBufDumpEntityDecl(buf, (xmlEntityPtr) cur); break; case XML_ELEMENT_NODE: @@ -1441,6 +1718,16 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { ctxt->indent_nr : ctxt->level), ctxt->indent); + /* + * Some users like lxml are known to pass nodes with a corrupted + * tree structure. Fall back to a recursive call to handle this + * case. + */ + if ((cur->parent != parent) && (cur->children != NULL)) { + xhtmlNodeDumpOutput(ctxt, cur); + break; + } + xmlOutputBufferWrite(buf, 1, "<"); if ((cur->ns != NULL) && (cur->ns->prefix != NULL)) { xmlOutputBufferWriteString(buf, (const char *)cur->ns->prefix); @@ -1449,7 +1736,7 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { xmlOutputBufferWriteString(buf, (const char *)cur->name); if (cur->nsDef) - xmlNsListDumpOutputCtxt(ctxt, cur->nsDef); + xmlNsListDumpOutputCtxt(ctxt, cur->doc, cur->nsDef); if ((xmlStrEqual(cur->name, BAD_CAST "html") && (cur->ns == NULL) && (cur->nsDef == NULL))) { /* @@ -1461,10 +1748,10 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { if (cur->properties != NULL) xhtmlAttrListDumpOutput(ctxt, cur->properties); - if ((cur->parent != NULL) && - (cur->parent->parent == (xmlNodePtr) cur->doc) && + if ((parent != NULL) && + (parent->parent == (xmlNodePtr) cur->doc) && xmlStrEqual(cur->name, BAD_CAST"head") && - xmlStrEqual(cur->parent->name, BAD_CAST"html")) { + xmlStrEqual(parent->name, BAD_CAST"html")) { tmp = cur->children; while (tmp != NULL) { @@ -1570,6 +1857,7 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { if (ctxt->format == 1) xmlOutputBufferWrite(buf, 1, "\n"); if (ctxt->level >= 0) ctxt->level++; + parent = cur; cur = cur->children; continue; } @@ -1664,13 +1952,9 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { break; } - /* - * The parent should never be NULL here but we want to handle - * corrupted documents gracefully. - */ - if (cur->parent == NULL) - return; - cur = cur->parent; + cur = parent; + /* cur->parent was validated when descending. */ + parent = cur->parent; if (cur->type == XML_ELEMENT_NODE) { if (ctxt->level > 0) ctxt->level--; @@ -1697,6 +1981,8 @@ xhtmlNodeDumpOutput(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) { } } } + + ctxt->options = oldoptions; } #endif @@ -1878,6 +2164,40 @@ xmlSaveTree(xmlSaveCtxtPtr ctxt, xmlNodePtr cur) return(ret); } +/** + * xmlSaveNotationDecl: + * @ctxt: save context + * @cur: notation + * + * Serialize a notation declaration. + * + * Return 0 on succes, -1 on error. + */ +int +xmlSaveNotationDecl(xmlSaveCtxtPtr ctxt, xmlNotationPtr cur) { + if (ctxt == NULL) + return(-1); + xmlBufDumpNotationDecl(ctxt->buf, cur); + return(0); +} + +/** + * xmlSaveNotationTable: + * @ctxt: save context + * @cur: notation table + * + * Serialize notation declarations of a document. + * + * Return 0 on succes, -1 on error. + */ +int +xmlSaveNotationTable(xmlSaveCtxtPtr ctxt, xmlNotationTablePtr cur) { + if (ctxt == NULL) + return(-1); + xmlBufDumpNotationTable(ctxt->buf, cur); + return(0); +} + /** * xmlSaveFlush: * @ctxt: a document saving context @@ -1915,6 +2235,30 @@ xmlSaveClose(xmlSaveCtxtPtr ctxt) return(ret); } +/** + * xmlSaveFinish: + * @ctxt: a document saving context + * + * Close a document saving context, i.e. make sure that all bytes have + * been output and free the associated data. + * + * Available since 2.13.0. + * + * Returns an xmlParserErrors code. + */ +int +xmlSaveFinish(xmlSaveCtxtPtr ctxt) +{ + int ret; + + if (ctxt == NULL) + return(XML_ERR_INTERNAL_ERROR); + xmlSaveFlush(ctxt); + ret = ctxt->buf->error; + xmlFreeSaveCtxt(ctxt); + return(ret); +} + /** * xmlSaveSetEscape: * @ctxt: a document saving context @@ -1957,63 +2301,62 @@ xmlSaveSetAttrEscape(xmlSaveCtxtPtr ctxt, xmlCharEncodingOutputFunc escape) /** * xmlBufAttrSerializeTxtContent: - * @buf: and xmlBufPtr output + * @buf: output buffer * @doc: the document - * @attr: the attribute node * @string: the text content * * Serialize text attribute values to an xmlBufPtr */ void -xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc, - xmlAttrPtr attr, const xmlChar * string) +xmlBufAttrSerializeTxtContent(xmlOutputBufferPtr buf, xmlDocPtr doc, + const xmlChar *string) { - xmlChar *base, *cur; + const xmlChar *base, *cur; if (string == NULL) return; - base = cur = (xmlChar *) string; + base = cur = string; while (*cur != 0) { if (*cur == '\n') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST " ", 5); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 5, " "); cur++; base = cur; } else if (*cur == '\r') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST " ", 5); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 5, " "); cur++; base = cur; } else if (*cur == '\t') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST " ", 4); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 4, " "); cur++; base = cur; } else if (*cur == '"') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST """, 6); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 6, """); cur++; base = cur; } else if (*cur == '<') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST "<", 4); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 4, "<"); cur++; base = cur; } else if (*cur == '>') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST ">", 4); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 4, ">"); cur++; base = cur; } else if (*cur == '&') { if (base != cur) - xmlBufAdd(buf, base, cur - base); - xmlBufAdd(buf, BAD_CAST "&", 5); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + xmlOutputBufferWrite(buf, 5, "&"); cur++; base = cur; } else if ((*cur >= 0x80) && (cur[1] != 0) && @@ -2022,61 +2365,34 @@ xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc, * We assume we have UTF-8 content. */ unsigned char tmp[12]; - int val = 0, l = 1; + int val = 0, l = 4; if (base != cur) - xmlBufAdd(buf, base, cur - base); - if (*cur < 0xC0) { - xmlSaveErr(XML_SAVE_NOT_UTF8, (xmlNodePtr) attr, NULL); - xmlSerializeHexCharRef(tmp, *cur); - xmlBufAdd(buf, (xmlChar *) tmp, -1); - cur++; - base = cur; - continue; - } else if (*cur < 0xE0) { - val = (cur[0]) & 0x1F; - val <<= 6; - val |= (cur[1]) & 0x3F; - l = 2; - } else if ((*cur < 0xF0) && (cur [2] != 0)) { - val = (cur[0]) & 0x0F; - val <<= 6; - val |= (cur[1]) & 0x3F; - val <<= 6; - val |= (cur[2]) & 0x3F; - l = 3; - } else if ((*cur < 0xF8) && (cur [2] != 0) && (cur[3] != 0)) { - val = (cur[0]) & 0x07; - val <<= 6; - val |= (cur[1]) & 0x3F; - val <<= 6; - val |= (cur[2]) & 0x3F; - val <<= 6; - val |= (cur[3]) & 0x3F; - l = 4; - } - if ((l == 1) || (!IS_CHAR(val))) { - xmlSaveErr(XML_SAVE_CHAR_INVALID, (xmlNodePtr) attr, NULL); - xmlSerializeHexCharRef(tmp, *cur); - xmlBufAdd(buf, (xmlChar *) tmp, -1); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); + + val = xmlGetUTF8Char(cur, &l); + if (val < 0) { + val = 0xFFFD; cur++; - base = cur; - continue; + } else { + if (!IS_CHAR(val)) + val = 0xFFFD; + cur += l; } + /* * We could do multiple things here. Just save * as a char ref */ xmlSerializeHexCharRef(tmp, val); - xmlBufAdd(buf, (xmlChar *) tmp, -1); - cur += l; + xmlOutputBufferWriteString(buf, (const char *) tmp); base = cur; } else { cur++; } } if (base != cur) - xmlBufAdd(buf, base, cur - base); + xmlOutputBufferWrite(buf, cur - base, (const char *) base); } /** @@ -2090,17 +2406,19 @@ xmlBufAttrSerializeTxtContent(xmlBufPtr buf, xmlDocPtr doc, */ void xmlAttrSerializeTxtContent(xmlBufferPtr buf, xmlDocPtr doc, - xmlAttrPtr attr, const xmlChar * string) + xmlAttrPtr attr ATTRIBUTE_UNUSED, + const xmlChar *string) { - xmlBufPtr buffer; + xmlOutputBufferPtr out; if ((buf == NULL) || (string == NULL)) return; - buffer = xmlBufFromBuffer(buf); - if (buffer == NULL) - return; - xmlBufAttrSerializeTxtContent(buffer, doc, attr, string); - xmlBufBackToBuffer(buffer); + out = xmlOutputBufferCreateBuffer(buf, NULL); + xmlBufAttrSerializeTxtContent(out, doc, string); + xmlOutputBufferFlush(out); + if ((out == NULL) || (out->error)) + xmlFree(xmlBufferDetach(buf)); + xmlOutputBufferClose(out); } /** @@ -2128,6 +2446,10 @@ xmlNodeDump(xmlBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, if ((buf == NULL) || (cur == NULL)) return(-1); + if (level < 0) + level = 0; + else if (level > 100) + level = 100; buffer = xmlBufFromBuffer(buf); if (buffer == NULL) return(-1); @@ -2159,22 +2481,22 @@ xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, int format) { size_t use; - int ret; + size_t ret; xmlOutputBufferPtr outbuf; int oldalloc; xmlInitParser(); if (cur == NULL) { - return (-1); + return ((size_t) -1); } if (buf == NULL) { - return (-1); + return ((size_t) -1); } outbuf = (xmlOutputBufferPtr) xmlMalloc(sizeof(xmlOutputBuffer)); if (outbuf == NULL) { - xmlSaveErrMemory("creating buffer"); - return (-1); + xmlSaveErrMemory(NULL); + return ((size_t) -1); } memset(outbuf, 0, (size_t) sizeof(xmlOutputBuffer)); outbuf->buffer = buf; @@ -2189,8 +2511,11 @@ xmlBufNodeDump(xmlBufPtr buf, xmlDocPtr doc, xmlNodePtr cur, int level, xmlBufSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT); xmlNodeDumpOutput(outbuf, doc, cur, level, format, NULL); xmlBufSetAllocationScheme(buf, oldalloc); + if (outbuf->error) + ret = (size_t) -1; + else + ret = xmlBufUse(buf) - use; xmlFree(outbuf); - ret = xmlBufUse(buf) - use; return (ret); } @@ -2216,13 +2541,11 @@ xmlElemDump(FILE * f, xmlDocPtr doc, xmlNodePtr cur) outbuf = xmlOutputBufferCreateFile(f, NULL); if (outbuf == NULL) return; - if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) { #ifdef LIBXML_HTML_ENABLED + if ((doc != NULL) && (doc->type == XML_HTML_DOCUMENT_NODE)) htmlNodeDumpOutput(outbuf, doc, cur, NULL); -#else - xmlSaveErr(XML_ERR_INTERNAL_ERROR, cur, "HTML support not compiled in\n"); + else #endif /* LIBXML_HTML_ENABLED */ - } else xmlNodeDumpOutput(outbuf, doc, cur, 0, 1, NULL); xmlOutputBufferClose(outbuf); } @@ -2262,6 +2585,11 @@ xmlNodeDumpOutput(xmlOutputBufferPtr buf, xmlDocPtr doc, xmlNodePtr cur, if ((buf == NULL) || (cur == NULL)) return; + if (level < 0) + level = 0; + else if (level > 100) + level = 100; + if (encoding == NULL) encoding = "UTF-8"; @@ -2337,16 +2665,18 @@ xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, if (txt_encoding == NULL) txt_encoding = (const char *) out_doc->encoding; if (txt_encoding != NULL) { - conv_hdlr = xmlFindCharEncodingHandler(txt_encoding); - if ( conv_hdlr == NULL ) { - xmlSaveErr(XML_SAVE_UNKNOWN_ENCODING, (xmlNodePtr) out_doc, - txt_encoding); + int res; + + res = xmlOpenCharEncodingHandler(txt_encoding, /* output */ 1, + &conv_hdlr); + if (res != XML_ERR_OK) { + xmlSaveErr(NULL, res, NULL, txt_encoding); return; } } if ((out_buff = xmlAllocOutputBuffer(conv_hdlr)) == NULL ) { - xmlSaveErrMemory("creating buffer"); + xmlSaveErrMemory(NULL); xmlCharEncCloseFunc(conv_hdlr); return; } @@ -2360,21 +2690,18 @@ xmlDocDumpFormatMemoryEnc(xmlDocPtr out_doc, xmlChar **doc_txt_ptr, ctxt.options |= XML_SAVE_AS_XML; xmlDocContentDumpOutput(&ctxt, out_doc); xmlOutputBufferFlush(out_buff); - if (out_buff->conv != NULL) { - *doc_txt_len = xmlBufUse(out_buff->conv); - *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->conv), *doc_txt_len); - } else { - *doc_txt_len = xmlBufUse(out_buff->buffer); - *doc_txt_ptr = xmlStrndup(xmlBufContent(out_buff->buffer),*doc_txt_len); - } - (void)xmlOutputBufferClose(out_buff); - if ((*doc_txt_ptr == NULL) && (*doc_txt_len > 0)) { - *doc_txt_len = 0; - xmlSaveErrMemory("creating output"); + if (!out_buff->error) { + if (out_buff->conv != NULL) { + *doc_txt_len = xmlBufUse(out_buff->conv); + *doc_txt_ptr = xmlBufDetach(out_buff->conv); + } else { + *doc_txt_len = xmlBufUse(out_buff->buffer); + *doc_txt_ptr = xmlBufDetach(out_buff->buffer); + } } - return; + xmlOutputBufferClose(out_buff); } /** @@ -2456,8 +2783,10 @@ xmlDocFormatDump(FILE *f, xmlDocPtr cur, int format) { encoding = (const char *) cur->encoding; if (encoding != NULL) { - handler = xmlFindCharEncodingHandler(encoding); - if (handler == NULL) { + int res; + + res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler); + if (res != XML_ERR_OK) { xmlFree((char *) cur->encoding); cur->encoding = NULL; encoding = NULL; @@ -2593,10 +2922,11 @@ xmlSaveFormatFileEnc( const char * filename, xmlDocPtr cur, encoding = (const char *) cur->encoding; if (encoding != NULL) { + int res; - handler = xmlFindCharEncodingHandler(encoding); - if (handler == NULL) - return(-1); + res = xmlOpenCharEncodingHandler(encoding, /* output */ 1, &handler); + if (res != XML_ERR_OK) + return(-1); } #ifdef LIBXML_ZLIB_ENABLED diff --git a/libraries/libxml2/xmlstring.c b/libraries/libxml2/xmlstring.c index d9e16d79..258ecc92 100644 --- a/libraries/libxml2/xmlstring.c +++ b/libraries/libxml2/xmlstring.c @@ -26,6 +26,14 @@ #include "private/parser.h" #include "private/string.h" +#ifndef va_copy + #ifdef __va_copy + #define va_copy(dest, src) __va_copy(dest, src) + #else + #define va_copy(dest, src) memcpy(dest, src, sizeof(va_list)) + #endif +#endif + /************************************************************************ * * * Commodity functions to handle xmlChars * @@ -461,7 +469,8 @@ xmlStrncat(xmlChar *cur, const xmlChar *add, int len) { return(NULL); ret = (xmlChar *) xmlRealloc(cur, (size_t) size + len + 1); if (ret == NULL) { - return(cur); + xmlFree(cur); + return(NULL); } memcpy(&ret[size], add, len); ret[size + len] = 0; @@ -490,18 +499,17 @@ xmlStrncatNew(const xmlChar *str1, const xmlChar *str2, int len) { if (len < 0) return(NULL); } - if ((str2 == NULL) || (len == 0)) - return(xmlStrdup(str1)); if (str1 == NULL) return(xmlStrndup(str2, len)); + if ((str2 == NULL) || (len == 0)) + return(xmlStrdup(str1)); size = xmlStrlen(str1); if ((size < 0) || (size > INT_MAX - len)) return(NULL); ret = (xmlChar *) xmlMalloc((size_t) size + len + 1); - if (ret == NULL) { - return(xmlStrndup(str1, size)); - } + if (ret == NULL) + return(NULL); memcpy(ret, str1, size); memcpy(&ret[size], str2, len); ret[size + len] = 0; @@ -585,6 +593,148 @@ xmlStrVPrintf(xmlChar *buf, int len, const char *msg, va_list ap) { return(ret); } +/** + * xmlStrVASPrintf: + * @out: pointer to the resulting string + * @maxSize: maximum size of the output buffer + * @msg: printf format string + * @ap: arguments for format string + * + * Creates a newly allocated string according to format. + * + * Returns 0 on success, 1 if the result was truncated or on other + * errors, -1 if a memory allocation failed. + */ +int +xmlStrVASPrintf(xmlChar **out, int maxSize, const char *msg, va_list ap) { + char empty[1]; + va_list copy; + xmlChar *buf; + int res, size; + int truncated = 0; + + if (out == NULL) + return(1); + *out = NULL; + if (msg == NULL) + return(1); + if (maxSize < 32) + maxSize = 32; + + va_copy(copy, ap); + res = vsnprintf(empty, 1, msg, copy); + va_end(copy); + + if (res > 0) { + /* snprintf seems to work according to C99. */ + + if (res < maxSize) { + size = res + 1; + } else { + size = maxSize; + truncated = 1; + } + buf = xmlMalloc(size); + if (buf == NULL) + return(-1); + if (vsnprintf((char *) buf, size, msg, ap) < 0) { + xmlFree(buf); + return(1); + } + } else { + /* + * Unfortunately, older snprintf implementations don't follow the + * C99 spec. If the output exceeds the size of the buffer, they can + * return -1, 0 or the number of characters written instead of the + * needed size. Older MSCVRT also won't write a terminating null + * byte if the buffer is too small. + * + * If the value returned is non-negative and strictly less than + * the buffer size (without terminating null), the result should + * have been written completely, so we double the buffer size + * until this condition is true. This assumes that snprintf will + * eventually return a non-negative value. Otherwise, we will + * allocate more and more memory until we run out. + * + * Note that this code path is also executed on conforming + * platforms if the output is the empty string. + */ + + buf = NULL; + size = 32; + while (1) { + buf = xmlMalloc(size); + if (buf == NULL) + return(-1); + + va_copy(copy, ap); + res = vsnprintf((char *) buf, size, msg, copy); + va_end(copy); + if ((res >= 0) && (res < size - 1)) + break; + + if (size >= maxSize) { + truncated = 1; + break; + } + + xmlFree(buf); + + if (size > maxSize / 2) + size = maxSize; + else + size *= 2; + } + } + + /* + * If the output was truncated, make sure that the buffer doesn't + * end with a truncated UTF-8 sequence. + */ + if (truncated != 0) { + int i = size - 1; + + while (i > 0) { + /* Break after ASCII */ + if (buf[i-1] < 0x80) + break; + i -= 1; + /* Break before non-ASCII */ + if (buf[i] >= 0xc0) + break; + } + + buf[i] = 0; + } + + *out = (xmlChar *) buf; + return(truncated); +} + +/** + * xmlStrASPrintf: + * @out: pointer to the resulting string + * @maxSize: maximum size of the output buffer + * @msg: printf format string + * @...: arguments for format string + * + * See xmlStrVASPrintf. + * + * Returns 0 on success, 1 if the result was truncated or on other + * errors, -1 if a memory allocation failed. + */ +int +xmlStrASPrintf(xmlChar **out, int maxSize, const char *msg, ...) { + va_list ap; + int ret; + + va_start(ap, msg); + ret = xmlStrVASPrintf(out, maxSize, msg, ap); + va_end(ap); + + return(ret); +} + /************************************************************************ * * * Generic UTF8 handling routines * @@ -956,8 +1106,9 @@ xmlUTF8Strloc(const xmlChar *utf, const xmlChar *utfchar) { * Create a substring from a given UTF-8 string * Note: positions are given in units of UTF-8 chars * - * Returns a pointer to a newly created string - * or NULL if any problem + * Returns a pointer to a newly created string or NULL if the + * start index is out of bounds or a memory allocation failed. + * If len is too large, the result is truncated. */ xmlChar * @@ -972,16 +1123,18 @@ xmlUTF8Strsub(const xmlChar *utf, int start, int len) { /* * Skip over any leading chars */ - for (i = 0;i < start;i++) { - if ((ch=*utf++) == 0) return(NULL); - if ( ch & 0x80 ) { - /* if not simple ascii, verify proper format */ - if ( (ch & 0xc0) != 0xc0 ) - return(NULL); - /* then skip over remaining bytes for this char */ - while ( (ch <<= 1) & 0x80 ) - if ( (*utf++ & 0xc0) != 0x80 ) + for (i = 0; i < start; i++) { + ch = *utf++; + if (ch == 0) + return(NULL); + /* skip over remaining bytes for this char */ + if (ch & 0x80) { + ch <<= 1; + while (ch & 0x80) { + if (*utf++ == 0) return(NULL); + ch <<= 1; + } } } diff --git a/libraries/libxml2/xmlwriter.c b/libraries/libxml2/xmlwriter.c index ad6e00cc..f648dc05 100644 --- a/libraries/libxml2/xmlwriter.c +++ b/libraries/libxml2/xmlwriter.c @@ -11,9 +11,11 @@ #define IN_LIBXML #include "libxml.h" #include +#include #include #include +#include #include #include #include @@ -30,26 +32,11 @@ #define B64LINELEN 72 #define B64CRLF "\r\n" -/* - * The following VA_COPY was coded following an example in - * the Samba project. It may not be sufficient for some - * esoteric implementations of va_list but (hopefully) will - * be sufficient for libxml2. - */ -#ifndef VA_COPY - #ifdef HAVE_VA_COPY - #define VA_COPY(dest, src) va_copy(dest, src) +#ifndef va_copy + #ifdef __va_copy + #define va_copy(dest, src) __va_copy(dest, src) #else - #ifdef HAVE___VA_COPY - #define VA_COPY(dest,src) __va_copy(dest, src) - #else - #ifndef VA_LIST_IS_ARRAY - #define VA_COPY(dest,src) (dest) = (src) - #else - #include - #define VA_COPY(dest,src) memcpy((char *)(dest),(char *)(src),sizeof(va_list)) - #endif - #endif + #define va_copy(dest, src) memcpy(dest, src, sizeof(va_list)) #endif #endif @@ -1509,8 +1496,8 @@ xmlTextWriterWriteString(xmlTextWriterPtr writer, const xmlChar * content) break; case XML_TEXTWRITER_ATTRIBUTE: buf = NULL; - xmlBufAttrSerializeTxtContent(writer->out->buffer, - writer->doc, NULL, content); + xmlBufAttrSerializeTxtContent(writer->out, writer->doc, + content); break; default: break; @@ -4249,6 +4236,35 @@ xmlTextWriterFlush(xmlTextWriterPtr writer) return count; } +/** + * xmlTextWriterClose: + * @writer: the xmlTextWriterPtr + * + * Flushes and closes the output buffer. + * + * Available since 2.13.0. + * + * Returns an xmlParserErrors code. + */ +int +xmlTextWriterClose(xmlTextWriterPtr writer) +{ + int result; + + if ((writer == NULL) || (writer->out == NULL)) + return XML_ERR_ARGUMENT; + + result = xmlOutputBufferClose(writer->out); + writer->out = NULL; + + if (result >= 0) + result = XML_ERR_OK; + else + result = -result; + + return result; +} + /** * misc */ @@ -4486,7 +4502,7 @@ xmlTextWriterVSprintf(const char *format, va_list argptr) return NULL; } - VA_COPY(locarg, argptr); + va_copy(locarg, argptr); while (((count = vsnprintf((char *) buf, size, format, locarg)) < 0) || (count == size - 1) || (count == size) || (count > size)) { va_end(locarg); @@ -4498,7 +4514,7 @@ xmlTextWriterVSprintf(const char *format, va_list argptr) "xmlTextWriterVSprintf : out of memory!\n"); return NULL; } - VA_COPY(locarg, argptr); + va_copy(locarg, argptr); } va_end(locarg); @@ -4517,28 +4533,17 @@ xmlTextWriterStartDocumentCallback(void *ctx) xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; xmlDocPtr doc; - if (ctxt->html) { #ifdef LIBXML_HTML_ENABLED + if (ctxt->html) { if (ctxt->myDoc == NULL) ctxt->myDoc = htmlNewDocNoDtD(NULL, NULL); if (ctxt->myDoc == NULL) { - if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) - ctxt->sax->error(ctxt->userData, - "SAX.startDocument(): out of memory\n"); - ctxt->errNo = XML_ERR_NO_MEMORY; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; + xmlCtxtErrMemory(ctxt); return; } -#else - xmlWriterErrMsg(NULL, XML_ERR_INTERNAL_ERROR, - "libxml2 built without HTML support\n"); - ctxt->errNo = XML_ERR_INTERNAL_ERROR; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; - return; + } else #endif - } else { + { doc = ctxt->myDoc; if (doc == NULL) doc = ctxt->myDoc = xmlNewDoc(ctxt->version); @@ -4551,12 +4556,7 @@ xmlTextWriterStartDocumentCallback(void *ctx) doc->standalone = ctxt->standalone; } } else { - if ((ctxt->sax != NULL) && (ctxt->sax->error != NULL)) - ctxt->sax->error(ctxt->userData, - "SAX.startDocument(): out of memory\n"); - ctxt->errNo = XML_ERR_NO_MEMORY; - ctxt->instate = XML_PARSER_EOF; - ctxt->disableSAX = 1; + xmlCtxtErrMemory(ctxt); return; } } diff --git a/msvc/Libraries/libxml/libxml.vcxproj b/msvc/Libraries/libxml/libxml.vcxproj index 011b979c..02d60688 100644 --- a/msvc/Libraries/libxml/libxml.vcxproj +++ b/msvc/Libraries/libxml/libxml.vcxproj @@ -217,6 +217,7 @@ + diff --git a/msvc/Libraries/libxml/libxml.vcxproj.filters b/msvc/Libraries/libxml/libxml.vcxproj.filters index 88e0e559..2be14d24 100644 --- a/msvc/Libraries/libxml/libxml.vcxproj.filters +++ b/msvc/Libraries/libxml/libxml.vcxproj.filters @@ -123,6 +123,9 @@ Headerdateien + + Headerdateien\libxml +