xmpedit

GTK+ editor for XMP metadata embedded in images
git clone https://code.djc.id.au/git/xmpedit/

lib/genx/genx.c (45002B) - raw

      1 /*
      2  * Copyright (c) 2004 by Tim Bray and Sun Microsystems.  For copying
      3  *  permission, see http://www.tbray.org/ongoing/genx/COPYING
      4  */
      5 
      6 #define GENX_VERSION "beta5"
      7 
      8 #include <stdlib.h>
      9 #include <string.h>
     10 
     11 #include "genx.h"
     12 
     13 #define Boolean int
     14 #define True 1
     15 #define False 0
     16 #define STRLEN_XMLNS_COLON 6
     17 
     18 
     19 /*******************************
     20  * writer state
     21  */
     22 typedef enum
     23 {
     24   SEQUENCE_NO_DOC,
     25   SEQUENCE_PRE_DOC,
     26   SEQUENCE_POST_DOC,
     27   SEQUENCE_START_TAG,
     28   SEQUENCE_ATTRIBUTES,
     29   SEQUENCE_CONTENT
     30 } writerSequence;
     31 
     32 /*******************************
     33  * generic pointer list
     34  */
     35 typedef struct
     36 {
     37   genxWriter writer;
     38   int        count;
     39   int        space;
     40   void * *   pointers;
     41 } plist;
     42 
     43 /*******************************
     44  * text collector, for attribute values
     45  */
     46 typedef struct
     47 {
     48   utf8 buf;
     49   int  used;
     50   int  space;
     51 } collector;
     52 
     53 /*******************************
     54  * Structs with opaquely-exposed handles
     55  */
     56 
     57 /*
     58  * This one's tricky, to handle stacking namespaces
     59  *  'declaration' is the current attribute which would be used to
     60  *    declare the currently-effective prefix
     61  *  'defDeclaration' is a appropriate declaration when this is being
     62  *    used with the default prefix as passed to genxDeclareNamespace
     63  *  baroque is true if this namespace has been used with more than one
     64  *   prefix, or is the default namespace but has been unset
     65  */
     66 struct genxNamespace_rec
     67 {
     68   genxWriter 	writer;
     69   utf8       	name;
     70   int           declCount;
     71   Boolean       baroque;
     72   genxAttribute declaration;
     73   genxAttribute defaultDecl;
     74 };
     75 
     76 struct genxElement_rec
     77 {
     78   genxWriter 	writer;
     79   utf8       	type;
     80   genxNamespace ns;
     81 };
     82 
     83 typedef enum
     84 {
     85   ATTR_NSDECL,
     86   ATTR_NAKED,
     87   ATTR_PREFIXED 
     88 } attrType;
     89 
     90 struct genxAttribute_rec
     91 {
     92   genxWriter 	writer;
     93   utf8       	name;
     94   genxNamespace ns;
     95   collector     value;
     96   int           provided;   /* provided for current element? */
     97   attrType      atype;
     98 };
     99 
    100 /*******************************
    101  * genx's sandbox
    102  */
    103 struct genxWriter_rec
    104 {
    105   FILE *       	  	   file;
    106   genxSender *    	   sender;
    107   genxStatus   	  	   status;
    108   writerSequence  	   sequence;
    109   char            	   xmlChars[0x10000];
    110   void *          	   userData;
    111   int             	   nextPrefix;
    112   utf8                     empty;
    113   Boolean                  defaultNsDeclared;
    114   genxAttribute            xmlnsEquals;
    115   genxElement              nowStarting;
    116   plist           	   namespaces;
    117   plist           	   elements;
    118   plist           	   attributes;
    119   plist                    prefixes;
    120   plist           	   stack;   
    121   struct genxAttribute_rec arec;
    122   char *                   etext[100];
    123   void *       		(* alloc)(void * userData, int bytes);
    124   void         		(* dealloc)(void * userData, void * data);
    125 };
    126 
    127 /*******************************
    128  * Forward declarations
    129  */
    130 static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
    131 				      constUtf8 name, constUtf8 valuestr,
    132 				      genxStatus * statusP);
    133 static genxStatus addNamespace(genxNamespace ns, utf8 prefix);
    134 static genxStatus unsetDefaultNamespace(genxWriter w);
    135 static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr);
    136 void genxSetCharProps(char * p);
    137 
    138 /*******************************
    139  * End of declarations
    140  */
    141 
    142 /*******************************
    143  * private memory utilities
    144  */
    145 static void * allocate(genxWriter w, int bytes)
    146 {
    147   if (w->alloc)
    148     return (void *) (*w->alloc)(w->userData, bytes);
    149   else
    150     return (void *) malloc(bytes);
    151 }
    152 
    153 static void deallocate(genxWriter w, void * data)
    154 {
    155   if (w->dealloc)
    156     (*w->dealloc)(w->userData, data);
    157   else if (w->alloc == NULL)
    158     free(data);
    159 }
    160 
    161 static utf8 copy(genxWriter w, constUtf8 from)
    162 {
    163   utf8 temp;
    164 
    165   if ((temp = (utf8) allocate(w, strlen((const char *) from) + 1)) == NULL)
    166     return NULL;
    167   strcpy((char *) temp, (const char *) from);
    168   return temp;
    169 }
    170 
    171 static genxStatus initCollector(genxWriter w, collector * c)
    172 {
    173   c->space = 100;
    174   if ((c->buf = (utf8) allocate(w, c->space)) == NULL)
    175     return GENX_ALLOC_FAILED;
    176   c->used = 0;
    177   return GENX_SUCCESS;
    178 }
    179 
    180 static genxStatus growCollector(genxWriter w, collector * c, int size)
    181 {
    182   utf8 newSpace; 
    183 
    184   c->space = size * 2;
    185   if ((newSpace = (utf8) allocate(w, c->space)) == NULL)
    186     return GENX_ALLOC_FAILED;
    187 
    188   strncpy((char *) newSpace, (const char *) c->buf, c->used);
    189   newSpace[c->used] = 0;
    190   deallocate(w, c->buf);
    191   c->buf = newSpace;
    192   return GENX_SUCCESS;
    193 }
    194 
    195 static void startCollect(collector * c)
    196 {
    197   c->used = 0;
    198 }
    199 static void endCollect(collector * c)
    200 {
    201   c->buf[c->used] = 0;
    202 }
    203 
    204 static genxStatus collectString(genxWriter w, collector * c, constUtf8 string)
    205 {
    206   int sl = strlen((const char *) string);
    207 
    208   if (sl >= c->space)
    209     if ((w->status = growCollector(w, c, sl)) != GENX_SUCCESS)
    210       return GENX_ALLOC_FAILED;
    211 
    212   strcpy((char *) c->buf, (const char *) string);
    213   return GENX_SUCCESS;
    214 }
    215 
    216 #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;}
    217 
    218 /*******************************
    219  * private list utilities
    220  */
    221 static genxStatus initPlist(genxWriter w, plist * pl)
    222 {
    223   pl->writer = w;
    224   pl->count = 0;
    225   pl->space = 10;
    226   pl->pointers = (void * *) allocate(w, pl->space * sizeof(void *));
    227   if (pl->pointers == NULL)
    228     return GENX_ALLOC_FAILED;
    229   
    230   return GENX_SUCCESS;
    231 }
    232 
    233 /*
    234  * make room in a plist
    235  */
    236 static Boolean checkExpand(plist * pl)
    237 {
    238   void * * newlist;
    239   int i;
    240 
    241   if (pl->count < pl->space)
    242     return True;
    243 
    244   pl->space *= 2;
    245   newlist = (void * *) allocate(pl->writer, pl->space * sizeof(void *));
    246   if (newlist == NULL)
    247     return False;
    248   for (i = 0; i < pl->count; i++)
    249     newlist[i] = pl->pointers[i];
    250   deallocate(pl->writer, pl->pointers); 
    251   pl->pointers = newlist;
    252 
    253   return True;
    254 }
    255 
    256 /*
    257  * stick something on the end of a plist
    258  */
    259 static genxStatus listAppend(plist * pl, void * pointer)
    260 {
    261   if (!checkExpand(pl))
    262     return GENX_ALLOC_FAILED;
    263 
    264   pl->pointers[pl->count++] = pointer;
    265   return GENX_SUCCESS;
    266 }
    267 
    268 /*
    269  * insert in place, shuffling up
    270  */
    271 static genxStatus listInsert(plist * pl, void * pointer, int at)
    272 {
    273   int i;
    274 
    275   if (!checkExpand(pl))
    276     return GENX_ALLOC_FAILED;
    277 
    278   for (i = pl->count; i > at; i--)
    279     pl->pointers[i] = pl->pointers[i - 1];
    280   pl->count++;
    281 
    282   pl->pointers[at] = pointer;
    283   return GENX_SUCCESS;
    284 }
    285 
    286 /*******************************
    287  * list lookups
    288  */
    289 
    290 static genxNamespace findNamespace(plist * pl, constUtf8 uri)
    291 {
    292   int i;
    293   genxNamespace * nn = (genxNamespace *) pl->pointers;
    294 
    295   for (i = 0; i < pl->count; i++)
    296     if (strcmp((char *) uri, (const char *) nn[i]->name) == 0)
    297       return nn[i];
    298 
    299   return NULL;
    300 }
    301 
    302 static genxElement findElement(plist * pl, constUtf8 xmlns, constUtf8 type)
    303 {
    304   int i;
    305   genxElement * ee = (genxElement *) pl->pointers;
    306 
    307   for (i = 0; i < pl->count; i++)
    308   {
    309     if (xmlns == NULL)
    310     {
    311       if (ee[i]->ns == NULL && strcmp((const char *) type,
    312 				      (const char *) ee[i]->type) == 0)
    313 	return ee[i];
    314     }
    315     else
    316     {
    317       if (ee[i]->ns != NULL &&
    318 	  strcmp((const char *) xmlns, (const char *) ee[i]->ns->name) == 0 &&
    319 	  strcmp((const char *) type, (const char *) ee[i]->type) == 0)
    320 	return ee[i];
    321     }
    322   }
    323 
    324   return NULL;
    325 }
    326 
    327 /*
    328  * store & intern a prefix, after giving it the
    329  *  "xmlns:" prefix.  Don't allow storing the same one twice unless 'force'
    330  *  is set.
    331  */
    332 static utf8 storePrefix(genxWriter w, constUtf8 prefix, Boolean force)
    333 {
    334   int high, low;
    335   utf8 * pp = (utf8 *) w->prefixes.pointers;
    336   unsigned char buf[1024];
    337 
    338   if (prefix[0] == 0)
    339     prefix = (utf8) "xmlns";
    340   else
    341   {
    342     sprintf((char *) buf, "xmlns:%s", prefix);
    343     prefix = buf;
    344   }
    345 
    346   high = w->prefixes.count; low = -1;
    347   while (high - low > 1)
    348   {
    349     int probe = (high + low) / 2;
    350     if (strcmp((const char *) prefix, (const char *) pp[probe]) < 0)
    351       high = probe;
    352     else
    353       low = probe;
    354   }
    355 
    356   /* already there? */
    357   if (low != -1 && strcmp((const char *) prefix, (const char *) pp[low]) == 0)
    358   {
    359     if (force)
    360       return pp[low];
    361 
    362     w->status = GENX_DUPLICATE_PREFIX;
    363     return NULL;
    364   }
    365 
    366   /* copy & insert */
    367   if ((prefix = copy(w, prefix)) == NULL)
    368   {
    369     w->status = GENX_ALLOC_FAILED;
    370     return NULL;
    371   }
    372   
    373   w->status = listInsert(&w->prefixes, (void *) prefix, high);
    374   if (w->status != GENX_SUCCESS)
    375     return NULL;
    376 
    377   return (utf8) prefix;
    378 }
    379 
    380 /*******************************
    381  * UTF8 bit-banging
    382  */
    383 
    384 /*
    385  * Retrieve the character pointed at, and advance the pointer; return -1 on
    386  *  error
    387  */
    388 int genxNextUnicodeChar(constUtf8 * sp)
    389 {
    390   utf8 s = (utf8) *sp;
    391   int c;
    392 
    393   if (*s == 0)
    394     return -1;
    395 
    396   if (*s < 0x80)
    397     c = *s++;
    398 
    399   /* all this encoding sanity-checking taken from section 3.10 of Unicode 4 */
    400   else if (*s < 0xc2)
    401     goto malformed;
    402 
    403   /* 2-byte encodings, first byte c2 .. df */
    404   else if (*s < 0xe0) 
    405   {
    406     c = (*s++ & 0x1f) << 6;
    407 
    408     /*
    409      * for this common idiom, if ((c & 0xc0) != 0x80) is slightly faster
    410      *  on MacOS (PPC)
    411      */
    412     if (*s < 0x80 || *s > 0xbf)
    413       goto malformed;
    414 
    415     c |= *s++ & 0x3f;
    416   }
    417 
    418   /* 3-byte encodings, first byte e0 .. ef */
    419   else if (*s < 0xf0)
    420   {
    421     int b0 = *s;
    422     c = (*s++ & 0x0f) << 12;
    423 
    424     if ((b0 == 0xe0 && (*s < 0xa0 || *s > 0xbf)) ||
    425 	(b0 <  0xed && (*s < 0x80 || *s > 0xbf)) ||
    426 	(b0 == 0xed && (*s < 0x80 || *s > 0x9f)) ||
    427 	(b0  > 0xed && (*s < 0x80 || *s > 0xbf)))
    428       goto malformed;
    429 
    430     c |= (*s++ & 0x3f) << 6;
    431 
    432     if (*s < 0x80 || *s > 0xbf)
    433       goto malformed;
    434 
    435     c |= *s++ & 0x3f;
    436   }
    437 
    438   /* 4-byte encodings, first byte f0 .. f4 */
    439   else if (*s < 0xf5)
    440   {
    441     int b0 = *s;
    442     c = (*s++ & 0x07) << 18;
    443 
    444     if ((b0 == 0xf0 && (*s < 0x90 || *s > 0xbf)) ||
    445 	(b0 <  0xf4 && (*s < 0x80 || *s > 0xbf)) ||
    446 	(b0 >= 0xf4 && (*s < 0x80 || *s > 0x8f)))
    447       goto malformed;
    448 
    449     c |= (*s++ & 0x3f) << 12;
    450 
    451     if (*s < 0x80 || *s > 0xbf)
    452       goto malformed;
    453 
    454     c |= (*s++ & 0x3f) << 6;
    455 
    456     if (*s < 0x80 || *s > 0xbf)
    457       goto malformed;
    458 
    459     c |= *s++ & 0x3f;
    460   }
    461   else
    462     goto malformed;
    463 
    464   *sp = s;
    465   return c;
    466 
    467   /*
    468    * this is needed by scrubText, which wants to get the pointer moved
    469    *  past the problem area.
    470    */
    471 malformed:
    472   if (*s)
    473     ++s;
    474   *sp = s;
    475   return -1;
    476 }
    477 
    478 static Boolean isXMLChar(genxWriter w, int c)
    479 {
    480   if (c < 0)
    481     return False;
    482   else if (c < 0x10000)
    483     return (int) w->xmlChars[c];
    484   else
    485     return (c <= 0x10ffff);
    486 }
    487 
    488 static Boolean isLetter(genxWriter w, int c)
    489 {
    490   if (c < 0 || c > 0xffff)
    491     return False;
    492   else
    493     return w->xmlChars[c] & GENX_LETTER;
    494 }
    495 
    496 static Boolean isNameChar(genxWriter w, int c)
    497 {
    498   if (c < 0 || c > 0xffff)
    499     return False;
    500   else
    501     return w->xmlChars[c] & GENX_NAMECHAR;
    502 }
    503 
    504 /*******************************
    505  * Constructors, setters/getters
    506  */
    507 
    508 /*
    509  * Construct a new genxWriter
    510  */
    511 genxWriter genxNew(void * (* alloc)(void * userData, int bytes),
    512 		   void (* dealloc)(void * userData, void * data),
    513 		   void * userData)
    514 {
    515   genxWriter w;
    516   genxNamespace xml;
    517 
    518   if (alloc)
    519     w = (genxWriter) (*alloc)(userData, sizeof(struct genxWriter_rec));
    520   else
    521     w = (genxWriter) malloc(sizeof(struct genxWriter_rec));
    522 
    523   if (w == NULL)
    524     return NULL;
    525 
    526   w->status = GENX_SUCCESS;
    527   w->alloc = alloc;
    528   w->dealloc = dealloc;
    529   w->userData = userData;
    530   w->sequence = SEQUENCE_NO_DOC;
    531 
    532   if (initPlist(w, &w->namespaces) != GENX_SUCCESS ||
    533       initPlist(w, &w->elements) != GENX_SUCCESS ||
    534       initPlist(w, &w->attributes) != GENX_SUCCESS ||
    535       initPlist(w, &w->prefixes) != GENX_SUCCESS ||
    536       initPlist(w, &w->stack) != GENX_SUCCESS)
    537     return NULL;
    538 
    539   if ((w->status = initCollector(w, &w->arec.value)) != GENX_SUCCESS)
    540     return NULL;
    541 
    542   if ((w->empty = copy(w, (utf8) "")) == NULL)
    543   {
    544     w->status = GENX_ALLOC_FAILED;
    545     return NULL;
    546   }
    547 
    548   w->xmlnsEquals = declareAttribute(w, NULL, (utf8) "xmlns", NULL, &w->status);
    549   if (w->xmlnsEquals == NULL || w->status != GENX_SUCCESS)
    550     return NULL;
    551   w->defaultNsDeclared = False;
    552 
    553   w->nextPrefix = 1;
    554 
    555   genxSetCharProps(w->xmlChars);
    556 
    557   w->etext[GENX_SUCCESS] = "Success";
    558   w->etext[GENX_BAD_UTF8] = "Bad UTF8";
    559   w->etext[GENX_NON_XML_CHARACTER] = "Non XML Character";
    560   w->etext[GENX_BAD_NAME] = "Bad NAME";
    561   w->etext[GENX_ALLOC_FAILED] = "Memory allocation failed";
    562   w->etext[GENX_BAD_NAMESPACE_NAME] = "Bad namespace name";
    563   w->etext[GENX_INTERNAL_ERROR] = "Internal error";
    564   w->etext[GENX_DUPLICATE_PREFIX] = "Duplicate prefix";
    565   w->etext[GENX_SEQUENCE_ERROR] = "Call out of sequence";
    566   w->etext[GENX_NO_START_TAG] = "No Start-tag for EndElement call";
    567   w->etext[GENX_IO_ERROR] = "I/O error";
    568   w->etext[GENX_MISSING_VALUE] = "Missing attribute value";
    569   w->etext[GENX_MALFORMED_COMMENT] = "Malformed comment body";
    570   w->etext[GENX_MALFORMED_PI] = "?> in PI";
    571   w->etext[GENX_XML_PI_TARGET] = "Target of PI matches [xX][mM][lL]";
    572   w->etext[GENX_DUPLICATE_ATTRIBUTE] =
    573     "Same attribute specified more than once";
    574   w->etext[GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE] =
    575     "Attribute cannot be in default namespace";
    576   w->etext[GENX_DUPLICATE_NAMESPACE] =
    577     "Declared namespace twice with different prefixes on one element.";
    578   w->etext[GENX_BAD_DEFAULT_DECLARATION] =
    579     "Declared a default namespace on an element which is in no namespace";
    580 
    581   /* the xml: namespace is pre-wired */
    582   xml = genxDeclareNamespace(w, (utf8) "http://www.w3.org/XML/1998/namespace",
    583 			     (utf8) "xml", &w->status);
    584   if (xml == NULL)
    585     return NULL;
    586   xml->declCount = 1;
    587   xml->declaration = xml->defaultDecl;
    588 
    589   return w;
    590 }
    591 
    592 /*
    593  * get/set userData
    594  */
    595 void genxSetUserData(genxWriter w, void * userData)
    596 {
    597   w->userData = userData;
    598 }
    599 void * genxGetUserData(genxWriter w)
    600 {
    601   return w->userData;
    602 }
    603 
    604 /*
    605  * get/set allocator
    606  */
    607 void genxSetAlloc(genxWriter w, void * (* alloc)(void * userData, int bytes))
    608 {
    609   w->alloc = alloc;
    610 }
    611 void genxSetDealloc(genxWriter w,
    612 		    void (* dealloc)(void * userData, void * data))
    613 {
    614   w->dealloc = dealloc;
    615 }
    616 void * (* genxGetAlloc(genxWriter w))(void * userData, int bytes)
    617 {
    618   return w->alloc;
    619 }
    620 void (* genxGetDealloc(genxWriter w))(void * userData, void * data)
    621 {
    622   return w->dealloc;
    623 }
    624 
    625 /*
    626  * Clean up
    627  */
    628 void genxDispose(genxWriter w)
    629 {
    630   int i;
    631   genxNamespace * nn = (genxNamespace *) w->namespaces.pointers;
    632   genxElement * ee = (genxElement *) w->elements.pointers;
    633   genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
    634   utf8 * pp = (utf8 *) w->prefixes.pointers;
    635 
    636   for (i = 0; i < w->namespaces.count; i++)
    637   {
    638     deallocate(w, nn[i]->name);
    639     deallocate(w, nn[i]);
    640   }
    641 
    642   for (i = 0; i < w->elements.count; i++)
    643   {
    644     deallocate(w, ee[i]->type);
    645     deallocate(w, ee[i]);
    646   }
    647 
    648   for (i = 0; i < w->attributes.count; i++)
    649   {
    650     deallocate(w, aa[i]->name);
    651     deallocate(w, aa[i]->value.buf);
    652     deallocate(w, aa[i]);
    653   }
    654 
    655   for(i = 0; i < w->prefixes.count; i++)
    656     deallocate(w, pp[i]);
    657 
    658   deallocate(w, w->namespaces.pointers);
    659   deallocate(w, w->elements.pointers);
    660   deallocate(w, w->attributes.pointers);
    661   deallocate(w, w->prefixes.pointers);
    662   deallocate(w, w->stack.pointers);
    663 
    664   deallocate(w, w->arec.value.buf);
    665 
    666   deallocate(w, w->empty);
    667 
    668   /* how Oscar dealt with Igli */
    669   deallocate(w, w);
    670 }
    671 
    672 /*******************************
    673  * External utility routines
    674  */
    675 
    676 /*
    677  * scan a buffer and report problems with UTF-8 encoding or non-XML characters
    678  */
    679 genxStatus genxCheckText(genxWriter w, constUtf8 s)
    680 {
    681   while (*s)
    682   {
    683     int c = genxNextUnicodeChar(&s);
    684     if (c == -1)
    685       return GENX_BAD_UTF8;
    686 
    687     if (!isXMLChar(w, c))
    688       return GENX_NON_XML_CHARACTER;
    689   }
    690   return GENX_SUCCESS;
    691 }
    692 
    693 /*
    694  * Purify some text
    695  */
    696 int genxScrubText(genxWriter w, constUtf8 in, utf8 out)
    697 {
    698   int problems = 0;
    699   constUtf8 last = in;
    700 
    701   while (*in)
    702   {
    703     int c = genxNextUnicodeChar(&in);
    704     if (c == -1)
    705     {
    706       problems++;
    707       last = in;
    708       continue;
    709     }
    710 
    711     if (!isXMLChar(w, c))
    712     {
    713       problems++;
    714       last = in;
    715       continue;
    716     }
    717 
    718     while (last < in)
    719       *out++ = *last++;
    720   }
    721   *out = 0;
    722   return problems;
    723 }
    724 
    725 /*
    726  * check one character
    727  */
    728 int genxCharClass(genxWriter w, int c)
    729 {
    730   int ret = 0;
    731 
    732   if (isXMLChar(w, c))
    733     ret |= GENX_XML_CHAR;
    734   if (isNameChar(w, c))
    735     ret |= GENX_NAMECHAR;
    736   if (isLetter(w, c))
    737     ret |= GENX_LETTER;
    738   return ret;
    739 }
    740 
    741 static genxStatus checkNCName(genxWriter w, constUtf8 name)
    742 {
    743   int c;
    744 
    745   if (name == NULL || *name == 0)
    746     return GENX_BAD_NAME;
    747 
    748   c = genxNextUnicodeChar(&name);
    749   if (!isLetter(w, c) && c != ':' && c != '_')
    750     return GENX_BAD_NAME;
    751 
    752   while (*name)
    753   {
    754     c = genxNextUnicodeChar(&name);
    755     if (c == -1)
    756       return GENX_BAD_UTF8;
    757     if (!isNameChar(w, c))
    758       return GENX_BAD_NAME;
    759   }
    760   return GENX_SUCCESS;
    761 }
    762 
    763 char * genxGetErrorMessage(genxWriter w, genxStatus status)
    764 {
    765   return w->etext[status];
    766 }
    767 char * genxLastErrorMessage(genxWriter w)
    768 {
    769   return w->etext[w->status];
    770 }
    771 
    772 /*******************************
    773  * Declarations: namespace/element/attribute
    774  */
    775 
    776 /*
    777  * DeclareNamespace - by far the most complex routine in Genx
    778  */
    779 genxNamespace genxDeclareNamespace(genxWriter w, constUtf8 uri,
    780 				   constUtf8 defaultPref,
    781 				   genxStatus * statusP)
    782 {
    783   genxNamespace ns;
    784   genxAttribute defaultDecl;
    785   unsigned char newPrefix[100];
    786 
    787   if (uri == NULL || uri[0] == 0)
    788   {
    789     w->status = GENX_BAD_NAMESPACE_NAME;
    790     goto busted;
    791   }
    792 
    793   if ((w->status = genxCheckText(w, uri)) != GENX_SUCCESS)
    794     goto busted;
    795 
    796   /* if a prefix is provided, it has to be an NCname */
    797   if (defaultPref != NULL && defaultPref[0] != 0 &&
    798       (w->status = checkNCName(w, defaultPref)) != GENX_SUCCESS)
    799     goto busted;
    800 
    801   /* previously declared? */
    802   if ((ns = findNamespace(&w->namespaces, uri)))
    803   {
    804     /* just a lookup, really */
    805     if ((defaultPref == NULL) ||
    806 	(defaultPref[0] == 0 && ns->defaultDecl == w->xmlnsEquals) ||
    807 	(strcmp((const char *) ns->defaultDecl->name + STRLEN_XMLNS_COLON,
    808 		(const char *) defaultPref) == 0))
    809     {
    810       w->status = *statusP = GENX_SUCCESS;
    811       return ns;
    812     }
    813   }
    814 
    815   /* wasn't already declared */
    816   else
    817   {
    818 
    819     /* make a default prefix if none provided */
    820     if (defaultPref == NULL)
    821     {
    822       sprintf((char *) newPrefix, "g%d", w->nextPrefix++);
    823       defaultPref = newPrefix;
    824     }
    825 
    826     ns = (genxNamespace) allocate(w, sizeof(struct genxNamespace_rec));
    827     if (ns == NULL)
    828     {
    829       w->status = GENX_ALLOC_FAILED;
    830       goto busted;
    831     }
    832     ns->writer = w;
    833     ns->baroque = False;
    834 
    835     if ((ns->name = copy(w, uri)) == NULL)
    836     {
    837       w->status = GENX_ALLOC_FAILED;
    838       goto busted;
    839     }
    840 
    841     if ((w->status = listAppend(&w->namespaces, ns)) != GENX_SUCCESS)
    842       goto busted;
    843     ns->defaultDecl = ns->declaration = NULL;
    844     ns->declCount = 0;
    845   }
    846 
    847   if (defaultPref[0] == 0)
    848   {
    849     if (w->defaultNsDeclared)
    850     {
    851       w->status = GENX_DUPLICATE_PREFIX;
    852       goto busted;
    853     }
    854     defaultDecl = w->xmlnsEquals;
    855     w->defaultNsDeclared = True;
    856   }
    857   else
    858   {
    859     /* this catches dupes too */
    860     if ((defaultPref = storePrefix(w, defaultPref, False)) == NULL)
    861       goto busted;
    862 
    863     defaultDecl = declareAttribute(w, NULL, defaultPref, ns->name, statusP);
    864     if (defaultDecl == NULL || *statusP != GENX_SUCCESS)
    865     {
    866       w->status = *statusP;
    867       return NULL;
    868     }
    869   }
    870 
    871   if (ns->defaultDecl != NULL && defaultDecl != ns->defaultDecl)
    872     ns->baroque = True;
    873   ns->defaultDecl = defaultDecl;
    874   
    875   *statusP = GENX_SUCCESS;
    876   return ns;
    877 
    878 busted:
    879   *statusP = w->status;
    880   return NULL;
    881 }
    882 
    883 /*
    884  * get namespace prefix
    885  */
    886 utf8 genxGetNamespacePrefix(genxNamespace ns)
    887 {
    888   if (ns->declaration == NULL)
    889     return NULL;
    890 
    891   if (ns->declaration == ns->writer->xmlnsEquals)
    892     return ns->writer->empty;
    893 
    894   return ns->declaration->name + STRLEN_XMLNS_COLON;
    895 }
    896 
    897 /*
    898  * DeclareElement - see genx.h for details
    899  */
    900 genxElement genxDeclareElement(genxWriter w,
    901 			       genxNamespace ns, constUtf8 type, 
    902 			       genxStatus * statusP)
    903 {
    904   genxElement old;
    905   genxElement el;
    906 
    907   if ((w->status = checkNCName(w, type)) != GENX_SUCCESS)
    908   {
    909     *statusP = w->status;
    910     return NULL;
    911   }
    912 
    913   /* already declared? */
    914   old = findElement(&w->elements, (ns == NULL) ? NULL : ns->name, type);
    915   if (old)
    916     return old;
    917 
    918   if ((el = (genxElement) allocate(w, sizeof(struct genxElement_rec))) == NULL)
    919   {
    920     w->status = *statusP = GENX_ALLOC_FAILED;
    921     return NULL;
    922   }
    923 
    924   el->writer = w;
    925   el->ns = ns;
    926   if ((el->type = copy(w, type)) == NULL)
    927   {
    928     w->status = *statusP = GENX_ALLOC_FAILED;
    929     return NULL;
    930   }
    931 
    932   if ((w->status = listAppend(&w->elements, el)) != GENX_SUCCESS)
    933   {
    934     *statusP = w->status;
    935     return NULL;
    936   }
    937   
    938   *statusP = GENX_SUCCESS;
    939   return el;
    940 }
    941 
    942 /*
    943  * C14n ordering for attributes: 
    944  * - first, namespace declarations by the prefix being declared
    945  * - second, unprefixed attributes by attr name
    946  * - third, prefixed attrs by ns uri then local part
    947  */
    948 static int orderAttributes(genxAttribute a1, genxAttribute a2)
    949 {
    950   if (a1->atype == a2->atype)
    951   {
    952     if (a1->atype == ATTR_PREFIXED && a1->ns != a2->ns)
    953       return strcmp((const char *) a1->ns->name, (const char *) a2->ns->name);
    954     else
    955       return strcmp((const char *) a1->name, (const char *) a2->name);
    956   }
    957 
    958   else if (a1->atype == ATTR_NSDECL)
    959     return -1;
    960 
    961   else if (a1->atype == ATTR_NAKED)
    962   {
    963     if (a2->atype == ATTR_NSDECL)
    964       return 1;
    965     else
    966       return -1;
    967   }
    968 
    969   else
    970     return 1;
    971 }
    972 
    973 /*
    974  * internal declare-attribute.  This one allows colonized values for
    975  *  names, so that you can declare xmlns:-type attributes
    976  */
    977 static genxAttribute declareAttribute(genxWriter w, genxNamespace ns,
    978 				      constUtf8 name, constUtf8 valuestr,
    979 				      genxStatus * statusP)
    980 {
    981   int high, low;
    982   genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
    983   genxAttribute a;
    984 
    985   w->arec.ns = ns;
    986   w->arec.name = (utf8) name;
    987 
    988   if (ns)
    989     w->arec.atype = ATTR_PREFIXED;
    990   else if (strncmp((const char *) name, "xmlns", STRLEN_XMLNS_COLON - 1) == 0)
    991     w->arec.atype = ATTR_NSDECL;
    992   else
    993     w->arec.atype = ATTR_NAKED;
    994 
    995   if (ns && (ns->defaultDecl == w->xmlnsEquals))
    996   {
    997     w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
    998     goto busted;
    999   }
   1000 
   1001   /* attribute list has to be kept sorted per c14n rules */
   1002   high = w->attributes.count; low = -1;
   1003   while (high - low > 1)
   1004   {
   1005     int probe = (high + low) / 2;
   1006     if (orderAttributes(&w->arec, aa[probe]) < 0)
   1007       high = probe;
   1008     else
   1009       low = probe;
   1010   }
   1011 
   1012   /* if it was already there */
   1013   if (low != -1 && orderAttributes(&w->arec, aa[low]) == 0)
   1014     return aa[low];
   1015 
   1016   /* not there, build it */
   1017   a = (genxAttribute) allocate(w, sizeof(struct genxAttribute_rec));
   1018   if (a == NULL)
   1019   {
   1020     w->status = GENX_ALLOC_FAILED;
   1021     goto busted;
   1022   }
   1023 
   1024   a->writer = w;
   1025   a->ns = ns;
   1026   a->provided = False;
   1027   a->atype = w->arec.atype;
   1028 
   1029   if ((a->name = copy(w, name)) == NULL)
   1030   {
   1031     w->status = GENX_ALLOC_FAILED;
   1032     goto busted;
   1033   }
   1034 
   1035   if ((w->status = initCollector(w, &a->value)) != GENX_SUCCESS)
   1036     goto busted;
   1037 
   1038   if (valuestr)
   1039     if ((w->status = collectString(w, &a->value, valuestr)) != GENX_SUCCESS)
   1040       goto busted;
   1041   
   1042   w->status = listInsert(&w->attributes, a, high);
   1043   if (w->status != GENX_SUCCESS)
   1044     goto busted;
   1045   
   1046   *statusP = GENX_SUCCESS;
   1047   return a;
   1048 
   1049 busted:
   1050   *statusP = w->status;
   1051   return NULL;
   1052 }
   1053 
   1054 /*
   1055  * genxDeclareAttribute - see genx.h for details
   1056  */
   1057 genxAttribute genxDeclareAttribute(genxWriter w,
   1058 				   genxNamespace ns, constUtf8 name,
   1059 				   genxStatus * statusP)
   1060 {
   1061   if ((w->status = checkNCName(w, name)) != GENX_SUCCESS)
   1062   {
   1063     *statusP = w->status;
   1064     return NULL;
   1065   }
   1066 
   1067   return declareAttribute(w, ns, name, NULL, statusP);
   1068 }
   1069 
   1070 /*******************************
   1071  * I/O
   1072  */
   1073 static genxStatus sendx(genxWriter w, constUtf8 s)
   1074 {
   1075   if (w->sender)
   1076     return (*w->sender->send)(w->userData, s);
   1077   else
   1078   {
   1079     if (fputs((const char *) s, w->file) == -1)
   1080       return GENX_IO_ERROR;
   1081     else
   1082       return GENX_SUCCESS;
   1083   }
   1084 }
   1085 
   1086 static genxStatus sendxBounded(genxWriter w, constUtf8 start, constUtf8 end)
   1087 {
   1088   if (w->sender)
   1089     return (*w->sender->sendBounded)(w->userData, start, end);
   1090   else
   1091     if (fwrite(start, 1, end - start, w->file) != (unsigned) (end - start))
   1092       return GENX_IO_ERROR;
   1093     else
   1094       return GENX_SUCCESS;
   1095 }
   1096 
   1097 #define SendCheck(w,s) if ((w->status=sendx(w,(utf8)s))!=GENX_SUCCESS) return w->status;
   1098 
   1099 /*******************************
   1100  * XML writing routines.  The semantics of the externally-facing ones are
   1101  *  written up in genx.h.  Commentary here is implementation notes and
   1102  *  for internal routines.
   1103  */
   1104 
   1105 /*
   1106  * Start a document
   1107  */
   1108 genxStatus genxStartDocFile(genxWriter w, FILE * file)
   1109 {
   1110   if (w->sequence != SEQUENCE_NO_DOC)
   1111     return w->status = GENX_SEQUENCE_ERROR;
   1112   
   1113   w->sequence = SEQUENCE_PRE_DOC;
   1114   w->file = file;
   1115   w->sender = NULL;
   1116   return GENX_SUCCESS;
   1117 }
   1118 
   1119 genxStatus genxStartDocSender(genxWriter w, genxSender * sender)
   1120 {
   1121   if (w->sequence != SEQUENCE_NO_DOC)
   1122     return w->status = GENX_SEQUENCE_ERROR;
   1123   
   1124   w->sequence = SEQUENCE_PRE_DOC;
   1125   w->file = NULL;
   1126   w->sender = sender;
   1127   return GENX_SUCCESS;
   1128 }
   1129 
   1130 /*
   1131  * Write out the attributes we've been gathering up for an element.  We save
   1132  *  them until we've gathered them all so they can be writen in canonical
   1133  *  order.
   1134  * Also, we end the start-tag.
   1135  * The trick here is that we keep the attribute list properly sorted as
   1136  *  we build it, then as each attribute is added, we fill in its value and
   1137  *  mark the fact that it's been added, in the "provided" field.
   1138  */
   1139 static genxStatus writeStartTag(genxWriter w)
   1140 {
   1141   int i;
   1142   genxAttribute * aa = (genxAttribute *) w->attributes.pointers;
   1143   genxElement e = w->nowStarting;
   1144 
   1145   /*
   1146    * make sure the right namespace decls are in effect;
   1147    *  if they are these might create an error, so ignore it
   1148    */
   1149   if (e->ns)
   1150     addNamespace(e->ns, NULL);
   1151   else
   1152     unsetDefaultNamespace(w);
   1153   w->status = GENX_SUCCESS;
   1154 
   1155   SendCheck(w, "<");
   1156   if (e->ns && (e->ns->declaration != w->xmlnsEquals))
   1157   {
   1158     SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
   1159     SendCheck(w, ":");
   1160   }
   1161   SendCheck(w, e->type);
   1162 
   1163   for (i = 0; i < w->attributes.count; i++)
   1164   {
   1165     if (aa[i]->provided)
   1166     {
   1167       if (aa[i]->ns && aa[i]->ns->baroque &&
   1168 	  aa[i]->ns->declaration == w->xmlnsEquals)
   1169 	return w->status = GENX_ATTRIBUTE_IN_DEFAULT_NAMESPACE;
   1170 
   1171       SendCheck(w, " ");
   1172 
   1173       if (aa[i]->ns)
   1174       {
   1175 	SendCheck(w, aa[i]->ns->declaration->name + STRLEN_XMLNS_COLON)
   1176 	SendCheck(w, ":");
   1177       }
   1178       SendCheck(w, aa[i]->name);
   1179       SendCheck(w, "=\"");
   1180       SendCheck(w, aa[i]->value.buf);
   1181       SendCheck(w, "\"");
   1182     }
   1183   }
   1184   SendCheck(w, ">");
   1185   return GENX_SUCCESS;
   1186 }
   1187 
   1188 /*
   1189  * internal clear-er; no sequence checking
   1190  */
   1191 static genxStatus unsetDefaultNamespace(genxWriter w)
   1192 {
   1193   int i;
   1194   Boolean found = False;
   1195 
   1196   /* don't put it in if not needed */
   1197   i = w->stack.count - 1;
   1198   while (found == False && i > 0)
   1199   {
   1200     while (w->stack.pointers[i] != NULL)
   1201     {
   1202       genxAttribute decl = (genxAttribute) w->stack.pointers[i--];
   1203       genxNamespace ns = (genxNamespace) w->stack.pointers[i--];
   1204 
   1205       /* if already unset */
   1206       if (ns == NULL)
   1207 	return w->status = GENX_SUCCESS;
   1208 
   1209       /*
   1210        * the default namespace was declared.  This namespace now
   1211        *  becomes baroque
   1212        */
   1213       if (decl == w->xmlnsEquals)
   1214       {
   1215 	ns->baroque = True;
   1216 	found = True;
   1217 	break;
   1218       }
   1219     }
   1220     i -= 2;
   1221   }
   1222 
   1223   if (!found)
   1224     return GENX_SUCCESS;
   1225 
   1226   /*
   1227    * push a signal on the stack
   1228    */
   1229   if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
   1230     return w->status;
   1231   w->status = listAppend(&w->stack, w->xmlnsEquals);
   1232   if (w->status != GENX_SUCCESS)
   1233     return w->status;
   1234 
   1235   /* add the xmlns= attribute, it must be the first one */
   1236   return addAttribute(w->xmlnsEquals, w->empty);
   1237 }
   1238 
   1239 /*
   1240  * clear the default namespace declaration
   1241  */
   1242 genxStatus genxUnsetDefaultNamespace(genxWriter w)
   1243 {
   1244 
   1245   /* can only do this while in start-tag mode */
   1246   if (w->sequence != SEQUENCE_START_TAG)
   1247     return w->status = GENX_SEQUENCE_ERROR;
   1248 
   1249   return unsetDefaultNamespace(w);
   1250 }
   1251 
   1252 genxStatus genxStartElement(genxElement e)
   1253 {
   1254   genxWriter w = e->writer;
   1255   int i;
   1256 
   1257   switch (w->sequence)
   1258   {
   1259   case SEQUENCE_NO_DOC:
   1260   case SEQUENCE_POST_DOC:
   1261     return w->status = GENX_SEQUENCE_ERROR;
   1262   case SEQUENCE_START_TAG:
   1263   case SEQUENCE_ATTRIBUTES:
   1264     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1265       return w->status;
   1266     break;
   1267   case SEQUENCE_PRE_DOC:
   1268   case SEQUENCE_CONTENT:
   1269     break;
   1270   }
   1271 
   1272   w->sequence = SEQUENCE_START_TAG;
   1273 
   1274   /* clear provided attributes */
   1275   for (i = 0; i < w->attributes.count; i++)
   1276     ((genxAttribute) w->attributes.pointers[i])->provided = 0;
   1277 
   1278   /*
   1279    * push the stack.  We push a NULL after a pointer to this element
   1280    *  because the stack will also contain pointers to the namespace
   1281    *  attributes that got declared here, so we can keep track of what's
   1282    *  in effect.  I.e. a single stack entry consists logically of a pointer
   1283    *  to an element object, a NULL, then zero or more pairs of pointers to
   1284    *  namespace objects/declarations
   1285    */
   1286   if ((w->status = listAppend(&w->stack, e)) != GENX_SUCCESS)
   1287     return w->status;
   1288   if ((w->status = listAppend(&w->stack, NULL)) != GENX_SUCCESS)
   1289     return w->status;
   1290 
   1291   w->nowStarting = e;
   1292 
   1293   return GENX_SUCCESS;
   1294 }
   1295 
   1296 /*
   1297  * internal namespace adder; no sequence checking
   1298  */
   1299 static genxStatus addNamespace(genxNamespace ns, utf8 prefix)
   1300 {
   1301   genxWriter w = ns->writer;
   1302   genxAttribute decl;
   1303   int i;
   1304   genxElement e;
   1305 
   1306   /*
   1307    * first, we'll find the declaring attribute
   1308    */
   1309   if (prefix == NULL)
   1310     decl = ns->defaultDecl;
   1311   else
   1312   {
   1313     if (prefix[0] == 0)
   1314       decl = w->xmlnsEquals;
   1315     else
   1316     {
   1317       if ((prefix = storePrefix(w, prefix, True)) == NULL)
   1318 	return w->status;
   1319       decl = declareAttribute(w, NULL, prefix, ns->name, &w->status);
   1320       if (decl == NULL || w->status != GENX_SUCCESS)
   1321 	return w->status;
   1322     }
   1323   }
   1324 
   1325   if (decl != ns->defaultDecl)
   1326     ns->baroque = True;
   1327 
   1328   /*
   1329    * avoid doing anything if this namespace is already declared.  If
   1330    *  they've shown good taste we can do this cheaply
   1331    */
   1332   if (!ns->baroque)
   1333   {
   1334     if (ns->declCount > 0)
   1335       return w->status = GENX_SUCCESS;
   1336   }
   1337   else
   1338   {
   1339 
   1340     /*
   1341      * First, we'll run all the way up the stack to see if there is
   1342      *  another declaration for this namespace/prefix in scope, in which
   1343      *  case it's a no-op; or, if there's another declaration for this
   1344      *  prefix on another namespace, in which case we have to over-ride
   1345      */
   1346     i = w->stack.count - 1;
   1347     while (i > 0)
   1348     {
   1349       while (w->stack.pointers[i] != NULL)
   1350       {
   1351 	genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
   1352 	genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
   1353 
   1354 	if (ns == otherNs)
   1355 	{
   1356 	  if (decl == otherDecl)
   1357 	    return w->status = GENX_SUCCESS;
   1358 	  else
   1359 	  {
   1360 	    i = 0;
   1361 	    break;
   1362 	  }
   1363 	}
   1364 	else
   1365 	{
   1366 	  /* different namespace, same prefix? */
   1367 	  if (decl == otherDecl)
   1368 	  {
   1369 	    i = 0;
   1370 	    break;
   1371 	  }
   1372 	}
   1373       }
   1374       i -= 2;
   1375     }
   1376   }
   1377 
   1378   /*
   1379    * If this namespace is already declared on
   1380    *  this element (with different prefix/decl) which is an error.
   1381    */
   1382   i = w->stack.count - 1;
   1383   while (w->stack.pointers[i] != NULL)
   1384   {
   1385     genxNamespace otherNs;
   1386     i--; /* don't need declaration */
   1387     otherNs = (genxNamespace) w->stack.pointers[i--];
   1388 
   1389     if (ns == otherNs)
   1390       return w->status = GENX_DUPLICATE_NAMESPACE;
   1391   }
   1392 
   1393   /* move pointer from NULL to element */
   1394   --i;
   1395 
   1396   /*
   1397    * It's also an error if this is a default-namespace declaration and the
   1398    *  element is in no namespace.
   1399    */
   1400   e = (genxElement) w->stack.pointers[i];    
   1401   if (e->ns == NULL && decl == w->xmlnsEquals)
   1402     return w->status = GENX_BAD_DEFAULT_DECLARATION;
   1403 
   1404   if ((w->status = listAppend(&w->stack, ns)) != GENX_SUCCESS)
   1405     return w->status;
   1406   if ((w->status = listAppend(&w->stack, decl)) != GENX_SUCCESS)
   1407     return w->status;
   1408 
   1409   ns->declaration = decl;
   1410   ns->declCount++;
   1411   return addAttribute(decl, ns->name);
   1412 }
   1413 
   1414 /*
   1415  * Add a namespace declaration
   1416  */
   1417 genxStatus genxAddNamespace(genxNamespace ns, utf8 prefix)
   1418 {
   1419   if (ns->writer->sequence != SEQUENCE_START_TAG)
   1420     return ns->writer->status = GENX_SEQUENCE_ERROR;
   1421 
   1422   return addNamespace(ns, prefix);
   1423 }
   1424 
   1425 /*
   1426  * Private attribute-adding code
   1427  * most of the work here is normalizing the value, which is the same
   1428  *  as regular normalization except for " is replaced by "&quot;"
   1429  */
   1430 static genxStatus addAttribute(genxAttribute a, constUtf8 valuestr)
   1431 {
   1432   utf8 lastv = (utf8) valuestr;
   1433   genxWriter w = a->writer;
   1434 
   1435   /* if valuestr not provided, this is an xmlns with a pre-cooked value */
   1436   if (valuestr)
   1437   {
   1438     startCollect(&a->value);
   1439     while (*valuestr)
   1440     {
   1441       int c = genxNextUnicodeChar(&valuestr);
   1442       
   1443       if (c == -1)
   1444 	return w->status = GENX_BAD_UTF8;
   1445       
   1446       if (!isXMLChar(w, c))
   1447 	return w->status = GENX_NON_XML_CHARACTER;
   1448       
   1449       switch(c)
   1450       {
   1451       case 9:
   1452 	collectPiece(w, &a->value, "&#x9;", 5);
   1453 	break;
   1454       case 0xa:
   1455 	collectPiece(w, &a->value, "&#xA;", 5); 
   1456 	break;
   1457       case 0xd:
   1458 	collectPiece(w, &a->value, "&#xD;", 5); 
   1459 	break;
   1460       case '"':
   1461 	collectPiece(w, &a->value, "&quot;", 6);
   1462 	break;
   1463       case '<':
   1464 	collectPiece(w, &a->value, "&lt;", 4);
   1465 	break;
   1466       case '&':
   1467 	collectPiece(w, &a->value, "&amp;", 5);
   1468 	break;
   1469 	/*
   1470       case '>':
   1471 	collectPiece(w, &a->value, "&gt;", 4);
   1472 	break;
   1473 	*/
   1474       default:
   1475 	collectPiece(w, &a->value, (const char *) lastv, valuestr - lastv);
   1476 	break;
   1477       }
   1478       lastv = (utf8) valuestr;
   1479     }
   1480     endCollect(&a->value);
   1481   }
   1482 
   1483   /* now add the namespace attribute; might fail if it's bee hand-declared */
   1484   if (a->ns)
   1485     addNamespace(a->ns, NULL);
   1486 
   1487   if (valuestr && a->provided)
   1488     return w->status = GENX_DUPLICATE_ATTRIBUTE;
   1489   a->provided = 1;
   1490 
   1491   return GENX_SUCCESS;
   1492 }
   1493 
   1494 /*
   1495  * public attribute adder.
   1496  * The only difference is that it doesn't allow a NULL value
   1497  */
   1498 genxStatus genxAddAttribute(genxAttribute a, constUtf8 valuestr)
   1499 {
   1500   if (a->writer->sequence != SEQUENCE_START_TAG &&
   1501       a->writer->sequence != SEQUENCE_ATTRIBUTES)
   1502     return a->writer->status = GENX_SEQUENCE_ERROR;
   1503   a->writer->sequence = SEQUENCE_ATTRIBUTES;
   1504 
   1505   if (valuestr == NULL)
   1506     return a->writer->status = GENX_MISSING_VALUE;
   1507 
   1508   return addAttribute(a, valuestr);
   1509 }
   1510 
   1511 genxStatus genxEndElement(genxWriter w)
   1512 {
   1513   genxElement e;
   1514   int i;
   1515 
   1516   switch (w->sequence)
   1517   {
   1518   case SEQUENCE_NO_DOC:
   1519   case SEQUENCE_PRE_DOC:
   1520   case SEQUENCE_POST_DOC:
   1521     return w->status = GENX_SEQUENCE_ERROR;
   1522   case SEQUENCE_START_TAG:
   1523   case SEQUENCE_ATTRIBUTES:
   1524     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1525       return w->status;
   1526     break;
   1527   case SEQUENCE_CONTENT:
   1528     break;
   1529   }
   1530 
   1531   /*
   1532    * first peek into the stack to find the right namespace declaration
   1533    *  (if any) so we can properly prefix the end-tag.  Have to do this
   1534    *  before unwinding the stack because that might reset some xmlns
   1535    *  prefixes to the context in the parent element
   1536    */
   1537   for (i = w->stack.count - 1; w->stack.pointers[i] != NULL; i -= 2)
   1538     ;
   1539   e = (genxElement) w->stack.pointers[--i];
   1540 
   1541   SendCheck(w, "</");
   1542   if (e->ns && e->ns->declaration != w->xmlnsEquals)
   1543   {
   1544     SendCheck(w, e->ns->declaration->name + STRLEN_XMLNS_COLON);
   1545     SendCheck(w, ":");
   1546   }
   1547   SendCheck(w, e->type);
   1548   SendCheck(w, ">");
   1549 
   1550   /*
   1551    * pop zero or more namespace declarations, then a null, then the
   1552    *  start-element declaration off the stack
   1553    */
   1554   w->stack.count--;
   1555   while (w->stack.pointers[w->stack.count] != NULL)
   1556   {
   1557     genxNamespace ns = (genxNamespace) w->stack.pointers[--w->stack.count];
   1558     w->stack.count--; /* don't need decl */
   1559 
   1560     /* if not a fake unset-default namespace */
   1561     if (ns)
   1562     {
   1563       /*
   1564        * if they've stupidly jammed in their own namespace-prefix
   1565        *  declarations, we have to go looking to see if there's another
   1566        *  one in effect
   1567        */
   1568       if (ns->baroque)
   1569       {
   1570 	i = w->stack.count;
   1571 	while (i > 0)
   1572 	{
   1573 	  while (w->stack.pointers[i] != NULL)
   1574 	  {
   1575 	    genxAttribute otherDecl = (genxAttribute) w->stack.pointers[i--];
   1576 	    genxNamespace otherNs = (genxNamespace) w->stack.pointers[i--];
   1577 	    
   1578 	    if (otherNs == ns)
   1579 	    {
   1580 	      ns->declaration = otherDecl;
   1581 	      i = 0;
   1582 	      break;
   1583 	    }
   1584 	  }
   1585 	  
   1586 	  /* skip NULL & element */
   1587 	  i -= 2;
   1588 	}
   1589       }
   1590       ns->declCount--;
   1591       if (ns->declCount == 0)
   1592 	ns->baroque = False;
   1593     }
   1594   }
   1595 
   1596   /* pop the NULL */
   1597   --w->stack.count;
   1598   if (w->stack.count < 0)
   1599     return w->status = GENX_NO_START_TAG;
   1600 
   1601   if (w->stack.count == 0)
   1602     w->sequence = SEQUENCE_POST_DOC;
   1603   else
   1604     w->sequence = SEQUENCE_CONTENT;
   1605 
   1606   return GENX_SUCCESS;
   1607 }
   1608 
   1609 /*
   1610  * Internal character-adder.  It tries to keep the number of sendx()
   1611  *  calls down by looking at each character but only doing the output
   1612  *  when it has to escape something; ordinary text gets saved up in
   1613  *  chunks the start of which is indicated by *breaker.
   1614  * c is the character, next points to the UTF8 representing the next
   1615  *  lastsP indirectly points to the UTF8 representing the
   1616  *  character, breakerP* indirectly points to the last place genx
   1617  *  changed the UTF8, e.g. by escaping a '<'
   1618  */
   1619 static genxStatus addChar(genxWriter w, int c, constUtf8 next,
   1620 			  constUtf8 * lastsP, constUtf8 * breakerP)
   1621 {
   1622   if (c == -1)
   1623     return GENX_BAD_UTF8;
   1624 
   1625   if (!isXMLChar(w, c))
   1626     return GENX_NON_XML_CHARACTER;
   1627 
   1628   switch(c)
   1629   {
   1630   case 0xd:
   1631     if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
   1632       return w->status;
   1633     *breakerP = next;
   1634     sendx(w, (utf8) "&#xD;");
   1635     break;
   1636   case '<':
   1637     if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
   1638       return w->status;
   1639     *breakerP = next;
   1640     sendx(w, (utf8) "&lt;");
   1641     break;
   1642   case '&':
   1643     if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
   1644       return w->status;
   1645     *breakerP = next;
   1646     sendx(w, (utf8) "&amp;");
   1647     break;
   1648   case '>':
   1649     if ((w->status = sendxBounded(w, *breakerP, *lastsP)) != GENX_SUCCESS)
   1650       return w->status;
   1651     *breakerP = next;
   1652     sendx(w, (utf8) "&gt;");
   1653     break;
   1654   default:
   1655     break;
   1656   }
   1657   *lastsP = next;
   1658   return GENX_SUCCESS;
   1659 }
   1660 
   1661 genxStatus genxAddText(genxWriter w, constUtf8 start)
   1662 {
   1663   constUtf8 lasts = start;
   1664   constUtf8 breaker = start;
   1665 
   1666   if (w->sequence == SEQUENCE_START_TAG ||
   1667       w->sequence == SEQUENCE_ATTRIBUTES)
   1668   {
   1669     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1670       return w->status;
   1671     w->sequence = SEQUENCE_CONTENT;
   1672   }
   1673 
   1674   if (w->sequence != SEQUENCE_CONTENT)
   1675     return w->status = GENX_SEQUENCE_ERROR;
   1676 
   1677   while (*start)
   1678   {
   1679     int c = genxNextUnicodeChar(&start);
   1680 
   1681     w->status = addChar(w, c, start, &lasts, &breaker);
   1682     if (w->status != GENX_SUCCESS)
   1683       return w->status;
   1684   }
   1685   return sendxBounded(w, breaker, (utf8) start);
   1686 }
   1687 
   1688 genxStatus genxAddBoundedText(genxWriter w, constUtf8 start, constUtf8 end)
   1689 {
   1690   constUtf8 lasts = start;
   1691   constUtf8 breaker = start;
   1692 
   1693   if (w->sequence == SEQUENCE_START_TAG ||
   1694       w->sequence == SEQUENCE_ATTRIBUTES)
   1695   {
   1696     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1697       return w->status;
   1698     w->sequence = SEQUENCE_CONTENT;
   1699   }
   1700 
   1701   if (w->sequence != SEQUENCE_CONTENT)
   1702     return w->status = GENX_SEQUENCE_ERROR;
   1703 
   1704   while (start < end)
   1705   {
   1706     int c = genxNextUnicodeChar(&start);
   1707 
   1708     w->status = addChar(w, c, (utf8) start, &lasts, &breaker);
   1709     if (w->status != GENX_SUCCESS)
   1710       return w->status;
   1711   }
   1712   return sendxBounded(w, breaker, (utf8) start);
   1713 }
   1714 
   1715 genxStatus genxAddCountedText(genxWriter w, constUtf8 start, int byteCount)
   1716 {
   1717   utf8 end = (utf8) (start + byteCount);
   1718 
   1719   return genxAddBoundedText(w, start, end);
   1720 }
   1721 
   1722 genxStatus genxAddCharacter(genxWriter w, int c)
   1723 {
   1724   unsigned char cUTF8[10];
   1725   utf8 lasts, breaker, next;
   1726 
   1727   if (w->sequence == SEQUENCE_START_TAG ||
   1728       w->sequence == SEQUENCE_ATTRIBUTES)
   1729   {
   1730     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1731       return w->status;
   1732     w->sequence = SEQUENCE_CONTENT;
   1733   }
   1734 
   1735   if (w->sequence != SEQUENCE_CONTENT)
   1736     return w->status = GENX_SEQUENCE_ERROR;
   1737 
   1738   if (!isXMLChar(w, c))
   1739     return w->status = GENX_NON_XML_CHARACTER;
   1740 
   1741   /* make UTF8 representation of character */
   1742   lasts = breaker = next = cUTF8;
   1743 
   1744   if (c < 0x80)
   1745     *next++ = c;
   1746   else if (c < 0x800)
   1747   {
   1748     *next++ = 0xc0 | (c >> 6);
   1749     *next++ = 0x80 | (c & 0x3f);
   1750   }
   1751   else if (c < 0x10000)
   1752   {
   1753     *next++ = 0xe0 | (c >> 12);
   1754     *next++ = 0x80 | ((c & 0xfc0) >> 6);
   1755     *next++ = 0x80 | (c & 0x3f);
   1756   }
   1757   else
   1758   {
   1759     *next++ = 0xf0 | (c >> 18);
   1760     *next++ = 0x80 | ((c & 0x3f000) >> 12);
   1761     *next++ = 0x80 | ((c & 0xfc0) >> 6);
   1762     *next++ = 0x80 | (c & 0x3f);
   1763   }
   1764   *next = 0;
   1765 
   1766   w->status =
   1767     addChar(w, c, next, (constUtf8 *) &lasts, (constUtf8 *) &breaker);
   1768   if (w->status != GENX_SUCCESS)
   1769     return w->status;
   1770 
   1771   return sendxBounded(w, breaker, next);
   1772 }  
   1773 
   1774 genxStatus genxEndDocument(genxWriter w)
   1775 {
   1776   if (w->sequence != SEQUENCE_POST_DOC)
   1777     return w->status = GENX_SEQUENCE_ERROR;
   1778 
   1779   if (w->file)
   1780     fflush(w->file);
   1781   else
   1782     if ((w->status = (*w->sender->flush)(w->userData)) != GENX_SUCCESS)
   1783       return w->status;
   1784 
   1785   w->sequence = SEQUENCE_NO_DOC;
   1786   return GENX_SUCCESS;
   1787 }
   1788 
   1789 genxStatus genxComment(genxWriter w, constUtf8 text)
   1790 {
   1791   int i;
   1792 
   1793   if (w->sequence == SEQUENCE_NO_DOC)
   1794     return w->status = GENX_SEQUENCE_ERROR;
   1795 
   1796   if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
   1797     return w->status;
   1798 
   1799   /* no leading '-', no trailing '-', no '--' */
   1800   if (text[0] == '-')
   1801     return w->status = GENX_MALFORMED_COMMENT;
   1802   for (i = 0; text[i]; i++)
   1803     if (text[i] == '-' && (text[i + 1] == '-' || text[i + 1] == 0))
   1804       return w->status = GENX_MALFORMED_COMMENT;
   1805 
   1806   if (w->sequence == SEQUENCE_START_TAG ||
   1807       w->sequence == SEQUENCE_ATTRIBUTES)
   1808   {
   1809     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1810       return w->status;
   1811     w->sequence = SEQUENCE_CONTENT;
   1812   }
   1813 
   1814   else if (w->sequence == SEQUENCE_POST_DOC)
   1815     if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
   1816       return w->status;
   1817 
   1818   if ((w->status = sendx(w, (utf8) "<!--")) != GENX_SUCCESS)
   1819     return w->status;
   1820   if ((w->status = sendx(w, (utf8) text)) != GENX_SUCCESS)
   1821     return w->status;
   1822   if ((w->status = sendx(w, (utf8) "-->")) != GENX_SUCCESS)
   1823     return w->status;
   1824 
   1825   if (w->sequence == SEQUENCE_PRE_DOC)
   1826     if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
   1827       return w->status;
   1828 
   1829   return GENX_SUCCESS;
   1830 }
   1831 
   1832 genxStatus genxPI(genxWriter w, constUtf8 target, constUtf8 text)
   1833 {
   1834   int i;
   1835 
   1836   if (w->sequence == SEQUENCE_NO_DOC)
   1837     return w->status = GENX_SEQUENCE_ERROR;
   1838 
   1839   if ((w->status = genxCheckText(w, target)) != GENX_SUCCESS)
   1840     return w->status;
   1841   if ((w->status = checkNCName(w, target)) != GENX_SUCCESS)
   1842     return w->status;
   1843   if ((strlen((const char *) target) >= 3) &&
   1844       (target[0] == 'x' || target[0] == 'X') &&
   1845       (target[1] == 'm' || target[1] == 'M') &&
   1846       (target[2] == 'l' || target[2] == 'L') &&
   1847       (target[3] == 0))
   1848     return w->status = GENX_XML_PI_TARGET;
   1849 
   1850   if ((w->status = genxCheckText(w, text)) != GENX_SUCCESS)
   1851     return w->status;
   1852 
   1853   /* no ?> within */
   1854   for (i = 1; text[i]; i++)
   1855     if (text[i] == '>' && text[i - 1] == '?')
   1856       return w->status = GENX_MALFORMED_PI;
   1857 
   1858   if (w->sequence == SEQUENCE_START_TAG ||
   1859       w->sequence == SEQUENCE_ATTRIBUTES)
   1860   {
   1861     if ((w->status = writeStartTag(w)) != GENX_SUCCESS)
   1862       return w->status;
   1863     w->sequence = SEQUENCE_CONTENT;
   1864   }
   1865 
   1866   else if (w->sequence == SEQUENCE_POST_DOC)
   1867     if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
   1868       return w->status;
   1869 
   1870   if ((w->status = sendx(w, (utf8) "<?")) != GENX_SUCCESS)
   1871     return w->status;
   1872   if ((w->status = sendx(w, target)) != GENX_SUCCESS)
   1873     return w->status;
   1874   if (text[0])
   1875   {
   1876     if ((w->status = sendx(w, (utf8) " ")) != GENX_SUCCESS)
   1877       return w->status;
   1878     if ((w->status = sendx(w, text)) != GENX_SUCCESS)
   1879       return w->status;
   1880   }
   1881   if ((w->status = sendx(w, (utf8) "?>")) != GENX_SUCCESS)
   1882     return w->status;
   1883 
   1884   if (w->sequence == SEQUENCE_PRE_DOC)
   1885     if ((w->status = sendx(w, (utf8) "\n")) != GENX_SUCCESS)
   1886       return w->status;
   1887 
   1888   return GENX_SUCCESS;
   1889 }
   1890 
   1891 /*******************************
   1892  * Literal versions of the writing routines
   1893  */
   1894 genxStatus genxStartElementLiteral(genxWriter w,
   1895 				   constUtf8 xmlns, constUtf8 type)
   1896 {
   1897   genxNamespace ns = NULL;
   1898   genxElement e;
   1899 
   1900   if (xmlns)
   1901   {
   1902     ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
   1903     if (ns == NULL || w->status != GENX_SUCCESS)
   1904       return w->status;
   1905   }
   1906   e = genxDeclareElement(w, ns, type, &w->status);
   1907   if (e == NULL || w->status != GENX_SUCCESS)
   1908     return w->status;
   1909 
   1910   return genxStartElement(e);
   1911 }
   1912 
   1913 genxStatus genxAddAttributeLiteral(genxWriter w, constUtf8 xmlns,
   1914 				   constUtf8 name, constUtf8 value)
   1915 {
   1916   genxNamespace ns = NULL;
   1917   genxAttribute a;
   1918 
   1919   if (xmlns)
   1920   {
   1921     ns = genxDeclareNamespace(w, xmlns, NULL, &w->status);
   1922     if (ns == NULL && w->status != GENX_SUCCESS)
   1923       return w->status;
   1924   }
   1925   
   1926   a = genxDeclareAttribute(w, ns, name, &w->status);
   1927   if (a == NULL || w->status != GENX_SUCCESS)
   1928     return w->status;
   1929 
   1930   return genxAddAttribute(a, value);
   1931 }
   1932 
   1933 /*
   1934  * return version
   1935  */
   1936 char * genxGetVersion()
   1937 {
   1938   return GENX_VERSION;
   1939 }
   1940