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