blob: c18abd290bd0040f1b4b649a42e7e3d44e0b6107 [file] [log] [blame]
#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 */
}