summaryrefslogtreecommitdiff
path: root/Build/source/extra/expat/xmlwf/wfcheck.c
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2009-11-10 14:02:38 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2009-11-10 14:02:38 +0000
commit7b75da706bf852114d792cf3b0f3c73ae0ca0fb8 (patch)
treec76bdff652167455b3e9534bedf558ae88a3dcdb /Build/source/extra/expat/xmlwf/wfcheck.c
parent84ef24a5151f308cbe96edd705d1cdedd98a4210 (diff)
directories not used for build moved to extra/
git-svn-id: svn://tug.org/texlive/trunk@15980 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/extra/expat/xmlwf/wfcheck.c')
-rw-r--r--Build/source/extra/expat/xmlwf/wfcheck.c953
1 files changed, 953 insertions, 0 deletions
diff --git a/Build/source/extra/expat/xmlwf/wfcheck.c b/Build/source/extra/expat/xmlwf/wfcheck.c
new file mode 100644
index 00000000000..c18abd290bd
--- /dev/null
+++ b/Build/source/extra/expat/xmlwf/wfcheck.c
@@ -0,0 +1,953 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "wfcheck.h"
+#include "hashtable.h"
+
+#include "xmltok.h"
+#include "xmlrole.h"
+
+typedef struct {
+ const char *name;
+ const char *textPtr;
+ size_t textLen;
+ const char *docTextPtr;
+ const char *systemId;
+ const char *publicId;
+ const char *notation;
+ char open;
+ char wfInContent;
+ char wfInAttribute;
+ char magic;
+} ENTITY;
+
+#define INIT_BLOCK_SIZE 1024
+
+typedef struct block {
+ struct block *next;
+ char s[1];
+} BLOCK;
+
+typedef struct {
+ BLOCK *blocks;
+ const char *end;
+ char *ptr;
+ char *start;
+} STRING_POOL;
+
+typedef struct {
+ HASH_TABLE generalEntities;
+ HASH_TABLE paramEntities;
+ STRING_POOL pool;
+ int containsRef;
+ int standalone;
+ char *groupConnector;
+ size_t groupSize;
+} DTD;
+
+typedef struct {
+ DTD dtd;
+ size_t stackSize;
+ const char **startName;
+ int attsSize;
+ ATTRIBUTE *atts;
+} CONTEXT;
+
+static void poolInit(STRING_POOL *);
+static void poolDestroy(STRING_POOL *);
+static const char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static const char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end);
+static int poolGrow(STRING_POOL *);
+static int dtdInit(DTD *);
+static void dtdDestroy(DTD *);
+static int contextInit(CONTEXT *);
+static void contextDestroy(CONTEXT *);
+
+#define poolStart(pool) ((pool)->start)
+#define poolDiscard(pool) ((pool)->ptr = (pool)->start)
+#define poolFinish(pool) ((pool)->start = (pool)->ptr)
+
+static enum WfCheckResult
+checkProlog(DTD *, const char *s, const char *end, const char **, const ENCODING **enc);
+static enum WfCheckResult
+checkContent(size_t level, CONTEXT *context, const ENCODING *enc,
+ const char *s, const char *end, const char **badPtr);
+static enum WfCheckResult
+checkGeneralTextEntity(CONTEXT *context,
+ const char *s, const char *end,
+ const char **nextPtr,
+ const ENCODING **enc);
+static enum WfCheckResult
+checkAttributeValue(DTD *, const ENCODING *, const char *, const char *, const char **);
+static enum WfCheckResult
+checkAttributeUniqueness(CONTEXT *context, const ENCODING *enc, int nAtts,
+ const char **badPtr);
+static enum WfCheckResult
+checkParsedEntities(CONTEXT *context, const char **badPtr);
+
+static
+enum WfCheckResult storeEntity(DTD *dtd,
+ const ENCODING *enc,
+ int isParam,
+ const char *entityNamePtr,
+ const char *entityNameEnd,
+ const char *entityTextPtr,
+ const char *entityTextEnd,
+ const char **badPtr);
+
+
+enum WfCheckResult
+wfCheck(enum EntityType entityType, const char *s, size_t n,
+ const char **badPtr, unsigned long *badLine, unsigned long *badCol)
+{
+ CONTEXT context;
+ const ENCODING *enc;
+ const char *start = s;
+ const char *end = s + n;
+ const char *next = 0;
+ enum WfCheckResult result;
+
+ if (!contextInit(&context)) {
+ contextDestroy(&context);
+ return noMemory;
+ }
+ if (entityType == documentEntity) {
+ result = checkProlog(&context.dtd, s, end, &next, &enc);
+ s = next;
+ if (!result) {
+ result = checkParsedEntities(&context, &next);
+ s = next;
+ if (!result) {
+ result = checkContent(0, &context, enc, s, end, &next);
+ s = next;
+ }
+ }
+ }
+ else {
+ result = checkGeneralTextEntity(&context, s, end, &next, &enc);
+ s = next;
+ }
+ if (result && s) {
+ POSITION pos;
+ memset(&pos, 0, sizeof(POSITION));
+ XmlUpdatePosition(enc, start, s, &pos);
+ *badPtr = s;
+ *badLine = pos.lineNumber;
+ *badCol = pos.columnNumber;
+ }
+ contextDestroy(&context);
+ return result;
+}
+
+static
+int contextInit(CONTEXT *p)
+{
+ p->stackSize = 1024;
+ p->startName = malloc(p->stackSize * sizeof(char *));
+ p->attsSize = 1024;
+ p->atts = malloc(p->attsSize * sizeof(ATTRIBUTE));
+ return dtdInit(&(p->dtd)) && p->atts && p->startName;
+}
+
+static
+void contextDestroy(CONTEXT *p)
+{
+ dtdDestroy(&(p->dtd));
+ free((void *)p->startName);
+ free((void *)p->atts);
+}
+
+static enum WfCheckResult
+checkContent(size_t level, CONTEXT *context, const ENCODING *enc,
+ const char *s, const char *end, const char **badPtr)
+{
+ size_t startLevel = level;
+ const char *next;
+ int tok = XmlContentTok(enc, s, end, &next);
+ for (;;) {
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ case XML_TOK_NONE:
+ if (startLevel > 0) {
+ if (level != startLevel) {
+ *badPtr = s;
+ return asyncEntity;
+ }
+ return wellFormed;
+ }
+ *badPtr = s;
+ return noElements;
+ case XML_TOK_INVALID:
+ *badPtr = next;
+ return invalidToken;
+ case XML_TOK_PARTIAL:
+ *badPtr = s;
+ return unclosedToken;
+ case XML_TOK_PARTIAL_CHAR:
+ *badPtr = s;
+ return partialChar;
+ case XML_TOK_EMPTY_ELEMENT_NO_ATTS:
+ break;
+ case XML_TOK_ENTITY_REF:
+ {
+ const char *name = poolStoreString(&context->dtd.pool, enc,
+ s + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ ENTITY *entity = (ENTITY *)lookup(&context->dtd.generalEntities, name, 0);
+ poolDiscard(&context->dtd.pool);
+ if (!entity) {
+ if (!context->dtd.containsRef || context->dtd.standalone) {
+ *badPtr = s;
+ return undefinedEntity;
+ }
+ break;
+ }
+ if (entity->wfInContent)
+ break;
+ if (entity->open) {
+ *badPtr = s;
+ return recursiveEntityRef;
+ }
+ if (entity->notation) {
+ *badPtr = s;
+ return binaryEntityRef;
+ }
+ if (entity) {
+ if (entity->textPtr) {
+ enum WfCheckResult result;
+ const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
+ entity->open = 1;
+ result = checkContent(level, context, internalEnc,
+ entity->textPtr, entity->textPtr + entity->textLen,
+ badPtr);
+ entity->open = 0;
+ if (result && *badPtr) {
+ *badPtr = s;
+ return result;
+ }
+ entity->wfInContent = 1;
+ }
+ }
+ break;
+ }
+ case XML_TOK_START_TAG_NO_ATTS:
+ if (level == context->stackSize) {
+ context->startName
+ = realloc((void *)context->startName, (context->stackSize *= 2) * sizeof(char *));
+ if (!context->startName)
+ return noMemory;
+ }
+ context->startName[level++] = s + enc->minBytesPerChar;
+ break;
+ case XML_TOK_START_TAG_WITH_ATTS:
+ if (level == context->stackSize) {
+ context->startName = realloc((void *)context->startName, (context->stackSize *= 2) * sizeof(char *));
+ if (!context->startName)
+ return noMemory;
+ }
+ context->startName[level++] = s + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_EMPTY_ELEMENT_WITH_ATTS:
+ {
+ int i;
+ int n = XmlGetAttributes(enc, s, context->attsSize, context->atts);
+ if (n > context->attsSize) {
+ context->attsSize = 2*n;
+ context->atts = realloc((void *)context->atts, context->attsSize * sizeof(ATTRIBUTE));
+ if (!context->atts)
+ return noMemory;
+ XmlGetAttributes(enc, s, n, context->atts);
+ }
+ for (i = 0; i < n; i++) {
+ if (!context->atts[i].normalized) {
+ enum WfCheckResult result
+ = checkAttributeValue(&context->dtd, enc,
+ context->atts[i].valuePtr,
+ context->atts[i].valueEnd,
+ badPtr);
+ if (result)
+ return result;
+ }
+ }
+ if (i > 1) {
+ enum WfCheckResult result = checkAttributeUniqueness(context, enc, n, badPtr);
+ if (result)
+ return result;
+ }
+ }
+ break;
+ case XML_TOK_END_TAG:
+ if (level == startLevel) {
+ *badPtr = s;
+ return asyncEntity;
+ }
+ --level;
+ if (!XmlSameName(enc, context->startName[level], s + enc->minBytesPerChar * 2)) {
+ *badPtr = s;
+ return tagMismatch;
+ }
+ break;
+ case XML_TOK_CHAR_REF:
+ if (XmlCharRefNumber(enc, s) < 0) {
+ *badPtr = s;
+ return badCharRef;
+ }
+ break;
+ case XML_TOK_XML_DECL:
+ *badPtr = s;
+ return misplacedXmlPi;
+ }
+ s = next;
+ if (level == 0) {
+ do {
+ tok = XmlPrologTok(enc, s, end, &next);
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ case XML_TOK_NONE:
+ return wellFormed;
+ case XML_TOK_PROLOG_S:
+ case XML_TOK_COMMENT:
+ case XML_TOK_PI:
+ s = next;
+ break;
+ default:
+ if (tok > 0) {
+ *badPtr = s;
+ return junkAfterDocElement;
+ }
+ break;
+ }
+ } while (tok > 0);
+ }
+ else
+ tok = XmlContentTok(enc, s, end, &next);
+ }
+ /* not reached */
+}
+
+static
+int attcmp(const void *p1, const void *p2)
+{
+ const ATTRIBUTE *a1 = p1;
+ const ATTRIBUTE *a2 = p2;
+ size_t n1 = a1->valuePtr - a1->name;
+ size_t n2 = a2->valuePtr - a2->name;
+
+ if (n1 == n2) {
+ int n = memcmp(a1->name, a2->name, n1);
+ if (n)
+ return n;
+ /* Sort identical attribute names by position, so that we always
+ report the first duplicate attribute. */
+ if (a1->name < a2->name)
+ return -1;
+ else if (a1->name > a2->name)
+ return 1;
+ else
+ return 0;
+ }
+ else if (n1 < n2)
+ return -1;
+ else
+ return 1;
+}
+
+/* Note that this trashes the attribute values. */
+
+static enum WfCheckResult
+checkAttributeUniqueness(CONTEXT *context, const ENCODING *enc, int nAtts,
+ const char **badPtr)
+{
+#define QSORT_MIN_ATTS 10
+ if (nAtts < QSORT_MIN_ATTS) {
+ int i;
+ for (i = 1; i < nAtts; i++) {
+ int j;
+ for (j = 0; j < i; j++) {
+ if (XmlSameName(enc, context->atts[i].name, context->atts[j].name)) {
+ *badPtr = context->atts[i].name;
+ return duplicateAttribute;
+ }
+ }
+ }
+ }
+ else {
+ int i;
+ const char *dup = 0;
+ /* Store the end of the name in valuePtr */
+ for (i = 0; i < nAtts; i++) {
+ ATTRIBUTE *a = context->atts + i;
+ a->valuePtr = a->name + XmlNameLength(enc, a->name);
+ }
+ qsort(context->atts, nAtts, sizeof(ATTRIBUTE), attcmp);
+ for (i = 1; i < nAtts; i++) {
+ ATTRIBUTE *a = context->atts + i;
+ if (XmlSameName(enc, a->name, a[-1].name)) {
+ if (!dup || a->name < dup)
+ dup = a->name;
+ }
+ }
+ if (dup) {
+ *badPtr = dup;
+ return duplicateAttribute;
+ }
+ }
+ return wellFormed;
+}
+
+static enum WfCheckResult
+checkProlog(DTD *dtd, const char *s, const char *end,
+ const char **nextPtr, const ENCODING **enc)
+{
+ const char *entityNamePtr, *entityNameEnd;
+ int entityIsParam;
+ PROLOG_STATE state;
+ ENTITY *entity;
+ INIT_ENCODING initEnc;
+ XmlInitEncoding(&initEnc, enc);
+ XmlPrologStateInit(&state);
+ for (;;) {
+ const char *next;
+ int tok = XmlPrologTok(*enc, s, end, &next);
+ switch (XmlTokenRole(&state, tok, s, next, *enc)) {
+ case XML_ROLE_XML_DECL:
+ {
+ const char *encodingName = 0;
+ const ENCODING *encoding = 0;
+ const char *version;
+ int standalone = -1;
+ if (!XmlParseXmlDecl(0,
+ *enc,
+ s,
+ next,
+ nextPtr,
+ &version,
+ &encodingName,
+ &encoding,
+ &standalone))
+ return syntaxError;
+ if (encoding) {
+ if (encoding->minBytesPerChar != (*enc)->minBytesPerChar) {
+ *nextPtr = encodingName;
+ return incorrectEncoding;
+ }
+ *enc = encoding;
+ }
+ else if (encodingName) {
+ *nextPtr = encodingName;
+ return unknownEncoding;
+ }
+ if (standalone == 1)
+ dtd->standalone = 1;
+ break;
+ }
+ case XML_ROLE_DOCTYPE_SYSTEM_ID:
+ dtd->containsRef = 1;
+ break;
+ case XML_ROLE_DOCTYPE_PUBLIC_ID:
+ case XML_ROLE_ENTITY_PUBLIC_ID:
+ case XML_ROLE_NOTATION_PUBLIC_ID:
+ if (!XmlIsPublicId(*enc, s, next, nextPtr))
+ return syntaxError;
+ break;
+ case XML_ROLE_INSTANCE_START:
+ *nextPtr = s;
+ return wellFormed;
+ case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE:
+ case XML_ROLE_FIXED_ATTRIBUTE_VALUE:
+ {
+ const char *tem = 0;
+ enum WfCheckResult result
+ = checkAttributeValue(dtd, *enc, s + (*enc)->minBytesPerChar,
+ next - (*enc)->minBytesPerChar,
+ &tem);
+ if (result) {
+ if (tem)
+ *nextPtr = tem;
+ return result;
+ }
+ break;
+ }
+ case XML_ROLE_ENTITY_VALUE:
+ {
+ enum WfCheckResult result
+ = storeEntity(dtd,
+ *enc,
+ entityIsParam,
+ entityNamePtr,
+ entityNameEnd,
+ s,
+ next,
+ nextPtr);
+ if (result != wellFormed)
+ return result;
+ }
+ break;
+ case XML_ROLE_ENTITY_SYSTEM_ID:
+ {
+ const char *name = poolStoreString(&dtd->pool, *enc, entityNamePtr, entityNameEnd);
+ entity = (ENTITY *)lookup(entityIsParam ? &dtd->paramEntities : &dtd->generalEntities,
+ name, sizeof(ENTITY));
+ if (entity->name != name) {
+ poolDiscard(&dtd->pool);
+ entity = 0;
+ }
+ else {
+ poolFinish(&dtd->pool);
+ entity->systemId = poolStoreString(&dtd->pool, *enc,
+ s + (*enc)->minBytesPerChar,
+ next - (*enc)->minBytesPerChar);
+ poolFinish(&dtd->pool);
+ }
+ }
+ break;
+ case XML_ROLE_PARAM_ENTITY_REF:
+ {
+ const char *name = poolStoreString(&dtd->pool, *enc,
+ s + (*enc)->minBytesPerChar,
+ next - (*enc)->minBytesPerChar);
+ ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0);
+ poolDiscard(&dtd->pool);
+ if (!entity) {
+ if (!dtd->containsRef || dtd->standalone) {
+ *nextPtr = s;
+ return undefinedEntity;
+ }
+ }
+ }
+ break;
+ case XML_ROLE_ENTITY_NOTATION_NAME:
+ if (entity) {
+ entity->notation = poolStoreString(&dtd->pool, *enc, s, next);
+ poolFinish(&dtd->pool);
+ }
+ break;
+ case XML_ROLE_GENERAL_ENTITY_NAME:
+ entityNamePtr = s;
+ entityNameEnd = next;
+ entityIsParam = 0;
+ break;
+ case XML_ROLE_PARAM_ENTITY_NAME:
+ entityNamePtr = s;
+ entityNameEnd = next;
+ entityIsParam = 1;
+ break;
+ case XML_ROLE_ERROR:
+ *nextPtr = s;
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ return paramEntityRef;
+ case XML_TOK_INVALID:
+ *nextPtr = next;
+ return invalidToken;
+ case XML_TOK_NONE:
+ return noElements;
+ case XML_TOK_PARTIAL:
+ return unclosedToken;
+ case XML_TOK_PARTIAL_CHAR:
+ return partialChar;
+ case XML_TOK_TRAILING_CR:
+ *nextPtr = s + (*enc)->minBytesPerChar;
+ return noElements;
+ case XML_TOK_XML_DECL:
+ return misplacedXmlPi;
+ default:
+ return syntaxError;
+ }
+ case XML_ROLE_GROUP_OPEN:
+ if (state.level >= dtd->groupSize) {
+ if (dtd->groupSize)
+ dtd->groupConnector = realloc(dtd->groupConnector, dtd->groupSize *= 2);
+ else
+ dtd->groupConnector = malloc(dtd->groupSize = 32);
+ if (!dtd->groupConnector)
+ return noMemory;
+ }
+ dtd->groupConnector[state.level] = 0;
+ break;
+ case XML_ROLE_GROUP_SEQUENCE:
+ if (dtd->groupConnector[state.level] == '|') {
+ *nextPtr = s;
+ return syntaxError;
+ }
+ dtd->groupConnector[state.level] = ',';
+ break;
+ case XML_ROLE_GROUP_CHOICE:
+ if (dtd->groupConnector[state.level] == ',') {
+ *nextPtr = s;
+ return syntaxError;
+ }
+ dtd->groupConnector[state.level] = '|';
+ break;
+ case XML_ROLE_NONE:
+ if (tok == XML_TOK_PARAM_ENTITY_REF)
+ dtd->containsRef = 1;
+ break;
+ }
+ s = next;
+ }
+ /* not reached */
+}
+
+static enum WfCheckResult
+checkParsedEntities(CONTEXT *context, const char **badPtr)
+{
+ HASH_TABLE_ITER iter;
+ hashTableIterInit(&iter, &context->dtd.generalEntities);
+ for (;;) {
+ ENTITY *entity = (ENTITY *)hashTableIterNext(&iter);
+ if (!entity)
+ break;
+ if (entity->textPtr && !entity->wfInContent && !entity->magic) {
+ enum WfCheckResult result;
+ const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
+ entity->open = 1;
+ result = checkContent(1, context, internalEnc,
+ entity->textPtr, entity->textPtr + entity->textLen,
+ badPtr);
+ entity->open = 0;
+ if (result && *badPtr) {
+ *badPtr = entity->docTextPtr;
+ return result;
+ }
+ entity->wfInContent = 1;
+ }
+ }
+ return wellFormed;
+}
+
+static enum WfCheckResult
+checkGeneralTextEntity(CONTEXT *context,
+ const char *s, const char *end,
+ const char **nextPtr,
+ const ENCODING **enc)
+{
+ INIT_ENCODING initEnc;
+ const char *next;
+ int tok;
+
+ XmlInitEncoding(&initEnc, enc);
+ tok = XmlContentTok(*enc, s, end, &next);
+
+ if (tok == XML_TOK_BOM) {
+ s = next;
+ tok = XmlContentTok(*enc, s, end, &next);
+ }
+ if (tok == XML_TOK_XML_DECL) {
+ const char *encodingName = 0;
+ const ENCODING *encoding = 0;
+ const char *version;
+ if (!XmlParseXmlDecl(1,
+ *enc,
+ s,
+ next,
+ nextPtr,
+ &version,
+ &encodingName,
+ &encoding,
+ 0))
+ return syntaxError;
+ if (encoding) {
+ if (encoding->minBytesPerChar != (*enc)->minBytesPerChar) {
+ *nextPtr = encodingName;
+ return incorrectEncoding;
+ }
+ *enc = encoding;
+ }
+ else if (encodingName) {
+ *nextPtr = encodingName;
+ return unknownEncoding;
+ }
+ s = next;
+ }
+ context->dtd.containsRef = 1;
+ return checkContent(1, context, *enc, s, end, nextPtr);
+}
+
+static enum WfCheckResult
+checkAttributeValue(DTD *dtd, const ENCODING *enc,
+ const char *ptr, const char *end, const char **badPtr)
+{
+ for (;;) {
+ const char *next;
+ int tok = XmlAttributeValueTok(enc, ptr, end, &next);
+ switch (tok) {
+ case XML_TOK_TRAILING_CR:
+ case XML_TOK_NONE:
+ return wellFormed;
+ case XML_TOK_INVALID:
+ *badPtr = next;
+ return invalidToken;
+ case XML_TOK_PARTIAL:
+ *badPtr = ptr;
+ return invalidToken;
+ case XML_TOK_CHAR_REF:
+ if (XmlCharRefNumber(enc, ptr) < 0) {
+ *badPtr = ptr;
+ return badCharRef;
+ }
+ break;
+ case XML_TOK_DATA_CHARS:
+ case XML_TOK_DATA_NEWLINE:
+ break;
+ case XML_TOK_ENTITY_REF:
+ {
+ const char *name = poolStoreString(&dtd->pool, enc,
+ ptr + enc->minBytesPerChar,
+ next - enc->minBytesPerChar);
+ ENTITY *entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0);
+ poolDiscard(&dtd->pool);
+ if (!entity) {
+ if (!dtd->containsRef) {
+ *badPtr = ptr;
+ return undefinedEntity;
+ }
+ break;
+ }
+ if (entity->wfInAttribute)
+ break;
+ if (entity->open) {
+ *badPtr = ptr;
+ return recursiveEntityRef;
+ }
+ if (entity->notation) {
+ *badPtr = ptr;
+ return binaryEntityRef;
+ }
+ if (entity) {
+ if (entity->textPtr) {
+ enum WfCheckResult result;
+ const ENCODING *internalEnc = XmlGetInternalEncoding(XML_UTF8_ENCODING);
+ const char *textEnd = entity->textPtr + entity->textLen;
+ entity->open = 1;
+ result = checkAttributeValue(dtd, internalEnc, entity->textPtr, textEnd, badPtr);
+ entity->open = 0;
+ if (result && *badPtr) {
+ *badPtr = ptr;
+ return result;
+ }
+ entity->wfInAttribute = 1;
+ }
+ else {
+ *badPtr = ptr;
+ return attributeExternalEntityRef;
+ }
+ }
+ break;
+ }
+ break;
+ default:
+ abort();
+ }
+ ptr = next;
+ }
+ /* not reached */
+}
+
+static
+void poolInit(STRING_POOL *pool)
+{
+ pool->blocks = 0;
+ pool->start = 0;
+ pool->ptr = 0;
+ pool->end = 0;
+}
+
+static
+void poolDestroy(STRING_POOL *pool)
+{
+ BLOCK *p = pool->blocks;
+ while (p) {
+ BLOCK *tem = p->next;
+ free(p);
+ p = tem;
+ }
+ pool->blocks = 0;
+ pool->ptr = 0;
+ pool->start = 0;
+ pool->end = 0;
+}
+
+static
+const char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ for (;;) {
+ XmlConvert(enc, XML_UTF8_ENCODING, &ptr, end, &(pool->ptr), pool->end);
+ if (ptr == end)
+ break;
+ if (!poolGrow(pool))
+ return 0;
+ }
+ return pool->start;
+}
+
+static
+const char *poolStoreString(STRING_POOL *pool, const ENCODING *enc,
+ const char *ptr, const char *end)
+{
+ if (!poolAppend(pool, enc, ptr, end))
+ return 0;
+ if (pool->ptr == pool->end && !poolGrow(pool))
+ return 0;
+ *(pool->ptr)++ = 0;
+ return pool->start;
+}
+
+static
+int poolGrow(STRING_POOL *pool)
+{
+ if (pool->blocks && pool->start == pool->blocks->s) {
+ size_t blockSize = (pool->end - pool->start)*2;
+ pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize);
+ if (!pool->blocks)
+ return 0;
+ pool->ptr = pool->blocks->s + (pool->ptr - pool->start);
+ pool->start = pool->blocks->s;
+ pool->end = pool->start + blockSize;
+ }
+ else {
+ BLOCK *tem;
+ size_t blockSize = pool->end - pool->start;
+ if (blockSize < INIT_BLOCK_SIZE)
+ blockSize = INIT_BLOCK_SIZE;
+ else
+ blockSize *= 2;
+ tem = malloc(offsetof(BLOCK, s) + blockSize);
+ if (!tem)
+ return 0;
+ tem->next = pool->blocks;
+ pool->blocks = tem;
+ memcpy(tem->s, pool->start, pool->ptr - pool->start);
+ pool->ptr = tem->s + (pool->ptr - pool->start);
+ pool->start = tem->s;
+ pool->end = tem->s + blockSize;
+ }
+ return 1;
+}
+
+static int dtdInit(DTD *dtd)
+{
+ static const char *names[] = { "lt", "amp", "gt", "quot", "apos" };
+ static const char chars[] = { '<', '&', '>', '"', '\'' };
+ int i;
+
+ poolInit(&(dtd->pool));
+ hashTableInit(&(dtd->generalEntities));
+ for (i = 0; i < 5; i++) {
+ ENTITY *entity = (ENTITY *)lookup(&(dtd->generalEntities), names[i], sizeof(ENTITY));
+ if (!entity)
+ return 0;
+ entity->textPtr = chars + i;
+ entity->textLen = 1;
+ entity->magic = 1;
+ entity->wfInContent = 1;
+ entity->wfInAttribute = 1;
+ }
+ hashTableInit(&(dtd->paramEntities));
+ dtd->containsRef = 0;
+ dtd->groupSize = 0;
+ dtd->groupConnector = 0;
+ return 1;
+}
+
+static void dtdDestroy(DTD *dtd)
+{
+ poolDestroy(&(dtd->pool));
+ hashTableDestroy(&(dtd->generalEntities));
+ hashTableDestroy(&(dtd->paramEntities));
+ free(dtd->groupConnector);
+}
+
+static
+enum WfCheckResult storeEntity(DTD *dtd,
+ const ENCODING *enc,
+ int isParam,
+ const char *entityNamePtr,
+ const char *entityNameEnd,
+ const char *entityTextPtr,
+ const char *entityTextEnd,
+ const char **badPtr)
+{
+ ENTITY *entity;
+ const ENCODING *utf8 = XmlGetInternalEncoding(XML_UTF8_ENCODING);
+ STRING_POOL *pool = &(dtd->pool);
+ if (!poolStoreString(pool, enc, entityNamePtr, entityNameEnd))
+ return noMemory;
+ entity = (ENTITY *)lookup(isParam ? &(dtd->paramEntities) : &(dtd->generalEntities),
+ pool->start,
+ sizeof(ENTITY));
+ if (entity->name != pool->start) {
+ poolDiscard(pool);
+ entityNamePtr = 0;
+ }
+ else
+ poolFinish(pool);
+ entityTextPtr += enc->minBytesPerChar;
+ entityTextEnd -= enc->minBytesPerChar;
+ entity->docTextPtr = entityTextPtr;
+ for (;;) {
+ const char *next;
+ int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
+ switch (tok) {
+ case XML_TOK_PARAM_ENTITY_REF:
+ *badPtr = entityTextPtr;
+ return syntaxError;
+ case XML_TOK_NONE:
+ if (entityNamePtr) {
+ entity->textPtr = pool->start;
+ entity->textLen = pool->ptr - pool->start;
+ poolFinish(pool);
+ }
+ else
+ poolDiscard(pool);
+ return wellFormed;
+ case XML_TOK_ENTITY_REF:
+ case XML_TOK_DATA_CHARS:
+ if (!poolAppend(pool, enc, entityTextPtr, next))
+ return noMemory;
+ break;
+ case XML_TOK_TRAILING_CR:
+ next = entityTextPtr + enc->minBytesPerChar;
+ /* fall through */
+ case XML_TOK_DATA_NEWLINE:
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return noMemory;
+ *(pool->ptr)++ = '\n';
+ break;
+ case XML_TOK_CHAR_REF:
+ {
+ char buf[XML_MAX_BYTES_PER_CHAR];
+ int i;
+ int n = XmlCharRefNumber(enc, entityTextPtr);
+ if (n < 0) {
+ *badPtr = entityTextPtr;
+ return badCharRef;
+ }
+ n = XmlEncode(utf8, n, buf);
+ if (!n) {
+ *badPtr = entityTextPtr;
+ return badCharRef;
+ }
+ for (i = 0; i < n; i++) {
+ if (pool->end == pool->ptr && !poolGrow(pool))
+ return noMemory;
+ *(pool->ptr)++ = buf[i];
+ }
+ }
+ break;
+ case XML_TOK_PARTIAL:
+ *badPtr = entityTextPtr;
+ return invalidToken;
+ case XML_TOK_INVALID:
+ *badPtr = next;
+ return invalidToken;
+ default:
+ abort();
+ }
+ entityTextPtr = next;
+ }
+ /* not reached */
+}