|
1 /* |
|
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
|
3 * |
|
4 * Copyright 2019 Olaf Wintermann. All rights reserved. |
|
5 * |
|
6 * Redistribution and use in source and binary forms, with or without |
|
7 * modification, are permitted provided that the following conditions are met: |
|
8 * |
|
9 * 1. Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * |
|
12 * 2. Redistributions in binary form must reproduce the above copyright |
|
13 * notice, this list of conditions and the following disclaimer in the |
|
14 * documentation and/or other materials provided with the distribution. |
|
15 * |
|
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
|
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
|
26 * POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <stdio.h> |
|
30 #include <stdlib.h> |
|
31 #include <string.h> |
|
32 |
|
33 #include <ucx/string.h> |
|
34 #include <ucx/map.h> |
|
35 #include <ucx/buffer.h> |
|
36 |
|
37 #include "../util/util.h" |
|
38 #include "../util/pool.h" |
|
39 |
|
40 #include "xml.h" |
|
41 |
|
42 /***************************************************************************** |
|
43 * Utility functions |
|
44 *****************************************************************************/ |
|
45 |
|
46 /* |
|
47 * generates a string key for an xml namespace |
|
48 * format: prefix '\0' href |
|
49 */ |
|
50 static sstr_t xml_namespace_key(UcxAllocator *a, WSNamespace *ns) { |
|
51 sstr_t key = sstrcat_a(a, 3, |
|
52 ns->prefix ? sstr((char*)ns->prefix) : S("\0"), |
|
53 S("\0"), |
|
54 sstr((char*)ns->href)); |
|
55 return key; |
|
56 } |
|
57 |
|
58 |
|
59 /***************************************************************************** |
|
60 * Public functions |
|
61 *****************************************************************************/ |
|
62 |
|
63 /* ------------------------ wsxml_iterator ------------------------ */ |
|
64 |
|
65 typedef struct StackElm { |
|
66 WSXmlNode *node; // list of nodes |
|
67 //WSXmlNode *parent; // if not NULL, call endcb after node->next is NULL |
|
68 int endonly; |
|
69 struct StackElm *next; |
|
70 } StackElm; |
|
71 |
|
72 #define STACK_PUSH(stack, elm) if(stack) { elm->next = stack; } stack = elm; |
|
73 |
|
74 int wsxml_iterator( |
|
75 pool_handle_t *pool, |
|
76 WSXmlNode *node, |
|
77 wsxml_func begincb, |
|
78 wsxml_func endcb, |
|
79 void *udata) |
|
80 { |
|
81 if(!node) { |
|
82 return 0; |
|
83 } |
|
84 |
|
85 StackElm *stack = pool_malloc(pool, sizeof(StackElm)); |
|
86 if(!stack) { |
|
87 return 1; // OOM |
|
88 } |
|
89 stack->next = NULL; |
|
90 stack->node = node; |
|
91 stack->endonly = 0; |
|
92 //stack->parent = NULL; |
|
93 |
|
94 int ret = 0; |
|
95 int br = 0; |
|
96 while(stack) { |
|
97 StackElm *cur = stack; |
|
98 WSXmlNode *xmlnode = cur->node; // get top stack element |
|
99 stack = cur->next; // and remove it |
|
100 cur->next = NULL; |
|
101 |
|
102 while(xmlnode && !cur->endonly) { |
|
103 // element begin callback |
|
104 if(begincb(xmlnode, udata)) { |
|
105 br = 1; |
|
106 break; // I don't like break with labels - is this wrong? |
|
107 } |
|
108 |
|
109 if(xmlnode->children) { |
|
110 // put the children on the stack |
|
111 // the next stack iteration will process the children |
|
112 StackElm *newelm = pool_malloc(pool, sizeof(StackElm)); |
|
113 if(!newelm) { |
|
114 ret = 1; |
|
115 br = 1; |
|
116 break; |
|
117 } |
|
118 newelm->next = NULL; |
|
119 newelm->node = xmlnode->children; |
|
120 // setting the parent will make sure endcb will be called |
|
121 // for the current xmlnode after all children are processed |
|
122 //newelm->parent = xmlnode; |
|
123 newelm->endonly = 0; |
|
124 |
|
125 // if xmlnode->next is not NULL, there are still nodes at |
|
126 // this level, therefore we have to put these also on the |
|
127 // stack |
|
128 // this way, the remaining nodes are processed after all |
|
129 // children and the end tag are processed |
|
130 if(xmlnode->next) { |
|
131 StackElm *nextelm = pool_malloc(pool, sizeof(StackElm)); |
|
132 if(!nextelm) { |
|
133 ret = 1; |
|
134 br = 1; |
|
135 break; |
|
136 } |
|
137 nextelm->node = xmlnode->next; |
|
138 nextelm->next = NULL; |
|
139 nextelm->endonly = 0; |
|
140 STACK_PUSH(stack, nextelm); |
|
141 } |
|
142 |
|
143 // we have to put the end tag of the current element |
|
144 // on the stack to ensure endcb is called for the current |
|
145 // element, after all children are processed |
|
146 // reuse cur |
|
147 cur->node = xmlnode; |
|
148 cur->endonly = 1; |
|
149 STACK_PUSH(stack, cur); |
|
150 |
|
151 cur = NULL; |
|
152 |
|
153 // now we can put the children on the stack |
|
154 STACK_PUSH(stack, newelm); |
|
155 // break, because we don't want to process xmlnode->next now |
|
156 break; |
|
157 } else { |
|
158 // no children means, the end callback can be called directly |
|
159 // after the begin callback (no intermediate nodes) |
|
160 cur->node = NULL; |
|
161 if(endcb(xmlnode, udata)) { |
|
162 br = 1; |
|
163 break; |
|
164 } |
|
165 } |
|
166 |
|
167 // continue with next node at this level |
|
168 xmlnode = xmlnode->next; |
|
169 } |
|
170 if(br) { |
|
171 break; // break because of an error |
|
172 } |
|
173 |
|
174 if(cur && cur->node) { |
|
175 //xmlNode *endNode = cur->parent ? cur->parent : cur->node; |
|
176 xmlNode *endNode = cur->node; |
|
177 if(endcb(endNode, udata)) { |
|
178 break; |
|
179 } |
|
180 pool_free(pool, cur); |
|
181 } |
|
182 } |
|
183 |
|
184 // free all remaining elements |
|
185 StackElm *elm = stack; |
|
186 while(elm) { |
|
187 StackElm *next = elm->next; |
|
188 pool_free(pool, elm); |
|
189 elm = next; |
|
190 } |
|
191 |
|
192 return ret; |
|
193 } |
|
194 |
|
195 /* ------------------- wsxml_get_required_namespaces ------------------- */ |
|
196 |
|
197 typedef struct WSNsCollector { |
|
198 UcxAllocator *a; |
|
199 UcxMap *nsmap; |
|
200 WebdavNSList *def; |
|
201 int error; |
|
202 } WSNsCollector; |
|
203 |
|
204 static int nslist_node_begin(xmlNode *node, void *userdata) { |
|
205 WSNsCollector *col = userdata; |
|
206 // namespace required for all elements |
|
207 if(node->type == XML_ELEMENT_NODE && node->ns) { |
|
208 // we create a list of unique prefix-href namespaces by putting |
|
209 // all namespaces in a map |
|
210 sstr_t nskey = xml_namespace_key(col->a, node->ns); |
|
211 if(!nskey.ptr) { |
|
212 col->error = 1; |
|
213 return 1; |
|
214 } |
|
215 if(ucx_map_sstr_put(col->nsmap, nskey, node->ns)) { |
|
216 col->error = 1; |
|
217 return 1; |
|
218 } |
|
219 |
|
220 // collect all namespace definitions for removing these namespaces |
|
221 // from col->nsmap later |
|
222 WSNamespace *def = node->nsDef; |
|
223 while(def) { |
|
224 WebdavNSList *newdef = col->a->malloc( |
|
225 col->a->pool, sizeof(WebdavNSList)); |
|
226 if(!newdef) { |
|
227 col->error = 1; |
|
228 return 1; |
|
229 } |
|
230 newdef->namespace = def; |
|
231 newdef->prev = NULL; |
|
232 newdef->next = NULL; |
|
233 // prepend newdef to the list |
|
234 if(col->def) { |
|
235 newdef->next = col->def; |
|
236 col->def->prev = newdef; |
|
237 } |
|
238 col->def = newdef; |
|
239 |
|
240 // continue with next namespace definition |
|
241 def = def->next; |
|
242 } |
|
243 } |
|
244 return 0; |
|
245 } |
|
246 |
|
247 static int nslist_node_end(xmlNode *node, void *userdata) { |
|
248 return 0; |
|
249 } |
|
250 |
|
251 WebdavNSList* wsxml_get_required_namespaces( |
|
252 pool_handle_t *pool, |
|
253 WSXmlNode *node, |
|
254 int *error) |
|
255 { |
|
256 if(error) *error = 0; |
|
257 |
|
258 UcxAllocator a = util_pool_allocator(pool); |
|
259 UcxMap *nsmap = ucx_map_new_a(&a, 16); |
|
260 if(!nsmap) { |
|
261 if(error) *error = 1; |
|
262 return NULL; |
|
263 } |
|
264 |
|
265 WSNsCollector col; |
|
266 col.a = &a; |
|
267 col.nsmap = nsmap; |
|
268 col.def = NULL; |
|
269 |
|
270 // iterate over all xml elements |
|
271 // this will fill the hashmap with all namespaces |
|
272 // all namespace definitions are added to col.def |
|
273 WebdavNSList *list = NULL; |
|
274 WebdavNSList *end = NULL; |
|
275 if(wsxml_iterator(pool, node, nslist_node_begin, nslist_node_end, &col)) { |
|
276 if(error) *error = 1; |
|
277 } else { |
|
278 // remove all namespace definitions from the map |
|
279 // what we get is a map that contains all missing namespace definitions |
|
280 WebdavNSList *def = col.def; |
|
281 while(def) { |
|
282 sstr_t nskey = xml_namespace_key(&a, def->namespace); |
|
283 if(!nskey.ptr) { |
|
284 if(error) *error = 1; |
|
285 break; |
|
286 } |
|
287 ucx_map_sstr_remove(nsmap, nskey); |
|
288 def = def->next; |
|
289 } |
|
290 |
|
291 // convert nsmap to a list |
|
292 UcxMapIterator i = ucx_map_iterator(nsmap); |
|
293 WSNamespace *ns; |
|
294 UCX_MAP_FOREACH(key, ns, i) { |
|
295 WebdavNSList *newelm = pool_malloc(pool, sizeof(WebdavNSList)); |
|
296 if(!newelm) { |
|
297 if(error) *error = 1; |
|
298 list = NULL; |
|
299 break; |
|
300 } |
|
301 newelm->namespace = ns; |
|
302 newelm->next = NULL; |
|
303 newelm->prev = end; // NULL or the end of list |
|
304 if(end) { |
|
305 end->next = newelm; // append new element |
|
306 } else { |
|
307 list = newelm; // start new list |
|
308 } |
|
309 end = newelm; |
|
310 } |
|
311 } |
|
312 |
|
313 ucx_map_free(nsmap); |
|
314 return list; |
|
315 } |
|
316 |
|
317 |
|
318 static ssize_t buf_writefunc(void *buf, const char *s, size_t len) { |
|
319 int w = ucx_buffer_write(s, 1, len, buf); |
|
320 return w == 0 ? IO_ERROR : w; |
|
321 } |
|
322 |
|
323 WSXmlData* wsxml_node2data( |
|
324 pool_handle_t *pool, |
|
325 WSXmlNode *node) |
|
326 { |
|
327 UcxBuffer *buf = ucx_buffer_new(NULL, 1024, UCX_BUFFER_AUTOEXTEND); |
|
328 if(!buf) { |
|
329 return NULL; |
|
330 } |
|
331 |
|
332 int error = 0; |
|
333 WebdavNSList *nslist = wsxml_get_required_namespaces(pool, node, &error); |
|
334 if(error) { |
|
335 return NULL; |
|
336 } |
|
337 |
|
338 Writer writer; |
|
339 char buffer[512]; |
|
340 writer_init_with_stream(&writer, buf, buf_writefunc, buffer, 512); |
|
341 |
|
342 WSXmlData *data = NULL; |
|
343 if(!wsxml_write_nodes(pool, &writer, NULL, node) && !writer_flush(&writer)) { |
|
344 data = pool_malloc(pool, sizeof(WSXmlData)); |
|
345 if(data) { |
|
346 data->data = pool_malloc(pool, buf->size + 1); |
|
347 if(data->data) { |
|
348 memcpy(data->data, buf->space, buf->size); |
|
349 data->data[buf->size] = '\0'; |
|
350 data->length = buf->size; |
|
351 data->namespaces = nslist; |
|
352 } |
|
353 } |
|
354 } |
|
355 |
|
356 ucx_buffer_free(buf); |
|
357 |
|
358 return data; |
|
359 } |
|
360 |
|
361 char* wsxml_nslist2string(pool_handle_t *pool, WebdavNSList *nslist) { |
|
362 if(!nslist) return NULL; |
|
363 |
|
364 // get required string length |
|
365 size_t len = 0; |
|
366 WebdavNSList *elm = nslist; |
|
367 while(elm) { |
|
368 WSNamespace *ns = elm->namespace; |
|
369 if(ns) { |
|
370 if(ns->prefix) len += strlen((const char*)ns->prefix); |
|
371 if(ns->href) len += strlen((const char*)ns->href); |
|
372 len += 2; // 1 char for ':', 1 char for \n or \0 |
|
373 } |
|
374 elm = elm->next; |
|
375 } |
|
376 |
|
377 // alloc string |
|
378 char *str = pool_malloc(pool, len); |
|
379 if(!str) { |
|
380 return NULL; |
|
381 } |
|
382 char *pos = str; |
|
383 |
|
384 // copy namespace definitions to the string |
|
385 elm = nslist; |
|
386 while(elm) { |
|
387 WSNamespace *ns = elm->namespace; |
|
388 if(ns) { |
|
389 if(ns->prefix) { |
|
390 size_t prefixlen = strlen((const char*)ns->prefix); |
|
391 memcpy(pos, ns->prefix, prefixlen); |
|
392 pos[prefixlen] = ':'; |
|
393 pos += prefixlen + 1; |
|
394 } else { |
|
395 pos[0] = ':'; |
|
396 pos++; |
|
397 } |
|
398 if(ns->href) { |
|
399 size_t hreflen = strlen((const char*)ns->href); |
|
400 memcpy(pos, ns->href, hreflen); |
|
401 pos[hreflen] = elm->next ? '\n' : '\0'; |
|
402 pos += hreflen + 1; |
|
403 } else { |
|
404 pos[0] = elm->next ? '\n' : '\0'; |
|
405 pos++; |
|
406 } |
|
407 } |
|
408 elm = elm->next; |
|
409 } |
|
410 |
|
411 return str; |
|
412 } |
|
413 |
|
414 WebdavNSList* wsxml_string2nslist(pool_handle_t *pool, char *nsliststr) { |
|
415 if(!nsliststr) return NULL; |
|
416 size_t len = strlen(nsliststr); |
|
417 WebdavNSList *list_start = NULL; |
|
418 WebdavNSList *list_current = NULL; |
|
419 |
|
420 char *prefix = nsliststr; |
|
421 size_t prefix_start = 0; |
|
422 size_t prefix_len = 0; |
|
423 char *href = NULL; |
|
424 size_t href_start = len; |
|
425 size_t i; |
|
426 for(i=0;i<=len;i++) { |
|
427 char c = nsliststr[i]; |
|
428 if(c == '\n' || c == '\0') { |
|
429 if(i > href_start) { |
|
430 WebdavNSList *elm = pool_malloc(pool, sizeof(WebdavNSList)); |
|
431 if(!elm) { |
|
432 break; |
|
433 } |
|
434 elm->prev = list_current; |
|
435 elm->next = NULL; |
|
436 WSNamespace *ns = pool_malloc(pool, sizeof(WSNamespace)); |
|
437 elm->namespace = ns; |
|
438 if(!ns) { |
|
439 break; |
|
440 } |
|
441 memset(ns, 0, sizeof(WSNamespace)); |
|
442 ns->prefix = prefix_len > 0 ? (xmlChar*)sstrdup_pool(pool, sstrn(prefix, prefix_len)).ptr : NULL; |
|
443 ns->href = (xmlChar*)sstrdup_pool(pool, sstrn(href, i-href_start)).ptr; |
|
444 if(list_current) { |
|
445 list_current->next = elm; |
|
446 } else { |
|
447 list_start = elm; |
|
448 } |
|
449 list_current = elm; |
|
450 } |
|
451 prefix_start = i + 1; |
|
452 prefix = nsliststr + prefix_start; |
|
453 prefix_len = 0; |
|
454 href_start = len; |
|
455 href = NULL; |
|
456 } else if(!href && c == ':') { |
|
457 prefix_len = i - prefix_start; |
|
458 href_start = i + 1; |
|
459 href = nsliststr + href_start; |
|
460 } |
|
461 } |
|
462 |
|
463 if(i < len) { |
|
464 // error, cleanup |
|
465 while(list_start) { |
|
466 if(list_start->namespace) { |
|
467 WSNamespace *ns = list_start->namespace; |
|
468 if(ns->prefix) { |
|
469 pool_free(pool, (char*)ns->prefix); |
|
470 } |
|
471 if(ns->href) { |
|
472 pool_free(pool, (char*)ns->href); |
|
473 } |
|
474 pool_free(pool, ns); |
|
475 } |
|
476 WebdavNSList *next = list_start->next; |
|
477 pool_free(pool, list_start); |
|
478 list_start = next; |
|
479 } |
|
480 list_start = NULL; |
|
481 } |
|
482 |
|
483 return list_start; |
|
484 } |
|
485 |
|
486 /***************************************************************************** |
|
487 * Non public functions |
|
488 *****************************************************************************/ |
|
489 |
|
490 typedef struct XmlWriter { |
|
491 /* |
|
492 * Memory pool for temp memory allocations |
|
493 */ |
|
494 pool_handle_t *pool; |
|
495 |
|
496 /* |
|
497 * Buffered output stream |
|
498 */ |
|
499 Writer *out; |
|
500 |
|
501 /* |
|
502 * Map for all previously defined namespaces |
|
503 * key: (char*) namespace prefix |
|
504 * value: WSNamespace* |
|
505 */ |
|
506 UcxMap *namespaces; |
|
507 |
|
508 /* |
|
509 * Should namespace definitions be created |
|
510 */ |
|
511 WSBool define_namespaces; |
|
512 } XmlWriter; |
|
513 |
|
514 /* |
|
515 * Serialize an XML text node |
|
516 * This replaces some special characters with entity refs |
|
517 * type: 0 = element text, 1 = attribute text |
|
518 */ |
|
519 static void xml_ser_text(Writer *out, int type, const char *text) { |
|
520 size_t start = 0; |
|
521 size_t i; |
|
522 sstr_t entityref = { NULL, 0 }; |
|
523 for(i=0;text[i]!='\0';i++) { |
|
524 char c = text[i]; |
|
525 if(c == '&') { |
|
526 entityref = S("&"); |
|
527 } else if(type == 0) { |
|
528 if(c == '<') { |
|
529 entityref = S("<"); |
|
530 } else if(c == '>') { |
|
531 entityref = S(">"); |
|
532 } |
|
533 } else { |
|
534 if(c == '\"') { |
|
535 entityref = S("""); |
|
536 } else if(c == '\'') { |
|
537 entityref = S("'"); |
|
538 } |
|
539 } |
|
540 |
|
541 if(entityref.ptr) { |
|
542 size_t len = i-start; |
|
543 if(len > 0) { |
|
544 writer_put(out, text+start, len); |
|
545 } |
|
546 writer_puts(out, entityref); |
|
547 entityref.ptr = NULL; |
|
548 entityref.length = 0; |
|
549 start = i+1; |
|
550 } |
|
551 } |
|
552 size_t len = i-start; |
|
553 if(len > 0) { |
|
554 writer_put(out, text+start, len); |
|
555 } |
|
556 } |
|
557 |
|
558 /* |
|
559 * Serialize an XML element node |
|
560 */ |
|
561 static void xml_ser_element(XmlWriter *xw, xmlNode *node) { |
|
562 Writer *out = xw->out; |
|
563 writer_putc(out, '<'); |
|
564 |
|
565 // write prefix and ':' |
|
566 if(node->ns && node->ns->prefix) { |
|
567 writer_puts(out, sstr((char*)node->ns->prefix)); |
|
568 writer_putc(out, ':'); |
|
569 } |
|
570 |
|
571 // node name |
|
572 writer_puts(out, sstr((char*)node->name)); |
|
573 |
|
574 // namespace definitions |
|
575 if(xw->define_namespaces) { |
|
576 xmlNs *nsdef = node->nsDef; |
|
577 while(nsdef) { |
|
578 // we define only namespaces without prefix or namespaces |
|
579 // with prefix, that are not already defined |
|
580 // xw->namespaces contains all namespace, that were defined |
|
581 // before xml serialization |
|
582 if(!nsdef->prefix) { |
|
583 writer_puts(out, S(" xmlns=\"")); |
|
584 writer_puts(out, sstr((char*)nsdef->href)); |
|
585 writer_putc(out, '"'); |
|
586 } else { |
|
587 WSNamespace *n = xw->namespaces ? |
|
588 ucx_map_cstr_get(xw->namespaces, (char*)nsdef->prefix) : |
|
589 NULL; |
|
590 if(!n) { |
|
591 writer_puts(out, S(" xmlns:")); |
|
592 writer_puts(out, sstr((char*)nsdef->prefix)); |
|
593 writer_puts(out, S("=\"")); |
|
594 writer_puts(out, sstr((char*)nsdef->href)); |
|
595 writer_putc(out, '"'); |
|
596 } |
|
597 } |
|
598 |
|
599 nsdef = nsdef->next; |
|
600 } |
|
601 } |
|
602 |
|
603 // attributes |
|
604 xmlAttr *attr = node->properties; |
|
605 while(attr) { |
|
606 // format: ' [<prefix>:]<name>="<value>"' |
|
607 writer_putc(out, ' '); |
|
608 // optional namespace |
|
609 if(attr->ns && attr->ns->prefix) { |
|
610 writer_puts(out, sstr((char*)attr->ns->prefix)); |
|
611 writer_putc(out, ':'); |
|
612 } |
|
613 // <name>=" |
|
614 writer_puts(out, sstr((char*)attr->name)); |
|
615 writer_puts(out, S("=\"")); |
|
616 // value |
|
617 xmlNode *value = attr->children; |
|
618 while(value) { |
|
619 if(value->content) { |
|
620 xml_ser_text(out, 1, (const char*)value->content); |
|
621 } |
|
622 value = value->next; |
|
623 } |
|
624 // trailing quote |
|
625 writer_putc(out, '"'); |
|
626 |
|
627 attr = attr->next; |
|
628 } |
|
629 |
|
630 if(node->children) { |
|
631 writer_putc(out, '>'); |
|
632 } else { |
|
633 writer_puts(out, S("/>")); |
|
634 } |
|
635 } |
|
636 |
|
637 static int xml_ser_node_begin(xmlNode *node, void *userdata) { |
|
638 XmlWriter *xw = userdata; |
|
639 switch(node->type) { |
|
640 case XML_ELEMENT_NODE: xml_ser_element(xw, node); break; |
|
641 case XML_ATTRIBUTE_NODE: break; |
|
642 case XML_TEXT_NODE: { |
|
643 xml_ser_text(xw->out, 0, (const char*)node->content); |
|
644 break; |
|
645 } |
|
646 case XML_CDATA_SECTION_NODE: { |
|
647 break; |
|
648 } |
|
649 case XML_ENTITY_REF_NODE: break; |
|
650 case XML_ENTITY_NODE: break; |
|
651 case XML_PI_NODE: break; |
|
652 case XML_COMMENT_NODE: break; |
|
653 case XML_DOCUMENT_NODE: break; |
|
654 case XML_DOCUMENT_TYPE_NODE: break; |
|
655 case XML_DOCUMENT_FRAG_NODE: break; |
|
656 case XML_NOTATION_NODE: break; |
|
657 case XML_HTML_DOCUMENT_NODE: break; |
|
658 case XML_DTD_NODE: break; |
|
659 case XML_ELEMENT_DECL: break; |
|
660 case XML_ATTRIBUTE_DECL: break; |
|
661 case XML_ENTITY_DECL: break; |
|
662 case XML_NAMESPACE_DECL: break; |
|
663 case XML_XINCLUDE_START: break; |
|
664 case XML_XINCLUDE_END: break; |
|
665 default: break; |
|
666 } |
|
667 return 0; |
|
668 } |
|
669 |
|
670 static int xml_ser_node_end(xmlNode *node, void *userdata) { |
|
671 XmlWriter *xw = userdata; |
|
672 Writer *out = xw->out; |
|
673 if(node->type == XML_ELEMENT_NODE) { |
|
674 if(node->children) { |
|
675 writer_puts(xw->out, S("</")); |
|
676 // write prefix and ':' |
|
677 if(node->ns && node->ns->prefix) { |
|
678 writer_puts(out, sstr((char*)node->ns->prefix)); |
|
679 writer_putc(out, ':'); |
|
680 } |
|
681 // name and close tag |
|
682 writer_puts(out, sstr((char*)node->name)); |
|
683 writer_putc(out, '>'); |
|
684 |
|
685 } // element was already closed in xml_ser_node_begin |
|
686 } |
|
687 return 0; |
|
688 } |
|
689 |
|
690 |
|
691 static int xml_write_nodes( |
|
692 pool_handle_t *pool, |
|
693 Writer *out, |
|
694 UcxMap *nsdefs, |
|
695 WSBool createdefs, |
|
696 xmlNode *node) |
|
697 { |
|
698 XmlWriter xmlwriter; |
|
699 xmlwriter.pool = pool; |
|
700 xmlwriter.out = out; |
|
701 xmlwriter.namespaces = nsdefs; |
|
702 xmlwriter.define_namespaces = createdefs; |
|
703 |
|
704 // iterate over xml nodes |
|
705 // this includes node->children and node->next |
|
706 int err = wsxml_iterator( |
|
707 pool, |
|
708 node, |
|
709 xml_ser_node_begin, |
|
710 xml_ser_node_end, |
|
711 &xmlwriter); |
|
712 if(err) { |
|
713 return -1; |
|
714 } |
|
715 |
|
716 return out->error; |
|
717 } |
|
718 |
|
719 int wsxml_write_nodes( |
|
720 pool_handle_t *pool, |
|
721 Writer *out, |
|
722 UcxMap *nsdefs, |
|
723 xmlNode *node) |
|
724 { |
|
725 return xml_write_nodes(pool, out, nsdefs, TRUE, node); |
|
726 } |
|
727 |
|
728 int wsxml_write_nodes_without_nsdef( |
|
729 pool_handle_t *pool, |
|
730 Writer *out, |
|
731 xmlNode *node) |
|
732 { |
|
733 return xml_write_nodes(pool, out, NULL, FALSE, node); |
|
734 } |