blob: c667be25617e3c06cf480f6d78da42f77b8a7e3e [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 Krejci812b10a2015-05-28 16:48:25 +020022#include <assert.h>
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 Krejci5248f132015-10-09 10:34:25 +020032#include "printer.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020033#include "tree_schema.h"
Radek Krejcic6704c82015-10-06 11:12:45 +020034#include "xml_private.h"
Radek Krejci54ea8de2015-04-09 18:02:56 +020035
Radek Krejci3045cf32015-05-28 10:58:52 +020036#ifndef NDEBUG
37unsigned int lineno, lws_lineno;
38#define COUNTLINE(c) if ((c) == 0xa) {lineno++;}
39#else
Radek Krejci8dfe2892015-05-28 15:04:25 +020040#define lineno 0
Radek Krejci3045cf32015-05-28 10:58:52 +020041#define COUNTLINE(C)
42#endif
43
Radek Krejci3045cf32015-05-28 10:58:52 +020044#define ign_xmlws(p) \
45 while (is_xmlws(*p)) { \
46 COUNTLINE(*p); \
47 p++; \
48 }
Radek Krejci02117302015-04-13 16:32:44 +020049
Michal Vaskof8879c22015-08-21 09:07:36 +020050struct lyxml_ns *
51lyxml_get_ns(struct lyxml_elem *elem, const char *prefix)
52{
53 struct lyxml_attr *attr;
54 int len;
55
56 if (!elem) {
57 return NULL;
58 }
59
60 if (!prefix) {
61 len = 0;
62 } else {
63 len = strlen(prefix) + 1;
64 }
65
66 for (attr = elem->attr; attr; attr = attr->next) {
67 if (attr->type != LYXML_ATTR_NS) {
68 continue;
69 }
70 if (!attr->name) {
71 if (!len) {
72 /* default namespace found */
73 if (!attr->value) {
74 /* empty default namespace -> no default namespace */
75 return NULL;
76 }
77 return (struct lyxml_ns *)attr;
78 }
79 } else if (len && !memcmp(attr->name, prefix, len)) {
80 /* prefix found */
81 return (struct lyxml_ns *)attr;
82 }
83 }
84
85 /* go recursively */
86 return lyxml_get_ns(elem->parent, prefix);
87}
88
89struct lyxml_attr *
90lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
91{
92 struct lyxml_attr *result, *a;
93
94 if (!attr || !parent) {
95 return NULL;
96 }
97
98 if (attr->type == LYXML_ATTR_NS) {
99 /* this is correct, despite that all attributes seems like a standard
100 * attributes (struct lyxml_attr), some of them can be namespace
101 * definitions (and in that case they are struct lyxml_ns).
102 */
103 result = (struct lyxml_attr *)calloc(1, sizeof (struct lyxml_ns));
104 } else {
105 result = calloc(1, sizeof (struct lyxml_attr));
106 }
107 result->value = lydict_insert(ctx, attr->value, 0);
108 result->name = lydict_insert(ctx, attr->name, 0);
109 result->type = attr->type;
110
111 /* set namespace in case of standard attributes */
112 if (result->type == LYXML_ATTR_STD && attr->ns) {
113 result->ns = lyxml_get_ns(parent, attr->ns->prefix);
114 }
115
116 /* set parent pointer in case of namespace attribute */
117 if (result->type == LYXML_ATTR_NS) {
118 ((struct lyxml_ns *)result)->parent = parent;
119 }
120
121 /* put attribute into the parent's attributes list */
122 if (parent->attr) {
123 /* go to the end of the list */
124 for (a = parent->attr; a->next; a = a->next);
125 /* and append new attribute */
126 a->next = result;
127 } else {
128 /* add the first attribute in the list */
129 parent->attr = result;
130 }
131
132 return result;
133}
134
135struct lyxml_elem *
136lyxml_dup_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, struct lyxml_elem *parent, int recursive)
137{
138 struct lyxml_elem *result, *child;
139 struct lyxml_attr *attr;
140
141 if (!elem) {
142 return NULL;
143 }
144
145 result = calloc(1, sizeof *result);
146 result->content = lydict_insert(ctx, elem->content, 0);
147 result->name = lydict_insert(ctx, elem->name, 0);
148 result->flags = elem->flags;
149#ifndef NDEBUG
150 result->line = elem->line;
151#endif
152 result->prev = result;
153
154 if (parent) {
155 lyxml_add_child(ctx, parent, result);
156 }
157
158 /* namespace */
159 if (elem->ns) {
160 result->ns = lyxml_get_ns(result, elem->ns->prefix);
161 }
162
163 /* duplicate attributes */
164 for (attr = elem->attr; attr; attr = attr->next) {
165 lyxml_dup_attr(ctx, result, attr);
166 }
167
168 if (!recursive) {
169 return result;
170 }
171
172 /* duplicate children */
173 LY_TREE_FOR(elem->child, child) {
174 lyxml_dup_elem(ctx, child, result, 1);
175 }
176
177 return result;
178}
179
180static struct lyxml_ns *
181lyxml_find_ns(struct lyxml_elem *elem, const char *prefix, const char *value)
182{
183 int pref_match, val_match;
184 struct lyxml_attr *attr;
185
186 if (!elem) {
187 return NULL;
188 }
189
190 for (attr = elem->attr; attr; attr = attr->next) {
191 if (attr->type != LYXML_ATTR_NS) {
192 continue;
193 }
194
195 pref_match = 0;
196 if (!prefix && !attr->name) {
197 pref_match = 1;
198 }
199 if (prefix && attr->name && !strcmp(attr->name, prefix)) {
200 pref_match = 1;
201 }
202
203 val_match = 0;
204 if (!value && !attr->value) {
205 val_match = 1;
206 }
207 if (value && attr->value && !strcmp(attr->value, value)) {
208 val_match = 1;
209 }
210
211 if (pref_match && val_match) {
212 return (struct lyxml_ns *)attr;
213 }
214 }
215
216 /* go recursively */
217 return lyxml_find_ns(elem->parent, prefix, value);
218}
219
220static void
221lyxml_correct_ns(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
222{
223 const struct lyxml_ns *elem_ns;
224 struct lyxml_elem *elem_root, *ns_root, *tmp;
225
226 LY_TREE_DFS_BEGIN(elem, tmp, elem) {
227 if (elem->ns) {
228 /* find the root of elem */
229 for (elem_root = elem; elem_root->parent; elem_root = elem_root->parent);
230
231 /* find the root of elem NS */
232 for (ns_root = elem->ns->parent; ns_root->parent; ns_root = ns_root->parent);
233
234 /* elem NS is defined outside elem subtree */
235 if (ns_root != elem_root) {
236 if (copy_ns) {
237 elem_ns = elem->ns;
238 /* we may have already copied the NS over? */
239 elem->ns = lyxml_find_ns(elem, elem_ns->prefix, elem_ns->value);
240
241 /* we haven't copied it over, copy it now */
242 if (!elem->ns) {
243 elem->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, elem, (struct lyxml_attr *)elem_ns);
244 }
245 } else {
246 elem->ns = NULL;
247 }
248 }
249 }
250 LY_TREE_DFS_END(elem, tmp, elem)
251 }
252}
253
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200254void
Michal Vaskof8879c22015-08-21 09:07:36 +0200255lyxml_unlink_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
Radek Krejci02117302015-04-13 16:32:44 +0200256{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200257 struct lyxml_elem *parent, *first;
Radek Krejci02117302015-04-13 16:32:44 +0200258
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200259 if (!elem) {
260 return;
261 }
Radek Krejci02117302015-04-13 16:32:44 +0200262
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200263 /* store pointers to important nodes */
264 parent = elem->parent;
Radek Krejcie1f13912015-05-26 15:17:38 +0200265
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200266 /* unlink from parent */
267 if (parent) {
268 if (parent->child == elem) {
269 /* we unlink the first child */
270 /* update the parent's link */
271 parent->child = elem->next;
272 }
273 /* forget about the parent */
274 elem->parent = NULL;
275 }
Radek Krejci02117302015-04-13 16:32:44 +0200276
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200277 /* unlink from siblings */
278 if (elem->prev == elem) {
279 /* there are no more siblings */
280 return;
281 }
282 if (elem->next) {
283 elem->next->prev = elem->prev;
284 } else {
285 /* unlinking the last element */
286 if (parent) {
287 first = parent->child;
288 } else {
289 first = elem;
290 while (elem->prev->next) {
291 first = elem->prev;
292 }
293 }
294 first->prev = elem->prev;
295 }
296 if (elem->prev->next) {
297 elem->prev->next = elem->next;
298 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200299
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200300 /* clean up the unlinked element */
301 elem->next = NULL;
302 elem->prev = elem;
Michal Vaskof8879c22015-08-21 09:07:36 +0200303
304 lyxml_correct_ns(ctx, elem, copy_ns);
Radek Krejci02117302015-04-13 16:32:44 +0200305}
306
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200307void
Radek Krejci00249f22015-07-07 13:43:28 +0200308lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200309{
Radek Krejci00249f22015-07-07 13:43:28 +0200310 struct lyxml_attr *aiter, *aprev;
311
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200312 if (!attr) {
313 return;
314 }
Radek Krejci02117302015-04-13 16:32:44 +0200315
Radek Krejci00249f22015-07-07 13:43:28 +0200316 if (parent) {
317 /* unlink attribute from the parent's list of attributes */
318 aprev = NULL;
319 for (aiter = parent->attr; aiter; aiter = aiter->next) {
320 if (aiter == attr) {
321 break;
322 }
323 aprev = aiter;
324 }
325 if (!aiter) {
326 /* attribute to remove not found */
327 return;
328 }
329
330 if (!aprev) {
331 /* attribute is first in parent's list of attributes */
332 parent->attr = attr->next;
333 } else {
334 /* reconnect previous attribute to the next */
335 aprev->next = attr->next;
336 }
337 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200338 lydict_remove(ctx, attr->name);
339 lydict_remove(ctx, attr->value);
340 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200341}
342
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200343void
344lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200345{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200346 struct lyxml_attr *a, *next;
347 if (!elem || !elem->attr) {
348 return;
349 }
Radek Krejci02117302015-04-13 16:32:44 +0200350
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200351 a = elem->attr;
352 do {
353 next = a->next;
Radek Krejci02117302015-04-13 16:32:44 +0200354
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200355 lydict_remove(ctx, a->name);
356 lydict_remove(ctx, a->value);
357 free(a);
Radek Krejci02117302015-04-13 16:32:44 +0200358
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200359 a = next;
360 } while (a);
Radek Krejci02117302015-04-13 16:32:44 +0200361}
362
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200363static void
364lyxml_free_elem_(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200365{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200366 struct lyxml_elem *e, *next;
Radek Krejci02117302015-04-13 16:32:44 +0200367
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200368 if (!elem) {
369 return;
370 }
Radek Krejci02117302015-04-13 16:32:44 +0200371
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200372 lyxml_free_attrs(ctx, elem);
373 LY_TREE_FOR_SAFE(elem->child, next, e) {
374 lyxml_free_elem_(ctx, e);
375 }
376 lydict_remove(ctx, elem->name);
377 lydict_remove(ctx, elem->content);
378 free(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200379}
380
Radek Krejcic6704c82015-10-06 11:12:45 +0200381API void
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200382lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200383{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200384 if (!elem) {
385 return;
386 }
Radek Krejci02117302015-04-13 16:32:44 +0200387
Michal Vaskof8879c22015-08-21 09:07:36 +0200388 lyxml_unlink_elem(ctx, elem, 0);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200389 lyxml_free_elem_(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200390}
391
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200392const char *
393lyxml_get_attr(struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200394{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200395 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200396
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200397 assert(elem);
398 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200399
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200400 for (a = elem->attr; a; a = a->next) {
401 if (a->type != LYXML_ATTR_STD) {
402 continue;
403 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200404
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200405 if (!strcmp(name, a->name)) {
406 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
407 return a->value;
408 }
409 }
410 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200411
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200412 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200413}
414
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200415int
Michal Vaskof8879c22015-08-21 09:07:36 +0200416lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200417{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200418 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200419
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200420 assert(parent);
421 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200422
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200423 /* (re)link element to parent */
424 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200425 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200426 }
427 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200428
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200429 /* link parent to element */
430 if (parent->child) {
431 e = parent->child;
432 elem->prev = e->prev;
433 elem->next = NULL;
434 elem->prev->next = elem;
435 e->prev = elem;
436 } else {
437 parent->child = elem;
438 elem->prev = elem;
439 elem->next = NULL;
440 }
Radek Krejci02117302015-04-13 16:32:44 +0200441
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200442 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200443}
444
Michal Vasko3b855722015-08-28 16:01:18 +0200445int
Michal Vasko076588e2015-10-06 15:39:57 +0200446lyxml_getutf8(const char *buf, unsigned int *read, unsigned int line)
Radek Krejci02117302015-04-13 16:32:44 +0200447{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200448 int c, aux;
449 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200450
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200451 c = buf[0];
452 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200453
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200454 /* buf is NULL terminated string, so 0 means EOF */
455 if (!c) {
Michal Vasko076588e2015-10-06 15:39:57 +0200456 LOGVAL(LYE_EOF, line);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200457 return 0;
458 }
459 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200460
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200461 /* process character byte(s) */
462 if ((c & 0xf8) == 0xf0) {
463 /* four bytes character */
464 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200465
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200466 c &= 0x07;
467 for (i = 1; i <= 3; i++) {
468 aux = buf[i];
469 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200470 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200471 return 0;
472 }
Radek Krejci02117302015-04-13 16:32:44 +0200473
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200474 c = (c << 6) | (aux & 0x3f);
475 }
Radek Krejci02117302015-04-13 16:32:44 +0200476
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200477 if (c < 0x1000 || c > 0x10ffff) {
Michal Vasko076588e2015-10-06 15:39:57 +0200478 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200479 return 0;
480 }
481 } else if ((c & 0xf0) == 0xe0) {
482 /* three bytes character */
483 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200484
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200485 c &= 0x0f;
486 for (i = 1; i <= 2; i++) {
487 aux = buf[i];
488 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200489 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200490 return 0;
491 }
Radek Krejci02117302015-04-13 16:32:44 +0200492
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200493 c = (c << 6) | (aux & 0x3f);
494 }
Radek Krejci02117302015-04-13 16:32:44 +0200495
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200496 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
Michal Vasko076588e2015-10-06 15:39:57 +0200497 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200498 return 0;
499 }
500 } else if ((c & 0xe0) == 0xc0) {
501 /* two bytes character */
502 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200503
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200504 aux = buf[1];
505 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200506 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200507 return 0;
508 }
509 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200510
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200511 if (c < 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200512 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200513 return 0;
514 }
515 } else if (!(c & 0x80)) {
516 /* one byte character */
517 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
518 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200519 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200520 return 0;
521 }
522 } else {
523 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200524 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200525 return 0;
526 }
Radek Krejci02117302015-04-13 16:32:44 +0200527
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200528 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200529}
530
Radek Krejci709fee62015-04-15 13:56:19 +0200531/**
532 * Store UTF-8 character specified as 4byte integer into the dst buffer.
533 * Returns number of written bytes (4 max), expects that dst has enough space.
534 *
535 * UTF-8 mapping:
536 * 00000000 -- 0000007F: 0xxxxxxx
537 * 00000080 -- 000007FF: 110xxxxx 10xxxxxx
538 * 00000800 -- 0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
539 * 00010000 -- 001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
540 *
541 */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200542static unsigned int
543pututf8(char *dst, int32_t value)
Radek Krejci709fee62015-04-15 13:56:19 +0200544{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200545 if (value < 0x80) {
546 /* one byte character */
547 dst[0] = value;
Radek Krejci709fee62015-04-15 13:56:19 +0200548
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200549 return 1;
550 } else if (value < 0x800) {
551 /* two bytes character */
552 dst[0] = 0xc0 | (value >> 6);
553 dst[1] = 0x80 | (value & 0x3f);
Radek Krejci709fee62015-04-15 13:56:19 +0200554
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200555 return 2;
556 } else if (value < 0x10000) {
557 /* three bytes character */
558 dst[0] = 0xe0 | (value >> 12);
559 dst[1] = 0x80 | ((value >> 6) & 0x3f);
560 dst[2] = 0x80 | (value & 0x3f);
Radek Krejci709fee62015-04-15 13:56:19 +0200561
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200562 return 3;
563 } else if (value < 0x200000) {
564 /* four bytes character */
565 dst[0] = 0xf0 | (value >> 18);
566 dst[1] = 0x80 | ((value >> 12) & 0x3f);
567 dst[2] = 0x80 | ((value >> 6) & 0x3f);
568 dst[3] = 0x80 | (value & 0x3f);
Radek Krejci709fee62015-04-15 13:56:19 +0200569
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200570 return 4;
571 } else {
572 /* out of range */
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200573 LOGVAL(LYE_SPEC, lineno, "Invalid UTF-8 value 0x%08x", value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200574 return 0;
575 }
Radek Krejci709fee62015-04-15 13:56:19 +0200576}
577
Michal Vasko0d343d12015-08-24 14:57:36 +0200578/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200579static int
580parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200581{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200582 unsigned int slen;
583 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200584
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200585 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200586
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200587 while (*c && memcmp(c, endstr, slen)) {
588 COUNTLINE(*c);
589 c++;
590 }
591 if (!*c) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200592 LOGVAL(LYE_XML_MISS, lineno, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200593 return EXIT_FAILURE;
594 }
595 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200596
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200597 *len = c - data;
598 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200599}
600
Michal Vasko0d343d12015-08-24 14:57:36 +0200601/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200602static char *
603parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200604{
Radek Krejci709fee62015-04-15 13:56:19 +0200605#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200606
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200607 char buf[BUFSIZE];
608 char *result = NULL, *aux;
609 unsigned int r;
610 int o, size = 0;
611 int cdsect = 0;
612 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200613
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200614 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
615 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200616 LOGVAL(LYE_XML_INVAL, lineno, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200617 goto error;
618 }
Radek Krejci709fee62015-04-15 13:56:19 +0200619
Radek Krejcia4a84062015-04-16 13:00:10 +0200620loop:
621
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200622 if (o > BUFSIZE - 3) {
623 /* add buffer into the result */
624 if (result) {
625 size = size + o;
626 aux = realloc(result, size + 1);
627 result = aux;
628 } else {
629 size = o;
630 result = malloc((size + 1) * sizeof *result);
631 }
632 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200633
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200634 /* write again into the beginning of the buffer */
635 o = 0;
636 }
Radek Krejci709fee62015-04-15 13:56:19 +0200637
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200638 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
639 /* CDSect */
640 if (!cdsect) {
641 cdsect = 1;
642 *len += 9;
643 }
644 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
645 *len += 3;
646 cdsect = 0;
647 o--; /* we don't write any data in this iteration */
648 } else {
649 buf[o] = data[*len];
650 (*len)++;
651 }
652 } else if (data[*len] == '&') {
653 (*len)++;
654 if (data[*len] != '#') {
655 /* entity reference - only predefined refs are supported */
656 if (!memcmp(&data[*len], "lt;", 3)) {
657 buf[o] = '<';
658 *len += 3;
659 } else if (!memcmp(&data[*len], "gt;", 3)) {
660 buf[o] = '>';
661 *len += 3;
662 } else if (!memcmp(&data[*len], "amp;", 4)) {
663 buf[o] = '&';
664 *len += 4;
665 } else if (!memcmp(&data[*len], "apos;", 5)) {
666 buf[o] = '\'';
667 *len += 5;
668 } else if (!memcmp(&data[*len], "quot;", 5)) {
669 buf[o] = '\"';
670 *len += 5;
671 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200672 LOGVAL(LYE_XML_INVAL, lineno, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200673 goto error;
674 }
675 } else {
676 /* character reference */
677 (*len)++;
678 if (isdigit(data[*len])) {
679 for (n = 0; isdigit(data[*len]); (*len)++) {
680 n = (10 * n) + (data[*len] - '0');
681 }
682 if (data[*len] != ';') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200683 LOGVAL(LYE_XML_INVAL, lineno, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200684 goto error;
685 }
686 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
687 for (n = 0; isxdigit(data[*len]); (*len)++) {
688 if (isdigit(data[*len])) {
689 r = (data[*len] - '0');
690 } else if (data[*len] > 'F') {
691 r = 10 + (data[*len] - 'a');
692 } else {
693 r = 10 + (data[*len] - 'A');
694 }
695 n = (16 * n) + r;
696 }
697 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200698 LOGVAL(LYE_XML_INVAL, lineno, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200699 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200700
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200701 }
702 r = pututf8(&buf[o], n);
703 if (!r) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200704 LOGVAL(LYE_XML_INVAL, lineno, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200705 goto error;
706 }
707 o += r - 1; /* o is ++ in for loop */
708 (*len)++;
709 }
710 } else {
711 buf[o] = data[*len];
712 COUNTLINE(buf[o]);
713 (*len)++;
714 }
715 }
Radek Krejci02117302015-04-13 16:32:44 +0200716
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200717 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
718 /* ignore loop's end condition on beginning of CDSect */
719 goto loop;
720 }
Radek Krejci709fee62015-04-15 13:56:19 +0200721#undef BUFSIZE
722
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200723 if (o) {
724 if (result) {
725 size = size + o;
726 aux = realloc(result, size + 1);
727 result = aux;
728 } else {
729 size = o;
730 result = malloc((size + 1) * sizeof *result);
731 }
732 memcpy(&result[size - o], buf, o);
733 }
734 if (result) {
735 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200736 } else {
737 size = 0;
738 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200739 }
Radek Krejci02117302015-04-13 16:32:44 +0200740
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200741 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200742
743error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200744 free(result);
745 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200746}
747
Michal Vasko0d343d12015-08-24 14:57:36 +0200748/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200749static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200750parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200751{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200752 const char *c = data, *start, *delim;
753 char prefix[32];
754 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200755 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200756 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200757
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200758 /* check if it is attribute or namespace */
759 if (!memcmp(c, "xmlns", 5)) {
760 /* namespace */
761 attr = calloc(1, sizeof (struct lyxml_ns));
762 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200763 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200764 c += 5;
765 if (*c != ':') {
766 /* default namespace, prefix will be empty */
767 goto equal;
768 }
769 c++; /* go after ':' to the prefix value */
770 } else {
771 /* attribute */
772 attr = calloc(1, sizeof *attr);
773 attr->type = LYXML_ATTR_STD;
774 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200775
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200776 /* process name part of the attribute */
777 start = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200778 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200779 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200780 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200781 free(attr);
782 return NULL;
783 }
784 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200785 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200786 while (is_xmlnamechar(uc)) {
787 if (attr->type == LYXML_ATTR_STD && *c == ':') {
788 /* attribute in a namespace */
789 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200790
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200791 /* look for the prefix in namespaces */
792 memcpy(prefix, data, c - data);
793 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200794 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200795 }
796 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200797 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200798 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200799
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200800 /* store the name */
801 size = c - start;
802 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200803
804equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200805 /* check Eq mark that can be surrounded by whitespaces */
806 ign_xmlws(c);
807 if (*c != '=') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200808 LOGVAL(LYE_XML_INVAL, lineno, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200809 goto error;
810 }
811 c++;
812 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200813
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200814 /* process value part of the attribute */
815 if (!*c || (*c != '"' && *c != '\'')) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200816 LOGVAL(LYE_XML_INVAL, lineno, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200817 goto error;
818 }
819 delim = c;
820 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
821 if (ly_errno) {
822 goto error;
823 }
Radek Krejci02117302015-04-13 16:32:44 +0200824
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200825 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200826
827 /* put attribute into the parent's attributes list */
828 if (parent->attr) {
829 /* go to the end of the list */
830 for (a = parent->attr; a->next; a = a->next);
831 /* and append new attribute */
832 a->next = attr;
833 } else {
834 /* add the first attribute in the list */
835 parent->attr = attr;
836 }
837
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200838 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200839
840error:
Radek Krejci00249f22015-07-07 13:43:28 +0200841 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200842 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200843}
844
Michal Vasko0d343d12015-08-24 14:57:36 +0200845/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200846static struct lyxml_elem *
847parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200848{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200849 const char *c = data, *start, *e;
850 const char *lws; /* leading white space for handling mixed content */
851 int uc;
852 char *str;
853 char prefix[32] = { 0 };
854 unsigned int prefix_len = 0;
855 struct lyxml_elem *elem = NULL, *child;
856 struct lyxml_attr *attr;
857 unsigned int size;
858 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200859
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200860 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200861
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200862 if (*c != '<') {
863 return NULL;
864 }
Radek Krejci02117302015-04-13 16:32:44 +0200865
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200866 /* locate element name */
867 c++;
868 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200869
Michal Vasko076588e2015-10-06 15:39:57 +0200870 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200871 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200872 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200873 return NULL;
874 }
875 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200876 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200877 while (is_xmlnamechar(uc)) {
878 if (*e == ':') {
879 if (prefix_len) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200880 LOGVAL(LYE_XML_INVAL, lineno, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200881 goto error;
882 }
883 /* element in a namespace */
884 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200885
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200886 /* look for the prefix in namespaces */
887 memcpy(prefix, c, prefix_len = e - c);
888 prefix[prefix_len] = '\0';
889 c = start;
890 }
891 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200892 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200893 }
894 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200895 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200896 return NULL;
897 }
Radek Krejci02117302015-04-13 16:32:44 +0200898
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200899 /* allocate element structure */
900 elem = calloc(1, sizeof *elem);
Radek Krejci3045cf32015-05-28 10:58:52 +0200901#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200902 elem->line = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +0200903#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200904 elem->next = NULL;
905 elem->prev = elem;
906 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200907 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200908 }
Radek Krejci02117302015-04-13 16:32:44 +0200909
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200910 /* store the name into the element structure */
911 elem->name = lydict_insert(ctx, c, e - c);
912 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200913
914process:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200915 ly_errno = 0;
916 ign_xmlws(c);
917 if (!memcmp("/>", c, 2)) {
918 /* we are done, it was EmptyElemTag */
919 c += 2;
920 closed_flag = 1;
921 } else if (*c == '>') {
922 /* process element content */
923 c++;
924 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200925
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200926 while (*c) {
927 if (!memcmp(c, "</", 2)) {
928 if (lws && !elem->child) {
929 /* leading white spaces were actually content */
930 goto store_content;
931 }
Radek Krejci02117302015-04-13 16:32:44 +0200932
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200933 /* Etag */
934 c += 2;
935 /* get name and check it */
936 e = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200937 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200938 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200939 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200940 goto error;
941 }
942 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200943 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200944 while (is_xmlnamechar(uc)) {
945 if (*e == ':') {
946 /* element in a namespace */
947 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200948
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200949 /* look for the prefix in namespaces */
950 if (memcmp(prefix, c, e - c)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200951 LOGVAL(LYE_SPEC, lineno,
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200952 "Mixed opening (%s) and closing element tags (different namespaces).", elem->name);
953 goto error;
954 }
955 c = start;
956 }
957 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200958 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200959 }
960 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200961 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200962 goto error;
963 }
Radek Krejci02117302015-04-13 16:32:44 +0200964
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200965 /* check that it corresponds to opening tag */
966 size = e - c;
967 str = malloc((size + 1) * sizeof *str);
968 memcpy(str, c, e - c);
969 str[e - c] = '\0';
970 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200971 LOGVAL(LYE_SPEC, lineno, "Mixed opening (%s) and closing (%s) element tags.", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200972 free(str);
973 goto error;
974 }
975 free(str);
976 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200977
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200978 ign_xmlws(c);
979 if (*c != '>') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200980 LOGVAL(LYE_SPEC, lineno, "Close element tag \"%s\" contain additional data.", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200981 goto error;
982 }
983 c++;
984 closed_flag = 1;
985 break;
Radek Krejci02117302015-04-13 16:32:44 +0200986
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200987 } else if (!memcmp(c, "<?", 2)) {
988 if (lws) {
989 /* leading white spaces were only formatting */
990 lws = NULL;
991 }
992 /* PI - ignore it */
993 c += 2;
994 if (parse_ignore(c, "?>", &size)) {
995 goto error;
996 }
997 c += size;
998 } else if (!memcmp(c, "<!--", 4)) {
999 if (lws) {
1000 /* leading white spaces were only formatting */
1001 lws = NULL;
1002 }
1003 /* Comment - ignore it */
1004 c += 4;
1005 if (parse_ignore(c, "-->", &size)) {
1006 goto error;
1007 }
1008 c += size;
1009 } else if (!memcmp(c, "<![CDATA[", 9)) {
1010 /* CDSect */
1011 goto store_content;
1012 } else if (*c == '<') {
1013 if (lws) {
1014 if (elem->flags & LYXML_ELEM_MIXED) {
1015 /* we have a mixed content */
1016 goto store_content;
1017 } else {
1018 /* leading white spaces were only formatting */
1019 lws = NULL;
1020 }
1021 }
1022 if (elem->content) {
1023 /* we have a mixed content */
1024 child = calloc(1, sizeof *child);
1025 child->content = elem->content;
1026 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001027 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001028 elem->flags |= LYXML_ELEM_MIXED;
1029 }
1030 child = parse_elem(ctx, c, &size, elem);
1031 if (!child) {
1032 goto error;
1033 }
1034 c += size; /* move after processed child element */
1035 } else if (is_xmlws(*c)) {
1036 lws = c;
Radek Krejci3045cf32015-05-28 10:58:52 +02001037#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001038 lws_lineno = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001039#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001040 ign_xmlws(c);
1041 } else {
Radek Krejci02117302015-04-13 16:32:44 +02001042store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001043 /* store text content */
1044 if (lws) {
1045 /* process content including the leading white spaces */
1046 c = lws;
Radek Krejci3045cf32015-05-28 10:58:52 +02001047#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001048 lineno = lws_lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001049#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001050 lws = NULL;
1051 }
1052 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1053 if (ly_errno) {
1054 goto error;
1055 }
1056 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001057
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001058 if (elem->child) {
1059 /* we have a mixed content */
1060 child = calloc(1, sizeof *child);
1061 child->content = elem->content;
1062 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001063 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001064 elem->flags |= LYXML_ELEM_MIXED;
1065 }
1066 }
1067 }
1068 } else {
1069 /* process attribute */
1070 attr = parse_attr(ctx, c, &size, elem);
1071 if (!attr) {
1072 goto error;
1073 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001074 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001075
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001076 /* check namespace */
1077 if (attr->type == LYXML_ATTR_NS) {
1078 if (!prefix[0] && !attr->name) {
1079 if (attr->value) {
1080 /* default prefix */
1081 elem->ns = (struct lyxml_ns *)attr;
1082 } else {
1083 /* xmlns="" -> no namespace */
1084 nons_flag = 1;
1085 }
1086 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1087 /* matching namespace with prefix */
1088 elem->ns = (struct lyxml_ns *)attr;
1089 }
1090 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001091
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001092 /* go back to finish element processing */
1093 goto process;
1094 }
Radek Krejci02117302015-04-13 16:32:44 +02001095
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001096 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001097
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001098 if (!closed_flag) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001099 LOGVAL(LYE_XML_MISS, lineno, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001100 goto error;
1101 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001102
Radek Krejci78a230a2015-07-07 17:04:40 +02001103 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001104 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001105 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001106
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001107 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001108
1109error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001110 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001111
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001112 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001113}
1114
Michal Vasko0d343d12015-08-24 14:57:36 +02001115/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001116API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001117lyxml_read(struct ly_ctx *ctx, const char *data, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001118{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001119 const char *c = data;
1120 unsigned int len;
1121 struct lyxml_elem *root = NULL;
Radek Krejci02117302015-04-13 16:32:44 +02001122
Radek Krejci3045cf32015-05-28 10:58:52 +02001123#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001124 /* TODO: threads support */
1125 lineno = 1;
Radek Krejci3045cf32015-05-28 10:58:52 +02001126#endif
1127
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001128 /* process document */
1129 while (*c) {
1130 if (is_xmlws(*c)) {
1131 /* skip whitespaces */
1132 ign_xmlws(c);
1133 } else if (!memcmp(c, "<?", 2)) {
1134 /* XMLDecl or PI - ignore it */
1135 c += 2;
1136 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001137 return NULL;
1138 }
1139 c += len;
1140 } else if (!memcmp(c, "<!--", 4)) {
1141 /* Comment - ignore it */
1142 c += 2;
1143 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001144 return NULL;
1145 }
1146 c += len;
1147 } else if (!memcmp(c, "<!", 2)) {
1148 /* DOCTYPE */
1149 /* TODO - standalone ignore counting < and > */
1150 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1151 return NULL;
1152 } else if (*c == '<') {
1153 /* element - process it in next loop to strictly follow XML
1154 * format
1155 */
1156 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001157 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001158 LOGVAL(LYE_XML_INCHAR, lineno, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001159 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001160 }
1161 }
Radek Krejci02117302015-04-13 16:32:44 +02001162
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001163 root = parse_elem(ctx, c, &len, NULL);
1164 if (!root) {
1165 return NULL;
1166 }
1167 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001168
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001169 /* ignore the rest of document where can be comments, PIs and whitespaces,
1170 * note that we are not detecting syntax errors in these parts
1171 */
1172 ign_xmlws(c);
1173 if (*c) {
1174 LOGWRN("There are some not parsed data:\n%s", c);
1175 }
Radek Krejci02117302015-04-13 16:32:44 +02001176
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001177 return root;
Radek Krejci02117302015-04-13 16:32:44 +02001178}
1179
Radek Krejcic6704c82015-10-06 11:12:45 +02001180API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001181lyxml_read_fd(struct ly_ctx *ctx, int fd, int UNUSED(options))
Radek Krejci02117302015-04-13 16:32:44 +02001182{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001183 if (fd == -1 || !ctx) {
1184 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1185 return NULL;
1186 }
Radek Krejci02117302015-04-13 16:32:44 +02001187
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001188 LOGERR(LY_EINT, "%s function is not implemented", __func__);
1189 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001190}
1191
Radek Krejcic6704c82015-10-06 11:12:45 +02001192API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001193lyxml_read_file(struct ly_ctx *ctx, const char *filename, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001194{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001195 if (!filename || !ctx) {
1196 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1197 return NULL;
1198 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001199
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001200 LOGERR(LY_EINT, "%s function is not implemented", __func__);
1201 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001202}
Radek Krejci02117302015-04-13 16:32:44 +02001203
Michal Vasko5db027d2015-10-09 14:38:50 +02001204int
1205lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001206{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001207 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001208
Michal Vasko5db027d2015-10-09 14:38:50 +02001209 if (!text) {
1210 return 0;
1211 }
1212
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001213 for (i = n = 0; text[i]; i++) {
1214 switch (text[i]) {
1215 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001216 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001217 break;
1218 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001219 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001220 break;
1221 case '>':
1222 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001223 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001224 break;
1225 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001226 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001227 n++;
1228 }
1229 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001230
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001231 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001232}
1233
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001234static int
Radek Krejci5248f132015-10-09 10:34:25 +02001235dump_elem(struct lyout *out, struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001236{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001237 int size = 0;
1238 struct lyxml_attr *a;
1239 struct lyxml_elem *child;
1240 const char *delim, *delim_outer;
1241 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001242
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001243 if (!e->name) {
1244 /* mixed content */
1245 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001246 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001247 } else {
1248 return 0;
1249 }
1250 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001251
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001252 delim = delim_outer = "\n";
1253 indent = 2 * level;
1254 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1255 delim = "";
1256 }
1257 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1258 delim_outer = "";
1259 indent = 0;
1260 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001261
Radek Krejcic6704c82015-10-06 11:12:45 +02001262 if (!options || (options & LYXML_DUMP_OPEN)) {
1263 /* opening tag */
1264 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001265 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001266 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001267 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001268 }
1269 } else if (options & LYXML_DUMP_CLOSE) {
1270 indent = 0;
1271 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001272 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001273
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001274 /* attributes */
1275 for (a = e->attr; a; a = a->next) {
1276 if (a->type == LYXML_ATTR_NS) {
1277 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001278 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001279 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001280 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001281 }
1282 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001283 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001284 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001285 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001286 }
1287 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001288
Radek Krejcic6704c82015-10-06 11:12:45 +02001289 /* apply options */
1290 if (options == (LYXML_DUMP_OPEN | LYXML_DUMP_CLOSE)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001291 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001292 return size;
1293 } else if (options & LYXML_DUMP_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001294 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001295 return ++size;
1296 } else if (options & LYXML_DUMP_ATTRS) {
1297 return size;
1298 }
1299
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001300 if (!e->child && !e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001301 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001302 return size;
1303 } else if (e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001304 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001305 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001306
Michal Vasko5db027d2015-10-09 14:38:50 +02001307 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001308
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001310 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001311 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001312 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001313 }
1314 return size;
1315 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001316 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001317 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001318
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001319 /* go recursively */
1320 LY_TREE_FOR(e->child, child) {
Radek Krejci5248f132015-10-09 10:34:25 +02001321 size += dump_elem(out, child, level + 1, 0);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001322 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001323
Radek Krejcic6704c82015-10-06 11:12:45 +02001324close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001325 /* closing tag */
1326 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001327 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001328 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001329 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001330 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001331
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001332 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001333}
1334
Radek Krejcic6704c82015-10-06 11:12:45 +02001335API int
Radek Krejci5248f132015-10-09 10:34:25 +02001336lyxml_dump(FILE *stream, struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001337{
Radek Krejci5248f132015-10-09 10:34:25 +02001338 struct lyout out;
1339
1340 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001341 return 0;
1342 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001343
Radek Krejci5248f132015-10-09 10:34:25 +02001344 out.type = LYOUT_STREAM;
1345 out.method.f = stream;
1346
1347 return dump_elem(&out, elem, 0, options);
1348}
1349
1350API int
1351lyxml_dump_fd(int fd, struct lyxml_elem *elem, int options)
1352{
1353 struct lyout out;
1354
1355 if (fd < 0 || !elem) {
1356 return 0;
1357 }
1358
1359 out.type = LYOUT_FD;
1360 out.method.fd = fd;
1361
1362 return dump_elem(&out, elem, 0, options);
Radek Krejcif0023a92015-04-20 20:51:39 +02001363}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001364
1365API int
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001366lyxml_dump_mem(char **strp, struct lyxml_elem *elem, int options)
1367{
1368 struct lyout out;
1369 int r;
1370
1371 if (!strp || !elem) {
1372 return 0;
1373 }
1374
1375 out.type = LYOUT_MEMORY;
1376 out.method.mem.buf = NULL;
1377 out.method.mem.len = 0;
1378 out.method.mem.size = 0;
1379
1380 r = dump_elem(&out, elem, 0, options);
1381
1382 *strp = out.method.mem.buf;
1383 return r;
1384}
1385
1386API int
Radek Krejci50929eb2015-10-09 18:14:15 +02001387lyxml_dump_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, struct lyxml_elem *elem, int options)
Radek Krejci6140e4e2015-10-09 15:50:55 +02001388{
1389 struct lyout out;
1390
1391 if (!writeclb || !elem) {
1392 return 0;
1393 }
1394
1395 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001396 out.method.clb.f = writeclb;
1397 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001398
1399 return dump_elem(&out, elem, 0, options);
1400}