| /* a30d2613dcfdef81475a9d1a349134d2d42722172fdaa7d5bb12ed2aa74b9596 (2.4.6+) |
| __ __ _ |
| ___\ \/ /_ __ __ _| |_ |
| / _ \\ /| '_ \ / _` | __| |
| | __// \| |_) | (_| | |_ |
| \___/_/\_\ .__/ \__,_|\__| |
| |_| XML parser |
| |
| Copyright (c) 1997-2000 Thai Open Source Software Center Ltd |
| Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net> |
| Copyright (c) 2000-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> |
| Copyright (c) 2001-2002 Greg Stein <gstein@users.sourceforge.net> |
| Copyright (c) 2002-2016 Karl Waclawek <karl@waclawek.net> |
| Copyright (c) 2005-2009 Steven Solie <steven@solie.ca> |
| Copyright (c) 2016 Eric Rahm <erahm@mozilla.com> |
| Copyright (c) 2016-2022 Sebastian Pipping <sebastian@pipping.org> |
| Copyright (c) 2016 Gaurav <g.gupta@samsung.com> |
| Copyright (c) 2016 Thomas Beutlich <tc@tbeu.de> |
| Copyright (c) 2016 Gustavo Grieco <gustavo.grieco@imag.fr> |
| Copyright (c) 2016 Pascal Cuoq <cuoq@trust-in-soft.com> |
| Copyright (c) 2016 Ed Schouten <ed@nuxi.nl> |
| Copyright (c) 2017-2018 Rhodri James <rhodri@wildebeest.org.uk> |
| Copyright (c) 2017 Václav Slavík <vaclav@slavik.io> |
| Copyright (c) 2017 Viktor Szakats <commit@vsz.me> |
| Copyright (c) 2017 Chanho Park <chanho61.park@samsung.com> |
| Copyright (c) 2017 Rolf Eike Beer <eike@sf-mail.de> |
| Copyright (c) 2017 Hans Wennborg <hans@chromium.org> |
| Copyright (c) 2018 Anton Maklakov <antmak.pub@gmail.com> |
| Copyright (c) 2018 Benjamin Peterson <benjamin@python.org> |
| Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it> |
| Copyright (c) 2018 Mariusz Zaborski <oshogbo@vexillium.org> |
| Copyright (c) 2019 David Loffredo <loffredo@steptools.com> |
| Copyright (c) 2019-2020 Ben Wagner <bungeman@chromium.org> |
| Copyright (c) 2019 Vadim Zeitlin <vadim@zeitlins.org> |
| Copyright (c) 2021 Dong-hee Na <donghee.na@python.org> |
| Copyright (c) 2022 Samanta Navarro <ferivoz@riseup.net> |
| Licensed under the MIT license: |
| |
| Permission is hereby granted, free of charge, to any person obtaining |
| a copy of this software and associated documentation files (the |
| "Software"), to deal in the Software without restriction, including |
| without limitation the rights to use, copy, modify, merge, publish, |
| distribute, sublicense, and/or sell copies of the Software, and to permit |
| persons to whom the Software is furnished to do so, subject to the |
| following conditions: |
| |
| The above copyright notice and this permission notice shall be included |
| in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #define XML_BUILDING_EXPAT 1 |
| |
| #include <expat_config.h> |
| |
| #if ! defined(_GNU_SOURCE) |
| # define _GNU_SOURCE 1 /* syscall prototype */ |
| #endif |
| |
| #ifdef _WIN32 |
| /* force stdlib to define rand_s() */ |
| # if ! defined(_CRT_RAND_S) |
| # define _CRT_RAND_S |
| # endif |
| #endif |
| |
| #include <stddef.h> |
| #include <string.h> /* memset(), memcpy() */ |
| #include <assert.h> |
| #include <limits.h> /* UINT_MAX */ |
| #include <stdio.h> /* fprintf */ |
| #include <stdlib.h> /* getenv, rand_s */ |
| #include <stdint.h> /* uintptr_t */ |
| #include <math.h> /* isnan */ |
| |
| #ifdef _WIN32 |
| # define getpid GetCurrentProcessId |
| #else |
| # include <sys/time.h> /* gettimeofday() */ |
| # include <sys/types.h> /* getpid() */ |
| # include <unistd.h> /* getpid() */ |
| # include <fcntl.h> /* O_RDONLY */ |
| # include <errno.h> |
| #endif |
| |
| #ifdef _WIN32 |
| # include "winconfig.h" |
| #endif |
| |
| #include "ascii.h" |
| #include "expat.h" |
| #include "siphash.h" |
| |
| #if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) |
| # if defined(HAVE_GETRANDOM) |
| # include <sys/random.h> /* getrandom */ |
| # else |
| # include <unistd.h> /* syscall */ |
| # include <sys/syscall.h> /* SYS_getrandom */ |
| # endif |
| # if ! defined(GRND_NONBLOCK) |
| # define GRND_NONBLOCK 0x0001 |
| # endif /* defined(GRND_NONBLOCK) */ |
| #endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ |
| |
| #if defined(HAVE_LIBBSD) \ |
| && (defined(HAVE_ARC4RANDOM_BUF) || defined(HAVE_ARC4RANDOM)) |
| # include <bsd/stdlib.h> |
| #endif |
| |
| #if defined(_WIN32) && ! defined(LOAD_LIBRARY_SEARCH_SYSTEM32) |
| # define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 |
| #endif |
| |
| #if ! defined(HAVE_GETRANDOM) && ! defined(HAVE_SYSCALL_GETRANDOM) \ |
| && ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) \ |
| && ! defined(XML_DEV_URANDOM) && ! defined(_WIN32) \ |
| && ! defined(XML_POOR_ENTROPY) |
| # error You do not have support for any sources of high quality entropy \ |
| enabled. For end user security, that is probably not what you want. \ |
| \ |
| Your options include: \ |
| * Linux >=3.17 + glibc >=2.25 (getrandom): HAVE_GETRANDOM, \ |
| * Linux >=3.17 + glibc (including <2.25) (syscall SYS_getrandom): HAVE_SYSCALL_GETRANDOM, \ |
| * BSD / macOS >=10.7 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \ |
| * BSD / macOS (including <10.7) (arc4random): HAVE_ARC4RANDOM, \ |
| * libbsd (arc4random_buf): HAVE_ARC4RANDOM_BUF + HAVE_LIBBSD, \ |
| * libbsd (arc4random): HAVE_ARC4RANDOM + HAVE_LIBBSD, \ |
| * Linux (including <3.17) / BSD / macOS (including <10.7) (/dev/urandom): XML_DEV_URANDOM, \ |
| * Windows >=Vista (rand_s): _WIN32. \ |
| \ |
| If insist on not using any of these, bypass this error by defining \ |
| XML_POOR_ENTROPY; you have been warned. \ |
| \ |
| If you have reasons to patch this detection code away or need changes \ |
| to the build system, please open a bug. Thank you! |
| #endif |
| |
| #ifdef XML_UNICODE |
| # define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX |
| # define XmlConvert XmlUtf16Convert |
| # define XmlGetInternalEncoding XmlGetUtf16InternalEncoding |
| # define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS |
| # define XmlEncode XmlUtf16Encode |
| # define MUST_CONVERT(enc, s) (! (enc)->isUtf16 || (((uintptr_t)(s)) & 1)) |
| typedef unsigned short ICHAR; |
| #else |
| # define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX |
| # define XmlConvert XmlUtf8Convert |
| # define XmlGetInternalEncoding XmlGetUtf8InternalEncoding |
| # define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS |
| # define XmlEncode XmlUtf8Encode |
| # define MUST_CONVERT(enc, s) (! (enc)->isUtf8) |
| typedef char ICHAR; |
| #endif |
| |
| #ifndef XML_NS |
| |
| # define XmlInitEncodingNS XmlInitEncoding |
| # define XmlInitUnknownEncodingNS XmlInitUnknownEncoding |
| # undef XmlGetInternalEncodingNS |
| # define XmlGetInternalEncodingNS XmlGetInternalEncoding |
| # define XmlParseXmlDeclNS XmlParseXmlDecl |
| |
| #endif |
| |
| #ifdef XML_UNICODE |
| |
| # ifdef XML_UNICODE_WCHAR_T |
| # define XML_T(x) (const wchar_t) x |
| # define XML_L(x) L##x |
| # else |
| # define XML_T(x) (const unsigned short)x |
| # define XML_L(x) x |
| # endif |
| |
| #else |
| |
| # define XML_T(x) x |
| # define XML_L(x) x |
| |
| #endif |
| |
| /* Round up n to be a multiple of sz, where sz is a power of 2. */ |
| #define ROUND_UP(n, sz) (((n) + ((sz)-1)) & ~((sz)-1)) |
| |
| /* Do safe (NULL-aware) pointer arithmetic */ |
| #define EXPAT_SAFE_PTR_DIFF(p, q) (((p) && (q)) ? ((p) - (q)) : 0) |
| |
| #include "internal.h" |
| #include "xmltok.h" |
| #include "xmlrole.h" |
| |
| typedef const XML_Char *KEY; |
| |
| typedef struct { |
| KEY name; |
| } NAMED; |
| |
| typedef struct { |
| NAMED **v; |
| unsigned char power; |
| size_t size; |
| size_t used; |
| const XML_Memory_Handling_Suite *mem; |
| } HASH_TABLE; |
| |
| static size_t keylen(KEY s); |
| |
| static void copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key); |
| |
| /* For probing (after a collision) we need a step size relative prime |
| to the hash table size, which is a power of 2. We use double-hashing, |
| since we can calculate a second hash value cheaply by taking those bits |
| of the first hash value that were discarded (masked out) when the table |
| index was calculated: index = hash & mask, where mask = table->size - 1. |
| We limit the maximum step size to table->size / 4 (mask >> 2) and make |
| it odd, since odd numbers are always relative prime to a power of 2. |
| */ |
| #define SECOND_HASH(hash, mask, power) \ |
| ((((hash) & ~(mask)) >> ((power)-1)) & ((mask) >> 2)) |
| #define PROBE_STEP(hash, mask, power) \ |
| ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) |
| |
| typedef struct { |
| NAMED **p; |
| NAMED **end; |
| } HASH_TABLE_ITER; |
| |
| #define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ |
| #define INIT_DATA_BUF_SIZE 1024 |
| #define INIT_ATTS_SIZE 16 |
| #define INIT_ATTS_VERSION 0xFFFFFFFF |
| #define INIT_BLOCK_SIZE 1024 |
| #define INIT_BUFFER_SIZE 1024 |
| |
| #define EXPAND_SPARE 24 |
| |
| typedef struct binding { |
| struct prefix *prefix; |
| struct binding *nextTagBinding; |
| struct binding *prevPrefixBinding; |
| const struct attribute_id *attId; |
| XML_Char *uri; |
| int uriLen; |
| int uriAlloc; |
| } BINDING; |
| |
| typedef struct prefix { |
| const XML_Char *name; |
| BINDING *binding; |
| } PREFIX; |
| |
| typedef struct { |
| const XML_Char *str; |
| const XML_Char *localPart; |
| const XML_Char *prefix; |
| int strLen; |
| int uriLen; |
| int prefixLen; |
| } TAG_NAME; |
| |
| /* TAG represents an open element. |
| The name of the element is stored in both the document and API |
| encodings. The memory buffer 'buf' is a separately-allocated |
| memory area which stores the name. During the XML_Parse()/ |
| XMLParseBuffer() when the element is open, the memory for the 'raw' |
| version of the name (in the document encoding) is shared with the |
| document buffer. If the element is open across calls to |
| XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to |
| contain the 'raw' name as well. |
| |
| A parser re-uses these structures, maintaining a list of allocated |
| TAG objects in a free list. |
| */ |
| typedef struct tag { |
| struct tag *parent; /* parent of this element */ |
| const char *rawName; /* tagName in the original encoding */ |
| int rawNameLength; |
| TAG_NAME name; /* tagName in the API encoding */ |
| char *buf; /* buffer for name components */ |
| char *bufEnd; /* end of the buffer */ |
| BINDING *bindings; |
| } TAG; |
| |
| typedef struct { |
| const XML_Char *name; |
| const XML_Char *textPtr; |
| int textLen; /* length in XML_Chars */ |
| int processed; /* # of processed bytes - when suspended */ |
| const XML_Char *systemId; |
| const XML_Char *base; |
| const XML_Char *publicId; |
| const XML_Char *notation; |
| XML_Bool open; |
| XML_Bool is_param; |
| XML_Bool is_internal; /* true if declared in internal subset outside PE */ |
| } ENTITY; |
| |
| typedef struct { |
| enum XML_Content_Type type; |
| enum XML_Content_Quant quant; |
| const XML_Char *name; |
| int firstchild; |
| int lastchild; |
| int childcnt; |
| int nextsib; |
| } CONTENT_SCAFFOLD; |
| |
| #define INIT_SCAFFOLD_ELEMENTS 32 |
| |
| typedef struct block { |
| struct block *next; |
| int size; |
| XML_Char s[1]; |
| } BLOCK; |
| |
| typedef struct { |
| BLOCK *blocks; |
| BLOCK *freeBlocks; |
| const XML_Char *end; |
| XML_Char *ptr; |
| XML_Char *start; |
| const XML_Memory_Handling_Suite *mem; |
| } STRING_POOL; |
| |
| /* The XML_Char before the name is used to determine whether |
| an attribute has been specified. */ |
| typedef struct attribute_id { |
| XML_Char *name; |
| PREFIX *prefix; |
| XML_Bool maybeTokenized; |
| XML_Bool xmlns; |
| } ATTRIBUTE_ID; |
| |
| typedef struct { |
| const ATTRIBUTE_ID *id; |
| XML_Bool isCdata; |
| const XML_Char *value; |
| } DEFAULT_ATTRIBUTE; |
| |
| typedef struct { |
| unsigned long version; |
| unsigned long hash; |
| const XML_Char *uriName; |
| } NS_ATT; |
| |
| typedef struct { |
| const XML_Char *name; |
| PREFIX *prefix; |
| const ATTRIBUTE_ID *idAtt; |
| int nDefaultAtts; |
| int allocDefaultAtts; |
| DEFAULT_ATTRIBUTE *defaultAtts; |
| } ELEMENT_TYPE; |
| |
| typedef struct { |
| HASH_TABLE generalEntities; |
| HASH_TABLE elementTypes; |
| HASH_TABLE attributeIds; |
| HASH_TABLE prefixes; |
| STRING_POOL pool; |
| STRING_POOL entityValuePool; |
| /* false once a parameter entity reference has been skipped */ |
| XML_Bool keepProcessing; |
| /* true once an internal or external PE reference has been encountered; |
| this includes the reference to an external subset */ |
| XML_Bool hasParamEntityRefs; |
| XML_Bool standalone; |
| #ifdef XML_DTD |
| /* indicates if external PE has been read */ |
| XML_Bool paramEntityRead; |
| HASH_TABLE paramEntities; |
| #endif /* XML_DTD */ |
| PREFIX defaultPrefix; |
| /* === scaffolding for building content model === */ |
| XML_Bool in_eldecl; |
| CONTENT_SCAFFOLD *scaffold; |
| unsigned contentStringLen; |
| unsigned scaffSize; |
| unsigned scaffCount; |
| int scaffLevel; |
| int *scaffIndex; |
| } DTD; |
| |
| typedef struct open_internal_entity { |
| const char *internalEventPtr; |
| const char *internalEventEndPtr; |
| struct open_internal_entity *next; |
| ENTITY *entity; |
| int startTagLevel; |
| XML_Bool betweenDecl; /* WFC: PE Between Declarations */ |
| } OPEN_INTERNAL_ENTITY; |
| |
| enum XML_Account { |
| XML_ACCOUNT_DIRECT, /* bytes directly passed to the Expat parser */ |
| XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity |
| expansion */ |
| XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */ |
| }; |
| |
| #ifdef XML_DTD |
| typedef unsigned long long XmlBigCount; |
| typedef struct accounting { |
| XmlBigCount countBytesDirect; |
| XmlBigCount countBytesIndirect; |
| int debugLevel; |
| float maximumAmplificationFactor; // >=1.0 |
| unsigned long long activationThresholdBytes; |
| } ACCOUNTING; |
| |
| typedef struct entity_stats { |
| unsigned int countEverOpened; |
| unsigned int currentDepth; |
| unsigned int maximumDepthSeen; |
| int debugLevel; |
| } ENTITY_STATS; |
| #endif /* XML_DTD */ |
| |
| typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start, |
| const char *end, const char **endPtr); |
| |
| static Processor prologProcessor; |
| static Processor prologInitProcessor; |
| static Processor contentProcessor; |
| static Processor cdataSectionProcessor; |
| #ifdef XML_DTD |
| static Processor ignoreSectionProcessor; |
| static Processor externalParEntProcessor; |
| static Processor externalParEntInitProcessor; |
| static Processor entityValueProcessor; |
| static Processor entityValueInitProcessor; |
| #endif /* XML_DTD */ |
| static Processor epilogProcessor; |
| static Processor errorProcessor; |
| static Processor externalEntityInitProcessor; |
| static Processor externalEntityInitProcessor2; |
| static Processor externalEntityInitProcessor3; |
| static Processor externalEntityContentProcessor; |
| static Processor internalEntityProcessor; |
| |
| static enum XML_Error handleUnknownEncoding(XML_Parser parser, |
| const XML_Char *encodingName); |
| static enum XML_Error processXmlDecl(XML_Parser parser, int isGeneralTextEntity, |
| const char *s, const char *next); |
| static enum XML_Error initializeEncoding(XML_Parser parser); |
| static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc, |
| const char *s, const char *end, int tok, |
| const char *next, const char **nextPtr, |
| XML_Bool haveMore, XML_Bool allowClosingDoctype, |
| enum XML_Account account); |
| static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity, |
| XML_Bool betweenDecl); |
| static enum XML_Error doContent(XML_Parser parser, int startTagLevel, |
| const ENCODING *enc, const char *start, |
| const char *end, const char **endPtr, |
| XML_Bool haveMore, enum XML_Account account); |
| static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *, |
| const char **startPtr, const char *end, |
| const char **nextPtr, XML_Bool haveMore, |
| enum XML_Account account); |
| #ifdef XML_DTD |
| static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *, |
| const char **startPtr, const char *end, |
| const char **nextPtr, XML_Bool haveMore); |
| #endif /* XML_DTD */ |
| |
| static void freeBindings(XML_Parser parser, BINDING *bindings); |
| static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, |
| const char *s, TAG_NAME *tagNamePtr, |
| BINDING **bindingsPtr, |
| enum XML_Account account); |
| static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix, |
| const ATTRIBUTE_ID *attId, const XML_Char *uri, |
| BINDING **bindingsPtr); |
| static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, |
| XML_Bool isId, const XML_Char *dfltValue, |
| XML_Parser parser); |
| static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *, |
| XML_Bool isCdata, const char *, |
| const char *, STRING_POOL *, |
| enum XML_Account account); |
| static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *, |
| XML_Bool isCdata, const char *, |
| const char *, STRING_POOL *, |
| enum XML_Account account); |
| static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end); |
| static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); |
| static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end, |
| enum XML_Account account); |
| static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end); |
| static int reportComment(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end); |
| static void reportDefault(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end); |
| |
| static const XML_Char *getContext(XML_Parser parser); |
| static XML_Bool setContext(XML_Parser parser, const XML_Char *context); |
| |
| static void FASTCALL normalizePublicId(XML_Char *s); |
| |
| static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms); |
| /* do not call if m_parentParser != NULL */ |
| static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); |
| static void dtdDestroy(DTD *p, XML_Bool isDocEntity, |
| const XML_Memory_Handling_Suite *ms); |
| static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, |
| const XML_Memory_Handling_Suite *ms); |
| static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *, STRING_POOL *, |
| const HASH_TABLE *); |
| static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name, |
| size_t createSize); |
| static void FASTCALL hashTableInit(HASH_TABLE *, |
| const XML_Memory_Handling_Suite *ms); |
| static void FASTCALL hashTableClear(HASH_TABLE *); |
| static void FASTCALL hashTableDestroy(HASH_TABLE *); |
| static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); |
| static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *); |
| |
| static void FASTCALL poolInit(STRING_POOL *, |
| const XML_Memory_Handling_Suite *ms); |
| static void FASTCALL poolClear(STRING_POOL *); |
| static void FASTCALL poolDestroy(STRING_POOL *); |
| static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, |
| const char *ptr, const char *end); |
| static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, |
| const char *ptr, const char *end); |
| static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); |
| static const XML_Char *FASTCALL poolCopyString(STRING_POOL *pool, |
| const XML_Char *s); |
| static const XML_Char *poolCopyStringN(STRING_POOL *pool, const XML_Char *s, |
| int n); |
| static const XML_Char *FASTCALL poolAppendString(STRING_POOL *pool, |
| const XML_Char *s); |
| |
| static int FASTCALL nextScaffoldPart(XML_Parser parser); |
| static XML_Content *build_model(XML_Parser parser); |
| static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc, |
| const char *ptr, const char *end); |
| |
| static XML_Char *copyString(const XML_Char *s, |
| const XML_Memory_Handling_Suite *memsuite); |
| |
| static unsigned long generate_hash_secret_salt(XML_Parser parser); |
| static XML_Bool startParsing(XML_Parser parser); |
| |
| static XML_Parser parserCreate(const XML_Char *encodingName, |
| const XML_Memory_Handling_Suite *memsuite, |
| const XML_Char *nameSep, DTD *dtd); |
| |
| static void parserInit(XML_Parser parser, const XML_Char *encodingName); |
| |
| #ifdef XML_DTD |
| static float accountingGetCurrentAmplification(XML_Parser rootParser); |
| static void accountingReportStats(XML_Parser originParser, const char *epilog); |
| static void accountingOnAbort(XML_Parser originParser); |
| static void accountingReportDiff(XML_Parser rootParser, |
| unsigned int levelsAwayFromRootParser, |
| const char *before, const char *after, |
| ptrdiff_t bytesMore, int source_line, |
| enum XML_Account account); |
| static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok, |
| const char *before, const char *after, |
| int source_line, |
| enum XML_Account account); |
| |
| static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity, |
| const char *action, int sourceLine); |
| static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity, |
| int sourceLine); |
| static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity, |
| int sourceLine); |
| |
| static XML_Parser getRootParserOf(XML_Parser parser, |
| unsigned int *outLevelDiff); |
| #endif /* XML_DTD */ |
| |
| static unsigned long getDebugLevel(const char *variableName, |
| unsigned long defaultDebugLevel); |
| |
| #define poolStart(pool) ((pool)->start) |
| #define poolEnd(pool) ((pool)->ptr) |
| #define poolLength(pool) ((pool)->ptr - (pool)->start) |
| #define poolChop(pool) ((void)--(pool->ptr)) |
| #define poolLastChar(pool) (((pool)->ptr)[-1]) |
| #define poolDiscard(pool) ((pool)->ptr = (pool)->start) |
| #define poolFinish(pool) ((pool)->start = (pool)->ptr) |
| #define poolAppendChar(pool, c) \ |
| (((pool)->ptr == (pool)->end && ! poolGrow(pool)) \ |
| ? 0 \ |
| : ((*((pool)->ptr)++ = c), 1)) |
| |
| struct XML_ParserStruct { |
| /* The first member must be m_userData so that the XML_GetUserData |
| macro works. */ |
| void *m_userData; |
| void *m_handlerArg; |
| char *m_buffer; |
| const XML_Memory_Handling_Suite m_mem; |
| /* first character to be parsed */ |
| const char *m_bufferPtr; |
| /* past last character to be parsed */ |
| char *m_bufferEnd; |
| /* allocated end of m_buffer */ |
| const char *m_bufferLim; |
| XML_Index m_parseEndByteIndex; |
| const char *m_parseEndPtr; |
| XML_Char *m_dataBuf; |
| XML_Char *m_dataBufEnd; |
| XML_StartElementHandler m_startElementHandler; |
| XML_EndElementHandler m_endElementHandler; |
| XML_CharacterDataHandler m_characterDataHandler; |
| XML_ProcessingInstructionHandler m_processingInstructionHandler; |
| XML_CommentHandler m_commentHandler; |
| XML_StartCdataSectionHandler m_startCdataSectionHandler; |
| XML_EndCdataSectionHandler m_endCdataSectionHandler; |
| XML_DefaultHandler m_defaultHandler; |
| XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; |
| XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; |
| XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; |
| XML_NotationDeclHandler m_notationDeclHandler; |
| XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; |
| XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; |
| XML_NotStandaloneHandler m_notStandaloneHandler; |
| XML_ExternalEntityRefHandler m_externalEntityRefHandler; |
| XML_Parser m_externalEntityRefHandlerArg; |
| XML_SkippedEntityHandler m_skippedEntityHandler; |
| XML_UnknownEncodingHandler m_unknownEncodingHandler; |
| XML_ElementDeclHandler m_elementDeclHandler; |
| XML_AttlistDeclHandler m_attlistDeclHandler; |
| XML_EntityDeclHandler m_entityDeclHandler; |
| XML_XmlDeclHandler m_xmlDeclHandler; |
| const ENCODING *m_encoding; |
| INIT_ENCODING m_initEncoding; |
| const ENCODING *m_internalEncoding; |
| const XML_Char *m_protocolEncodingName; |
| XML_Bool m_ns; |
| XML_Bool m_ns_triplets; |
| void *m_unknownEncodingMem; |
| void *m_unknownEncodingData; |
| void *m_unknownEncodingHandlerData; |
| void(XMLCALL *m_unknownEncodingRelease)(void *); |
| PROLOG_STATE m_prologState; |
| Processor *m_processor; |
| enum XML_Error m_errorCode; |
| const char *m_eventPtr; |
| const char *m_eventEndPtr; |
| const char *m_positionPtr; |
| OPEN_INTERNAL_ENTITY *m_openInternalEntities; |
| OPEN_INTERNAL_ENTITY *m_freeInternalEntities; |
| XML_Bool m_defaultExpandInternalEntities; |
| int m_tagLevel; |
| ENTITY *m_declEntity; |
| const XML_Char *m_doctypeName; |
| const XML_Char *m_doctypeSysid; |
| const XML_Char *m_doctypePubid; |
| const XML_Char *m_declAttributeType; |
| const XML_Char *m_declNotationName; |
| const XML_Char *m_declNotationPublicId; |
| ELEMENT_TYPE *m_declElementType; |
| ATTRIBUTE_ID *m_declAttributeId; |
| XML_Bool m_declAttributeIsCdata; |
| XML_Bool m_declAttributeIsId; |
| DTD *m_dtd; |
| const XML_Char *m_curBase; |
| TAG *m_tagStack; |
| TAG *m_freeTagList; |
| BINDING *m_inheritedBindings; |
| BINDING *m_freeBindingList; |
| int m_attsSize; |
| int m_nSpecifiedAtts; |
| int m_idAttIndex; |
| ATTRIBUTE *m_atts; |
| NS_ATT *m_nsAtts; |
| unsigned long m_nsAttsVersion; |
| unsigned char m_nsAttsPower; |
| #ifdef XML_ATTR_INFO |
| XML_AttrInfo *m_attInfo; |
| #endif |
| POSITION m_position; |
| STRING_POOL m_tempPool; |
| STRING_POOL m_temp2Pool; |
| char *m_groupConnector; |
| unsigned int m_groupSize; |
| XML_Char m_namespaceSeparator; |
| XML_Parser m_parentParser; |
| XML_ParsingStatus m_parsingStatus; |
| #ifdef XML_DTD |
| XML_Bool m_isParamEntity; |
| XML_Bool m_useForeignDTD; |
| enum XML_ParamEntityParsing m_paramEntityParsing; |
| #endif |
| unsigned long m_hash_secret_salt; |
| #ifdef XML_DTD |
| ACCOUNTING m_accounting; |
| ENTITY_STATS m_entity_stats; |
| #endif |
| }; |
| |
| #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s))) |
| #define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s))) |
| #define FREE(parser, p) (parser->m_mem.free_fcn((p))) |
| |
| XML_Parser XMLCALL |
| XML_ParserCreate(const XML_Char *encodingName) { |
| return XML_ParserCreate_MM(encodingName, NULL, NULL); |
| } |
| |
| XML_Parser XMLCALL |
| XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) { |
| XML_Char tmp[2] = {nsSep, 0}; |
| return XML_ParserCreate_MM(encodingName, NULL, tmp); |
| } |
| |
| static const XML_Char implicitContext[] |
| = {ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, |
| ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, |
| ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, |
| ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, |
| ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, |
| ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, |
| ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, |
| ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, |
| '\0'}; |
| |
| /* To avoid warnings about unused functions: */ |
| #if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) |
| |
| # if defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) |
| |
| /* Obtain entropy on Linux 3.17+ */ |
| static int |
| writeRandomBytes_getrandom_nonblock(void *target, size_t count) { |
| int success = 0; /* full count bytes written? */ |
| size_t bytesWrittenTotal = 0; |
| const unsigned int getrandomFlags = GRND_NONBLOCK; |
| |
| do { |
| void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); |
| const size_t bytesToWrite = count - bytesWrittenTotal; |
| |
| const int bytesWrittenMore = |
| # if defined(HAVE_GETRANDOM) |
| getrandom(currentTarget, bytesToWrite, getrandomFlags); |
| # else |
| syscall(SYS_getrandom, currentTarget, bytesToWrite, getrandomFlags); |
| # endif |
| |
| if (bytesWrittenMore > 0) { |
| bytesWrittenTotal += bytesWrittenMore; |
| if (bytesWrittenTotal >= count) |
| success = 1; |
| } |
| } while (! success && (errno == EINTR)); |
| |
| return success; |
| } |
| |
| # endif /* defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) */ |
| |
| # if ! defined(_WIN32) && defined(XML_DEV_URANDOM) |
| |
| /* Extract entropy from /dev/urandom */ |
| static int |
| writeRandomBytes_dev_urandom(void *target, size_t count) { |
| int success = 0; /* full count bytes written? */ |
| size_t bytesWrittenTotal = 0; |
| |
| const int fd = open("/dev/urandom", O_RDONLY); |
| if (fd < 0) { |
| return 0; |
| } |
| |
| do { |
| void *const currentTarget = (void *)((char *)target + bytesWrittenTotal); |
| const size_t bytesToWrite = count - bytesWrittenTotal; |
| |
| const ssize_t bytesWrittenMore = read(fd, currentTarget, bytesToWrite); |
| |
| if (bytesWrittenMore > 0) { |
| bytesWrittenTotal += bytesWrittenMore; |
| if (bytesWrittenTotal >= count) |
| success = 1; |
| } |
| } while (! success && (errno == EINTR)); |
| |
| close(fd); |
| return success; |
| } |
| |
| # endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ |
| |
| #endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ |
| |
| #if defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) |
| |
| static void |
| writeRandomBytes_arc4random(void *target, size_t count) { |
| size_t bytesWrittenTotal = 0; |
| |
| while (bytesWrittenTotal < count) { |
| const uint32_t random32 = arc4random(); |
| size_t i = 0; |
| |
| for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); |
| i++, bytesWrittenTotal++) { |
| const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); |
| ((uint8_t *)target)[bytesWrittenTotal] = random8; |
| } |
| } |
| } |
| |
| #endif /* defined(HAVE_ARC4RANDOM) && ! defined(HAVE_ARC4RANDOM_BUF) */ |
| |
| #ifdef _WIN32 |
| |
| /* Provide declaration of rand_s() for MinGW-32 (not 64, which has it), |
| as it didn't declare it in its header prior to version 5.3.0 of its |
| runtime package (mingwrt, containing stdlib.h). The upstream fix |
| was introduced at https://osdn.net/projects/mingw/ticket/39658 . */ |
| # if defined(__MINGW32__) && defined(__MINGW32_VERSION) \ |
| && __MINGW32_VERSION < 5003000L && ! defined(__MINGW64_VERSION_MAJOR) |
| __declspec(dllimport) int rand_s(unsigned int *); |
| # endif |
| |
| /* Obtain entropy on Windows using the rand_s() function which |
| * generates cryptographically secure random numbers. Internally it |
| * uses RtlGenRandom API which is present in Windows XP and later. |
| */ |
| static int |
| writeRandomBytes_rand_s(void *target, size_t count) { |
| size_t bytesWrittenTotal = 0; |
| |
| while (bytesWrittenTotal < count) { |
| unsigned int random32 = 0; |
| size_t i = 0; |
| |
| if (rand_s(&random32)) |
| return 0; /* failure */ |
| |
| for (; (i < sizeof(random32)) && (bytesWrittenTotal < count); |
| i++, bytesWrittenTotal++) { |
| const uint8_t random8 = (uint8_t)(random32 >> (i * 8)); |
| ((uint8_t *)target)[bytesWrittenTotal] = random8; |
| } |
| } |
| return 1; /* success */ |
| } |
| |
| #endif /* _WIN32 */ |
| |
| #if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) |
| |
| static unsigned long |
| gather_time_entropy(void) { |
| # ifdef _WIN32 |
| FILETIME ft; |
| GetSystemTimeAsFileTime(&ft); /* never fails */ |
| return ft.dwHighDateTime ^ ft.dwLowDateTime; |
| # else |
| struct timeval tv; |
| int gettimeofday_res; |
| |
| gettimeofday_res = gettimeofday(&tv, NULL); |
| |
| # if defined(NDEBUG) |
| (void)gettimeofday_res; |
| # else |
| assert(gettimeofday_res == 0); |
| # endif /* defined(NDEBUG) */ |
| |
| /* Microseconds time is <20 bits entropy */ |
| return tv.tv_usec; |
| # endif |
| } |
| |
| #endif /* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ |
| |
| static unsigned long |
| ENTROPY_DEBUG(const char *label, unsigned long entropy) { |
| if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) { |
| fprintf(stderr, "expat: Entropy: %s --> 0x%0*lx (%lu bytes)\n", label, |
| (int)sizeof(entropy) * 2, entropy, (unsigned long)sizeof(entropy)); |
| } |
| return entropy; |
| } |
| |
| static unsigned long |
| generate_hash_secret_salt(XML_Parser parser) { |
| unsigned long entropy; |
| (void)parser; |
| |
| /* "Failproof" high quality providers: */ |
| #if defined(HAVE_ARC4RANDOM_BUF) |
| arc4random_buf(&entropy, sizeof(entropy)); |
| return ENTROPY_DEBUG("arc4random_buf", entropy); |
| #elif defined(HAVE_ARC4RANDOM) |
| writeRandomBytes_arc4random((void *)&entropy, sizeof(entropy)); |
| return ENTROPY_DEBUG("arc4random", entropy); |
| #else |
| /* Try high quality providers first .. */ |
| # ifdef _WIN32 |
| if (writeRandomBytes_rand_s((void *)&entropy, sizeof(entropy))) { |
| return ENTROPY_DEBUG("rand_s", entropy); |
| } |
| # elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) |
| if (writeRandomBytes_getrandom_nonblock((void *)&entropy, sizeof(entropy))) { |
| return ENTROPY_DEBUG("getrandom", entropy); |
| } |
| # endif |
| # if ! defined(_WIN32) && defined(XML_DEV_URANDOM) |
| if (writeRandomBytes_dev_urandom((void *)&entropy, sizeof(entropy))) { |
| return ENTROPY_DEBUG("/dev/urandom", entropy); |
| } |
| # endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ |
| /* .. and self-made low quality for backup: */ |
| |
| /* Process ID is 0 bits entropy if attacker has local access */ |
| entropy = gather_time_entropy() ^ getpid(); |
| |
| /* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ |
| if (sizeof(unsigned long) == 4) { |
| return ENTROPY_DEBUG("fallback(4)", entropy * 2147483647); |
| } else { |
| return ENTROPY_DEBUG("fallback(8)", |
| entropy * (unsigned long)2305843009213693951ULL); |
| } |
| #endif |
| } |
| |
| static unsigned long |
| get_hash_secret_salt(XML_Parser parser) { |
| if (parser->m_parentParser != NULL) |
| return get_hash_secret_salt(parser->m_parentParser); |
| return parser->m_hash_secret_salt; |
| } |
| |
| static XML_Bool /* only valid for root parser */ |
| startParsing(XML_Parser parser) { |
| /* hash functions must be initialized before setContext() is called */ |
| if (parser->m_hash_secret_salt == 0) |
| parser->m_hash_secret_salt = generate_hash_secret_salt(parser); |
| if (parser->m_ns) { |
| /* implicit context only set for root parser, since child |
| parsers (i.e. external entity parsers) will inherit it |
| */ |
| return setContext(parser, implicitContext); |
| } |
| return XML_TRUE; |
| } |
| |
| XML_Parser XMLCALL |
| XML_ParserCreate_MM(const XML_Char *encodingName, |
| const XML_Memory_Handling_Suite *memsuite, |
| const XML_Char *nameSep) { |
| return parserCreate(encodingName, memsuite, nameSep, NULL); |
| } |
| |
| static XML_Parser |
| parserCreate(const XML_Char *encodingName, |
| const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep, |
| DTD *dtd) { |
| XML_Parser parser; |
| |
| if (memsuite) { |
| XML_Memory_Handling_Suite *mtemp; |
| parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); |
| if (parser != NULL) { |
| mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); |
| mtemp->malloc_fcn = memsuite->malloc_fcn; |
| mtemp->realloc_fcn = memsuite->realloc_fcn; |
| mtemp->free_fcn = memsuite->free_fcn; |
| } |
| } else { |
| XML_Memory_Handling_Suite *mtemp; |
| parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); |
| if (parser != NULL) { |
| mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); |
| mtemp->malloc_fcn = malloc; |
| mtemp->realloc_fcn = realloc; |
| mtemp->free_fcn = free; |
| } |
| } |
| |
| if (! parser) |
| return parser; |
| |
| parser->m_buffer = NULL; |
| parser->m_bufferLim = NULL; |
| |
| parser->m_attsSize = INIT_ATTS_SIZE; |
| parser->m_atts |
| = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE)); |
| if (parser->m_atts == NULL) { |
| FREE(parser, parser); |
| return NULL; |
| } |
| #ifdef XML_ATTR_INFO |
| parser->m_attInfo = (XML_AttrInfo *)MALLOC( |
| parser, parser->m_attsSize * sizeof(XML_AttrInfo)); |
| if (parser->m_attInfo == NULL) { |
| FREE(parser, parser->m_atts); |
| FREE(parser, parser); |
| return NULL; |
| } |
| #endif |
| parser->m_dataBuf |
| = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char)); |
| if (parser->m_dataBuf == NULL) { |
| FREE(parser, parser->m_atts); |
| #ifdef XML_ATTR_INFO |
| FREE(parser, parser->m_attInfo); |
| #endif |
| FREE(parser, parser); |
| return NULL; |
| } |
| parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE; |
| |
| if (dtd) |
| parser->m_dtd = dtd; |
| else { |
| parser->m_dtd = dtdCreate(&parser->m_mem); |
| if (parser->m_dtd == NULL) { |
| FREE(parser, parser->m_dataBuf); |
| FREE(parser, parser->m_atts); |
| #ifdef XML_ATTR_INFO |
| FREE(parser, parser->m_attInfo); |
| #endif |
| FREE(parser, parser); |
| return NULL; |
| } |
| } |
| |
| parser->m_freeBindingList = NULL; |
| parser->m_freeTagList = NULL; |
| parser->m_freeInternalEntities = NULL; |
| |
| parser->m_groupSize = 0; |
| parser->m_groupConnector = NULL; |
| |
| parser->m_unknownEncodingHandler = NULL; |
| parser->m_unknownEncodingHandlerData = NULL; |
| |
| parser->m_namespaceSeparator = ASCII_EXCL; |
| parser->m_ns = XML_FALSE; |
| parser->m_ns_triplets = XML_FALSE; |
| |
| parser->m_nsAtts = NULL; |
| parser->m_nsAttsVersion = 0; |
| parser->m_nsAttsPower = 0; |
| |
| parser->m_protocolEncodingName = NULL; |
| |
| poolInit(&parser->m_tempPool, &(parser->m_mem)); |
| poolInit(&parser->m_temp2Pool, &(parser->m_mem)); |
| parserInit(parser, encodingName); |
| |
| if (encodingName && ! parser->m_protocolEncodingName) { |
| if (dtd) { |
| // We need to stop the upcoming call to XML_ParserFree from happily |
| // destroying parser->m_dtd because the DTD is shared with the parent |
| // parser and the only guard that keeps XML_ParserFree from destroying |
| // parser->m_dtd is parser->m_isParamEntity but it will be set to |
| // XML_TRUE only later in XML_ExternalEntityParserCreate (or not at all). |
| parser->m_dtd = NULL; |
| } |
| XML_ParserFree(parser); |
| return NULL; |
| } |
| |
| if (nameSep) { |
| parser->m_ns = XML_TRUE; |
| parser->m_internalEncoding = XmlGetInternalEncodingNS(); |
| parser->m_namespaceSeparator = *nameSep; |
| } else { |
| parser->m_internalEncoding = XmlGetInternalEncoding(); |
| } |
| |
| return parser; |
| } |
| |
| static void |
| parserInit(XML_Parser parser, const XML_Char *encodingName) { |
| parser->m_processor = prologInitProcessor; |
| XmlPrologStateInit(&parser->m_prologState); |
| if (encodingName != NULL) { |
| parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); |
| } |
| parser->m_curBase = NULL; |
| XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0); |
| parser->m_userData = NULL; |
| parser->m_handlerArg = NULL; |
| parser->m_startElementHandler = NULL; |
| parser->m_endElementHandler = NULL; |
| parser->m_characterDataHandler = NULL; |
| parser->m_processingInstructionHandler = NULL; |
| parser->m_commentHandler = NULL; |
| parser->m_startCdataSectionHandler = NULL; |
| parser->m_endCdataSectionHandler = NULL; |
| parser->m_defaultHandler = NULL; |
| parser->m_startDoctypeDeclHandler = NULL; |
| parser->m_endDoctypeDeclHandler = NULL; |
| parser->m_unparsedEntityDeclHandler = NULL; |
| parser->m_notationDeclHandler = NULL; |
| parser->m_startNamespaceDeclHandler = NULL; |
| parser->m_endNamespaceDeclHandler = NULL; |
| parser->m_notStandaloneHandler = NULL; |
| parser->m_externalEntityRefHandler = NULL; |
| parser->m_externalEntityRefHandlerArg = parser; |
| parser->m_skippedEntityHandler = NULL; |
| parser->m_elementDeclHandler = NULL; |
| parser->m_attlistDeclHandler = NULL; |
| parser->m_entityDeclHandler = NULL; |
| parser->m_xmlDeclHandler = NULL; |
| parser->m_bufferPtr = parser->m_buffer; |
| parser->m_bufferEnd = parser->m_buffer; |
| parser->m_parseEndByteIndex = 0; |
| parser->m_parseEndPtr = NULL; |
| parser->m_declElementType = NULL; |
| parser->m_declAttributeId = NULL; |
| parser->m_declEntity = NULL; |
| parser->m_doctypeName = NULL; |
| parser->m_doctypeSysid = NULL; |
| parser->m_doctypePubid = NULL; |
| parser->m_declAttributeType = NULL; |
| parser->m_declNotationName = NULL; |
| parser->m_declNotationPublicId = NULL; |
| parser->m_declAttributeIsCdata = XML_FALSE; |
| parser->m_declAttributeIsId = XML_FALSE; |
| memset(&parser->m_position, 0, sizeof(POSITION)); |
| parser->m_errorCode = XML_ERROR_NONE; |
| parser->m_eventPtr = NULL; |
| parser->m_eventEndPtr = NULL; |
| parser->m_positionPtr = NULL; |
| parser->m_openInternalEntities = NULL; |
| parser->m_defaultExpandInternalEntities = XML_TRUE; |
| parser->m_tagLevel = 0; |
| parser->m_tagStack = NULL; |
| parser->m_inheritedBindings = NULL; |
| parser->m_nSpecifiedAtts = 0; |
| parser->m_unknownEncodingMem = NULL; |
| parser->m_unknownEncodingRelease = NULL; |
| parser->m_unknownEncodingData = NULL; |
| parser->m_parentParser = NULL; |
| parser->m_parsingStatus.parsing = XML_INITIALIZED; |
| #ifdef XML_DTD |
| parser->m_isParamEntity = XML_FALSE; |
| parser->m_useForeignDTD = XML_FALSE; |
| parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; |
| #endif |
| parser->m_hash_secret_salt = 0; |
| |
| #ifdef XML_DTD |
| memset(&parser->m_accounting, 0, sizeof(ACCOUNTING)); |
| parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u); |
| parser->m_accounting.maximumAmplificationFactor |
| = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT; |
| parser->m_accounting.activationThresholdBytes |
| = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT; |
| |
| memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS)); |
| parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u); |
| #endif |
| } |
| |
| /* moves list of bindings to m_freeBindingList */ |
| static void FASTCALL |
| moveToFreeBindingList(XML_Parser parser, BINDING *bindings) { |
| while (bindings) { |
| BINDING *b = bindings; |
| bindings = bindings->nextTagBinding; |
| b->nextTagBinding = parser->m_freeBindingList; |
| parser->m_freeBindingList = b; |
| } |
| } |
| |
| XML_Bool XMLCALL |
| XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) { |
| TAG *tStk; |
| OPEN_INTERNAL_ENTITY *openEntityList; |
| |
| if (parser == NULL) |
| return XML_FALSE; |
| |
| if (parser->m_parentParser) |
| return XML_FALSE; |
| /* move m_tagStack to m_freeTagList */ |
| tStk = parser->m_tagStack; |
| while (tStk) { |
| TAG *tag = tStk; |
| tStk = tStk->parent; |
| tag->parent = parser->m_freeTagList; |
| moveToFreeBindingList(parser, tag->bindings); |
| tag->bindings = NULL; |
| parser->m_freeTagList = tag; |
| } |
| /* move m_openInternalEntities to m_freeInternalEntities */ |
| openEntityList = parser->m_openInternalEntities; |
| while (openEntityList) { |
| OPEN_INTERNAL_ENTITY *openEntity = openEntityList; |
| openEntityList = openEntity->next; |
| openEntity->next = parser->m_freeInternalEntities; |
| parser->m_freeInternalEntities = openEntity; |
| } |
| moveToFreeBindingList(parser, parser->m_inheritedBindings); |
| FREE(parser, parser->m_unknownEncodingMem); |
| if (parser->m_unknownEncodingRelease) |
| parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); |
| poolClear(&parser->m_tempPool); |
| poolClear(&parser->m_temp2Pool); |
| FREE(parser, (void *)parser->m_protocolEncodingName); |
| parser->m_protocolEncodingName = NULL; |
| parserInit(parser, encodingName); |
| dtdReset(parser->m_dtd, &parser->m_mem); |
| return XML_TRUE; |
| } |
| |
| enum XML_Status XMLCALL |
| XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) { |
| if (parser == NULL) |
| return XML_STATUS_ERROR; |
| /* Block after XML_Parse()/XML_ParseBuffer() has been called. |
| XXX There's no way for the caller to determine which of the |
| XXX possible error cases caused the XML_STATUS_ERROR return. |
| */ |
| if (parser->m_parsingStatus.parsing == XML_PARSING |
| || parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| return XML_STATUS_ERROR; |
| |
| /* Get rid of any previous encoding name */ |
| FREE(parser, (void *)parser->m_protocolEncodingName); |
| |
| if (encodingName == NULL) |
| /* No new encoding name */ |
| parser->m_protocolEncodingName = NULL; |
| else { |
| /* Copy the new encoding name into allocated memory */ |
| parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem)); |
| if (! parser->m_protocolEncodingName) |
| return XML_STATUS_ERROR; |
| } |
| return XML_STATUS_OK; |
| } |
| |
| XML_Parser XMLCALL |
| XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context, |
| const XML_Char *encodingName) { |
| XML_Parser parser = oldParser; |
| DTD *newDtd = NULL; |
| DTD *oldDtd; |
| XML_StartElementHandler oldStartElementHandler; |
| XML_EndElementHandler oldEndElementHandler; |
| XML_CharacterDataHandler oldCharacterDataHandler; |
| XML_ProcessingInstructionHandler oldProcessingInstructionHandler; |
| XML_CommentHandler oldCommentHandler; |
| XML_StartCdataSectionHandler oldStartCdataSectionHandler; |
| XML_EndCdataSectionHandler oldEndCdataSectionHandler; |
| XML_DefaultHandler oldDefaultHandler; |
| XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler; |
| XML_NotationDeclHandler oldNotationDeclHandler; |
| XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler; |
| XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler; |
| XML_NotStandaloneHandler oldNotStandaloneHandler; |
| XML_ExternalEntityRefHandler oldExternalEntityRefHandler; |
| XML_SkippedEntityHandler oldSkippedEntityHandler; |
| XML_UnknownEncodingHandler oldUnknownEncodingHandler; |
| XML_ElementDeclHandler oldElementDeclHandler; |
| XML_AttlistDeclHandler oldAttlistDeclHandler; |
| XML_EntityDeclHandler oldEntityDeclHandler; |
| XML_XmlDeclHandler oldXmlDeclHandler; |
| ELEMENT_TYPE *oldDeclElementType; |
| |
| void *oldUserData; |
| void *oldHandlerArg; |
| XML_Bool oldDefaultExpandInternalEntities; |
| XML_Parser oldExternalEntityRefHandlerArg; |
| #ifdef XML_DTD |
| enum XML_ParamEntityParsing oldParamEntityParsing; |
| int oldInEntityValue; |
| #endif |
| XML_Bool oldns_triplets; |
| /* Note that the new parser shares the same hash secret as the old |
| parser, so that dtdCopy and copyEntityTable can lookup values |
| from hash tables associated with either parser without us having |
| to worry which hash secrets each table has. |
| */ |
| unsigned long oldhash_secret_salt; |
| |
| /* Validate the oldParser parameter before we pull everything out of it */ |
| if (oldParser == NULL) |
| return NULL; |
| |
| /* Stash the original parser contents on the stack */ |
| oldDtd = parser->m_dtd; |
| oldStartElementHandler = parser->m_startElementHandler; |
| oldEndElementHandler = parser->m_endElementHandler; |
| oldCharacterDataHandler = parser->m_characterDataHandler; |
| oldProcessingInstructionHandler = parser->m_processingInstructionHandler; |
| oldCommentHandler = parser->m_commentHandler; |
| oldStartCdataSectionHandler = parser->m_startCdataSectionHandler; |
| oldEndCdataSectionHandler = parser->m_endCdataSectionHandler; |
| oldDefaultHandler = parser->m_defaultHandler; |
| oldUnparsedEntityDeclHandler = parser->m_unparsedEntityDeclHandler; |
| oldNotationDeclHandler = parser->m_notationDeclHandler; |
| oldStartNamespaceDeclHandler = parser->m_startNamespaceDeclHandler; |
| oldEndNamespaceDeclHandler = parser->m_endNamespaceDeclHandler; |
| oldNotStandaloneHandler = parser->m_notStandaloneHandler; |
| oldExternalEntityRefHandler = parser->m_externalEntityRefHandler; |
| oldSkippedEntityHandler = parser->m_skippedEntityHandler; |
| oldUnknownEncodingHandler = parser->m_unknownEncodingHandler; |
| oldElementDeclHandler = parser->m_elementDeclHandler; |
| oldAttlistDeclHandler = parser->m_attlistDeclHandler; |
| oldEntityDeclHandler = parser->m_entityDeclHandler; |
| oldXmlDeclHandler = parser->m_xmlDeclHandler; |
| oldDeclElementType = parser->m_declElementType; |
| |
| oldUserData = parser->m_userData; |
| oldHandlerArg = parser->m_handlerArg; |
| oldDefaultExpandInternalEntities = parser->m_defaultExpandInternalEntities; |
| oldExternalEntityRefHandlerArg = parser->m_externalEntityRefHandlerArg; |
| #ifdef XML_DTD |
| oldParamEntityParsing = parser->m_paramEntityParsing; |
| oldInEntityValue = parser->m_prologState.inEntityValue; |
| #endif |
| oldns_triplets = parser->m_ns_triplets; |
| /* Note that the new parser shares the same hash secret as the old |
| parser, so that dtdCopy and copyEntityTable can lookup values |
| from hash tables associated with either parser without us having |
| to worry which hash secrets each table has. |
| */ |
| oldhash_secret_salt = parser->m_hash_secret_salt; |
| |
| #ifdef XML_DTD |
| if (! context) |
| newDtd = oldDtd; |
| #endif /* XML_DTD */ |
| |
| /* Note that the magical uses of the pre-processor to make field |
| access look more like C++ require that `parser' be overwritten |
| here. This makes this function more painful to follow than it |
| would be otherwise. |
| */ |
| if (parser->m_ns) { |
| XML_Char tmp[2] = {parser->m_namespaceSeparator, 0}; |
| parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); |
| } else { |
| parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); |
| } |
| |
| if (! parser) |
| return NULL; |
| |
| parser->m_startElementHandler = oldStartElementHandler; |
| parser->m_endElementHandler = oldEndElementHandler; |
| parser->m_characterDataHandler = oldCharacterDataHandler; |
| parser->m_processingInstructionHandler = oldProcessingInstructionHandler; |
| parser->m_commentHandler = oldCommentHandler; |
| parser->m_startCdataSectionHandler = oldStartCdataSectionHandler; |
| parser->m_endCdataSectionHandler = oldEndCdataSectionHandler; |
| parser->m_defaultHandler = oldDefaultHandler; |
| parser->m_unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; |
| parser->m_notationDeclHandler = oldNotationDeclHandler; |
| parser->m_startNamespaceDeclHandler = oldStartNamespaceDeclHandler; |
| parser->m_endNamespaceDeclHandler = oldEndNamespaceDeclHandler; |
| parser->m_notStandaloneHandler = oldNotStandaloneHandler; |
| parser->m_externalEntityRefHandler = oldExternalEntityRefHandler; |
| parser->m_skippedEntityHandler = oldSkippedEntityHandler; |
| parser->m_unknownEncodingHandler = oldUnknownEncodingHandler; |
| parser->m_elementDeclHandler = oldElementDeclHandler; |
| parser->m_attlistDeclHandler = oldAttlistDeclHandler; |
| parser->m_entityDeclHandler = oldEntityDeclHandler; |
| parser->m_xmlDeclHandler = oldXmlDeclHandler; |
| parser->m_declElementType = oldDeclElementType; |
| parser->m_userData = oldUserData; |
| if (oldUserData == oldHandlerArg) |
| parser->m_handlerArg = parser->m_userData; |
| else |
| parser->m_handlerArg = parser; |
| if (oldExternalEntityRefHandlerArg != oldParser) |
| parser->m_externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; |
| parser->m_defaultExpandInternalEntities = oldDefaultExpandInternalEntities; |
| parser->m_ns_triplets = oldns_triplets; |
| parser->m_hash_secret_salt = oldhash_secret_salt; |
| parser->m_parentParser = oldParser; |
| #ifdef XML_DTD |
| parser->m_paramEntityParsing = oldParamEntityParsing; |
| parser->m_prologState.inEntityValue = oldInEntityValue; |
| if (context) { |
| #endif /* XML_DTD */ |
| if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem) |
| || ! setContext(parser, context)) { |
| XML_ParserFree(parser); |
| return NULL; |
| } |
| parser->m_processor = externalEntityInitProcessor; |
| #ifdef XML_DTD |
| } else { |
| /* The DTD instance referenced by parser->m_dtd is shared between the |
| document's root parser and external PE parsers, therefore one does not |
| need to call setContext. In addition, one also *must* not call |
| setContext, because this would overwrite existing prefix->binding |
| pointers in parser->m_dtd with ones that get destroyed with the external |
| PE parser. This would leave those prefixes with dangling pointers. |
| */ |
| parser->m_isParamEntity = XML_TRUE; |
| XmlPrologStateInitExternalEntity(&parser->m_prologState); |
| parser->m_processor = externalParEntInitProcessor; |
| } |
| #endif /* XML_DTD */ |
| return parser; |
| } |
| |
| static void FASTCALL |
| destroyBindings(BINDING *bindings, XML_Parser parser) { |
| for (;;) { |
| BINDING *b = bindings; |
| if (! b) |
| break; |
| bindings = b->nextTagBinding; |
| FREE(parser, b->uri); |
| FREE(parser, b); |
| } |
| } |
| |
| void XMLCALL |
| XML_ParserFree(XML_Parser parser) { |
| TAG *tagList; |
| OPEN_INTERNAL_ENTITY *entityList; |
| if (parser == NULL) |
| return; |
| /* free m_tagStack and m_freeTagList */ |
| tagList = parser->m_tagStack; |
| for (;;) { |
| TAG *p; |
| if (tagList == NULL) { |
| if (parser->m_freeTagList == NULL) |
| break; |
| tagList = parser->m_freeTagList; |
| parser->m_freeTagList = NULL; |
| } |
| p = tagList; |
| tagList = tagList->parent; |
| FREE(parser, p->buf); |
| destroyBindings(p->bindings, parser); |
| FREE(parser, p); |
| } |
| /* free m_openInternalEntities and m_freeInternalEntities */ |
| entityList = parser->m_openInternalEntities; |
| for (;;) { |
| OPEN_INTERNAL_ENTITY *openEntity; |
| if (entityList == NULL) { |
| if (parser->m_freeInternalEntities == NULL) |
| break; |
| entityList = parser->m_freeInternalEntities; |
| parser->m_freeInternalEntities = NULL; |
| } |
| openEntity = entityList; |
| entityList = entityList->next; |
| FREE(parser, openEntity); |
| } |
| |
| destroyBindings(parser->m_freeBindingList, parser); |
| destroyBindings(parser->m_inheritedBindings, parser); |
| poolDestroy(&parser->m_tempPool); |
| poolDestroy(&parser->m_temp2Pool); |
| FREE(parser, (void *)parser->m_protocolEncodingName); |
| #ifdef XML_DTD |
| /* external parameter entity parsers share the DTD structure |
| parser->m_dtd with the root parser, so we must not destroy it |
| */ |
| if (! parser->m_isParamEntity && parser->m_dtd) |
| #else |
| if (parser->m_dtd) |
| #endif /* XML_DTD */ |
| dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, |
| &parser->m_mem); |
| FREE(parser, (void *)parser->m_atts); |
| #ifdef XML_ATTR_INFO |
| FREE(parser, (void *)parser->m_attInfo); |
| #endif |
| FREE(parser, parser->m_groupConnector); |
| FREE(parser, parser->m_buffer); |
| FREE(parser, parser->m_dataBuf); |
| FREE(parser, parser->m_nsAtts); |
| FREE(parser, parser->m_unknownEncodingMem); |
| if (parser->m_unknownEncodingRelease) |
| parser->m_unknownEncodingRelease(parser->m_unknownEncodingData); |
| FREE(parser, parser); |
| } |
| |
| void XMLCALL |
| XML_UseParserAsHandlerArg(XML_Parser parser) { |
| if (parser != NULL) |
| parser->m_handlerArg = parser; |
| } |
| |
| enum XML_Error XMLCALL |
| XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) { |
| if (parser == NULL) |
| return XML_ERROR_INVALID_ARGUMENT; |
| #ifdef XML_DTD |
| /* block after XML_Parse()/XML_ParseBuffer() has been called */ |
| if (parser->m_parsingStatus.parsing == XML_PARSING |
| || parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; |
| parser->m_useForeignDTD = useDTD; |
| return XML_ERROR_NONE; |
| #else |
| UNUSED_P(useDTD); |
| return XML_ERROR_FEATURE_REQUIRES_XML_DTD; |
| #endif |
| } |
| |
| void XMLCALL |
| XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) { |
| if (parser == NULL) |
| return; |
| /* block after XML_Parse()/XML_ParseBuffer() has been called */ |
| if (parser->m_parsingStatus.parsing == XML_PARSING |
| || parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| return; |
| parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE; |
| } |
| |
| void XMLCALL |
| XML_SetUserData(XML_Parser parser, void *p) { |
| if (parser == NULL) |
| return; |
| if (parser->m_handlerArg == parser->m_userData) |
| parser->m_handlerArg = parser->m_userData = p; |
| else |
| parser->m_userData = p; |
| } |
| |
| enum XML_Status XMLCALL |
| XML_SetBase(XML_Parser parser, const XML_Char *p) { |
| if (parser == NULL) |
| return XML_STATUS_ERROR; |
| if (p) { |
| p = poolCopyString(&parser->m_dtd->pool, p); |
| if (! p) |
| return XML_STATUS_ERROR; |
| parser->m_curBase = p; |
| } else |
| parser->m_curBase = NULL; |
| return XML_STATUS_OK; |
| } |
| |
| const XML_Char *XMLCALL |
| XML_GetBase(XML_Parser parser) { |
| if (parser == NULL) |
| return NULL; |
| return parser->m_curBase; |
| } |
| |
| int XMLCALL |
| XML_GetSpecifiedAttributeCount(XML_Parser parser) { |
| if (parser == NULL) |
| return -1; |
| return parser->m_nSpecifiedAtts; |
| } |
| |
| int XMLCALL |
| XML_GetIdAttributeIndex(XML_Parser parser) { |
| if (parser == NULL) |
| return -1; |
| return parser->m_idAttIndex; |
| } |
| |
| #ifdef XML_ATTR_INFO |
| const XML_AttrInfo *XMLCALL |
| XML_GetAttributeInfo(XML_Parser parser) { |
| if (parser == NULL) |
| return NULL; |
| return parser->m_attInfo; |
| } |
| #endif |
| |
| void XMLCALL |
| XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, |
| XML_EndElementHandler end) { |
| if (parser == NULL) |
| return; |
| parser->m_startElementHandler = start; |
| parser->m_endElementHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetStartElementHandler(XML_Parser parser, XML_StartElementHandler start) { |
| if (parser != NULL) |
| parser->m_startElementHandler = start; |
| } |
| |
| void XMLCALL |
| XML_SetEndElementHandler(XML_Parser parser, XML_EndElementHandler end) { |
| if (parser != NULL) |
| parser->m_endElementHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetCharacterDataHandler(XML_Parser parser, |
| XML_CharacterDataHandler handler) { |
| if (parser != NULL) |
| parser->m_characterDataHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetProcessingInstructionHandler(XML_Parser parser, |
| XML_ProcessingInstructionHandler handler) { |
| if (parser != NULL) |
| parser->m_processingInstructionHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetCommentHandler(XML_Parser parser, XML_CommentHandler handler) { |
| if (parser != NULL) |
| parser->m_commentHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetCdataSectionHandler(XML_Parser parser, |
| XML_StartCdataSectionHandler start, |
| XML_EndCdataSectionHandler end) { |
| if (parser == NULL) |
| return; |
| parser->m_startCdataSectionHandler = start; |
| parser->m_endCdataSectionHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetStartCdataSectionHandler(XML_Parser parser, |
| XML_StartCdataSectionHandler start) { |
| if (parser != NULL) |
| parser->m_startCdataSectionHandler = start; |
| } |
| |
| void XMLCALL |
| XML_SetEndCdataSectionHandler(XML_Parser parser, |
| XML_EndCdataSectionHandler end) { |
| if (parser != NULL) |
| parser->m_endCdataSectionHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetDefaultHandler(XML_Parser parser, XML_DefaultHandler handler) { |
| if (parser == NULL) |
| return; |
| parser->m_defaultHandler = handler; |
| parser->m_defaultExpandInternalEntities = XML_FALSE; |
| } |
| |
| void XMLCALL |
| XML_SetDefaultHandlerExpand(XML_Parser parser, XML_DefaultHandler handler) { |
| if (parser == NULL) |
| return; |
| parser->m_defaultHandler = handler; |
| parser->m_defaultExpandInternalEntities = XML_TRUE; |
| } |
| |
| void XMLCALL |
| XML_SetDoctypeDeclHandler(XML_Parser parser, XML_StartDoctypeDeclHandler start, |
| XML_EndDoctypeDeclHandler end) { |
| if (parser == NULL) |
| return; |
| parser->m_startDoctypeDeclHandler = start; |
| parser->m_endDoctypeDeclHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetStartDoctypeDeclHandler(XML_Parser parser, |
| XML_StartDoctypeDeclHandler start) { |
| if (parser != NULL) |
| parser->m_startDoctypeDeclHandler = start; |
| } |
| |
| void XMLCALL |
| XML_SetEndDoctypeDeclHandler(XML_Parser parser, XML_EndDoctypeDeclHandler end) { |
| if (parser != NULL) |
| parser->m_endDoctypeDeclHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetUnparsedEntityDeclHandler(XML_Parser parser, |
| XML_UnparsedEntityDeclHandler handler) { |
| if (parser != NULL) |
| parser->m_unparsedEntityDeclHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetNotationDeclHandler(XML_Parser parser, XML_NotationDeclHandler handler) { |
| if (parser != NULL) |
| parser->m_notationDeclHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetNamespaceDeclHandler(XML_Parser parser, |
| XML_StartNamespaceDeclHandler start, |
| XML_EndNamespaceDeclHandler end) { |
| if (parser == NULL) |
| return; |
| parser->m_startNamespaceDeclHandler = start; |
| parser->m_endNamespaceDeclHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetStartNamespaceDeclHandler(XML_Parser parser, |
| XML_StartNamespaceDeclHandler start) { |
| if (parser != NULL) |
| parser->m_startNamespaceDeclHandler = start; |
| } |
| |
| void XMLCALL |
| XML_SetEndNamespaceDeclHandler(XML_Parser parser, |
| XML_EndNamespaceDeclHandler end) { |
| if (parser != NULL) |
| parser->m_endNamespaceDeclHandler = end; |
| } |
| |
| void XMLCALL |
| XML_SetNotStandaloneHandler(XML_Parser parser, |
| XML_NotStandaloneHandler handler) { |
| if (parser != NULL) |
| parser->m_notStandaloneHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetExternalEntityRefHandler(XML_Parser parser, |
| XML_ExternalEntityRefHandler handler) { |
| if (parser != NULL) |
| parser->m_externalEntityRefHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) { |
| if (parser == NULL) |
| return; |
| if (arg) |
| parser->m_externalEntityRefHandlerArg = (XML_Parser)arg; |
| else |
| parser->m_externalEntityRefHandlerArg = parser; |
| } |
| |
| void XMLCALL |
| XML_SetSkippedEntityHandler(XML_Parser parser, |
| XML_SkippedEntityHandler handler) { |
| if (parser != NULL) |
| parser->m_skippedEntityHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetUnknownEncodingHandler(XML_Parser parser, |
| XML_UnknownEncodingHandler handler, void *data) { |
| if (parser == NULL) |
| return; |
| parser->m_unknownEncodingHandler = handler; |
| parser->m_unknownEncodingHandlerData = data; |
| } |
| |
| void XMLCALL |
| XML_SetElementDeclHandler(XML_Parser parser, XML_ElementDeclHandler eldecl) { |
| if (parser != NULL) |
| parser->m_elementDeclHandler = eldecl; |
| } |
| |
| void XMLCALL |
| XML_SetAttlistDeclHandler(XML_Parser parser, XML_AttlistDeclHandler attdecl) { |
| if (parser != NULL) |
| parser->m_attlistDeclHandler = attdecl; |
| } |
| |
| void XMLCALL |
| XML_SetEntityDeclHandler(XML_Parser parser, XML_EntityDeclHandler handler) { |
| if (parser != NULL) |
| parser->m_entityDeclHandler = handler; |
| } |
| |
| void XMLCALL |
| XML_SetXmlDeclHandler(XML_Parser parser, XML_XmlDeclHandler handler) { |
| if (parser != NULL) |
| parser->m_xmlDeclHandler = handler; |
| } |
| |
| int XMLCALL |
| XML_SetParamEntityParsing(XML_Parser parser, |
| enum XML_ParamEntityParsing peParsing) { |
| if (parser == NULL) |
| return 0; |
| /* block after XML_Parse()/XML_ParseBuffer() has been called */ |
| if (parser->m_parsingStatus.parsing == XML_PARSING |
| || parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| return 0; |
| #ifdef XML_DTD |
| parser->m_paramEntityParsing = peParsing; |
| return 1; |
| #else |
| return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; |
| #endif |
| } |
| |
| int XMLCALL |
| XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) { |
| if (parser == NULL) |
| return 0; |
| if (parser->m_parentParser) |
| return XML_SetHashSalt(parser->m_parentParser, hash_salt); |
| /* block after XML_Parse()/XML_ParseBuffer() has been called */ |
| if (parser->m_parsingStatus.parsing == XML_PARSING |
| || parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| return 0; |
| parser->m_hash_secret_salt = hash_salt; |
| return 1; |
| } |
| |
| enum XML_Status XMLCALL |
| XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) { |
| if ((parser == NULL) || (len < 0) || ((s == NULL) && (len != 0))) { |
| if (parser != NULL) |
| parser->m_errorCode = XML_ERROR_INVALID_ARGUMENT; |
| return XML_STATUS_ERROR; |
| } |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| parser->m_errorCode = XML_ERROR_SUSPENDED; |
| return XML_STATUS_ERROR; |
| case XML_FINISHED: |
| parser->m_errorCode = XML_ERROR_FINISHED; |
| return XML_STATUS_ERROR; |
| case XML_INITIALIZED: |
| if (parser->m_parentParser == NULL && ! startParsing(parser)) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return XML_STATUS_ERROR; |
| } |
| /* fall through */ |
| default: |
| parser->m_parsingStatus.parsing = XML_PARSING; |
| } |
| |
| if (len == 0) { |
| parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; |
| if (! isFinal) |
| return XML_STATUS_OK; |
| parser->m_positionPtr = parser->m_bufferPtr; |
| parser->m_parseEndPtr = parser->m_bufferEnd; |
| |
| /* If data are left over from last buffer, and we now know that these |
| data are the final chunk of input, then we have to check them again |
| to detect errors based on that fact. |
| */ |
| parser->m_errorCode |
| = parser->m_processor(parser, parser->m_bufferPtr, |
| parser->m_parseEndPtr, &parser->m_bufferPtr); |
| |
| if (parser->m_errorCode == XML_ERROR_NONE) { |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| /* It is hard to be certain, but it seems that this case |
| * cannot occur. This code is cleaning up a previous parse |
| * with no new data (since len == 0). Changing the parsing |
| * state requires getting to execute a handler function, and |
| * there doesn't seem to be an opportunity for that while in |
| * this circumstance. |
| * |
| * Given the uncertainty, we retain the code but exclude it |
| * from coverage tests. |
| * |
| * LCOV_EXCL_START |
| */ |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, |
| parser->m_bufferPtr, &parser->m_position); |
| parser->m_positionPtr = parser->m_bufferPtr; |
| return XML_STATUS_SUSPENDED; |
| /* LCOV_EXCL_STOP */ |
| case XML_INITIALIZED: |
| case XML_PARSING: |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| /* fall through */ |
| default: |
| return XML_STATUS_OK; |
| } |
| } |
| parser->m_eventEndPtr = parser->m_eventPtr; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } |
| #ifndef XML_CONTEXT_BYTES |
| else if (parser->m_bufferPtr == parser->m_bufferEnd) { |
| const char *end; |
| int nLeftOver; |
| enum XML_Status result; |
| /* Detect overflow (a+b > MAX <==> b > MAX-a) */ |
| if ((XML_Size)len > ((XML_Size)-1) / 2 - parser->m_parseEndByteIndex) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| parser->m_eventPtr = parser->m_eventEndPtr = NULL; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } |
| parser->m_parseEndByteIndex += len; |
| parser->m_positionPtr = s; |
| parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; |
| |
| parser->m_errorCode |
| = parser->m_processor(parser, s, parser->m_parseEndPtr = s + len, &end); |
| |
| if (parser->m_errorCode != XML_ERROR_NONE) { |
| parser->m_eventEndPtr = parser->m_eventPtr; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } else { |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| result = XML_STATUS_SUSPENDED; |
| break; |
| case XML_INITIALIZED: |
| case XML_PARSING: |
| if (isFinal) { |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| return XML_STATUS_OK; |
| } |
| /* fall through */ |
| default: |
| result = XML_STATUS_OK; |
| } |
| } |
| |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, end, |
| &parser->m_position); |
| nLeftOver = s + len - end; |
| if (nLeftOver) { |
| if (parser->m_buffer == NULL |
| || nLeftOver > parser->m_bufferLim - parser->m_buffer) { |
| /* avoid _signed_ integer overflow */ |
| char *temp = NULL; |
| const int bytesToAllocate = (int)((unsigned)len * 2U); |
| if (bytesToAllocate > 0) { |
| temp = (char *)REALLOC(parser, parser->m_buffer, bytesToAllocate); |
| } |
| if (temp == NULL) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| parser->m_eventPtr = parser->m_eventEndPtr = NULL; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } |
| parser->m_buffer = temp; |
| parser->m_bufferLim = parser->m_buffer + bytesToAllocate; |
| } |
| memcpy(parser->m_buffer, end, nLeftOver); |
| } |
| parser->m_bufferPtr = parser->m_buffer; |
| parser->m_bufferEnd = parser->m_buffer + nLeftOver; |
| parser->m_positionPtr = parser->m_bufferPtr; |
| parser->m_parseEndPtr = parser->m_bufferEnd; |
| parser->m_eventPtr = parser->m_bufferPtr; |
| parser->m_eventEndPtr = parser->m_bufferPtr; |
| return result; |
| } |
| #endif /* not defined XML_CONTEXT_BYTES */ |
| else { |
| void *buff = XML_GetBuffer(parser, len); |
| if (buff == NULL) |
| return XML_STATUS_ERROR; |
| else { |
| memcpy(buff, s, len); |
| return XML_ParseBuffer(parser, len, isFinal); |
| } |
| } |
| } |
| |
| enum XML_Status XMLCALL |
| XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { |
| const char *start; |
| enum XML_Status result = XML_STATUS_OK; |
| |
| if (parser == NULL) |
| return XML_STATUS_ERROR; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| parser->m_errorCode = XML_ERROR_SUSPENDED; |
| return XML_STATUS_ERROR; |
| case XML_FINISHED: |
| parser->m_errorCode = XML_ERROR_FINISHED; |
| return XML_STATUS_ERROR; |
| case XML_INITIALIZED: |
| /* Has someone called XML_GetBuffer successfully before? */ |
| if (! parser->m_bufferPtr) { |
| parser->m_errorCode = XML_ERROR_NO_BUFFER; |
| return XML_STATUS_ERROR; |
| } |
| |
| if (parser->m_parentParser == NULL && ! startParsing(parser)) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return XML_STATUS_ERROR; |
| } |
| /* fall through */ |
| default: |
| parser->m_parsingStatus.parsing = XML_PARSING; |
| } |
| |
| start = parser->m_bufferPtr; |
| parser->m_positionPtr = start; |
| parser->m_bufferEnd += len; |
| parser->m_parseEndPtr = parser->m_bufferEnd; |
| parser->m_parseEndByteIndex += len; |
| parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal; |
| |
| parser->m_errorCode = parser->m_processor( |
| parser, start, parser->m_parseEndPtr, &parser->m_bufferPtr); |
| |
| if (parser->m_errorCode != XML_ERROR_NONE) { |
| parser->m_eventEndPtr = parser->m_eventPtr; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } else { |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| result = XML_STATUS_SUSPENDED; |
| break; |
| case XML_INITIALIZED: |
| case XML_PARSING: |
| if (isFinal) { |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| return result; |
| } |
| default:; /* should not happen */ |
| } |
| } |
| |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, |
| parser->m_bufferPtr, &parser->m_position); |
| parser->m_positionPtr = parser->m_bufferPtr; |
| return result; |
| } |
| |
| void *XMLCALL |
| XML_GetBuffer(XML_Parser parser, int len) { |
| if (parser == NULL) |
| return NULL; |
| if (len < 0) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return NULL; |
| } |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| parser->m_errorCode = XML_ERROR_SUSPENDED; |
| return NULL; |
| case XML_FINISHED: |
| parser->m_errorCode = XML_ERROR_FINISHED; |
| return NULL; |
| default:; |
| } |
| |
| if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)) { |
| #ifdef XML_CONTEXT_BYTES |
| int keep; |
| #endif /* defined XML_CONTEXT_BYTES */ |
| /* Do not invoke signed arithmetic overflow: */ |
| int neededSize = (int)((unsigned)len |
| + (unsigned)EXPAT_SAFE_PTR_DIFF( |
| parser->m_bufferEnd, parser->m_bufferPtr)); |
| if (neededSize < 0) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return NULL; |
| } |
| #ifdef XML_CONTEXT_BYTES |
| keep = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); |
| if (keep > XML_CONTEXT_BYTES) |
| keep = XML_CONTEXT_BYTES; |
| /* Detect and prevent integer overflow */ |
| if (keep > INT_MAX - neededSize) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return NULL; |
| } |
| neededSize += keep; |
| #endif /* defined XML_CONTEXT_BYTES */ |
| if (neededSize |
| <= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { |
| #ifdef XML_CONTEXT_BYTES |
| if (keep < EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer)) { |
| int offset |
| = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer) |
| - keep; |
| /* The buffer pointers cannot be NULL here; we have at least some bytes |
| * in the buffer */ |
| memmove(parser->m_buffer, &parser->m_buffer[offset], |
| parser->m_bufferEnd - parser->m_bufferPtr + keep); |
| parser->m_bufferEnd -= offset; |
| parser->m_bufferPtr -= offset; |
| } |
| #else |
| if (parser->m_buffer && parser->m_bufferPtr) { |
| memmove(parser->m_buffer, parser->m_bufferPtr, |
| EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); |
| parser->m_bufferEnd |
| = parser->m_buffer |
| + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); |
| parser->m_bufferPtr = parser->m_buffer; |
| } |
| #endif /* not defined XML_CONTEXT_BYTES */ |
| } else { |
| char *newBuf; |
| int bufferSize |
| = (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferPtr); |
| if (bufferSize == 0) |
| bufferSize = INIT_BUFFER_SIZE; |
| do { |
| /* Do not invoke signed arithmetic overflow: */ |
| bufferSize = (int)(2U * (unsigned)bufferSize); |
| } while (bufferSize < neededSize && bufferSize > 0); |
| if (bufferSize <= 0) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return NULL; |
| } |
| newBuf = (char *)MALLOC(parser, bufferSize); |
| if (newBuf == 0) { |
| parser->m_errorCode = XML_ERROR_NO_MEMORY; |
| return NULL; |
| } |
| parser->m_bufferLim = newBuf + bufferSize; |
| #ifdef XML_CONTEXT_BYTES |
| if (parser->m_bufferPtr) { |
| memcpy(newBuf, &parser->m_bufferPtr[-keep], |
| EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) |
| + keep); |
| FREE(parser, parser->m_buffer); |
| parser->m_buffer = newBuf; |
| parser->m_bufferEnd |
| = parser->m_buffer |
| + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr) |
| + keep; |
| parser->m_bufferPtr = parser->m_buffer + keep; |
| } else { |
| /* This must be a brand new buffer with no data in it yet */ |
| parser->m_bufferEnd = newBuf; |
| parser->m_bufferPtr = parser->m_buffer = newBuf; |
| } |
| #else |
| if (parser->m_bufferPtr) { |
| memcpy(newBuf, parser->m_bufferPtr, |
| EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)); |
| FREE(parser, parser->m_buffer); |
| parser->m_bufferEnd |
| = newBuf |
| + EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr); |
| } else { |
| /* This must be a brand new buffer with no data in it yet */ |
| parser->m_bufferEnd = newBuf; |
| } |
| parser->m_bufferPtr = parser->m_buffer = newBuf; |
| #endif /* not defined XML_CONTEXT_BYTES */ |
| } |
| parser->m_eventPtr = parser->m_eventEndPtr = NULL; |
| parser->m_positionPtr = NULL; |
| } |
| return parser->m_bufferEnd; |
| } |
| |
| enum XML_Status XMLCALL |
| XML_StopParser(XML_Parser parser, XML_Bool resumable) { |
| if (parser == NULL) |
| return XML_STATUS_ERROR; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| if (resumable) { |
| parser->m_errorCode = XML_ERROR_SUSPENDED; |
| return XML_STATUS_ERROR; |
| } |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| break; |
| case XML_FINISHED: |
| parser->m_errorCode = XML_ERROR_FINISHED; |
| return XML_STATUS_ERROR; |
| default: |
| if (resumable) { |
| #ifdef XML_DTD |
| if (parser->m_isParamEntity) { |
| parser->m_errorCode = XML_ERROR_SUSPEND_PE; |
| return XML_STATUS_ERROR; |
| } |
| #endif |
| parser->m_parsingStatus.parsing = XML_SUSPENDED; |
| } else |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| } |
| return XML_STATUS_OK; |
| } |
| |
| enum XML_Status XMLCALL |
| XML_ResumeParser(XML_Parser parser) { |
| enum XML_Status result = XML_STATUS_OK; |
| |
| if (parser == NULL) |
| return XML_STATUS_ERROR; |
| if (parser->m_parsingStatus.parsing != XML_SUSPENDED) { |
| parser->m_errorCode = XML_ERROR_NOT_SUSPENDED; |
| return XML_STATUS_ERROR; |
| } |
| parser->m_parsingStatus.parsing = XML_PARSING; |
| |
| parser->m_errorCode = parser->m_processor( |
| parser, parser->m_bufferPtr, parser->m_parseEndPtr, &parser->m_bufferPtr); |
| |
| if (parser->m_errorCode != XML_ERROR_NONE) { |
| parser->m_eventEndPtr = parser->m_eventPtr; |
| parser->m_processor = errorProcessor; |
| return XML_STATUS_ERROR; |
| } else { |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| result = XML_STATUS_SUSPENDED; |
| break; |
| case XML_INITIALIZED: |
| case XML_PARSING: |
| if (parser->m_parsingStatus.finalBuffer) { |
| parser->m_parsingStatus.parsing = XML_FINISHED; |
| return result; |
| } |
| default:; |
| } |
| } |
| |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, |
| parser->m_bufferPtr, &parser->m_position); |
| parser->m_positionPtr = parser->m_bufferPtr; |
| return result; |
| } |
| |
| void XMLCALL |
| XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) { |
| if (parser == NULL) |
| return; |
| assert(status != NULL); |
| *status = parser->m_parsingStatus; |
| } |
| |
| enum XML_Error XMLCALL |
| XML_GetErrorCode(XML_Parser parser) { |
| if (parser == NULL) |
| return XML_ERROR_INVALID_ARGUMENT; |
| return parser->m_errorCode; |
| } |
| |
| XML_Index XMLCALL |
| XML_GetCurrentByteIndex(XML_Parser parser) { |
| if (parser == NULL) |
| return -1; |
| if (parser->m_eventPtr) |
| return (XML_Index)(parser->m_parseEndByteIndex |
| - (parser->m_parseEndPtr - parser->m_eventPtr)); |
| return -1; |
| } |
| |
| int XMLCALL |
| XML_GetCurrentByteCount(XML_Parser parser) { |
| if (parser == NULL) |
| return 0; |
| if (parser->m_eventEndPtr && parser->m_eventPtr) |
| return (int)(parser->m_eventEndPtr - parser->m_eventPtr); |
| return 0; |
| } |
| |
| const char *XMLCALL |
| XML_GetInputContext(XML_Parser parser, int *offset, int *size) { |
| #ifdef XML_CONTEXT_BYTES |
| if (parser == NULL) |
| return NULL; |
| if (parser->m_eventPtr && parser->m_buffer) { |
| if (offset != NULL) |
| *offset = (int)(parser->m_eventPtr - parser->m_buffer); |
| if (size != NULL) |
| *size = (int)(parser->m_bufferEnd - parser->m_buffer); |
| return parser->m_buffer; |
| } |
| #else |
| (void)parser; |
| (void)offset; |
| (void)size; |
| #endif /* defined XML_CONTEXT_BYTES */ |
| return (const char *)0; |
| } |
| |
| XML_Size XMLCALL |
| XML_GetCurrentLineNumber(XML_Parser parser) { |
| if (parser == NULL) |
| return 0; |
| if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, |
| parser->m_eventPtr, &parser->m_position); |
| parser->m_positionPtr = parser->m_eventPtr; |
| } |
| return parser->m_position.lineNumber + 1; |
| } |
| |
| XML_Size XMLCALL |
| XML_GetCurrentColumnNumber(XML_Parser parser) { |
| if (parser == NULL) |
| return 0; |
| if (parser->m_eventPtr && parser->m_eventPtr >= parser->m_positionPtr) { |
| XmlUpdatePosition(parser->m_encoding, parser->m_positionPtr, |
| parser->m_eventPtr, &parser->m_position); |
| parser->m_positionPtr = parser->m_eventPtr; |
| } |
| return parser->m_position.columnNumber; |
| } |
| |
| void XMLCALL |
| XML_FreeContentModel(XML_Parser parser, XML_Content *model) { |
| if (parser != NULL) |
| FREE(parser, model); |
| } |
| |
| void *XMLCALL |
| XML_MemMalloc(XML_Parser parser, size_t size) { |
| if (parser == NULL) |
| return NULL; |
| return MALLOC(parser, size); |
| } |
| |
| void *XMLCALL |
| XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) { |
| if (parser == NULL) |
| return NULL; |
| return REALLOC(parser, ptr, size); |
| } |
| |
| void XMLCALL |
| XML_MemFree(XML_Parser parser, void *ptr) { |
| if (parser != NULL) |
| FREE(parser, ptr); |
| } |
| |
| void XMLCALL |
| XML_DefaultCurrent(XML_Parser parser) { |
| if (parser == NULL) |
| return; |
| if (parser->m_defaultHandler) { |
| if (parser->m_openInternalEntities) |
| reportDefault(parser, parser->m_internalEncoding, |
| parser->m_openInternalEntities->internalEventPtr, |
| parser->m_openInternalEntities->internalEventEndPtr); |
| else |
| reportDefault(parser, parser->m_encoding, parser->m_eventPtr, |
| parser->m_eventEndPtr); |
| } |
| } |
| |
| const XML_LChar *XMLCALL |
| XML_ErrorString(enum XML_Error code) { |
| switch (code) { |
| case XML_ERROR_NONE: |
| return NULL; |
| case XML_ERROR_NO_MEMORY: |
| return XML_L("out of memory"); |
| case XML_ERROR_SYNTAX: |
| return XML_L("syntax error"); |
| case XML_ERROR_NO_ELEMENTS: |
| return XML_L("no element found"); |
| case XML_ERROR_INVALID_TOKEN: |
| return XML_L("not well-formed (invalid token)"); |
| case XML_ERROR_UNCLOSED_TOKEN: |
| return XML_L("unclosed token"); |
| case XML_ERROR_PARTIAL_CHAR: |
| return XML_L("partial character"); |
| case XML_ERROR_TAG_MISMATCH: |
| return XML_L("mismatched tag"); |
| case XML_ERROR_DUPLICATE_ATTRIBUTE: |
| return XML_L("duplicate attribute"); |
| case XML_ERROR_JUNK_AFTER_DOC_ELEMENT: |
| return XML_L("junk after document element"); |
| case XML_ERROR_PARAM_ENTITY_REF: |
| return XML_L("illegal parameter entity reference"); |
| case XML_ERROR_UNDEFINED_ENTITY: |
| return XML_L("undefined entity"); |
| case XML_ERROR_RECURSIVE_ENTITY_REF: |
| return XML_L("recursive entity reference"); |
| case XML_ERROR_ASYNC_ENTITY: |
| return XML_L("asynchronous entity"); |
| case XML_ERROR_BAD_CHAR_REF: |
| return XML_L("reference to invalid character number"); |
| case XML_ERROR_BINARY_ENTITY_REF: |
| return XML_L("reference to binary entity"); |
| case XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF: |
| return XML_L("reference to external entity in attribute"); |
| case XML_ERROR_MISPLACED_XML_PI: |
| return XML_L("XML or text declaration not at start of entity"); |
| case XML_ERROR_UNKNOWN_ENCODING: |
| return XML_L("unknown encoding"); |
| case XML_ERROR_INCORRECT_ENCODING: |
| return XML_L("encoding specified in XML declaration is incorrect"); |
| case XML_ERROR_UNCLOSED_CDATA_SECTION: |
| return XML_L("unclosed CDATA section"); |
| case XML_ERROR_EXTERNAL_ENTITY_HANDLING: |
| return XML_L("error in processing external entity reference"); |
| case XML_ERROR_NOT_STANDALONE: |
| return XML_L("document is not standalone"); |
| case XML_ERROR_UNEXPECTED_STATE: |
| return XML_L("unexpected parser state - please send a bug report"); |
| case XML_ERROR_ENTITY_DECLARED_IN_PE: |
| return XML_L("entity declared in parameter entity"); |
| case XML_ERROR_FEATURE_REQUIRES_XML_DTD: |
| return XML_L("requested feature requires XML_DTD support in Expat"); |
| case XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING: |
| return XML_L("cannot change setting once parsing has begun"); |
| /* Added in 1.95.7. */ |
| case XML_ERROR_UNBOUND_PREFIX: |
| return XML_L("unbound prefix"); |
| /* Added in 1.95.8. */ |
| case XML_ERROR_UNDECLARING_PREFIX: |
| return XML_L("must not undeclare prefix"); |
| case XML_ERROR_INCOMPLETE_PE: |
| return XML_L("incomplete markup in parameter entity"); |
| case XML_ERROR_XML_DECL: |
| return XML_L("XML declaration not well-formed"); |
| case XML_ERROR_TEXT_DECL: |
| return XML_L("text declaration not well-formed"); |
| case XML_ERROR_PUBLICID: |
| return XML_L("illegal character(s) in public id"); |
| case XML_ERROR_SUSPENDED: |
| return XML_L("parser suspended"); |
| case XML_ERROR_NOT_SUSPENDED: |
| return XML_L("parser not suspended"); |
| case XML_ERROR_ABORTED: |
| return XML_L("parsing aborted"); |
| case XML_ERROR_FINISHED: |
| return XML_L("parsing finished"); |
| case XML_ERROR_SUSPEND_PE: |
| return XML_L("cannot suspend in external parameter entity"); |
| /* Added in 2.0.0. */ |
| case XML_ERROR_RESERVED_PREFIX_XML: |
| return XML_L( |
| "reserved prefix (xml) must not be undeclared or bound to another namespace name"); |
| case XML_ERROR_RESERVED_PREFIX_XMLNS: |
| return XML_L("reserved prefix (xmlns) must not be declared or undeclared"); |
| case XML_ERROR_RESERVED_NAMESPACE_URI: |
| return XML_L( |
| "prefix must not be bound to one of the reserved namespace names"); |
| /* Added in 2.2.5. */ |
| case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */ |
| return XML_L("invalid argument"); |
| /* Added in 2.3.0. */ |
| case XML_ERROR_NO_BUFFER: |
| return XML_L( |
| "a successful prior call to function XML_GetBuffer is required"); |
| /* Added in 2.4.0. */ |
| case XML_ERROR_AMPLIFICATION_LIMIT_BREACH: |
| return XML_L( |
| "limit on input amplification factor (from DTD and entities) breached"); |
| } |
| return NULL; |
| } |
| |
| const XML_LChar *XMLCALL |
| XML_ExpatVersion(void) { |
| /* V1 is used to string-ize the version number. However, it would |
| string-ize the actual version macro *names* unless we get them |
| substituted before being passed to V1. CPP is defined to expand |
| a macro, then rescan for more expansions. Thus, we use V2 to expand |
| the version macros, then CPP will expand the resulting V1() macro |
| with the correct numerals. */ |
| /* ### I'm assuming cpp is portable in this respect... */ |
| |
| #define V1(a, b, c) XML_L(#a) XML_L(".") XML_L(#b) XML_L(".") XML_L(#c) |
| #define V2(a, b, c) XML_L("expat_") V1(a, b, c) |
| |
| return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); |
| |
| #undef V1 |
| #undef V2 |
| } |
| |
| XML_Expat_Version XMLCALL |
| XML_ExpatVersionInfo(void) { |
| XML_Expat_Version version; |
| |
| version.major = XML_MAJOR_VERSION; |
| version.minor = XML_MINOR_VERSION; |
| version.micro = XML_MICRO_VERSION; |
| |
| return version; |
| } |
| |
| const XML_Feature *XMLCALL |
| XML_GetFeatureList(void) { |
| static const XML_Feature features[] = { |
| {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), |
| sizeof(XML_Char)}, |
| {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), |
| sizeof(XML_LChar)}, |
| #ifdef XML_UNICODE |
| {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, |
| #endif |
| #ifdef XML_UNICODE_WCHAR_T |
| {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, |
| #endif |
| #ifdef XML_DTD |
| {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, |
| #endif |
| #ifdef XML_CONTEXT_BYTES |
| {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), |
| XML_CONTEXT_BYTES}, |
| #endif |
| #ifdef XML_MIN_SIZE |
| {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, |
| #endif |
| #ifdef XML_NS |
| {XML_FEATURE_NS, XML_L("XML_NS"), 0}, |
| #endif |
| #ifdef XML_LARGE_SIZE |
| {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, |
| #endif |
| #ifdef XML_ATTR_INFO |
| {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, |
| #endif |
| #ifdef XML_DTD |
| /* Added in Expat 2.4.0. */ |
| {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT, |
| XML_L("XML_BLAP_MAX_AMP"), |
| (long int) |
| EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT}, |
| {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT, |
| XML_L("XML_BLAP_ACT_THRES"), |
| EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT}, |
| #endif |
| {XML_FEATURE_END, NULL, 0}}; |
| |
| return features; |
| } |
| |
| #ifdef XML_DTD |
| XML_Bool XMLCALL |
| XML_SetBillionLaughsAttackProtectionMaximumAmplification( |
| XML_Parser parser, float maximumAmplificationFactor) { |
| if ((parser == NULL) || (parser->m_parentParser != NULL) |
| || isnan(maximumAmplificationFactor) |
| || (maximumAmplificationFactor < 1.0f)) { |
| return XML_FALSE; |
| } |
| parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor; |
| return XML_TRUE; |
| } |
| |
| XML_Bool XMLCALL |
| XML_SetBillionLaughsAttackProtectionActivationThreshold( |
| XML_Parser parser, unsigned long long activationThresholdBytes) { |
| if ((parser == NULL) || (parser->m_parentParser != NULL)) { |
| return XML_FALSE; |
| } |
| parser->m_accounting.activationThresholdBytes = activationThresholdBytes; |
| return XML_TRUE; |
| } |
| #endif /* XML_DTD */ |
| |
| /* Initially tag->rawName always points into the parse buffer; |
| for those TAG instances opened while the current parse buffer was |
| processed, and not yet closed, we need to store tag->rawName in a more |
| permanent location, since the parse buffer is about to be discarded. |
| */ |
| static XML_Bool |
| storeRawNames(XML_Parser parser) { |
| TAG *tag = parser->m_tagStack; |
| while (tag) { |
| int bufSize; |
| int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); |
| size_t rawNameLen; |
| char *rawNameBuf = tag->buf + nameLen; |
| /* Stop if already stored. Since m_tagStack is a stack, we can stop |
| at the first entry that has already been copied; everything |
| below it in the stack is already been accounted for in a |
| previous call to this function. |
| */ |
| if (tag->rawName == rawNameBuf) |
| break; |
| /* For re-use purposes we need to ensure that the |
| size of tag->buf is a multiple of sizeof(XML_Char). |
| */ |
| rawNameLen = ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); |
| /* Detect and prevent integer overflow. */ |
| if (rawNameLen > (size_t)INT_MAX - nameLen) |
| return XML_FALSE; |
| bufSize = nameLen + (int)rawNameLen; |
| if (bufSize > tag->bufEnd - tag->buf) { |
| char *temp = (char *)REALLOC(parser, tag->buf, bufSize); |
| if (temp == NULL) |
| return XML_FALSE; |
| /* if tag->name.str points to tag->buf (only when namespace |
| processing is off) then we have to update it |
| */ |
| if (tag->name.str == (XML_Char *)tag->buf) |
| tag->name.str = (XML_Char *)temp; |
| /* if tag->name.localPart is set (when namespace processing is on) |
| then update it as well, since it will always point into tag->buf |
| */ |
| if (tag->name.localPart) |
| tag->name.localPart |
| = (XML_Char *)temp + (tag->name.localPart - (XML_Char *)tag->buf); |
| tag->buf = temp; |
| tag->bufEnd = temp + bufSize; |
| rawNameBuf = temp + nameLen; |
| } |
| memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); |
| tag->rawName = rawNameBuf; |
| tag = tag->parent; |
| } |
| return XML_TRUE; |
| } |
| |
| static enum XML_Error PTRCALL |
| contentProcessor(XML_Parser parser, const char *start, const char *end, |
| const char **endPtr) { |
| enum XML_Error result = doContent( |
| parser, 0, parser->m_encoding, start, end, endPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); |
| if (result == XML_ERROR_NONE) { |
| if (! storeRawNames(parser)) |
| return XML_ERROR_NO_MEMORY; |
| } |
| return result; |
| } |
| |
| static enum XML_Error PTRCALL |
| externalEntityInitProcessor(XML_Parser parser, const char *start, |
| const char *end, const char **endPtr) { |
| enum XML_Error result = initializeEncoding(parser); |
| if (result != XML_ERROR_NONE) |
| return result; |
| parser->m_processor = externalEntityInitProcessor2; |
| return externalEntityInitProcessor2(parser, start, end, endPtr); |
| } |
| |
| static enum XML_Error PTRCALL |
| externalEntityInitProcessor2(XML_Parser parser, const char *start, |
| const char *end, const char **endPtr) { |
| const char *next = start; /* XmlContentTok doesn't always set the last arg */ |
| int tok = XmlContentTok(parser->m_encoding, start, end, &next); |
| switch (tok) { |
| case XML_TOK_BOM: |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, start, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #endif /* XML_DTD */ |
| |
| /* If we are at the end of the buffer, this would cause the next stage, |
| i.e. externalEntityInitProcessor3, to pass control directly to |
| doContent (by detecting XML_TOK_NONE) without processing any xml text |
| declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. |
| */ |
| if (next == end && ! parser->m_parsingStatus.finalBuffer) { |
| *endPtr = next; |
| return XML_ERROR_NONE; |
| } |
| start = next; |
| break; |
| case XML_TOK_PARTIAL: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *endPtr = start; |
| return XML_ERROR_NONE; |
| } |
| parser->m_eventPtr = start; |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *endPtr = start; |
| return XML_ERROR_NONE; |
| } |
| parser->m_eventPtr = start; |
| return XML_ERROR_PARTIAL_CHAR; |
| } |
| parser->m_processor = externalEntityInitProcessor3; |
| return externalEntityInitProcessor3(parser, start, end, endPtr); |
| } |
| |
| static enum XML_Error PTRCALL |
| externalEntityInitProcessor3(XML_Parser parser, const char *start, |
| const char *end, const char **endPtr) { |
| int tok; |
| const char *next = start; /* XmlContentTok doesn't always set the last arg */ |
| parser->m_eventPtr = start; |
| tok = XmlContentTok(parser->m_encoding, start, end, &next); |
| /* Note: These bytes are accounted later in: |
| - processXmlDecl |
| - externalEntityContentProcessor |
| */ |
| parser->m_eventEndPtr = next; |
| |
| switch (tok) { |
| case XML_TOK_XML_DECL: { |
| enum XML_Error result; |
| result = processXmlDecl(parser, 1, start, next); |
| if (result != XML_ERROR_NONE) |
| return result; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| *endPtr = next; |
| return XML_ERROR_NONE; |
| case XML_FINISHED: |
| return XML_ERROR_ABORTED; |
| default: |
| start = next; |
| } |
| } break; |
| case XML_TOK_PARTIAL: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *endPtr = start; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *endPtr = start; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_PARTIAL_CHAR; |
| } |
| parser->m_processor = externalEntityContentProcessor; |
| parser->m_tagLevel = 1; |
| return externalEntityContentProcessor(parser, start, end, endPtr); |
| } |
| |
| static enum XML_Error PTRCALL |
| externalEntityContentProcessor(XML_Parser parser, const char *start, |
| const char *end, const char **endPtr) { |
| enum XML_Error result |
| = doContent(parser, 1, parser->m_encoding, start, end, endPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| if (result == XML_ERROR_NONE) { |
| if (! storeRawNames(parser)) |
| return XML_ERROR_NO_MEMORY; |
| } |
| return result; |
| } |
| |
| static enum XML_Error |
| doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, |
| const char *s, const char *end, const char **nextPtr, |
| XML_Bool haveMore, enum XML_Account account) { |
| /* save one level of indirection */ |
| DTD *const dtd = parser->m_dtd; |
| |
| const char **eventPP; |
| const char **eventEndPP; |
| if (enc == parser->m_encoding) { |
| eventPP = &parser->m_eventPtr; |
| eventEndPP = &parser->m_eventEndPtr; |
| } else { |
| eventPP = &(parser->m_openInternalEntities->internalEventPtr); |
| eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); |
| } |
| *eventPP = s; |
| |
| for (;;) { |
| const char *next = s; /* XmlContentTok doesn't always set the last arg */ |
| int tok = XmlContentTok(enc, s, end, &next); |
| #ifdef XML_DTD |
| const char *accountAfter |
| = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR)) |
| ? (haveMore ? s /* i.e. 0 bytes */ : end) |
| : next; |
| if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__, |
| account)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #endif |
| *eventEndPP = next; |
| switch (tok) { |
| case XML_TOK_TRAILING_CR: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| *eventEndPP = end; |
| if (parser->m_characterDataHandler) { |
| XML_Char c = 0xA; |
| parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, end); |
| /* We are at the end of the final buffer, should we check for |
| XML_SUSPENDED, XML_FINISHED? |
| */ |
| if (startTagLevel == 0) |
| return XML_ERROR_NO_ELEMENTS; |
| if (parser->m_tagLevel != startTagLevel) |
| return XML_ERROR_ASYNC_ENTITY; |
| *nextPtr = end; |
| return XML_ERROR_NONE; |
| case XML_TOK_NONE: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| if (startTagLevel > 0) { |
| if (parser->m_tagLevel != startTagLevel) |
| return XML_ERROR_ASYNC_ENTITY; |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_NO_ELEMENTS; |
| case XML_TOK_INVALID: |
| *eventPP = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_ENTITY_REF: { |
| const XML_Char *name; |
| ENTITY *entity; |
| XML_Char ch = (XML_Char)XmlPredefinedEntityName( |
| enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar); |
| if (ch) { |
| #ifdef XML_DTD |
| /* NOTE: We are replacing 4-6 characters original input for 1 character |
| * so there is no amplification and hence recording without |
| * protection. */ |
| accountingDiffTolerated(parser, tok, (char *)&ch, |
| ((char *)&ch) + sizeof(XML_Char), __LINE__, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| #endif /* XML_DTD */ |
| if (parser->m_characterDataHandler) |
| parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| } |
| name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); |
| poolDiscard(&dtd->pool); |
| /* First, determine if a check for an existing declaration is needed; |
| if yes, check that the entity exists, and that it is internal, |
| otherwise call the skipped entity or default handler. |
| */ |
| if (! dtd->hasParamEntityRefs || dtd->standalone) { |
| if (! entity) |
| return XML_ERROR_UNDEFINED_ENTITY; |
| else if (! entity->is_internal) |
| return XML_ERROR_ENTITY_DECLARED_IN_PE; |
| } else if (! entity) { |
| if (parser->m_skippedEntityHandler) |
| parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| } |
| if (entity->open) |
| return XML_ERROR_RECURSIVE_ENTITY_REF; |
| if (entity->notation) |
| return XML_ERROR_BINARY_ENTITY_REF; |
| if (entity->textPtr) { |
| enum XML_Error result; |
| if (! parser->m_defaultExpandInternalEntities) { |
| if (parser->m_skippedEntityHandler) |
| parser->m_skippedEntityHandler(parser->m_handlerArg, entity->name, |
| 0); |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| } |
| result = processInternalEntity(parser, entity, XML_FALSE); |
| if (result != XML_ERROR_NONE) |
| return result; |
| } else if (parser->m_externalEntityRefHandler) { |
| const XML_Char *context; |
| entity->open = XML_TRUE; |
| context = getContext(parser); |
| entity->open = XML_FALSE; |
| if (! context) |
| return XML_ERROR_NO_MEMORY; |
| if (! parser->m_externalEntityRefHandler( |
| parser->m_externalEntityRefHandlerArg, context, entity->base, |
| entity->systemId, entity->publicId)) |
| return XML_ERROR_EXTERNAL_ENTITY_HANDLING; |
| poolDiscard(&parser->m_tempPool); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| } |
| case XML_TOK_START_TAG_NO_ATTS: |
| /* fall through */ |
| case XML_TOK_START_TAG_WITH_ATTS: { |
| TAG *tag; |
| enum XML_Error result; |
| XML_Char *toPtr; |
| if (parser->m_freeTagList) { |
| tag = parser->m_freeTagList; |
| parser->m_freeTagList = parser->m_freeTagList->parent; |
| } else { |
| tag = (TAG *)MALLOC(parser, sizeof(TAG)); |
| if (! tag) |
| return XML_ERROR_NO_MEMORY; |
| tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE); |
| if (! tag->buf) { |
| FREE(parser, tag); |
| return XML_ERROR_NO_MEMORY; |
| } |
| tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; |
| } |
| tag->bindings = NULL; |
| tag->parent = parser->m_tagStack; |
| parser->m_tagStack = tag; |
| tag->name.localPart = NULL; |
| tag->name.prefix = NULL; |
| tag->rawName = s + enc->minBytesPerChar; |
| tag->rawNameLength = XmlNameLength(enc, tag->rawName); |
| ++parser->m_tagLevel; |
| { |
| const char *rawNameEnd = tag->rawName + tag->rawNameLength; |
| const char *fromPtr = tag->rawName; |
| toPtr = (XML_Char *)tag->buf; |
| for (;;) { |
| int bufSize; |
| int convLen; |
| const enum XML_Convert_Result convert_res |
| = XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr, |
| (ICHAR *)tag->bufEnd - 1); |
| convLen = (int)(toPtr - (XML_Char *)tag->buf); |
| if ((fromPtr >= rawNameEnd) |
| || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) { |
| tag->name.strLen = convLen; |
| break; |
| } |
| bufSize = (int)(tag->bufEnd - tag->buf) << 1; |
| { |
| char *temp = (char *)REALLOC(parser, tag->buf, bufSize); |
| if (temp == NULL) |
| return XML_ERROR_NO_MEMORY; |
| tag->buf = temp; |
| tag->bufEnd = temp + bufSize; |
| toPtr = (XML_Char *)temp + convLen; |
| } |
| } |
| } |
| tag->name.str = (XML_Char *)tag->buf; |
| *toPtr = XML_T('\0'); |
| result |
| = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account); |
| if (result) |
| return result; |
| if (parser->m_startElementHandler) |
| parser->m_startElementHandler(parser->m_handlerArg, tag->name.str, |
| (const XML_Char **)parser->m_atts); |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| poolClear(&parser->m_tempPool); |
| break; |
| } |
| case XML_TOK_EMPTY_ELEMENT_NO_ATTS: |
| /* fall through */ |
| case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: { |
| const char *rawName = s + enc->minBytesPerChar; |
| enum XML_Error result; |
| BINDING *bindings = NULL; |
| XML_Bool noElmHandlers = XML_TRUE; |
| TAG_NAME name; |
| name.str = poolStoreString(&parser->m_tempPool, enc, rawName, |
| rawName + XmlNameLength(enc, rawName)); |
| if (! name.str) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_tempPool); |
| result = storeAtts(parser, enc, s, &name, &bindings, |
| XML_ACCOUNT_NONE /* token spans whole start tag */); |
| if (result != XML_ERROR_NONE) { |
| freeBindings(parser, bindings); |
| return result; |
| } |
| poolFinish(&parser->m_tempPool); |
| if (parser->m_startElementHandler) { |
| parser->m_startElementHandler(parser->m_handlerArg, name.str, |
| (const XML_Char **)parser->m_atts); |
| noElmHandlers = XML_FALSE; |
| } |
| if (parser->m_endElementHandler) { |
| if (parser->m_startElementHandler) |
| *eventPP = *eventEndPP; |
| parser->m_endElementHandler(parser->m_handlerArg, name.str); |
| noElmHandlers = XML_FALSE; |
| } |
| if (noElmHandlers && parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| poolClear(&parser->m_tempPool); |
| freeBindings(parser, bindings); |
| } |
| if ((parser->m_tagLevel == 0) |
| && (parser->m_parsingStatus.parsing != XML_FINISHED)) { |
| if (parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| parser->m_processor = epilogProcessor; |
| else |
| return epilogProcessor(parser, next, end, nextPtr); |
| } |
| break; |
| case XML_TOK_END_TAG: |
| if (parser->m_tagLevel == startTagLevel) |
| return XML_ERROR_ASYNC_ENTITY; |
| else { |
| int len; |
| const char *rawName; |
| TAG *tag = parser->m_tagStack; |
| parser->m_tagStack = tag->parent; |
| tag->parent = parser->m_freeTagList; |
| parser->m_freeTagList = tag; |
| rawName = s + enc->minBytesPerChar * 2; |
| len = XmlNameLength(enc, rawName); |
| if (len != tag->rawNameLength |
| || memcmp(tag->rawName, rawName, len) != 0) { |
| *eventPP = rawName; |
| return XML_ERROR_TAG_MISMATCH; |
| } |
| --parser->m_tagLevel; |
| if (parser->m_endElementHandler) { |
| const XML_Char *localPart; |
| const XML_Char *prefix; |
| XML_Char *uri; |
| localPart = tag->name.localPart; |
| if (parser->m_ns && localPart) { |
| /* localPart and prefix may have been overwritten in |
| tag->name.str, since this points to the binding->uri |
| buffer which gets re-used; so we have to add them again |
| */ |
| uri = (XML_Char *)tag->name.str + tag->name.uriLen; |
| /* don't need to check for space - already done in storeAtts() */ |
| while (*localPart) |
| *uri++ = *localPart++; |
| prefix = (XML_Char *)tag->name.prefix; |
| if (parser->m_ns_triplets && prefix) { |
| *uri++ = parser->m_namespaceSeparator; |
| while (*prefix) |
| *uri++ = *prefix++; |
| } |
| *uri = XML_T('\0'); |
| } |
| parser->m_endElementHandler(parser->m_handlerArg, tag->name.str); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| while (tag->bindings) { |
| BINDING *b = tag->bindings; |
| if (parser->m_endNamespaceDeclHandler) |
| parser->m_endNamespaceDeclHandler(parser->m_handlerArg, |
| b->prefix->name); |
| tag->bindings = tag->bindings->nextTagBinding; |
| b->nextTagBinding = parser->m_freeBindingList; |
| parser->m_freeBindingList = b; |
| b->prefix->binding = b->prevPrefixBinding; |
| } |
| if ((parser->m_tagLevel == 0) |
| && (parser->m_parsingStatus.parsing != XML_FINISHED)) { |
| if (parser->m_parsingStatus.parsing == XML_SUSPENDED) |
| parser->m_processor = epilogProcessor; |
| else |
| return epilogProcessor(parser, next, end, nextPtr); |
| } |
| } |
| break; |
| case XML_TOK_CHAR_REF: { |
| int n = XmlCharRefNumber(enc, s); |
| if (n < 0) |
| return XML_ERROR_BAD_CHAR_REF; |
| if (parser->m_characterDataHandler) { |
| XML_Char buf[XML_ENCODE_MAX]; |
| parser->m_characterDataHandler(parser->m_handlerArg, buf, |
| XmlEncode(n, (ICHAR *)buf)); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| } break; |
| case XML_TOK_XML_DECL: |
| return XML_ERROR_MISPLACED_XML_PI; |
| case XML_TOK_DATA_NEWLINE: |
| if (parser->m_characterDataHandler) { |
| XML_Char c = 0xA; |
| parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| case XML_TOK_CDATA_SECT_OPEN: { |
| enum XML_Error result; |
| if (parser->m_startCdataSectionHandler) |
| parser->m_startCdataSectionHandler(parser->m_handlerArg); |
| /* BEGIN disabled code */ |
| /* Suppose you doing a transformation on a document that involves |
| changing only the character data. You set up a defaultHandler |
| and a characterDataHandler. The defaultHandler simply copies |
| characters through. The characterDataHandler does the |
| transformation and writes the characters out escaping them as |
| necessary. This case will fail to work if we leave out the |
| following two lines (because & and < inside CDATA sections will |
| be incorrectly escaped). |
| |
| However, now we have a start/endCdataSectionHandler, so it seems |
| easier to let the user deal with this. |
| */ |
| else if (0 && parser->m_characterDataHandler) |
| parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, |
| 0); |
| /* END disabled code */ |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| result |
| = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account); |
| if (result != XML_ERROR_NONE) |
| return result; |
| else if (! next) { |
| parser->m_processor = cdataSectionProcessor; |
| return result; |
| } |
| } break; |
| case XML_TOK_TRAILING_RSQB: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| if (parser->m_characterDataHandler) { |
| if (MUST_CONVERT(enc, s)) { |
| ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; |
| XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd); |
| parser->m_characterDataHandler( |
| parser->m_handlerArg, parser->m_dataBuf, |
| (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); |
| } else |
| parser->m_characterDataHandler( |
| parser->m_handlerArg, (XML_Char *)s, |
| (int)((XML_Char *)end - (XML_Char *)s)); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, end); |
| /* We are at the end of the final buffer, should we check for |
| XML_SUSPENDED, XML_FINISHED? |
| */ |
| if (startTagLevel == 0) { |
| *eventPP = end; |
| return XML_ERROR_NO_ELEMENTS; |
| } |
| if (parser->m_tagLevel != startTagLevel) { |
| *eventPP = end; |
| return XML_ERROR_ASYNC_ENTITY; |
| } |
| *nextPtr = end; |
| return XML_ERROR_NONE; |
| case XML_TOK_DATA_CHARS: { |
| XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler; |
| if (charDataHandler) { |
| if (MUST_CONVERT(enc, s)) { |
| for (;;) { |
| ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; |
| const enum XML_Convert_Result convert_res = XmlConvert( |
| enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd); |
| *eventEndPP = s; |
| charDataHandler(parser->m_handlerArg, parser->m_dataBuf, |
| (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); |
| if ((convert_res == XML_CONVERT_COMPLETED) |
| || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) |
| break; |
| *eventPP = s; |
| } |
| } else |
| charDataHandler(parser->m_handlerArg, (XML_Char *)s, |
| (int)((XML_Char *)next - (XML_Char *)s)); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| } break; |
| case XML_TOK_PI: |
| if (! reportProcessingInstruction(parser, enc, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| case XML_TOK_COMMENT: |
| if (! reportComment(parser, enc, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| default: |
| /* All of the tokens produced by XmlContentTok() have their own |
| * explicit cases, so this default is not strictly necessary. |
| * However it is a useful safety net, so we retain the code and |
| * simply exclude it from the coverage tests. |
| * |
| * LCOV_EXCL_START |
| */ |
| if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| /* LCOV_EXCL_STOP */ |
| } |
| *eventPP = s = next; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| case XML_FINISHED: |
| return XML_ERROR_ABORTED; |
| default:; |
| } |
| } |
| /* not reached */ |
| } |
| |
| /* This function does not call free() on the allocated memory, merely |
| * moving it to the parser's m_freeBindingList where it can be freed or |
| * reused as appropriate. |
| */ |
| static void |
| freeBindings(XML_Parser parser, BINDING *bindings) { |
| while (bindings) { |
| BINDING *b = bindings; |
| |
| /* m_startNamespaceDeclHandler will have been called for this |
| * binding in addBindings(), so call the end handler now. |
| */ |
| if (parser->m_endNamespaceDeclHandler) |
| parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name); |
| |
| bindings = bindings->nextTagBinding; |
| b->nextTagBinding = parser->m_freeBindingList; |
| parser->m_freeBindingList = b; |
| b->prefix->binding = b->prevPrefixBinding; |
| } |
| } |
| |
| /* Precondition: all arguments must be non-NULL; |
| Purpose: |
| - normalize attributes |
| - check attributes for well-formedness |
| - generate namespace aware attribute names (URI, prefix) |
| - build list of attributes for startElementHandler |
| - default attributes |
| - process namespace declarations (check and report them) |
| - generate namespace aware element name (URI, prefix) |
| */ |
| static enum XML_Error |
| storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr, |
| TAG_NAME *tagNamePtr, BINDING **bindingsPtr, |
| enum XML_Account account) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| ELEMENT_TYPE *elementType; |
| int nDefaultAtts; |
| const XML_Char **appAtts; /* the attribute list for the application */ |
| int attIndex = 0; |
| int prefixLen; |
| int i; |
| int n; |
| XML_Char *uri; |
| int nPrefixes = 0; |
| BINDING *binding; |
| const XML_Char *localPart; |
| |
| /* lookup the element type name */ |
| elementType |
| = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str, 0); |
| if (! elementType) { |
| const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, |
| sizeof(ELEMENT_TYPE)); |
| if (! elementType) |
| return XML_ERROR_NO_MEMORY; |
| if (parser->m_ns && ! setElementTypePrefix(parser, elementType)) |
| return XML_ERROR_NO_MEMORY; |
| } |
| nDefaultAtts = elementType->nDefaultAtts; |
| |
| /* get the attributes from the tokenizer */ |
| n = XmlGetAttributes(enc, attStr, parser->m_attsSize, parser->m_atts); |
| |
| /* Detect and prevent integer overflow */ |
| if (n > INT_MAX - nDefaultAtts) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| if (n + nDefaultAtts > parser->m_attsSize) { |
| int oldAttsSize = parser->m_attsSize; |
| ATTRIBUTE *temp; |
| #ifdef XML_ATTR_INFO |
| XML_AttrInfo *temp2; |
| #endif |
| |
| /* Detect and prevent integer overflow */ |
| if ((nDefaultAtts > INT_MAX - INIT_ATTS_SIZE) |
| || (n > INT_MAX - (nDefaultAtts + INIT_ATTS_SIZE))) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| parser->m_attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; |
| |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(ATTRIBUTE)) { |
| parser->m_attsSize = oldAttsSize; |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts, |
| parser->m_attsSize * sizeof(ATTRIBUTE)); |
| if (temp == NULL) { |
| parser->m_attsSize = oldAttsSize; |
| return XML_ERROR_NO_MEMORY; |
| } |
| parser->m_atts = temp; |
| #ifdef XML_ATTR_INFO |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| # if UINT_MAX >= SIZE_MAX |
| if ((unsigned)parser->m_attsSize > (size_t)(-1) / sizeof(XML_AttrInfo)) { |
| parser->m_attsSize = oldAttsSize; |
| return XML_ERROR_NO_MEMORY; |
| } |
| # endif |
| |
| temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo, |
| parser->m_attsSize * sizeof(XML_AttrInfo)); |
| if (temp2 == NULL) { |
| parser->m_attsSize = oldAttsSize; |
| return XML_ERROR_NO_MEMORY; |
| } |
| parser->m_attInfo = temp2; |
| #endif |
| if (n > oldAttsSize) |
| XmlGetAttributes(enc, attStr, n, parser->m_atts); |
| } |
| |
| appAtts = (const XML_Char **)parser->m_atts; |
| for (i = 0; i < n; i++) { |
| ATTRIBUTE *currAtt = &parser->m_atts[i]; |
| #ifdef XML_ATTR_INFO |
| XML_AttrInfo *currAttInfo = &parser->m_attInfo[i]; |
| #endif |
| /* add the name and value to the attribute list */ |
| ATTRIBUTE_ID *attId |
| = getAttributeId(parser, enc, currAtt->name, |
| currAtt->name + XmlNameLength(enc, currAtt->name)); |
| if (! attId) |
| return XML_ERROR_NO_MEMORY; |
| #ifdef XML_ATTR_INFO |
| currAttInfo->nameStart |
| = parser->m_parseEndByteIndex - (parser->m_parseEndPtr - currAtt->name); |
| currAttInfo->nameEnd |
| = currAttInfo->nameStart + XmlNameLength(enc, currAtt->name); |
| currAttInfo->valueStart = parser->m_parseEndByteIndex |
| - (parser->m_parseEndPtr - currAtt->valuePtr); |
| currAttInfo->valueEnd = parser->m_parseEndByteIndex |
| - (parser->m_parseEndPtr - currAtt->valueEnd); |
| #endif |
| /* Detect duplicate attributes by their QNames. This does not work when |
| namespace processing is turned on and different prefixes for the same |
| namespace are used. For this case we have a check further down. |
| */ |
| if ((attId->name)[-1]) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = parser->m_atts[i].name; |
| return XML_ERROR_DUPLICATE_ATTRIBUTE; |
| } |
| (attId->name)[-1] = 1; |
| appAtts[attIndex++] = attId->name; |
| if (! parser->m_atts[i].normalized) { |
| enum XML_Error result; |
| XML_Bool isCdata = XML_TRUE; |
| |
| /* figure out whether declared as other than CDATA */ |
| if (attId->maybeTokenized) { |
| int j; |
| for (j = 0; j < nDefaultAtts; j++) { |
| if (attId == elementType->defaultAtts[j].id) { |
| isCdata = elementType->defaultAtts[j].isCdata; |
| break; |
| } |
| } |
| } |
| |
| /* normalize the attribute value */ |
| result = storeAttributeValue( |
| parser, enc, isCdata, parser->m_atts[i].valuePtr, |
| parser->m_atts[i].valueEnd, &parser->m_tempPool, account); |
| if (result) |
| return result; |
| appAtts[attIndex] = poolStart(&parser->m_tempPool); |
| poolFinish(&parser->m_tempPool); |
| } else { |
| /* the value did not need normalizing */ |
| appAtts[attIndex] = poolStoreString(&parser->m_tempPool, enc, |
| parser->m_atts[i].valuePtr, |
| parser->m_atts[i].valueEnd); |
| if (appAtts[attIndex] == 0) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_tempPool); |
| } |
| /* handle prefixed attribute names */ |
| if (attId->prefix) { |
| if (attId->xmlns) { |
| /* deal with namespace declarations here */ |
| enum XML_Error result = addBinding(parser, attId->prefix, attId, |
| appAtts[attIndex], bindingsPtr); |
| if (result) |
| return result; |
| --attIndex; |
| } else { |
| /* deal with other prefixed names later */ |
| attIndex++; |
| nPrefixes++; |
| (attId->name)[-1] = 2; |
| } |
| } else |
| attIndex++; |
| } |
| |
| /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ |
| parser->m_nSpecifiedAtts = attIndex; |
| if (elementType->idAtt && (elementType->idAtt->name)[-1]) { |
| for (i = 0; i < attIndex; i += 2) |
| if (appAtts[i] == elementType->idAtt->name) { |
| parser->m_idAttIndex = i; |
| break; |
| } |
| } else |
| parser->m_idAttIndex = -1; |
| |
| /* do attribute defaulting */ |
| for (i = 0; i < nDefaultAtts; i++) { |
| const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; |
| if (! (da->id->name)[-1] && da->value) { |
| if (da->id->prefix) { |
| if (da->id->xmlns) { |
| enum XML_Error result = addBinding(parser, da->id->prefix, da->id, |
| da->value, bindingsPtr); |
| if (result) |
| return result; |
| } else { |
| (da->id->name)[-1] = 2; |
| nPrefixes++; |
| appAtts[attIndex++] = da->id->name; |
| appAtts[attIndex++] = da->value; |
| } |
| } else { |
| (da->id->name)[-1] = 1; |
| appAtts[attIndex++] = da->id->name; |
| appAtts[attIndex++] = da->value; |
| } |
| } |
| } |
| appAtts[attIndex] = 0; |
| |
| /* expand prefixed attribute names, check for duplicates, |
| and clear flags that say whether attributes were specified */ |
| i = 0; |
| if (nPrefixes) { |
| int j; /* hash table index */ |
| unsigned long version = parser->m_nsAttsVersion; |
| |
| /* Detect and prevent invalid shift */ |
| if (parser->m_nsAttsPower >= sizeof(unsigned int) * 8 /* bits per byte */) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| unsigned int nsAttsSize = 1u << parser->m_nsAttsPower; |
| unsigned char oldNsAttsPower = parser->m_nsAttsPower; |
| /* size of hash table must be at least 2 * (# of prefixed attributes) */ |
| if ((nPrefixes << 1) |
| >> parser->m_nsAttsPower) { /* true for m_nsAttsPower = 0 */ |
| NS_ATT *temp; |
| /* hash table size must also be a power of 2 and >= 8 */ |
| while (nPrefixes >> parser->m_nsAttsPower++) |
| ; |
| if (parser->m_nsAttsPower < 3) |
| parser->m_nsAttsPower = 3; |
| |
| /* Detect and prevent invalid shift */ |
| if (parser->m_nsAttsPower >= sizeof(nsAttsSize) * 8 /* bits per byte */) { |
| /* Restore actual size of memory in m_nsAtts */ |
| parser->m_nsAttsPower = oldNsAttsPower; |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| nsAttsSize = 1u << parser->m_nsAttsPower; |
| |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if (nsAttsSize > (size_t)(-1) / sizeof(NS_ATT)) { |
| /* Restore actual size of memory in m_nsAtts */ |
| parser->m_nsAttsPower = oldNsAttsPower; |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts, |
| nsAttsSize * sizeof(NS_ATT)); |
| if (! temp) { |
| /* Restore actual size of memory in m_nsAtts */ |
| parser->m_nsAttsPower = oldNsAttsPower; |
| return XML_ERROR_NO_MEMORY; |
| } |
| parser->m_nsAtts = temp; |
| version = 0; /* force re-initialization of m_nsAtts hash table */ |
| } |
| /* using a version flag saves us from initializing m_nsAtts every time */ |
| if (! version) { /* initialize version flags when version wraps around */ |
| version = INIT_ATTS_VERSION; |
| for (j = nsAttsSize; j != 0;) |
| parser->m_nsAtts[--j].version = version; |
| } |
| parser->m_nsAttsVersion = --version; |
| |
| /* expand prefixed names and check for duplicates */ |
| for (; i < attIndex; i += 2) { |
| const XML_Char *s = appAtts[i]; |
| if (s[-1] == 2) { /* prefixed */ |
| ATTRIBUTE_ID *id; |
| const BINDING *b; |
| unsigned long uriHash; |
| struct siphash sip_state; |
| struct sipkey sip_key; |
| |
| copy_salt_to_sipkey(parser, &sip_key); |
| sip24_init(&sip_state, &sip_key); |
| |
| ((XML_Char *)s)[-1] = 0; /* clear flag */ |
| id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); |
| if (! id || ! id->prefix) { |
| /* This code is walking through the appAtts array, dealing |
| * with (in this case) a prefixed attribute name. To be in |
| * the array, the attribute must have already been bound, so |
| * has to have passed through the hash table lookup once |
| * already. That implies that an entry for it already |
| * exists, so the lookup above will return a pointer to |
| * already allocated memory. There is no opportunaity for |
| * the allocator to fail, so the condition above cannot be |
| * fulfilled. |
| * |
| * Since it is difficult to be certain that the above |
| * analysis is complete, we retain the test and merely |
| * remove the code from coverage tests. |
| */ |
| return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */ |
| } |
| b = id->prefix->binding; |
| if (! b) |
| return XML_ERROR_UNBOUND_PREFIX; |
| |
| for (j = 0; j < b->uriLen; j++) { |
| const XML_Char c = b->uri[j]; |
| if (! poolAppendChar(&parser->m_tempPool, c)) |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| sip24_update(&sip_state, b->uri, b->uriLen * sizeof(XML_Char)); |
| |
| while (*s++ != XML_T(ASCII_COLON)) |
| ; |
| |
| sip24_update(&sip_state, s, keylen(s) * sizeof(XML_Char)); |
| |
| do { /* copies null terminator */ |
| if (! poolAppendChar(&parser->m_tempPool, *s)) |
| return XML_ERROR_NO_MEMORY; |
| } while (*s++); |
| |
| uriHash = (unsigned long)sip24_final(&sip_state); |
| |
| { /* Check hash table for duplicate of expanded name (uriName). |
| Derived from code in lookup(parser, HASH_TABLE *table, ...). |
| */ |
| unsigned char step = 0; |
| unsigned long mask = nsAttsSize - 1; |
| j = uriHash & mask; /* index into hash table */ |
| while (parser->m_nsAtts[j].version == version) { |
| /* for speed we compare stored hash values first */ |
| if (uriHash == parser->m_nsAtts[j].hash) { |
| const XML_Char *s1 = poolStart(&parser->m_tempPool); |
| const XML_Char *s2 = parser->m_nsAtts[j].uriName; |
| /* s1 is null terminated, but not s2 */ |
| for (; *s1 == *s2 && *s1 != 0; s1++, s2++) |
| ; |
| if (*s1 == 0) |
| return XML_ERROR_DUPLICATE_ATTRIBUTE; |
| } |
| if (! step) |
| step = PROBE_STEP(uriHash, mask, parser->m_nsAttsPower); |
| j < step ? (j += nsAttsSize - step) : (j -= step); |
| } |
| } |
| |
| if (parser->m_ns_triplets) { /* append namespace separator and prefix */ |
| parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator; |
| s = b->prefix->name; |
| do { |
| if (! poolAppendChar(&parser->m_tempPool, *s)) |
| return XML_ERROR_NO_MEMORY; |
| } while (*s++); |
| } |
| |
| /* store expanded name in attribute list */ |
| s = poolStart(&parser->m_tempPool); |
| poolFinish(&parser->m_tempPool); |
| appAtts[i] = s; |
| |
| /* fill empty slot with new version, uriName and hash value */ |
| parser->m_nsAtts[j].version = version; |
| parser->m_nsAtts[j].hash = uriHash; |
| parser->m_nsAtts[j].uriName = s; |
| |
| if (! --nPrefixes) { |
| i += 2; |
| break; |
| } |
| } else /* not prefixed */ |
| ((XML_Char *)s)[-1] = 0; /* clear flag */ |
| } |
| } |
| /* clear flags for the remaining attributes */ |
| for (; i < attIndex; i += 2) |
| ((XML_Char *)(appAtts[i]))[-1] = 0; |
| for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) |
| binding->attId->name[-1] = 0; |
| |
| if (! parser->m_ns) |
| return XML_ERROR_NONE; |
| |
| /* expand the element type name */ |
| if (elementType->prefix) { |
| binding = elementType->prefix->binding; |
| if (! binding) |
| return XML_ERROR_UNBOUND_PREFIX; |
| localPart = tagNamePtr->str; |
| while (*localPart++ != XML_T(ASCII_COLON)) |
| ; |
| } else if (dtd->defaultPrefix.binding) { |
| binding = dtd->defaultPrefix.binding; |
| localPart = tagNamePtr->str; |
| } else |
| return XML_ERROR_NONE; |
| prefixLen = 0; |
| if (parser->m_ns_triplets && binding->prefix->name) { |
| for (; binding->prefix->name[prefixLen++];) |
| ; /* prefixLen includes null terminator */ |
| } |
| tagNamePtr->localPart = localPart; |
| tagNamePtr->uriLen = binding->uriLen; |
| tagNamePtr->prefix = binding->prefix->name; |
| tagNamePtr->prefixLen = prefixLen; |
| for (i = 0; localPart[i++];) |
| ; /* i includes null terminator */ |
| |
| /* Detect and prevent integer overflow */ |
| if (binding->uriLen > INT_MAX - prefixLen |
| || i > INT_MAX - (binding->uriLen + prefixLen)) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| n = i + binding->uriLen + prefixLen; |
| if (n > binding->uriAlloc) { |
| TAG *p; |
| |
| /* Detect and prevent integer overflow */ |
| if (n > INT_MAX - EXPAND_SPARE) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if ((unsigned)(n + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char)); |
| if (! uri) |
| return XML_ERROR_NO_MEMORY; |
| binding->uriAlloc = n + EXPAND_SPARE; |
| memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); |
| for (p = parser->m_tagStack; p; p = p->parent) |
| if (p->name.str == binding->uri) |
| p->name.str = uri; |
| FREE(parser, binding->uri); |
| binding->uri = uri; |
| } |
| /* if m_namespaceSeparator != '\0' then uri includes it already */ |
| uri = binding->uri + binding->uriLen; |
| memcpy(uri, localPart, i * sizeof(XML_Char)); |
| /* we always have a namespace separator between localPart and prefix */ |
| if (prefixLen) { |
| uri += i - 1; |
| *uri = parser->m_namespaceSeparator; /* replace null terminator */ |
| memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); |
| } |
| tagNamePtr->str = binding->uri; |
| return XML_ERROR_NONE; |
| } |
| |
| /* addBinding() overwrites the value of prefix->binding without checking. |
| Therefore one must keep track of the old value outside of addBinding(). |
| */ |
| static enum XML_Error |
| addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, |
| const XML_Char *uri, BINDING **bindingsPtr) { |
| static const XML_Char xmlNamespace[] |
| = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, |
| ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, |
| ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, |
| ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, |
| ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, |
| ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, |
| ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, |
| ASCII_e, '\0'}; |
| static const int xmlLen = (int)sizeof(xmlNamespace) / sizeof(XML_Char) - 1; |
| static const XML_Char xmlnsNamespace[] |
| = {ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, |
| ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, |
| ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, |
| ASCII_2, ASCII_0, ASCII_0, ASCII_0, ASCII_SLASH, ASCII_x, |
| ASCII_m, ASCII_l, ASCII_n, ASCII_s, ASCII_SLASH, '\0'}; |
| static const int xmlnsLen |
| = (int)sizeof(xmlnsNamespace) / sizeof(XML_Char) - 1; |
| |
| XML_Bool mustBeXML = XML_FALSE; |
| XML_Bool isXML = XML_TRUE; |
| XML_Bool isXMLNS = XML_TRUE; |
| |
| BINDING *b; |
| int len; |
| |
| /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ |
| if (*uri == XML_T('\0') && prefix->name) |
| return XML_ERROR_UNDECLARING_PREFIX; |
| |
| if (prefix->name && prefix->name[0] == XML_T(ASCII_x) |
| && prefix->name[1] == XML_T(ASCII_m) |
| && prefix->name[2] == XML_T(ASCII_l)) { |
| /* Not allowed to bind xmlns */ |
| if (prefix->name[3] == XML_T(ASCII_n) && prefix->name[4] == XML_T(ASCII_s) |
| && prefix->name[5] == XML_T('\0')) |
| return XML_ERROR_RESERVED_PREFIX_XMLNS; |
| |
| if (prefix->name[3] == XML_T('\0')) |
| mustBeXML = XML_TRUE; |
| } |
| |
| for (len = 0; uri[len]; len++) { |
| if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) |
| isXML = XML_FALSE; |
| |
| if (! mustBeXML && isXMLNS |
| && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) |
| isXMLNS = XML_FALSE; |
| |
| // NOTE: While Expat does not validate namespace URIs against RFC 3986, |
| // we have to at least make sure that the XML processor on top of |
| // Expat (that is splitting tag names by namespace separator into |
| // 2- or 3-tuples (uri-local or uri-local-prefix)) cannot be confused |
| // by an attacker putting additional namespace separator characters |
| // into namespace declarations. That would be ambiguous and not to |
| // be expected. |
| if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)) { |
| return XML_ERROR_SYNTAX; |
| } |
| } |
| isXML = isXML && len == xmlLen; |
| isXMLNS = isXMLNS && len == xmlnsLen; |
| |
| if (mustBeXML != isXML) |
| return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML |
| : XML_ERROR_RESERVED_NAMESPACE_URI; |
| |
| if (isXMLNS) |
| return XML_ERROR_RESERVED_NAMESPACE_URI; |
| |
| if (parser->m_namespaceSeparator) |
| len++; |
| if (parser->m_freeBindingList) { |
| b = parser->m_freeBindingList; |
| if (len > b->uriAlloc) { |
| /* Detect and prevent integer overflow */ |
| if (len > INT_MAX - EXPAND_SPARE) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| XML_Char *temp = (XML_Char *)REALLOC( |
| parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE)); |
| if (temp == NULL) |
| return XML_ERROR_NO_MEMORY; |
| b->uri = temp; |
| b->uriAlloc = len + EXPAND_SPARE; |
| } |
| parser->m_freeBindingList = b->nextTagBinding; |
| } else { |
| b = (BINDING *)MALLOC(parser, sizeof(BINDING)); |
| if (! b) |
| return XML_ERROR_NO_MEMORY; |
| |
| /* Detect and prevent integer overflow */ |
| if (len > INT_MAX - EXPAND_SPARE) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if ((unsigned)(len + EXPAND_SPARE) > (size_t)(-1) / sizeof(XML_Char)) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| b->uri |
| = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE)); |
| if (! b->uri) { |
| FREE(parser, b); |
| return XML_ERROR_NO_MEMORY; |
| } |
| b->uriAlloc = len + EXPAND_SPARE; |
| } |
| b->uriLen = len; |
| memcpy(b->uri, uri, len * sizeof(XML_Char)); |
| if (parser->m_namespaceSeparator) |
| b->uri[len - 1] = parser->m_namespaceSeparator; |
| b->prefix = prefix; |
| b->attId = attId; |
| b->prevPrefixBinding = prefix->binding; |
| /* NULL binding when default namespace undeclared */ |
| if (*uri == XML_T('\0') && prefix == &parser->m_dtd->defaultPrefix) |
| prefix->binding = NULL; |
| else |
| prefix->binding = b; |
| b->nextTagBinding = *bindingsPtr; |
| *bindingsPtr = b; |
| /* if attId == NULL then we are not starting a namespace scope */ |
| if (attId && parser->m_startNamespaceDeclHandler) |
| parser->m_startNamespaceDeclHandler(parser->m_handlerArg, prefix->name, |
| prefix->binding ? uri : 0); |
| return XML_ERROR_NONE; |
| } |
| |
| /* The idea here is to avoid using stack for each CDATA section when |
| the whole file is parsed with one call. |
| */ |
| static enum XML_Error PTRCALL |
| cdataSectionProcessor(XML_Parser parser, const char *start, const char *end, |
| const char **endPtr) { |
| enum XML_Error result = doCdataSection( |
| parser, parser->m_encoding, &start, end, endPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT); |
| if (result != XML_ERROR_NONE) |
| return result; |
| if (start) { |
| if (parser->m_parentParser) { /* we are parsing an external entity */ |
| parser->m_processor = externalEntityContentProcessor; |
| return externalEntityContentProcessor(parser, start, end, endPtr); |
| } else { |
| parser->m_processor = contentProcessor; |
| return contentProcessor(parser, start, end, endPtr); |
| } |
| } |
| return result; |
| } |
| |
| /* startPtr gets set to non-null if the section is closed, and to null if |
| the section is not yet closed. |
| */ |
| static enum XML_Error |
| doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, |
| const char *end, const char **nextPtr, XML_Bool haveMore, |
| enum XML_Account account) { |
| const char *s = *startPtr; |
| const char **eventPP; |
| const char **eventEndPP; |
| if (enc == parser->m_encoding) { |
| eventPP = &parser->m_eventPtr; |
| *eventPP = s; |
| eventEndPP = &parser->m_eventEndPtr; |
| } else { |
| eventPP = &(parser->m_openInternalEntities->internalEventPtr); |
| eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); |
| } |
| *eventPP = s; |
| *startPtr = NULL; |
| |
| for (;;) { |
| const char *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ |
| int tok = XmlCdataSectionTok(enc, s, end, &next); |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #else |
| UNUSED_P(account); |
| #endif |
| *eventEndPP = next; |
| switch (tok) { |
| case XML_TOK_CDATA_SECT_CLOSE: |
| if (parser->m_endCdataSectionHandler) |
| parser->m_endCdataSectionHandler(parser->m_handlerArg); |
| /* BEGIN disabled code */ |
| /* see comment under XML_TOK_CDATA_SECT_OPEN */ |
| else if (0 && parser->m_characterDataHandler) |
| parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, |
| 0); |
| /* END disabled code */ |
| else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| *startPtr = next; |
| *nextPtr = next; |
| if (parser->m_parsingStatus.parsing == XML_FINISHED) |
| return XML_ERROR_ABORTED; |
| else |
| return XML_ERROR_NONE; |
| case XML_TOK_DATA_NEWLINE: |
| if (parser->m_characterDataHandler) { |
| XML_Char c = 0xA; |
| parser->m_characterDataHandler(parser->m_handlerArg, &c, 1); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| break; |
| case XML_TOK_DATA_CHARS: { |
| XML_CharacterDataHandler charDataHandler = parser->m_characterDataHandler; |
| if (charDataHandler) { |
| if (MUST_CONVERT(enc, s)) { |
| for (;;) { |
| ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; |
| const enum XML_Convert_Result convert_res = XmlConvert( |
| enc, &s, next, &dataPtr, (ICHAR *)parser->m_dataBufEnd); |
| *eventEndPP = next; |
| charDataHandler(parser->m_handlerArg, parser->m_dataBuf, |
| (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); |
| if ((convert_res == XML_CONVERT_COMPLETED) |
| || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) |
| break; |
| *eventPP = s; |
| } |
| } else |
| charDataHandler(parser->m_handlerArg, (XML_Char *)s, |
| (int)((XML_Char *)next - (XML_Char *)s)); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| } break; |
| case XML_TOK_INVALID: |
| *eventPP = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_PARTIAL: |
| case XML_TOK_NONE: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_UNCLOSED_CDATA_SECTION; |
| default: |
| /* Every token returned by XmlCdataSectionTok() has its own |
| * explicit case, so this default case will never be executed. |
| * We retain it as a safety net and exclude it from the coverage |
| * statistics. |
| * |
| * LCOV_EXCL_START |
| */ |
| *eventPP = next; |
| return XML_ERROR_UNEXPECTED_STATE; |
| /* LCOV_EXCL_STOP */ |
| } |
| |
| *eventPP = s = next; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| case XML_FINISHED: |
| return XML_ERROR_ABORTED; |
| default:; |
| } |
| } |
| /* not reached */ |
| } |
| |
| #ifdef XML_DTD |
| |
| /* The idea here is to avoid using stack for each IGNORE section when |
| the whole file is parsed with one call. |
| */ |
| static enum XML_Error PTRCALL |
| ignoreSectionProcessor(XML_Parser parser, const char *start, const char *end, |
| const char **endPtr) { |
| enum XML_Error result |
| = doIgnoreSection(parser, parser->m_encoding, &start, end, endPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer); |
| if (result != XML_ERROR_NONE) |
| return result; |
| if (start) { |
| parser->m_processor = prologProcessor; |
| return prologProcessor(parser, start, end, endPtr); |
| } |
| return result; |
| } |
| |
| /* startPtr gets set to non-null is the section is closed, and to null |
| if the section is not yet closed. |
| */ |
| static enum XML_Error |
| doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr, |
| const char *end, const char **nextPtr, XML_Bool haveMore) { |
| const char *next = *startPtr; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ |
| int tok; |
| const char *s = *startPtr; |
| const char **eventPP; |
| const char **eventEndPP; |
| if (enc == parser->m_encoding) { |
| eventPP = &parser->m_eventPtr; |
| *eventPP = s; |
| eventEndPP = &parser->m_eventEndPtr; |
| } else { |
| /* It's not entirely clear, but it seems the following two lines |
| * of code cannot be executed. The only occasions on which 'enc' |
| * is not 'encoding' are when this function is called |
| * from the internal entity processing, and IGNORE sections are an |
| * error in internal entities. |
| * |
| * Since it really isn't clear that this is true, we keep the code |
| * and just remove it from our coverage tests. |
| * |
| * LCOV_EXCL_START |
| */ |
| eventPP = &(parser->m_openInternalEntities->internalEventPtr); |
| eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); |
| /* LCOV_EXCL_STOP */ |
| } |
| *eventPP = s; |
| *startPtr = NULL; |
| tok = XmlIgnoreSectionTok(enc, s, end, &next); |
| # ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| # endif |
| *eventEndPP = next; |
| switch (tok) { |
| case XML_TOK_IGNORE_SECT: |
| if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| *startPtr = next; |
| *nextPtr = next; |
| if (parser->m_parsingStatus.parsing == XML_FINISHED) |
| return XML_ERROR_ABORTED; |
| else |
| return XML_ERROR_NONE; |
| case XML_TOK_INVALID: |
| *eventPP = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_PARTIAL: |
| case XML_TOK_NONE: |
| if (haveMore) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ |
| default: |
| /* All of the tokens that XmlIgnoreSectionTok() returns have |
| * explicit cases to handle them, so this default case is never |
| * executed. We keep it as a safety net anyway, and remove it |
| * from our test coverage statistics. |
| * |
| * LCOV_EXCL_START |
| */ |
| *eventPP = next; |
| return XML_ERROR_UNEXPECTED_STATE; |
| /* LCOV_EXCL_STOP */ |
| } |
| /* not reached */ |
| } |
| |
| #endif /* XML_DTD */ |
| |
| static enum XML_Error |
| initializeEncoding(XML_Parser parser) { |
| const char *s; |
| #ifdef XML_UNICODE |
| char encodingBuf[128]; |
| /* See comments about `protocolEncodingName` in parserInit() */ |
| if (! parser->m_protocolEncodingName) |
| s = NULL; |
| else { |
| int i; |
| for (i = 0; parser->m_protocolEncodingName[i]; i++) { |
| if (i == sizeof(encodingBuf) - 1 |
| || (parser->m_protocolEncodingName[i] & ~0x7f) != 0) { |
| encodingBuf[0] = '\0'; |
| break; |
| } |
| encodingBuf[i] = (char)parser->m_protocolEncodingName[i]; |
| } |
| encodingBuf[i] = '\0'; |
| s = encodingBuf; |
| } |
| #else |
| s = parser->m_protocolEncodingName; |
| #endif |
| if ((parser->m_ns ? XmlInitEncodingNS : XmlInitEncoding)( |
| &parser->m_initEncoding, &parser->m_encoding, s)) |
| return XML_ERROR_NONE; |
| return handleUnknownEncoding(parser, parser->m_protocolEncodingName); |
| } |
| |
| static enum XML_Error |
| processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s, |
| const char *next) { |
| const char *encodingName = NULL; |
| const XML_Char *storedEncName = NULL; |
| const ENCODING *newEncoding = NULL; |
| const char *version = NULL; |
| const char *versionend; |
| const XML_Char *storedversion = NULL; |
| int standalone = -1; |
| |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #endif |
| |
| if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)( |
| isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr, |
| &version, &versionend, &encodingName, &newEncoding, &standalone)) { |
| if (isGeneralTextEntity) |
| return XML_ERROR_TEXT_DECL; |
| else |
| return XML_ERROR_XML_DECL; |
| } |
| if (! isGeneralTextEntity && standalone == 1) { |
| parser->m_dtd->standalone = XML_TRUE; |
| #ifdef XML_DTD |
| if (parser->m_paramEntityParsing |
| == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) |
| parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; |
| #endif /* XML_DTD */ |
| } |
| if (parser->m_xmlDeclHandler) { |
| if (encodingName != NULL) { |
| storedEncName = poolStoreString( |
| &parser->m_temp2Pool, parser->m_encoding, encodingName, |
| encodingName + XmlNameLength(parser->m_encoding, encodingName)); |
| if (! storedEncName) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_temp2Pool); |
| } |
| if (version) { |
| storedversion |
| = poolStoreString(&parser->m_temp2Pool, parser->m_encoding, version, |
| versionend - parser->m_encoding->minBytesPerChar); |
| if (! storedversion) |
| return XML_ERROR_NO_MEMORY; |
| } |
| parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName, |
| standalone); |
| } else if (parser->m_defaultHandler) |
| reportDefault(parser, parser->m_encoding, s, next); |
| if (parser->m_protocolEncodingName == NULL) { |
| if (newEncoding) { |
| /* Check that the specified encoding does not conflict with what |
| * the parser has already deduced. Do we have the same number |
| * of bytes in the smallest representation of a character? If |
| * this is UTF-16, is it the same endianness? |
| */ |
| if (newEncoding->minBytesPerChar != parser->m_encoding->minBytesPerChar |
| || (newEncoding->minBytesPerChar == 2 |
| && newEncoding != parser->m_encoding)) { |
| parser->m_eventPtr = encodingName; |
| return XML_ERROR_INCORRECT_ENCODING; |
| } |
| parser->m_encoding = newEncoding; |
| } else if (encodingName) { |
| enum XML_Error result; |
| if (! storedEncName) { |
| storedEncName = poolStoreString( |
| &parser->m_temp2Pool, parser->m_encoding, encodingName, |
| encodingName + XmlNameLength(parser->m_encoding, encodingName)); |
| if (! storedEncName) |
| return XML_ERROR_NO_MEMORY; |
| } |
| result = handleUnknownEncoding(parser, storedEncName); |
| poolClear(&parser->m_temp2Pool); |
| if (result == XML_ERROR_UNKNOWN_ENCODING) |
| parser->m_eventPtr = encodingName; |
| return result; |
| } |
| } |
| |
| if (storedEncName || storedversion) |
| poolClear(&parser->m_temp2Pool); |
| |
| return XML_ERROR_NONE; |
| } |
| |
| static enum XML_Error |
| handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) { |
| if (parser->m_unknownEncodingHandler) { |
| XML_Encoding info; |
| int i; |
| for (i = 0; i < 256; i++) |
| info.map[i] = -1; |
| info.convert = NULL; |
| info.data = NULL; |
| info.release = NULL; |
| if (parser->m_unknownEncodingHandler(parser->m_unknownEncodingHandlerData, |
| encodingName, &info)) { |
| ENCODING *enc; |
| parser->m_unknownEncodingMem = MALLOC(parser, XmlSizeOfUnknownEncoding()); |
| if (! parser->m_unknownEncodingMem) { |
| if (info.release) |
| info.release(info.data); |
| return XML_ERROR_NO_MEMORY; |
| } |
| enc = (parser->m_ns ? XmlInitUnknownEncodingNS : XmlInitUnknownEncoding)( |
| parser->m_unknownEncodingMem, info.map, info.convert, info.data); |
| if (enc) { |
| parser->m_unknownEncodingData = info.data; |
| parser->m_unknownEncodingRelease = info.release; |
| parser->m_encoding = enc; |
| return XML_ERROR_NONE; |
| } |
| } |
| if (info.release != NULL) |
| info.release(info.data); |
| } |
| return XML_ERROR_UNKNOWN_ENCODING; |
| } |
| |
| static enum XML_Error PTRCALL |
| prologInitProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| enum XML_Error result = initializeEncoding(parser); |
| if (result != XML_ERROR_NONE) |
| return result; |
| parser->m_processor = prologProcessor; |
| return prologProcessor(parser, s, end, nextPtr); |
| } |
| |
| #ifdef XML_DTD |
| |
| static enum XML_Error PTRCALL |
| externalParEntInitProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| enum XML_Error result = initializeEncoding(parser); |
| if (result != XML_ERROR_NONE) |
| return result; |
| |
| /* we know now that XML_Parse(Buffer) has been called, |
| so we consider the external parameter entity read */ |
| parser->m_dtd->paramEntityRead = XML_TRUE; |
| |
| if (parser->m_prologState.inEntityValue) { |
| parser->m_processor = entityValueInitProcessor; |
| return entityValueInitProcessor(parser, s, end, nextPtr); |
| } else { |
| parser->m_processor = externalParEntProcessor; |
| return externalParEntProcessor(parser, s, end, nextPtr); |
| } |
| } |
| |
| static enum XML_Error PTRCALL |
| entityValueInitProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| int tok; |
| const char *start = s; |
| const char *next = start; |
| parser->m_eventPtr = start; |
| |
| for (;;) { |
| tok = XmlPrologTok(parser->m_encoding, start, end, &next); |
| /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in: |
| - storeEntityValue |
| - processXmlDecl |
| */ |
| parser->m_eventEndPtr = next; |
| if (tok <= 0) { |
| if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| switch (tok) { |
| case XML_TOK_INVALID: |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_NONE: /* start == end */ |
| default: |
| break; |
| } |
| /* found end of entity value - can store it now */ |
| return storeEntityValue(parser, parser->m_encoding, s, end, |
| XML_ACCOUNT_DIRECT); |
| } else if (tok == XML_TOK_XML_DECL) { |
| enum XML_Error result; |
| result = processXmlDecl(parser, 0, start, next); |
| if (result != XML_ERROR_NONE) |
| return result; |
| /* At this point, m_parsingStatus.parsing cannot be XML_SUSPENDED. For |
| * that to happen, a parameter entity parsing handler must have attempted |
| * to suspend the parser, which fails and raises an error. The parser can |
| * be aborted, but can't be suspended. |
| */ |
| if (parser->m_parsingStatus.parsing == XML_FINISHED) |
| return XML_ERROR_ABORTED; |
| *nextPtr = next; |
| /* stop scanning for text declaration - we found one */ |
| parser->m_processor = entityValueProcessor; |
| return entityValueProcessor(parser, next, end, nextPtr); |
| } |
| /* If we are at the end of the buffer, this would cause XmlPrologTok to |
| return XML_TOK_NONE on the next call, which would then cause the |
| function to exit with *nextPtr set to s - that is what we want for other |
| tokens, but not for the BOM - we would rather like to skip it; |
| then, when this routine is entered the next time, XmlPrologTok will |
| return XML_TOK_INVALID, since the BOM is still in the buffer |
| */ |
| else if (tok == XML_TOK_BOM && next == end |
| && ! parser->m_parsingStatus.finalBuffer) { |
| # ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| # endif |
| |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| } |
| /* If we get this token, we have the start of what might be a |
| normal tag, but not a declaration (i.e. it doesn't begin with |
| "<!"). In a DTD context, that isn't legal. |
| */ |
| else if (tok == XML_TOK_INSTANCE_START) { |
| *nextPtr = next; |
| return XML_ERROR_SYNTAX; |
| } |
| start = next; |
| parser->m_eventPtr = start; |
| } |
| } |
| |
| static enum XML_Error PTRCALL |
| externalParEntProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| const char *next = s; |
| int tok; |
| |
| tok = XmlPrologTok(parser->m_encoding, s, end, &next); |
| if (tok <= 0) { |
| if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| switch (tok) { |
| case XML_TOK_INVALID: |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_NONE: /* start == end */ |
| default: |
| break; |
| } |
| } |
| /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. |
| However, when parsing an external subset, doProlog will not accept a BOM |
| as valid, and report a syntax error, so we have to skip the BOM, and |
| account for the BOM bytes. |
| */ |
| else if (tok == XML_TOK_BOM) { |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| |
| s = next; |
| tok = XmlPrologTok(parser->m_encoding, s, end, &next); |
| } |
| |
| parser->m_processor = prologProcessor; |
| return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, |
| XML_ACCOUNT_DIRECT); |
| } |
| |
| static enum XML_Error PTRCALL |
| entityValueProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| const char *start = s; |
| const char *next = s; |
| const ENCODING *enc = parser->m_encoding; |
| int tok; |
| |
| for (;;) { |
| tok = XmlPrologTok(enc, start, end, &next); |
| /* Note: These bytes are accounted later in: |
| - storeEntityValue |
| */ |
| if (tok <= 0) { |
| if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| switch (tok) { |
| case XML_TOK_INVALID: |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| return XML_ERROR_PARTIAL_CHAR; |
| case XML_TOK_NONE: /* start == end */ |
| default: |
| break; |
| } |
| /* found end of entity value - can store it now */ |
| return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT); |
| } |
| start = next; |
| } |
| } |
| |
| #endif /* XML_DTD */ |
| |
| static enum XML_Error PTRCALL |
| prologProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| const char *next = s; |
| int tok = XmlPrologTok(parser->m_encoding, s, end, &next); |
| return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, |
| XML_ACCOUNT_DIRECT); |
| } |
| |
| static enum XML_Error |
| doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end, |
| int tok, const char *next, const char **nextPtr, XML_Bool haveMore, |
| XML_Bool allowClosingDoctype, enum XML_Account account) { |
| #ifdef XML_DTD |
| static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'}; |
| #endif /* XML_DTD */ |
| static const XML_Char atypeCDATA[] |
| = {ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0'}; |
| static const XML_Char atypeID[] = {ASCII_I, ASCII_D, '\0'}; |
| static const XML_Char atypeIDREF[] |
| = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0'}; |
| static const XML_Char atypeIDREFS[] |
| = {ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0'}; |
| static const XML_Char atypeENTITY[] |
| = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0'}; |
| static const XML_Char atypeENTITIES[] |
| = {ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, |
| ASCII_I, ASCII_E, ASCII_S, '\0'}; |
| static const XML_Char atypeNMTOKEN[] |
| = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0'}; |
| static const XML_Char atypeNMTOKENS[] |
| = {ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, |
| ASCII_E, ASCII_N, ASCII_S, '\0'}; |
| static const XML_Char notationPrefix[] |
| = {ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, |
| ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0'}; |
| static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'}; |
| static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'}; |
| |
| #ifndef XML_DTD |
| UNUSED_P(account); |
| #endif |
| |
| /* save one level of indirection */ |
| DTD *const dtd = parser->m_dtd; |
| |
| const char **eventPP; |
| const char **eventEndPP; |
| enum XML_Content_Quant quant; |
| |
| if (enc == parser->m_encoding) { |
| eventPP = &parser->m_eventPtr; |
| eventEndPP = &parser->m_eventEndPtr; |
| } else { |
| eventPP = &(parser->m_openInternalEntities->internalEventPtr); |
| eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); |
| } |
| |
| for (;;) { |
| int role; |
| XML_Bool handleDefault = XML_TRUE; |
| *eventPP = s; |
| *eventEndPP = next; |
| if (tok <= 0) { |
| if (haveMore && tok != XML_TOK_INVALID) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| switch (tok) { |
| case XML_TOK_INVALID: |
| *eventPP = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| return XML_ERROR_PARTIAL_CHAR; |
| case -XML_TOK_PROLOG_S: |
| tok = -tok; |
| break; |
| case XML_TOK_NONE: |
| #ifdef XML_DTD |
| /* for internal PE NOT referenced between declarations */ |
| if (enc != parser->m_encoding |
| && ! parser->m_openInternalEntities->betweenDecl) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| /* WFC: PE Between Declarations - must check that PE contains |
| complete markup, not only for external PEs, but also for |
| internal PEs if the reference occurs between declarations. |
| */ |
| if (parser->m_isParamEntity || enc != parser->m_encoding) { |
| if (XmlTokenRole(&parser->m_prologState, XML_TOK_NONE, end, end, enc) |
| == XML_ROLE_ERROR) |
| return XML_ERROR_INCOMPLETE_PE; |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| #endif /* XML_DTD */ |
| return XML_ERROR_NO_ELEMENTS; |
| default: |
| tok = -tok; |
| next = end; |
| break; |
| } |
| } |
| role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc); |
| #ifdef XML_DTD |
| switch (role) { |
| case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor |
| case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl |
| case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl |
| break; |
| default: |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| } |
| #endif |
| switch (role) { |
| case XML_ROLE_XML_DECL: { |
| enum XML_Error result = processXmlDecl(parser, 0, s, next); |
| if (result != XML_ERROR_NONE) |
| return result; |
| enc = parser->m_encoding; |
| handleDefault = XML_FALSE; |
| } break; |
| case XML_ROLE_DOCTYPE_NAME: |
| if (parser->m_startDoctypeDeclHandler) { |
| parser->m_doctypeName |
| = poolStoreString(&parser->m_tempPool, enc, s, next); |
| if (! parser->m_doctypeName) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_tempPool); |
| parser->m_doctypePubid = NULL; |
| handleDefault = XML_FALSE; |
| } |
| parser->m_doctypeSysid = NULL; /* always initialize to NULL */ |
| break; |
| case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: |
| if (parser->m_startDoctypeDeclHandler) { |
| parser->m_startDoctypeDeclHandler( |
| parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, |
| parser->m_doctypePubid, 1); |
| parser->m_doctypeName = NULL; |
| poolClear(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| #ifdef XML_DTD |
| case XML_ROLE_TEXT_DECL: { |
| enum XML_Error result = processXmlDecl(parser, 1, s, next); |
| if (result != XML_ERROR_NONE) |
| return result; |
| enc = parser->m_encoding; |
| handleDefault = XML_FALSE; |
| } break; |
| #endif /* XML_DTD */ |
| case XML_ROLE_DOCTYPE_PUBLIC_ID: |
| #ifdef XML_DTD |
| parser->m_useForeignDTD = XML_FALSE; |
| parser->m_declEntity = (ENTITY *)lookup( |
| parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY)); |
| if (! parser->m_declEntity) |
| return XML_ERROR_NO_MEMORY; |
| #endif /* XML_DTD */ |
| dtd->hasParamEntityRefs = XML_TRUE; |
| if (parser->m_startDoctypeDeclHandler) { |
| XML_Char *pubId; |
| if (! XmlIsPublicId(enc, s, next, eventPP)) |
| return XML_ERROR_PUBLICID; |
| pubId = poolStoreString(&parser->m_tempPool, enc, |
| s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! pubId) |
| return XML_ERROR_NO_MEMORY; |
| normalizePublicId(pubId); |
| poolFinish(&parser->m_tempPool); |
| parser->m_doctypePubid = pubId; |
| handleDefault = XML_FALSE; |
| goto alreadyChecked; |
| } |
| /* fall through */ |
| case XML_ROLE_ENTITY_PUBLIC_ID: |
| if (! XmlIsPublicId(enc, s, next, eventPP)) |
| return XML_ERROR_PUBLICID; |
| alreadyChecked: |
| if (dtd->keepProcessing && parser->m_declEntity) { |
| XML_Char *tem |
| = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! tem) |
| return XML_ERROR_NO_MEMORY; |
| normalizePublicId(tem); |
| parser->m_declEntity->publicId = tem; |
| poolFinish(&dtd->pool); |
| /* Don't suppress the default handler if we fell through from |
| * the XML_ROLE_DOCTYPE_PUBLIC_ID case. |
| */ |
| if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_PUBLIC_ID) |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_DOCTYPE_CLOSE: |
| if (allowClosingDoctype != XML_TRUE) { |
| /* Must not close doctype from within expanded parameter entities */ |
| return XML_ERROR_INVALID_TOKEN; |
| } |
| |
| if (parser->m_doctypeName) { |
| parser->m_startDoctypeDeclHandler( |
| parser->m_handlerArg, parser->m_doctypeName, parser->m_doctypeSysid, |
| parser->m_doctypePubid, 0); |
| poolClear(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| /* parser->m_doctypeSysid will be non-NULL in the case of a previous |
| XML_ROLE_DOCTYPE_SYSTEM_ID, even if parser->m_startDoctypeDeclHandler |
| was not set, indicating an external subset |
| */ |
| #ifdef XML_DTD |
| if (parser->m_doctypeSysid || parser->m_useForeignDTD) { |
| XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; |
| dtd->hasParamEntityRefs = XML_TRUE; |
| if (parser->m_paramEntityParsing |
| && parser->m_externalEntityRefHandler) { |
| ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, |
| externalSubsetName, sizeof(ENTITY)); |
| if (! entity) { |
| /* The external subset name "#" will have already been |
| * inserted into the hash table at the start of the |
| * external entity parsing, so no allocation will happen |
| * and lookup() cannot fail. |
| */ |
| return XML_ERROR_NO_MEMORY; /* LCOV_EXCL_LINE */ |
| } |
| if (parser->m_useForeignDTD) |
| entity->base = parser->m_curBase; |
| dtd->paramEntityRead = XML_FALSE; |
| if (! parser->m_externalEntityRefHandler( |
| parser->m_externalEntityRefHandlerArg, 0, entity->base, |
| entity->systemId, entity->publicId)) |
| return XML_ERROR_EXTERNAL_ENTITY_HANDLING; |
| if (dtd->paramEntityRead) { |
| if (! dtd->standalone && parser->m_notStandaloneHandler |
| && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) |
| return XML_ERROR_NOT_STANDALONE; |
| } |
| /* if we didn't read the foreign DTD then this means that there |
| is no external subset and we must reset dtd->hasParamEntityRefs |
| */ |
| else if (! parser->m_doctypeSysid) |
| dtd->hasParamEntityRefs = hadParamEntityRefs; |
| /* end of DTD - no need to update dtd->keepProcessing */ |
| } |
| parser->m_useForeignDTD = XML_FALSE; |
| } |
| #endif /* XML_DTD */ |
| if (parser->m_endDoctypeDeclHandler) { |
| parser->m_endDoctypeDeclHandler(parser->m_handlerArg); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_INSTANCE_START: |
| #ifdef XML_DTD |
| /* if there is no DOCTYPE declaration then now is the |
| last chance to read the foreign DTD |
| */ |
| if (parser->m_useForeignDTD) { |
| XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; |
| dtd->hasParamEntityRefs = XML_TRUE; |
| if (parser->m_paramEntityParsing |
| && parser->m_externalEntityRefHandler) { |
| ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, |
| externalSubsetName, sizeof(ENTITY)); |
| if (! entity) |
| return XML_ERROR_NO_MEMORY; |
| entity->base = parser->m_curBase; |
| dtd->paramEntityRead = XML_FALSE; |
| if (! parser->m_externalEntityRefHandler( |
| parser->m_externalEntityRefHandlerArg, 0, entity->base, |
| entity->systemId, entity->publicId)) |
| return XML_ERROR_EXTERNAL_ENTITY_HANDLING; |
| if (dtd->paramEntityRead) { |
| if (! dtd->standalone && parser->m_notStandaloneHandler |
| && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) |
| return XML_ERROR_NOT_STANDALONE; |
| } |
| /* if we didn't read the foreign DTD then this means that there |
| is no external subset and we must reset dtd->hasParamEntityRefs |
| */ |
| else |
| dtd->hasParamEntityRefs = hadParamEntityRefs; |
| /* end of DTD - no need to update dtd->keepProcessing */ |
| } |
| } |
| #endif /* XML_DTD */ |
| parser->m_processor = contentProcessor; |
| return contentProcessor(parser, s, end, nextPtr); |
| case XML_ROLE_ATTLIST_ELEMENT_NAME: |
| parser->m_declElementType = getElementType(parser, enc, s, next); |
| if (! parser->m_declElementType) |
| return XML_ERROR_NO_MEMORY; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_NAME: |
| parser->m_declAttributeId = getAttributeId(parser, enc, s, next); |
| if (! parser->m_declAttributeId) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declAttributeIsCdata = XML_FALSE; |
| parser->m_declAttributeType = NULL; |
| parser->m_declAttributeIsId = XML_FALSE; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_CDATA: |
| parser->m_declAttributeIsCdata = XML_TRUE; |
| parser->m_declAttributeType = atypeCDATA; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_ID: |
| parser->m_declAttributeIsId = XML_TRUE; |
| parser->m_declAttributeType = atypeID; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_IDREF: |
| parser->m_declAttributeType = atypeIDREF; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: |
| parser->m_declAttributeType = atypeIDREFS; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: |
| parser->m_declAttributeType = atypeENTITY; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: |
| parser->m_declAttributeType = atypeENTITIES; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: |
| parser->m_declAttributeType = atypeNMTOKEN; |
| goto checkAttListDeclHandler; |
| case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: |
| parser->m_declAttributeType = atypeNMTOKENS; |
| checkAttListDeclHandler: |
| if (dtd->keepProcessing && parser->m_attlistDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_ATTRIBUTE_ENUM_VALUE: |
| case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: |
| if (dtd->keepProcessing && parser->m_attlistDeclHandler) { |
| const XML_Char *prefix; |
| if (parser->m_declAttributeType) { |
| prefix = enumValueSep; |
| } else { |
| prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE ? notationPrefix |
| : enumValueStart); |
| } |
| if (! poolAppendString(&parser->m_tempPool, prefix)) |
| return XML_ERROR_NO_MEMORY; |
| if (! poolAppend(&parser->m_tempPool, enc, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declAttributeType = parser->m_tempPool.start; |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: |
| case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: |
| if (dtd->keepProcessing) { |
| if (! defineAttribute(parser->m_declElementType, |
| parser->m_declAttributeId, |
| parser->m_declAttributeIsCdata, |
| parser->m_declAttributeIsId, 0, parser)) |
| return XML_ERROR_NO_MEMORY; |
| if (parser->m_attlistDeclHandler && parser->m_declAttributeType) { |
| if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN) |
| || (*parser->m_declAttributeType == XML_T(ASCII_N) |
| && parser->m_declAttributeType[1] == XML_T(ASCII_O))) { |
| /* Enumerated or Notation type */ |
| if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN)) |
| || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declAttributeType = parser->m_tempPool.start; |
| poolFinish(&parser->m_tempPool); |
| } |
| *eventEndPP = s; |
| parser->m_attlistDeclHandler( |
| parser->m_handlerArg, parser->m_declElementType->name, |
| parser->m_declAttributeId->name, parser->m_declAttributeType, 0, |
| role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); |
| poolClear(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| } |
| break; |
| case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: |
| case XML_ROLE_FIXED_ATTRIBUTE_VALUE: |
| if (dtd->keepProcessing) { |
| const XML_Char *attVal; |
| enum XML_Error result = storeAttributeValue( |
| parser, enc, parser->m_declAttributeIsCdata, |
| s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool, |
| XML_ACCOUNT_NONE); |
| if (result) |
| return result; |
| attVal = poolStart(&dtd->pool); |
| poolFinish(&dtd->pool); |
| /* ID attributes aren't allowed to have a default */ |
| if (! defineAttribute( |
| parser->m_declElementType, parser->m_declAttributeId, |
| parser->m_declAttributeIsCdata, XML_FALSE, attVal, parser)) |
| return XML_ERROR_NO_MEMORY; |
| if (parser->m_attlistDeclHandler && parser->m_declAttributeType) { |
| if (*parser->m_declAttributeType == XML_T(ASCII_LPAREN) |
| || (*parser->m_declAttributeType == XML_T(ASCII_N) |
| && parser->m_declAttributeType[1] == XML_T(ASCII_O))) { |
| /* Enumerated or Notation type */ |
| if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_RPAREN)) |
| || ! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declAttributeType = parser->m_tempPool.start; |
| poolFinish(&parser->m_tempPool); |
| } |
| *eventEndPP = s; |
| parser->m_attlistDeclHandler( |
| parser->m_handlerArg, parser->m_declElementType->name, |
| parser->m_declAttributeId->name, parser->m_declAttributeType, |
| attVal, role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); |
| poolClear(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| } |
| break; |
| case XML_ROLE_ENTITY_VALUE: |
| if (dtd->keepProcessing) { |
| enum XML_Error result |
| = storeEntityValue(parser, enc, s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar, XML_ACCOUNT_NONE); |
| if (parser->m_declEntity) { |
| parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool); |
| parser->m_declEntity->textLen |
| = (int)(poolLength(&dtd->entityValuePool)); |
| poolFinish(&dtd->entityValuePool); |
| if (parser->m_entityDeclHandler) { |
| *eventEndPP = s; |
| parser->m_entityDeclHandler( |
| parser->m_handlerArg, parser->m_declEntity->name, |
| parser->m_declEntity->is_param, parser->m_declEntity->textPtr, |
| parser->m_declEntity->textLen, parser->m_curBase, 0, 0, 0); |
| handleDefault = XML_FALSE; |
| } |
| } else |
| poolDiscard(&dtd->entityValuePool); |
| if (result != XML_ERROR_NONE) |
| return result; |
| } |
| break; |
| case XML_ROLE_DOCTYPE_SYSTEM_ID: |
| #ifdef XML_DTD |
| parser->m_useForeignDTD = XML_FALSE; |
| #endif /* XML_DTD */ |
| dtd->hasParamEntityRefs = XML_TRUE; |
| if (parser->m_startDoctypeDeclHandler) { |
| parser->m_doctypeSysid = poolStoreString(&parser->m_tempPool, enc, |
| s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (parser->m_doctypeSysid == NULL) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| #ifdef XML_DTD |
| else |
| /* use externalSubsetName to make parser->m_doctypeSysid non-NULL |
| for the case where no parser->m_startDoctypeDeclHandler is set */ |
| parser->m_doctypeSysid = externalSubsetName; |
| #endif /* XML_DTD */ |
| if (! dtd->standalone |
| #ifdef XML_DTD |
| && ! parser->m_paramEntityParsing |
| #endif /* XML_DTD */ |
| && parser->m_notStandaloneHandler |
| && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) |
| return XML_ERROR_NOT_STANDALONE; |
| #ifndef XML_DTD |
| break; |
| #else /* XML_DTD */ |
| if (! parser->m_declEntity) { |
| parser->m_declEntity = (ENTITY *)lookup( |
| parser, &dtd->paramEntities, externalSubsetName, sizeof(ENTITY)); |
| if (! parser->m_declEntity) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declEntity->publicId = NULL; |
| } |
| #endif /* XML_DTD */ |
| /* fall through */ |
| case XML_ROLE_ENTITY_SYSTEM_ID: |
| if (dtd->keepProcessing && parser->m_declEntity) { |
| parser->m_declEntity->systemId |
| = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! parser->m_declEntity->systemId) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declEntity->base = parser->m_curBase; |
| poolFinish(&dtd->pool); |
| /* Don't suppress the default handler if we fell through from |
| * the XML_ROLE_DOCTYPE_SYSTEM_ID case. |
| */ |
| if (parser->m_entityDeclHandler && role == XML_ROLE_ENTITY_SYSTEM_ID) |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_ENTITY_COMPLETE: |
| if (dtd->keepProcessing && parser->m_declEntity |
| && parser->m_entityDeclHandler) { |
| *eventEndPP = s; |
| parser->m_entityDeclHandler( |
| parser->m_handlerArg, parser->m_declEntity->name, |
| parser->m_declEntity->is_param, 0, 0, parser->m_declEntity->base, |
| parser->m_declEntity->systemId, parser->m_declEntity->publicId, 0); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_ENTITY_NOTATION_NAME: |
| if (dtd->keepProcessing && parser->m_declEntity) { |
| parser->m_declEntity->notation |
| = poolStoreString(&dtd->pool, enc, s, next); |
| if (! parser->m_declEntity->notation) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&dtd->pool); |
| if (parser->m_unparsedEntityDeclHandler) { |
| *eventEndPP = s; |
| parser->m_unparsedEntityDeclHandler( |
| parser->m_handlerArg, parser->m_declEntity->name, |
| parser->m_declEntity->base, parser->m_declEntity->systemId, |
| parser->m_declEntity->publicId, parser->m_declEntity->notation); |
| handleDefault = XML_FALSE; |
| } else if (parser->m_entityDeclHandler) { |
| *eventEndPP = s; |
| parser->m_entityDeclHandler( |
| parser->m_handlerArg, parser->m_declEntity->name, 0, 0, 0, |
| parser->m_declEntity->base, parser->m_declEntity->systemId, |
| parser->m_declEntity->publicId, parser->m_declEntity->notation); |
| handleDefault = XML_FALSE; |
| } |
| } |
| break; |
| case XML_ROLE_GENERAL_ENTITY_NAME: { |
| if (XmlPredefinedEntityName(enc, s, next)) { |
| parser->m_declEntity = NULL; |
| break; |
| } |
| if (dtd->keepProcessing) { |
| const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, |
| name, sizeof(ENTITY)); |
| if (! parser->m_declEntity) |
| return XML_ERROR_NO_MEMORY; |
| if (parser->m_declEntity->name != name) { |
| poolDiscard(&dtd->pool); |
| parser->m_declEntity = NULL; |
| } else { |
| poolFinish(&dtd->pool); |
| parser->m_declEntity->publicId = NULL; |
| parser->m_declEntity->is_param = XML_FALSE; |
| /* if we have a parent parser or are reading an internal parameter |
| entity, then the entity declaration is not considered "internal" |
| */ |
| parser->m_declEntity->is_internal |
| = ! (parser->m_parentParser || parser->m_openInternalEntities); |
| if (parser->m_entityDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| } else { |
| poolDiscard(&dtd->pool); |
| parser->m_declEntity = NULL; |
| } |
| } break; |
| case XML_ROLE_PARAM_ENTITY_NAME: |
| #ifdef XML_DTD |
| if (dtd->keepProcessing) { |
| const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| parser->m_declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, |
| name, sizeof(ENTITY)); |
| if (! parser->m_declEntity) |
| return XML_ERROR_NO_MEMORY; |
| if (parser->m_declEntity->name != name) { |
| poolDiscard(&dtd->pool); |
| parser->m_declEntity = NULL; |
| } else { |
| poolFinish(&dtd->pool); |
| parser->m_declEntity->publicId = NULL; |
| parser->m_declEntity->is_param = XML_TRUE; |
| /* if we have a parent parser or are reading an internal parameter |
| entity, then the entity declaration is not considered "internal" |
| */ |
| parser->m_declEntity->is_internal |
| = ! (parser->m_parentParser || parser->m_openInternalEntities); |
| if (parser->m_entityDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| } else { |
| poolDiscard(&dtd->pool); |
| parser->m_declEntity = NULL; |
| } |
| #else /* not XML_DTD */ |
| parser->m_declEntity = NULL; |
| #endif /* XML_DTD */ |
| break; |
| case XML_ROLE_NOTATION_NAME: |
| parser->m_declNotationPublicId = NULL; |
| parser->m_declNotationName = NULL; |
| if (parser->m_notationDeclHandler) { |
| parser->m_declNotationName |
| = poolStoreString(&parser->m_tempPool, enc, s, next); |
| if (! parser->m_declNotationName) |
| return XML_ERROR_NO_MEMORY; |
| poolFinish(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_NOTATION_PUBLIC_ID: |
| if (! XmlIsPublicId(enc, s, next, eventPP)) |
| return XML_ERROR_PUBLICID; |
| if (parser |
| ->m_declNotationName) { /* means m_notationDeclHandler != NULL */ |
| XML_Char *tem = poolStoreString(&parser->m_tempPool, enc, |
| s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! tem) |
| return XML_ERROR_NO_MEMORY; |
| normalizePublicId(tem); |
| parser->m_declNotationPublicId = tem; |
| poolFinish(&parser->m_tempPool); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_NOTATION_SYSTEM_ID: |
| if (parser->m_declNotationName && parser->m_notationDeclHandler) { |
| const XML_Char *systemId = poolStoreString(&parser->m_tempPool, enc, |
| s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! systemId) |
| return XML_ERROR_NO_MEMORY; |
| *eventEndPP = s; |
| parser->m_notationDeclHandler( |
| parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, |
| systemId, parser->m_declNotationPublicId); |
| handleDefault = XML_FALSE; |
| } |
| poolClear(&parser->m_tempPool); |
| break; |
| case XML_ROLE_NOTATION_NO_SYSTEM_ID: |
| if (parser->m_declNotationPublicId && parser->m_notationDeclHandler) { |
| *eventEndPP = s; |
| parser->m_notationDeclHandler( |
| parser->m_handlerArg, parser->m_declNotationName, parser->m_curBase, |
| 0, parser->m_declNotationPublicId); |
| handleDefault = XML_FALSE; |
| } |
| poolClear(&parser->m_tempPool); |
| break; |
| case XML_ROLE_ERROR: |
| switch (tok) { |
| case XML_TOK_PARAM_ENTITY_REF: |
| /* PE references in internal subset are |
| not allowed within declarations. */ |
| return XML_ERROR_PARAM_ENTITY_REF; |
| case XML_TOK_XML_DECL: |
| return XML_ERROR_MISPLACED_XML_PI; |
| default: |
| return XML_ERROR_SYNTAX; |
| } |
| #ifdef XML_DTD |
| case XML_ROLE_IGNORE_SECT: { |
| enum XML_Error result; |
| if (parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| handleDefault = XML_FALSE; |
| result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); |
| if (result != XML_ERROR_NONE) |
| return result; |
| else if (! next) { |
| parser->m_processor = ignoreSectionProcessor; |
| return result; |
| } |
| } break; |
| #endif /* XML_DTD */ |
| case XML_ROLE_GROUP_OPEN: |
| if (parser->m_prologState.level >= parser->m_groupSize) { |
| if (parser->m_groupSize) { |
| { |
| /* Detect and prevent integer overflow */ |
| if (parser->m_groupSize > (unsigned int)(-1) / 2u) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| char *const new_connector = (char *)REALLOC( |
| parser, parser->m_groupConnector, parser->m_groupSize *= 2); |
| if (new_connector == NULL) { |
| parser->m_groupSize /= 2; |
| return XML_ERROR_NO_MEMORY; |
| } |
| parser->m_groupConnector = new_connector; |
| } |
| |
| if (dtd->scaffIndex) { |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if (parser->m_groupSize > (size_t)(-1) / sizeof(int)) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| #endif |
| |
| int *const new_scaff_index = (int *)REALLOC( |
| parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); |
| if (new_scaff_index == NULL) |
| return XML_ERROR_NO_MEMORY; |
| dtd->scaffIndex = new_scaff_index; |
| } |
| } else { |
| parser->m_groupConnector |
| = (char *)MALLOC(parser, parser->m_groupSize = 32); |
| if (! parser->m_groupConnector) { |
| parser->m_groupSize = 0; |
| return XML_ERROR_NO_MEMORY; |
| } |
| } |
| } |
| parser->m_groupConnector[parser->m_prologState.level] = 0; |
| if (dtd->in_eldecl) { |
| int myindex = nextScaffoldPart(parser); |
| if (myindex < 0) |
| return XML_ERROR_NO_MEMORY; |
| assert(dtd->scaffIndex != NULL); |
| dtd->scaffIndex[dtd->scaffLevel] = myindex; |
| dtd->scaffLevel++; |
| dtd->scaffold[myindex].type = XML_CTYPE_SEQ; |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| break; |
| case XML_ROLE_GROUP_SEQUENCE: |
| if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_PIPE) |
| return XML_ERROR_SYNTAX; |
| parser->m_groupConnector[parser->m_prologState.level] = ASCII_COMMA; |
| if (dtd->in_eldecl && parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_GROUP_CHOICE: |
| if (parser->m_groupConnector[parser->m_prologState.level] == ASCII_COMMA) |
| return XML_ERROR_SYNTAX; |
| if (dtd->in_eldecl |
| && ! parser->m_groupConnector[parser->m_prologState.level] |
| && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type |
| != XML_CTYPE_MIXED)) { |
| dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type |
| = XML_CTYPE_CHOICE; |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| parser->m_groupConnector[parser->m_prologState.level] = ASCII_PIPE; |
| break; |
| case XML_ROLE_PARAM_ENTITY_REF: |
| #ifdef XML_DTD |
| case XML_ROLE_INNER_PARAM_ENTITY_REF: |
| dtd->hasParamEntityRefs = XML_TRUE; |
| if (! parser->m_paramEntityParsing) |
| dtd->keepProcessing = dtd->standalone; |
| else { |
| const XML_Char *name; |
| ENTITY *entity; |
| name = poolStoreString(&dtd->pool, enc, s + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); |
| poolDiscard(&dtd->pool); |
| /* first, determine if a check for an existing declaration is needed; |
| if yes, check that the entity exists, and that it is internal, |
| otherwise call the skipped entity handler |
| */ |
| if (parser->m_prologState.documentEntity |
| && (dtd->standalone ? ! parser->m_openInternalEntities |
| : ! dtd->hasParamEntityRefs)) { |
| if (! entity) |
| return XML_ERROR_UNDEFINED_ENTITY; |
| else if (! entity->is_internal) { |
| /* It's hard to exhaustively search the code to be sure, |
| * but there doesn't seem to be a way of executing the |
| * following line. There are two cases: |
| * |
| * If 'standalone' is false, the DTD must have no |
| * parameter entities or we wouldn't have passed the outer |
| * 'if' statement. That measn the only entity in the hash |
| * table is the external subset name "#" which cannot be |
| * given as a parameter entity name in XML syntax, so the |
| * lookup must have returned NULL and we don't even reach |
| * the test for an internal entity. |
| * |
| * If 'standalone' is true, it does not seem to be |
| * possible to create entities taking this code path that |
| * are not internal entities, so fail the test above. |
| * |
| * Because this analysis is very uncertain, the code is |
| * being left in place and merely removed from the |
| * coverage test statistics. |
| */ |
| return XML_ERROR_ENTITY_DECLARED_IN_PE; /* LCOV_EXCL_LINE */ |
| } |
| } else if (! entity) { |
| dtd->keepProcessing = dtd->standalone; |
| /* cannot report skipped entities in declarations */ |
| if ((role == XML_ROLE_PARAM_ENTITY_REF) |
| && parser->m_skippedEntityHandler) { |
| parser->m_skippedEntityHandler(parser->m_handlerArg, name, 1); |
| handleDefault = XML_FALSE; |
| } |
| break; |
| } |
| if (entity->open) |
| return XML_ERROR_RECURSIVE_ENTITY_REF; |
| if (entity->textPtr) { |
| enum XML_Error result; |
| XML_Bool betweenDecl |
| = (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); |
| result = processInternalEntity(parser, entity, betweenDecl); |
| if (result != XML_ERROR_NONE) |
| return result; |
| handleDefault = XML_FALSE; |
| break; |
| } |
| if (parser->m_externalEntityRefHandler) { |
| dtd->paramEntityRead = XML_FALSE; |
| entity->open = XML_TRUE; |
| entityTrackingOnOpen(parser, entity, __LINE__); |
| if (! parser->m_externalEntityRefHandler( |
| parser->m_externalEntityRefHandlerArg, 0, entity->base, |
| entity->systemId, entity->publicId)) { |
| entityTrackingOnClose(parser, entity, __LINE__); |
| entity->open = XML_FALSE; |
| return XML_ERROR_EXTERNAL_ENTITY_HANDLING; |
| } |
| entityTrackingOnClose(parser, entity, __LINE__); |
| entity->open = XML_FALSE; |
| handleDefault = XML_FALSE; |
| if (! dtd->paramEntityRead) { |
| dtd->keepProcessing = dtd->standalone; |
| break; |
| } |
| } else { |
| dtd->keepProcessing = dtd->standalone; |
| break; |
| } |
| } |
| #endif /* XML_DTD */ |
| if (! dtd->standalone && parser->m_notStandaloneHandler |
| && ! parser->m_notStandaloneHandler(parser->m_handlerArg)) |
| return XML_ERROR_NOT_STANDALONE; |
| break; |
| |
| /* Element declaration stuff */ |
| |
| case XML_ROLE_ELEMENT_NAME: |
| if (parser->m_elementDeclHandler) { |
| parser->m_declElementType = getElementType(parser, enc, s, next); |
| if (! parser->m_declElementType) |
| return XML_ERROR_NO_MEMORY; |
| dtd->scaffLevel = 0; |
| dtd->scaffCount = 0; |
| dtd->in_eldecl = XML_TRUE; |
| handleDefault = XML_FALSE; |
| } |
| break; |
| |
| case XML_ROLE_CONTENT_ANY: |
| case XML_ROLE_CONTENT_EMPTY: |
| if (dtd->in_eldecl) { |
| if (parser->m_elementDeclHandler) { |
| XML_Content *content |
| = (XML_Content *)MALLOC(parser, sizeof(XML_Content)); |
| if (! content) |
| return XML_ERROR_NO_MEMORY; |
| content->quant = XML_CQUANT_NONE; |
| content->name = NULL; |
| content->numchildren = 0; |
| content->children = NULL; |
| content->type = ((role == XML_ROLE_CONTENT_ANY) ? XML_CTYPE_ANY |
| : XML_CTYPE_EMPTY); |
| *eventEndPP = s; |
| parser->m_elementDeclHandler( |
| parser->m_handlerArg, parser->m_declElementType->name, content); |
| handleDefault = XML_FALSE; |
| } |
| dtd->in_eldecl = XML_FALSE; |
| } |
| break; |
| |
| case XML_ROLE_CONTENT_PCDATA: |
| if (dtd->in_eldecl) { |
| dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type |
| = XML_CTYPE_MIXED; |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| break; |
| |
| case XML_ROLE_CONTENT_ELEMENT: |
| quant = XML_CQUANT_NONE; |
| goto elementContent; |
| case XML_ROLE_CONTENT_ELEMENT_OPT: |
| quant = XML_CQUANT_OPT; |
| goto elementContent; |
| case XML_ROLE_CONTENT_ELEMENT_REP: |
| quant = XML_CQUANT_REP; |
| goto elementContent; |
| case XML_ROLE_CONTENT_ELEMENT_PLUS: |
| quant = XML_CQUANT_PLUS; |
| elementContent: |
| if (dtd->in_eldecl) { |
| ELEMENT_TYPE *el; |
| const XML_Char *name; |
| size_t nameLen; |
| const char *nxt |
| = (quant == XML_CQUANT_NONE ? next : next - enc->minBytesPerChar); |
| int myindex = nextScaffoldPart(parser); |
| if (myindex < 0) |
| return XML_ERROR_NO_MEMORY; |
| dtd->scaffold[myindex].type = XML_CTYPE_NAME; |
| dtd->scaffold[myindex].quant = quant; |
| el = getElementType(parser, enc, s, nxt); |
| if (! el) |
| return XML_ERROR_NO_MEMORY; |
| name = el->name; |
| dtd->scaffold[myindex].name = name; |
| nameLen = 0; |
| for (; name[nameLen++];) |
| ; |
| |
| /* Detect and prevent integer overflow */ |
| if (nameLen > UINT_MAX - dtd->contentStringLen) { |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| dtd->contentStringLen += (unsigned)nameLen; |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| } |
| break; |
| |
| case XML_ROLE_GROUP_CLOSE: |
| quant = XML_CQUANT_NONE; |
| goto closeGroup; |
| case XML_ROLE_GROUP_CLOSE_OPT: |
| quant = XML_CQUANT_OPT; |
| goto closeGroup; |
| case XML_ROLE_GROUP_CLOSE_REP: |
| quant = XML_CQUANT_REP; |
| goto closeGroup; |
| case XML_ROLE_GROUP_CLOSE_PLUS: |
| quant = XML_CQUANT_PLUS; |
| closeGroup: |
| if (dtd->in_eldecl) { |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| dtd->scaffLevel--; |
| dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; |
| if (dtd->scaffLevel == 0) { |
| if (! handleDefault) { |
| XML_Content *model = build_model(parser); |
| if (! model) |
| return XML_ERROR_NO_MEMORY; |
| *eventEndPP = s; |
| parser->m_elementDeclHandler( |
| parser->m_handlerArg, parser->m_declElementType->name, model); |
| } |
| dtd->in_eldecl = XML_FALSE; |
| dtd->contentStringLen = 0; |
| } |
| } |
| break; |
| /* End element declaration stuff */ |
| |
| case XML_ROLE_PI: |
| if (! reportProcessingInstruction(parser, enc, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_COMMENT: |
| if (! reportComment(parser, enc, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_NONE: |
| switch (tok) { |
| case XML_TOK_BOM: |
| handleDefault = XML_FALSE; |
| break; |
| } |
| break; |
| case XML_ROLE_DOCTYPE_NONE: |
| if (parser->m_startDoctypeDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_ENTITY_NONE: |
| if (dtd->keepProcessing && parser->m_entityDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_NOTATION_NONE: |
| if (parser->m_notationDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_ATTLIST_NONE: |
| if (dtd->keepProcessing && parser->m_attlistDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| case XML_ROLE_ELEMENT_NONE: |
| if (parser->m_elementDeclHandler) |
| handleDefault = XML_FALSE; |
| break; |
| } /* end of big switch */ |
| |
| if (handleDefault && parser->m_defaultHandler) |
| reportDefault(parser, enc, s, next); |
| |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| case XML_FINISHED: |
| return XML_ERROR_ABORTED; |
| default: |
| s = next; |
| tok = XmlPrologTok(enc, s, end, &next); |
| } |
| } |
| /* not reached */ |
| } |
| |
| static enum XML_Error PTRCALL |
| epilogProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| parser->m_processor = epilogProcessor; |
| parser->m_eventPtr = s; |
| for (;;) { |
| const char *next = NULL; |
| int tok = XmlPrologTok(parser->m_encoding, s, end, &next); |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, |
| XML_ACCOUNT_DIRECT)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #endif |
| parser->m_eventEndPtr = next; |
| switch (tok) { |
| /* report partial linebreak - it might be the last token */ |
| case -XML_TOK_PROLOG_S: |
| if (parser->m_defaultHandler) { |
| reportDefault(parser, parser->m_encoding, s, next); |
| if (parser->m_parsingStatus.parsing == XML_FINISHED) |
| return XML_ERROR_ABORTED; |
| } |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| case XML_TOK_NONE: |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| case XML_TOK_PROLOG_S: |
| if (parser->m_defaultHandler) |
| reportDefault(parser, parser->m_encoding, s, next); |
| break; |
| case XML_TOK_PI: |
| if (! reportProcessingInstruction(parser, parser->m_encoding, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| case XML_TOK_COMMENT: |
| if (! reportComment(parser, parser->m_encoding, s, next)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| case XML_TOK_INVALID: |
| parser->m_eventPtr = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_UNCLOSED_TOKEN; |
| case XML_TOK_PARTIAL_CHAR: |
| if (! parser->m_parsingStatus.finalBuffer) { |
| *nextPtr = s; |
| return XML_ERROR_NONE; |
| } |
| return XML_ERROR_PARTIAL_CHAR; |
| default: |
| return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; |
| } |
| parser->m_eventPtr = s = next; |
| switch (parser->m_parsingStatus.parsing) { |
| case XML_SUSPENDED: |
| *nextPtr = next; |
| return XML_ERROR_NONE; |
| case XML_FINISHED: |
| return XML_ERROR_ABORTED; |
| default:; |
| } |
| } |
| } |
| |
| static enum XML_Error |
| processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) { |
| const char *textStart, *textEnd; |
| const char *next; |
| enum XML_Error result; |
| OPEN_INTERNAL_ENTITY *openEntity; |
| |
| if (parser->m_freeInternalEntities) { |
| openEntity = parser->m_freeInternalEntities; |
| parser->m_freeInternalEntities = openEntity->next; |
| } else { |
| openEntity |
| = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY)); |
| if (! openEntity) |
| return XML_ERROR_NO_MEMORY; |
| } |
| entity->open = XML_TRUE; |
| #ifdef XML_DTD |
| entityTrackingOnOpen(parser, entity, __LINE__); |
| #endif |
| entity->processed = 0; |
| openEntity->next = parser->m_openInternalEntities; |
| parser->m_openInternalEntities = openEntity; |
| openEntity->entity = entity; |
| openEntity->startTagLevel = parser->m_tagLevel; |
| openEntity->betweenDecl = betweenDecl; |
| openEntity->internalEventPtr = NULL; |
| openEntity->internalEventEndPtr = NULL; |
| textStart = (const char *)entity->textPtr; |
| textEnd = (const char *)(entity->textPtr + entity->textLen); |
| /* Set a safe default value in case 'next' does not get set */ |
| next = textStart; |
| |
| #ifdef XML_DTD |
| if (entity->is_param) { |
| int tok |
| = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); |
| result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, |
| tok, next, &next, XML_FALSE, XML_FALSE, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| } else |
| #endif /* XML_DTD */ |
| result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding, |
| textStart, textEnd, &next, XML_FALSE, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| |
| if (result == XML_ERROR_NONE) { |
| if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) { |
| entity->processed = (int)(next - textStart); |
| parser->m_processor = internalEntityProcessor; |
| } else { |
| #ifdef XML_DTD |
| entityTrackingOnClose(parser, entity, __LINE__); |
| #endif /* XML_DTD */ |
| entity->open = XML_FALSE; |
| parser->m_openInternalEntities = openEntity->next; |
| /* put openEntity back in list of free instances */ |
| openEntity->next = parser->m_freeInternalEntities; |
| parser->m_freeInternalEntities = openEntity; |
| } |
| } |
| return result; |
| } |
| |
| static enum XML_Error PTRCALL |
| internalEntityProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| ENTITY *entity; |
| const char *textStart, *textEnd; |
| const char *next; |
| enum XML_Error result; |
| OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities; |
| if (! openEntity) |
| return XML_ERROR_UNEXPECTED_STATE; |
| |
| entity = openEntity->entity; |
| textStart = ((const char *)entity->textPtr) + entity->processed; |
| textEnd = (const char *)(entity->textPtr + entity->textLen); |
| /* Set a safe default value in case 'next' does not get set */ |
| next = textStart; |
| |
| #ifdef XML_DTD |
| if (entity->is_param) { |
| int tok |
| = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next); |
| result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd, |
| tok, next, &next, XML_FALSE, XML_TRUE, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| } else |
| #endif /* XML_DTD */ |
| result = doContent(parser, openEntity->startTagLevel, |
| parser->m_internalEncoding, textStart, textEnd, &next, |
| XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION); |
| |
| if (result != XML_ERROR_NONE) |
| return result; |
| else if (textEnd != next |
| && parser->m_parsingStatus.parsing == XML_SUSPENDED) { |
| entity->processed = (int)(next - (const char *)entity->textPtr); |
| return result; |
| } else { |
| #ifdef XML_DTD |
| entityTrackingOnClose(parser, entity, __LINE__); |
| #endif |
| entity->open = XML_FALSE; |
| parser->m_openInternalEntities = openEntity->next; |
| /* put openEntity back in list of free instances */ |
| openEntity->next = parser->m_freeInternalEntities; |
| parser->m_freeInternalEntities = openEntity; |
| } |
| |
| #ifdef XML_DTD |
| if (entity->is_param) { |
| int tok; |
| parser->m_processor = prologProcessor; |
| tok = XmlPrologTok(parser->m_encoding, s, end, &next); |
| return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE, |
| XML_ACCOUNT_DIRECT); |
| } else |
| #endif /* XML_DTD */ |
| { |
| parser->m_processor = contentProcessor; |
| /* see externalEntityContentProcessor vs contentProcessor */ |
| return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding, |
| s, end, nextPtr, |
| (XML_Bool)! parser->m_parsingStatus.finalBuffer, |
| XML_ACCOUNT_DIRECT); |
| } |
| } |
| |
| static enum XML_Error PTRCALL |
| errorProcessor(XML_Parser parser, const char *s, const char *end, |
| const char **nextPtr) { |
| UNUSED_P(s); |
| UNUSED_P(end); |
| UNUSED_P(nextPtr); |
| return parser->m_errorCode; |
| } |
| |
| static enum XML_Error |
| storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, |
| const char *ptr, const char *end, STRING_POOL *pool, |
| enum XML_Account account) { |
| enum XML_Error result |
| = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account); |
| if (result) |
| return result; |
| if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) |
| poolChop(pool); |
| if (! poolAppendChar(pool, XML_T('\0'))) |
| return XML_ERROR_NO_MEMORY; |
| return XML_ERROR_NONE; |
| } |
| |
| static enum XML_Error |
| appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, |
| const char *ptr, const char *end, STRING_POOL *pool, |
| enum XML_Account account) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| #ifndef XML_DTD |
| UNUSED_P(account); |
| #endif |
| |
| for (;;) { |
| const char *next |
| = ptr; /* XmlAttributeValueTok doesn't always set the last arg */ |
| int tok = XmlAttributeValueTok(enc, ptr, end, &next); |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) { |
| accountingOnAbort(parser); |
| return XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| } |
| #endif |
| switch (tok) { |
| case XML_TOK_NONE: |
| return XML_ERROR_NONE; |
| case XML_TOK_INVALID: |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = next; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_PARTIAL: |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = ptr; |
| return XML_ERROR_INVALID_TOKEN; |
| case XML_TOK_CHAR_REF: { |
| XML_Char buf[XML_ENCODE_MAX]; |
| int i; |
| int n = XmlCharRefNumber(enc, ptr); |
| if (n < 0) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = ptr; |
| return XML_ERROR_BAD_CHAR_REF; |
| } |
| if (! isCdata && n == 0x20 /* space */ |
| && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) |
| break; |
| n = XmlEncode(n, (ICHAR *)buf); |
| /* The XmlEncode() functions can never return 0 here. That |
| * error return happens if the code point passed in is either |
| * negative or greater than or equal to 0x110000. The |
| * XmlCharRefNumber() functions will all return a number |
| * strictly less than 0x110000 or a negative value if an error |
| * occurred. The negative value is intercepted above, so |
| * XmlEncode() is never passed a value it might return an |
| * error for. |
| */ |
| for (i = 0; i < n; i++) { |
| if (! poolAppendChar(pool, buf[i])) |
| return XML_ERROR_NO_MEMORY; |
| } |
| } break; |
| case XML_TOK_DATA_CHARS: |
| if (! poolAppend(pool, enc, ptr, next)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| case XML_TOK_TRAILING_CR: |
| next = ptr + enc->minBytesPerChar; |
| /* fall through */ |
| case XML_TOK_ATTRIBUTE_VALUE_S: |
| case XML_TOK_DATA_NEWLINE: |
| if (! isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) |
| break; |
| if (! poolAppendChar(pool, 0x20)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| case XML_TOK_ENTITY_REF: { |
| const XML_Char *name; |
| ENTITY *entity; |
| char checkEntityDecl; |
| XML_Char ch = (XML_Char)XmlPredefinedEntityName( |
| enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); |
| if (ch) { |
| #ifdef XML_DTD |
| /* NOTE: We are replacing 4-6 characters original input for 1 character |
| * so there is no amplification and hence recording without |
| * protection. */ |
| accountingDiffTolerated(parser, tok, (char *)&ch, |
| ((char *)&ch) + sizeof(XML_Char), __LINE__, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| #endif /* XML_DTD */ |
| if (! poolAppendChar(pool, ch)) |
| return XML_ERROR_NO_MEMORY; |
| break; |
| } |
| name = poolStoreString(&parser->m_temp2Pool, enc, |
| ptr + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! name) |
| return XML_ERROR_NO_MEMORY; |
| entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); |
| poolDiscard(&parser->m_temp2Pool); |
| /* First, determine if a check for an existing declaration is needed; |
| if yes, check that the entity exists, and that it is internal. |
| */ |
| if (pool == &dtd->pool) /* are we called from prolog? */ |
| checkEntityDecl = |
| #ifdef XML_DTD |
| parser->m_prologState.documentEntity && |
| #endif /* XML_DTD */ |
| (dtd->standalone ? ! parser->m_openInternalEntities |
| : ! dtd->hasParamEntityRefs); |
| else /* if (pool == &parser->m_tempPool): we are called from content */ |
| checkEntityDecl = ! dtd->hasParamEntityRefs || dtd->standalone; |
| if (checkEntityDecl) { |
| if (! entity) |
| return XML_ERROR_UNDEFINED_ENTITY; |
| else if (! entity->is_internal) |
| return XML_ERROR_ENTITY_DECLARED_IN_PE; |
| } else if (! entity) { |
| /* Cannot report skipped entity here - see comments on |
| parser->m_skippedEntityHandler. |
| if (parser->m_skippedEntityHandler) |
| parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); |
| */ |
| /* Cannot call the default handler because this would be |
| out of sync with the call to the startElementHandler. |
| if ((pool == &parser->m_tempPool) && parser->m_defaultHandler) |
| reportDefault(parser, enc, ptr, next); |
| */ |
| break; |
| } |
| if (entity->open) { |
| if (enc == parser->m_encoding) { |
| /* It does not appear that this line can be executed. |
| * |
| * The "if (entity->open)" check catches recursive entity |
| * definitions. In order to be called with an open |
| * entity, it must have gone through this code before and |
| * been through the recursive call to |
| * appendAttributeValue() some lines below. That call |
| * sets the local encoding ("enc") to the parser's |
| * internal encoding (internal_utf8 or internal_utf16), |
| * which can never be the same as the principle encoding. |
| * It doesn't appear there is another code path that gets |
| * here with entity->open being TRUE. |
| * |
| * Since it is not certain that this logic is watertight, |
| * we keep the line and merely exclude it from coverage |
| * tests. |
| */ |
| parser->m_eventPtr = ptr; /* LCOV_EXCL_LINE */ |
| } |
| return XML_ERROR_RECURSIVE_ENTITY_REF; |
| } |
| if (entity->notation) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = ptr; |
| return XML_ERROR_BINARY_ENTITY_REF; |
| } |
| if (! entity->textPtr) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = ptr; |
| return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; |
| } else { |
| enum XML_Error result; |
| const XML_Char *textEnd = entity->textPtr + entity->textLen; |
| entity->open = XML_TRUE; |
| #ifdef XML_DTD |
| entityTrackingOnOpen(parser, entity, __LINE__); |
| #endif |
| result = appendAttributeValue(parser, parser->m_internalEncoding, |
| isCdata, (const char *)entity->textPtr, |
| (const char *)textEnd, pool, |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| #ifdef XML_DTD |
| entityTrackingOnClose(parser, entity, __LINE__); |
| #endif |
| entity->open = XML_FALSE; |
| if (result) |
| return result; |
| } |
| } break; |
| default: |
| /* The only token returned by XmlAttributeValueTok() that does |
| * not have an explicit case here is XML_TOK_PARTIAL_CHAR. |
| * Getting that would require an entity name to contain an |
| * incomplete XML character (e.g. \xE2\x82); however previous |
| * tokenisers will have already recognised and rejected such |
| * names before XmlAttributeValueTok() gets a look-in. This |
| * default case should be retained as a safety net, but the code |
| * excluded from coverage tests. |
| * |
| * LCOV_EXCL_START |
| */ |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = ptr; |
| return XML_ERROR_UNEXPECTED_STATE; |
| /* LCOV_EXCL_STOP */ |
| } |
| ptr = next; |
| } |
| /* not reached */ |
| } |
| |
| static enum XML_Error |
| storeEntityValue(XML_Parser parser, const ENCODING *enc, |
| const char *entityTextPtr, const char *entityTextEnd, |
| enum XML_Account account) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| STRING_POOL *pool = &(dtd->entityValuePool); |
| enum XML_Error result = XML_ERROR_NONE; |
| #ifdef XML_DTD |
| int oldInEntityValue = parser->m_prologState.inEntityValue; |
| parser->m_prologState.inEntityValue = 1; |
| #else |
| UNUSED_P(account); |
| #endif /* XML_DTD */ |
| /* never return Null for the value argument in EntityDeclHandler, |
| since this would indicate an external entity; therefore we |
| have to make sure that entityValuePool.start is not null */ |
| if (! pool->blocks) { |
| if (! poolGrow(pool)) |
| return XML_ERROR_NO_MEMORY; |
| } |
| |
| for (;;) { |
| const char *next |
| = entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ |
| int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); |
| |
| #ifdef XML_DTD |
| if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__, |
| account)) { |
| accountingOnAbort(parser); |
| result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; |
| goto endEntityValue; |
| } |
| #endif |
| |
| switch (tok) { |
| case XML_TOK_PARAM_ENTITY_REF: |
| #ifdef XML_DTD |
| if (parser->m_isParamEntity || enc != parser->m_encoding) { |
| const XML_Char *name; |
| ENTITY *entity; |
| name = poolStoreString(&parser->m_tempPool, enc, |
| entityTextPtr + enc->minBytesPerChar, |
| next - enc->minBytesPerChar); |
| if (! name) { |
| result = XML_ERROR_NO_MEMORY; |
| goto endEntityValue; |
| } |
| entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); |
| poolDiscard(&parser->m_tempPool); |
| if (! entity) { |
| /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ |
| /* cannot report skipped entity here - see comments on |
| parser->m_skippedEntityHandler |
| if (parser->m_skippedEntityHandler) |
| parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); |
| */ |
| dtd->keepProcessing = dtd->standalone; |
| goto endEntityValue; |
| } |
| if (entity->open) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = entityTextPtr; |
| result = XML_ERROR_RECURSIVE_ENTITY_REF; |
| goto endEntityValue; |
| } |
| if (entity->systemId) { |
| if (parser->m_externalEntityRefHandler) { |
| dtd->paramEntityRead = XML_FALSE; |
| entity->open = XML_TRUE; |
| entityTrackingOnOpen(parser, entity, __LINE__); |
| if (! parser->m_externalEntityRefHandler( |
| parser->m_externalEntityRefHandlerArg, 0, entity->base, |
| entity->systemId, entity->publicId)) { |
| entityTrackingOnClose(parser, entity, __LINE__); |
| entity->open = XML_FALSE; |
| result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; |
| goto endEntityValue; |
| } |
| entityTrackingOnClose(parser, entity, __LINE__); |
| entity->open = XML_FALSE; |
| if (! dtd->paramEntityRead) |
| dtd->keepProcessing = dtd->standalone; |
| } else |
| dtd->keepProcessing = dtd->standalone; |
| } else { |
| entity->open = XML_TRUE; |
| entityTrackingOnOpen(parser, entity, __LINE__); |
| result = storeEntityValue( |
| parser, parser->m_internalEncoding, (const char *)entity->textPtr, |
| (const char *)(entity->textPtr + entity->textLen), |
| XML_ACCOUNT_ENTITY_EXPANSION); |
| entityTrackingOnClose(parser, entity, __LINE__); |
| entity->open = XML_FALSE; |
| if (result) |
| goto endEntityValue; |
| } |
| break; |
| } |
| #endif /* XML_DTD */ |
| /* In the internal subset, PE references are not legal |
| within markup declarations, e.g entity values in this case. */ |
| parser->m_eventPtr = entityTextPtr; |
| result = XML_ERROR_PARAM_ENTITY_REF; |
| goto endEntityValue; |
| case XML_TOK_NONE: |
| result = XML_ERROR_NONE; |
| goto endEntityValue; |
| case XML_TOK_ENTITY_REF: |
| case XML_TOK_DATA_CHARS: |
| if (! poolAppend(pool, enc, entityTextPtr, next)) { |
| result = XML_ERROR_NO_MEMORY; |
| goto endEntityValue; |
| } |
| break; |
| case XML_TOK_TRAILING_CR: |
| next = entityTextPtr + enc->minBytesPerChar; |
| /* fall through */ |
| case XML_TOK_DATA_NEWLINE: |
| if (pool->end == pool->ptr && ! poolGrow(pool)) { |
| result = XML_ERROR_NO_MEMORY; |
| goto endEntityValue; |
| } |
| *(pool->ptr)++ = 0xA; |
| break; |
| case XML_TOK_CHAR_REF: { |
| XML_Char buf[XML_ENCODE_MAX]; |
| int i; |
| int n = XmlCharRefNumber(enc, entityTextPtr); |
| if (n < 0) { |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = entityTextPtr; |
| result = XML_ERROR_BAD_CHAR_REF; |
| goto endEntityValue; |
| } |
| n = XmlEncode(n, (ICHAR *)buf); |
| /* The XmlEncode() functions can never return 0 here. That |
| * error return happens if the code point passed in is either |
| * negative or greater than or equal to 0x110000. The |
| * XmlCharRefNumber() functions will all return a number |
| * strictly less than 0x110000 or a negative value if an error |
| * occurred. The negative value is intercepted above, so |
| * XmlEncode() is never passed a value it might return an |
| * error for. |
| */ |
| for (i = 0; i < n; i++) { |
| if (pool->end == pool->ptr && ! poolGrow(pool)) { |
| result = XML_ERROR_NO_MEMORY; |
| goto endEntityValue; |
| } |
| *(pool->ptr)++ = buf[i]; |
| } |
| } break; |
| case XML_TOK_PARTIAL: |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = entityTextPtr; |
| result = XML_ERROR_INVALID_TOKEN; |
| goto endEntityValue; |
| case XML_TOK_INVALID: |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = next; |
| result = XML_ERROR_INVALID_TOKEN; |
| goto endEntityValue; |
| default: |
| /* This default case should be unnecessary -- all the tokens |
| * that XmlEntityValueTok() can return have their own explicit |
| * cases -- but should be retained for safety. We do however |
| * exclude it from the coverage statistics. |
| * |
| * LCOV_EXCL_START |
| */ |
| if (enc == parser->m_encoding) |
| parser->m_eventPtr = entityTextPtr; |
| result = XML_ERROR_UNEXPECTED_STATE; |
| goto endEntityValue; |
| /* LCOV_EXCL_STOP */ |
| } |
| entityTextPtr = next; |
| } |
| endEntityValue: |
| #ifdef XML_DTD |
| parser->m_prologState.inEntityValue = oldInEntityValue; |
| #endif /* XML_DTD */ |
| return result; |
| } |
| |
| static void FASTCALL |
| normalizeLines(XML_Char *s) { |
| XML_Char *p; |
| for (;; s++) { |
| if (*s == XML_T('\0')) |
| return; |
| if (*s == 0xD) |
| break; |
| } |
| p = s; |
| do { |
| if (*s == 0xD) { |
| *p++ = 0xA; |
| if (*++s == 0xA) |
| s++; |
| } else |
| *p++ = *s++; |
| } while (*s); |
| *p = XML_T('\0'); |
| } |
| |
| static int |
| reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, |
| const char *start, const char *end) { |
| const XML_Char *target; |
| XML_Char *data; |
| const char *tem; |
| if (! parser->m_processingInstructionHandler) { |
| if (parser->m_defaultHandler) |
| reportDefault(parser, enc, start, end); |
| return 1; |
| } |
| start += enc->minBytesPerChar * 2; |
| tem = start + XmlNameLength(enc, start); |
| target = poolStoreString(&parser->m_tempPool, enc, start, tem); |
| if (! target) |
| return 0; |
| poolFinish(&parser->m_tempPool); |
| data = poolStoreString(&parser->m_tempPool, enc, XmlSkipS(enc, tem), |
| end - enc->minBytesPerChar * 2); |
| if (! data) |
| return 0; |
| normalizeLines(data); |
| parser->m_processingInstructionHandler(parser->m_handlerArg, target, data); |
| poolClear(&parser->m_tempPool); |
| return 1; |
| } |
| |
| static int |
| reportComment(XML_Parser parser, const ENCODING *enc, const char *start, |
| const char *end) { |
| XML_Char *data; |
| if (! parser->m_commentHandler) { |
| if (parser->m_defaultHandler) |
| reportDefault(parser, enc, start, end); |
| return 1; |
| } |
| data = poolStoreString(&parser->m_tempPool, enc, |
| start + enc->minBytesPerChar * 4, |
| end - enc->minBytesPerChar * 3); |
| if (! data) |
| return 0; |
| normalizeLines(data); |
| parser->m_commentHandler(parser->m_handlerArg, data); |
| poolClear(&parser->m_tempPool); |
| return 1; |
| } |
| |
| static void |
| reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, |
| const char *end) { |
| if (MUST_CONVERT(enc, s)) { |
| enum XML_Convert_Result convert_res; |
| const char **eventPP; |
| const char **eventEndPP; |
| if (enc == parser->m_encoding) { |
| eventPP = &parser->m_eventPtr; |
| eventEndPP = &parser->m_eventEndPtr; |
| } else { |
| /* To get here, two things must be true; the parser must be |
| * using a character encoding that is not the same as the |
| * encoding passed in, and the encoding passed in must need |
| * conversion to the internal format (UTF-8 unless XML_UNICODE |
| * is defined). The only occasions on which the encoding passed |
| * in is not the same as the parser's encoding are when it is |
| * the internal encoding (e.g. a previously defined parameter |
| * entity, already converted to internal format). This by |
| * definition doesn't need conversion, so the whole branch never |
| * gets executed. |
| * |
| * For safety's sake we don't delete these lines and merely |
| * exclude them from coverage statistics. |
| * |
| * LCOV_EXCL_START |
| */ |
| eventPP = &(parser->m_openInternalEntities->internalEventPtr); |
| eventEndPP = &(parser->m_openInternalEntities->internalEventEndPtr); |
| /* LCOV_EXCL_STOP */ |
| } |
| do { |
| ICHAR *dataPtr = (ICHAR *)parser->m_dataBuf; |
| convert_res |
| = XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)parser->m_dataBufEnd); |
| *eventEndPP = s; |
| parser->m_defaultHandler(parser->m_handlerArg, parser->m_dataBuf, |
| (int)(dataPtr - (ICHAR *)parser->m_dataBuf)); |
| *eventPP = s; |
| } while ((convert_res != XML_CONVERT_COMPLETED) |
| && (convert_res != XML_CONVERT_INPUT_INCOMPLETE)); |
| } else |
| parser->m_defaultHandler(parser->m_handlerArg, (XML_Char *)s, |
| (int)((XML_Char *)end - (XML_Char *)s)); |
| } |
| |
| static int |
| defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, |
| XML_Bool isId, const XML_Char *value, XML_Parser parser) { |
| DEFAULT_ATTRIBUTE *att; |
| if (value || isId) { |
| /* The handling of default attributes gets messed up if we have |
| a default which duplicates a non-default. */ |
| int i; |
| for (i = 0; i < type->nDefaultAtts; i++) |
| if (attId == type->defaultAtts[i].id) |
| return 1; |
| if (isId && ! type->idAtt && ! attId->xmlns) |
| type->idAtt = attId; |
| } |
| if (type->nDefaultAtts == type->allocDefaultAtts) { |
| if (type->allocDefaultAtts == 0) { |
| type->allocDefaultAtts = 8; |
| type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC( |
| parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); |
| if (! type->defaultAtts) { |
| type->allocDefaultAtts = 0; |
| return 0; |
| } |
| } else { |
| DEFAULT_ATTRIBUTE *temp; |
| |
| /* Detect and prevent integer overflow */ |
| if (type->allocDefaultAtts > INT_MAX / 2) { |
| return 0; |
| } |
| |
| int count = type->allocDefaultAtts * 2; |
| |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if ((unsigned)count > (size_t)(-1) / sizeof(DEFAULT_ATTRIBUTE)) { |
| return 0; |
| } |
| #endif |
| |
| temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts, |
| (count * sizeof(DEFAULT_ATTRIBUTE))); |
| if (temp == NULL) |
| return 0; |
| type->allocDefaultAtts = count; |
| type->defaultAtts = temp; |
| } |
| } |
| att = type->defaultAtts + type->nDefaultAtts; |
| att->id = attId; |
| att->value = value; |
| att->isCdata = isCdata; |
| if (! isCdata) |
| attId->maybeTokenized = XML_TRUE; |
| type->nDefaultAtts += 1; |
| return 1; |
| } |
| |
| static int |
| setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| const XML_Char *name; |
| for (name = elementType->name; *name; name++) { |
| if (*name == XML_T(ASCII_COLON)) { |
| PREFIX *prefix; |
| const XML_Char *s; |
| for (s = elementType->name; s != name; s++) { |
| if (! poolAppendChar(&dtd->pool, *s)) |
| return 0; |
| } |
| if (! poolAppendChar(&dtd->pool, XML_T('\0'))) |
| return 0; |
| prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), |
| sizeof(PREFIX)); |
| if (! prefix) |
| return 0; |
| if (prefix->name == poolStart(&dtd->pool)) |
| poolFinish(&dtd->pool); |
| else |
| poolDiscard(&dtd->pool); |
| elementType->prefix = prefix; |
| break; |
| } |
| } |
| return 1; |
| } |
| |
| static ATTRIBUTE_ID * |
| getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, |
| const char *end) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| ATTRIBUTE_ID *id; |
| const XML_Char *name; |
| if (! poolAppendChar(&dtd->pool, XML_T('\0'))) |
| return NULL; |
| name = poolStoreString(&dtd->pool, enc, start, end); |
| if (! name) |
| return NULL; |
| /* skip quotation mark - its storage will be re-used (like in name[-1]) */ |
| ++name; |
| id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, |
| sizeof(ATTRIBUTE_ID)); |
| if (! id) |
| return NULL; |
| if (id->name != name) |
| poolDiscard(&dtd->pool); |
| else { |
| poolFinish(&dtd->pool); |
| if (! parser->m_ns) |
| ; |
| else if (name[0] == XML_T(ASCII_x) && name[1] == XML_T(ASCII_m) |
| && name[2] == XML_T(ASCII_l) && name[3] == XML_T(ASCII_n) |
| && name[4] == XML_T(ASCII_s) |
| && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { |
| if (name[5] == XML_T('\0')) |
| id->prefix = &dtd->defaultPrefix; |
| else |
| id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, |
| sizeof(PREFIX)); |
| id->xmlns = XML_TRUE; |
| } else { |
| int i; |
| for (i = 0; name[i]; i++) { |
| /* attributes without prefix are *not* in the default namespace */ |
| if (name[i] == XML_T(ASCII_COLON)) { |
| int j; |
| for (j = 0; j < i; j++) { |
| if (! poolAppendChar(&dtd->pool, name[j])) |
| return NULL; |
| } |
| if (! poolAppendChar(&dtd->pool, XML_T('\0'))) |
| return NULL; |
| id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, |
| poolStart(&dtd->pool), sizeof(PREFIX)); |
| if (! id->prefix) |
| return NULL; |
| if (id->prefix->name == poolStart(&dtd->pool)) |
| poolFinish(&dtd->pool); |
| else |
| poolDiscard(&dtd->pool); |
| break; |
| } |
| } |
| } |
| } |
| return id; |
| } |
| |
| #define CONTEXT_SEP XML_T(ASCII_FF) |
| |
| static const XML_Char * |
| getContext(XML_Parser parser) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| HASH_TABLE_ITER iter; |
| XML_Bool needSep = XML_FALSE; |
| |
| if (dtd->defaultPrefix.binding) { |
| int i; |
| int len; |
| if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS))) |
| return NULL; |
| len = dtd->defaultPrefix.binding->uriLen; |
| if (parser->m_namespaceSeparator) |
| len--; |
| for (i = 0; i < len; i++) { |
| if (! poolAppendChar(&parser->m_tempPool, |
| dtd->defaultPrefix.binding->uri[i])) { |
| /* Because of memory caching, I don't believe this line can be |
| * executed. |
| * |
| * This is part of a loop copying the default prefix binding |
| * URI into the parser's temporary string pool. Previously, |
| * that URI was copied into the same string pool, with a |
| * terminating NUL character, as part of setContext(). When |
| * the pool was cleared, that leaves a block definitely big |
| * enough to hold the URI on the free block list of the pool. |
| * The URI copy in getContext() therefore cannot run out of |
| * memory. |
| * |
| * If the pool is used between the setContext() and |
| * getContext() calls, the worst it can do is leave a bigger |
| * block on the front of the free list. Given that this is |
| * all somewhat inobvious and program logic can be changed, we |
| * don't delete the line but we do exclude it from the test |
| * coverage statistics. |
| */ |
| return NULL; /* LCOV_EXCL_LINE */ |
| } |
| } |
| needSep = XML_TRUE; |
| } |
| |
| hashTableIterInit(&iter, &(dtd->prefixes)); |
| for (;;) { |
| int i; |
| int len; |
| const XML_Char *s; |
| PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); |
| if (! prefix) |
| break; |
| if (! prefix->binding) { |
| /* This test appears to be (justifiable) paranoia. There does |
| * not seem to be a way of injecting a prefix without a binding |
| * that doesn't get errored long before this function is called. |
| * The test should remain for safety's sake, so we instead |
| * exclude the following line from the coverage statistics. |
| */ |
| continue; /* LCOV_EXCL_LINE */ |
| } |
| if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) |
| return NULL; |
| for (s = prefix->name; *s; s++) |
| if (! poolAppendChar(&parser->m_tempPool, *s)) |
| return NULL; |
| if (! poolAppendChar(&parser->m_tempPool, XML_T(ASCII_EQUALS))) |
| return NULL; |
| len = prefix->binding->uriLen; |
| if (parser->m_namespaceSeparator) |
| len--; |
| for (i = 0; i < len; i++) |
| if (! poolAppendChar(&parser->m_tempPool, prefix->binding->uri[i])) |
| return NULL; |
| needSep = XML_TRUE; |
| } |
| |
| hashTableIterInit(&iter, &(dtd->generalEntities)); |
| for (;;) { |
| const XML_Char *s; |
| ENTITY *e = (ENTITY *)hashTableIterNext(&iter); |
| if (! e) |
| break; |
| if (! e->open) |
| continue; |
| if (needSep && ! poolAppendChar(&parser->m_tempPool, CONTEXT_SEP)) |
| return NULL; |
| for (s = e->name; *s; s++) |
| if (! poolAppendChar(&parser->m_tempPool, *s)) |
| return 0; |
| needSep = XML_TRUE; |
| } |
| |
| if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return NULL; |
| return parser->m_tempPool.start; |
| } |
| |
| static XML_Bool |
| setContext(XML_Parser parser, const XML_Char *context) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| const XML_Char *s = context; |
| |
| while (*context != XML_T('\0')) { |
| if (*s == CONTEXT_SEP || *s == XML_T('\0')) { |
| ENTITY *e; |
| if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return XML_FALSE; |
| e = (ENTITY *)lookup(parser, &dtd->generalEntities, |
| poolStart(&parser->m_tempPool), 0); |
| if (e) |
| e->open = XML_TRUE; |
| if (*s != XML_T('\0')) |
| s++; |
| context = s; |
| poolDiscard(&parser->m_tempPool); |
| } else if (*s == XML_T(ASCII_EQUALS)) { |
| PREFIX *prefix; |
| if (poolLength(&parser->m_tempPool) == 0) |
| prefix = &dtd->defaultPrefix; |
| else { |
| if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return XML_FALSE; |
| prefix |
| = (PREFIX *)lookup(parser, &dtd->prefixes, |
| poolStart(&parser->m_tempPool), sizeof(PREFIX)); |
| if (! prefix) |
| return XML_FALSE; |
| if (prefix->name == poolStart(&parser->m_tempPool)) { |
| prefix->name = poolCopyString(&dtd->pool, prefix->name); |
| if (! prefix->name) |
| return XML_FALSE; |
| } |
| poolDiscard(&parser->m_tempPool); |
| } |
| for (context = s + 1; *context != CONTEXT_SEP && *context != XML_T('\0'); |
| context++) |
| if (! poolAppendChar(&parser->m_tempPool, *context)) |
| return XML_FALSE; |
| if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) |
| return XML_FALSE; |
| if (addBinding(parser, prefix, NULL, poolStart(&parser->m_tempPool), |
| &parser->m_inheritedBindings) |
| != XML_ERROR_NONE) |
| return XML_FALSE; |
| poolDiscard(&parser->m_tempPool); |
| if (*context != XML_T('\0')) |
| ++context; |
| s = context; |
| } else { |
| if (! poolAppendChar(&parser->m_tempPool, *s)) |
| return XML_FALSE; |
| s++; |
| } |
| } |
| return XML_TRUE; |
| } |
| |
| static void FASTCALL |
| normalizePublicId(XML_Char *publicId) { |
| XML_Char *p = publicId; |
| XML_Char *s; |
| for (s = publicId; *s; s++) { |
| switch (*s) { |
| case 0x20: |
| case 0xD: |
| case 0xA: |
| if (p != publicId && p[-1] != 0x20) |
| *p++ = 0x20; |
| break; |
| default: |
| *p++ = *s; |
| } |
| } |
| if (p != publicId && p[-1] == 0x20) |
| --p; |
| *p = XML_T('\0'); |
| } |
| |
| static DTD * |
| dtdCreate(const XML_Memory_Handling_Suite *ms) { |
| DTD *p = ms->malloc_fcn(sizeof(DTD)); |
| if (p == NULL) |
| return p; |
| poolInit(&(p->pool), ms); |
| poolInit(&(p->entityValuePool), ms); |
| hashTableInit(&(p->generalEntities), ms); |
| hashTableInit(&(p->elementTypes), ms); |
| hashTableInit(&(p->attributeIds), ms); |
| hashTableInit(&(p->prefixes), ms); |
| #ifdef XML_DTD |
| p->paramEntityRead = XML_FALSE; |
| hashTableInit(&(p->paramEntities), ms); |
| #endif /* XML_DTD */ |
| p->defaultPrefix.name = NULL; |
| p->defaultPrefix.binding = NULL; |
| |
| p->in_eldecl = XML_FALSE; |
| p->scaffIndex = NULL; |
| p->scaffold = NULL; |
| p->scaffLevel = 0; |
| p->scaffSize = 0; |
| p->scaffCount = 0; |
| p->contentStringLen = 0; |
| |
| p->keepProcessing = XML_TRUE; |
| p->hasParamEntityRefs = XML_FALSE; |
| p->standalone = XML_FALSE; |
| return p; |
| } |
| |
| static void |
| dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) { |
| HASH_TABLE_ITER iter; |
| hashTableIterInit(&iter, &(p->elementTypes)); |
| for (;;) { |
| ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); |
| if (! e) |
| break; |
| if (e->allocDefaultAtts != 0) |
| ms->free_fcn(e->defaultAtts); |
| } |
| hashTableClear(&(p->generalEntities)); |
| #ifdef XML_DTD |
| p->paramEntityRead = XML_FALSE; |
| hashTableClear(&(p->paramEntities)); |
| #endif /* XML_DTD */ |
| hashTableClear(&(p->elementTypes)); |
| hashTableClear(&(p->attributeIds)); |
| hashTableClear(&(p->prefixes)); |
| poolClear(&(p->pool)); |
| poolClear(&(p->entityValuePool)); |
| p->defaultPrefix.name = NULL; |
| p->defaultPrefix.binding = NULL; |
| |
| p->in_eldecl = XML_FALSE; |
| |
| ms->free_fcn(p->scaffIndex); |
| p->scaffIndex = NULL; |
| ms->free_fcn(p->scaffold); |
| p->scaffold = NULL; |
| |
| p->scaffLevel = 0; |
| p->scaffSize = 0; |
| p->scaffCount = 0; |
| p->contentStringLen = 0; |
| |
| p->keepProcessing = XML_TRUE; |
| p->hasParamEntityRefs = XML_FALSE; |
| p->standalone = XML_FALSE; |
| } |
| |
| static void |
| dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) { |
| HASH_TABLE_ITER iter; |
| hashTableIterInit(&iter, &(p->elementTypes)); |
| for (;;) { |
| ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); |
| if (! e) |
| break; |
| if (e->allocDefaultAtts != 0) |
| ms->free_fcn(e->defaultAtts); |
| } |
| hashTableDestroy(&(p->generalEntities)); |
| #ifdef XML_DTD |
| hashTableDestroy(&(p->paramEntities)); |
| #endif /* XML_DTD */ |
| hashTableDestroy(&(p->elementTypes)); |
| hashTableDestroy(&(p->attributeIds)); |
| hashTableDestroy(&(p->prefixes)); |
| poolDestroy(&(p->pool)); |
| poolDestroy(&(p->entityValuePool)); |
| if (isDocEntity) { |
| ms->free_fcn(p->scaffIndex); |
| ms->free_fcn(p->scaffold); |
| } |
| ms->free_fcn(p); |
| } |
| |
| /* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. |
| The new DTD has already been initialized. |
| */ |
| static int |
| dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, |
| const XML_Memory_Handling_Suite *ms) { |
| HASH_TABLE_ITER iter; |
| |
| /* Copy the prefix table. */ |
| |
| hashTableIterInit(&iter, &(oldDtd->prefixes)); |
| for (;;) { |
| const XML_Char *name; |
| const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); |
| if (! oldP) |
| break; |
| name = poolCopyString(&(newDtd->pool), oldP->name); |
| if (! name) |
| return 0; |
| if (! lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) |
| return 0; |
| } |
| |
| hashTableIterInit(&iter, &(oldDtd->attributeIds)); |
| |
| /* Copy the attribute id table. */ |
| |
| for (;;) { |
| ATTRIBUTE_ID *newA; |
| const XML_Char *name; |
| const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); |
| |
| if (! oldA) |
| break; |
| /* Remember to allocate the scratch byte before the name. */ |
| if (! poolAppendChar(&(newDtd->pool), XML_T('\0'))) |
| return 0; |
| name = poolCopyString(&(newDtd->pool), oldA->name); |
| if (! name) |
| return 0; |
| ++name; |
| newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, |
| sizeof(ATTRIBUTE_ID)); |
| if (! newA) |
| return 0; |
| newA->maybeTokenized = oldA->maybeTokenized; |
| if (oldA->prefix) { |
| newA->xmlns = oldA->xmlns; |
| if (oldA->prefix == &oldDtd->defaultPrefix) |
| newA->prefix = &newDtd->defaultPrefix; |
| else |
| newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), |
| oldA->prefix->name, 0); |
| } |
| } |
| |
| /* Copy the element type table. */ |
| |
| hashTableIterInit(&iter, &(oldDtd->elementTypes)); |
| |
| for (;;) { |
| int i; |
| ELEMENT_TYPE *newE; |
| const XML_Char *name; |
| const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); |
| if (! oldE) |
| break; |
| name = poolCopyString(&(newDtd->pool), oldE->name); |
| if (! name) |
| return 0; |
| newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, |
| sizeof(ELEMENT_TYPE)); |
| if (! newE) |
| return 0; |
| if (oldE->nDefaultAtts) { |
| newE->defaultAtts |
| = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); |
| if (! newE->defaultAtts) { |
| return 0; |
| } |
| } |
| if (oldE->idAtt) |
| newE->idAtt = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), |
| oldE->idAtt->name, 0); |
| newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; |
| if (oldE->prefix) |
| newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), |
| oldE->prefix->name, 0); |
| for (i = 0; i < newE->nDefaultAtts; i++) { |
| newE->defaultAtts[i].id = (ATTRIBUTE_ID *)lookup( |
| oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); |
| newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; |
| if (oldE->defaultAtts[i].value) { |
| newE->defaultAtts[i].value |
| = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); |
| if (! newE->defaultAtts[i].value) |
| return 0; |
| } else |
| newE->defaultAtts[i].value = NULL; |
| } |
| } |
| |
| /* Copy the entity tables. */ |
| if (! copyEntityTable(oldParser, &(newDtd->generalEntities), &(newDtd->pool), |
| &(oldDtd->generalEntities))) |
| return 0; |
| |
| #ifdef XML_DTD |
| if (! copyEntityTable(oldParser, &(newDtd->paramEntities), &(newDtd->pool), |
| &(oldDtd->paramEntities))) |
| return 0; |
| newDtd->paramEntityRead = oldDtd->paramEntityRead; |
| #endif /* XML_DTD */ |
| |
| newDtd->keepProcessing = oldDtd->keepProcessing; |
| newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; |
| newDtd->standalone = oldDtd->standalone; |
| |
| /* Don't want deep copying for scaffolding */ |
| newDtd->in_eldecl = oldDtd->in_eldecl; |
| newDtd->scaffold = oldDtd->scaffold; |
| newDtd->contentStringLen = oldDtd->contentStringLen; |
| newDtd->scaffSize = oldDtd->scaffSize; |
| newDtd->scaffLevel = oldDtd->scaffLevel; |
| newDtd->scaffIndex = oldDtd->scaffIndex; |
| |
| return 1; |
| } /* End dtdCopy */ |
| |
| static int |
| copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable, |
| STRING_POOL *newPool, const HASH_TABLE *oldTable) { |
| HASH_TABLE_ITER iter; |
| const XML_Char *cachedOldBase = NULL; |
| const XML_Char *cachedNewBase = NULL; |
| |
| hashTableIterInit(&iter, oldTable); |
| |
| for (;;) { |
| ENTITY *newE; |
| const XML_Char *name; |
| const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); |
| if (! oldE) |
| break; |
| name = poolCopyString(newPool, oldE->name); |
| if (! name) |
| return 0; |
| newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); |
| if (! newE) |
| return 0; |
| if (oldE->systemId) { |
| const XML_Char *tem = poolCopyString(newPool, oldE->systemId); |
| if (! tem) |
| return 0; |
| newE->systemId = tem; |
| if (oldE->base) { |
| if (oldE->base == cachedOldBase) |
| newE->base = cachedNewBase; |
| else { |
| cachedOldBase = oldE->base; |
| tem = poolCopyString(newPool, cachedOldBase); |
| if (! tem) |
| return 0; |
| cachedNewBase = newE->base = tem; |
| } |
| } |
| if (oldE->publicId) { |
| tem = poolCopyString(newPool, oldE->publicId); |
| if (! tem) |
| return 0; |
| newE->publicId = tem; |
| } |
| } else { |
| const XML_Char *tem |
| = poolCopyStringN(newPool, oldE->textPtr, oldE->textLen); |
| if (! tem) |
| return 0; |
| newE->textPtr = tem; |
| newE->textLen = oldE->textLen; |
| } |
| if (oldE->notation) { |
| const XML_Char *tem = poolCopyString(newPool, oldE->notation); |
| if (! tem) |
| return 0; |
| newE->notation = tem; |
| } |
| newE->is_param = oldE->is_param; |
| newE->is_internal = oldE->is_internal; |
| } |
| return 1; |
| } |
| |
| #define INIT_POWER 6 |
| |
| static XML_Bool FASTCALL |
| keyeq(KEY s1, KEY s2) { |
| for (; *s1 == *s2; s1++, s2++) |
| if (*s1 == 0) |
| return XML_TRUE; |
| return XML_FALSE; |
| } |
| |
| static size_t |
| keylen(KEY s) { |
| size_t len = 0; |
| for (; *s; s++, len++) |
| ; |
| return len; |
| } |
| |
| static void |
| copy_salt_to_sipkey(XML_Parser parser, struct sipkey *key) { |
| key->k[0] = 0; |
| key->k[1] = get_hash_secret_salt(parser); |
| } |
| |
| static unsigned long FASTCALL |
| hash(XML_Parser parser, KEY s) { |
| struct siphash state; |
| struct sipkey key; |
| (void)sip24_valid; |
| copy_salt_to_sipkey(parser, &key); |
| sip24_init(&state, &key); |
| sip24_update(&state, s, keylen(s) * sizeof(XML_Char)); |
| return (unsigned long)sip24_final(&state); |
| } |
| |
| static NAMED * |
| lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) { |
| size_t i; |
| if (table->size == 0) { |
| size_t tsize; |
| if (! createSize) |
| return NULL; |
| table->power = INIT_POWER; |
| /* table->size is a power of 2 */ |
| table->size = (size_t)1 << INIT_POWER; |
| tsize = table->size * sizeof(NAMED *); |
| table->v = table->mem->malloc_fcn(tsize); |
| if (! table->v) { |
| table->size = 0; |
| return NULL; |
| } |
| memset(table->v, 0, tsize); |
| i = hash(parser, name) & ((unsigned long)table->size - 1); |
| } else { |
| unsigned long h = hash(parser, name); |
| unsigned long mask = (unsigned long)table->size - 1; |
| unsigned char step = 0; |
| i = h & mask; |
| while (table->v[i]) { |
| if (keyeq(name, table->v[i]->name)) |
| return table->v[i]; |
| if (! step) |
| step = PROBE_STEP(h, mask, table->power); |
| i < step ? (i += table->size - step) : (i -= step); |
| } |
| if (! createSize) |
| return NULL; |
| |
| /* check for overflow (table is half full) */ |
| if (table->used >> (table->power - 1)) { |
| unsigned char newPower = table->power + 1; |
| |
| /* Detect and prevent invalid shift */ |
| if (newPower >= sizeof(unsigned long) * 8 /* bits per byte */) { |
| return NULL; |
| } |
| |
| size_t newSize = (size_t)1 << newPower; |
| unsigned long newMask = (unsigned long)newSize - 1; |
| |
| /* Detect and prevent integer overflow */ |
| if (newSize > (size_t)(-1) / sizeof(NAMED *)) { |
| return NULL; |
| } |
| |
| size_t tsize = newSize * sizeof(NAMED *); |
| NAMED **newV = table->mem->malloc_fcn(tsize); |
| if (! newV) |
| return NULL; |
| memset(newV, 0, tsize); |
| for (i = 0; i < table->size; i++) |
| if (table->v[i]) { |
| unsigned long newHash = hash(parser, table->v[i]->name); |
| size_t j = newHash & newMask; |
| step = 0; |
| while (newV[j]) { |
| if (! step) |
| step = PROBE_STEP(newHash, newMask, newPower); |
| j < step ? (j += newSize - step) : (j -= step); |
| } |
| newV[j] = table->v[i]; |
| } |
| table->mem->free_fcn(table->v); |
| table->v = newV; |
| table->power = newPower; |
| table->size = newSize; |
| i = h & newMask; |
| step = 0; |
| while (table->v[i]) { |
| if (! step) |
| step = PROBE_STEP(h, newMask, newPower); |
| i < step ? (i += newSize - step) : (i -= step); |
| } |
| } |
| } |
| table->v[i] = table->mem->malloc_fcn(createSize); |
| if (! table->v[i]) |
| return NULL; |
| memset(table->v[i], 0, createSize); |
| table->v[i]->name = name; |
| (table->used)++; |
| return table->v[i]; |
| } |
| |
| static void FASTCALL |
| hashTableClear(HASH_TABLE *table) { |
| size_t i; |
| for (i = 0; i < table->size; i++) { |
| table->mem->free_fcn(table->v[i]); |
| table->v[i] = NULL; |
| } |
| table->used = 0; |
| } |
| |
| static void FASTCALL |
| hashTableDestroy(HASH_TABLE *table) { |
| size_t i; |
| for (i = 0; i < table->size; i++) |
| table->mem->free_fcn(table->v[i]); |
| table->mem->free_fcn(table->v); |
| } |
| |
| static void FASTCALL |
| hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) { |
| p->power = 0; |
| p->size = 0; |
| p->used = 0; |
| p->v = NULL; |
| p->mem = ms; |
| } |
| |
| static void FASTCALL |
| hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) { |
| iter->p = table->v; |
| iter->end = iter->p ? iter->p + table->size : NULL; |
| } |
| |
| static NAMED *FASTCALL |
| hashTableIterNext(HASH_TABLE_ITER *iter) { |
| while (iter->p != iter->end) { |
| NAMED *tem = *(iter->p)++; |
| if (tem) |
| return tem; |
| } |
| return NULL; |
| } |
| |
| static void FASTCALL |
| poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) { |
| pool->blocks = NULL; |
| pool->freeBlocks = NULL; |
| pool->start = NULL; |
| pool->ptr = NULL; |
| pool->end = NULL; |
| pool->mem = ms; |
| } |
| |
| static void FASTCALL |
| poolClear(STRING_POOL *pool) { |
| if (! pool->freeBlocks) |
| pool->freeBlocks = pool->blocks; |
| else { |
| BLOCK *p = pool->blocks; |
| while (p) { |
| BLOCK *tem = p->next; |
| p->next = pool->freeBlocks; |
| pool->freeBlocks = p; |
| p = tem; |
| } |
| } |
| pool->blocks = NULL; |
| pool->start = NULL; |
| pool->ptr = NULL; |
| pool->end = NULL; |
| } |
| |
| static void FASTCALL |
| poolDestroy(STRING_POOL *pool) { |
| BLOCK *p = pool->blocks; |
| while (p) { |
| BLOCK *tem = p->next; |
| pool->mem->free_fcn(p); |
| p = tem; |
| } |
| p = pool->freeBlocks; |
| while (p) { |
| BLOCK *tem = p->next; |
| pool->mem->free_fcn(p); |
| p = tem; |
| } |
| } |
| |
| static XML_Char * |
| poolAppend(STRING_POOL *pool, const ENCODING *enc, const char *ptr, |
| const char *end) { |
| if (! pool->ptr && ! poolGrow(pool)) |
| return NULL; |
| for (;;) { |
| const enum XML_Convert_Result convert_res = XmlConvert( |
| enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); |
| if ((convert_res == XML_CONVERT_COMPLETED) |
| || (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) |
| break; |
| if (! poolGrow(pool)) |
| return NULL; |
| } |
| return pool->start; |
| } |
| |
| static const XML_Char *FASTCALL |
| poolCopyString(STRING_POOL *pool, const XML_Char *s) { |
| do { |
| if (! poolAppendChar(pool, *s)) |
| return NULL; |
| } while (*s++); |
| s = pool->start; |
| poolFinish(pool); |
| return s; |
| } |
| |
| static const XML_Char * |
| poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) { |
| if (! pool->ptr && ! poolGrow(pool)) { |
| /* The following line is unreachable given the current usage of |
| * poolCopyStringN(). Currently it is called from exactly one |
| * place to copy the text of a simple general entity. By that |
| * point, the name of the entity is already stored in the pool, so |
| * pool->ptr cannot be NULL. |
| * |
| * If poolCopyStringN() is used elsewhere as it well might be, |
| * this line may well become executable again. Regardless, this |
| * sort of check shouldn't be removed lightly, so we just exclude |
| * it from the coverage statistics. |
| */ |
| return NULL; /* LCOV_EXCL_LINE */ |
| } |
| for (; n > 0; --n, s++) { |
| if (! poolAppendChar(pool, *s)) |
| return NULL; |
| } |
| s = pool->start; |
| poolFinish(pool); |
| return s; |
| } |
| |
| static const XML_Char *FASTCALL |
| poolAppendString(STRING_POOL *pool, const XML_Char *s) { |
| while (*s) { |
| if (! poolAppendChar(pool, *s)) |
| return NULL; |
| s++; |
| } |
| return pool->start; |
| } |
| |
| static XML_Char * |
| poolStoreString(STRING_POOL *pool, const ENCODING *enc, const char *ptr, |
| const char *end) { |
| if (! poolAppend(pool, enc, ptr, end)) |
| return NULL; |
| if (pool->ptr == pool->end && ! poolGrow(pool)) |
| return NULL; |
| *(pool->ptr)++ = 0; |
| return pool->start; |
| } |
| |
| static size_t |
| poolBytesToAllocateFor(int blockSize) { |
| /* Unprotected math would be: |
| ** return offsetof(BLOCK, s) + blockSize * sizeof(XML_Char); |
| ** |
| ** Detect overflow, avoiding _signed_ overflow undefined behavior |
| ** For a + b * c we check b * c in isolation first, so that addition of a |
| ** on top has no chance of making us accept a small non-negative number |
| */ |
| const size_t stretch = sizeof(XML_Char); /* can be 4 bytes */ |
| |
| if (blockSize <= 0) |
| return 0; |
| |
| if (blockSize > (int)(INT_MAX / stretch)) |
| return 0; |
| |
| { |
| const int stretchedBlockSize = blockSize * (int)stretch; |
| const int bytesToAllocate |
| = (int)(offsetof(BLOCK, s) + (unsigned)stretchedBlockSize); |
| if (bytesToAllocate < 0) |
| return 0; |
| |
| return (size_t)bytesToAllocate; |
| } |
| } |
| |
| static XML_Bool FASTCALL |
| poolGrow(STRING_POOL *pool) { |
| if (pool->freeBlocks) { |
| if (pool->start == 0) { |
| pool->blocks = pool->freeBlocks; |
| pool->freeBlocks = pool->freeBlocks->next; |
| pool->blocks->next = NULL; |
| pool->start = pool->blocks->s; |
| pool->end = pool->start + pool->blocks->size; |
| pool->ptr = pool->start; |
| return XML_TRUE; |
| } |
| if (pool->end - pool->start < pool->freeBlocks->size) { |
| BLOCK *tem = pool->freeBlocks->next; |
| pool->freeBlocks->next = pool->blocks; |
| pool->blocks = pool->freeBlocks; |
| pool->freeBlocks = tem; |
| memcpy(pool->blocks->s, pool->start, |
| (pool->end - pool->start) * sizeof(XML_Char)); |
| pool->ptr = pool->blocks->s + (pool->ptr - pool->start); |
| pool->start = pool->blocks->s; |
| pool->end = pool->start + pool->blocks->size; |
| return XML_TRUE; |
| } |
| } |
| if (pool->blocks && pool->start == pool->blocks->s) { |
| BLOCK *temp; |
| int blockSize = (int)((unsigned)(pool->end - pool->start) * 2U); |
| size_t bytesToAllocate; |
| |
| /* NOTE: Needs to be calculated prior to calling `realloc` |
| to avoid dangling pointers: */ |
| const ptrdiff_t offsetInsideBlock = pool->ptr - pool->start; |
| |
| if (blockSize < 0) { |
| /* This condition traps a situation where either more than |
| * INT_MAX/2 bytes have already been allocated. This isn't |
| * readily testable, since it is unlikely that an average |
| * machine will have that much memory, so we exclude it from the |
| * coverage statistics. |
| */ |
| return XML_FALSE; /* LCOV_EXCL_LINE */ |
| } |
| |
| bytesToAllocate = poolBytesToAllocateFor(blockSize); |
| if (bytesToAllocate == 0) |
| return XML_FALSE; |
| |
| temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks, |
| (unsigned)bytesToAllocate); |
| if (temp == NULL) |
| return XML_FALSE; |
| pool->blocks = temp; |
| pool->blocks->size = blockSize; |
| pool->ptr = pool->blocks->s + offsetInsideBlock; |
| pool->start = pool->blocks->s; |
| pool->end = pool->start + blockSize; |
| } else { |
| BLOCK *tem; |
| int blockSize = (int)(pool->end - pool->start); |
| size_t bytesToAllocate; |
| |
| if (blockSize < 0) { |
| /* This condition traps a situation where either more than |
| * INT_MAX bytes have already been allocated (which is prevented |
| * by various pieces of program logic, not least this one, never |
| * mind the unlikelihood of actually having that much memory) or |
| * the pool control fields have been corrupted (which could |
| * conceivably happen in an extremely buggy user handler |
| * function). Either way it isn't readily testable, so we |
| * exclude it from the coverage statistics. |
| */ |
| return XML_FALSE; /* LCOV_EXCL_LINE */ |
| } |
| |
| if (blockSize < INIT_BLOCK_SIZE) |
| blockSize = INIT_BLOCK_SIZE; |
| else { |
| /* Detect overflow, avoiding _signed_ overflow undefined behavior */ |
| if ((int)((unsigned)blockSize * 2U) < 0) { |
| return XML_FALSE; |
| } |
| blockSize *= 2; |
| } |
| |
| bytesToAllocate = poolBytesToAllocateFor(blockSize); |
| if (bytesToAllocate == 0) |
| return XML_FALSE; |
| |
| tem = pool->mem->malloc_fcn(bytesToAllocate); |
| if (! tem) |
| return XML_FALSE; |
| tem->size = blockSize; |
| tem->next = pool->blocks; |
| pool->blocks = tem; |
| if (pool->ptr != pool->start) |
| memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char)); |
| pool->ptr = tem->s + (pool->ptr - pool->start); |
| pool->start = tem->s; |
| pool->end = tem->s + blockSize; |
| } |
| return XML_TRUE; |
| } |
| |
| static int FASTCALL |
| nextScaffoldPart(XML_Parser parser) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| CONTENT_SCAFFOLD *me; |
| int next; |
| |
| if (! dtd->scaffIndex) { |
| dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int)); |
| if (! dtd->scaffIndex) |
| return -1; |
| dtd->scaffIndex[0] = 0; |
| } |
| |
| if (dtd->scaffCount >= dtd->scaffSize) { |
| CONTENT_SCAFFOLD *temp; |
| if (dtd->scaffold) { |
| /* Detect and prevent integer overflow */ |
| if (dtd->scaffSize > UINT_MAX / 2u) { |
| return -1; |
| } |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if (dtd->scaffSize > (size_t)(-1) / 2u / sizeof(CONTENT_SCAFFOLD)) { |
| return -1; |
| } |
| #endif |
| |
| temp = (CONTENT_SCAFFOLD *)REALLOC( |
| parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); |
| if (temp == NULL) |
| return -1; |
| dtd->scaffSize *= 2; |
| } else { |
| temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS |
| * sizeof(CONTENT_SCAFFOLD)); |
| if (temp == NULL) |
| return -1; |
| dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; |
| } |
| dtd->scaffold = temp; |
| } |
| next = dtd->scaffCount++; |
| me = &dtd->scaffold[next]; |
| if (dtd->scaffLevel) { |
| CONTENT_SCAFFOLD *parent |
| = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]]; |
| if (parent->lastchild) { |
| dtd->scaffold[parent->lastchild].nextsib = next; |
| } |
| if (! parent->childcnt) |
| parent->firstchild = next; |
| parent->lastchild = next; |
| parent->childcnt++; |
| } |
| me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; |
| return next; |
| } |
| |
| static XML_Content * |
| build_model(XML_Parser parser) { |
| /* Function build_model transforms the existing parser->m_dtd->scaffold |
| * array of CONTENT_SCAFFOLD tree nodes into a new array of |
| * XML_Content tree nodes followed by a gapless list of zero-terminated |
| * strings. */ |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| XML_Content *ret; |
| XML_Char *str; /* the current string writing location */ |
| |
| /* Detect and prevent integer overflow. |
| * The preprocessor guard addresses the "always false" warning |
| * from -Wtype-limits on platforms where |
| * sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ |
| #if UINT_MAX >= SIZE_MAX |
| if (dtd->scaffCount > (size_t)(-1) / sizeof(XML_Content)) { |
| return NULL; |
| } |
| if (dtd->contentStringLen > (size_t)(-1) / sizeof(XML_Char)) { |
| return NULL; |
| } |
| #endif |
| if (dtd->scaffCount * sizeof(XML_Content) |
| > (size_t)(-1) - dtd->contentStringLen * sizeof(XML_Char)) { |
| return NULL; |
| } |
| |
| const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content) |
| + (dtd->contentStringLen * sizeof(XML_Char))); |
| |
| ret = (XML_Content *)MALLOC(parser, allocsize); |
| if (! ret) |
| return NULL; |
| |
| /* What follows is an iterative implementation (of what was previously done |
| * recursively in a dedicated function called "build_node". The old recursive |
| * build_node could be forced into stack exhaustion from input as small as a |
| * few megabyte, and so that was a security issue. Hence, a function call |
| * stack is avoided now by resolving recursion.) |
| * |
| * The iterative approach works as follows: |
| * |
| * - We have two writing pointers, both walking up the result array; one does |
| * the work, the other creates "jobs" for its colleague to do, and leads |
| * the way: |
| * |
| * - The faster one, pointer jobDest, always leads and writes "what job |
| * to do" by the other, once they reach that place in the |
| * array: leader "jobDest" stores the source node array index (relative |
| * to array dtd->scaffold) in field "numchildren". |
| * |
| * - The slower one, pointer dest, looks at the value stored in the |
| * "numchildren" field (which actually holds a source node array index |
| * at that time) and puts the real data from dtd->scaffold in. |
| * |
| * - Before the loop starts, jobDest writes source array index 0 |
| * (where the root node is located) so that dest will have something to do |
| * when it starts operation. |
| * |
| * - Whenever nodes with children are encountered, jobDest appends |
| * them as new jobs, in order. As a result, tree node siblings are |
| * adjacent in the resulting array, for example: |
| * |
| * [0] root, has two children |
| * [1] first child of 0, has three children |
| * [3] first child of 1, does not have children |
| * [4] second child of 1, does not have children |
| * [5] third child of 1, does not have children |
| * [2] second child of 0, does not have children |
| * |
| * Or (the same data) presented in flat array view: |
| * |
| * [0] root, has two children |
| * |
| * [1] first child of 0, has three children |
| * [2] second child of 0, does not have children |
| * |
| * [3] first child of 1, does not have children |
| * [4] second child of 1, does not have children |
| * [5] third child of 1, does not have children |
| * |
| * - The algorithm repeats until all target array indices have been processed. |
| */ |
| XML_Content *dest = ret; /* tree node writing location, moves upwards */ |
| XML_Content *const destLimit = &ret[dtd->scaffCount]; |
| XML_Content *jobDest = ret; /* next free writing location in target array */ |
| str = (XML_Char *)&ret[dtd->scaffCount]; |
| |
| /* Add the starting job, the root node (index 0) of the source tree */ |
| (jobDest++)->numchildren = 0; |
| |
| for (; dest < destLimit; dest++) { |
| /* Retrieve source tree array index from job storage */ |
| const int src_node = (int)dest->numchildren; |
| |
| /* Convert item */ |
| dest->type = dtd->scaffold[src_node].type; |
| dest->quant = dtd->scaffold[src_node].quant; |
| if (dest->type == XML_CTYPE_NAME) { |
| const XML_Char *src; |
| dest->name = str; |
| src = dtd->scaffold[src_node].name; |
| for (;;) { |
| *str++ = *src; |
| if (! *src) |
| break; |
| src++; |
| } |
| dest->numchildren = 0; |
| dest->children = NULL; |
| } else { |
| unsigned int i; |
| int cn; |
| dest->name = NULL; |
| dest->numchildren = dtd->scaffold[src_node].childcnt; |
| dest->children = jobDest; |
| |
| /* Append scaffold indices of children to array */ |
| for (i = 0, cn = dtd->scaffold[src_node].firstchild; |
| i < dest->numchildren; i++, cn = dtd->scaffold[cn].nextsib) |
| (jobDest++)->numchildren = (unsigned int)cn; |
| } |
| } |
| |
| return ret; |
| } |
| |
| static ELEMENT_TYPE * |
| getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr, |
| const char *end) { |
| DTD *const dtd = parser->m_dtd; /* save one level of indirection */ |
| const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); |
| ELEMENT_TYPE *ret; |
| |
| if (! name) |
| return NULL; |
| ret = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, |
| sizeof(ELEMENT_TYPE)); |
| if (! ret) |
| return NULL; |
| if (ret->name != name) |
| poolDiscard(&dtd->pool); |
| else { |
| poolFinish(&dtd->pool); |
| if (! setElementTypePrefix(parser, ret)) |
| return NULL; |
| } |
| return ret; |
| } |
| |
| static XML_Char * |
| copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) { |
| size_t charsRequired = 0; |
| XML_Char *result; |
| |
| /* First determine how long the string is */ |
| while (s[charsRequired] != 0) { |
| charsRequired++; |
| } |
| /* Include the terminator */ |
| charsRequired++; |
| |
| /* Now allocate space for the copy */ |
| result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char)); |
| if (result == NULL) |
| return NULL; |
| /* Copy the original into place */ |
| memcpy(result, s, charsRequired * sizeof(XML_Char)); |
| return result; |
| } |
| |
| #ifdef XML_DTD |
| |
| static float |
| accountingGetCurrentAmplification(XML_Parser rootParser) { |
| const XmlBigCount countBytesOutput |
| = rootParser->m_accounting.countBytesDirect |
| + rootParser->m_accounting.countBytesIndirect; |
| const float amplificationFactor |
| = rootParser->m_accounting.countBytesDirect |
| ? (countBytesOutput |
| / (float)(rootParser->m_accounting.countBytesDirect)) |
| : 1.0f; |
| assert(! rootParser->m_parentParser); |
| return amplificationFactor; |
| } |
| |
| static void |
| accountingReportStats(XML_Parser originParser, const char *epilog) { |
| const XML_Parser rootParser = getRootParserOf(originParser, NULL); |
| assert(! rootParser->m_parentParser); |
| |
| if (rootParser->m_accounting.debugLevel < 1) { |
| return; |
| } |
| |
| const float amplificationFactor |
| = accountingGetCurrentAmplification(rootParser); |
| fprintf(stderr, |
| "expat: Accounting(%p): Direct " EXPAT_FMT_ULL( |
| "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s", |
| (void *)rootParser, rootParser->m_accounting.countBytesDirect, |
| rootParser->m_accounting.countBytesIndirect, |
| (double)amplificationFactor, epilog); |
| } |
| |
| static void |
| accountingOnAbort(XML_Parser originParser) { |
| accountingReportStats(originParser, " ABORTING\n"); |
| } |
| |
| static void |
| accountingReportDiff(XML_Parser rootParser, |
| unsigned int levelsAwayFromRootParser, const char *before, |
| const char *after, ptrdiff_t bytesMore, int source_line, |
| enum XML_Account account) { |
| assert(! rootParser->m_parentParser); |
| |
| fprintf(stderr, |
| " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"", |
| bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP", |
| levelsAwayFromRootParser, source_line, 10, ""); |
| |
| const char ellipis[] = "[..]"; |
| const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1; |
| const unsigned int contextLength = 10; |
| |
| /* Note: Performance is of no concern here */ |
| const char *walker = before; |
| if ((rootParser->m_accounting.debugLevel >= 3) |
| || (after - before) |
| <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) { |
| for (; walker < after; walker++) { |
| fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); |
| } |
| } else { |
| for (; walker < before + contextLength; walker++) { |
| fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); |
| } |
| fprintf(stderr, ellipis); |
| walker = after - contextLength; |
| for (; walker < after; walker++) { |
| fprintf(stderr, "%s", unsignedCharToPrintable(walker[0])); |
| } |
| } |
| fprintf(stderr, "\"\n"); |
| } |
| |
| static XML_Bool |
| accountingDiffTolerated(XML_Parser originParser, int tok, const char *before, |
| const char *after, int source_line, |
| enum XML_Account account) { |
| /* Note: We need to check the token type *first* to be sure that |
| * we can even access variable <after>, safely. |
| * E.g. for XML_TOK_NONE <after> may hold an invalid pointer. */ |
| switch (tok) { |
| case XML_TOK_INVALID: |
| case XML_TOK_PARTIAL: |
| case XML_TOK_PARTIAL_CHAR: |
| case XML_TOK_NONE: |
| return XML_TRUE; |
| } |
| |
| if (account == XML_ACCOUNT_NONE) |
| return XML_TRUE; /* because these bytes have been accounted for, already */ |
| |
| unsigned int levelsAwayFromRootParser; |
| const XML_Parser rootParser |
| = getRootParserOf(originParser, &levelsAwayFromRootParser); |
| assert(! rootParser->m_parentParser); |
| |
| const int isDirect |
| = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser); |
| const ptrdiff_t bytesMore = after - before; |
| |
| XmlBigCount *const additionTarget |
| = isDirect ? &rootParser->m_accounting.countBytesDirect |
| : &rootParser->m_accounting.countBytesIndirect; |
| |
| /* Detect and avoid integer overflow */ |
| if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore) |
| return XML_FALSE; |
| *additionTarget += bytesMore; |
| |
| const XmlBigCount countBytesOutput |
| = rootParser->m_accounting.countBytesDirect |
| + rootParser->m_accounting.countBytesIndirect; |
| const float amplificationFactor |
| = accountingGetCurrentAmplification(rootParser); |
| const XML_Bool tolerated |
| = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes) |
| || (amplificationFactor |
| <= rootParser->m_accounting.maximumAmplificationFactor); |
| |
| if (rootParser->m_accounting.debugLevel >= 2) { |
| accountingReportStats(rootParser, ""); |
| accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after, |
| bytesMore, source_line, account); |
| } |
| |
| return tolerated; |
| } |
| |
| unsigned long long |
| testingAccountingGetCountBytesDirect(XML_Parser parser) { |
| if (! parser) |
| return 0; |
| return parser->m_accounting.countBytesDirect; |
| } |
| |
| unsigned long long |
| testingAccountingGetCountBytesIndirect(XML_Parser parser) { |
| if (! parser) |
| return 0; |
| return parser->m_accounting.countBytesIndirect; |
| } |
| |
| static void |
| entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity, |
| const char *action, int sourceLine) { |
| assert(! rootParser->m_parentParser); |
| if (rootParser->m_entity_stats.debugLevel < 1) |
| return; |
| |
| # if defined(XML_UNICODE) |
| const char *const entityName = "[..]"; |
| # else |
| const char *const entityName = entity->name; |
| # endif |
| |
| fprintf( |
| stderr, |
| "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n", |
| (void *)rootParser, rootParser->m_entity_stats.countEverOpened, |
| rootParser->m_entity_stats.currentDepth, |
| rootParser->m_entity_stats.maximumDepthSeen, |
| (rootParser->m_entity_stats.currentDepth - 1) * 2, "", |
| entity->is_param ? "%" : "&", entityName, action, entity->textLen, |
| sourceLine); |
| } |
| |
| static void |
| entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) { |
| const XML_Parser rootParser = getRootParserOf(originParser, NULL); |
| assert(! rootParser->m_parentParser); |
| |
| rootParser->m_entity_stats.countEverOpened++; |
| rootParser->m_entity_stats.currentDepth++; |
| if (rootParser->m_entity_stats.currentDepth |
| > rootParser->m_entity_stats.maximumDepthSeen) { |
| rootParser->m_entity_stats.maximumDepthSeen++; |
| } |
| |
| entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine); |
| } |
| |
| static void |
| entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) { |
| const XML_Parser rootParser = getRootParserOf(originParser, NULL); |
| assert(! rootParser->m_parentParser); |
| |
| entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine); |
| rootParser->m_entity_stats.currentDepth--; |
| } |
| |
| static XML_Parser |
| getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) { |
| XML_Parser rootParser = parser; |
| unsigned int stepsTakenUpwards = 0; |
| while (rootParser->m_parentParser) { |
| rootParser = rootParser->m_parentParser; |
| stepsTakenUpwards++; |
| } |
| assert(! rootParser->m_parentParser); |
| if (outLevelDiff != NULL) { |
| *outLevelDiff = stepsTakenUpwards; |
| } |
| return rootParser; |
| } |
| |
| const char * |
| unsignedCharToPrintable(unsigned char c) { |
| switch (c) { |
| case 0: |
| return "\\0"; |
| case 1: |
| return "\\x1"; |
| case 2: |
| return "\\x2"; |
| case 3: |
| return "\\x3"; |
| case 4: |
| return "\\x4"; |
| case 5: |
| return "\\x5"; |
| case 6: |
| return "\\x6"; |
| case 7: |
| return "\\x7"; |
| case 8: |
| return "\\x8"; |
| case 9: |
| return "\\t"; |
| case 10: |
| return "\\n"; |
| case 11: |
| return "\\xB"; |
| case 12: |
| return "\\xC"; |
| case 13: |
| return "\\r"; |
| case 14: |
| return "\\xE"; |
| case 15: |
| return "\\xF"; |
| case 16: |
| return "\\x10"; |
| case 17: |
| return "\\x11"; |
| case 18: |
| return "\\x12"; |
| case 19: |
| return "\\x13"; |
| case 20: |
| return "\\x14"; |
| case 21: |
| return "\\x15"; |
| case 22: |
| return "\\x16"; |
| case 23: |
| return "\\x17"; |
| case 24: |
| return "\\x18"; |
| case 25: |
| return "\\x19"; |
| case 26: |
| return "\\x1A"; |
| case 27: |
| return "\\x1B"; |
| case 28: |
| return "\\x1C"; |
| case 29: |
| return "\\x1D"; |
| case 30: |
| return "\\x1E"; |
| case 31: |
| return "\\x1F"; |
| case 32: |
| return " "; |
| case 33: |
| return "!"; |
| case 34: |
| return "\\\""; |
| case 35: |
| return "#"; |
| case 36: |
| return "$"; |
| case 37: |
| return "%"; |
| case 38: |
| return "&"; |
| case 39: |
| return "'"; |
| case 40: |
| return "("; |
| case 41: |
| return ")"; |
| case 42: |
| return "*"; |
| case 43: |
| return "+"; |
| case 44: |
| return ","; |
| case 45: |
| return "-"; |
| case 46: |
| return "."; |
| case 47: |
| return "/"; |
| case 48: |
| return "0"; |
| case 49: |
| return "1"; |
| case 50: |
| return "2"; |
| case 51: |
| return "3"; |
| case 52: |
| return "4"; |
| case 53: |
| return "5"; |
| case 54: |
| return "6"; |
| case 55: |
| return "7"; |
| case 56: |
| return "8"; |
| case 57: |
| return "9"; |
| case 58: |
| return ":"; |
| case 59: |
| return ";"; |
| case 60: |
| return "<"; |
| case 61: |
| return "="; |
| case 62: |
| return ">"; |
| case 63: |
| return "?"; |
| case 64: |
| return "@"; |
| case 65: |
| return "A"; |
| case 66: |
| return "B"; |
| case 67: |
| return "C"; |
| case 68: |
| return "D"; |
| case 69: |
| return "E"; |
| case 70: |
| return "F"; |
| case 71: |
| return "G"; |
| case 72: |
| return "H"; |
| case 73: |
| return "I"; |
| case 74: |
| return "J"; |
| case 75: |
| return "K"; |
| case 76: |
| return "L"; |
| case 77: |
| return "M"; |
| case 78: |
| return "N"; |
| case 79: |
| return "O"; |
| case 80: |
| return "P"; |
| case 81: |
| return "Q"; |
| case 82: |
| return "R"; |
| case 83: |
| return "S"; |
| case 84: |
| return "T"; |
| case 85: |
| return "U"; |
| case 86: |
| return "V"; |
| case 87: |
| return "W"; |
| case 88: |
| return "X"; |
| case 89: |
| return "Y"; |
| case 90: |
| return "Z"; |
| case 91: |
| return "["; |
| case 92: |
| return "\\\\"; |
| case 93: |
| return "]"; |
| case 94: |
| return "^"; |
| case 95: |
| return "_"; |
| case 96: |
| return "`"; |
| case 97: |
| return "a"; |
| case 98: |
| return "b"; |
| case 99: |
| return "c"; |
| case 100: |
| return "d"; |
| case 101: |
| return "e"; |
| case 102: |
| return "f"; |
| case 103: |
| return "g"; |
| case 104: |
| return "h"; |
| case 105: |
| return "i"; |
| case 106: |
| return "j"; |
| case 107: |
| return "k"; |
| case 108: |
| return "l"; |
| case 109: |
| return "m"; |
| case 110: |
| return "n"; |
| case 111: |
| return "o"; |
| case 112: |
| return "p"; |
| case 113: |
| return "q"; |
| case 114: |
| return "r"; |
| case 115: |
| return "s"; |
| case 116: |
| return "t"; |
| case 117: |
| return "u"; |
| case 118: |
| return "v"; |
| case 119: |
| return "w"; |
| case 120: |
| return "x"; |
| case 121: |
| return "y"; |
| case 122: |
| return "z"; |
| case 123: |
| return "{"; |
| case 124: |
| return "|"; |
| case 125: |
| return "}"; |
| case 126: |
| return "~"; |
| case 127: |
| return "\\x7F"; |
| case 128: |
| return "\\x80"; |
| case 129: |
| return "\\x81"; |
| case 130: |
| return "\\x82"; |
| case 131: |
| return "\\x83"; |
| case 132: |
| return "\\x84"; |
| case 133: |
| return "\\x85"; |
| case 134: |
| return "\\x86"; |
| case 135: |
| return "\\x87"; |
| case 136: |
| return "\\x88"; |
| case 137: |
| return "\\x89"; |
| case 138: |
| return "\\x8A"; |
| case 139: |
| return "\\x8B"; |
| case 140: |
| return "\\x8C"; |
| case 141: |
| return "\\x8D"; |
| case 142: |
| return "\\x8E"; |
| case 143: |
| return "\\x8F"; |
| case 144: |
| return "\\x90"; |
| case 145: |
| return "\\x91"; |
| case 146: |
| return "\\x92"; |
| case 147: |
| return "\\x93"; |
| case 148: |
| return "\\x94"; |
| case 149: |
| return "\\x95"; |
| case 150: |
| return "\\x96"; |
| case 151: |
| return "\\x97"; |
| case 152: |
| return "\\x98"; |
| case 153: |
| return "\\x99"; |
| case 154: |
| return "\\x9A"; |
| case 155: |
| return "\\x9B"; |
| case 156: |
| return "\\x9C"; |
| case 157: |
| return "\\x9D"; |
| case 158: |
| return "\\x9E"; |
| case 159: |
| return "\\x9F"; |
| case 160: |
| return "\\xA0"; |
| case 161: |
| return "\\xA1"; |
| case 162: |
| return "\\xA2"; |
| case 163: |
| return "\\xA3"; |
| case 164: |
| return "\\xA4"; |
| case 165: |
| return "\\xA5"; |
| case 166: |
| return "\\xA6"; |
| case 167: |
| return "\\xA7"; |
| case 168: |
| return "\\xA8"; |
| case 169: |
| return "\\xA9"; |
| case 170: |
| return "\\xAA"; |
| case 171: |
| return "\\xAB"; |
| case 172: |
| return "\\xAC"; |
| case 173: |
| return "\\xAD"; |
| case 174: |
| return "\\xAE"; |
| case 175: |
| return "\\xAF"; |
| case 176: |
| return "\\xB0"; |
| case 177: |
| return "\\xB1"; |
| case 178: |
| return "\\xB2"; |
| case 179: |
| return "\\xB3"; |
| case 180: |
| return "\\xB4"; |
| case 181: |
| return "\\xB5"; |
| case 182: |
| return "\\xB6"; |
| case 183: |
| return "\\xB7"; |
| case 184: |
| return "\\xB8"; |
| case 185: |
| return "\\xB9"; |
| case 186: |
| return "\\xBA"; |
| case 187: |
| return "\\xBB"; |
| case 188: |
| return "\\xBC"; |
| case 189: |
| return "\\xBD"; |
| case 190: |
| return "\\xBE"; |
| case 191: |
| return "\\xBF"; |
| case 192: |
| return "\\xC0"; |
| case 193: |
| return "\\xC1"; |
| case 194: |
| return "\\xC2"; |
| case 195: |
| return "\\xC3"; |
| case 196: |
| return "\\xC4"; |
| case 197: |
| return "\\xC5"; |
| case 198: |
| return "\\xC6"; |
| case 199: |
| return "\\xC7"; |
| case 200: |
| return "\\xC8"; |
| case 201: |
| return "\\xC9"; |
| case 202: |
| return "\\xCA"; |
| case 203: |
| return "\\xCB"; |
| case 204: |
| return "\\xCC"; |
| case 205: |
| return "\\xCD"; |
| case 206: |
| return "\\xCE"; |
| case 207: |
| return "\\xCF"; |
| case 208: |
| return "\\xD0"; |
| case 209: |
| return "\\xD1"; |
| case 210: |
| return "\\xD2"; |
| case 211: |
| return "\\xD3"; |
| case 212: |
| return "\\xD4"; |
| case 213: |
| return "\\xD5"; |
| case 214: |
| return "\\xD6"; |
| case 215: |
| return "\\xD7"; |
| case 216: |
| return "\\xD8"; |
| case 217: |
| return "\\xD9"; |
| case 218: |
| return "\\xDA"; |
| case 219: |
| return "\\xDB"; |
| case 220: |
| return "\\xDC"; |
| case 221: |
| return "\\xDD"; |
| case 222: |
| return "\\xDE"; |
| case 223: |
| return "\\xDF"; |
| case 224: |
| return "\\xE0"; |
| case 225: |
| return "\\xE1"; |
| case 226: |
| return "\\xE2"; |
| case 227: |
| return "\\xE3"; |
| case 228: |
| return "\\xE4"; |
| case 229: |
| return "\\xE5"; |
| case 230: |
| return "\\xE6"; |
| case 231: |
| return "\\xE7"; |
| case 232: |
| return "\\xE8"; |
| case 233: |
| return "\\xE9"; |
| case 234: |
| return "\\xEA"; |
| case 235: |
| return "\\xEB"; |
| case 236: |
| return "\\xEC"; |
| case 237: |
| return "\\xED"; |
| case 238: |
| return "\\xEE"; |
| case 239: |
| return "\\xEF"; |
| case 240: |
| return "\\xF0"; |
| case 241: |
| return "\\xF1"; |
| case 242: |
| return "\\xF2"; |
| case 243: |
| return "\\xF3"; |
| case 244: |
| return "\\xF4"; |
| case 245: |
| return "\\xF5"; |
| case 246: |
| return "\\xF6"; |
| case 247: |
| return "\\xF7"; |
| case 248: |
| return "\\xF8"; |
| case 249: |
| return "\\xF9"; |
| case 250: |
| return "\\xFA"; |
| case 251: |
| return "\\xFB"; |
| case 252: |
| return "\\xFC"; |
| case 253: |
| return "\\xFD"; |
| case 254: |
| return "\\xFE"; |
| case 255: |
| return "\\xFF"; |
| default: |
| assert(0); /* never gets here */ |
| return "dead code"; |
| } |
| assert(0); /* never gets here */ |
| } |
| |
| #endif /* XML_DTD */ |
| |
| static unsigned long |
| getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) { |
| const char *const valueOrNull = getenv(variableName); |
| if (valueOrNull == NULL) { |
| return defaultDebugLevel; |
| } |
| const char *const value = valueOrNull; |
| |
| errno = 0; |
| char *afterValue = (char *)value; |
| unsigned long debugLevel = strtoul(value, &afterValue, 10); |
| if ((errno != 0) || (afterValue[0] != '\0')) { |
| errno = 0; |
| return defaultDebugLevel; |
| } |
| |
| return debugLevel; |
| } |