xlnt/third-party/libstudxml/xml/details/genx/genx.c

2503 lines
56 KiB
C
Raw Normal View History

/*
* Copyright (c) 2007-2017 Code Synthesis Tools CC.
* Copyright (c) 2004 by Tim Bray and Sun Microsystems.
*
* For copying permission, see the accompanying COPYING file.
*/
#define GENX_VERSION "cs-1"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xml/details/genx/genx.h>
#define Boolean int
#define True 1
#define False 0
#define STRLEN_XMLNS_COLON 6
/*******************************
* writer state
*/
typedef enum
{
SEQUENCE_NO_DOC,
SEQUENCE_PRE_DOC,
SEQUENCE_POST_DOC,
SEQUENCE_START_TAG,
SEQUENCE_ATTRIBUTES,
SEQUENCE_START_ATTR,
SEQUENCE_CONTENT
} writerSequence;
/*******************************
* generic pointer list
*/
typedef struct
{
genxWriter writer;
size_t count;
size_t space;
void * * pointers;
} plist;
/*******************************
* text collector, for attribute values
*/
typedef struct
{
utf8 buf;
size_t used;
size_t space;
} collector;
/*******************************
* Structs with opaquely-exposed handles
*/
/*
* This one's tricky, to handle stacking namespaces
* 'declaration' is the current attribute which would be used to
* declare the currently-effective prefix
* 'defDeclaration' is a appropriate declaration when this is being
* used with the default prefix as passed to genxDeclareNamespace
* baroque is true if this namespace has been used with more than one
* prefix, or is the default namespace but has been unset
*/
struct genxNamespace_rec
{
genxWriter writer;
utf8 name;
size_t declCount;
Boolean baroque;
genxAttribute declaration;
genxAttribute defaultDecl;
};
struct genxElement_rec
{
genxWriter writer;
utf8 name;
genxNamespace ns;
};
typedef enum
{
ATTR_NSDECL,
ATTR_NAKED,
ATTR_PREFIXED
} attrType;
struct genxAttribute_rec
{
genxWriter writer;
utf8 name;
genxNamespace ns;
collector value;
int provided; /* provided for current element? */
attrType atype;
genxAttribute next; /* Attribute order chain if not canonical. */
};
/*******************************
* genx's sandbox
*/
struct genxWriter_rec
{
genxSender * sender;
genxStatus status;
writerSequence sequence;
char xmlChars[GENX_CHAR_TABLE_SIZE];
void * userData;
int nextPrefix;
utf8 empty;
Boolean defaultNsDeclared;
genxAttribute xmlnsEquals;
genxElement nowStarting;
genxAttribute nowStartingAttr;
plist namespaces;
plist elements;
plist attributes;
plist prefixes;
plist stack;
struct genxAttribute_rec arec; /* Dummy attribute used for lookup. */
char * etext[100];
genxAlloc alloc;
genxDealloc dealloc;
/* Pretty-printing state */
int ppIndent;
int ppDepth;
int ppSuspendDepth; /* Non-0 means we are suspended. */
Boolean ppSimple;
/* Canonicalization. */
Boolean canonical;
/* Attrbute order when not canonical. */
genxAttribute firstAttribute;
genxAttribute lastAttribute;
};
/*******************************
* Forward declarations
*/
static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
constUtf8 name, constUtf8 valuestr,
genxStatus * statusP);
static genxStatus addNamespace(genxNamespace ns, constUtf8 prefix);
static genxStatus unsetDefaultNamespace(genxWriter w);
static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr);
void genxSetCharProps(char * p);
/*******************************
* End of declarations
*/
/*******************************
* private memory utilities
*/
static void * allocate(genxWriter w, size_t bytes)
{
if (w->alloc)
return (void *) (*w->alloc)(w->userData, bytes);
else
return (void *) malloc(bytes);
}
static void deallocate(genxWriter w, void * data)
{
if (w->dealloc)
(*w->dealloc)(w->userData, data);
else if (w->alloc == NULL)
free(data);
}
static utf8 copy(genxWriter w, constUtf8 from)
{
utf8 temp;
if ((temp = (utf8) allocate(w, strlen((const char *) from) + 1)) == NULL)
return NULL;
strcpy((char *) temp, (const char *) from);
return temp;
}
static genxStatus initCollector(genxWriter w, collector * c)
{
c->space = 100;
if ((c->buf = (utf8) allocate(w, c->space)) == NULL)
return GENX_ALLOC_FAILED;
c->used = 0;
return GENX_SUCCESS;
}
static genxStatus growCollector(genxWriter w, collector * c, size_t size)
{
utf8 newSpace;
c->space = size * 2;
if ((newSpace = (utf8) allocate(w, c->space)) == NULL)
return GENX_ALLOC_FAILED;
strncpy((char *) newSpace, (const char *) c->buf, c->used);
newSpace[c->used] = 0;
deallocate(w, c->buf);
c->buf = newSpace;
return GENX_SUCCESS;
}
static void startCollect(collector * c)
{
c->used = 0;
}
static void endCollect(collector * c)
{
c->buf[c->used] = 0;
}
static genxStatus collectString(genxWriter w, collector * c, constUtf8 string)
{
size_t sl = strlen((const char *) string);
if (sl >= c->space)
if ((w->status = growCollector(w, c, sl)) != GENX_SUCCESS)
return GENX_ALLOC_FAILED;
strcpy((char *) c->buf, (const char *) string);
return GENX_SUCCESS;
}
#define collectPiece(w,c,d,size) {if (((c)->used+(size))>=(c)->space){if (((w)->status=growCollector(w,c,(c)->used+(size)))!=GENX_SUCCESS) return (w)->status;}strncpy((char *)(c)->buf+(c)->used,d,size);(c)->used+=size;}
/*******************************
* private list utilities
*/
static genxStatus initPlist(genxWriter w, plist * pl)
{
pl->writer = w;
pl->count = 0;
pl->space = 10;
pl->pointers = (void * *) allocate(w, pl->space * sizeof(void *));
if (pl->pointers == NULL)
return GENX_ALLOC_FAILED;
return GENX_SUCCESS;
}
/*
* make room in a plist
*/
static Boolean checkExpand(plist * pl)
{
void * * newlist;
size_t i;
if (pl->count < pl->space)
return True;
pl->space *= 2;
newlist = (void * *) allocate(pl->writer, pl->space * sizeof(void *));
if (newlist == NULL)
return False;
for (i = 0; i < pl->count; i++)
newlist[i] = pl->pointers[i];
deallocate(pl->writer, pl->pointers);
pl->pointers = newlist;
return True;
}
/*
* stick something on the end of a plist
*/
static genxStatus listAppend(plist * pl, void * pointer)
{
if (!checkExpand(pl))
return GENX_ALLOC_FAILED;
pl->pointers[pl->count++] = pointer;
return GENX_SUCCESS;
}
/*
* insert in place, shuffling up
*/
static genxStatus listInsert(plist * pl, void * pointer, size_t at)
{
size_t i;
if (!checkExpand(pl))
return GENX_ALLOC_FAILED;
for (i = pl->count; i > at; i--)
pl->pointers[i] = pl->pointers[i - 1];
pl->count++;
pl->pointers[at] = pointer;
return GENX_SUCCESS;
}
/*******************************
* list lookups
*/
static genxNamespace findNamespace(plist * pl, constUtf8 uri)
{
size_t i;
genxNamespace * nn = (genxNamespace *) pl->pointers;
for (i = 0; i < pl->count; i++)
if (strcmp((char *) uri, (const char *) nn[i]->name) == 0)
return nn[i];
return NULL;
}
static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 name)
{
size_t i;
genxElement * ee = (genxElement *) pl->pointers;
for (i = 0; i < pl->count; i++)
{
if (xmlns == NULL)
{
if (ee[i]->ns == NULL && strcmp((const char *) name,
(const char *) ee[i]->name) == 0)
return ee[i];
}
else
{
if (ee[i]->ns != NULL &&
strcmp((const char *) xmlns, (const char *) ee[i]->ns->name) == 0 &&
strcmp((const char *) name, (const char *) ee[i]->name) == 0)
return ee[i];
}
}
return NULL;
}
/*
* store & intern a prefix, after giving it the
* "xmlns:" prefix. Don't allow storing the same one twice unless 'force'
* is set.
*/
static utf8 storePrefix(genxWriter w, constUtf8 prefix, Boolean force)
{
int high, low;
utf8 * pp = (utf8 *) w->prefixes.pointers;
unsigned char buf[1024];
if (prefix[0] == 0)
prefix = (utf8) "xmlns";
else
{
sprintf((char *) buf, "xmlns:%s", prefix);
prefix = buf;
}
high = (int) w->prefixes.count;
low = -1;
while (high - low > 1)
{
int probe = (high + low) / 2;
if (strcmp((const char *) prefix, (const char *) pp[probe]) < 0)
high = probe;
else
low = probe;
}
/* already there? */
if (low != -1 && strcmp((const char *) prefix, (const char *) pp[low]) == 0)
{
if (force)
return pp[low];
w->status = GENX_DUPLICATE_PREFIX;
return NULL;
}
/* copy & insert */
if ((prefix = copy(w, prefix)) == NULL)
{
w->status = GENX_ALLOC_FAILED;
return NULL;
}
w->status = listInsert(&w->prefixes, (void *) prefix, (size_t) high);
if (w->status != GENX_SUCCESS)
return NULL;
return (utf8) prefix;
}
/*******************************
* UTF8 bit-banging
*/
/*
* Retrieve the character pointed at, and advance the pointer; return -1 on
* error
*/
int genxNextUnicodeChar(constUtf8 * sp)
{
utf8 s = (utf8) *sp;
int c;
if (*s == 0)
return -1;
if (*s < 0x80)
c = *s++;
/* all this encoding sanity-checking taken from section 3.10 of Unicode 4 */
else if (*s < 0xc2)
goto malformed;
/* 2-byte encodings, first byte c2 .. df */
else if (*s < 0xe0)
{
c = (*s++ & 0x1f) << 6;
/*
* for this common idiom, if ((c & 0xc0) != 0x80) is slightly faster
* on MacOS (PPC)
*/
if (*s < 0x80 || *s > 0xbf)
goto malformed;
c |= *s++ & 0x3f;
}
/* 3-byte encodings, first byte e0 .. ef */
else if (*s < 0xf0)
{
int b0 = *s;
c = (*s++ & 0x0f) << 12;
if ((b0 == 0xe0 && (*s < 0xa0 || *s > 0xbf)) ||
(b0 < 0xed && (*s < 0x80 || *s > 0xbf)) ||
(b0 == 0xed && (*s < 0x80 || *s > 0x9f)) ||
(b0 > 0xed && (*s < 0x80 || *s > 0xbf)))
goto malformed;
c |= (*s++ & 0x3f) << 6;
if (*s < 0x80 || *s > 0xbf)
goto malformed;
c |= *s++ & 0x3f;
}
/* 4-byte encodings, first byte f0 .. f4 */
else if (*s < 0xf5)
{
int b0 = *s;
c = (*s++ & 0x07) << 18;
if ((b0 == 0xf0 && (*s < 0x90 || *s > 0xbf)) ||
(b0 < 0xf4 && (*s < 0x80 || *s > 0xbf)) ||
(b0 >= 0xf4 && (*s < 0x80 || *s > 0x8f)))
goto malformed;
c |= (*s++ & 0x3f) << 12;
if (*s < 0x80 || *s > 0xbf)
goto malformed;
c |= (*s++ & 0x3f) << 6;
if (*s < 0x80 || *s > 0xbf)
goto malformed;
c |= *s++ & 0x3f;
}
else
goto malformed;
*sp = s;
return c;
/*
* this is needed by scrubText, which wants to get the pointer moved
* past the problem area.
*/
malformed:
if (*s)
++s;
*sp = s;
return -1;
}
static Boolean isXMLChar(genxWriter w, int c)
{
if (c < 0)
return False;
else if (c < GENX_CHAR_TABLE_SIZE)
return (int) w->xmlChars[c];
else
return (c <= 0x10ffff);
}
static Boolean isLetter(genxWriter w, int c)
{
if (c < 0 || c > 0xffff)
return False;
else
{
#if GENX_CHAR_TABLE_SIZE == 0x10000
return w->xmlChars[c] & GENX_LETTER;
#else
return c < GENX_CHAR_TABLE_SIZE ? (w->xmlChars[c] & GENX_LETTER) : True;
#endif
}
}
static Boolean isNameChar(genxWriter w, int c)
{
if (c < 0 || c > 0xffff)
return False;
else
{
#if GENX_CHAR_TABLE_SIZE == 0x10000
return w->xmlChars[c] & GENX_NAMECHAR;
#else
return c < GENX_CHAR_TABLE_SIZE ? (w->xmlChars[c] & GENX_NAMECHAR) : True;
#endif
}
}
/*******************************
* Constructors, setters/getters
*/
/*
* Construct a new genxWriter
*/
genxWriter genxNew(genxAlloc alloc, genxDealloc dealloc, void * userData)
{
genxWriter w;
genxNamespace xml;
if (alloc)
w = (genxWriter) (*alloc)(userData, sizeof(struct genxWriter_rec));
else
w = (genxWriter) malloc(sizeof(struct genxWriter_rec));
if (w == NULL)
return NULL;
w->status = GENX_SUCCESS;
w->alloc = alloc;
w->dealloc = dealloc;
w->userData = userData;
w->sequence = SEQUENCE_NO_DOC;
if (initPlist(w, &w->namespaces) != GENX_SUCCESS ||
initPlist(w, &w->elements) != GENX_SUCCESS ||
initPlist(w, &w->attributes) != GENX_SUCCESS ||
initPlist(w, &w->prefixes) != GENX_SUCCESS ||
initPlist(w, &w->stack) != GENX_SUCCESS)
return NULL;
if ((w->status = initCollector(w, &w->arec.value)) != GENX_SUCCESS)
return NULL;
if ((w->empty = copy(w, (utf8) "")) == NULL)
{
w->status = GENX_ALLOC_FAILED;
return NULL;
}
w->xmlnsEquals = declareAttribute(w, NULL, (utf8) "xmlns", NULL, &w->status);
if (w->xmlnsEquals == NULL || w->status != GENX_SUCCESS)
return NULL;
w->defaultNsDeclared = False;
w->nextPrefix = 1;
genxSetCharProps(w->xmlChars);
w->etext[GENX_SUCCESS] = "success";
w->etext[GENX_BAD_UTF8] = "invalid UTF-8";
w->etext[GENX_NON_XML_CHARACTER] = "non-XML character";
w->etext[GENX_BAD_NAME] = "invalid name";
w->etext[GENX_ALLOC_FAILED] = "memory allocation failed";
w->etext[GENX_BAD_NAMESPACE_NAME] = "invalid namespace name";
w->etext[GENX_INTERNAL_ERROR] = "internal error";
w->etext[GENX_DUPLICATE_PREFIX] = "duplicate prefix";
w->etext[GENX_SEQUENCE_ERROR] = "call out of sequence";
w->etext[GENX_NO_START_TAG] = "no start tag for end element call";
w->etext[GENX_IO_ERROR] = "io error";
w->etext[GENX_MISSING_VALUE] = "missing attribute value";
w->etext[GENX_MALFORMED_COMMENT] = "malformed comment body";
w->etext[GENX_MALFORMED_PI] = "?> in PI";
w->etext[GENX_XML_PI_TARGET] = "target of PI matches [xX][mM][lL]";
w->etext[GENX_DUPLICATE_ATTRIBUTE] = "duplicate attribute";
w->etext[GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE] =
"attribute is default namespace";
w->etext[GENX_DUPLICATE_NAMESPACE] =
"namespace declared twice with different prefixes";
w->etext[GENX_BAD_DEFAULT_DECLARATION] =
"default namespace declared on an element which is not in a namespace";
/* the xml: namespace is pre-wired */
xml = genxDeclareNamespace(w, (utf8) "http://www.w3.org/XML/1998/namespace",
(utf8) "xml", &w->status);
if (xml == NULL)
return NULL;
xml->declCount = 1;
xml->declaration = xml->defaultDecl;
w->ppIndent = 0; /* Pretty-printing is disabled by default. */
w->canonical = False; /* No canonicalization by default. */
w->firstAttribute = NULL;
w->lastAttribute = NULL;
return w;
}
genxStatus genxReset (genxWriter w)
{
size_t i;
/* Clean up the stack. */
w->stack.count = 0;
/* Reset namespace declaration counts. The first entry is the pre-wired
xml namespace. */
((genxNamespace) w->namespaces.pointers[0])->declCount = 1;
for (i = 1; i < w->namespaces.count; i++)
{
((genxNamespace) w->namespaces.pointers[i])->declCount = 0;
((genxNamespace) w->namespaces.pointers[i])->baroque = False;
}
/* Clear provided attributes. */
for (i = 0; i < w->attributes.count; i++)
((genxAttribute) w->attributes.pointers[i])->provided = False;
/* Clear attribute list. */
if (!w->canonical)
{
while (w->firstAttribute != NULL)
{
genxAttribute t = w->firstAttribute->next;
w->firstAttribute->next = NULL;
w->firstAttribute = t;
}
w->lastAttribute = NULL;
}
w->status = GENX_SUCCESS;
w->sequence = SEQUENCE_NO_DOC;
return w->status;
}
/*
* get/set userData
*/
void genxSetUserData(genxWriter w, void * userData)
{
w->userData = userData;
}
void * genxGetUserData(genxWriter w)
{
return w->userData;
}
/*
* get/set pretty-printing
*/
genxStatus genxSetPrettyPrint(genxWriter w, int ind)
{
if (w->sequence == SEQUENCE_NO_DOC)
w->ppIndent = ind;
else
w->status = GENX_SEQUENCE_ERROR;
return w->status;
}
int genxGetPrettyPrint(genxWriter w)
{
return w->ppIndent;
}
/*
* Suspend/resume pretty-printing.
*/
genxStatus genxSuspendPrettyPrint(genxWriter w)
{
int d = w->ppDepth;
if (w->ppIndent == 0)
return w->status = GENX_SEQUENCE_ERROR;
switch (w->sequence)
{
case SEQUENCE_START_TAG:
case SEQUENCE_ATTRIBUTES:
d++; /* No start tag written, still outer depth. */
case SEQUENCE_CONTENT:
break;
default:
return w->status = GENX_SEQUENCE_ERROR;
}
if (w->ppSuspendDepth == 0) /* Ignore nested suspend/resume calls. */
w->ppSuspendDepth = d;
return w->status;
}
genxStatus genxResumePrettyPrint(genxWriter w)
{
int d = w->ppDepth;
if (w->ppIndent == 0 || w->ppSuspendDepth == 0)
return w->status = GENX_SEQUENCE_ERROR;
switch (w->sequence)
{
case SEQUENCE_START_TAG:
case SEQUENCE_ATTRIBUTES:
d++; /* No start tag written, still outer depth. */
case SEQUENCE_CONTENT:
break;
default:
return w->status = GENX_SEQUENCE_ERROR;
}
if (w->ppSuspendDepth == d) /* Ignore nested suspend/resume calls. */
w->ppSuspendDepth = 0;
return w->status;
}
int genxPrettyPrintSuspended(genxWriter w)
{
return w->ppSuspendDepth;
}
/*
* get/set canonicalization.
*/
genxStatus genxSetCanonical(genxWriter w, int flag)
{
if (w->sequence == SEQUENCE_NO_DOC)
w->canonical = flag;
else
w->status = GENX_SEQUENCE_ERROR;
return w->status;
}
int genxGetCanonical(genxWriter w)
{
return w->canonical;
}
/*
* get/set allocator
*/
void genxSetAlloc(genxWriter w, genxAlloc alloc)
{
w->alloc = alloc;
}
void genxSetDealloc(genxWriter w, genxDealloc dealloc)
{
w->dealloc = dealloc;
}
genxAlloc genxGetAlloc(genxWriter w)
{
return w->alloc;
}
genxDealloc genxGetDealloc(genxWriter w)
{
return w->dealloc;
}
/*
* Clean up
*/
void genxDispose(genxWriter w)
{
size_t i;
genxNamespace * nn = (genxNamespace *) w->namespaces.pointers;
genxElement * ee = (genxElement *) w->elements.pointers;
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
utf8 * pp = (utf8 *) w->prefixes.pointers;
for (i = 0; i < w->namespaces.count; i++)
{
deallocate(w, nn[i]->name);
deallocate(w, nn[i]);
}
for (i = 0; i < w->elements.count; i++)
{
deallocate(w, ee[i]->name);
deallocate(w, ee[i]);
}
for (i = 0; i < w->attributes.count; i++)
{
deallocate(w, aa[i]->name);
deallocate(w, aa[i]->value.buf);
deallocate(w, aa[i]);
}
for(i = 0; i < w->prefixes.count; i++)
deallocate(w, pp[i]);
deallocate(w, w->namespaces.pointers);
deallocate(w, w->elements.pointers);
deallocate(w, w->attributes.pointers);
deallocate(w, w->prefixes.pointers);
deallocate(w, w->stack.pointers);
deallocate(w, w->arec.value.buf);
deallocate(w, w->empty);
/* how Oscar dealt with Igli */
deallocate(w, w);
}
/*******************************
* External utility routines
*/
/*
* scan a buffer and report problems with UTF-8 encoding or non-XML characters
*/
genxStatus genxCheckText(genxWriter w, constUtf8 s)
{
while (*s)
{
int c = genxNextUnicodeChar(&s);
if (c == -1)
return GENX_BAD_UTF8;
if (!isXMLChar(w, c))
return GENX_NON_XML_CHARACTER;
}
return GENX_SUCCESS;
}
/*
* Purify some text
*/
int genxScrubText(genxWriter w, constUtf8 in, utf8 out)
{
int problems = 0;
constUtf8 last = in;
while (*in)
{
int c = genxNextUnicodeChar(&in);
if (c == -1)
{
problems++;
last = in;
continue;
}
if (!isXMLChar(w, c))
{
problems++;
last = in;
continue;
}
while (last < in)
*out++ = *last++;
}
*out = 0;
return problems;
}
/*
* check one character
*/
int genxCharClass(genxWriter w, int c)
{
int ret = 0;
if (isXMLChar(w, c))
ret |= GENX_XML_CHAR;
if (isNameChar(w, c))
ret |= GENX_NAMECHAR;
if (isLetter(w, c))
ret |= GENX_LETTER;
return ret;
}
static genxStatus checkNCName(genxWriter w, constUtf8 name)
{
int c;
if (name == NULL || *name == 0)
return GENX_BAD_NAME;
c = genxNextUnicodeChar(&name);
if (!isLetter(w, c) && c != ':' && c != '_')
return GENX_BAD_NAME;
while (*name)
{
c = genxNextUnicodeChar(&name);
if (c == -1)
return GENX_BAD_UTF8;
if (!isNameChar(w, c))
return GENX_BAD_NAME;
}
return GENX_SUCCESS;
}
char * genxGetErrorMessage(genxWriter w, genxStatus status)
{
return w->etext[status];
}
char * genxLastErrorMessage(genxWriter w)
{
return w->etext[w->status];
}
/*******************************
* Declarations: namespace/element/attribute
*/
/*
* DeclareNamespace - by far the most complex routine in Genx
*/
genxNamespace genxDeclareNamespace(genxWriter w, constUtf8 uri,
constUtf8 defaultPref,
genxStatus * statusP)
{
genxNamespace ns;
genxAttribute defaultDecl;
unsigned char newPrefix[100];
if (uri == NULL || uri[0] == 0)
{
w->status = GENX_BAD_NAMESPACE_NAME;
goto busted;
}
if ((w->status = genxCheckText(w, uri)) != GENX_SUCCESS)
goto busted;
/* if a prefix is provided, it has to be an NCname */
if (defaultPref != NULL && defaultPref[0] != 0 &&
(w->status = checkNCName(w, defaultPref)) != GENX_SUCCESS)
goto busted;
/* previously declared? */
if ((ns = findNamespace(&w->namespaces, uri)))
{
/* just a lookup, really */
if ((defaultPref == NULL) ||
(defaultPref[0] == 0 && ns->defaultDecl == w->xmlnsEquals) ||
(strcmp((const char *) ns->defaultDecl->name + STRLEN_XMLNS_COLON,
(const char *) defaultPref) == 0))
{
w->status = *statusP = GENX_SUCCESS;
return ns;
}
}
/* wasn't already declared */
else
{
/* make a default prefix if none provided */
if (defaultPref == NULL)
{
sprintf((char *) newPrefix, "g%d", w->nextPrefix++);
defaultPref = newPrefix;
}
ns = (genxNamespace) allocate(w, sizeof(struct genxNamespace_rec));
if (ns == NULL)
{
w->status = GENX_ALLOC_FAILED;
goto busted;
}
ns->writer = w;
ns->baroque = False;
if ((ns->name = copy(w, uri)) == NULL)
{
w->status = GENX_ALLOC_FAILED;
goto busted;
}
if ((w->status = listAppend(&w->namespaces, ns)) != GENX_SUCCESS)
goto busted;
ns->defaultDecl = ns->declaration = NULL;
ns->declCount = 0;
}
if (defaultPref[0] == 0)
{
if (w->defaultNsDeclared)
{
w->status = GENX_DUPLICATE_PREFIX;
goto busted;
}
defaultDecl = w->xmlnsEquals;
w->defaultNsDeclared = True;
}
else
{
/* this catches dupes too */
if ((defaultPref = storePrefix(w, defaultPref, False)) == NULL)
goto busted;
defaultDecl = declareAttribute(w, NULL, defaultPref, ns->name, statusP);
if (defaultDecl == NULL || *statusP != GENX_SUCCESS)
{
w->status = *statusP;
return NULL;
}
}
if (ns->defaultDecl != NULL && defaultDecl != ns->defaultDecl)
ns->baroque = True;
ns->defaultDecl = defaultDecl;
*statusP = GENX_SUCCESS;
return ns;
busted:
*statusP = w->status;
return NULL;
}
/*
* get namespace prefix
*/
utf8 genxGetNamespacePrefix(genxNamespace ns)
{
if (ns->declaration == NULL)
return NULL;
if (ns->declaration == ns->writer->xmlnsEquals)
return ns->writer->empty;
return ns->declaration->name + STRLEN_XMLNS_COLON;
}
/*
* DeclareElement - see genx.h for details
*/
genxElement genxDeclareElement(genxWriter w,
genxNamespace ns, constUtf8 name,
genxStatus * statusP)
{
genxElement old;
genxElement el;
if ((w->status = checkNCName(w, name)) != GENX_SUCCESS)
{
*statusP = w->status;
return NULL;
}
/* already declared? */
old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, name);
if (old)
return old;
if ((el = (genxElement) allocate(w, sizeof(struct genxElement_rec))) == NULL)
{
w->status = *statusP = GENX_ALLOC_FAILED;
return NULL;
}
el->writer = w;
el->ns = ns;
if ((el->name = copy(w, name)) == NULL)
{
w->status = *statusP = GENX_ALLOC_FAILED;
return NULL;
}
if ((w->status = listAppend(&w->elements, el)) != GENX_SUCCESS)
{
*statusP = w->status;
return NULL;
}
*statusP = GENX_SUCCESS;
return el;
}
/*
* C14n ordering for attributes:
* - first, namespace declarations by the prefix being declared
* - second, unprefixed attributes by attr name
* - third, prefixed attrs by ns uri then local part
*/
static int orderAttributes(genxAttribute a1, genxAttribute a2)
{
if (a1->atype == a2->atype)
{
if (a1->atype == ATTR_PREFIXED && a1->ns != a2->ns)
return strcmp((const char *) a1->ns->name, (const char *) a2->ns->name);
else
return strcmp((const char *) a1->name, (const char *) a2->name);
}
else if (a1->atype == ATTR_NSDECL)
return -1;
else if (a1->atype == ATTR_NAKED)
{
if (a2->atype == ATTR_NSDECL)
return 1;
else
return -1;
}
else
return 1;
}
/*
* internal declare-attribute. This one allows colonized values for
* names, so that you can declare xmlns:-type attributes
*/
static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
constUtf8 name, constUtf8 valuestr,
genxStatus * statusP)
{
int high, low;
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
genxAttribute a;
w->arec.ns = ns;
w->arec.name = (utf8) name;
if (ns)
w->arec.atype = ATTR_PREFIXED;
else if (strncmp((const char *) name, "xmlns", STRLEN_XMLNS_COLON - 1) == 0)
w->arec.atype = ATTR_NSDECL;
else
w->arec.atype = ATTR_NAKED;
if (ns && (ns->defaultDecl == w->xmlnsEquals))
{
w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
goto busted;
}
/* attribute list has to be kept sorted per c14n rules */
high = (int) w->attributes.count;
low = -1;
while (high - low > 1)
{
int probe = (high + low) / 2;
if (orderAttributes(&w->arec, aa[probe]) < 0)
high = probe;
else
low = probe;
}
/* if it was already there */
if (low != -1 && orderAttributes(&w->arec, aa[low]) == 0)
return aa[low];
/* not there, build it */
a = (genxAttribute) allocate(w, sizeof(struct genxAttribute_rec));
if (a == NULL)
{
w->status = GENX_ALLOC_FAILED;
goto busted;
}
a->writer = w;
a->ns = ns;
a->provided = False;
a->atype = w->arec.atype;
a->next = NULL;
if ((a->name = copy(w, name)) == NULL)
{
w->status = GENX_ALLOC_FAILED;
goto busted;
}
if ((w->status = initCollector(w, &a->value)) != GENX_SUCCESS)
goto busted;
if (valuestr)
if ((w->status = collectString(w, &a->value, valuestr)) != GENX_SUCCESS)
goto busted;
w->status = listInsert(&w->attributes, a, (size_t) high);
if (w->status != GENX_SUCCESS)
goto busted;
*statusP = GENX_SUCCESS;
return a;
busted:
*statusP = w->status;
return NULL;
}
/*
* genxDeclareAttribute - see genx.h for details
*/
genxAttribute genxDeclareAttribute(genxWriter w,
genxNamespace ns, constUtf8 name,
genxStatus * statusP)
{
if ((w->status = checkNCName(w, name)) != GENX_SUCCESS)
{
*statusP = w->status;
return NULL;
}
return declareAttribute(w, ns, name, NULL, statusP);
}
/*******************************
* I/O
*/
static genxStatus sendx(genxWriter w, constUtf8 s)
{
if (w->sender)
return (*w->sender->send)(w->userData, s);
else
return GENX_IO_ERROR;
}
static genxStatus sendxBounded(genxWriter w, constUtf8 start, constUtf8 end)
{
if (w->sender)
return (*w->sender->sendBounded)(w->userData, start, end);
else
return GENX_IO_ERROR;
}
#define SendCheck(w,s) if ((w->status=sendx(w,(constUtf8)s))!=GENX_SUCCESS) return w->status
/*******************************
* XML writing routines. The semantics of the externally-facing ones are
* written up in genx.h. Commentary here is implementation notes and
* for internal routines.
*/
genxStatus genxStartDocSender(genxWriter w, genxSender * sender)
{
if (w->sequence != SEQUENCE_NO_DOC)
return w->status = GENX_SEQUENCE_ERROR;
w->sequence = SEQUENCE_PRE_DOC;
w->sender = sender;
if (w->ppIndent)
{
w->ppDepth = 0;
w->ppSuspendDepth = 0;
w->ppSimple = True;
}
return GENX_SUCCESS;
}
/*
* Output new line and indentation.
*/
static genxStatus writeIndentation(genxWriter w)
{
int i, n;
SendCheck(w, "\n");
n = w->ppDepth * w->ppIndent;
for (i = 0; i < n; i++)
SendCheck(w, " ");
return w->status;
}
/*
* Output attribute.
*/
static genxStatus writeAttribute(genxAttribute a)
{
genxWriter w = a->writer;
if (a->ns && a->ns->baroque && a->ns->declaration == w->xmlnsEquals)
return w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
SendCheck(w, " ");
if (a->ns)
{
SendCheck(w, a->ns->declaration->name + STRLEN_XMLNS_COLON);
SendCheck(w, ":");
}
SendCheck(w, a->name);
SendCheck(w, "=\"");
SendCheck(w, a->value.buf);
SendCheck(w, "\"");
return w->status;
}
/*
* Write out the attributes we've been gathering up for an element. We save
* them until we've gathered them all so they can be writen in canonical
* order.
* Also, we end the start-tag.
* The trick here is that we keep the attribute list properly sorted as
* we build it, then as each attribute is added, we fill in its value and
* mark the fact that it's been added, in the "provided" field.
*/
static genxStatus writeStartTag(genxWriter w, Boolean close)
{
size_t i;
genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
genxElement e = w->nowStarting;
/*
* make sure the right namespace decls are in effect;
* if they are these might create an error, so ignore it
*/
if (e->ns)
addNamespace(e->ns, NULL);
else
unsetDefaultNamespace(w);
w->status = GENX_SUCCESS;
if (w->ppIndent)
{
if (w->ppDepth &&
/* Suspend depth could be at this element's depth (after ++ below). */
(w->ppSuspendDepth == 0 || w->ppSuspendDepth > w->ppDepth))
if (writeIndentation (w) != GENX_SUCCESS)
return w->status;
if (!close)
{
w->ppDepth++;
w->ppSimple = True;
}
else
{
/*
* Conceptually we incremented/decremented the depth, so check if we
* need to resume pretty-printing.
*/
if (w->ppSuspendDepth > w->ppDepth)
w->ppSuspendDepth = 0;
}
}
SendCheck(w, "<");
if (e->ns && (e->ns->declaration != w->xmlnsEquals))
{
SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
SendCheck(w, ":");
}
SendCheck(w, e->name);
/* If we are canonicalizing, then write sorted attributes. Otherwise
write them in the order specified. */
if (w->canonical)
{
for (i = 0; i < w->attributes.count; i++)
{
if (aa[i]->provided)
{
if (writeAttribute (aa[i]) != GENX_SUCCESS)
return w->status;
aa[i]->provided = False;
}
}
}
else
{
/* Keep the chain consistent even if we bail out mid way because of
an error. This way we will still be able to clear it in reset().*/
while (w->firstAttribute != NULL)
{
genxAttribute t = w->firstAttribute->next;
if (writeAttribute (w->firstAttribute) != GENX_SUCCESS)
return w->status;
w->firstAttribute->provided = False;
w->firstAttribute->next = NULL;
w->firstAttribute = t;
}
w->lastAttribute = NULL;
}
if (close)
SendCheck(w, "/");
SendCheck(w, ">");
return GENX_SUCCESS;
}
/*
* internal clear-er; no sequence checking
*/
static genxStatus unsetDefaultNamespace(genxWriter w)
{
int i;
Boolean found = False;
/* don't put it in if not needed */
i = (int) (w->stack.count) - 1;
while (found == False && i > 0)
{
while (w->stack.pointers[i] != NULL)
{
genxAttribute decl = (genxAttribute) w->stack.pointers[i--];
genxNamespace ns = (genxNamespace) w->stack.pointers[i--];
/* if already unset */
if (ns == NULL)
return w->status = GENX_SUCCESS;
/*
* the default namespace was declared. This namespace now
* becomes baroque
*/
if (decl == w->xmlnsEquals)
{
ns->baroque = True;
found = True;
break;
}
}
i -= 2;
}
if (!found)
return GENX_SUCCESS;
/*
* push a signal on the stack
*/
if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
return w->status;
w->status = listAppend(&w->stack, w->xmlnsEquals);
if (w->status != GENX_SUCCESS)
return w->status;
/* add the xmlns= attribute, it must be the first one */
return addAttribute(w->xmlnsEquals, w->empty);
}
/*
* clear the default namespace declaration
*/
genxStatus genxUnsetDefaultNamespace(genxWriter w)
{
/* can only do this while in start-tag mode */
if (w->sequence != SEQUENCE_START_TAG)
return w->status = GENX_SEQUENCE_ERROR;
return unsetDefaultNamespace(w);
}
genxStatus genxStartElement(genxElement e)
{
genxWriter w = e->writer;
switch (w->sequence)
{
case SEQUENCE_NO_DOC:
case SEQUENCE_POST_DOC:
case SEQUENCE_START_ATTR:
return w->status = GENX_SEQUENCE_ERROR;
case SEQUENCE_START_TAG:
case SEQUENCE_ATTRIBUTES:
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
break;
case SEQUENCE_PRE_DOC:
case SEQUENCE_CONTENT:
break;
}
w->sequence = SEQUENCE_START_TAG;
/*
* push the stack. We push a NULL after a pointer to this element
* because the stack will also contain pointers to the namespace
* attributes that got declared here, so we can keep track of what's
* in effect. I.e. a single stack entry consists logically of a pointer
* to an element object, a NULL, then zero or more pairs of pointers to
* namespace objects/declarations
*/
if ((w->status = listAppend(&w->stack, e)) != GENX_SUCCESS)
return w->status;
if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
return w->status;
w->nowStarting = e;
return GENX_SUCCESS;
}
/*
* internal namespace adder; no sequence checking
*/
static genxStatus addNamespace(genxNamespace ns, constUtf8 prefix)
{
genxWriter w = ns->writer;
genxAttribute decl;
int i;
genxElement e;
/*
* first, we'll find the declaring attribute
*/
if (prefix == NULL)
decl = ns->defaultDecl;
else
{
if (prefix[0] == 0)
decl = w->xmlnsEquals;
else
{
if ((prefix = storePrefix(w, prefix, True)) == NULL)
return w->status;
decl = declareAttribute(w, NULL, prefix, ns->name, &w->status);
if (decl == NULL || w->status != GENX_SUCCESS)
return w->status;
}
}
if (decl != ns->defaultDecl)
ns->baroque = True;
/*
* avoid doing anything if this namespace is already declared. If
* they've shown good taste we can do this cheaply
*/
if (!ns->baroque)
{
if (ns->declCount > 0)
return w->status = GENX_SUCCESS;
}
else
{
/*
* First, we'll run all the way up the stack to see if there is
* another declaration for this namespace/prefix in scope, in which
* case it's a no-op; or, if there's another declaration for this
* prefix on another namespace, in which case we have to over-ride
*/
i = (int) (w->stack.count) - 1;
while (i > 0)
{
while (w->stack.pointers[i] != NULL)
{
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
if (ns == otherNs)
{
if (decl == otherDecl)
return w->status = GENX_SUCCESS;
else
{
i = 0;
break;
}
}
else
{
/* different namespace, same prefix? */
if (decl == otherDecl)
{
i = 0;
break;
}
}
}
i -= 2;
}
}
/*
* If this namespace is already declared on
* this element (with different prefix/decl) which is an error.
*/
i = (int) (w->stack.count) - 1;
while (w->stack.pointers[i] != NULL)
{
genxNamespace otherNs;
i--; /* don't need declaration */
otherNs = (genxNamespace) w->stack.pointers[i--];
if (ns == otherNs)
return w->status = GENX_DUPLICATE_NAMESPACE;
}
/* move pointer from NULL to element */
--i;
/*
* It's also an error if this is a default-namespace declaration and the
* element is in no namespace.
*/
e = (genxElement) w->stack.pointers[i];
if (e->ns == NULL && decl == w->xmlnsEquals)
return w->status = GENX_BAD_DEFAULT_DECLARATION;
if ((w->status = listAppend(&w->stack, ns)) != GENX_SUCCESS)
return w->status;
if ((w->status = listAppend(&w->stack, decl)) != GENX_SUCCESS)
return w->status;
ns->declaration = decl;
ns->declCount++;
return addAttribute(decl, ns->name);
}
/*
* Add a namespace declaration
*/
genxStatus genxAddNamespace(genxNamespace ns, constUtf8 prefix)
{
if (ns->writer->sequence != SEQUENCE_START_TAG)
return ns->writer->status = GENX_SEQUENCE_ERROR;
return addNamespace(ns, prefix);
}
/*
* Private attribute-adding code
* most of the work here is normalizing the value, which is the same
* as regular normalization except for " is replaced by "&quot;"
*/
static genxStatus collectAttributeValue (genxWriter w, collector* value,
constUtf8 start, constUtf8 end)
{
/* If end is NULL then the length of the value is unknown and
the value is 0-terminated. */
utf8 last = (utf8) start;
while (end != NULL ? start < end : *start)
{
int c = genxNextUnicodeChar(&start);
if (c == -1)
return w->status = GENX_BAD_UTF8;
if (!isXMLChar(w, c))
return w->status = GENX_NON_XML_CHARACTER;
switch(c)
{
case 9:
collectPiece(w, value, "&#x9;", 5);
break;
case 0xa:
collectPiece(w, value, "&#xA;", 5);
break;
case 0xd:
collectPiece(w, value, "&#xD;", 5);
break;
case '"':
collectPiece(w, value, "&quot;", 6);
break;
case '<':
collectPiece(w, value, "&lt;", 4);
break;
case '&':
collectPiece(w, value, "&amp;", 5);
break;
/*
case '>':
collectPiece(w, value, "&gt;", 4);
break;
*/
default:
collectPiece(w, value, (const char *) last, start - last);
break;
}
last = (utf8) start;
}
return GENX_SUCCESS;
}
static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr)
{
genxWriter w = a->writer;
/* if valuestr not provided, this is an xmlns with a pre-cooked value */
if (valuestr)
{
startCollect(&a->value);
if (collectAttributeValue (w, &a->value, valuestr, NULL) != GENX_SUCCESS)
return w->status;
endCollect(&a->value);
}
/* now add the namespace attribute; might fail if it's been hand-declared */
if (a->ns)
addNamespace(a->ns, NULL);
if (valuestr && a->provided)
return w->status = GENX_DUPLICATE_ATTRIBUTE;
a->provided = True;
/* Add the attribute to the ordered list if not canonical. */
if (!w->canonical)
{
if (w->lastAttribute != NULL)
w->lastAttribute = w->lastAttribute->next = a;
else
w->lastAttribute = w->firstAttribute = a;
}
return GENX_SUCCESS;
}
/*
* public attribute adder.
* The only difference is that it doesn't allow a NULL value
*/
genxStatus genxAddAttribute(genxAttribute a, constUtf8 valuestr)
{
if (a->writer->sequence != SEQUENCE_START_TAG &&
a->writer->sequence != SEQUENCE_ATTRIBUTES)
return a->writer->status = GENX_SEQUENCE_ERROR;
a->writer->sequence = SEQUENCE_ATTRIBUTES;
if (valuestr == NULL)
return a->writer->status = GENX_MISSING_VALUE;
return addAttribute(a, valuestr);
}
genxStatus genxStartAttribute(genxAttribute a)
{
genxWriter w = a->writer;
if (w->sequence != SEQUENCE_START_TAG &&
w->sequence != SEQUENCE_ATTRIBUTES)
return w->status = GENX_SEQUENCE_ERROR;
w->sequence = SEQUENCE_START_ATTR;
w->nowStartingAttr = a;
startCollect(&a->value);
return GENX_SUCCESS;
}
genxStatus genxGetCurrentAttribute (genxWriter w,
constUtf8* xmlns, constUtf8* name)
{
genxAttribute a;
if (w->sequence != SEQUENCE_START_ATTR)
return w->status = GENX_SEQUENCE_ERROR;
a = w->nowStartingAttr;
*xmlns = a->ns ? a->ns->name : NULL;
*name = a->name;
return GENX_SUCCESS;
}
genxStatus genxEndAttribute(genxWriter w)
{
genxAttribute a;
if (w->sequence != SEQUENCE_START_ATTR)
return w->status = GENX_SEQUENCE_ERROR;
a = w->nowStartingAttr;
w->sequence = SEQUENCE_ATTRIBUTES;
endCollect(&a->value);
/* now add the namespace attribute; might fail if it's been hand-declared */
if (a->ns)
addNamespace(a->ns, NULL);
if (a->provided)
return w->status = GENX_DUPLICATE_ATTRIBUTE;
a->provided = True;
/* Add the attribute to the ordered list if not canonical. */
if (!w->canonical)
{
if (w->lastAttribute != NULL)
w->lastAttribute = w->lastAttribute->next = a;
else
w->lastAttribute = w->firstAttribute = a;
}
return GENX_SUCCESS;
}
genxStatus genxGetCurrentElement (genxWriter w,
constUtf8* xmlns, constUtf8* name)
{
int i;
genxElement e;
switch (w->sequence)
{
case SEQUENCE_START_TAG:
case SEQUENCE_ATTRIBUTES:
e = w->nowStarting;
break;
case SEQUENCE_CONTENT:
/* Find the element. The same code as in EndElement() below. */
for (i = (int) (w->stack.count) - 1;
w->stack.pointers[i] != NULL;
i -= 2)
;
e = (genxElement) w->stack.pointers[--i];
break;
default:
return w->status = GENX_SEQUENCE_ERROR;
}
*xmlns = e->ns ? e->ns->name : NULL;
*name = e->name;
return GENX_SUCCESS;
}
genxStatus genxEndElement(genxWriter w)
{
int i;
Boolean close = True;
switch (w->sequence)
{
case SEQUENCE_NO_DOC:
case SEQUENCE_PRE_DOC:
case SEQUENCE_POST_DOC:
case SEQUENCE_START_ATTR:
return w->status = GENX_SEQUENCE_ERROR;
case SEQUENCE_START_TAG:
case SEQUENCE_ATTRIBUTES:
if ((w->status = writeStartTag(w, !w->canonical)) != GENX_SUCCESS)
return w->status;
close = w->canonical;
break;
case SEQUENCE_CONTENT:
break;
}
/*
* Output the closing tag.
*/
if (close)
{
genxElement e;
/*
* first peek into the stack to find the right namespace declaration
* (if any) so we can properly prefix the end-tag. Have to do this
* before unwinding the stack because that might reset some xmlns
* prefixes to the context in the parent element
*/
for (i = (int) (w->stack.count) - 1;
w->stack.pointers[i] != NULL;
i -= 2)
;
e = (genxElement) w->stack.pointers[--i];
if (w->ppIndent)
{
w->ppDepth--;
if (!w->ppSimple && w->ppSuspendDepth == 0)
if (writeIndentation (w) != GENX_SUCCESS)
return w->status;
if (w->ppSuspendDepth > w->ppDepth)
w->ppSuspendDepth = 0; /* Resume pretty-printing. */
}
SendCheck(w, "</");
if (e->ns && e->ns->declaration != w->xmlnsEquals)
{
SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
SendCheck(w, ":");
}
SendCheck(w, e->name);
SendCheck(w, ">");
}
/* If this element is written while pretty-printing is suspended,
treat it as simple. As an example, think of an XHTML <b> element
for which we suspend pretty-printing before writing the opening
tag and resume it after the closing one. */
if (w->ppIndent && w->ppSuspendDepth == 0)
w->ppSimple = False;
/*
* pop zero or more namespace declarations, then a null, then the
* start-element declaration off the stack
*/
w->stack.count--;
while (w->stack.pointers[w->stack.count] != NULL)
{
genxNamespace ns = (genxNamespace) w->stack.pointers[--w->stack.count];
w->stack.count--; /* don't need decl */
/* if not a fake unset-default namespace */
if (ns)
{
/*
* if they've stupidly jammed in their own namespace-prefix
* declarations, we have to go looking to see if there's another
* one in effect
*/
if (ns->baroque)
{
i = (int) w->stack.count;
while (i > 0)
{
while (w->stack.pointers[i] != NULL)
{
genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
if (otherNs == ns)
{
ns->declaration = otherDecl;
i = 0;
break;
}
}
/* skip NULL & element */
i -= 2;
}
}
ns->declCount--;
if (ns->declCount == 0)
ns->baroque = False;
}
}
/* pop the NULL */
if (w->stack.count == 0)
return w->status = GENX_NO_START_TAG;
--w->stack.count;
if (w->stack.count == 0)
w->sequence = SEQUENCE_POST_DOC;
else
w->sequence = SEQUENCE_CONTENT;
return GENX_SUCCESS;
}
/*
* Internal character-adder. It tries to keep the number of sendx()
* calls down by looking at each character but only doing the output
* when it has to escape something; ordinary text gets saved up in
* chunks the start of which is indicated by *breaker.
* c is the character, next points to the UTF8 representing the next
* lastsP indirectly points to the UTF8 representing the
* character, breakerP* indirectly points to the last place genx
* changed the UTF8, e.g. by escaping a '<'
*/
static genxStatus addChar(genxWriter w, int c, constUtf8 next,
constUtf8 * lastsP, constUtf8 * breakerP)
{
if (c == -1)
return GENX_BAD_UTF8;
if (!isXMLChar(w, c))
return GENX_NON_XML_CHARACTER;
switch(c)
{
case 0xd:
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
return w->status;
*breakerP = next;
sendx(w, (utf8) "&#xD;");
break;
case '<':
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
return w->status;
*breakerP = next;
sendx(w, (utf8) "&lt;");
break;
case '&':
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
return w->status;
*breakerP = next;
sendx(w, (utf8) "&amp;");
break;
case '>':
if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
return w->status;
*breakerP = next;
sendx(w, (utf8) "&gt;");
break;
default:
break;
}
*lastsP = next;
return GENX_SUCCESS;
}
genxStatus genxAddText(genxWriter w, constUtf8 start)
{
constUtf8 lasts = start;
constUtf8 breaker = start;
if (w->sequence == SEQUENCE_START_TAG ||
w->sequence == SEQUENCE_ATTRIBUTES)
{
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_CONTENT;
}
if (w->sequence == SEQUENCE_CONTENT)
{
while (*start)
{
int c = genxNextUnicodeChar(&start);
w->status = addChar(w, c, start, &lasts, &breaker);
if (w->status != GENX_SUCCESS)
return w->status;
}
return sendxBounded(w, breaker, (utf8) start);
}
else if (w->sequence == SEQUENCE_START_ATTR)
{
return collectAttributeValue (w, &w->nowStartingAttr->value, start, NULL);
}
else
return w->status = GENX_SEQUENCE_ERROR;
}
genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end)
{
constUtf8 lasts = start;
constUtf8 breaker = start;
if (w->sequence == SEQUENCE_START_TAG ||
w->sequence == SEQUENCE_ATTRIBUTES)
{
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_CONTENT;
}
if (w->sequence == SEQUENCE_CONTENT)
{
while (start < end)
{
int c = genxNextUnicodeChar(&start);
w->status = addChar(w, c, (utf8) start, &lasts, &breaker);
if (w->status != GENX_SUCCESS)
return w->status;
}
return sendxBounded(w, breaker, (utf8) start);
}
else if (w->sequence == SEQUENCE_START_ATTR)
{
return collectAttributeValue (w, &w->nowStartingAttr->value, start, end);
}
else
return w->status = GENX_SEQUENCE_ERROR;
}
genxStatus genxAddCountedText(genxWriter w, constUtf8 start, size_t byteCount)
{
utf8 end = (utf8) (start + byteCount);
return genxAddBoundedText(w, start, end);
}
genxStatus genxAddCharacter(genxWriter w, int c)
{
unsigned char cUTF8[10];
utf8 lasts, breaker, next;
if (w->sequence == SEQUENCE_START_TAG ||
w->sequence == SEQUENCE_ATTRIBUTES)
{
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_CONTENT;
}
if (!isXMLChar(w, c))
return w->status = GENX_NON_XML_CHARACTER;
if (w->sequence == SEQUENCE_START_ATTR)
{
int done = 1;
collector* value = &w->nowStartingAttr->value;
switch(c)
{
case 9:
collectPiece(w, value, "&#x9;", 5);
break;
case 0xa:
collectPiece(w, value, "&#xA;", 5);
break;
case 0xd:
collectPiece(w, value, "&#xD;", 5);
break;
case '"':
collectPiece(w, value, "&quot;", 6);
break;
case '<':
collectPiece(w, value, "&lt;", 4);
break;
case '&':
collectPiece(w, value, "&amp;", 5);
break;
/*
case '>':
collectPiece(w, value, "&gt;", 4);
break;
*/
default:
done = 0;
break;
}
if (done)
return GENX_SUCCESS;
}
/* make UTF8 representation of character */
lasts = breaker = next = cUTF8;
if (c < 0x80)
*next++ = c;
else if (c < 0x800)
{
*next++ = 0xc0 | (c >> 6);
*next++ = 0x80 | (c & 0x3f);
}
else if (c < 0x10000)
{
*next++ = 0xe0 | (c >> 12);
*next++ = 0x80 | ((c & 0xfc0) >> 6);
*next++ = 0x80 | (c & 0x3f);
}
else
{
*next++ = 0xf0 | (c >> 18);
*next++ = 0x80 | ((c & 0x3f000) >> 12);
*next++ = 0x80 | ((c & 0xfc0) >> 6);
*next++ = 0x80 | (c & 0x3f);
}
*next = 0;
if (w->sequence == SEQUENCE_CONTENT)
{
w->status =
addChar(w, c, next, (constUtf8 *) &lasts, (constUtf8 *) &breaker);
if (w->status != GENX_SUCCESS)
return w->status;
return sendxBounded(w, breaker, next);
}
else if (w->sequence == SEQUENCE_START_ATTR)
{
collectPiece(w, &w->nowStartingAttr->value,
(const char *) cUTF8, next - cUTF8);
return GENX_SUCCESS;
}
else
return w->status = GENX_SEQUENCE_ERROR;
}
genxStatus genxEndDocument(genxWriter w)
{
if (w->sequence != SEQUENCE_POST_DOC)
return w->status = GENX_SEQUENCE_ERROR;
/* Write a newline after the closing tag. */
SendCheck (w, "\n");
if ((w->status = (*w->sender->flush)(w->userData)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_NO_DOC;
return GENX_SUCCESS;
}
genxStatus genxXmlDeclaration(genxWriter w,
constUtf8 ver,
constUtf8 enc,
constUtf8 stl)
{
if (w->sequence != SEQUENCE_PRE_DOC)
return w->status = GENX_SEQUENCE_ERROR;
if ((w->status = genxCheckText(w, ver)) != GENX_SUCCESS)
return w->status;
if (enc != NULL && (w->status = genxCheckText(w, enc)) != GENX_SUCCESS)
return w->status;
if (stl != NULL && (w->status = genxCheckText(w, stl)) != GENX_SUCCESS)
return w->status;
SendCheck (w, "<?xml version=\"");
SendCheck (w, ver);
if (enc != NULL)
{
SendCheck (w, "\" encoding=\"");
SendCheck (w, enc);
}
if (stl != NULL)
{
SendCheck (w, "\" standalone=\"");
SendCheck (w, stl);
}
SendCheck (w, "\" ?>\n");
return GENX_SUCCESS;
}
genxStatus genxDoctypeDeclaration(genxWriter w,
constUtf8 re,
constUtf8 pi,
constUtf8 si,
constUtf8 is)
{
if (w->sequence != SEQUENCE_PRE_DOC)
return w->status = GENX_SEQUENCE_ERROR;
if ((w->status = genxCheckText(w, re)) != GENX_SUCCESS)
return w->status;
if (pi != NULL && (w->status = genxCheckText(w, pi)) != GENX_SUCCESS)
return w->status;
if (si != NULL && (w->status = genxCheckText(w, si)) != GENX_SUCCESS)
return w->status;
if (is != NULL && (w->status = genxCheckText(w, is)) != GENX_SUCCESS)
return w->status;
SendCheck (w, "<!DOCTYPE ");
SendCheck (w, re);
if (pi != NULL)
{
SendCheck (w, " PUBLIC\n \"");
SendCheck (w, pi);
SendCheck (w, "\"");
}
if (si != NULL)
{
if (pi == NULL)
SendCheck (w, " SYSTEM");
SendCheck (w, "\n \"");
SendCheck (w, si);
SendCheck (w, "\"");
}
if (is != NULL)
{
SendCheck (w, " [\n");
SendCheck (w, is);
SendCheck (w, "\n]");
}
SendCheck (w, ">\n");
return GENX_SUCCESS;
}
genxStatus genxComment(genxWriter w, constUtf8 text)
{
size_t i;
if (w->sequence == SEQUENCE_NO_DOC ||
w->sequence == SEQUENCE_START_ATTR)
return w->status = GENX_SEQUENCE_ERROR;
if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
return w->status;
/* no leading '-', no trailing '-', no '--' */
if (text[0] == '-')
return w->status = GENX_MALFORMED_COMMENT;
for (i = 0; text[i]; i++)
if (text[i] == '-' && (text[i + 1] == '-' || text[i + 1] == 0))
return w->status = GENX_MALFORMED_COMMENT;
if (w->sequence == SEQUENCE_START_TAG ||
w->sequence == SEQUENCE_ATTRIBUTES)
{
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_CONTENT;
}
else if (w->sequence == SEQUENCE_POST_DOC)
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, (utf8) "<!--")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, (utf8) text)) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, (utf8) "-->")) != GENX_SUCCESS)
return w->status;
if (w->sequence == SEQUENCE_PRE_DOC)
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
return w->status;
return GENX_SUCCESS;
}
genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text)
{
size_t i;
if (w->sequence == SEQUENCE_NO_DOC ||
w->sequence == SEQUENCE_START_ATTR)
return w->status = GENX_SEQUENCE_ERROR;
if ((w->status = genxCheckText(w, target)) != GENX_SUCCESS)
return w->status;
if ((w->status = checkNCName(w, target)) != GENX_SUCCESS)
return w->status;
if ((strlen((const char *) target) >= 3) &&
(target[0] == 'x' || target[0] == 'X') &&
(target[1] == 'm' || target[1] == 'M') &&
(target[2] == 'l' || target[2] == 'L') &&
(target[3] == 0))
return w->status = GENX_XML_PI_TARGET;
if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
return w->status;
/* no ?> within */
for (i = 1; text[i]; i++)
if (text[i] == '>' && text[i - 1] == '?')
return w->status = GENX_MALFORMED_PI;
if (w->sequence == SEQUENCE_START_TAG ||
w->sequence == SEQUENCE_ATTRIBUTES)
{
if ((w->status = writeStartTag(w, False)) != GENX_SUCCESS)
return w->status;
w->sequence = SEQUENCE_CONTENT;
}
else if (w->sequence == SEQUENCE_POST_DOC)
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, (utf8) "<?")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, target)) != GENX_SUCCESS)
return w->status;
if (text[0])
{
if ((w->status = sendx(w, (utf8) " ")) != GENX_SUCCESS)
return w->status;
if ((w->status = sendx(w, text)) != GENX_SUCCESS)
return w->status;
}
if ((w->status = sendx(w, (utf8) "?>")) != GENX_SUCCESS)
return w->status;
if (w->sequence == SEQUENCE_PRE_DOC)
if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
return w->status;
return GENX_SUCCESS;
}
/*******************************
* Literal versions of the writing routines
*/
genxStatus genxStartElementLiteral(genxWriter w,
constUtf8 xmlns, constUtf8 name)
{
genxNamespace ns = NULL;
genxElement e;
if (xmlns)
{
ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
if (ns == NULL || w->status != GENX_SUCCESS)
return w->status;
}
e = genxDeclareElement(w, ns, name, &w->status);
if (e == NULL || w->status != GENX_SUCCESS)
return w->status;
return genxStartElement(e);
}
genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns,
constUtf8 name, constUtf8 value)
{
genxNamespace ns = NULL;
genxAttribute a;
if (xmlns)
{
ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
if (ns == NULL && w->status != GENX_SUCCESS)
return w->status;
}
a = genxDeclareAttribute(w, ns, name, &w->status);
if (a == NULL || w->status != GENX_SUCCESS)
return w->status;
return genxAddAttribute(a, value);
}
genxStatus genxStartAttributeLiteral(genxWriter w,
constUtf8 xmlns, constUtf8 name)
{
genxNamespace ns = NULL;
genxAttribute a;
if (xmlns)
{
ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
if (ns == NULL && w->status != GENX_SUCCESS)
return w->status;
}
a = genxDeclareAttribute(w, ns, name, &w->status);
if (a == NULL || w->status != GENX_SUCCESS)
return w->status;
return genxStartAttribute(a);
}
genxStatus genxAddNamespaceLiteral(genxWriter w,
constUtf8 uri, constUtf8 prefix)
{
genxNamespace ns = genxDeclareNamespace(w, uri, prefix, &w->status);
if (ns == NULL && w->status != GENX_SUCCESS)
return w->status;
return genxAddNamespace(ns, NULL);
}
/*
* return version
*/
char * genxGetVersion()
{
return GENX_VERSION;
}