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 """
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, "	", 5);
1453 break;
1454 case 0xa:
1455 collectPiece(w, &a->value, "
", 5);
1456 break;
1457 case 0xd:
1458 collectPiece(w, &a->value, "
", 5);
1459 break;
1460 case '"':
1461 collectPiece(w, &a->value, """, 6);
1462 break;
1463 case '<':
1464 collectPiece(w, &a->value, "<", 4);
1465 break;
1466 case '&':
1467 collectPiece(w, &a->value, "&", 5);
1468 break;
1469 /*
1470 case '>':
1471 collectPiece(w, &a->value, ">", 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) "
");
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) "<");
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) "&");
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) ">");
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