#if ! defined(XML_GE) || (1 - XML_GE - 1 == 2) || (XML_GE < 0) || (XML_GE > 1) # error XML_GE (for general entities) must be defined, non-empty, either 1or0 (0 to disable, 1 to enable; 1 is a common default) #endif
#ifdefined(XML_DTD) && XML_GE == 0 # error Either undefine XML_DTD or define XML_GE to 1. #endif
#if ! defined(XML_CONTEXT_BYTES) || (1 - XML_CONTEXT_BYTES - 1 == 2) \
|| (XML_CONTEXT_BYTES + 0 < 0) # error XML_CONTEXT_BYTES must be defined, non-empty and >=0 (0 to disable, >=1 to enable; 1024 is a common default) #endif
/* BEGIN MOZILLA CHANGE (we already set a salt through XML_SetHashSalt16Bytes) */ #if0 #ifdefined(HAVE_ARC4RANDOM) # include "random_arc4random.h" #endif/* defined(HAVE_ARC4RANDOM) */
#ifdefined(HAVE_ARC4RANDOM_BUF) # include "random_arc4random_buf.h" #endif// defined(HAVE_ARC4RANDOM_BUF)
#ifdefined(XML_DEV_URANDOM) # include "random_dev_urandom.h" #endif/* defined(XML_DEV_URANDOM) */
#ifdefined(HAVE_GETENTROPY) # include "random_getentropy.h" #endif// defined(HAVE_GETENTROPY)
#ifdefined(_WIN32) # include "random_rand_s.h" #endif/* defined(_WIN32) */ #endif /* END MOZILLA CHANGE */
#if ! defined(HAVE_GETRANDOM) && ! defined(HAVE_SYSCALL_GETRANDOM) \
&& ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) \
&& ! defined(HAVE_GETENTROPY) && ! defined(XML_DEV_URANDOM) \
&& ! defined(_WIN32) && ! defined(XML_POOR_ENTROPY) # error You donot 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 / glibc >=2.36 (arc4random_buf): HAVE_ARC4RANDOM_BUF, \
* BSD / macOS (including <10.7) / glibc >=2.36 (arc4random): HAVE_ARC4RANDOM, \
* BSD / macOS >=10.12 / glibc >=2.25 (getentropy): HAVE_GETENTROPY, \
* Linux (including <3.17) / BSD / macOS (including <10.7) / Solaris >=8 (/dev/urandom): XML_DEV_URANDOM, \
* Windows >=Vista (rand_s): _WIN32. \
\ If you 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
/* For probing (after a collision) we need a step size relative prime tothehashtablesize,whichisapowerof2.Weusedouble-hashing, sincewecancalculateasecondhashvaluecheaplybytakingthosebits ofthefirsthashvaluethatwerediscarded(maskedout)whenthetable indexwascalculated:index=hash&mask,wheremask=table->size-1. Welimitthemaximumstepsizetotable->size/4(mask>>2)andmake itodd,sinceoddnumbersarealwaysrelativeprimetoapowerof2.
*/ #define SECOND_HASH(hash, mask, power) \
((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) #define PROBE_STEP(hash, mask, power) \
((unsignedchar)((SECOND_HASH(hash, mask, power)) | 1))
typedefstruct {
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 /* BEGIN MOZILLA CHANGE (Avoid slop in poolGrow() allocations) */ #define INIT_BLOCK_SIZE ((int)(1024 - (offsetof(BLOCK, s) / sizeof(XML_Char)))) /* END MOZILLA CHANGE */ #define INIT_BUFFER_SIZE 1024
typedefstruct { 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. ThenameoftheelementisstoredinboththedocumentandAPI encodings.Thememorybuffer'buf'isaseparately-allocated memoryareawhichstoresthename.DuringtheXML_Parse()/ XML_ParseBuffer()whentheelementisopen,thememoryforthe'raw' versionofthename(inthedocumentencoding)issharedwiththe documentbuffer.Iftheelementisopenacrosscallsto XML_Parse()/XML_ParseBuffer(),thebufferisre-allocatedto containthe'raw'nameaswell.
Aparserreusesthesestructures,maintainingalistofallocated TAGobjectsinafreelist.
*/ typedefstruct tag { struct tag *parent; /* parent of this element */ constchar *rawName; /* tagName in the original encoding */ int rawNameLength;
TAG_NAME name; /* tagName in the API encoding */ union { char *raw; /* for byte-level access (rawName storage) */
XML_Char *str; /* for character-level access (converted name) */
} buf; /* buffer for name components */ char *bufEnd; /* end of the buffer */
BINDING *bindings;
} TAG;
typedefstruct { 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 hasMore; /* true if entity has not been completely processed */ /* An entity can be open while being already completely processed (hasMore == XML_FALSE).Thereasonisthedelayedclosingofentitiesuntiltheirinner
entities are processed and closed */
XML_Bool is_param;
XML_Bool is_internal; /* true if declared in internal subset outside PE */
} ENTITY;
typedefstruct { enum XML_Content_Type type; enum XML_Content_Quant quant; const XML_Char *name; int firstchild; int lastchild; int childcnt; int nextsib;
} CONTENT_SCAFFOLD;
/* The XML_Char before the name is used to determine whether
an attribute has been specified. */ typedefstruct attribute_id {
XML_Char *name;
PREFIX *prefix;
XML_Bool maybeTokenized;
XML_Bool xmlns;
} ATTRIBUTE_ID;
typedefstruct { const XML_Char *name;
PREFIX *prefix; const ATTRIBUTE_ID *idAtt; int nDefaultAtts; int allocDefaultAtts;
DEFAULT_ATTRIBUTE *defaultAtts;
HASH_TABLE defaultAttsNames;
} ELEMENT_TYPE;
typedefstruct {
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;
typedefstruct open_internal_entity { constchar *internalEventPtr; constchar *internalEventEndPtr; struct open_internal_entity *next;
ENTITY *entity; int startTagLevel;
XML_Bool betweenDecl; /* WFC: PE Between Declarations */ enum EntityType type;
} 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 */
};
#if XML_GE == 1 typedefunsignedlonglong XmlBigCount; typedefstruct accounting {
XmlBigCount countBytesDirect;
XmlBigCount countBytesIndirect; /* BEGIN MOZILLA CHANGE (don't report debug information) */ #if0 unsignedlong debugLevel; #endif /* END MOZILLA CHANGE */ float maximumAmplificationFactor; // >=1.0 unsignedlonglong activationThresholdBytes;
} ACCOUNTING;
typedefstruct MALLOC_TRACKER {
XmlBigCount bytesAllocated; /* BEGIN MOZILLA CHANGE (don't report debug information) */ # if0
XmlBigCount peakBytesAllocated; // updated live only for debug level >=2 unsignedlong debugLevel; #endif /* END MOZILLA CHANGE */ float maximumAmplificationFactor; // >=1.0
XmlBigCount activationThresholdBytes;
} MALLOC_TRACKER;
typedefstruct entity_stats { /* BEGIN MOZILLA CHANGE (don't report debug information) */ #if0 unsignedint countEverOpened; unsignedint currentDepth; unsignedint maximumDepthSeen; unsignedlong debugLevel; #endif /* END MOZILLA CHANGE */
} ENTITY_STATS; #endif/* XML_GE == 1 */
#if ! defined(XML_TESTING) const #endif
XML_Bool g_reparseDeferralEnabledDefault
= XML_TRUE; // write ONLY in runtests.c #ifdefined(XML_TESTING) unsignedint g_bytesScanned = 0; // used for testing only #endif
struct XML_ParserStruct { /* The first member must be m_userData so that the XML_GetUserData
macro works. */ void *m_userData; void *m_handlerArg;
// How the four parse buffer pointers below relate in time and space: // // m_buffer <= m_bufferPtr <= m_bufferEnd <= m_bufferLim // | | | | // <--parsed-->| | | // <---parsing--->| | // <--unoccupied-->| // <---------total-malloced/realloced-------->|
char *m_buffer; // malloc/realloc base pointer of parse buffer const XML_Memory_Handling_Suite m_mem; constchar *m_bufferPtr; // first character to be parsed char *m_bufferEnd; // past last character to be parsed constchar *m_bufferLim; // allocated end of m_buffer
if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) {
assert(newTotal > 0); // NOTE: This can be +infinity when dividing by zero but not -nan constfloat amplification
= (float)newTotal / (float)rootParser->m_accounting.countBytesDirect; if (amplification
> rootParser->m_alloc_tracker.maximumAmplificationFactor) {
tolerable = false;
}
}
}
/* BEGIN MOZILLA CHANGE (don't report debug information) */ #if0 if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) {
expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine);
} #endif /* END MOZILLA CHANGE */
// Extract original size (to the eyes of the caller) and the original // pointer returned by malloc/realloc void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t); const size_t prevSize = *(size_t *)mallocedPtr;
// Ask for permission from accounting if (isIncrease) { if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) { return NULL; // i.e. signal violation as out-of-memory
}
}
// NOTE: Integer overflow detection has already been done for us // by expat_heap_increase_tolerable(..) above
assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
/* BEGIN MOZILLA CHANGE (we already set a salt through XML_SetHashSalt16Bytes) */ #if0 #if ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM)
/* Microseconds time is <20 bits entropy */ return tv.tv_usec; # endif
}
#endif/* ! defined(HAVE_ARC4RANDOM_BUF) && ! defined(HAVE_ARC4RANDOM) */ #endif /* END MOZILLA CHANGE */
staticstruct sipkey
ENTROPY_DEBUG(constchar *label, struct sipkey entropy_128) { /* BEGIN MOZILLA CHANGE (we already set a salt through XML_SetHashSalt16Bytes) */
(void)label; #if0 if (getDebugLevel("EXPAT_ENTROPY_DEBUG", 0) >= 1u) {
fprintf(stderr, "expat: Entropy: %s --> [0x" EXPAT_FMT_LLX( "016") ", 0x" EXPAT_FMT_LLX("016") "] (16 bytes)\n",
label, (unsignedlonglong)entropy_128.k[0],
(unsignedlonglong)entropy_128.k[1]);
} #endif /* END MOZILLA CHANGE */ return entropy_128;
}
staticstruct sipkey
generate_hash_secret_salt(void) { /* BEGIN MOZILLA CHANGE (we already set a salt through XML_SetHashSalt16Bytes) */
abort(); #if0 struct sipkey entropy;
/* "Failproof" high quality providers: */ #ifdefined(HAVE_ARC4RANDOM_BUF)
writeRandomBytes_arc4random_buf(&entropy, sizeof(entropy)); return ENTROPY_DEBUG("arc4random_buf", entropy); #elifdefined(HAVE_ARC4RANDOM)
writeRandomBytes_arc4random(&entropy, sizeof(entropy)); return ENTROPY_DEBUG("arc4random", entropy); #else /* Try high quality providers first .. */ # ifdef _WIN32 if (writeRandomBytes_rand_s(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("rand_s", entropy);
} # elif defined(HAVE_GETENTROPY) if (writeRandomBytes_getentropy(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("getentropy", entropy);
}
errno = 0; # elif defined(HAVE_GETRANDOM) || defined(HAVE_SYSCALL_GETRANDOM) if (writeRandomBytes_getrandom_nonblock(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("getrandom", entropy);
} # endif # if ! defined(_WIN32) && defined(XML_DEV_URANDOM) if (writeRandomBytes_dev_urandom(&entropy, sizeof(entropy))) { return ENTROPY_DEBUG("/dev/urandom", entropy);
} # endif /* ! defined(_WIN32) && defined(XML_DEV_URANDOM) */ /* .. and self-made low quality for backup: */
entropy.k[0] = 0;
entropy.k[1] = gather_time_entropy(); # if ! defined(__wasi__) /* Process ID is 0 bits entropy if attacker has local access */
entropy.k[1] ^= getpid(); # endif
/* Factors are 2^31-1 and 2^61-1 (Mersenne primes M31 and M61) */ if (sizeof(unsignedlong) == 4) {
entropy.k[1] *= 2147483647; return ENTROPY_DEBUG("fallback(4)", entropy);
} else {
entropy.k[1] *= 2305843009213693951ULL; return ENTROPY_DEBUG("fallback(8)", entropy);
} #endif #endif /* END MOZILLA CHANGE */
}
if (parser->m_reparseDeferralEnabled
&& ! parser->m_parsingStatus.finalBuffer) { // Heuristic: don't try to parse a partial token again until the amount of // available data has increased significantly. const size_t had_before = parser->m_partialTokenBytesBefore; // ...but *do* try anyway if we're close to causing a reallocation.
size_t available_buffer
= EXPAT_SAFE_PTR_DIFF(parser->m_bufferPtr, parser->m_buffer); #if XML_CONTEXT_BYTES > 0
available_buffer -= EXPAT_MIN(available_buffer, XML_CONTEXT_BYTES); #endif
available_buffer
+= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd); // m_lastBufferRequestSize is never assigned a value < 0, so the cast is ok constbool enough
= (have_now >= 2 * had_before)
|| ((size_t)parser->m_lastBufferRequestSize > available_buffer);
if (! enough) {
*endPtr = start; // callers may expect this to be set return XML_ERROR_NONE;
}
} #ifdefined(XML_TESTING)
g_bytesScanned += (unsigned)have_now; #endif // Run in a loop to eliminate dangerous recursion depths enum XML_Error ret;
*endPtr = start; while (1) { // Use endPtr as the new start in each iteration, since it will // be set to the next start point by m_processor.
ret = parser->m_processor(parser, *endPtr, end, endPtr);
// Make parsing status (and in particular XML_SUSPENDED) take // precedence over re-enter flag when they disagree if (parser->m_parsingStatus.parsing != XML_PARSING) {
parser->m_reenter = XML_FALSE;
}
if (! parser->m_reenter) { break;
}
parser->m_reenter = XML_FALSE; if (ret != XML_ERROR_NONE) return ret;
}
if (ret == XML_ERROR_NONE) { // if we consumed nothing, remember what we had on this parse attempt. if (*endPtr == start) {
parser->m_partialTokenBytesBefore = have_now;
} else {
parser->m_partialTokenBytesBefore = 0;
}
} return ret;
}
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_set != XML_TRUE) {
parser->m_hash_secret_salt_128 = generate_hash_secret_salt();
parser->m_hash_secret_salt_set = XML_TRUE;
} if (parser->m_ns) { /* implicit context only set for root parser, since child parsers(i.e.externalentityparsers)willinheritit
*/ return setContext(parser, implicitContext);
} return XML_TRUE;
}
#if XML_GE == 1 // Initialize .m_alloc_tracker
memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER)); if (parentParser == NULL) { /* BEGIN MOZILLA CHANGE (don't report debug information) */ #if0
parser->m_alloc_tracker.debugLevel
= getDebugLevel("EXPAT_MALLOC_DEBUG", 0u); #endif /* END MOZILLA CHANGE */
parser->m_alloc_tracker.maximumAmplificationFactor
= EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT;
parser->m_alloc_tracker.activationThresholdBytes
= EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT;
// NOTE: This initialization needs to come this early because these fields // are read by allocation tracking code
parser->m_parentParser = NULL;
parser->m_accounting.countBytesDirect = 0;
} else {
parser->m_parentParser = parentParser;
}
// Record XML_ParserStruct allocation we did a few lines up before const XML_Parser rootParser = getRootParserOf(parser, NULL);
assert(rootParser->m_parentParser == NULL);
assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase);
rootParser->m_alloc_tracker.bytesAllocated += increase;
/* BEGIN MOZILLA CHANGE (don't report debug information) */ #if0 // Report on allocation if (rootParser->m_alloc_tracker.debugLevel >= 2) { if (rootParser->m_alloc_tracker.bytesAllocated
> rootParser->m_alloc_tracker.peakBytesAllocated) {
rootParser->m_alloc_tracker.peakBytesAllocated
= rootParser->m_alloc_tracker.bytesAllocated;
}
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 (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;
} /* move m_openAttributeEntities to m_freeAttributeEntities (i.e. same task but
* for attributes) */
openEntityList = parser->m_openAttributeEntities; while (openEntityList) {
OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
openEntityList = openEntity->next;
openEntity->next = parser->m_freeAttributeEntities;
parser->m_freeAttributeEntities = openEntity;
} /* move m_openValueEntities to m_freeValueEntities (i.e. same task but
* for value entities) */
openEntityList = parser->m_openValueEntities; while (openEntityList) {
OPEN_INTERNAL_ENTITY *openEntity = openEntityList;
openEntityList = openEntity->next;
openEntity->next = parser->m_freeValueEntities;
parser->m_freeValueEntities = 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); return XML_TRUE;
} #endif /* END MOZILLA CHANGE */
static XML_Bool
parserBusy(XML_Parser parser) { switch (parser->m_parsingStatus.parsing) { case XML_PARSING: case XML_SUSPENDED: return XML_TRUE; case XML_INITIALIZED: case XML_FINISHED: default: return XML_FALSE;
}
}
/* BEGIN MOZILLA CHANGE (unused API) */ #if0 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. XXXThere'snowayforthecallertodeterminewhichofthe XXXpossibleerrorcasescausedtheXML_STATUS_ERRORreturn.
*/ if (parserBusy(parser)) 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); if (! parser->m_protocolEncodingName) return XML_STATUS_ERROR;
} return XML_STATUS_OK;
} #endif /* END MOZILLA CHANGE */
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,sothatdtdCopyandcopyEntityTablecanlookupvalues fromhashtablesassociatedwitheitherparserwithoutushaving toworrywhichhashsecretseachtablehas.
*/ struct sipkey oldhash_secret_salt_128;
XML_Bool oldhash_secret_salt_set;
XML_Bool oldReparseDeferralEnabled;
/* Validate the oldParser parameter before we pull everything out of it */ if (oldParser == NULL) return NULL;
/* BEGIN MOZILLA CHANGE (unused API) */ #if0 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 (parserBusy(parser)) 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
} #endif /* END MOZILLA CHANGE */
void XMLCALL
XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) { if (parser == NULL) return; /* block after XML_Parse()/XML_ParseBuffer() has been called */ if (parserBusy(parser)) return;
parser->m_ns_triplets = do_nst ? XML_TRUE : XML_FALSE;
}
enum XML_Status XMLCALL
XML_Parse(XML_Parser parser, constchar *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 XML_CONTEXT_BYTES == 0 if (parser->m_bufferPtr == parser->m_bufferEnd) { constchar *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;
} // though this isn't a buffer request, we assume that `len` is the app's // preferred buffer fill size, and therefore save it here.
parser->m_lastBufferRequestSize = len;
parser->m_parseEndByteIndex += len;
parser->m_positionPtr = s;
parser->m_parsingStatus.finalBuffer = (XML_Bool)isFinal;
parser->m_errorCode
= callProcessor(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) { // Back up and restore the parsing status to avoid XML_ERROR_SUSPENDED // (and XML_ERROR_FINISHED) from XML_GetBuffer. constenum XML_Parsing originalStatus = parser->m_parsingStatus.parsing;
parser->m_parsingStatus.parsing = XML_PARSING; void *const temp = XML_GetBuffer(parser, nLeftOver);
parser->m_parsingStatus.parsing = originalStatus; // GetBuffer may have overwritten this, but we want to remember what the // app requested, not how many bytes were left over after parsing.
parser->m_lastBufferRequestSize = len; if (temp == NULL) { // NOTE: parser->m_errorCode has already been set by XML_GetBuffer().
parser->m_eventPtr = parser->m_eventEndPtr = NULL;
parser->m_processor = errorProcessor; return XML_STATUS_ERROR;
} // Since we know that the buffer was empty and XML_CONTEXT_BYTES is 0, we // don't have any data to preserve, and can copy straight into the start // of the buffer rather than the GetBuffer return pointer (which may be // pointing further into the allocated buffer).
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/* XML_CONTEXT_BYTES == 0 */ void *buff = XML_GetBuffer(parser, len); if (buff == NULL) return XML_STATUS_ERROR; if (len > 0) {
assert(s != NULL); // make sure s==NULL && len!=0 was rejected above
memcpy(buff, s, len);
} return XML_ParseBuffer(parser, len, isFinal);
}
enum XML_Status XMLCALL
XML_ParseBuffer(XML_Parser parser, int len, int isFinal) { constchar *start; enum XML_Status result = XML_STATUS_OK;
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; /* BEGIN MOZILLA CHANGE (always set m_eventPtr/m_eventEndPtr) */
parser->m_eventPtr = parser->m_bufferPtr;
parser->m_eventEndPtr = parser->m_bufferPtr; /* END MOZILLA CHANGE */ 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:;
}
// whether or not the request succeeds, `len` seems to be the app's preferred // buffer fill size; remember it.
parser->m_lastBufferRequestSize = len; if (len > EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_bufferEnd)
|| parser->m_buffer == NULL) { #if XML_CONTEXT_BYTES > 0 int keep; #endif/* XML_CONTEXT_BYTES > 0 */ /* 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;
} #if XML_CONTEXT_BYTES > 0
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/* XML_CONTEXT_BYTES > 0 */ if (parser->m_buffer && parser->m_bufferPtr
&& neededSize
<= EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer)) { #if XML_CONTEXT_BYTES > 0 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
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/* XML_CONTEXT_BYTES > 0 */
} else { char *newBuf; int bufferSize
= (int)EXPAT_SAFE_PTR_DIFF(parser->m_bufferLim, parser->m_buffer); 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;
} // NOTE: We are avoiding MALLOC(..) here to leave limiting // the input size to the application using Expat.
newBuf = parser->m_mem.malloc_fcn(bufferSize); if (newBuf == NULL) {
parser->m_errorCode = XML_ERROR_NO_MEMORY; return NULL;
}
parser->m_bufferLim = newBuf + bufferSize; #if XML_CONTEXT_BYTES > 0 if (parser->m_bufferPtr) {
memcpy(newBuf, &parser->m_bufferPtr[-keep],
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+ keep); // NOTE: We are avoiding FREE(..) here because parser->m_buffer // is not being allocated with MALLOC(..) but with plain // .malloc_fcn(..).
parser->m_mem.free_fcn(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)); // NOTE: We are avoiding FREE(..) here because parser->m_buffer // is not being allocated with MALLOC(..) but with plain // .malloc_fcn(..).
parser->m_mem.free_fcn(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/* XML_CONTEXT_BYTES > 0 */
}
parser->m_eventPtr = parser->m_eventEndPtr = NULL;
parser->m_positionPtr = NULL;
} return parser->m_bufferEnd;
}
/* BEGIN MOZILLA CHANGE (unused API) */ #if0 void XMLCALL
XML_FreeContentModel(XML_Parser parser, XML_Content *model) { if (parser == NULL) return;
// NOTE: We are avoiding FREE(..) here because the content model // has been created using plain .malloc_fcn(..) rather than MALLOC(..).
parser->m_mem.free_fcn(model);
}
// NOTE: We are avoiding MALLOC(..) here to not include // user allocations with allocation tracking and limiting. return parser->m_mem.malloc_fcn(size);
}
// NOTE: We are avoiding REALLOC(..) here to not include // user allocations with allocation tracking and limiting. return parser->m_mem.realloc_fcn(ptr, size);
}
// NOTE: We are avoiding FREE(..) here because XML_MemMalloc and // XML_MemRealloc are not using MALLOC(..) and REALLOC(..) // but plain .malloc_fcn(..) and .realloc_fcn(..), internally.
parser->m_mem.free_fcn(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"); /* Added in 2.6.4. */ case XML_ERROR_NOT_STARTED: return XML_L("parser not started");
} return NULL;
}
const XML_LChar *XMLCALL
XML_ExpatVersion(void) { /* V1 is used to string-ize the version number. However, it would string-izetheactualversionmacro*names*unlesswegetthem substitutedbeforebeingpassedtoV1.CPPisdefinedtoexpand amacro,thenrescanformoreexpansions.Thus,weuseV2toexpand theversionmacros,thenCPPwillexpandtheresultingV1()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)
staticenum XML_Error PTRCALL
externalEntityInitProcessor2(XML_Parser parser, constchar *start, constchar *end, constchar **endPtr) { constchar *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: #if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
XML_ACCOUNT_DIRECT)) {
accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
} #endif/* XML_GE == 1 */
/* If we are at the end of the buffer, this would cause the next stage, i.e.externalEntityInitProcessor3,topasscontroldirectlyto doContent(bydetectingXML_TOK_NONE)withoutprocessinganyxmltext declaration-causingtheerrorXML_ERROR_MISPLACED_XML_PIindoContent.
*/ 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);
}
staticenum XML_Error PTRCALL
externalEntityInitProcessor3(XML_Parser parser, constchar *start, constchar *end, constchar **endPtr) { int tok; constchar *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; case XML_PARSING: if (parser->m_reenter) { return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
} /* Fall through */ 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);
}
staticenum XML_Error PTRCALL
externalEntityContentProcessor(XML_Parser parser, constchar *start, constchar *end, constchar **endPtr) { enum XML_Error result
= doContent(parser, 1, parser->m_encoding, start, end, endPtr,
(XML_Bool)! parser->m_parsingStatus.finalBuffer,
XML_ACCOUNT_ENTITY_EXPANSION); /* BEGIN MOZILLA CHANGE (Bug 1746996 - Ensure that storeRawNames is always called) */ #if0 if (result == XML_ERROR_NONE) { if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY;
} #endif /* END MOZILLA CHANGE */ return result;
}
staticenum XML_Error
doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, constchar *s, constchar *end, constchar **nextPtr,
XML_Bool haveMore, enum XML_Account account) { /* BEGIN MOZILLA CHANGE (Bug 1746996 - Ensure that storeRawNames is always called) */ enum XML_Error result = doContentInternal(parser, startTagLevel, enc, s, end,
nextPtr, haveMore, account); if (result == XML_ERROR_NONE) { if (! storeRawNames(parser)) return XML_ERROR_NO_MEMORY;
} return result;
}
staticenum XML_Error
doContentInternal(XML_Parser parser, int startTagLevel, const ENCODING *enc, constchar *s, constchar *end, constchar **nextPtr,
XML_Bool haveMore, enum XML_Account account) { /* END MOZILLA CHANGE */ /* save one level of indirection */
DTD *const dtd = parser->m_dtd;
for (;;) { constchar *next = s; /* XmlContentTok doesn't always set the last arg */ int tok = XmlContentTok(enc, s, end, &next); #if XML_GE == 1 constchar *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);
} elseif (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) { #if XML_GE == 1 /* NOTE: We are replacing 4-6 characters original input for 1 character *sothereisnoamplificationandhencerecordingwithout
* protection. */
accountingDiffTolerated(parser, tok, (char *)&ch,
((char *)&ch) + sizeof(XML_Char), __LINE__,
XML_ACCOUNT_ENTITY_EXPANSION); #endif/* XML_GE == 1 */ if (parser->m_characterDataHandler)
parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1); elseif (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; ifyes,checkthattheentityexists,andthatitisinternal, otherwisecalltheskippedentityordefaulthandler.
*/ if (! dtd->hasParamEntityRefs || dtd->standalone) { if (! entity) return XML_ERROR_UNDEFINED_ENTITY; elseif (! entity->is_internal) return XML_ERROR_ENTITY_DECLARED_IN_PE;
} elseif (! entity) { if (parser->m_skippedEntityHandler)
parser->m_skippedEntityHandler(parser->m_handlerArg, name, 0); /* BEGIN MOZILLA CHANGE (Bug 35984 - Undeclared entities are ignored when external DTD not found) */ #if0 elseif (parser->m_defaultHandler)
reportDefault(parser, enc, s, next); break; #else return XML_ERROR_UNDEFINED_ENTITY; #endif /* END MOZILLA CHANGE */
} 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); elseif (parser->m_defaultHandler)
reportDefault(parser, enc, s, next); break;
}
result = processEntity(parser, entity, XML_FALSE, ENTITY_INTERNAL); if (result != XML_ERROR_NONE) return result;
} elseif (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);
} elseif (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 = MALLOC(parser, sizeof(TAG)); if (! tag) return XML_ERROR_NO_MEMORY;
tag->buf.raw = MALLOC(parser, INIT_TAG_BUF_SIZE); if (! tag->buf.raw) {
FREE(parser, tag); return XML_ERROR_NO_MEMORY;
}
tag->bufEnd = tag->buf.raw + 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;
{ constchar *rawNameEnd = tag->rawName + tag->rawNameLength; constchar *fromPtr = tag->rawName;
toPtr = tag->buf.str; for (;;) { int convLen; constenum XML_Convert_Result convert_res
= XmlConvert(enc, &fromPtr, rawNameEnd, (ICHAR **)&toPtr,
(ICHAR *)tag->bufEnd - 1);
convLen = (int)(toPtr - tag->buf.str); if ((fromPtr >= rawNameEnd)
|| (convert_res == XML_CONVERT_INPUT_INCOMPLETE)) {
tag->name.strLen = convLen; break;
} if (SIZE_MAX / 2 < (size_t)(tag->bufEnd - tag->buf.raw)) return XML_ERROR_NO_MEMORY; const size_t bufSize = (size_t)(tag->bufEnd - tag->buf.raw) * 2;
{ char *temp = REALLOC(parser, tag->buf.raw, bufSize); if (temp == NULL) return XML_ERROR_NO_MEMORY;
tag->buf.raw = temp;
tag->bufEnd = temp + bufSize;
toPtr = (XML_Char *)temp + convLen;
}
}
}
tag->name.str = tag->buf.str;
*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); elseif (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: { constchar *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_parsingStatus.parsing == XML_PARSING
&& parser->m_reenter))
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; constchar *rawName;
TAG *tag = parser->m_tagStack;
rawName = s + enc->minBytesPerChar * 2;
len = XmlNameLength(enc, rawName); if (len != tag->rawNameLength
|| memcmp(tag->rawName, rawName, len) != 0) { /* BEGIN MOZILLA CHANGE (Report opening tag of mismatched closing tag) */ /* This code is copied from the |if (parser->m_endElementHandler)| blockbelow
*/ 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,sincethispointstothebinding->uri bufferwhichgetsreused;sowehavetoaddthemagain
*/
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 = tag->name.prefix; if (parser->m_ns_triplets && prefix) {
*uri++ = parser->m_namespaceSeparator; while (*prefix)
*uri++ = *prefix++;
}
*uri = XML_T('\0');
}
parser->m_mismatch = tag->name.str; /* END MOZILLA CHANGE */
*eventPP = rawName; return XML_ERROR_TAG_MISMATCH;
}
parser->m_tagStack = tag->parent;
tag->parent = parser->m_freeTagList;
parser->m_freeTagList = tag;
--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,sincethispointstothebinding->uri bufferwhichgetsreused;sowehavetoaddthemagain
*/
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 = 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);
} elseif (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_parsingStatus.parsing == XML_PARSING
&& parser->m_reenter))
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));
} elseif (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);
} elseif (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 changingonlythecharacterdata.YousetupadefaultHandler andacharacterDataHandler.ThedefaultHandlersimplycopies charactersthrough.ThecharacterDataHandlerdoesthe transformationandwritesthecharactersoutescapingthemas necessary.Thiscasewillfailtoworkifweleaveoutthe followingtwolines(because&and<insideCDATAsectionswill beincorrectlyescaped).
However,nowwehaveastart/endCdataSectionHandler,soitseems easiertolettheuserdealwiththis.
*/ elseif ((0) && parser->m_characterDataHandler)
parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0); /* END disabled code */ elseif (parser->m_defaultHandler)
reportDefault(parser, enc, s, next);
result
= doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account); if (result != XML_ERROR_NONE) return result; elseif (! 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, (const XML_Char *)s,
(int)((const XML_Char *)end - (const XML_Char *)s));
} elseif (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; constenum 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, (const XML_Char *)s,
(int)((const XML_Char *)next - (const XML_Char *)s));
} elseif (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 *explicitcases,sothisdefaultisnotstrictlynecessary. *Howeveritisausefulsafetynet,soweretainthecodeand *simplyexcludeitfromthecoveragetests. * *LCOV_EXCL_START
*/ if (parser->m_defaultHandler)
reportDefault(parser, enc, s, next); break; /* LCOV_EXCL_STOP */
} switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED:
*eventPP = next;
*nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED:
*eventPP = next; return XML_ERROR_ABORTED; case XML_PARSING: if (parser->m_reenter) {
*nextPtr = next; return XML_ERROR_NONE;
} /* Fall through */ default:;
*eventPP = s = next;
}
} /* not reached */
}
/* This function does not call free() on the allocated memory, merely *movingittotheparser'sm_freeBindingListwhereitcanbefreedor *reusedasappropriate.
*/ staticvoid
freeBindings(XML_Parser parser, BINDING *bindings) { while (bindings) {
BINDING *b = bindings;
/* m_startNamespaceDeclHandler will have been called for this *bindinginaddBindings(),socalltheendhandlernow.
*/ if (parser->m_endNamespaceDeclHandler)
parser->m_endNamespaceDeclHandler(parser->m_handlerArg, b->prefix->name);
/* Precondition: all arguments must be non-NULL; Purpose: -normalizeattributes -checkattributesforwell-formedness -generatenamespaceawareattributenames(URI,prefix) -buildlistofattributesforstartElementHandler -defaultattributes -processnamespacedeclarations(checkandreportthem) -generatenamespaceawareelementname(URI,prefix)
*/ staticenum XML_Error
storeAtts(XML_Parser parser, const ENCODING *enc, constchar *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; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */ int nXMLNSDeclarations = 0; /* END MOZILLA CHANGE */
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 (! elementType->defaultAttsNames.parser)
hashTableInit(&(elementType->defaultAttsNames), parser); 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
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 namespaceprocessingisturnedonanddifferentprefixesforthesame namespaceareused.Forthiscasewehaveacheckfurtherdown.
*/ 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; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */ #if0
--attIndex; #else
attIndex++;
nXMLNSDeclarations++;
(attId->name)[-1] = 3; #endif /* END MOZILLA CHANGE */
} 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; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */
(da->id->name)[-1] = 3;
nXMLNSDeclarations++;
appAtts[attIndex++] = da->id->name;
appAtts[attIndex++] = da->value; /* END MOZILLA CHANGE */
} 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; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */ #if0 if (nPrefixes) { #else if (nPrefixes || nXMLNSDeclarations) { #endif /* END MOZILLA CHANGE */ unsignedint j; /* hash table index */ unsignedlong version = parser->m_nsAttsVersion;
/* Detect and prevent invalid shift */ if (parser->m_nsAttsPower >= sizeof(unsignedint) * 8/* bits per byte */) { return XML_ERROR_NO_MEMORY;
}
unsignedint nsAttsSize = 1u << parser->m_nsAttsPower; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */ if (nPrefixes) { /* END MOZILLA CHANGE */ unsignedchar 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. *Thepreprocessorguardaddressesthe"alwaysfalse"warning *from-Wtype-limitsonplatformswhere
* sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX if (nsAttsSize > SIZE_MAX / sizeof(NS_ATT)) { /* Restore actual size of memory in m_nsAtts */
parser->m_nsAttsPower = oldNsAttsPower; return XML_ERROR_NO_MEMORY;
} #endif
temp = 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; /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */
} /* END MOZILLA CHANGE */
/* 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; unsignedlong uriHash; struct siphash sip_state; struct sipkey sip_key;
do { /* copies null terminator */ if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_ERROR_NO_MEMORY;
} while (*s++);
uriHash = (unsignedlong)sip24_final(&sip_state);
{ /* Check hash table for duplicate of expanded name (uriName). Derivedfromcodeinlookup(parser,HASH_TABLE*table,...).
*/ unsignedchar step = 0; unsignedlong 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;
/* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */ #if0 if (! --nPrefixes) { #else if (! --nPrefixes && ! nXMLNSDeclarations) { #endif /* END MOZILLA CHANGE */
i += 2; break;
} /* BEGIN MOZILLA CHANGE (Include xmlns attributes in attributes array) */
} elseif (s[-1] == 3) { /* xmlns attribute */ staticconst 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'
}; staticconst XML_Char xmlnsPrefix[] = {
ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, '\0'
};
((XML_Char *)s)[-1] = 0; /* clear flag */ if (! poolAppendString(&parser->m_tempPool, xmlnsNamespace)
|| ! poolAppendChar(&parser->m_tempPool, parser->m_namespaceSeparator)) return XML_ERROR_NO_MEMORY;
s += sizeof(xmlnsPrefix) / sizeof(xmlnsPrefix[0]) - 1; if (*s == XML_T(':')) {
++s; do { /* copies null terminator */ if (! poolAppendChar(&parser->m_tempPool, *s)) return XML_ERROR_NO_MEMORY;
} while (*s++); if (parser->m_ns_triplets) { /* append namespace separator and prefix */
parser->m_tempPool.ptr[-1] = parser->m_namespaceSeparator; if (! poolAppendString(&parser->m_tempPool, xmlnsPrefix)
|| ! poolAppendChar(&parser->m_tempPool, '\0')) return XML_ERROR_NO_MEMORY;
}
} else { /* xlmns attribute without a prefix. */ if (! poolAppendString(&parser->m_tempPool, xmlnsPrefix)
|| ! poolAppendChar(&parser->m_tempPool, '\0')) return XML_ERROR_NO_MEMORY;
}
/* store expanded name in attribute list */
s = poolStart(&parser->m_tempPool);
poolFinish(&parser->m_tempPool);
appAtts[i] = s;
if (! --nXMLNSDeclarations && ! nPrefixes) {
i += 2; break;
} /* END MOZILLA CHANGE */
} 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))
;
} elseif (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) { while (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. *Thepreprocessorguardaddressesthe"alwaysfalse"warning *from-Wtype-limitsonplatformswhere
* sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX if ((unsigned)(n + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY;
} #endif
uri = 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;
}
/* 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;
// NOTE: While Expat does not validate namespace URIs against RFC 3986 // today (and is not REQUIRED to do so with regard to the XML 1.0 // namespaces specification) we have to at least make sure, that // the application on top of Expat (that is likely splitting expanded // element names ("qualified names") of form // "[uri sep] local [sep prefix] '\0'" back into 1, 2 or 3 pieces // in its element handler code) cannot be confused by an attacker // putting additional namespace separator characters into namespace // declarations. That would be ambiguous and not to be expected. // // While the HTML API docs of function XML_ParserCreateNS have been // advising against use of a namespace separator character that can // appear in a URI for >20 years now, some widespread applications // are using URI characters (':' (colon) in particular) for a // namespace separator, in practice. To keep these applications // functional, we only reject namespaces URIs containing the // application-chosen namespace separator if the chosen separator // is a non-URI character with regard to RFC 3986. if (parser->m_ns && (uri[len] == parser->m_namespaceSeparator)
&& ! is_rfc3986_uri_char(uri[len])) { 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. *Thepreprocessorguardaddressesthe"alwaysfalse"warning *from-Wtype-limitsonplatformswhere
* sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY;
} #endif
XML_Char *temp
= 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 = 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. *Thepreprocessorguardaddressesthe"alwaysfalse"warning *from-Wtype-limitsonplatformswhere
* sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX if ((unsigned)(len + EXPAND_SPARE) > SIZE_MAX / sizeof(XML_Char)) { return XML_ERROR_NO_MEMORY;
} #endif
b->uri = 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 thewholefileisparsedwithonecall.
*/ staticenum XML_Error PTRCALL
cdataSectionProcessor(XML_Parser parser, constchar *start, constchar *end, constchar **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 thesectionisnotyetclosed.
*/ staticenum XML_Error
doCdataSection(XML_Parser parser, const ENCODING *enc, constchar **startPtr, constchar *end, constchar **nextPtr, XML_Bool haveMore, enum XML_Account account) { constchar *s = *startPtr; constchar **eventPP; constchar **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 (;;) { constchar *next = s; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok = XmlCdataSectionTok(enc, s, end, &next); #if XML_GE == 1 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 */ elseif ((0) && parser->m_characterDataHandler)
parser->m_characterDataHandler(parser->m_handlerArg, parser->m_dataBuf, 0); /* END disabled code */ elseif (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);
} elseif (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; constenum 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, (const XML_Char *)s,
(int)((const XML_Char *)next - (const XML_Char *)s));
} elseif (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 *explicitcase,sothisdefaultcasewillneverbeexecuted. *Weretainitasasafetynetandexcludeitfromthecoverage *statistics. * *LCOV_EXCL_START
*/
*eventPP = next; return XML_ERROR_UNEXPECTED_STATE; /* LCOV_EXCL_STOP */
}
switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED:
*eventPP = next;
*nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED:
*eventPP = next; return XML_ERROR_ABORTED; case XML_PARSING: if (parser->m_reenter) { return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
} /* Fall through */ default:;
*eventPP = s = next;
}
} /* not reached */
}
#ifdef XML_DTD
/* The idea here is to avoid using stack for each IGNORE section when thewholefileisparsedwithonecall.
*/ staticenum XML_Error PTRCALL
ignoreSectionProcessor(XML_Parser parser, constchar *start, constchar *end, constchar **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 ifthesectionisnotyetclosed.
*/ staticenum XML_Error
doIgnoreSection(XML_Parser parser, const ENCODING *enc, constchar **startPtr, constchar *end, constchar **nextPtr, XML_Bool haveMore) { constchar *next = *startPtr; /* in case of XML_TOK_NONE or XML_TOK_PARTIAL */ int tok; constchar *s = *startPtr; constchar **eventPP; constchar **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 *ofcodecannotbeexecuted.Theonlyoccasionsonwhich'enc' *isnot'encoding'arewhenthisfunctioniscalled *fromtheinternalentityprocessing,andIGNOREsectionsarean *errorininternalentities. * *Sinceitreallyisn'tclearthatthisistrue,wekeepthecode *andjustremoveitfromourcoveragetests. * *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); # if XML_GE == 1 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 *explicitcasestohandlethem,sothisdefaultcaseisnever *executed.Wekeepitasasafetynetanyway,andremoveit *fromourtestcoveragestatistics. * *LCOV_EXCL_START
*/
*eventPP = next; return XML_ERROR_UNEXPECTED_STATE; /* LCOV_EXCL_STOP */
} /* not reached */
}
#endif/* XML_DTD */
staticenum XML_Error
initializeEncoding(XML_Parser parser) { constchar *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);
}
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, NULL);
} elseif (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 *thattohappen,aparameterentityparsinghandlermusthaveattempted *tosuspendtheparser,whichfailsandraisesanerror.Theparsercan *beaborted,butcan'tbesuspended.
*/ 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);
} /* XmlPrologTok has now set the encoding based on the BOM it found, and we mustmovesandnextPtrforwardtoconsumetheBOM.
*nextPtr = next;
s = next;
} /* If we get this token, we have the start of what might be a normaltag,butnotadeclaration(i.e.itdoesn'tbeginwith "<!"or"<?").InaDTDcontext,thatisn'tlegal.
*/ elseif (tok == XML_TOK_INSTANCE_START) {
*nextPtr = next; return XML_ERROR_SYNTAX;
}
start = next;
parser->m_eventPtr = start;
}
}
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,whenparsinganexternalsubset,doPrologwillnotacceptaBOM asvalid,andreportasyntaxerror,sowehavetoskiptheBOM,and accountfortheBOMbytes.
*/ elseif (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);
}
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, NULL);
} /* If we get this token, we have the start of what might be a normaltag,butnotadeclaration(i.e.itdoesn'tbeginwith "<!"or"<?").InaDTDcontext,thatisn'tlegal.
*/ elseif (tok == XML_TOK_INSTANCE_START) {
*nextPtr = next; return XML_ERROR_SYNTAX;
}
if (dtd->scaffIndex) { /* Detect and prevent integer overflow. *Thepreprocessorguardaddressesthe"alwaysfalse"warning *from-Wtype-limitsonplatformswhere
* sizeof(unsigned int) < sizeof(size_t), e.g. on x86_64. */ #if UINT_MAX >= SIZE_MAX if (parser->m_groupSize > SIZE_MAX / sizeof(int)) {
parser->m_groupSize /= 2; return XML_ERROR_NO_MEMORY;
} #endif
int *const new_scaff_index = REALLOC(
parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int)); if (new_scaff_index == NULL) {
parser->m_groupSize /= 2; return XML_ERROR_NO_MEMORY;
}
dtd->scaffIndex = new_scaff_index;
}
} else {
parser->m_groupConnector = 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; ifyes,checkthattheentityexists,andthatitisinternal, otherwisecalltheskippedentityhandler
*/ if (parser->m_prologState.documentEntity
&& (dtd->standalone ? ! parser->m_openInternalEntities
: ! dtd->hasParamEntityRefs)) { if (! entity) return XML_ERROR_UNDEFINED_ENTITY; elseif (! entity->is_internal) { /* It's hard to exhaustively search the code to be sure, *buttheredoesn'tseemtobeawayofexecutingthe *followingline.Therearetwocases: * *If'standalone'isfalse,theDTDmusthaveno *parameterentitiesorwewouldn'thavepassedtheouter *'if'statement.Thatmeanstheonlyentityinthehash *tableistheexternalsubsetname"#"whichcannotbe *givenasaparameterentitynameinXMLsyntax,sothe *lookupmusthavereturnedNULLandwedon'tevenreach *thetestforaninternalentity. * *If'standalone'istrue,itdoesnotseemtobe *possibletocreateentitiestakingthiscodepaththat *arenotinternalentities,sofailthetestabove. * *Becausethisanalysisisveryuncertain,thecodeis *beingleftinplaceandmerelyremovedfromthe *coverageteststatistics.
*/ return XML_ERROR_ENTITY_DECLARED_IN_PE; /* LCOV_EXCL_LINE */
}
} elseif (! 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 = processEntity(parser, entity, betweenDecl, ENTITY_INTERNAL); 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( /* BEGIN MOZILLA CHANGE (Bug 191482 - Add external entity inclusions to internalSubset) */ #if0
parser->m_externalEntityRefHandlerArg, 0, entity->base, #else
parser->m_externalEntityRefHandlerArg, entity->name, entity->base, #endif /* END MOZILLA CHANGE */
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) { // NOTE: We are avoiding MALLOC(..) here to so that // applications that are not using XML_FreeContentModel but // plain free(..) or .free_fcn() to free the content model's // memory are safe.
XML_Content *content = parser->m_mem.malloc_fcn(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; constchar *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; while (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; case XML_PARSING: if (parser->m_reenter) {
*nextPtr = next; return XML_ERROR_NONE;
} /* Fall through */ default:
s = next;
tok = XmlPrologTok(enc, s, end, &next);
}
} /* not reached */
}
staticenum XML_Error PTRCALL
epilogProcessor(XML_Parser parser, constchar *s, constchar *end, constchar **nextPtr) {
parser->m_processor = epilogProcessor;
parser->m_eventPtr = s; for (;;) { constchar *next = NULL; int tok = XmlPrologTok(parser->m_encoding, s, end, &next); #if XML_GE == 1 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;
} switch (parser->m_parsingStatus.parsing) { case XML_SUSPENDED:
parser->m_eventPtr = next;
*nextPtr = next; return XML_ERROR_NONE; case XML_FINISHED:
parser->m_eventPtr = next; return XML_ERROR_ABORTED; case XML_PARSING: if (parser->m_reenter) { return XML_ERROR_UNEXPECTED_STATE; // LCOV_EXCL_LINE
} /* Fall through */ default:;
parser->m_eventPtr = s = next;
}
}
}
staticenum XML_Error
processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl, enum EntityType type) {
OPEN_INTERNAL_ENTITY *openEntity, **openEntityList, **freeEntityList; switch (type) { case ENTITY_INTERNAL:
parser->m_processor = internalEntityProcessor;
openEntityList = &parser->m_openInternalEntities;
freeEntityList = &parser->m_freeInternalEntities; break; case ENTITY_ATTRIBUTE:
openEntityList = &parser->m_openAttributeEntities;
freeEntityList = &parser->m_freeAttributeEntities; break; case ENTITY_VALUE:
openEntityList = &parser->m_openValueEntities;
freeEntityList = &parser->m_freeValueEntities; break; /* default case serves merely as a safety net in case of a *wrongentityType.Thereforeweexcludethefollowinglines *fromthetestcoverage. * *LCOV_EXCL_START
*/ default: // Should not reach here
assert(0); /* LCOV_EXCL_STOP */
}
// Only internal entities make use of the reenter flag // therefore no need to set it for other entity types if (type == ENTITY_INTERNAL) {
triggerReenter(parser);
} return XML_ERROR_NONE;
}
// This will return early if (entity->hasMore) {
textStart = ((constchar *)entity->textPtr) + entity->processed;
textEnd = (constchar *)(entity->textPtr + entity->textLen); /* Set a safe default value in case 'next' does not get set */
next = textStart;
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 {
result = doContent(parser, openEntity->startTagLevel,
parser->m_internalEncoding, textStart, textEnd, &next,
XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
}
if (result != XML_ERROR_NONE) return result; // Check if entity is complete, if not, mark down how much of it is // processed if (textEnd != next
&& (parser->m_parsingStatus.parsing == XML_SUSPENDED
|| (parser->m_parsingStatus.parsing == XML_PARSING
&& parser->m_reenter))) {
entity->processed = (int)(next - (constchar *)entity->textPtr); return result;
}
// Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openInternalEntities during doProlog or doContent calls above)
entity->hasMore = XML_FALSE; if (! entity->is_param
&& (openEntity->startTagLevel != parser->m_tagLevel)) { return XML_ERROR_ASYNC_ENTITY;
}
triggerReenter(parser); return result;
} // End of entity processing, "if" block will return here
// Remove fully processed openEntity from open entity list. #if XML_GE == 1
entityTrackingOnClose(parser, entity, __LINE__); #endif // openEntity is m_openInternalEntities' head, as we set it at the start of // this function and we skipped doProlog and doContent calls with hasMore set // to false. This means we can directly remove the head of // m_openInternalEntities
assert(parser->m_openInternalEntities == openEntity);
entity->open = XML_FALSE;
parser->m_openInternalEntities = parser->m_openInternalEntities->next;
/* put openEntity back in list of free instances */
openEntity->next = parser->m_freeInternalEntities;
parser->m_freeInternalEntities = openEntity;
while (1) { if (! parser->m_openAttributeEntities) {
result = appendAttributeValue(parser, enc, isCdata, next, end, pool,
account, &next);
} else {
OPEN_INTERNAL_ENTITY *const openEntity = parser->m_openAttributeEntities; if (! openEntity) return XML_ERROR_UNEXPECTED_STATE;
ENTITY *const entity = openEntity->entity; constchar *const textStart
= ((constchar *)entity->textPtr) + entity->processed; constchar *const textEnd
= (constchar *)(entity->textPtr + entity->textLen); /* Set a safe default value in case 'next' does not get set */ constchar *nextInEntity = textStart; if (entity->hasMore) {
result = appendAttributeValue(
parser, parser->m_internalEncoding, isCdata, textStart, textEnd,
pool, XML_ACCOUNT_ENTITY_EXPANSION, &nextInEntity); if (result != XML_ERROR_NONE) break; // Check if entity is complete, if not, mark down how much of it is // processed. A XML_SUSPENDED check here is not required as // appendAttributeValue will never suspend the parser. if (textEnd != nextInEntity) {
entity->processed
= (int)(nextInEntity - (constchar *)entity->textPtr); continue;
}
// Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openAttributeEntities during appendAttributeValue)
entity->hasMore = XML_FALSE; continue;
} // End of entity processing, "if" block skips the rest
// Remove fully processed openEntity from open entity list. #if XML_GE == 1
entityTrackingOnClose(parser, entity, __LINE__); #endif // openEntity is m_openAttributeEntities' head, since we set it at the // start of this function and because we skipped appendAttributeValue call // with hasMore set to false. This means we can directly remove the head // of m_openAttributeEntities
assert(parser->m_openAttributeEntities == openEntity);
entity->open = XML_FALSE;
parser->m_openAttributeEntities = parser->m_openAttributeEntities->next;
/* put openEntity back in list of free instances */
openEntity->next = parser->m_freeAttributeEntities;
parser->m_freeAttributeEntities = openEntity;
}
// Break if an error occurred or there is nothing left to process if (result || (parser->m_openAttributeEntities == NULL && end == next)) { break;
}
}
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;
}
for (;;) { constchar *next
= ptr; /* XmlAttributeValueTok doesn't always set the last arg */ int tok = XmlAttributeValueTok(enc, ptr, end, &next); #if XML_GE == 1 if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
accountingOnAbort(parser); return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
} #endif switch (tok) { case XML_TOK_NONE: if (nextPtr) {
*nextPtr = next;
} 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 *errorreturnhappensifthecodepointpassediniseither *negativeorgreaterthanorequalto0x110000.The *XmlCharRefNumber()functionswillallreturnanumber *strictlylessthan0x110000oranegativevalueifanerror *occurred.Thenegativevalueisinterceptedabove,so *XmlEncode()isneverpassedavalueitmightreturnan *errorfor.
*/ 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; bool checkEntityDecl;
XML_Char ch = (XML_Char)XmlPredefinedEntityName(
enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar); if (ch) { #if XML_GE == 1 /* NOTE: We are replacing 4-6 characters original input for 1 character *sothereisnoamplificationandhencerecordingwithout
* protection. */
accountingDiffTolerated(parser, tok, (char *)&ch,
((char *)&ch) + sizeof(XML_Char), __LINE__,
XML_ACCOUNT_ENTITY_EXPANSION); #endif/* XML_GE == 1 */ 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; ifyes,checkthattheentityexists,andthatitisinternal.
*/ 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; elseif (! entity->is_internal) return XML_ERROR_ENTITY_DECLARED_IN_PE;
} elseif (! 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 outofsyncwiththecalltothestartElementHandler. if((pool==&parser->m_tempPool)&&parser->m_defaultHandler) reportDefault(parser,enc,ptr,next);
*/ /* BEGIN MOZILLA CHANGE (Bug 35984 - Undeclared entities are ignored when external DTD not found) */ #if0 break; #else return XML_ERROR_UNDEFINED_ENTITY; #endif /* END MOZILLA CHANGE */
} if (entity->open) { if (enc == parser->m_encoding) { /* It does not appear that this line can be executed. * *The"if(entity->open)"checkcatchesrecursiveentity *definitions.Inordertobecalledwithanopen *entity,itmusthavegonethroughthiscodebeforeand *beenthroughtherecursivecallto *appendAttributeValue()somelinesbelow.Thatcall *setsthelocalencoding("enc")totheparser's *internalencoding(internal_utf8orinternal_utf16), *whichcanneverbethesameastheprincipleencoding. *Itdoesn'tappearthereisanothercodepaththatgets *herewithentity->openbeingTRUE. * *Sinceitisnotcertainthatthislogiciswatertight, *wekeepthelineandmerelyexcludeitfromcoverage *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;
result = processEntity(parser, entity, XML_FALSE, ENTITY_ATTRIBUTE); if ((result == XML_ERROR_NONE) && (nextPtr != NULL)) {
*nextPtr = next;
} return result;
}
} break; default: /* The only token returned by XmlAttributeValueTok() that does *nothaveanexplicitcasehereisXML_TOK_PARTIAL_CHAR. *Gettingthatwouldrequireanentitynametocontainan *incompleteXMLcharacter(e.g.\xE2\x82);howeverprevious *tokeniserswillhavealreadyrecognisedandrejectedsuch *namesbeforeXmlAttributeValueTok()getsalook-in.This *defaultcaseshouldberetainedasasafetynet,butthecode *excludedfromcoveragetests. * *LCOV_EXCL_START
*/ if (enc == parser->m_encoding)
parser->m_eventPtr = ptr; return XML_ERROR_UNEXPECTED_STATE; /* LCOV_EXCL_STOP */
}
ptr = next;
} /* not reached */
}
#if XML_GE == 1 staticenum XML_Error
storeEntityValue(XML_Parser parser, const ENCODING *enc, constchar *entityTextPtr, constchar *entityTextEnd, enum XML_Account account, constchar **nextPtr) {
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, sincethiswouldindicateanexternalentity;thereforewe
have to make sure that entityValuePool.start is not null */ if (! pool->blocks) { if (! poolGrow(pool)) return XML_ERROR_NO_MEMORY;
}
constchar *next = entityTextPtr;
/* Nothing to tokenize. */ if (entityTextPtr >= entityTextEnd) {
result = XML_ERROR_NONE; goto endEntityValue;
}
for (;;) {
next
= entityTextPtr; /* XmlEntityValueTok doesn't always set the last arg */ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
account)) {
accountingOnAbort(parser);
result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH; goto endEntityValue;
}
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 || (entity == parser->m_declEntity)) { 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 {
result = processEntity(parser, entity, XML_FALSE, ENTITY_VALUE); 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 *errorreturnhappensifthecodepointpassediniseither *negativeorgreaterthanorequalto0x110000.The *XmlCharRefNumber()functionswillallreturnanumber *strictlylessthan0x110000oranegativevalueifanerror *occurred.Thenegativevalueisinterceptedabove,so *XmlEncode()isneverpassedavalueitmightreturnan *errorfor.
*/ 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 *thatXmlEntityValueTok()canreturnhavetheirownexplicit *cases--butshouldberetainedforsafety.Wedohowever *excludeitfromthecoveragestatistics. * *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 */ // If 'nextPtr' is given, it should be updated during the processing if (nextPtr != NULL) {
*nextPtr = next;
} return result;
}
ENTITY *const entity = openEntity->entity; constchar *const textStart
= ((constchar *)entity->textPtr) + entity->processed; constchar *const textEnd
= (constchar *)(entity->textPtr + entity->textLen); /* Set a safe default value in case 'next' does not get set */ constchar *nextInEntity = textStart; if (entity->hasMore) {
result = storeEntityValue(parser, parser->m_internalEncoding, textStart,
textEnd, XML_ACCOUNT_ENTITY_EXPANSION,
&nextInEntity); if (result != XML_ERROR_NONE) break; // Check if entity is complete, if not, mark down how much of it is // processed. A XML_SUSPENDED check here is not required as // appendAttributeValue will never suspend the parser. if (textEnd != nextInEntity) {
entity->processed
= (int)(nextInEntity - (constchar *)entity->textPtr); continue;
}
// Entity is complete. We cannot close it here since we need to first // process its possible inner entities (which are added to the // m_openValueEntities during storeEntityValue)
entity->hasMore = XML_FALSE; continue;
} // End of entity processing, "if" block skips the rest
// Remove fully processed openEntity from open entity list. # if XML_GE == 1
entityTrackingOnClose(parser, entity, __LINE__); # endif // openEntity is m_openValueEntities' head, since we set it at the // start of this function and because we skipped storeEntityValue call // with hasMore set to false. This means we can directly remove the head // of m_openValueEntities
assert(parser->m_openValueEntities == openEntity);
entity->open = XML_FALSE;
parser->m_openValueEntities = parser->m_openValueEntities->next;
/* put openEntity back in list of free instances */
openEntity->next = parser->m_freeValueEntities;
parser->m_freeValueEntities = openEntity;
}
// Break if an error occurred or there is nothing left to process if (result
|| (parser->m_openValueEntities == NULL && entityTextEnd == next)) { break;
}
}
return result;
}
#else/* XML_GE == 0 */
staticenum XML_Error
storeSelfEntityValue(XML_Parser parser, ENTITY *entity) { // This will store "&entity123;" in entity->textPtr // to end up as "&entity123;" in the handler. constchar *const entity_start = "&"; constchar *const entity_end = ";";
NAMED *const nameAddedOrFound = (NAMED *)lookup(
parser, &(type->defaultAttsNames), attId->name, sizeof(NAMED)); if (! nameAddedOrFound) return0;
type->nDefaultAtts += 1; return1;
}
staticint
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)) return0;
} if (! poolAppendChar(&dtd->pool, XML_T('\0'))) return0;
prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), sizeof(PREFIX)); if (! prefix) return0; if (prefix->name == poolStart(&dtd->pool))
poolFinish(&dtd->pool); else
poolDiscard(&dtd->pool);
elementType->prefix = prefix; break;
}
} return1;
}
static ATTRIBUTE_ID *
getAttributeId(XML_Parser parser, const ENCODING *enc, constchar *start, constchar *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 reused (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)
; elseif (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)
staticconst 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. * *Thisispartofaloopcopyingthedefaultprefixbinding *URIintotheparser'stemporarystringpool.Previously, *thatURIwascopiedintothesamestringpool,witha *terminatingNULcharacter,aspartofsetContext().When *thepoolwascleared,thatleavesablockdefinitelybig *enoughtoholdtheURIonthefreeblocklistofthepool. *TheURIcopyingetContext()thereforecannotrunoutof *memory. * *IfthepoolisusedbetweenthesetContext()and *getContext()calls,theworstitcandoisleaveabigger *blockonthefrontofthefreelist.Giventhatthisis *allsomewhatinobviousandprogramlogiccanbechanged,we *don'tdeletethelinebutwedoexcludeitfromthetest *coveragestatistics.
*/ 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 *notseemtobeawayofinjectingaprefixwithoutabinding *thatdoesn'tgeterroredlongbeforethisfunctioniscalled. *Thetestshouldremainforsafety'ssake,soweinstead *excludethefollowinglinefromthecoveragestatistics.
*/ 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)) return0;
needSep = XML_TRUE;
}
if (! poolAppendChar(&parser->m_tempPool, XML_T('\0'))) return NULL; return parser->m_tempPool.start;
}
/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. ThenewDTDhasalreadybeeninitialized.
*/ staticint
dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
XML_Parser parser) {
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) return0; if (! lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) return0;
}
staticconst 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;
}
// A version of `poolCopyString` that does not call `poolFinish` // and reverts any partial advancement upon failure. staticconst XML_Char *FASTCALL
poolCopyStringNoFinish(STRING_POOL *pool, const XML_Char *s) { const XML_Char *const original = s; do { if (! poolAppendChar(pool, *s)) { // Revert any previously successful advancement const ptrdiff_t advancedBy = s - original; if (advancedBy > 0)
pool->ptr -= advancedBy; return NULL;
}
} while (*s++); return pool->start;
}
staticconst 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().Currentlyitiscalledfromexactlyone *placetocopythetextofasimplegeneralentity.Bythat *point,thenameoftheentityisalreadystoredinthepool,so *pool->ptrcannotbeNULL. * *IfpoolCopyStringN()isusedelsewhereasitwellmightbe, *thislinemaywellbecomeexecutableagain.Regardless,this *sortofcheckshouldn'tberemovedlightly,sowejustexclude *itfromthecoveragestatistics.
*/ return NULL; /* LCOV_EXCL_LINE */
} for (; n > 0; --n, s++) { if (! poolAppendChar(pool, *s)) return NULL;
}
s = pool->start;
poolFinish(pool); return s;
}
/* 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/2byteshavealreadybeenallocated.Thisisn't *readilytestable,sinceitisunlikelythatanaverage *machinewillhavethatmuchmemory,soweexcludeitfromthe *coveragestatistics.
*/ return XML_FALSE; /* LCOV_EXCL_LINE */
}
bytesToAllocate = poolBytesToAllocateFor(blockSize); if (bytesToAllocate == 0) return XML_FALSE;
if (blockSize < 0) { /* This condition traps a situation where either more than *INT_MAXbyteshavealreadybeenallocated(whichisprevented *byvariouspiecesofprogramlogic,notleastthisone,never *mindtheunlikelihoodofactuallyhavingthatmuchmemory)or *thepoolcontrolfieldshavebeencorrupted(whichcould *conceivablyhappeninanextremelybuggyuserhandler *function).Eitherwayitisn'treadilytestable,sowe *excludeitfromthecoveragestatistics.
*/ return XML_FALSE; /* LCOV_EXCL_LINE */
}
// NOTE: We are avoiding MALLOC(..) here to so that // applications that are not using XML_FreeContentModel but plain // free(..) or .free_fcn() to free the content model's memory are safe.
ret = parser->m_mem.malloc_fcn(allocsize); if (! ret) return NULL;
/* 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 = (unsignedint)cn;
}
}
/* First determine how long the string is */ while (s[charsRequired] != 0) {
charsRequired++;
} /* Include the terminator */
charsRequired++;
/* Now allocate space for the copy */
result = MALLOC(parser, charsRequired * sizeof(XML_Char)); if (result == NULL) return NULL; /* Copy the original into place */
memcpy(result, s, charsRequired * sizeof(XML_Char)); return result;
}
/* Note: Performance is of no concern here */ constchar *walker = before; if ((rootParser->m_accounting.debugLevel >= 3u)
|| (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");
} #endif /* END MOZILLA CHANGE */
static XML_Bool
accountingDiffTolerated(XML_Parser originParser, int tok, constchar *before, constchar *after, int source_line, enum XML_Account account) { /* Note: We need to check the token type *first* to be sure that *wecanevenaccessvariable<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 */
return debugLevel;
} #endif /* END MOZILLA CHANGE */
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.451Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-08-26)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.