From 305902b2f0e7d12e99dae4bddf8f622f77639d84 Mon Sep 17 00:00:00 2001 From: "cosmin.apreutesei" Date: Wed, 18 Dec 2013 21:43:42 +0200 Subject: [PATCH] init --- bin/genx.dll | Bin 0 -> 17920 bytes csrc/genx/COPYING | 21 + csrc/genx/WHAT | 1 + csrc/genx/build-linux32.sh | 1 + csrc/genx/build-mingw32.sh | 1 + csrc/genx/charProps.c | 378 +++++++ csrc/genx/genx.c | 1940 ++++++++++++++++++++++++++++++++++++ csrc/genx/genx.h | 287 ++++++ csrc/genx/test-linux32.sh | 3 + csrc/genx/test-mingw32.sh | 3 + csrc/genx/tgx.c | 1675 +++++++++++++++++++++++++++++++ genx.lua | 145 +++ genx.md | 63 ++ genx_demo.lua | 49 + genx_h.lua | 98 ++ linux/bin/libgenx.so | Bin 0 -> 41904 bytes 16 files changed, 4665 insertions(+) create mode 100644 bin/genx.dll create mode 100644 csrc/genx/COPYING create mode 100644 csrc/genx/WHAT create mode 100644 csrc/genx/build-linux32.sh create mode 100644 csrc/genx/build-mingw32.sh create mode 100644 csrc/genx/charProps.c create mode 100644 csrc/genx/genx.c create mode 100644 csrc/genx/genx.h create mode 100644 csrc/genx/test-linux32.sh create mode 100644 csrc/genx/test-mingw32.sh create mode 100644 csrc/genx/tgx.c create mode 100644 genx.lua create mode 100644 genx.md create mode 100644 genx_demo.lua create mode 100644 genx_h.lua create mode 100644 linux/bin/libgenx.so diff --git a/bin/genx.dll b/bin/genx.dll new file mode 100644 index 0000000000000000000000000000000000000000..1b734267473adb5df0ca22ad96c5b3c387f93324 GIT binary patch literal 17920 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 Y%8nXL4W&j +#include + +#include "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_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; +}; + +/******************************* + * genx's sandbox + */ +struct genxWriter_rec +{ + FILE * file; + genxSender * sender; + genxStatus status; + writerSequence sequence; + char xmlChars[0x10000]; + void * userData; + int nextPrefix; + utf8 empty; + Boolean defaultNsDeclared; + genxAttribute xmlnsEquals; + genxElement nowStarting; + plist namespaces; + plist elements; + plist attributes; + plist prefixes; + plist stack; + struct genxAttribute_rec arec; + char * etext[100]; + void * (* alloc)(void * userData, int bytes); + void (* dealloc)(void * userData, void * data); +}; + +/******************************* + * Forward declarations + */ +static genxAttribute declareAttribute(genxWriter w, genxNamespace ns, + constUtf8 name, constUtf8 valuestr, + genxStatus * statusP); +static genxStatus addNamespace(genxNamespace ns, utf8 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 < 0x10000) + return (int) w->xmlChars[c]; + else + return (c <= 0x10ffff); +} + +static Boolean isLetter(genxWriter w, int c) +{ + if (c < 0 || c > 0xffff) + return False; + else + return w->xmlChars[c] & GENX_LETTER; +} + +static Boolean isNameChar(genxWriter w, int c) +{ + if (c < 0 || c > 0xffff) + return False; + else + return w->xmlChars[c] & GENX_NAMECHAR; +} + +/******************************* + * Constructors, setters/getters + */ + +/* + * Construct a new genxWriter + */ +genxWriter genxNew(void * (* alloc)(void * userData, int bytes), + void (* dealloc)(void * userData, void * data), + 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] = "Bad UTF8"; + w->etext[GENX_NON_XML_CHARACTER] = "Non XML Character"; + w->etext[GENX_BAD_NAME] = "Bad NAME"; + w->etext[GENX_ALLOC_FAILED] = "Memory allocation failed"; + w->etext[GENX_BAD_NAMESPACE_NAME] = "Bad 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 EndElement call"; + w->etext[GENX_IO_ERROR] = "I/O 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] = + "Same attribute specified more than once"; + w->etext[GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE] = + "Attribute cannot be in default namespace"; + w->etext[GENX_DUPLICATE_NAMESPACE] = + "Declared namespace twice with different prefixes on one element."; + w->etext[GENX_BAD_DEFAULT_DECLARATION] = + "Declared a default namespace on an element which is in no 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; + + return w; +} + +/* + * get/set userData + */ +void genxSetUserData(genxWriter w, void * userData) +{ + w->userData = userData; +} +void * genxGetUserData(genxWriter w) +{ + return w->userData; +} + +/* + * get/set allocator + */ +void genxSetAlloc(genxWriter w, void * (* alloc)(void * userData, int bytes)) +{ + w->alloc = alloc; +} +void genxSetDealloc(genxWriter w, + void (* dealloc)(void * userData, void * data)) +{ + w->dealloc = dealloc; +} +void * (* genxGetAlloc(genxWriter w))(void * userData, int bytes) +{ + return w->alloc; +} +void (* genxGetDealloc(genxWriter w))(void * userData, void * data) +{ + 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; + + 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 + { + if (fputs((const char *) s, w->file) == -1) + return GENX_IO_ERROR; + else + return GENX_SUCCESS; + } +} + +static genxStatus sendxBounded(genxWriter w, constUtf8 start, constUtf8 end) +{ + if (w->sender) + return (*w->sender->sendBounded)(w->userData, start, end); + else + if (fwrite(start, 1, end - start, w->file) != (unsigned) (end - start)) + return GENX_IO_ERROR; + else + return GENX_SUCCESS; +} + +#define SendCheck(w,s) if ((w->status=sendx(w,(utf8)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. + */ + +/* + * Start a document + */ +genxStatus genxStartDocFile(genxWriter w, FILE * file) +{ + if (w->sequence != SEQUENCE_NO_DOC) + return w->status = GENX_SEQUENCE_ERROR; + + w->sequence = SEQUENCE_PRE_DOC; + w->file = file; + w->sender = NULL; + return GENX_SUCCESS; +} + +genxStatus genxStartDocSender(genxWriter w, genxSender * sender) +{ + if (w->sequence != SEQUENCE_NO_DOC) + return w->status = GENX_SEQUENCE_ERROR; + + w->sequence = SEQUENCE_PRE_DOC; + w->file = NULL; + w->sender = sender; + return GENX_SUCCESS; +} + +/* + * 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) +{ + 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; + + 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); + + for (i = 0; i < w->attributes.count; i++) + { + if (aa[i]->provided) + { + if (aa[i]->ns && aa[i]->ns->baroque && + aa[i]->ns->declaration == w->xmlnsEquals) + return w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE; + + SendCheck(w, " "); + + if (aa[i]->ns) + { + SendCheck(w, aa[i]->ns->declaration->name + STRLEN_XMLNS_COLON) + SendCheck(w, ":"); + } + SendCheck(w, aa[i]->name); + SendCheck(w, "=\""); + SendCheck(w, aa[i]->value.buf); + 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; + int i; + + switch (w->sequence) + { + case SEQUENCE_NO_DOC: + case SEQUENCE_POST_DOC: + return w->status = GENX_SEQUENCE_ERROR; + case SEQUENCE_START_TAG: + case SEQUENCE_ATTRIBUTES: + if ((w->status = writeStartTag(w)) != GENX_SUCCESS) + return w->status; + break; + case SEQUENCE_PRE_DOC: + case SEQUENCE_CONTENT: + break; + } + + w->sequence = SEQUENCE_START_TAG; + + /* clear provided attributes */ + for (i = 0; i < w->attributes.count; i++) + ((genxAttribute) w->attributes.pointers[i])->provided = 0; + + /* + * 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, utf8 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, utf8 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 """ + */ +static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr) +{ + utf8 lastv = (utf8) valuestr; + genxWriter w = a->writer; + + /* if valuestr not provided, this is an xmlns with a pre-cooked value */ + if (valuestr) + { + startCollect(&a->value); + while (*valuestr) + { + int c = genxNextUnicodeChar(&valuestr); + + 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, &a->value, " ", 5); + break; + case 0xa: + collectPiece(w, &a->value, " ", 5); + break; + case 0xd: + collectPiece(w, &a->value, " ", 5); + break; + case '"': + collectPiece(w, &a->value, """, 6); + break; + case '<': + collectPiece(w, &a->value, "<", 4); + break; + case '&': + collectPiece(w, &a->value, "&", 5); + break; + /* + case '>': + collectPiece(w, &a->value, ">", 4); + break; + */ + default: + collectPiece(w, &a->value, (const char *) lastv, valuestr - lastv); + break; + } + lastv = (utf8) valuestr; + } + endCollect(&a->value); + } + + /* now add the namespace attribute; might fail if it's bee hand-declared */ + if (a->ns) + addNamespace(a->ns, NULL); + + if (valuestr && a->provided) + return w->status = GENX_DUPLICATE_ATTRIBUTE; + a->provided = 1; + + 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 genxEndElement(genxWriter w) +{ + genxElement e; + int i; + + switch (w->sequence) + { + case SEQUENCE_NO_DOC: + case SEQUENCE_PRE_DOC: + case SEQUENCE_POST_DOC: + return w->status = GENX_SEQUENCE_ERROR; + case SEQUENCE_START_TAG: + case SEQUENCE_ATTRIBUTES: + if ((w->status = writeStartTag(w)) != GENX_SUCCESS) + return w->status; + break; + case SEQUENCE_CONTENT: + break; + } + + /* + * 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]; + + SendCheck(w, "ns && e->ns->declaration != w->xmlnsEquals) + { + SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON); + SendCheck(w, ":"); + } + SendCheck(w, e->type); + SendCheck(w, ">"); + + /* + * 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) " "); + break; + case '<': + if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS) + return w->status; + *breakerP = next; + sendx(w, (utf8) "<"); + break; + case '&': + if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS) + return w->status; + *breakerP = next; + sendx(w, (utf8) "&"); + break; + case '>': + if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS) + return w->status; + *breakerP = next; + sendx(w, (utf8) ">"); + 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)) != GENX_SUCCESS) + return w->status; + w->sequence = SEQUENCE_CONTENT; + } + + if (w->sequence != SEQUENCE_CONTENT) + return w->status = GENX_SEQUENCE_ERROR; + + 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); +} + +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)) != GENX_SUCCESS) + return w->status; + w->sequence = SEQUENCE_CONTENT; + } + + if (w->sequence != SEQUENCE_CONTENT) + return w->status = GENX_SEQUENCE_ERROR; + + 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); +} + +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)) != GENX_SUCCESS) + return w->status; + w->sequence = SEQUENCE_CONTENT; + } + + if (w->sequence != SEQUENCE_CONTENT) + return w->status = GENX_SEQUENCE_ERROR; + + if (!isXMLChar(w, c)) + return w->status = GENX_NON_XML_CHARACTER; + + /* 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; + + w->status = + addChar(w, c, next, (constUtf8 *) &lasts, (constUtf8 *) &breaker); + if (w->status != GENX_SUCCESS) + return w->status; + + return sendxBounded(w, breaker, next); +} + +genxStatus genxEndDocument(genxWriter w) +{ + if (w->sequence != SEQUENCE_POST_DOC) + return w->status = GENX_SEQUENCE_ERROR; + + if (w->file) + fflush(w->file); + else + if ((w->status = (*w->sender->flush)(w->userData)) != GENX_SUCCESS) + return w->status; + + w->sequence = SEQUENCE_NO_DOC; + return GENX_SUCCESS; +} + +genxStatus genxComment(genxWriter w, constUtf8 text) +{ + int i; + + if (w->sequence == SEQUENCE_NO_DOC) + 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)) != 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->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) + 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)) != 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) "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); +} + +/* + * return version + */ +char * genxGetVersion() +{ + return GENX_VERSION; +} + diff --git a/csrc/genx/genx.h b/csrc/genx/genx.h new file mode 100644 index 0000000..538c9c1 --- /dev/null +++ b/csrc/genx/genx.h @@ -0,0 +1,287 @@ + +/* + * genx - C-callable library for generating XML documents + */ + +/* + * Copyright (c) 2004 by Tim Bray and Sun Microsystems. For copying + * permission, see http://www.tbray.org/ongoing/genx/COPYING + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Note on error handling: genx routines mostly return + * GENX_SUCCESS (guaranteed to be zero) in normal circumstances, one of + * these other GENX_ values on a memory allocation or I/O failure or if the + * call would result in non-well-formed output. + * You can associate an error message with one of these codes explicitly + * or with the most recent error using genxGetErrorMessage() and + * genxLastErrorMessage(); see below. + */ +typedef enum +{ + GENX_SUCCESS = 0, + GENX_BAD_UTF8, + GENX_NON_XML_CHARACTER, + GENX_BAD_NAME, + GENX_ALLOC_FAILED, + GENX_BAD_NAMESPACE_NAME, + GENX_INTERNAL_ERROR, + GENX_DUPLICATE_PREFIX, + GENX_SEQUENCE_ERROR, + GENX_NO_START_TAG, + GENX_IO_ERROR, + GENX_MISSING_VALUE, + GENX_MALFORMED_COMMENT, + GENX_XML_PI_TARGET, + GENX_MALFORMED_PI, + GENX_DUPLICATE_ATTRIBUTE, + GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE, + GENX_DUPLICATE_NAMESPACE, + GENX_BAD_DEFAULT_DECLARATION +} genxStatus; + +/* character types */ +#define GENX_XML_CHAR 1 +#define GENX_LETTER 2 +#define GENX_NAMECHAR 4 + +/* a UTF-8 string */ +typedef unsigned char * utf8; +typedef const unsigned char * constUtf8; + +/* + * genx's own types + */ +typedef struct genxWriter_rec * genxWriter; +typedef struct genxNamespace_rec * genxNamespace; +typedef struct genxElement_rec * genxElement; +typedef struct genxAttribute_rec * genxAttribute; + +/* + * Constructors, set/get + */ + +/* + * Create a new writer. For generating multiple XML documents, it's most + * efficient to re-use the same genx object. However, you can only write + * one document at a time with a writer. + * Returns NULL if it fails, which can only be due to an allocation failure. + */ +genxWriter genxNew(void * (*alloc)(void * userData, int bytes), + void (* dealloc)(void * userData, void * data), + void * userData); + +/* + * Dispose of a writer, freeing all associated memory + */ +void genxDispose(genxWriter w); + +/* + * Set/get + */ + +/* + * The userdata pointer will be passed to memory-allocation + * and I/O callbacks. If not set, genx will pass NULL + */ +void genxSetUserData(genxWriter w, void * userData); +void * genxGetUserData(genxWriter w); + +/* + * User-provided memory allocator, if desired. For example, if you were + * in an Apache module, you could arrange for genx to use ap_palloc by + * making the pool accessible via the userData call. + * The "dealloc" is to be used to free memory allocated with "alloc". If + * alloc is provided but dealloc is NULL, genx will not attempt to free + * the memory; this would be appropriate in an Apache context. + * If "alloc" is not provided, genx routines use malloc() to allocate memory + */ +void genxSetAlloc(genxWriter w, + void * (* alloc)(void * userData, int bytes)); +void genxSetDealloc(genxWriter w, + void (* dealloc)(void * userData, void * data)); +void * (* genxGetAlloc(genxWriter w))(void * userData, int bytes); +void (* genxGetDealloc(genxWriter w))(void * userData, void * data); + +/* + * Get the prefix associated with a namespace + */ +utf8 genxGetNamespacePrefix(genxNamespace ns); + +/* + * Declaration functions + */ + +/* + * Declare a namespace. The provided prefix is the default but can be + * overridden by genxAddNamespace. If no default prefiix is provided, + * genx will generate one of the form g-%d. + * On error, returns NULL and signals via statusp + */ +genxNamespace genxDeclareNamespace(genxWriter w, + constUtf8 uri, constUtf8 prefix, + genxStatus * statusP); + +/* + * Declare an element + * If something failed, returns NULL and sets the status code via statusP + */ +genxElement genxDeclareElement(genxWriter w, + genxNamespace ns, constUtf8 type, + genxStatus * statusP); + +/* + * Declare an attribute + */ +genxAttribute genxDeclareAttribute(genxWriter w, + genxNamespace ns, + constUtf8 name, genxStatus * statusP); + +/* + * Writing XML + */ + +/* + * Start a new document. + */ +genxStatus genxStartDocFile(genxWriter w, FILE * file); + +/* + * Caller-provided I/O package. + * First form is for a null-terminated string. + * for second, if you have s="abcdef" and want to send "abc", you'd call + * sendBounded(userData, s, s + 3) + */ +typedef struct +{ + genxStatus (* send)(void * userData, constUtf8 s); + genxStatus (* sendBounded)(void * userData, constUtf8 start, constUtf8 end); + genxStatus (* flush)(void * userData); +} genxSender; + +genxStatus genxStartDocSender(genxWriter w, genxSender * sender); + +/* + * End a document. Calls "flush" + */ +genxStatus genxEndDocument(genxWriter w); + +/* + * Write a comment + */ +genxStatus genxComment(genxWriter w, constUtf8 text); + +/* + * Write a PI + */ +genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text); + +/* + * Start an element + */ +genxStatus genxStartElementLiteral(genxWriter w, + constUtf8 xmlns, constUtf8 type); + +/* + * Start a predeclared element + * - element must have been declared + */ +genxStatus genxStartElement(genxElement e); + +/* + * Write an attribute + */ +genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns, + constUtf8 name, constUtf8 value); + +/* + * Write a predeclared attribute + */ +genxStatus genxAddAttribute(genxAttribute a, constUtf8 value); + +/* + * add a namespace declaration + */ +genxStatus genxAddNamespace(genxNamespace ns, utf8 prefix); + +/* + * Clear default namespace declaration + */ +genxStatus genxUnsetDefaultNamespace(genxWriter w); + +/* + * Write an end tag + */ +genxStatus genxEndElement(genxWriter w); + +/* + * Write some text + * You can't write any text outside the root element, except with + * genxComment and genxPI + */ +genxStatus genxAddText(genxWriter w, constUtf8 start); +genxStatus genxAddCountedText(genxWriter w, constUtf8 start, int byteCount); +genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end); + +/* + * Write one character. The integer value is the Unicode character + * value, as usually expressed in U+XXXX notation. + */ +genxStatus genxAddCharacter(genxWriter w, int c); + +/* + * Utility routines + */ + +/* + * Return the Unicode character encoded by the UTF-8 pointed-to by the + * argument, and advance the argument past the encoding of the character. + * Returns -1 if the UTF-8 is malformed, in which case advances the + * argument to point at the first byte past the point past the malformed + * ones. + */ +int genxNextUnicodeChar(constUtf8 * sp); + +/* + * Scan a buffer allegedly full of UTF-8 encoded XML characters; return + * one of GENX_SUCCESS, GENX_BAD_UTF8, or GENX_NON_XML_CHARACTER + */ +genxStatus genxCheckText(genxWriter w, constUtf8 s); + +/* + * return character status, the OR of GENX_XML_CHAR, + * GENX_LETTER, and GENX_NAMECHAR + */ +int genxCharClass(genxWriter w, int c); + +/* + * Silently wipe any non-XML characters out of a chunk of text. + * If you call this on a string before you pass it addText or + * addAttribute, you will never get an error from genx unless + * (a) there's a bug in your software, e.g. a malformed element name, or + * (b) there's a memory allocation or I/O error + * The output can never be longer than the input. + * Returns true if any changes were made. + */ +int genxScrubText(genxWriter w, constUtf8 in, utf8 out); + +/* + * return error messages + */ +char * genxGetErrorMessage(genxWriter w, genxStatus status); +char * genxLastErrorMessage(genxWriter w); + +/* + * return version + */ +char * genxGetVersion(); + +#ifdef __cplusplus +} +#endif diff --git a/csrc/genx/test-linux32.sh b/csrc/genx/test-linux32.sh new file mode 100644 index 0000000..6b5a86a --- /dev/null +++ b/csrc/genx/test-linux32.sh @@ -0,0 +1,3 @@ +gcc tgx.c -lgenx -L../../linux/bin -o tgx +LD_LIBRARY_PATH=../../linux/bin ./tgx >/dev/null +rm tgx diff --git a/csrc/genx/test-mingw32.sh b/csrc/genx/test-mingw32.sh new file mode 100644 index 0000000..f16c221 --- /dev/null +++ b/csrc/genx/test-mingw32.sh @@ -0,0 +1,3 @@ +gcc tgx.c -lgenx -L../../bin -o tgx.exe +./tgx.exe >/dev/null +rm tgx.exe diff --git a/csrc/genx/tgx.c b/csrc/genx/tgx.c new file mode 100644 index 0000000..ac284d0 --- /dev/null +++ b/csrc/genx/tgx.c @@ -0,0 +1,1675 @@ +#include "genx.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#define DEVNULL "NUL" +#define RANDOM rand +#define SRANDOM srand +#else +#define DEVNULL "/dev/null" +#define RANDOM random +#define SRANDOM srandom +#endif + +static int errorcount = 0; + +static void ouch2(genxWriter w, char * message) +{ + fprintf(stderr, "*** stress test error '%s': %s\n", message, + genxLastErrorMessage(w)); + errorcount++; +} + + +static void ouch(genxWriter w, char * message, genxStatus code, genxStatus wanted) +{ + if (code == -1) + fprintf(stderr, "*** %s\n", message); + else if (wanted == -1) + fprintf(stderr, + "*** %s: got %s\n", message, genxGetErrorMessage(w, code)); + + else + fprintf(stderr, + "*** %s: got %s wanted %s\n", message, + genxGetErrorMessage(w, code), + genxGetErrorMessage(w, wanted)); + errorcount++; +} + +typedef struct +{ + unsigned char buf[BUFSIZ]; + utf8 nowAt; +} iobuf_rec; + +genxStatus sends(void * userData, constUtf8 s) +{ + iobuf_rec * io = (iobuf_rec *) userData; + while (*s) + *io->nowAt++ = *s++; + *io->nowAt = 0; + return GENX_SUCCESS; +} +genxStatus brokenSends(void * userData, constUtf8 s) +{ + return GENX_IO_ERROR; +} + +genxStatus sendb(void * userData, constUtf8 start, constUtf8 end) +{ + iobuf_rec * io = (iobuf_rec *) userData; + while (start < end) + *io->nowAt++ = *start++; + *io->nowAt = 0; + return GENX_SUCCESS; +} +genxStatus sflush(void * userData) +{ + return GENX_SUCCESS; +} + +iobuf_rec iobuf; +genxSender sender = { &sends, &sendb, &sflush }; + +static void checkUTF8() +{ + genxStatus ret; + genxWriter w; + + // see http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF + // this is a legal UTF-8 4-char string + unsigned char t1[] = + { + 0x26, + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + int charsInT1[] = { 0x26, 0x416, 0x4e2d, 0x10346, 0 }; + + unsigned char t2[] = + { + '<', 'a', '>', 1, 0 + }; + unsigned char t3[] = { 0xc0, 0xaf, 0 }; + unsigned char t4[] = { 0xc2, 0x7f, 0 }; + unsigned char t5[] = { 0x80, 0x80, 0 }; + unsigned char t6[] = { 0x80, 0x9f, 0 }; + unsigned char t7[] = { 0xe1, 0x7f, 0 }; + unsigned char t8[] = { 0xe1, 0x80, 0 }; + unsigned char t9[] = { 0xe1, 0x80, 0x7f, 0 }; + + unsigned char * tBadUtf8[] = + { + t3, t4, t5, t6, t7, t8, t9, NULL + }; + int i; + utf8 s; + + fprintf(stderr, "Testing genxCheckText\n"); + w = genxNew(NULL, NULL, NULL); + if (!w) + { + perror("genxNew"); + exit(1); + } + + if ((ret = genxCheckText(w, t1)) != GENX_SUCCESS) + ouch(w, "Error on string t1", ret, GENX_SUCCESS); + if ((ret = genxCheckText(w, t2)) != GENX_NON_XML_CHARACTER) + ouch(w, "Error on string t2", ret, GENX_NON_XML_CHARACTER); + + for (i = 0; tBadUtf8[i]; i++) + if ((ret = genxCheckText(w, tBadUtf8[i])) != GENX_BAD_UTF8) + { + char msg[1024]; + sprintf(msg, "Error on BadUTF8 #%d", i); + ouch(w, msg, ret, GENX_BAD_UTF8); + } + + s = t1; + for (i = 0; i < 4; i++) + { + int c = genxNextUnicodeChar((constUtf8 *) &s); + char msg[1024]; + + if (c == charsInT1[i]) + continue; + + sprintf(msg, "t1[%d] got %d wanted %d", i, c, charsInT1[i]); + ouch(w, msg, -1, -1); + } +} + +static void checkScrub() +{ + genxWriter w; + + // see http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF + // this is a legal UTF-8 4-char string + unsigned char t1[] = + { + 0x26, + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + unsigned char t2[] = + { + '<', 'a', '>', 1, 0 + }; + unsigned char t3[] = { 0xc0, 0xaf, 0 }; + unsigned char t4[] = { 0xc2, 0x7f, 0 }; + unsigned char t5[] = { 0x80, 0x80, 0 }; + unsigned char t6[] = { 0x80, 0x9f, 0 }; + unsigned char t7[] = { 0xe1, 0x7f, 0 }; + unsigned char t8[] = { 0xe1, 0x80, 0 }; + unsigned char t9[] = { 0xe1, 0x80, 0x7f, 0 }; + unsigned char t10[] = { 0x80, 'a', 0 }; + unsigned char t11[] = { 0x05, 'a', 0 }; + + + unsigned char * tBadUtf8[] = + { + t3, t4, t5, t6, t7, t8, t9, t10, t11, NULL + }; + unsigned char out[1024]; + int i; + + fprintf(stderr, "Testing genxScrubText\n"); + w = genxNew(NULL, NULL, NULL); + if (!w) + { + perror("genxNew"); + exit(1); + } + + if (genxScrubText(w, t1, out) != GENX_SUCCESS || + strcmp(t1, out) != 0) + ouch(w, "Error on string t1", -1, -1); + if (genxScrubText(w, t2, out) == 0 || + strlen(out) >= strlen(t2)) + ouch(w, "Error on string t2", -1, -1); + + for (i = 0; tBadUtf8[i]; i++) + if (genxScrubText(w, tBadUtf8[i], out) == 0 || + strlen(out) >= strlen(tBadUtf8[i]) || + genxCheckText(w, out) != GENX_SUCCESS) + { + char msg[1024]; + sprintf(msg, "Error on BadUTF8 #%d", i); + ouch(w, msg, -1, -1); + } +} + +void goodAttrVals(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + int status; + + // see http://www.tbray.org/ongoing/When/200x/2003/04/26/UTF + // this is a legal UTF-8 4-char string + unsigned char t1[] = + { + 0x26, + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + unsigned char t2[] = + { + ' ', '<', ' ', '>', ' ', 0xd, ' ', '"', ' ', 0 + }; + + fprintf(stderr, "Testing good Attribute values\n"); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "startEl 1", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, t1)) != GENX_SUCCESS) + ouch(w, "add a1/t1", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "end el 1", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "end doc 1", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "startEl 3", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, t2)) != GENX_SUCCESS) + ouch(w, "add a1/t2", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "end el 2", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "end doc 2", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, " " \">")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } + +} + +void checkDeclareNS() +{ + genxStatus status; + genxNamespace ns; + genxWriter w; + + fprintf(stderr, "Testing genxDeclareNamespace\n"); + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + ns = genxDeclareNamespace(w, "http://www.textuality.com/ns/", NULL, &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Declare namespace no prefix", status, GENX_SUCCESS); + + ns = genxDeclareNamespace(w, "http://www.textuality.com/ns/", "foo", + &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Declare dupe namespace", status, GENX_SUCCESS); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 2"); + exit(1); + } + + ns = genxDeclareNamespace(w, NULL, NULL, &status); + if (status != GENX_BAD_NAMESPACE_NAME || ns != NULL) + ouch(w, "Declare NULL ns uri", status, GENX_BAD_NAMESPACE_NAME); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 3"); + exit(1); + } + + ns = genxDeclareNamespace(w, "", NULL, &status); + if (status != GENX_BAD_NAMESPACE_NAME || ns != NULL) + ouch(w, "Declare EMPTY ns uri", status, GENX_BAD_NAMESPACE_NAME); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 4"); + exit(1); + } + + ns = genxDeclareNamespace(w, "", NULL, &status); + if (status != GENX_BAD_NAMESPACE_NAME || ns != NULL) + ouch(w, "Declare EMPTY ns uri", status, GENX_BAD_NAMESPACE_NAME); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 5"); + exit(1); + } + + ns = genxDeclareNamespace(w, "http://tbray.org/", "foo", &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Can't declare tbray.org=>foo", status, GENX_SUCCESS); + ns = genxDeclareNamespace(w, "http://tbray.org/", "foo", &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Can't declare tbray.org=>foo twice", status, GENX_SUCCESS); + ns = genxDeclareNamespace(w, "http://textuality.com/", "foo", &status); + if (status != GENX_DUPLICATE_PREFIX || ns != NULL) + ouch(w, "Accepted dupe prefix", status, GENX_DUPLICATE_PREFIX); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 6"); + exit(1); + } + + ns = genxDeclareNamespace(w, "http://tbray.org/", NULL, &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Can't declare tbray.org=>NULL", status, GENX_SUCCESS); + ns = genxDeclareNamespace(w, "http://foo.org/", "g1", &status); + if (status != GENX_DUPLICATE_PREFIX || ns != NULL) + ouch(w, "Accepted g-1 collision", status, GENX_DUPLICATE_PREFIX); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 6"); + exit(1); + } + ns = genxDeclareNamespace(w, "http://tbray.org/", "", &status); + if (status != GENX_SUCCESS || ns == NULL) + ouch(w, "Disallowed empty prefix", status, GENX_SUCCESS); + + ns = genxDeclareNamespace(w, "http://tbray2.org/xyz", "", &status); + if (status != GENX_DUPLICATE_PREFIX || ns != NULL) + ouch(w, "Accepted dupe URI with empty prefix", status, + GENX_DUPLICATE_PREFIX); +} + +void checkDeclareEl() +{ + genxWriter w; + genxElement el, el2; + genxStatus status; + genxNamespace ns1, ns2; + + fprintf(stderr, "Testing genxDeclareElement\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + el = genxDeclareElement(w, NULL, "a", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Ordinary declare el", status, GENX_SUCCESS); + + el = genxDeclareElement(w, NULL, "???", &status); + if (status != GENX_BAD_NAME || el != NULL) + ouch(w, "Should catch bad name", status, GENX_BAD_NAME); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 2"); + exit(1); + } + + el = genxDeclareElement(w, NULL, "a", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Ordinary declare el (2)", status, GENX_SUCCESS); + + el = genxDeclareElement(w, NULL, "a", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Dupe declare el", status, GENX_SUCCESS); + + ns1 = genxDeclareNamespace(w, "http://tbray.org/", NULL, &status); + ns2 = genxDeclareNamespace(w, "http://foo.org/", "foo", &status); + + el = genxDeclareElement(w, ns1, "x", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Basic with ns 1", status, GENX_SUCCESS); + el2 = genxDeclareElement(w, ns2, "y", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Basic with ns 2", status, GENX_SUCCESS); + el2 = genxDeclareElement(w, ns1, "x", &status); + if (status != GENX_SUCCESS || el == NULL) + ouch(w, "Dupe with ns", status, GENX_SUCCESS); + if (el != el2) + ouch(w, "Dupe made new el object?!?", -1, -1); +} + +void checkDeclareAttr() +{ + genxWriter w; + genxAttribute a, a2; + genxStatus status; + genxNamespace ns1, ns2; + + fprintf(stderr, "Testing genxDeclareAttribute\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + a = genxDeclareAttribute(w, NULL, "a", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Ordinary declare attr", status, GENX_SUCCESS); + + a = genxDeclareAttribute(w, NULL, "^^^", &status); + if (status != GENX_BAD_NAME || a != NULL) + ouch(w, "Should catch bad name", status, GENX_BAD_NAME); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew 2"); + exit(1); + } + + a = genxDeclareAttribute(w, NULL, "a", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Ordinary declare attr (2)", status, GENX_SUCCESS); + + a = genxDeclareAttribute(w, NULL, "a", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Dupe declare attr", status, GENX_SUCCESS); + + ns1 = genxDeclareNamespace(w, "http://tbray.org/", NULL, &status); + ns2 = genxDeclareNamespace(w, "http://foo.org/", "foo", &status); + + a = genxDeclareAttribute(w, ns1, "x", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Basic with ns 1", status, GENX_SUCCESS); + a2 = genxDeclareAttribute(w, ns2, "y", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Basic with ns 2", status, GENX_SUCCESS); + a2 = genxDeclareAttribute(w, ns1, "x", &status); + if (status != GENX_SUCCESS || a == NULL) + ouch(w, "Dupe with ns", status, GENX_SUCCESS); + if (a != a2) + ouch(w, "Dupe made new attr object?!?", -1, -1); +} + +void checkSeq1(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* 2 start-docs */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq1 startDoc", status, GENX_SUCCESS); + if ((status = genxStartDocFile(w, f)) != GENX_SEQUENCE_ERROR) + ouch(w, "seq1 double startDoc", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq2(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* missing startDoc */ + if ((status = genxStartElement(ela)) != GENX_SEQUENCE_ERROR) + ouch(w, "seq1 startel no startdoc", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq3(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* endel without start */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq1 startDoc", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SEQUENCE_ERROR) + ouch(w, "seq1 bogus endel", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq4(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* enddoc with stuff on stack */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq4 startDoc", status, GENX_SUCCESS); + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "seq4 startEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SEQUENCE_ERROR) + ouch(w, "premature end", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq5(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* premature end-doc */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq5 startDoc", status, GENX_SUCCESS); + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "seq5 startEl a", status, GENX_SUCCESS); + if ((status = genxStartElement(elb)) != GENX_SUCCESS) + ouch(w, "seq5 startEl b", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "seq5 end el", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SEQUENCE_ERROR) + ouch(w, "premature end", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq6(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* addAttr not after startEl */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq5 startDoc", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, "x")) != GENX_SEQUENCE_ERROR) + ouch(w, "bogus addattr", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq7(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* addText w/o startEl */ + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "seq5 startDoc", status, GENX_SUCCESS); + if ((status = genxAddText(w, "x")) != GENX_SEQUENCE_ERROR) + ouch(w, "bogus addattr", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkSeq8(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + /* start comment without startdoc */ + if ((status = genxComment(w, "foo")) != GENX_SEQUENCE_ERROR) + ouch(w, "comment pre startdoc", status, GENX_SEQUENCE_ERROR); + fclose(f); +} + +void checkNSDecls(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + genxStatus status; + char * wanted = ""; + + fprintf(stderr, "Testing namespace declarations\n"); + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "StartEl ela", status, GENX_SUCCESS); + if ((status = genxStartElement(elb)) != GENX_SUCCESS) + ouch(w, "StartEl elb", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "EndElb", status, GENX_SUCCESS); + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "StartEl child ela", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a3, "x")) != GENX_SUCCESS) + ouch(w, "AddAttr a3", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "EndEla child", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "EndEla root", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "enddoc", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, wanted)) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got \n[%s], wanted \n[%s]\n", iobuf.buf, + wanted); + ouch(w, msg, -1, -1); + } + + /* + * see if it blows up, anyhwo + */ + genxDispose(w); +} + +void checkDupeAttr(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + genxStatus status; + + fprintf(stderr, "Testing duplicate attributes\n"); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "AddEl ela", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, "1")) != GENX_SUCCESS) + ouch(w, "AddAttr a1", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, "1")) != GENX_DUPLICATE_ATTRIBUTE) + ouch(w, "Dupe attr", status, GENX_DUPLICATE_ATTRIBUTE); +} + +void checkAttrOrder(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + genxStatus status; + utf8 wanted = ""; + + fprintf(stderr, "Testing attribute sorting\n"); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "AddEl ela", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, "1")) != GENX_SUCCESS) + ouch(w, "AddAttr a1", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a2, "2")) != GENX_SUCCESS) + ouch(w, "AddAttr a2", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a3, "3")) != GENX_SUCCESS) + ouch(w, "AddAttr a3", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "EndEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "EndDoc", status, GENX_SUCCESS); + + + if (strcmp(iobuf.buf, wanted)) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got \n[%s], wanted\n[%s]", iobuf.buf, wanted); + ouch(w, msg, -1, -1); + } +} + + + +void checkWriting(void (*t)(genxWriter w, + genxNamespace, genxNamespace, + genxElement, genxElement, genxElement, + genxAttribute, genxAttribute, genxAttribute)) +{ + genxWriter w; + genxNamespace ns1, ns2; + genxElement ela, elb, elc; + genxAttribute a1, a2, a3; + genxStatus status; + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + ns1 = genxDeclareNamespace(w, "http://example.com/1", NULL, &status); + if (ns1 == NULL || status != GENX_SUCCESS) + ouch(w, "declare ns1", status, GENX_SUCCESS); + ns2 = genxDeclareNamespace(w, "http://example.com/2", "a-ns2", &status); + if (ns2 == NULL || status != GENX_SUCCESS) + ouch(w, "declare ns2", status, GENX_SUCCESS); + + ela = genxDeclareElement(w, NULL, "a", &status); + if (ela == NULL || status != GENX_SUCCESS) + ouch(w, "declare ela", status, GENX_SUCCESS); + elb = genxDeclareElement(w, ns1, "b", &status); + if (elb == NULL || status != GENX_SUCCESS) + ouch(w, "declare elb", status, GENX_SUCCESS); + elc = genxDeclareElement(w, ns2, "c", &status); + if (elc == NULL || status != GENX_SUCCESS) + ouch(w, "declare elc", status, GENX_SUCCESS); + + a1 = genxDeclareAttribute(w, NULL, "a1", &status); + if (a1 == NULL || status != GENX_SUCCESS) + ouch(w, "declare a1", status, GENX_SUCCESS); + a2 = genxDeclareAttribute(w, ns2, "a2", &status); + if (a2 == NULL || status != GENX_SUCCESS) + ouch(w, "declare a1", status, GENX_SUCCESS); + a3 = genxDeclareAttribute(w, ns1, "a3", &status); + if (a3 == NULL || status != GENX_SUCCESS) + ouch(w, "declare a3", status, GENX_SUCCESS); + + (*t)(w, ns1, ns2, ela, elb, elc, a1, a2, a3); +} + +utf8 badVal; +genxStatus baWanted; + +void checkBA(genxWriter w, + genxNamespace ns1, genxNamespace ns2, + genxElement ela, genxElement elb, genxElement elc, + genxAttribute a1, genxAttribute a2, genxAttribute a3) +{ + FILE * f = fopen(DEVNULL, "w"); + genxStatus status; + + if ((status = genxStartDocFile(w, f)) != GENX_SUCCESS) + ouch(w, "ba startDoc", status, GENX_SUCCESS); + if ((status = genxStartElement(ela)) != GENX_SUCCESS) + ouch(w, "ba startEl", status, GENX_SUCCESS); + if ((status = genxAddAttribute(a1, badVal)) != baWanted) + ouch(w, "ba addAttr", status, baWanted); + fclose(f); +} + +void checkBadAttrVals() +{ + unsigned char t2[] = + { + '<', 'a', '>', 1, 0 + }; + unsigned char t3[] = { 0xc0, 0xaf, 0 }; + unsigned char t4[] = { 0xc2, 0x7f, 0 }; + unsigned char t5[] = { 0x80, 0x80, 0 }; + unsigned char t6[] = { 0x80, 0x9f, 0 }; + unsigned char t7[] = { 0xe1, 0x7f, 0 }; + unsigned char t8[] = { 0xe1, 0x80, 0 }; + unsigned char t9[] = { 0xe1, 0x80, 0x7f, 0 }; + + unsigned char * tBadUtf8[] = + { + t3, t4, t5, t6, t7, t8, t9, NULL + }; + int i; + + fprintf(stderr, "Testing bad attribute values\n"); + badVal = t2; + baWanted = GENX_NON_XML_CHARACTER; + checkWriting(&checkBA); + + baWanted = GENX_BAD_UTF8; + for (i = 0; tBadUtf8[i]; i++) + { + badVal = tBadUtf8[i]; + checkWriting(&checkBA); + } +} + +void checkHello() +{ + genxElement greeting; + genxStatus status; + genxWriter w; + + fprintf(stderr, "Testing Hello world\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxAddText(w, "Hello world!")) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endElement", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, "Hello world!")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } +} + +void * bustedAlloc(void * userData, int bytes) +{ + return NULL; +} + +void checkAllocator() +{ + genxElement greeting; + genxStatus status; + genxWriter w; + + fprintf(stderr, "Testing allocator\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + genxSetAlloc(w, &bustedAlloc); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting != NULL || status != GENX_ALLOC_FAILED) + ouch(w, "expectd alloc_failed", status, GENX_ALLOC_FAILED); +} + +void checkHelloNS() +{ + genxElement greeting; + genxStatus status; + genxWriter w; + genxNamespace ns; + + fprintf(stderr, "Testing Namespaced Hello world\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + ns = genxDeclareNamespace(w, "http://example.org/x", "eg", &status); + if (ns == NULL || status != GENX_SUCCESS) + ouch(w, "DeclareNS", status, GENX_SUCCESS); + greeting = genxDeclareElement(w, ns, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxAddText(w, "Hello world!")) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endElement", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, "Hello world!")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } +} + +void checkIOError() +{ + genxElement greeting; + genxStatus status; + genxWriter w; + genxSender sender = { &brokenSends, &sendb, &sflush }; + + fprintf(stderr, "Testing IO error\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "Start Element", status, GENX_SUCCESS); + if ((status = genxAddText(w, "x")) != GENX_IO_ERROR) + ouch(w, "AddText", status, GENX_IO_ERROR); +} + +void checkAddText() +{ + int i; + genxElement greeting; + genxStatus status; + genxWriter w; + unsigned char input[] = + { + '&', + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + + unsigned char expected [] = + { + '&', 'a', 'm', 'p', ';', + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + unsigned char t2[] = + { + ' ', '<', ' ', '>', ' ', 0xd, ' ', '"', ' ', 0 + }; + + fprintf(stderr, "Testing AddText\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + + if ((status = genxAddText(w, input)) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + for (i = 1; iobuf.buf[i] != '<'; i++) + ; + iobuf.buf[i] = 0; + for (i = 0; iobuf.buf[i] != '>'; i++) + ; + i++; + if (strcmp(iobuf.buf + i, expected)) + ouch(w, "incorrect UTF8", -1, -1); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + genxSetUserData(w, &iobuf); + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + + if ((status = genxAddText(w, t2)) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + for (i = 1; iobuf.buf[i] != '<'; i++) + ; + iobuf.buf[i] = 0; + for (i = 0; iobuf.buf[i] != '>'; i++) + ; + i++; + if (strcmp(iobuf.buf + i, " < > \" ")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf + i); + ouch(w, msg, -1, -1); + } + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + genxSetUserData(w, &iobuf); + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + + if ((status = genxAddBoundedText(w, input, input + 10)) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + for (i = 1; iobuf.buf[i] != '<'; i++) + ; + iobuf.buf[i] = 0; + for (i = 0; iobuf.buf[i] != '>'; i++) + ; + i++; + if (strcmp(iobuf.buf + i, expected)) + ouch(w, "incorrect UTF8", -1, -1); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + genxSetUserData(w, &iobuf); + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + + if ((status = genxAddCountedText(w, input, 10)) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + for (i = 1; iobuf.buf[i] != '<'; i++) + ; + iobuf.buf[i] = 0; + for (i = 0; iobuf.buf[i] != '>'; i++) + ; + i++; + if (strcmp(iobuf.buf + i, expected)) + ouch(w, "incorrect UTF8", -1, -1); + + +} + +void checkAddChar() +{ + int i; + genxElement greeting; + genxStatus status; + genxWriter w; + unsigned char expected [] = + { + '&', 'a', 'm', 'p', ';', + 0xd0, 0x96, + 0xe4, 0xb8, 0xad, + 0xF0, 0x90, 0x8D, 0x86, + 0 + }; + int input[] = { 0x26, 0x416, 0x4e2d, 0x10346, 0 }; + + fprintf(stderr, "Testing AddChar\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + + for (i = 0; input[i]; i++) + if ((status = genxAddCharacter(w, input[i])) != GENX_SUCCESS) + ouch(w, "addchar", status, GENX_SUCCESS); + + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endEl", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + /* find the string in the output */ + for (i = 1; iobuf.buf[i] != '<'; i++) + ; + iobuf.buf[i] = 0; + for (i = 0; iobuf.buf[i] != '>'; i++) + ; + i++; + if (strcmp(iobuf.buf + i, expected)) + ouch(w, "incorrect UTF8", -1, -1); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + genxSetUserData(w, &iobuf); + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxAddCharacter(w, -5)) != GENX_NON_XML_CHARACTER) + ouch(w, "Add -5", status, GENX_NON_XML_CHARACTER); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + genxSetUserData(w, &iobuf); + iobuf.nowAt = iobuf.buf; + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxAddCharacter(w, 1)) != GENX_NON_XML_CHARACTER) + ouch(w, "Add -5", status, GENX_NON_XML_CHARACTER); +} + +void checkComment() +{ + genxWriter w; + genxElement greeting; + genxStatus status; + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + fprintf(stderr, "Testing comments\n"); + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxComment(w, "1")) != GENX_SUCCESS) + ouch(w, "Comment 1", status, GENX_SUCCESS); + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxComment(w, "2")) != GENX_SUCCESS) + ouch(w, "Comment 2", status, GENX_SUCCESS); + if ((status = genxAddText(w, "[1]")) != GENX_SUCCESS) + ouch(w, "Addtext 1", status, GENX_SUCCESS); + if ((status = genxComment(w, "3")) != GENX_SUCCESS) + ouch(w, "Comment 3", status, GENX_SUCCESS); + if ((status = genxAddText(w, "[2]")) != GENX_SUCCESS) + ouch(w, "Addtext 2", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endel", status, GENX_SUCCESS); + if ((status = genxComment(w, "4")) != GENX_SUCCESS) + ouch(w, "Comment 3", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, "\n[1][2]\n")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender 2", status, GENX_SUCCESS); + + if ((status = genxComment(w, "-foo")) != GENX_MALFORMED_COMMENT) + ouch(w, "missed leading -", status, GENX_MALFORMED_COMMENT); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender 3", status, GENX_SUCCESS); + + if ((status = genxComment(w, "foo-")) != GENX_MALFORMED_COMMENT) + ouch(w, "missed trailing -", status, GENX_MALFORMED_COMMENT); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender 3", status, GENX_SUCCESS); + + if ((status = genxComment(w, "foo--bar")) != GENX_MALFORMED_COMMENT) + ouch(w, "missed --", status, GENX_MALFORMED_COMMENT); +} + +void checkPI() +{ + genxWriter w; + genxElement greeting; + genxStatus status; + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + fprintf(stderr, "Testing PIs\n"); + greeting = genxDeclareElement(w, NULL, "greeting", &status); + if (greeting == NULL || status != GENX_SUCCESS) + ouch(w, "Declare greeting", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxPI(w, "pi1", "1")) != GENX_SUCCESS) + ouch(w, "PI 1", status, GENX_SUCCESS); + if ((status = genxStartElement(greeting)) != GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxPI(w, "pi2", "2")) != GENX_SUCCESS) + ouch(w, "PI 2", status, GENX_SUCCESS); + if ((status = genxAddText(w, "[1]")) != GENX_SUCCESS) + ouch(w, "Addtext 1", status, GENX_SUCCESS); + if ((status = genxPI(w, "pi3", "3")) != GENX_SUCCESS) + ouch(w, "PI 3", status, GENX_SUCCESS); + if ((status = genxAddText(w, "[2]")) != GENX_SUCCESS) + ouch(w, "Addtext 2", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endel", status, GENX_SUCCESS); + if ((status = genxPI(w, "pi4", "4")) != GENX_SUCCESS) + ouch(w, "PI 4", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + /* + if (strcmp(iobuf.buf, "\n[1][2]\n")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } + */ + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender 3", status, GENX_SUCCESS); + + if ((status = genxPI(w, "pi5", "foo?>bar")) != GENX_MALFORMED_PI) + ouch(w, "missed ?>", status, GENX_MALFORMED_PI); +} + +void checkHelloLiteral() +{ + genxWriter w; + genxStatus status; + genxNamespace ns; + + + fprintf(stderr, "Testing Hello world (Literal)\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + ns = genxDeclareNamespace(w, "foo:bar", "baz", &status); + if (ns == NULL || status != GENX_SUCCESS) + ouch(w, "Declare namespace", status, GENX_SUCCESS); + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + if ((status = genxStartElementLiteral(w, "foo:bar", "greeting")) != + GENX_SUCCESS) + ouch(w, "StartElement", status, GENX_SUCCESS); + if ((status = genxAddText(w, "Hello world!")) != GENX_SUCCESS) + ouch(w, "addText", status, GENX_SUCCESS); + if ((status = genxEndElement(w)) != GENX_SUCCESS) + ouch(w, "endElement", status, GENX_SUCCESS); + if ((status = genxEndDocument(w)) != GENX_SUCCESS) + ouch(w, "endDoc", status, GENX_SUCCESS); + + if (strcmp(iobuf.buf, "Hello world!")) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got [%s]", iobuf.buf); + ouch(w, msg, -1, -1); + } +} + +void checkStress() +{ + genxWriter w; + unsigned char ename[100]; + unsigned char aname[100]; + unsigned char nname[100]; + int acount; + int alength; + char avchar; + unsigned char aval[10000]; + genxStatus status; + int i, j, k; + + fprintf(stderr, "Testing memory management\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + status = genxStartDocFile(w, stdout); + if (status != GENX_SUCCESS) + ouch(w, "startDocFile", status, GENX_SUCCESS); + + if (genxStartElementLiteral(w, NULL, "root")) + ouch2(w, "root"); + SRANDOM(581213); + for (i = 1; i < 100; i++) + { + if ((i % 3) == 0) + if (genxEndElement(w)) + ouch2(w, "end el"); + + /* + if ((i % 200) == 0) + fprintf(stderr, " %d elements\n", i); + */ + sprintf(nname, "n%d", (int) (RANDOM() % 10000)); + sprintf(ename, "e%d", (int) (RANDOM() % 10000)); + if (genxStartElementLiteral(w, nname, ename)) + ouch2(w, "start el"); + acount = RANDOM() % 20; + for (j = 0; j < acount; j++) + { + alength = RANDOM() % 10000; + avchar = 'A' + RANDOM() % 40; + for (k = 0; k < alength; k++) + aval[k] = avchar; + aval[k] = 0; + sprintf(nname, "n%d", (int) (RANDOM() % 10000)); + sprintf(aname, "a%d", (int) (RANDOM() % 10000)); + if ((status = genxAddAttributeLiteral(w, nname, aname, aval))) + { + if (status != GENX_DUPLICATE_ATTRIBUTE) + ouch2(w, "add attr"); + } + } + if (genxAddText(w, "\n")) + ouch2(w, "add text"); + } + while ((status = genxEndElement(w)) == GENX_SUCCESS) + ; + if (status != GENX_SEQUENCE_ERROR) + ouch2(w, "unwind"); + if (genxEndDocument(w)) + ouch2(w, "end doc"); +} + +void checkNCNames() +{ + genxStatus status; + genxElement e; + genxAttribute a; + genxNamespace n; + genxWriter w; + + fprintf(stderr, "Testing colon suppression\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + if ((status = genxStartDocFile(w, stdout))) + ouch(w, "start doc", status, GENX_SUCCESS); + + n = genxDeclareNamespace(w, "http://somewhere/", "foo:bar", &status); + if (n != NULL || status != GENX_BAD_NAME) + ouch(w, "declare ns with colon", status, GENX_BAD_NAME); + e = genxDeclareElement(w, NULL, "foo:bar", &status); + if (e != NULL || status != GENX_BAD_NAME) + ouch(w, "declare e with colon", status, GENX_BAD_NAME); + a = genxDeclareAttribute(w, NULL, "foo:bar", &status); + if (a != NULL || status != GENX_BAD_NAME) + ouch(w, "declare e with colon", status, GENX_BAD_NAME); +} + +void checkDefaultNS() +{ + genxStatus status; + genxAttribute a; + genxNamespace nDefault, nPrefix, nBad; + genxWriter w; + genxElement eNaked, eDefault, ePrefix; + utf8 wanted; + + fprintf(stderr, "Testing namespace defaulting\n"); + + if ((w = genxNew(NULL, NULL, NULL)) == NULL) + { + perror("genxNew"); + exit(1); + } + + iobuf.nowAt = iobuf.buf; + genxSetUserData(w, &iobuf); + status = genxStartDocSender(w, &sender); + if (status != GENX_SUCCESS) + ouch(w, "startDocSender", status, GENX_SUCCESS); + + nDefault = genxDeclareNamespace(w, "http://def", "", &status); + if (nDefault == NULL || status != GENX_SUCCESS) + ouch(w, "declare nDefault", status, GENX_SUCCESS); + nPrefix = genxDeclareNamespace(w, "http://pref", "pref", &status); + if (nPrefix == NULL || status != GENX_SUCCESS) + ouch(w, "declare nPrefix", status, GENX_SUCCESS); + + a = genxDeclareAttribute(w, nDefault, "foo", &status); + if (a != NULL || status != GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE) + ouch(w, "Allowed declaration of attr in default namespace", status, + GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE); + + eNaked = genxDeclareElement(w, NULL, "e", &status); + if (eNaked == NULL || status != GENX_SUCCESS) + ouch(w, "declare eNaked", status, GENX_SUCCESS); + eDefault = genxDeclareElement(w, nDefault, "eD", &status); + if (eDefault == NULL || status != GENX_SUCCESS) + ouch(w, "declare eDefault", status, GENX_SUCCESS); + ePrefix = genxDeclareElement(w, nPrefix, "eP", &status); + if (ePrefix == NULL || status != GENX_SUCCESS) + ouch(w, "declare ePrefix", status, GENX_SUCCESS); + + if (genxStartElement(eDefault)) + ouch2(w, "start eDefault"); + if (genxAddNamespace(nPrefix, NULL)) + ouch2(w, "add nPrefix"); + if (genxStartElement(eNaked)) + ouch2(w, "start naked"); + + nBad = genxDeclareNamespace(w, "http://pref", "foobar", &status); + if (nBad == NULL || status != GENX_SUCCESS) + ouch(w, "bogus redeclare", status, GENX_SUCCESS); + + if (genxEndElement(w)) + ouch2(w, "end naked"); + if (genxStartElement(ePrefix)) + ouch2(w, "inner prefix"); + if (genxUnsetDefaultNamespace(w)) + ouch2(w, "unset default"); + if (genxStartElement(eNaked)) + ouch2(w, "inner naked"); + if (genxEndElement(w)) + ouch2(w, "end inner naked"); + if (genxStartElement(eDefault)) + ouch2(w, "inner default"); + + while ((status = genxEndElement(w)) == GENX_SUCCESS) + ; + if (status != GENX_SEQUENCE_ERROR) + ouch2(w, "unwind"); + if (genxEndDocument(w)) + ouch2(w, "end doc"); + + wanted = ""; + if (strcmp(iobuf.buf, wanted)) + { + char msg[1024]; + sprintf(msg, "strcmp failed, got \n[%s] wanted \n[%s]", iobuf.buf, wanted); + ouch(w, msg, -1, -1); + } +} + +/* + * genx test driver + */ +int main(int argc, char * argv[]) +{ + checkUTF8(); + checkScrub(); + + checkDeclareNS(); + checkDefaultNS(); + + checkDeclareEl(); + checkDeclareAttr(); + + checkNCNames(); + + fprintf(stderr, "Testing Sequencing\n"); + checkWriting(&checkSeq1); + checkWriting(&checkSeq2); + checkWriting(&checkSeq3); + checkWriting(&checkSeq4); + checkWriting(&checkSeq5); + checkWriting(&checkSeq6); + checkWriting(&checkSeq7); + checkWriting(&checkSeq8); + + checkHello(); + checkHelloNS(); + + checkWriting(&checkAttrOrder); + checkWriting(&checkDupeAttr); + + checkIOError(); + + checkWriting(&checkNSDecls); + + checkAllocator(); + + checkWriting(&goodAttrVals); + + checkBadAttrVals(); + + checkAddChar(); + checkAddText(); + + checkComment(); + checkPI(); + + checkHelloLiteral(); + checkStress(); + + fprintf(stderr, "FAILED TESTS: %d\n", errorcount); + exit(errorcount); +} + diff --git a/genx.lua b/genx.lua new file mode 100644 index 0000000..8a0e143 --- /dev/null +++ b/genx.lua @@ -0,0 +1,145 @@ +--genx xml generator binding. +local ffi = require'ffi' +local C = ffi.load'genx' +local M = {C = C} +require'genx_h' + +local function checkh(w, statusP, h) + if h ~= nil then return h end + local s = C.genxGetErrorMessage(w, statusP[0]) + error(s ~= nil and ffi.string(s) or 'unknown error') +end + +local function checknz(w, status) + if status == 0 then return end + local s = C.genxGetErrorMessage(w, status) + error(s ~= nil and ffi.string(s) or 'unknown error') +end + +local function nzcaller(f) + return function(w, ...) + return checknz(w, f(w, ...)) + end +end + +function M.version() + return ffi.string(C.genxGetVersion()) +end + +function M.new(alloc, dealloc, userdata) + local w = C.genxNew(alloc, dealloc, userdata) + assert(w ~= nil, 'out of memory') + ffi.gc(w, C.genxDispose) + return w +end + +local senders = {} --{[genxWriter] = genxSender} + +function free_sender(w) + local sender = senders[w] + if not sender then return end + sender.send:free() + sender.sendBounded:free() + sender.flush:free() + senders[w] = nil +end + +local function free(w) + C.genxDispose(ffi.gc(w, nil)) + free_sender(w) +end + +ffi.metatype('genxWriter_rec', {__index = { + + free = free, + + start_doc = function(w, f, ...) + free_sender(w) + if type(f) == 'function' then + --f is called as either: f(s), f(s, sz), or f() to signal EOF. + local sender = ffi.new'genxSender' + sender.send = ffi.new('send_callback', function(_, s) f(s); return 0 end) + sender.sendBounded = ffi.new('sendBounded_callback', function(_, p1, p2) f(p1, p2-p1); return 0 end) + sender.flush = ffi.new('flush_callback', function() f(); return 0 end) + senders[w] = sender + checknz(w, C.genxStartDocSender(w, sender)) + else + checknz(w, C.genxStartDocFile(w, f)) + end + end, + + end_doc = nzcaller(C.genxEndDocument), + + ns = function(w, uri, prefix, statusP) + statusP = statusP or ffi.new'genxStatus[1]' + return checkh(w, statusP, C.genxDeclareNamespace(w, uri, prefix, statusP)) + end, + + element = function(w, name, ns, statusP) + statusP = statusP or ffi.new'genxStatus[1]' + return checkh(w, statusP, C.genxDeclareElement(w, ns, name, statusP)) + end, + + attr = function(w, name, ns, statusP) + statusP = statusP or ffi.new'genxStatus[1]' + return checkh(w, statusP, C.genxDeclareAttribute(w, ns, name, statusP)) + end, + + comment = nzcaller(C.genxComment), + + pi = nzcaller(C.genxPI), + + start_element = function(w, e, ns, prefix) + if type(ns) == 'string' then + ns = w:ns(ns, prefix) + end + if type(e) == 'string' then + e = w:element(e, ns) + end + checknz(w, C.genxStartElement(e)) + end, + + add_attr = function(w, a, val, ns, prefix) + if type(ns) == 'string' then + ns = w:ns(ns, prefix) + end + if type(a) == 'string' then + a = w:attr(a, ns) + end + checknz(w, C.genxAddAttribute(a, val)) + end, + + add_ns = function(w, ns, prefix) + if type(ns) == 'string' then + ns = w:ns(ns, prefix) + end + checknz(w, C.genxAddNamespace(ns, prefix)) + end, + + unset_default_namespace = nzcaller(C.genxUnsetDefaultNamespace), + + end_element = nzcaller(C.genxEndElement), + + text = function(w, s, sz) + checknz(w, C.genxAddCountedText(w, s, sz or #s)) + end, + + char = nzcaller(C.genxAddCharacter), + + check_text = C.genxCheckText, + + scrub_text = function(s_in) + s_out = ffi.new('constUtf8[?]', #s_in + 1) + if C.genxScrubText(s_in, s_out) ~= 0 then + return ffi.string(s_out) + else + return s_in + end + end, + +}, __gc = free}) + +if not ... then require'genx_demo' end + +return M + diff --git a/genx.md b/genx.md new file mode 100644 index 0000000..7826a8a --- /dev/null +++ b/genx.md @@ -0,0 +1,63 @@ +--- +project: genx +tagline: genx Lua+ffi binding (XML generation) +--- + +v1.0 | genx beta5 | LuaJIT 2 + +## `local genx = require'genx'` + +A ffi binding of [genx], a library for generating well-formed canonical XML documents, written by Tim Bray. + +## Features: + + * does all necessary XML escaping. + * prevents generating text that isn't well-formed. + * generates namespace prefixes. + * produces Canonical XML, suitable for use with digital signatures. + +## Limitations: + + * only UTF8 encoding supported + * no empty element tags + * no `` declarations (write it yourself before calling `w:start_doc()`) + * no pretty-printing (add linebreaks and indentation yourself with `w:text()` where needed) + +## Example: + +~~~{.lua} +local w = genx.new() +w:start_doc(io.stdout) +w:start_element'root' +w:text'hello' +w:end_element() +w:end_doc() +w:free() +~~~ + +------------------------------------------------------------ -------------------------------------------------------------------------------- +`genx.new() -> w` Create a new genx writer. +`w:free()` Free the genx writer. +`w:start_doc(file)` Start an XML document on a `FILE *` or Lua file object +`w:start_doc(write)` Start an XML document on a write function to be called as `write([s[, size]])` +`w:end_doc()` Flush pending updates and release the file handle +`w:ns(uri[, prefix]) -> ns` Declare a namespace for reuse. The same namespace can be declared multiple times. +`w:element(name[, ns | uri,prefix]) -> elem` Declare an element for reuse. The same element can be declared multiple times. +`w:attr(name[, ns | uri,prefix]) -> attr` Declare an attribute for reuse. The same attribute can be declared multiple times. +`w:comment(s)` Add a comment to the current XML stream. +`w:pi(target, text)` Add a PI to the current XML stream. +`w:start_element(elem | name [, ns | uri,prefix])` Start a new XML element. +`w:end_element()` End the current element. +`w:add_attr(attr, val[, ns | uri,prefix])` Add an attribute to the current element. Attributes are sorted by name in the output stream. +`w:add_ns(ns | [uri,prefix])` Add a namespace to the current element. +`w:unset_default_namespace()` Add a `xmlns=""` declaration to unset the default namespace declaration. This is a no-op if no default namespace is in effect. +`w:text(s[, size])` Add utf-8 text. +`w:char(char)` Add an unicode code point. +`w:check_text(s) -> genxStatus` Check utf-8 text. +`w:scrub_text(s) -> s` Scrub utf-8 text of invalid characters. +------------------------------------------------------------ -------------------------------------------------------------------------------- + +See the [genx manual] for more info. + +[genx]: http://www.tbray.org/ongoing/When/200x/2004/02/20/GenxStatus +[genx manual]: http://www.tbray.org/ongoing/genx/docs/Guide.html diff --git a/genx_demo.lua b/genx_demo.lua new file mode 100644 index 0000000..1b5053d --- /dev/null +++ b/genx_demo.lua @@ -0,0 +1,49 @@ +local genx = require'genx' +local ffi = require'ffi' + +print('version', genx.version()) + +local w = genx.new() + +local ns1 = w:ns('ns1', 'pns1') +local ns2 = w:ns('ns2', 'pns2') +local body = w:element('body', ns1) +local a1 = w:attr('a1') +local a2 = w:attr('a2') + +w:start_doc(io.stdout) +w:start_element'root' +w:text'hello' +w:end_element() +w:end_doc() +print() + +w:start_doc(function(s, sz) + s = s and (sz and ffi.string(s, sz) or ffi.string(s)) or '\n!EOF\n' + io.write(s) +end) + +w:start_element('html') +w:add_ns(ns1) +w:add_ns(ns2, 'g') + + w:text'\n\t' + w:start_element('head') + w:add_attr('b', 'vb') + w:add_attr('a', 'va') + w:text'hello' + w:end_element() + w:text'\n\t' + + w:start_element(body) + w:add_attr(a1, 'v1') + w:add_attr(a2, 'v2') + w:text'hey' + w:end_element() + w:text'\n' + +w:end_element() + +w:end_doc() + +w:free() diff --git a/genx_h.lua b/genx_h.lua new file mode 100644 index 0000000..51596ef --- /dev/null +++ b/genx_h.lua @@ -0,0 +1,98 @@ +--result of cpp genx.h from genx beta5 +local ffi = require'ffi' +require'stdio_h' + +ffi.cdef[[ +typedef enum +{ + GENX_SUCCESS = 0, + GENX_BAD_UTF8, + GENX_NON_XML_CHARACTER, + GENX_BAD_NAME, + GENX_ALLOC_FAILED, + GENX_BAD_NAMESPACE_NAME, + GENX_INTERNAL_ERROR, + GENX_DUPLICATE_PREFIX, + GENX_SEQUENCE_ERROR, + GENX_NO_START_TAG, + GENX_IO_ERROR, + GENX_MISSING_VALUE, + GENX_MALFORMED_COMMENT, + GENX_XML_PI_TARGET, + GENX_MALFORMED_PI, + GENX_DUPLICATE_ATTRIBUTE, + GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE, + GENX_DUPLICATE_NAMESPACE, + GENX_BAD_DEFAULT_DECLARATION +} genxStatus; + +typedef unsigned char * utf8; +typedef const unsigned char * constUtf8; + +typedef struct genxWriter_rec_ genxWriter_rec, * genxWriter; +typedef struct genxNamespace_rec_ genxNamespace_rec, * genxNamespace; +typedef struct genxElement_rec_ genxElement_rec, * genxElement; +typedef struct genxAttribute_rec_ genxAttribute_rec, * genxAttribute; + +genxWriter genxNew( + void * (* alloc)(void * userData, int bytes), + void (* dealloc)(void * userData, void * data), + void * userData); +void genxDispose(genxWriter w); + +void genxSetUserData(genxWriter w, void * userData); +void * genxGetUserData(genxWriter w); +void genxSetAlloc(genxWriter w, void * (* alloc)(void * userData, int bytes)); +void genxSetDealloc(genxWriter w, void (* dealloc)(void * userData, void * data)); +void * (* genxGetAlloc(genxWriter w))(void * userData, int bytes); +void (* genxGetDealloc(genxWriter w))(void * userData, void * data); + +utf8 genxGetNamespacePrefix(genxNamespace ns); + +genxNamespace genxDeclareNamespace(genxWriter w, + constUtf8 uri, constUtf8 prefix, + genxStatus * statusP); + +genxElement genxDeclareElement(genxWriter w, + genxNamespace ns, constUtf8 type, + genxStatus * statusP); + +genxAttribute genxDeclareAttribute(genxWriter w, + genxNamespace ns, + constUtf8 name, genxStatus * statusP); + +genxStatus genxStartDocFile(genxWriter w, FILE * file); + +typedef genxStatus (* send_callback)(void * userData, constUtf8 s); +typedef genxStatus (* sendBounded_callback)(void * userData, constUtf8 start, constUtf8 end); +typedef genxStatus (* flush_callback)(void * userData); + +typedef struct { + send_callback send; + sendBounded_callback sendBounded; + flush_callback flush; +} genxSender; + +genxStatus genxStartDocSender(genxWriter w, genxSender * sender); +genxStatus genxEndDocument(genxWriter w); +genxStatus genxComment(genxWriter w, constUtf8 text); +genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text); +genxStatus genxStartElementLiteral(genxWriter w, constUtf8 xmlns, constUtf8 type); +genxStatus genxStartElement(genxElement e); +genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns, constUtf8 name, constUtf8 value); +genxStatus genxAddAttribute(genxAttribute a, constUtf8 value); +genxStatus genxAddNamespace(genxNamespace ns, constUtf8 prefix); // NOTE: prefix changed from utf8 to constUtf8 +genxStatus genxUnsetDefaultNamespace(genxWriter w); +genxStatus genxEndElement(genxWriter w); +genxStatus genxAddText(genxWriter w, constUtf8 start); +genxStatus genxAddCountedText(genxWriter w, constUtf8 start, int byteCount); +genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end); +genxStatus genxAddCharacter(genxWriter w, int c); +int genxNextUnicodeChar(constUtf8 * sp); +genxStatus genxCheckText(genxWriter w, constUtf8 s); +int genxCharClass(genxWriter w, int c); +int genxScrubText(genxWriter w, constUtf8 in, utf8 out); +char * genxGetErrorMessage(genxWriter w, genxStatus status); +char * genxLastErrorMessage(genxWriter w); +char * genxGetVersion(); +]] diff --git a/linux/bin/libgenx.so b/linux/bin/libgenx.so new file mode 100644 index 0000000000000000000000000000000000000000..194ff243bead6b8ada93f97583ac36382181d1f1 GIT binary patch literal 41904 zcmb__3tUxI_W$9bl2I3nDJv@~YqWe(R-&dNqLxyYVh>{!6_gnSgL~B`N%V4+)9ET^ z@5j_s({Gv{V`)Cnug&EE-!iX^F8gRB@3K$QcgD_XoVR33C6hH)maKdx(W^~2R2*A=)<$8|2Qt8nqx1sB9)MBzFES3Ir@a5=x;&O;kG zK>J;yk^EnX>uOx6D&rzt&abcY0Hn-ZxFCM@yA=0}ah;EApz<+{%QO;B3waKG{H=DJ zqmgw@d7q`-g!6Fq((WrY{zfC~oImnSc~J(u;-*FPxPL@)*z8M-AB-t}uA8wu=E>RV zG5(ACbnb0LGDh={>G)$Eu*?o^`jIcnojmZz^6^I*h5lIfIPF1MvRn?YPCZe6tWV0E z@@2jAM|n{fUOY0&y?^mzZ&YlJX}IF`hyNS+;M+4V{c_e%V|sV~?%tyQOYh1bdgQIn zk$;GvQPgxszpZB6Ik^Yir*A5IXL@wW(>D#RbItu_)&*ZKIq#{DuRD6zYvZOp`SM@> zef}F;Ug&Yg-dkQRj(qr<^rO8eKRbHm_2_QP?{h7`_z#I2{okGWPSrzBHIQxfyTwrexL9X9 zTDe1RE*Uk!=C^!`@vj8^XaR_;Hxa%Z=4k8S1tODi|qr1PT=uY*FBPcn=r z@O+jLZ6qLm94en}lJ0|Wk3faJhIn+p0E`QXPkp)LN!;)|3$1Ylun?ZqF~WqjkM7?f{5kMP zeWiONFb?$oqTOqmAGG9i?S2LL9Lm2?yC(u;;AUH6`d*-0nOe;6S6SK2(XE>2ZWuyn4h}#?nM`&2m?2H+In1l=^;fy@b^q`BGE54i@lk@U& z^HMVM^V4T#D8KRP`R?O9k;BdDb29Shrq9Tjke88}wLoZ7Afs|;jLyoI#1pm0#EhJo z8B$6lot%-EpOu?q%n|-G=X%^CmE@e6NbQ-Ek>fTpGqXMUvyII8d0FlZBj25uosmPB zG521T*Ql)gxw-inMrK}yVz95FUrg*1xYI*YL8ITkXC3kyf!02!3X;x)CSzISIk;kl>`; zIZAffS<`3q%g^n1oiR0ICe?B3)am*8qVMp^nQ4p}f8)rcsaN-l?{`h`ncagCjz0(a z?EKNv{Qv(KZhXeJsd6-o{EM?-(!i>X5WkqLESO&5h6`@~E=8Y0D`OD)LgL3T28b78 z03>E;d|Bg*Fbe6vu%F;T7zcDOxK{8fjo)cJs{)KXnaHCMvX&;2w#^b3;Hx3(wH(@{Qs=+zZ!2JBmM;%Kht>S zSn*$~ai7NBH;RA7IKl5U_COy?`Q4x~TjPoe;{S!lOK%nT+Zs>Dz{qriHBQl3pmDv% zT6FY`SEsRF;~|X=8h_N-sPPw#2IeVDAE_})W3tPg&eoW%ajr(U#swM|YAn=Pq_J4zgBnXT zKCJO^jZbTQPUDLj|Elp-jT>MCP`;(-pjE;+A9b$aRE*VhFVuK_Z*fo7n24T*;pd|L z5wFuYX0W)|j}-h^VOB&zO_ze0B#@~&J1#u<$1vbJ)CB~qiAl{uQn5}W4#s@V%t?^Zj zn>Ahx+mPueYJ8G>hZ)9lv}58lv`^wvjg=a^juroFG)~vJLgSkn4>)eLW764%c1Zk> z#?i2C6@9cj;^)l2i(yQs+`1aZs;dM))p!B=QTk8RSi*QwnA2S@_`1fsFd3tN_i=(- z81FR0xRv3+QCA2)&v2)PY3$ZZ=cDm8);s9_kRW*B2*G5Hb2L7pu}b6D8h?{0@y}xYg3cg~ zy)K12+AxM`{9NO)%f$b#o`M@ShW}RFhbecYyMl592H2i}m&OY=QofxGSQWdLM2W-%#IyUs2CdM->=TVOXnv zY-(!q?QwUiM_Z0?df9qQ62j_hJ z(Y0(kqAxz!#Ije0Ek4$O2UpSM^gQ+>pM6vC>{@re|Gq-Aaj2^}t=6?{i^LB4BG*?O zufhH?ijBvZcwKU>_h1+A7sD$Kp5^^&c*Pf;k%`$2Y*o}opVn|%&1sGHMG!<|eo{}A`Z$G+yXZ!_=f!u<;h&8p($TCj8R{Ofet6?enu|5j!B1xsCVFd9@* zFr8m#Kb!oLy-3RJs+s@dbdVyK(}MC z?Jyh>V+_}y%3bTq1D`;S2#+sUfg%RRhu#I_qkOl=Hwg>K-xFijD7s{0oIg35ZQS8x z3OE^~I9W8!N{s>|;b6o)a7bEo{<&Jru5}UKs<7hD-fdwCY0>i=q(q*#wskhlMr1za zcT>UWJOlpT>M*k^@TjyqD^0m;0!zgmT8^h!FsJ_F%cVwAqI`Q6T(I~E1#^Ewd^w*~ z$$jOnM_7Cbn*STumPy@W4p&CHBfQ%q3qKDhhJy!@2MWQ7hO^7-{xZ*D*%DOcMT~>@0Hl!pDRNO9WWTdyeb1+}4u)9(}(Kl;Cv~Q2+0%%(G zh-g+%^$2#`h8YuIF7;YHB8q+V2HunaL-Ziev_fU7MnCj}=;qGou>KQ4G0h-XXdxs+q^u0Lk{SvwwdJi3^VW8)VtYe#h_%w9y!SHJ4kudaW-J(7f{nPB#q)2NO+b{=-2~oasGbzel6WA=}Gt(MP&o9>8NZ(=i<>vUFQ7t*09BHLBS{@k| zL{Gr?JH0n#io|Gm{w{LUbM6(J5X)yLGN*5y{ zG^9UeVR;p&5bz$e%J-=|+I07f4E6zA(I(k~Wt`FN5iMsQn+4@%egKJ?;ngFR&Qy<# zg57CYC8`SdxGyKez38L*G*;stZ)2F56n!4W<*kjx_*qgN*^_ZptpsY(K%49HcJ>Z^ zP$QOi6a;En19{&xiNa0ZZ+T~`hg!?q2M=g{%fEuX0vE~{f7mRjGv7BW1IfA>LD-?i zm1tJ%der84RSCgL+a+OUCA*R}q<#NgR`hz`9_z+P&j#-%7SHf}ZbpkDhEVdXgPvKU z!7g8KNClDy6;(KiCj`yPMXzbTtb#hr8j4Kf5BqkwmdB$AVBIR%A4n3pcaU$A>=@s_ zCkw~(O@giY9uGfqeE>BWe>gA=)z@0ik?YaNNZBFkd)gGWRD-9@FjWc6=l%k-GFS~y zSW1=AcO3}ULyMd|k2>m4j{1M~>pyU^u7~im29%eG5N?{1AH;_1slNHEUBO)+#m! zD11@QiKDQUFp7<;IRO>6A4W+!F%`FtGUUWjigc9f6KBK`hZ!j&mHm2ac|kL#po?Vujv2SefP0OM4G9!*2JiUT?KL0#Aq|M&fIOK)|#=4IlD>1 zSnEg&{m<$C$g?o!RD2#`EtX=q?^6n}(ixR@ncIAaH?Yq(J$3G_7_ofi_s_8Ii;mxe z^wE!*(T~EqO{iS>gLSs`0J~F+sTZ3IQ9nzZ@nSbpnfcLHN}ZjDA|c&FDziNv+Z?Jp z#-zx={t`!e=C;5C$RhBnE~k{wduXVY5(Dz5TgfqIaviKc&xIS%0U5CK(20+XvQja^ zc()EU8%nO7VM4x)U1~NW8kyaXqW@;0ZhiRH+h$1MZl;&-x?ihrN%J8kr-P{ z1ekzXn~=75(H2Y(N@;D>?>@}QsJYEmR0$^-F=G@%G3MwfM5+(NY!%_7BV9$9jKkuM zGQaOM2u#-hAKOSOjgpa3p6OO{EE_E*;7>yl%_HPBaHcLlbN!$IW3I66`4$YCsXK-9 z7>9E!bth*8_7o5hc`73x`fCrN-lxW*62tAG&NFza4yWe9tR&c%O2~2kU4sN$*aK`2 z0x0;`rjaMaag;jF@OkHj(WvhjWVx-I5R`JcnVcV=FB(VFK6}`r~vqwUWnO5iW ztpGi5W4Np6B|KP5m@kY22!*4cnYvd7{M5a^@_QGVKRJ0UdcoXfA=4`F5$NU%OqyJ4 z#gHXx1YrhsT8^z8#q* zo@G)Wre~{tE>(Gnh^fv>j7mtZgEZ)wRtIgg#ziW8wSPibGtDb$YqT5@%t|(&%Mf6p za#7|`k*bc_O5IyMI(8=5fFe(Zmfwe*$r-&uIAa@#b`=dKUueanx!`2Lt|u^$zXI7f zv}HSFbnL=KVpoJAWh|rAAgxmQ5JOq<_a%B3Yc$(ylr@^>xRRh#^P^+HH_-i=$f9FP&27g)Tc^U(QWKnZi{5+7NV#2M0&3pi+KLX z(@ZaUulOv&TA~NoqNR>Z9#S&l!kQ>R5i3U(ng-@VirZrGFwHHfF+H^=cvitMmCg4! zZY?vTmw@wx28>&tTC1SO@-knmSVl0&&Q(-Lsww+bEjY6hD18%#S5(6)W?-#Sl1{C) zpF z!G2jsL*~7*KVnwA5gnEB!%H$jHB|$$lVBD&O-M<|_b()<9!=(iJI$#*nGL(pv0#2n zSMq-LO^$Y^?zOwY*U>N-QCEU#$Bu?`Ay^b?L(2ANszge)lK0#HhcM*CwzPpa`ztAr zn%@q>q9>Pfu)LWTh1E~Ya-+~U6b>^y7wb_y7Pg5Q9$(&a2^7;l%-6Kxa08cC=3U2e z{YE{mXO_5cc7nM!m8Ig$f93F~2n^_-LY{$VrN5(3INY9X;tp&Oc8e{(*0>7P_%z`eE&*clmCI7)ir&Xp9C8Wk9y#gX_^JF11e? z^`KMe&^h)s=v)cvC00c5-DuuXW=dU~3nnRb2^F64y4K0dN{2=%X&i($i#$()j;m-X z_yjGkdu-s%_MA$3&sNQc-JklU*1=AS{|B9Pe`n)U=^URs=_Bhim_IB&G#(v<>EDmu z!z$*4&-B&ex!PBUJNuf#!B~f1k7~0XVL%xeX|mqD8!<6lfR6cZv!Qn*Eu9^pfvt7BJV{2k2kcM!+l?=b!f0Y3{Q3|1#96NRJjVzOkZBAAhD+LDXk zSC53Ilx&i$5}IoYd|$v{>fda{B!^>);$rC>ZOZ zJ9rO{;`@<6jj7F}xH5R)Eo7|+SP+inU(!#xL}u}O%(p37^L?%V(W}hp63#!5U>sLL zmdIwM%F~&T4pQp{PJX73>esZ}QW%=2xkPEbsw?|?meZN2evGN2q!U$8Ndr}qTYM90 zp$mAmb20omW21cqTZd@NbZbc*T-8hHhj7E12+Q1kdcrPu7p%T3A1o0h z#hNwMh)u<%h^6ZQ|B#roFJ3-I9Z!dZ^xC?QN&&V_z(75#>Mqaes@wAvsr&oPhGXVnMiMDK*SgcS9zAOZfgLdtqcECh`K$6Uz=Gyt_9^DKK>y=Q8s#n>OQ;iKXq)aUt_616%K9SJ*B zdnG-T_R*;(6n25~i?w1@r79cEo{xOh{;&5?FJ({DlC}zHRr?ORidG>pRc`>+f3w@-PZ07jr3Dxf`u<}b#rsa7V0iZO9UiZW#~c1tv&G{n@p!J<`yf1Kf<`HCh`Rd=)sDPH@OKsELG7x&#dzkg zv=C=U&=p9*>krpS)b1+k7{rO+vyo;ck~U{Og~F^~X}G_Ef}KllQ(Tw5$tR zjHH-qoFS1Ms&NEGdLpM2tM)z)pBF&2+WRz~UQtia;c2aUdJ#|C)YD(_^uBs}6;Ji* zX*HgXs;9Mh>I(L&y&Ld!fqL4Er-#*3Ii3cnr>%Gzte&>x={EJW3r|_NMQpjb)iTKTaF`&>mX1nLgl+{ZvO8-ZBR zgZ<>&fr+PU`OT^utu^1b9&$4*wGNA0{>Y8~NgIC&oF?tw`)#-tg$3kWhMe!7;;oPJ z{wFHoJ&%o50V@@8OZ}5de+hKTEYa~?MYZImtB(1O=PP8rp&R_rObO0PI^z497Oe=Z z)BO#IzQRQk=~6Lu+6UK0illxejzOU%T*uXx3U8&X;Gm0S2U&{_GBKc33yUO`h|i)X zqN{fmvF8b_z-E#)CMvKDVzH9APUQQR62C9X*K~g@NXD1zuF2ty39xQhMsZn%N-zaA z-fzQ9EQBQ2+2K%F6x|wC`ir#yL}UsHLQJ=0E)p%SW)mBdT!R$%kK;lK)_A1*Gka{n zX$csfOB8ji8+>1%_Z0}H)S_`DM!9_Z!JIcKD$H!KP{jNgXFH__ie~Qi#~S8J=HZY1 z2Sfu4gIU#Ij1yl-&t*q0Z<3Q0%zy^rPI^4XVqj?pYf(}zPo2}xM=vp>mvUXFk?T4& zOuLLkopl|z>~1)#Ij{jC9F)IUsZ)uPPbJjKKAVC;>M{{oon6Dux1d(_#MNV@ll~w5 zQfd?4pr*Qf|3GP>7<@lv18?@f7!FyYgWrQk?*^{37#_>9ssoFqEcPV!8)|0eFGE7r zKFp6~M`tMZk_T|Hw0i`V#@^0i1=ay?-~x$jMLJp_ z4w>Lk4)$$aMgK&IxBfJ5<2A0O%-Bl8DrB8MI=uJKL&n9r9=r?Dt)=8QY==26mKxZE zBx*o^GzP^*8JyL(a~L;CmN%1{mpAvDsWoCOllFeK!kL6kpc*uHYpGgiLoM&cx;V;( zdQf)aQbn8VPy3rv_aYhNPmBh6ZPS2DyBx-=vb&mk`gpXw1i}6ZkwYee@x*4v_n5H$ z!1!U%SL*{IjAK6IDoUdf+|0O4^`Pet*V`hyKnUd28izfc{Uo*<^pzBrD^8;!1%#OQ zg|OJnpW>^N;_F`KB?5K$$~h5V|6o^kihON63BDRZSL{{FOZd9+c)kvj6jza2%d2k( z`1%?2e@$N7+u&;@^J*!t-<^oBN7z%Is=Pq|*ZAV=fz9&jtm-l0c)kv^h;7O1f6}mM z|Dm^rZA2CS75(~|T_cZiw4+}%Ih*;CdW@$GTFPr!+kCmRPE}TKodi#7QPz_9J?L#X zgQ~P2!4$g$Z5DMV<}0>kgYzsjr`|`xDj>8dF7Y_?LJTctB?2bK=>8nDP_r6l6=+!# zvB(*{Eiwi*&7ILM7%SiUiTy8ju&^UMu{&lGKDD`%)E)e)V^)$Q*83+$hgDXGUy(j% z?zWMUL%1BD6w@j2oa2Alg2Yh&V)$1_hF_75MJN?D(%HAxbS_&kDkQ$0itbzLBN(0# z8h&l-@OwPDnK%gLkngO98`wosfgILP$5;Zfpc|CVD0RQxh#|xo=Tt>A-Z*%+iZ`gO zc()u+kNd30(YuDLD=qVlgV%BKX5%0u`)t)yW55(HQ)<5Ea@xM9(fZev?? z#~q*lpyqhcCswVRa)s&>{Lw58H6;r5cLrLs+KvWO&F2fM?T1^rdmxw4@E2RTx1s z`+?JNb%N51gQ#*Y(zmBNF`6yOeTMS1KZWLVzLXesI%D$|*aeT~{f65o#~)TF;;>|o zLvaN>1ADoDBJE9O!6OAHrojVzu()tWfb&IP6ZT;W>af`4Iaf{mv3H>B-?1wF&GyC( zZSKEh`f=<;AHGx;$v;6@MV3HS)Hrjr#skWXtZ>hl)V|?JV};>>nfE^t-Xmf2&WCwUS)))&+|oO#YQj+j9C4+< zv3hhVPnB%1pN)eQ_EEN(d7^-J9UUq@Cj(A+2tu~@I}%^*cX5SBRk!!v=m>Xg zXiejMs-m;Q{``AApI7G@S#>yJ8sR<%g$&V)XvHh~wN12JVa~(+|D&}=>|&}x>>w-p zQS^(yvnD5>--^=S6E3Bbg2LF9-cf ztW}&5TV95Ex2nADyZ%c){eL`$dC!CKp_Z`I+smajX;v3+cXwJigvw623$_%CSjG*a0lA}SUH6ljNaJZ*n?a)7= zv)&P7dNno&&mc%zq3r0h6#=bTfGns&s)Z}1?ZnKbNV-1*!xJJk>2ICkwH0a4%d#ts z@o4!EELW2~6ulPpf+hK58zpi1crMC{+=3I^sI9xKkuj2wx3V*(P;{bF_y#i_&t$oZ zinXX%j@F`b`M8OzGH}@E`e~bz@o`(rp@~RjdSRP1-)2NI4dYj_@Zbq)KGaOJMAIy2 zlV%nps$F%QyifSZ~q0a%OFYa^HQF0ttcWT{L^bDgZE_{!Y1Z$L{4y{r5 z^}06gm59QAg|VnwEn;z4&OvzL!HA*bwR3*GfG@bH)g8~dNEFiT_mLqA=cc*;!_A+8AtN!5I!uNji$|j= z+`wUZ(e>Eak5HQuSF?E}tNosxK8NApIR9HU&S#c@*TCaw7f9{wg9Iv^@h|LEpVa-0HlAmS*z_j_{Is z{aow1486l0Idp;NGS|8>VM9~!JjZh`p2LTZ$8)yl^uk3EhO1~0dY6*WHxtxQ;qq;P z>bcfU4lCN@PEadxtHT0kV5P{lF1E8-0pH6Cj|}sCQ@HTVrs1Biz{8oWEZ%G{lIs3H zDcV2be=r_gOCLpuI@R|I9!eKWeZTl_Q2TC1BJ&7zprqMvF#K@5#eG$&>X~Zl-OXVa zG5+_6Kdsr1=Ne0kg!GNg-aq`0>&?+&E}xC3(nS)X7b2*0l%VmT9O}fG58pOK!OAI? z8Vog0aiagc(_yt}7+SggClxR148+^Oo9%~(GN@5M_7`xpsjo0;x=Z07R|aud%>S&S zqxyxEI`chjlQh!v3A@-^ zrLQ}_xuKatqzSONMU`Z|U!OSF<*?WlaXoVtxvr> zZhAqTt7s%SR(#P|m=xqcevkBwFyhSU$DH%kUe(`~sFgDirhKXBA#co>s{0HTpyjEv z{|Pnvbv_lj6Yz;02UI1ig^RH3=@Mh}*ZIUD>vnj=5_BrKYd(QSd&V3nvn;zW9g%v< zRc%)@rhZ|_v%!fKcpY^bTxGD65h*0i5RedF?afTC*tYM4uSg`g-irM*?1BXkqj3w( z(+JBlo0~s+*8}BN17!1#hdUJ~?eSvC#f*Zbj3|zuA;H(FU>GVm_qD6}EnXANCgcc~ zdcACmk1DNyLX%7ee&Zjt^ha5z zz|KcFb~W%i-j5i(EjK0!ups*)C9RcZ=uui*kT}n!PNb&gF+Z+iwxF)pJ0|KKoQ z#Qk&I#0}@iVCGO-p=Oi&Zr#bKh9~lFZ8x|FIdOQJM&?oXFGlsRy@E;>oWC7}y_0bF zqrZ5*Hpj)-X(%$%1x-;q5{Zwu9rru5w=o!7=|>2D+Q(SkICyF^n!C)-U^p+%+t|rH0J#PgGE}>O zjH#Un_6t26`3^6@d)~BD?w^VSk9g8=#=7tD9$U2N4`xGg@;-mTNB-n}=Fi)@7#JO~ z?2ugRm#qS^H9+}U$F%d9+*JD+UYoa}h893tTAnb2u89f!h4WL`L9$L{-_ab7%(0iU zD9&uCXzc9WUXF~-2EIMNy|6g>BY#1SJs*)XON8A;0~jybyS-M$ySyCBJt`d|H>a!d z7ksQv^q~BRf0lPU3uHE2ZZt$7KGIA47U^r6(|_zQ_{464Sk(T$S`Sd~H{vxDEGA

sQjIgO_v`cxmO*$;H>!zyr`n9!0(CnWR`E&A^y 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 Q<@{<8Z@c3FG7f+L4>}Nq`v3p{ literal 0 HcmV?d00001