summaryrefslogtreecommitdiff
path: root/libcutl/cutl/details/genx/genx.c
diff options
context:
space:
mode:
Diffstat (limited to 'libcutl/cutl/details/genx/genx.c')
-rw-r--r--libcutl/cutl/details/genx/genx.c2324
1 files changed, 2324 insertions, 0 deletions
diff --git a/libcutl/cutl/details/genx/genx.c b/libcutl/cutl/details/genx/genx.c
new file mode 100644
index 0000000..6ad55f0
--- /dev/null
+++ b/libcutl/cutl/details/genx/genx.c
@@ -0,0 +1,2324 @@
+/*
+ * Copyright (c) 2007-2013 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 <cutl/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;
+ int count;
+ int space;
+ void * * pointers;
+} plist;
+
+/*******************************
+ * text collector, for attribute values
+ */
+typedef struct
+{
+ utf8 buf;
+ int used;
+ int 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;
+ int declCount;
+ Boolean baroque;
+ genxAttribute declaration;
+ genxAttribute defaultDecl;
+};
+
+struct genxElement_rec
+{
+ genxWriter writer;
+ utf8 type;
+ 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;
+ 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, int 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, int 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)
+{
+ int 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;
+ int 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, int at)
+{
+ int 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)
+{
+ int 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 type)
+{
+ int i;
+ genxElement * ee = (genxElement *) pl->pointers;
+
+ for (i = 0; i < pl->count; i++)
+ {
+ if (xmlns == NULL)
+ {
+ if (ee[i]->ns == NULL && strcmp((const char *) type,
+ (const char *) ee[i]->type) == 0)
+ return ee[i];
+ }
+ else
+ {
+ if (ee[i]->ns != NULL &&
+ strcmp((const char *) xmlns, (const char *) ee[i]->ns->name) == 0 &&
+ strcmp((const char *) type, (const char *) ee[i]->type) == 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 = 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, 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)
+{
+ int 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;
+}
+
+/*
+ * 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)
+{
+ int 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]->type);
+ 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 type,
+ genxStatus * statusP)
+{
+ genxElement old;
+ genxElement el;
+
+ if ((w->status = checkNCName(w, type)) != GENX_SUCCESS)
+ {
+ *statusP = w->status;
+ return NULL;
+ }
+
+ /* already declared? */
+ old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, type);
+ 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->type = copy(w, type)) == 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 = 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, 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->ppSimple = True;
+ w->ppDepth = 0;
+ }
+
+ 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)
+{
+ int 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)
+ if (writeIndentation (w) != GENX_SUCCESS)
+ return w->status;
+
+ if (!close)
+ {
+ w->ppDepth++;
+ w->ppSimple = True;
+ }
+ }
+
+ SendCheck(w, "<");
+ if (e->ns && (e->ns->declaration != w->xmlnsEquals))
+ {
+ SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
+ SendCheck(w, ":");
+ }
+ SendCheck(w, e->type);
+
+ /* 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 = 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 = 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 = 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 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 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 = 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)
+ if (writeIndentation (w) != GENX_SUCCESS)
+ return w->status;
+ }
+
+ SendCheck(w, "</");
+ if (e->ns && e->ns->declaration != w->xmlnsEquals)
+ {
+ SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
+ SendCheck(w, ":");
+ }
+ SendCheck(w, e->type);
+ SendCheck(w, ">");
+ }
+
+ if (w->ppIndent)
+ 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 = 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 */
+ --w->stack.count;
+ if (w->stack.count < 0)
+ return w->status = GENX_NO_START_TAG;
+
+ 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, int 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 genxComment(genxWriter w, constUtf8 text)
+{
+ int 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)
+{
+ int 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 type)
+{
+ 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, type, &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;
+}