blob: 85513a30f07dd2e1dbb12482b90886c9198bb621 [file] [log] [blame]
Radek Krejci54ea8de2015-04-09 18:02:56 +02001/**
2 * @file xml.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @brief XML parser implementation for libyang
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 */
21
Radek Krejci02117302015-04-13 16:32:44 +020022
Radek Krejci709fee62015-04-15 13:56:19 +020023#include <ctype.h>
24#include <stdint.h>
Radek Krejcif0023a92015-04-20 20:51:39 +020025#include <stdio.h>
Radek Krejci02117302015-04-13 16:32:44 +020026#include <stdlib.h>
27#include <string.h>
Radek Krejci54ea8de2015-04-09 18:02:56 +020028#include <unistd.h>
29
Radek Krejci06a704e2015-04-22 14:50:49 +020030#include "common.h"
31#include "dict.h"
Radek Krejcida04f4a2015-05-21 12:54:09 +020032#include "tree.h"
Radek Krejci54ea8de2015-04-09 18:02:56 +020033#include "xml.h"
34
Radek Krejci02117302015-04-13 16:32:44 +020035/*
36 * Macro to test if character is #x20 | #x9 | #xA | #xD (whitespace)
37 */
38#define is_xmlws(c) (c == 0x20 || c == 0x9 || c == 0xa || c == 0xd)
Radek Krejci54ea8de2015-04-09 18:02:56 +020039
Radek Krejci02117302015-04-13 16:32:44 +020040#define is_xmlnamestartchar(c) ((c >= 'a' && c <= 'z') || c == '_' || \
41 (c >= 'A' && c <= 'Z') || c == ':' || \
42 (c >= 0x370 && c <= 0x1fff && c != 0x37e ) || \
43 (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
44 c == 0x200d || (c >= 0x2070 && c <= 0x218f) || \
45 (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
46 (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
47 (c >= 0x10000 && c <= 0xeffff))
48
49#define is_xmlnamechar(c) ((c >= 'a' && c <= 'z') || c == '_' || c == '-' || \
50 (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == ':' || \
51 c == '.' || c == 0xb7 || (c >= 0x370 && c <= 0x1fff && c != 0x37e ) ||\
52 (c >= 0xc0 && c <= 0x2ff && c != 0xd7 && c != 0xf7) || c == 0x200c || \
53 c == 0x200d || (c >= 0x300 && c <= 0x36f) || \
54 (c >= 0x2070 && c <= 0x218f) || (c >= 0x2030f && c <= 0x2040) || \
55 (c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) || \
56 (c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) || \
57 (c >= 0x10000 && c <= 0xeffff))
58
59#define ign_xmlws(p) while(is_xmlws(*p)) {p++;}
60
Radek Krejcida04f4a2015-05-21 12:54:09 +020061int lyxml_unlink_attr(struct lyxml_attr *attr)
Radek Krejci54ea8de2015-04-09 18:02:56 +020062{
Radek Krejci02117302015-04-13 16:32:44 +020063 struct lyxml_attr *a;
64
65 if (!attr) {
66 LY_ERR(LY_EINVAL, NULL);
67 return EXIT_FAILURE;
68 }
69
70 if (!attr->parent) {
71 return EXIT_SUCCESS;
72 }
73
74 a = attr->parent->attr;
75 if (!a) {
76 LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
77 return EXIT_FAILURE;
78 } else if (a == attr) {
79 attr->parent->attr = attr->next;
80 } else {
81 while(a->next != attr) {
82 a = a->next;
83 }
84
85 if (!a) {
86 LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
87 return EXIT_FAILURE;
88 }
89
90 a->next = attr->next;
91 }
92
93 attr->parent = NULL;
94 attr->next = NULL;
95
96 return EXIT_SUCCESS;
97}
98
Radek Krejcida04f4a2015-05-21 12:54:09 +020099int lyxml_unlink_elem(struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200100{
101 struct lyxml_elem *e;
102
103 if (!elem) {
104 LY_ERR(LY_EINVAL, NULL);
105 return EXIT_FAILURE;
106 }
107
108 if (!elem->parent) {
Radek Krejcida04f4a2015-05-21 12:54:09 +0200109 goto siblings;
Radek Krejci02117302015-04-13 16:32:44 +0200110 }
111
112 e = elem->parent->child;
113 if (!e) {
114 LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
115 return EXIT_FAILURE;
116 } else if (e == elem) {
Radek Krejcida04f4a2015-05-21 12:54:09 +0200117 /* we unlink the first child */
118 /* update the parent's link */
119 elem->parent->child = e->next;
Radek Krejci02117302015-04-13 16:32:44 +0200120 }
121
122 /* remove elem from ring list of sibling elements */
Radek Krejcida04f4a2015-05-21 12:54:09 +0200123 while (e && e != elem) {
Radek Krejci02117302015-04-13 16:32:44 +0200124 e = e->next;
125 }
126 if (!e) {
127 LY_ERR(LY_EINVAL, "Broken structure (%s).", __func__);
128 return EXIT_FAILURE;
129 }
Radek Krejci02117302015-04-13 16:32:44 +0200130
Radek Krejcida04f4a2015-05-21 12:54:09 +0200131siblings:
132 if (elem == elem->prev) {
133 /* there are no more siblings */
134 goto end;
135 }
136
137 if (elem->next) {
138 elem->next->prev = elem->prev;
139 } else {
140 /* unlinking the last child -> update the first's prev pointer */
141 elem->parent->child->prev = elem->prev;
142 }
143 if (elem->prev && elem->prev->next) {
144 elem->prev->next = elem->next;
145 }
146
147end:
Radek Krejci02117302015-04-13 16:32:44 +0200148 /* clean up the unlinked element */
Radek Krejcida04f4a2015-05-21 12:54:09 +0200149 elem->next = NULL;
150 elem->prev = elem;
151 elem->parent = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200152
153 return EXIT_SUCCESS;
154}
155
Radek Krejcida04f4a2015-05-21 12:54:09 +0200156void lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200157{
158 if (!attr) {
159 return;
160 }
161
162 lyxml_unlink_attr(attr);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200163 lydict_remove(ctx, attr->name);
164 lydict_remove(ctx, attr->value);
Radek Krejci02117302015-04-13 16:32:44 +0200165 free(attr);
166}
167
Radek Krejcida04f4a2015-05-21 12:54:09 +0200168void lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200169{
170 struct lyxml_attr *a, *next;
171 if (!elem || !elem->attr) {
172 return;
173 }
174
175 a = elem->attr;
176 do {
177 next = a->next;
178
Radek Krejcida04f4a2015-05-21 12:54:09 +0200179 lydict_remove(ctx, a->name);
180 lydict_remove(ctx, a->value);
Radek Krejci02117302015-04-13 16:32:44 +0200181 free(a);
182
183 a = next;
184 } while (a);
185}
186
Radek Krejcida04f4a2015-05-21 12:54:09 +0200187static void lyxml_free_elem_(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200188{
189 struct lyxml_elem *e, *next;
190
191 if (!elem) {
192 return;
193 }
194
Radek Krejcida04f4a2015-05-21 12:54:09 +0200195 lyxml_free_attrs(ctx, elem);
196 LY_TREE_FOR_SAFE(elem->child, next, e) {
197 lyxml_free_elem_(ctx, e);
Radek Krejci02117302015-04-13 16:32:44 +0200198 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200199 lydict_remove(ctx, elem->name);
200 lydict_remove(ctx, elem->content);
Radek Krejci02117302015-04-13 16:32:44 +0200201 free(elem);
202}
203
Radek Krejcida04f4a2015-05-21 12:54:09 +0200204void lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200205{
206 if (!elem) {
207 return;
208 }
209
210 lyxml_unlink_elem(elem);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200211 lyxml_free_elem_(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200212}
213
Radek Krejcida04f4a2015-05-21 12:54:09 +0200214int lyxml_add_attr(struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200215{
216 struct lyxml_attr *a;
217
218 if (!parent || !attr) {
219 LY_ERR(LY_EINVAL, NULL);
220 return EXIT_FAILURE;
221 }
222
223 /* (re)link attribute to parent */
224 if (attr->parent) {
225 lyxml_unlink_attr(attr);
226 }
227 attr->parent = parent;
228
229 /* link parent to attribute */
230 if (parent->attr) {
231 for (a = parent->attr; a->next; a = a->next);
232 a->next = attr;
233 } else {
234 parent->attr = attr;
235 }
236
237 return EXIT_SUCCESS;
238}
239
Radek Krejcida04f4a2015-05-21 12:54:09 +0200240const char *lyxml_get_attr(struct lyxml_elem *elem, const char *name,
241 const char *ns)
242{
243 struct lyxml_attr *a;
244
245 if (!elem || !name) {
246 LY_ERR(LY_EINVAL, NULL);
247 return NULL;
248 }
249
250 for (a = elem->attr; a; a = a->next) {
251 if (a->type != LYXML_ATTR_STD) {
252 continue;
253 }
254
255 if (!strcmp(name, a->name)) {
256 if ((!ns && !a->ns)
257 || (ns && a->ns && !strcmp(ns, a->ns->value))) {
258 return a->value;
259 }
260 }
261 }
262
263 return NULL;
264}
265
266int lyxml_add_child(struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200267{
268 struct lyxml_elem *e;
269
270 if (!parent || !elem) {
271 LY_ERR(LY_EINVAL, NULL);
272 return EXIT_FAILURE;
273 }
274
275 /* (re)link element to parent */
276 if (elem->parent) {
277 lyxml_unlink_elem(elem);
278 }
279 elem->parent = parent;
280
281 /* link parent to element */
282 if (parent->child) {
283 e = parent->child;
284 elem->prev = e->prev;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200285 elem->next = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200286 elem->prev->next = elem;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200287 e->prev = elem;
Radek Krejci02117302015-04-13 16:32:44 +0200288 } else {
289 parent->child = elem;
Radek Krejci02117302015-04-13 16:32:44 +0200290 elem->prev = elem;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200291 elem->next = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200292 }
293
294 return EXIT_SUCCESS;
295}
296
297/**
298 * @brief Get the first UTF-8 character value (4bytes) from buffer
299 * @param[in] buf pointr to the current position in input buffer
300 * @param[out] read Number of processed bytes in buf (length of UTF-8
301 * character).
302 * @return UTF-8 value as 4 byte number. 0 means error, only UTF-8 characters
303 * valid for XML are returned, so:
304 * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
305 * = any Unicode character, excluding the surrogate blocks, FFFE, and FFFF.
306 *
307 * UTF-8 mapping:
308 * 00000000 -- 0000007F: 0xxxxxxx
309 * 00000080 -- 000007FF: 110xxxxx 10xxxxxx
310 * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
311 * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
312 *
313 */
314static int getutf8(const char *buf, unsigned int *read)
315{
316 int c, aux;
317 int i;
318
319 /* check input variable */
320 if (!buf || !read) {
321 LY_ERR(LY_EINVAL, NULL);
322 return 0;
323 }
324 c = buf[0];
325 *read = 0;
326
327 /* buf is NULL terminated string, so 0 means EOF */
328 if (!c) {
329 LY_ERR(LY_EEOF, NULL);
330 return 0;
331 }
332 *read = 1;
333
334 /* process character byte(s) */
335 if ((c && 0xf8) == 0xf0) {
336 /* four bytes character */
337 *read = 4;
338
339 c &= 0x07;
340 for (i = 1; i <= 3; i++) {
341 aux = buf[i];
342 if ((aux & 0xc0) != 0x80) {
343 LY_ERR(LY_EINVAL, NULL);
344 return 0;
345 }
346
347 c = (c << 6) | (aux & 0x3f);
348 }
349
350
351 if (c < 0x1000 || c > 0x10ffff) {
352 LY_ERR(LY_EINVAL, NULL);
353 return 0;
354 }
355 } else if ((c & 0xf0) == 0xe0) {
356 /* three bytes character */
357 *read = 3;
358
359 c &= 0x0f;
360 for (i = 1; i <= 2; i++) {
361 aux = buf[i];
362 if ((aux & 0xc0) != 0x80) {
363 LY_ERR(LY_EINVAL, NULL);
364 return 0;
365 }
366
367 c = (c << 6) | (aux & 0x3f);
368 }
369
370
371 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd ) {
372 LY_ERR(LY_EINVAL, NULL);
373 return 0;
374 }
375 } else if ((c & 0xe0) == 0xc0) {
376 /* two bytes character */
377 *read = 2;
378
379 aux = buf[1];
380 if ((aux & 0xc0) != 0x80) {
381 LY_ERR(LY_EINVAL, NULL);
382 return 0;
383 }
384 c = ((c & 0x1f) << 6) | (aux & 0x3f);
385
386 if (c < 0x80) {
387 LY_ERR(LY_EINVAL, NULL);
388 return 0;
389 }
390 } else if (!(c & 0x80)) {
391 /* one byte character */
392 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
393 /* invalid character */
394 LY_ERR(LY_EINVAL, NULL);
395 return 0;
396 }
397 } else {
398 /* invalid character */
399 LY_ERR(LY_EINVAL, NULL);
400 return 0;
401 }
402
403 return c;
404}
405
Radek Krejci709fee62015-04-15 13:56:19 +0200406/**
407 * Store UTF-8 character specified as 4byte integer into the dst buffer.
408 * Returns number of written bytes (4 max), expects that dst has enough space.
409 *
410 * UTF-8 mapping:
411 * 00000000 -- 0000007F: 0xxxxxxx
412 * 00000080 -- 000007FF: 110xxxxx 10xxxxxx
413 * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
414 * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
415 *
416 */
417static unsigned int pututf8(char *dst, int32_t value)
418{
419 if (value < 0x80) {
420 /* one byte character */
421 dst[0] = value;
422
423 return 1;
424 } else if (value < 0x800) {
425 /* two bytes character */
426 dst[0] = 0xc0 | (value >> 6);
427 dst[1] = 0x80 | (value & 0x3f);
428
429 return 2;
430 } else if (value < 0x10000) {
431 /* three bytes character */
432 dst[0] = 0xe0 | (value >> 12);
433 dst[1] = 0x80 | ((value >> 6) & 0x3f);
434 dst[2] = 0x80 | (value & 0x3f);
435
436 return 3;
437 } else if (value < 0x200000) {
438 /* four bytes character */
439 dst[0] = 0xf0 | (value >> 18);
440 dst[1] = 0x80 | ((value >> 12) & 0x3f);
441 dst[2] = 0x80 | ((value >> 6) & 0x3f);
442 dst[3] = 0x80 | (value & 0x3f);
443
444 return 4;
445 } else {
446 /* out of range */
447 LY_ERR(LY_EINVAL, NULL);
448 return 0;
449 }
450}
451
Radek Krejci05e37a32015-04-15 14:40:34 +0200452static int parse_ignore(const char *data, const char *endstr,
453 unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200454{
Radek Krejci05e37a32015-04-15 14:40:34 +0200455 unsigned int slen;
Radek Krejci02117302015-04-13 16:32:44 +0200456 const char *c = data;
457
Radek Krejci05e37a32015-04-15 14:40:34 +0200458 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200459
Radek Krejci05e37a32015-04-15 14:40:34 +0200460 while (*c && memcmp(c, endstr, slen)) {
Radek Krejci02117302015-04-13 16:32:44 +0200461 c++;
462 }
463 if (!*c) {
464 LY_ERR(LY_EWELLFORM, "Missing close sequence \"%s\".", endstr);
Radek Krejci05e37a32015-04-15 14:40:34 +0200465 return EXIT_FAILURE;
Radek Krejci02117302015-04-13 16:32:44 +0200466 }
Radek Krejci05e37a32015-04-15 14:40:34 +0200467 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200468
Radek Krejci05e37a32015-04-15 14:40:34 +0200469 *len = c - data;
470 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200471}
472
Radek Krejci521008e2015-04-15 14:41:07 +0200473static char *parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200474{
Radek Krejci709fee62015-04-15 13:56:19 +0200475#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200476
Radek Krejci709fee62015-04-15 13:56:19 +0200477 char buf[BUFSIZE];
478 char *result = NULL, *aux;
479 unsigned int r;
480 int o, size = 0;
Radek Krejcia4a84062015-04-16 13:00:10 +0200481 int cdsect = 0;
Radek Krejci709fee62015-04-15 13:56:19 +0200482 int32_t n;
483
Radek Krejcia4a84062015-04-16 13:00:10 +0200484 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
485 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Radek Krejci02117302015-04-13 16:32:44 +0200486 LY_ERR(LY_EWELLFORM, "Invalid element content, \"]]>\" found.");
Radek Krejci709fee62015-04-15 13:56:19 +0200487 goto error;
Radek Krejci02117302015-04-13 16:32:44 +0200488 }
Radek Krejci709fee62015-04-15 13:56:19 +0200489
Radek Krejcia4a84062015-04-16 13:00:10 +0200490loop:
491
Radek Krejci709fee62015-04-15 13:56:19 +0200492 if (o > BUFSIZE - 3) {
493 /* add buffer into the result */
494 if (result) {
495 size = size + o;
496 aux = realloc(result, size + 1);
497 result = aux;
498 } else {
499 size = o;
500 result = malloc((size + 1) * sizeof *result);
501 }
502 memcpy(&result[size - o], buf, o);
503
504 /* write again into the beginning of the buffer */
505 o = 0;
506 }
507
Radek Krejcia4a84062015-04-16 13:00:10 +0200508 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
509 /* CDSect */
510 if (!cdsect) {
511 cdsect = 1;
512 *len += 9;
513 }
514 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
515 *len += 3;
516 cdsect = 0;
517 o--; /* we don't write any data in this iteration */
518 } else {
519 buf[o] = data[*len];
520 (*len)++;
521 }
522 } else if (data[*len] == '&') {
Radek Krejci709fee62015-04-15 13:56:19 +0200523 (*len)++;
524 if (data[*len] != '#') {
525 /* entity reference - only predefined refs are supported */
526 if (!memcmp(&data[*len], "lt;", 3)) {
527 buf[o] = '<';
528 *len += 3;
529 } else if (!memcmp(&data[*len], "gt;", 3)) {
530 buf[o] = '>';
531 *len += 3;
532 } else if (!memcmp(&data[*len], "amp;", 4)) {
533 buf[o] = '&';
534 *len += 4;
535 } else if (!memcmp(&data[*len], "apos;", 5)) {
536 buf[o] = '\'';
537 *len += 5;
538 } else if (!memcmp(&data[*len], "quot;", 5)) {
539 buf[o] = '\"';
540 *len += 5;
541 } else {
542 LY_ERR(LY_EWELLFORM,
543 "Invalid entity reference, only predefined entity references are supported.");
544 goto error;
545 }
546 } else {
547 /* character reference */
548 (*len)++;
549 if (isdigit(data[*len])) {
550 for (n = 0; isdigit(data[*len]); (*len)++) {
551 n = (10 * n) + (data[*len] - '0');
552 }
553 if (data[*len] != ';') {
554 LY_ERR(LY_EWELLFORM,
555 "Invalid character reference, missing semicolon.");
556 goto error;
557 }
558 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
559 for (n = 0; isxdigit(data[*len]); (*len)++) {
560 if (isdigit(data[*len])) {
561 r = (data[*len] - '0');
562 } else if (data[*len] > 'F') {
563 r = 10 + (data[*len] - 'a');
564 } else {
565 r = 10 + (data[*len] - 'A');
566 }
567 n = (16 * n) + r;
568 }
569 } else {
570 LY_ERR(LY_EWELLFORM, "Invalid character reference.");
571 goto error;
572
573 }
574 r = pututf8(&buf[o], n);
575 if (!r) {
576 LY_ERR(LY_EWELLFORM, "Invalid character reference value.");
577 goto error;
578 }
579 o += r - 1; /* o is ++ in for loop */
580 (*len)++;
581 }
582 } else {
583 buf[o] = data[*len];
584 (*len)++;
585 }
Radek Krejci02117302015-04-13 16:32:44 +0200586 }
587
Radek Krejcia4a84062015-04-16 13:00:10 +0200588 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
589 /* ignore loop's end condition on beginning of CDSect */
590 goto loop;
591 }
592
Radek Krejci709fee62015-04-15 13:56:19 +0200593#undef BUFSIZE
594
595 if (o) {
596 if (result) {
597 size = size + o;
Radek Krejci9c16b332015-04-20 17:37:54 +0200598 aux = realloc(result, size + 1);
Radek Krejci709fee62015-04-15 13:56:19 +0200599 result = aux;
600 } else {
601 size = o;
Radek Krejci9c16b332015-04-20 17:37:54 +0200602 result = malloc((size + 1) * sizeof *result);
Radek Krejci709fee62015-04-15 13:56:19 +0200603 }
604 memcpy(&result[size - o], buf, o);
605 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200606 if (result) {
607 result[size] = '\0';
608 }
Radek Krejci02117302015-04-13 16:32:44 +0200609
Radek Krejci02117302015-04-13 16:32:44 +0200610 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200611
612error:
613 free(result);
614 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200615}
616
Radek Krejci674e1f82015-04-21 14:12:19 +0200617static struct lyxml_ns *get_ns(struct lyxml_elem *elem, const char *prefix)
Radek Krejci02117302015-04-13 16:32:44 +0200618{
Radek Krejci674e1f82015-04-21 14:12:19 +0200619 struct lyxml_attr *attr;
620 int len;
621
622 if (!elem) {
623 return NULL;
624 }
625
626 if (!prefix) {
627 len = 0;
628 } else {
629 len = strlen(prefix);
630 }
631
632 for (attr = elem->attr; attr; attr = attr->next) {
633 if (attr->type != LYXML_ATTR_NS) {
634 continue;
635 }
636 if (!attr->name) {
637 if (!len) {
638 /* default namespace found */
639 if (!attr->value) {
640 /* empty default namespace -> no default namespace */
641 return NULL;
642 }
643 return (struct lyxml_ns *)attr;
644 }
645 } else if (len && !memcmp(attr->name, prefix, len)) {
646 /* prefix found */
647 return (struct lyxml_ns *)attr;
648 }
649 }
650
651 /* go recursively */
652 return get_ns(elem->parent, prefix);
653}
654
Radek Krejcida04f4a2015-05-21 12:54:09 +0200655static struct lyxml_attr *parse_attr(struct ly_ctx *ctx, const char *data,
656 unsigned int *len, struct lyxml_elem *elem)
Radek Krejci674e1f82015-04-21 14:12:19 +0200657{
658 const char *c = data, *start, *delim;
659 char prefix[32];
Radek Krejci02117302015-04-13 16:32:44 +0200660 int uc;
661 struct lyxml_attr *attr = NULL;
662 unsigned int size;
663
Radek Krejci4ea08382015-04-21 09:41:40 +0200664
Radek Krejci674e1f82015-04-21 14:12:19 +0200665 /* check if it is attribute or namespace */
666 if (!memcmp(c, "xmlns", 5)) {
667 /* namespace */
668 attr = calloc(1, sizeof(struct lyxml_ns));
669 attr->type = LYXML_ATTR_NS;
670 c += 5;
671 if (*c != ':') {
672 /* default namespace, prefix will be empty */
673 goto equal;
674 }
675 c++; /* go after ':' to the prefix value */
676 } else {
677 /* attribute */
678 attr = calloc(1, sizeof(struct lyxml_attr));
679 attr->type = LYXML_ATTR_STD;
680 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200681
Radek Krejci02117302015-04-13 16:32:44 +0200682 /* process name part of the attribute */
Radek Krejci674e1f82015-04-21 14:12:19 +0200683 start = c;
Radek Krejci02117302015-04-13 16:32:44 +0200684 uc = getutf8(c, &size);
685 if (!is_xmlnamestartchar(uc)) {
686 LY_ERR(LY_EWELLFORM, "Invalid NameStartChar of the attribute");
Radek Krejci674e1f82015-04-21 14:12:19 +0200687 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200688 return NULL;
689 }
690 c += size;
691 uc = getutf8(c, &size);
692 while (is_xmlnamechar(uc)) {
Radek Krejci674e1f82015-04-21 14:12:19 +0200693 if (attr->type == LYXML_ATTR_STD && *c == ':') {
694 /* attribute in a namespace */
695 start = c + 1;
696
697 /* look for the prefix in namespaces */
698 memcpy(prefix, data, c - data);
699 prefix[c - data] = '\0';
700 attr->ns = get_ns(elem, prefix);
701 }
Radek Krejci02117302015-04-13 16:32:44 +0200702 c += size;
703 uc = getutf8(c, &size);
704 }
705
Radek Krejci02117302015-04-13 16:32:44 +0200706 /* store the name */
Radek Krejci674e1f82015-04-21 14:12:19 +0200707 size = c - start;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200708 attr->name = lydict_insert(ctx, start, size);
Radek Krejci02117302015-04-13 16:32:44 +0200709
Radek Krejci674e1f82015-04-21 14:12:19 +0200710
711equal:
Radek Krejci02117302015-04-13 16:32:44 +0200712 /* check Eq mark that can be surrounded by whitespaces */
713 ign_xmlws(c);
714 if (*c != '=') {
715 LY_ERR(LY_EWELLFORM, "Invalid attribute definition, \"=\" expected.");
716 goto error;
717 }
718 c++;
719 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200720
721 /* process value part of the attribute */
722 if (!*c || (*c != '"' && *c != '\'')) {
723 LY_ERR(LY_EWELLFORM, "Invalid attribute value, \" or \' expected.");
724 goto error;
725 }
726 delim = c;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200727 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
Radek Krejci521008e2015-04-15 14:41:07 +0200728 if (ly_errno) {
Radek Krejci02117302015-04-13 16:32:44 +0200729 goto error;
730 }
731
Radek Krejci521008e2015-04-15 14:41:07 +0200732 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci02117302015-04-13 16:32:44 +0200733 return attr;
734
735error:
Radek Krejcida04f4a2015-05-21 12:54:09 +0200736 lyxml_free_attr(ctx, attr);
Radek Krejci54ea8de2015-04-09 18:02:56 +0200737 return NULL;
738}
739
Radek Krejcida04f4a2015-05-21 12:54:09 +0200740static struct lyxml_elem *parse_elem(struct ly_ctx *ctx, const char *data,
741 unsigned int *len,
742 struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200743{
Radek Krejci674e1f82015-04-21 14:12:19 +0200744 const char *c = data, *start, *e;
Radek Krejci02117302015-04-13 16:32:44 +0200745 const char *lws; /* leading white space for handling mixed content */
746 int uc;
747 char *str;
Radek Krejci674e1f82015-04-21 14:12:19 +0200748 char prefix[32] = {0};
749 unsigned int prefix_len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200750 struct lyxml_elem *elem, *child;
751 struct lyxml_attr *attr;
Radek Krejci05e37a32015-04-15 14:40:34 +0200752 unsigned int size;
Radek Krejci674e1f82015-04-21 14:12:19 +0200753 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200754
755 *len = 0;
756
757 if (*c != '<') {
758 return NULL;
759 }
760
761 /* locate element name */
762 c++;
763 e = c;
764
Radek Krejci05e37a32015-04-15 14:40:34 +0200765 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200766 if (!is_xmlnamestartchar(uc)) {
767 LY_ERR(LY_EWELLFORM, "Invalid NameStartChar of the attribute");
768 return NULL;
769 }
Radek Krejci05e37a32015-04-15 14:40:34 +0200770 e += size;
771 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200772 while (is_xmlnamechar(uc)) {
Radek Krejci674e1f82015-04-21 14:12:19 +0200773 if (*e == ':') {
774 if (prefix_len) {
775 LY_ERR(LY_EWELLFORM, "Multiple colons in element name.");
776 goto error;
777 }
778 /* element in a namespace */
779 start = e + 1;
780
781 /* look for the prefix in namespaces */
782 memcpy(prefix, c, prefix_len = e - c);
783 prefix[prefix_len] = '\0';
784 c = start;
785 }
Radek Krejci05e37a32015-04-15 14:40:34 +0200786 e += size;
787 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200788 }
789 if (!*e) {
790 LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
791 return NULL;
792 }
793
794 /* allocate element structure */
795 elem = calloc(1, sizeof *elem);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200796 elem->next = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200797 elem->prev = elem;
Radek Krejci674e1f82015-04-21 14:12:19 +0200798 if (parent) {
799 lyxml_add_child(parent, elem);
800 }
Radek Krejci02117302015-04-13 16:32:44 +0200801
802 /* store the name into the element structure */
Radek Krejcida04f4a2015-05-21 12:54:09 +0200803 elem->name = lydict_insert(ctx, c, e - c);
Radek Krejci02117302015-04-13 16:32:44 +0200804 c = e;
805
806process:
Radek Krejci709fee62015-04-15 13:56:19 +0200807 ly_errno = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200808 ign_xmlws(c);
809 if (!memcmp("/>", c, 2)) {
810 /* we are done, it was EmptyElemTag */
811 c += 2;
Radek Krejci674e1f82015-04-21 14:12:19 +0200812 closed_flag = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200813 } else if (*c == '>') {
814 /* process element content */
815 c++;
816 lws = NULL;
817
818 while (*c) {
819 if (!memcmp(c, "</", 2)) {
Radek Krejci674e1f82015-04-21 14:12:19 +0200820 if (lws && !elem->child) {
Radek Krejci02117302015-04-13 16:32:44 +0200821 /* leading white spaces were actually content */
822 goto store_content;
823 }
824
825 /* Etag */
826 c += 2;
827 /* get name and check it */
828 e = c;
Radek Krejci05e37a32015-04-15 14:40:34 +0200829 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200830 if (!is_xmlnamestartchar(uc)) {
831 LY_ERR(LY_EWELLFORM,
832 "Invalid NameStartChar of the attribute");
833 goto error;
834 }
Radek Krejci05e37a32015-04-15 14:40:34 +0200835 e += size;
836 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200837 while (is_xmlnamechar(uc)) {
Radek Krejci674e1f82015-04-21 14:12:19 +0200838 if (*e == ':') {
839 /* element in a namespace */
840 start = e + 1;
841
842 /* look for the prefix in namespaces */
843 if (memcmp(prefix, c, e - c)) {
844 LY_ERR(LY_EWELLFORM,
845 "Mixed opening (%s) and closing element tag - different namespaces",
846 elem->name);
847 }
848 c = start;
849 }
Radek Krejci05e37a32015-04-15 14:40:34 +0200850 e += size;
851 uc = getutf8(e, &size);
Radek Krejci02117302015-04-13 16:32:44 +0200852 }
853 if (!*e) {
854 LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
855 goto error;
856 }
857
858 /* check that it corresponds to opening tag */
Radek Krejci05e37a32015-04-15 14:40:34 +0200859 size = e - c;
860 str = malloc((size + 1) * sizeof *str);
Radek Krejci02117302015-04-13 16:32:44 +0200861 memcpy(str, c, e - c);
862 str[e - c] = '\0';
Radek Krejci05e37a32015-04-15 14:40:34 +0200863 if (size != strlen(elem->name) ||
864 memcmp(str, elem->name, size)) {
Radek Krejci02117302015-04-13 16:32:44 +0200865 LY_ERR(LY_EWELLFORM,
866 "Mixed opening (%s) and closing (%s) element tag",
Radek Krejci674e1f82015-04-21 14:12:19 +0200867 elem->name, str);
Radek Krejci6f0c6f92015-05-25 15:01:15 +0200868 free(str);
Radek Krejci02117302015-04-13 16:32:44 +0200869 goto error;
870 }
871 free(str);
872 c = e;
873
874 ign_xmlws(c);
875 if (*c != '>') {
876 LY_ERR(LY_EWELLFORM,
877 "Close element tag \"%s\" contain additional data.",
878 elem->name);
879 goto error;
880 }
881 c++;
Radek Krejci674e1f82015-04-21 14:12:19 +0200882 closed_flag = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200883 break;
884
885 } else if (!memcmp(c, "<?", 2)) {
886 if (lws) {
887 /* leading white spaces were only formatting */
888 lws = NULL;
889 }
890 /* PI - ignore it */
891 c += 2;
Radek Krejci05e37a32015-04-15 14:40:34 +0200892 if (parse_ignore(c, "?>", &size)) {
Radek Krejci02117302015-04-13 16:32:44 +0200893 goto error;
894 }
895 c += size;
896 } else if (!memcmp(c, "<!--", 4)) {
897 if (lws) {
898 /* leading white spaces were only formatting */
899 lws = NULL;
900 }
901 /* Comment - ignore it */
902 c += 4;
Radek Krejci05e37a32015-04-15 14:40:34 +0200903 if (parse_ignore(c, "-->", &size)) {
Radek Krejci02117302015-04-13 16:32:44 +0200904 goto error;
905 }
906 c += size;
907 } else if (!memcmp(c, "<![CDATA[", 9)) {
908 /* CDSect */
Radek Krejcia4a84062015-04-16 13:00:10 +0200909 goto store_content;
Radek Krejci02117302015-04-13 16:32:44 +0200910 } else if (*c == '<') {
911 if (lws) {
Radek Krejcif0023a92015-04-20 20:51:39 +0200912 if (elem->flags & LYXML_ELEM_MIXED) {
913 /* we have a mixed content */
914 goto store_content;
915 } else {
916 /* leading white spaces were only formatting */
917 lws = NULL;
918 }
Radek Krejci02117302015-04-13 16:32:44 +0200919 }
920 if (elem->content) {
921 /* we have a mixed content */
922 child = calloc(1, sizeof *child);
923 child->content = elem->content;
924 elem->content = NULL;
925 lyxml_add_child(elem, child);
Radek Krejcif0023a92015-04-20 20:51:39 +0200926 elem->flags |= LYXML_ELEM_MIXED;
Radek Krejci02117302015-04-13 16:32:44 +0200927 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200928 child = parse_elem(ctx, c, &size, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200929 if (!child) {
930 LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
931 goto error;
932 }
Radek Krejci02117302015-04-13 16:32:44 +0200933 c += size; /* move after processed child element */
934 } else if (is_xmlws(*c)) {
935 lws = c;
936 ign_xmlws(c);
937 } else {
938store_content:
939 /* store text content */
940 if (lws) {
941 /* process content including the leading white spaces */
942 c = lws;
943 lws = NULL;
944 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200945 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
Radek Krejci521008e2015-04-15 14:41:07 +0200946 if (ly_errno) {
Radek Krejci709fee62015-04-15 13:56:19 +0200947 goto error;
948 }
Radek Krejci02117302015-04-13 16:32:44 +0200949 c += size; /* move after processed text content */
950
951 if (elem->child) {
952 /* we have a mixed content */
953 child = calloc(1, sizeof *child);
954 child->content = elem->content;
955 elem->content = NULL;
956 lyxml_add_child(elem, child);
Radek Krejcif0023a92015-04-20 20:51:39 +0200957 elem->flags |= LYXML_ELEM_MIXED;
Radek Krejci02117302015-04-13 16:32:44 +0200958 }
959 }
960 }
961 } else {
962 /* process attribute */
Radek Krejcida04f4a2015-05-21 12:54:09 +0200963 attr = parse_attr(ctx, c, &size, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200964 if (!attr) {
965 LY_ERR(LY_EWELLFORM, "Unexpected end of input data.");
966 goto error;
967 }
968 lyxml_add_attr(elem, attr);
969 c += size; /* move after processed attribute */
970
Radek Krejci674e1f82015-04-21 14:12:19 +0200971 /* check namespace */
972 if (attr->type == LYXML_ATTR_NS) {
973 if (!prefix[0] && !attr->name) {
974 if (attr->value) {
975 /* default prefix */
976 elem->ns = (struct lyxml_ns *)attr;
977 } else {
978 /* xmlns="" -> no namespace */
979 nons_flag = 1;
980 }
981 } else if (prefix[0] && attr->name &&
982 !memcmp(attr->name, prefix, prefix_len + 1)) {
983 /* matching namespace with prefix */
984 elem->ns = (struct lyxml_ns *)attr;
985 }
986 }
987
Radek Krejci02117302015-04-13 16:32:44 +0200988 /* go back to finish element processing */
989 goto process;
990 }
991
992 *len = c - data;
993
Radek Krejci674e1f82015-04-21 14:12:19 +0200994 if (!closed_flag) {
995 LY_ERR(LY_EWELLFORM, "Missing closing element tag (%s).", elem->name);
996 goto error;
997 }
998
999 if (!nons_flag && parent) {
1000 elem->ns = get_ns(parent, prefix_len ? prefix : NULL);
1001 }
1002
Radek Krejci02117302015-04-13 16:32:44 +02001003 return elem;
1004
1005error:
Radek Krejcida04f4a2015-05-21 12:54:09 +02001006 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001007
Radek Krejci54ea8de2015-04-09 18:02:56 +02001008 return NULL;
1009}
1010
Radek Krejcida04f4a2015-05-21 12:54:09 +02001011struct lyxml_elem *lyxml_read(struct ly_ctx *ctx, const char *data,
1012 int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001013{
Radek Krejci02117302015-04-13 16:32:44 +02001014 const char *c = data;
Radek Krejci05e37a32015-04-15 14:40:34 +02001015 unsigned int len;
Radek Krejci02117302015-04-13 16:32:44 +02001016 struct lyxml_elem *root = NULL;
1017
Radek Krejcida04f4a2015-05-21 12:54:09 +02001018 if (!data || !ctx) {
Radek Krejci02117302015-04-13 16:32:44 +02001019 ly_errno = LY_EINVAL;
1020 return NULL;
1021 }
1022
1023 /* process document */
1024 while (*c) {
1025 if (is_xmlws(*c)) {
1026 /* skip whitespaces */
1027 c++;
1028 } else if (!memcmp(c, "<?", 2)) {
1029 /* XMLDecl or PI - ignore it */
1030 c += 2;
Radek Krejci05e37a32015-04-15 14:40:34 +02001031 if (parse_ignore(c, "?>", &len)) {
Radek Krejci02117302015-04-13 16:32:44 +02001032 LY_ERR(LY_EWELLFORM, "Missing close sequence \"?>\".");
1033 return NULL;
1034 }
1035 c += len;
1036 } else if (!memcmp(c, "<!--", 4)) {
1037 /* Comment - ignore it */
1038 c += 2;
Radek Krejci05e37a32015-04-15 14:40:34 +02001039 if (parse_ignore(c, "-->", &len)) {
Radek Krejci02117302015-04-13 16:32:44 +02001040 LY_ERR(LY_EWELLFORM, "Missing close sequence \"-->\".");
1041 return NULL;
1042 }
1043 c += len;
1044 } else if (!memcmp(c, "<!", 2)) {
1045 /* DOCTYPE */
1046 /* TODO - standalone ignore counting < and > */
1047 LY_ERR(LY_EINVAL, "DOCTYPE not implemented.");
1048 return NULL;
1049 } else if (*c == '<') {
1050 /* element - process it in next loop to strictly follow XML
1051 * format
1052 */
1053 break;
1054 }
1055 }
1056
Radek Krejcida04f4a2015-05-21 12:54:09 +02001057 root = parse_elem(ctx, c, &len, NULL);
Radek Krejci02117302015-04-13 16:32:44 +02001058 if (!root) {
1059 return NULL;
1060 }
1061 c += len;
1062
1063 /* ignore the rest of document where can be comments, PIs and whitespaces,
1064 * note that we are not detecting syntax errors in these parts
1065 */
1066 ign_xmlws(c);
1067 if (*c) {
1068 LY_WRN("There are some not parsed data:\n%s", c);
1069 }
1070
1071 return root;
1072}
1073
Radek Krejcida04f4a2015-05-21 12:54:09 +02001074struct lyxml_elem *lyxml_read_fd(struct ly_ctx *ctx, int fd,
1075 int UNUSED(options))
Radek Krejci02117302015-04-13 16:32:44 +02001076{
Radek Krejcida04f4a2015-05-21 12:54:09 +02001077 if (fd == -1 || !ctx) {
Radek Krejci02117302015-04-13 16:32:44 +02001078 ly_errno = LY_EINVAL;
1079 return NULL;
1080 }
1081
Radek Krejci54ea8de2015-04-09 18:02:56 +02001082 return NULL;
1083}
1084
Radek Krejcida04f4a2015-05-21 12:54:09 +02001085struct lyxml_elem *lyxml_read_file(struct ly_ctx *ctx, const char *filename,
1086 int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001087{
Radek Krejcida04f4a2015-05-21 12:54:09 +02001088 if (!filename || !ctx) {
Radek Krejci02117302015-04-13 16:32:44 +02001089 LY_ERR(LY_EINVAL, NULL);
1090 return NULL;
1091 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001092
Radek Krejci02117302015-04-13 16:32:44 +02001093 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001094}
Radek Krejci02117302015-04-13 16:32:44 +02001095
Radek Krejcif0023a92015-04-20 20:51:39 +02001096static int dump_text(FILE *f, char* text)
1097{
1098 unsigned int i, n;
1099
1100 for (i = n = 0; text[i]; i++) {
1101 switch (text[i]) {
1102 case '&':
1103 n += fprintf(f, "&amp;");
1104 break;
1105 case '<':
1106 n += fprintf(f, "&lt;");
1107 break;
Radek Krejci674e1f82015-04-21 14:12:19 +02001108 case '>':
1109 /* not needed, just for readability */
1110 n += fprintf(f, "&gt;");
1111 break;
Radek Krejcif0023a92015-04-20 20:51:39 +02001112 default:
1113 fputc(text[i], f);
1114 n++;
1115 }
1116 }
1117
1118 return n;
1119}
1120
1121static int dump_elem(FILE *f, struct lyxml_elem *e, int level)
1122{
1123 int size = 0;
1124 struct lyxml_attr *a;
1125 struct lyxml_elem *child;
Radek Krejci674e1f82015-04-21 14:12:19 +02001126 const char *delim, *delim_outer;
1127 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001128
1129 if (!e->name) {
1130 /* mixed content */
1131 if (e->content) {
1132 return dump_text(f, e->content);
1133 } else {
1134 return 0;
1135 }
1136 }
1137
Radek Krejci674e1f82015-04-21 14:12:19 +02001138 delim = delim_outer = "\n";
1139 indent = 2 * level;
1140 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1141 delim = "";
1142 }
1143 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1144 delim_outer = "";
1145 indent = 0;
Radek Krejcif0023a92015-04-20 20:51:39 +02001146 }
1147
Radek Krejci674e1f82015-04-21 14:12:19 +02001148 /* opening tag */
1149 if (e->ns && e->ns->prefix) {
1150 size += fprintf(f, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
1151 } else {
1152 size += fprintf(f, "%*s<%s", indent, "", e->name);
Radek Krejcif0023a92015-04-20 20:51:39 +02001153 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001154
1155 /* attributes */
1156 for (a = e->attr; a; a = a->next) {
1157 if (a->type == LYXML_ATTR_NS) {
1158 if (a->name) {
1159 size += fprintf(f, " xmlns:%s=\"%s\"", a->name,
1160 a->value ? a->value : "");
1161 } else {
1162 size += fprintf(f, " xmlns=\"%s\"", a->value ? a->value : "");
1163 }
1164 } else if (a->ns && a->ns->prefix) {
1165 size += fprintf(f, " %s:%s=\"%s\"", a->ns->prefix, a->name,
1166 a->value);
1167 } else {
1168 size += fprintf(f, " %s=\"%s\"", a->name, a->value);
1169 }
1170 }
1171
Radek Krejcif0023a92015-04-20 20:51:39 +02001172 if (!e->child && !e->content) {
Radek Krejci674e1f82015-04-21 14:12:19 +02001173 size += fprintf(f, "/>%s", delim);
Radek Krejcif0023a92015-04-20 20:51:39 +02001174 return size;
1175 } else if (e->content) {
1176 fputc('>', f);
1177 size++;
1178
1179 size += dump_text(f, e->content);
1180
Radek Krejci674e1f82015-04-21 14:12:19 +02001181
1182 if (e->ns && e->ns->prefix) {
1183 size += fprintf(f, "</%s:%s>%s", e->ns->prefix, e->name, delim);
1184 } else {
1185 size += fprintf(f, "</%s>%s", e->name, delim);
1186 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001187 return size;
1188 } else {
Radek Krejci674e1f82015-04-21 14:12:19 +02001189 size += fprintf(f, ">%s", delim);
Radek Krejcif0023a92015-04-20 20:51:39 +02001190 }
1191
1192 /* go recursively */
Radek Krejcida04f4a2015-05-21 12:54:09 +02001193 LY_TREE_FOR(e->child, child) {
Radek Krejcif0023a92015-04-20 20:51:39 +02001194 size += dump_elem(f, child, level + 1);
Radek Krejcida04f4a2015-05-21 12:54:09 +02001195 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001196
Radek Krejci674e1f82015-04-21 14:12:19 +02001197 /* closing tag */
1198 if (e->ns && e->ns->prefix) {
1199 size += fprintf(f, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name,
1200 delim_outer);
Radek Krejcif0023a92015-04-20 20:51:39 +02001201 } else {
Radek Krejci674e1f82015-04-21 14:12:19 +02001202 size += fprintf(f, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejcif0023a92015-04-20 20:51:39 +02001203 }
1204
1205 return size;
1206}
1207
Radek Krejcida04f4a2015-05-21 12:54:09 +02001208int lyxml_dump(FILE *stream, struct lyxml_elem *elem, int UNUSED(options))
Radek Krejcif0023a92015-04-20 20:51:39 +02001209{
1210 if (!elem) {
1211 return 0;
1212 }
1213
1214 return dump_elem(stream, elem, 0);
1215}