blob: d816b114535b9facded0f8b31f8ade2998cf95ea [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>
Pavol Vicanb2570c12015-11-12 13:50:20 +010029#include <sys/stat.h>
30#include <sys/mman.h>
31#include <fcntl.h>
Radek Krejci54ea8de2015-04-09 18:02:56 +020032
Radek Krejci06a704e2015-04-22 14:50:49 +020033#include "common.h"
Radek Krejci41912fe2015-10-22 10:22:12 +020034#include "dict_private.h"
Radek Krejci5248f132015-10-09 10:34:25 +020035#include "printer.h"
Radek Krejci5449d472015-10-26 14:35:56 +010036#include "parser.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020037#include "tree_schema.h"
Michal Vaskofc5744d2015-10-22 12:09:34 +020038#include "xml_internal.h"
Radek Krejci54ea8de2015-04-09 18:02:56 +020039
Radek Krejci3045cf32015-05-28 10:58:52 +020040#ifndef NDEBUG
Radek Krejci967e4bf2015-11-28 10:06:40 +010041static unsigned int lws_lineno = 0;
Radek Krejci3045cf32015-05-28 10:58:52 +020042#endif
Radek Krejci967e4bf2015-11-28 10:06:40 +010043static unsigned int lineno = 0;
Radek Krejci3045cf32015-05-28 10:58:52 +020044
Radek Krejci3045cf32015-05-28 10:58:52 +020045#define ign_xmlws(p) \
46 while (is_xmlws(*p)) { \
47 COUNTLINE(*p); \
48 p++; \
49 }
Radek Krejci02117302015-04-13 16:32:44 +020050
Michal Vasko88c29542015-11-27 14:57:53 +010051static struct lyxml_attr *lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr);
52
Michal Vasko1e62a092015-12-01 12:27:20 +010053API const struct lyxml_ns *
54lyxml_get_ns(const struct lyxml_elem *elem, const char *prefix)
Michal Vaskof8879c22015-08-21 09:07:36 +020055{
56 struct lyxml_attr *attr;
57 int len;
58
59 if (!elem) {
60 return NULL;
61 }
62
63 if (!prefix) {
64 len = 0;
65 } else {
66 len = strlen(prefix) + 1;
67 }
68
69 for (attr = elem->attr; attr; attr = attr->next) {
70 if (attr->type != LYXML_ATTR_NS) {
71 continue;
72 }
73 if (!attr->name) {
74 if (!len) {
75 /* default namespace found */
76 if (!attr->value) {
77 /* empty default namespace -> no default namespace */
78 return NULL;
79 }
80 return (struct lyxml_ns *)attr;
81 }
82 } else if (len && !memcmp(attr->name, prefix, len)) {
83 /* prefix found */
84 return (struct lyxml_ns *)attr;
85 }
86 }
87
88 /* go recursively */
89 return lyxml_get_ns(elem->parent, prefix);
90}
91
Michal Vasko88c29542015-11-27 14:57:53 +010092static void
93lyxml_correct_attr_ns(struct ly_ctx *ctx, struct lyxml_attr *attr, struct lyxml_elem *attr_parent, int copy_ns)
94{
95 const struct lyxml_ns *tmp_ns;
96 struct lyxml_elem *ns_root;
97
98 if ((attr->type != LYXML_ATTR_NS) && attr->ns) {
99 for (; attr_parent->parent; attr_parent = attr_parent->parent);
100
101 /* find the root of attr NS */
102 for (ns_root = attr->ns->parent; ns_root->parent; ns_root = ns_root->parent);
103
104 /* elem NS is defined outside elem subtree */
105 if (ns_root != attr_parent) {
106 if (copy_ns) {
107 tmp_ns = attr->ns;
108 /* we may have already copied the NS over? */
109 attr->ns = lyxml_get_ns(attr->ns->parent, tmp_ns->prefix);
110
111 /* we haven't copied it over, copy it now */
112 if (!attr->ns) {
113 attr->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, attr->ns->parent, (struct lyxml_attr *)tmp_ns);
114 }
115 } else {
116 attr->ns = NULL;
117 }
118 }
119 }
120}
121
122static struct lyxml_attr *
Michal Vaskof8879c22015-08-21 09:07:36 +0200123lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
124{
125 struct lyxml_attr *result, *a;
126
127 if (!attr || !parent) {
128 return NULL;
129 }
130
131 if (attr->type == LYXML_ATTR_NS) {
132 /* this is correct, despite that all attributes seems like a standard
133 * attributes (struct lyxml_attr), some of them can be namespace
134 * definitions (and in that case they are struct lyxml_ns).
135 */
136 result = (struct lyxml_attr *)calloc(1, sizeof (struct lyxml_ns));
137 } else {
138 result = calloc(1, sizeof (struct lyxml_attr));
139 }
140 result->value = lydict_insert(ctx, attr->value, 0);
141 result->name = lydict_insert(ctx, attr->name, 0);
142 result->type = attr->type;
143
144 /* set namespace in case of standard attributes */
145 if (result->type == LYXML_ATTR_STD && attr->ns) {
Michal Vasko88c29542015-11-27 14:57:53 +0100146 result->ns = attr->ns;
147 lyxml_correct_attr_ns(ctx, result, parent, 1);
Michal Vaskof8879c22015-08-21 09:07:36 +0200148 }
149
150 /* set parent pointer in case of namespace attribute */
151 if (result->type == LYXML_ATTR_NS) {
152 ((struct lyxml_ns *)result)->parent = parent;
153 }
154
155 /* put attribute into the parent's attributes list */
156 if (parent->attr) {
157 /* go to the end of the list */
158 for (a = parent->attr; a->next; a = a->next);
159 /* and append new attribute */
160 a->next = result;
161 } else {
162 /* add the first attribute in the list */
163 parent->attr = result;
164 }
165
166 return result;
167}
168
Michal Vasko88c29542015-11-27 14:57:53 +0100169/* copy_ns: 0 - set invalid namespaces to NULL, 1 - copy them into this subtree */
170static void
171lyxml_correct_elem_ns(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns, int correct_attrs)
172{
173 const struct lyxml_ns *tmp_ns;
174 struct lyxml_elem *elem_root, *ns_root, *tmp;
175 struct lyxml_attr *attr;
176
177 /* find the root of elem */
178 for (elem_root = elem; elem_root->parent; elem_root = elem_root->parent);
179
180 LY_TREE_DFS_BEGIN(elem, tmp, elem) {
181 if (elem->ns) {
182 /* find the root of elem NS */
183 for (ns_root = elem->ns->parent; ns_root->parent; ns_root = ns_root->parent);
184
185 /* elem NS is defined outside elem subtree */
186 if (ns_root != elem_root) {
187 if (copy_ns) {
188 tmp_ns = elem->ns;
189 /* we may have already copied the NS over? */
190 elem->ns = lyxml_get_ns(elem, tmp_ns->prefix);
191
192 /* we haven't copied it over, copy it now */
193 if (!elem->ns) {
194 elem->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, elem, (struct lyxml_attr *)tmp_ns);
195 }
196 } else {
197 elem->ns = NULL;
198 }
199 }
200 }
201 if (correct_attrs) {
202 LY_TREE_FOR(elem->attr, attr) {
203 lyxml_correct_attr_ns(ctx, attr, elem_root, copy_ns);
204 }
205 }
206 LY_TREE_DFS_END(elem, tmp, elem);
207 }
208}
209
Michal Vaskof8879c22015-08-21 09:07:36 +0200210struct lyxml_elem *
211lyxml_dup_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, struct lyxml_elem *parent, int recursive)
212{
213 struct lyxml_elem *result, *child;
214 struct lyxml_attr *attr;
215
216 if (!elem) {
217 return NULL;
218 }
219
220 result = calloc(1, sizeof *result);
221 result->content = lydict_insert(ctx, elem->content, 0);
222 result->name = lydict_insert(ctx, elem->name, 0);
223 result->flags = elem->flags;
224#ifndef NDEBUG
225 result->line = elem->line;
226#endif
227 result->prev = result;
228
229 if (parent) {
230 lyxml_add_child(ctx, parent, result);
231 }
232
Michal Vasko88c29542015-11-27 14:57:53 +0100233 /* keep old namespace for now */
234 result->ns = elem->ns;
235
236 /* correct namespaces */
237 lyxml_correct_elem_ns(ctx, result, 1, 0);
Michal Vaskof8879c22015-08-21 09:07:36 +0200238
239 /* duplicate attributes */
240 for (attr = elem->attr; attr; attr = attr->next) {
241 lyxml_dup_attr(ctx, result, attr);
242 }
243
244 if (!recursive) {
245 return result;
246 }
247
248 /* duplicate children */
249 LY_TREE_FOR(elem->child, child) {
250 lyxml_dup_elem(ctx, child, result, 1);
251 }
252
253 return result;
254}
255
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200256void
Michal Vaskof8879c22015-08-21 09:07:36 +0200257lyxml_unlink_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
Radek Krejci02117302015-04-13 16:32:44 +0200258{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200259 struct lyxml_elem *parent, *first;
Radek Krejci02117302015-04-13 16:32:44 +0200260
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200261 if (!elem) {
262 return;
263 }
Radek Krejci02117302015-04-13 16:32:44 +0200264
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200265 /* store pointers to important nodes */
266 parent = elem->parent;
Radek Krejcie1f13912015-05-26 15:17:38 +0200267
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200268 /* unlink from parent */
269 if (parent) {
270 if (parent->child == elem) {
271 /* we unlink the first child */
272 /* update the parent's link */
273 parent->child = elem->next;
274 }
275 /* forget about the parent */
276 elem->parent = NULL;
277 }
Radek Krejci02117302015-04-13 16:32:44 +0200278
Michal Vasko88c29542015-11-27 14:57:53 +0100279 if (copy_ns < 2) {
280 lyxml_correct_elem_ns(ctx, elem, copy_ns, 1);
281 }
282
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200283 /* unlink from siblings */
284 if (elem->prev == elem) {
285 /* there are no more siblings */
286 return;
287 }
288 if (elem->next) {
289 elem->next->prev = elem->prev;
290 } else {
291 /* unlinking the last element */
292 if (parent) {
293 first = parent->child;
294 } else {
295 first = elem;
296 while (elem->prev->next) {
297 first = elem->prev;
298 }
299 }
300 first->prev = elem->prev;
301 }
302 if (elem->prev->next) {
303 elem->prev->next = elem->next;
304 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200305
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200306 /* clean up the unlinked element */
307 elem->next = NULL;
308 elem->prev = elem;
Radek Krejci02117302015-04-13 16:32:44 +0200309}
310
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200311void
Radek Krejci00249f22015-07-07 13:43:28 +0200312lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200313{
Radek Krejci00249f22015-07-07 13:43:28 +0200314 struct lyxml_attr *aiter, *aprev;
315
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200316 if (!attr) {
317 return;
318 }
Radek Krejci02117302015-04-13 16:32:44 +0200319
Radek Krejci00249f22015-07-07 13:43:28 +0200320 if (parent) {
321 /* unlink attribute from the parent's list of attributes */
322 aprev = NULL;
323 for (aiter = parent->attr; aiter; aiter = aiter->next) {
324 if (aiter == attr) {
325 break;
326 }
327 aprev = aiter;
328 }
329 if (!aiter) {
330 /* attribute to remove not found */
331 return;
332 }
333
334 if (!aprev) {
335 /* attribute is first in parent's list of attributes */
336 parent->attr = attr->next;
337 } else {
338 /* reconnect previous attribute to the next */
339 aprev->next = attr->next;
340 }
341 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200342 lydict_remove(ctx, attr->name);
343 lydict_remove(ctx, attr->value);
344 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200345}
346
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200347void
348lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200349{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200350 struct lyxml_attr *a, *next;
351 if (!elem || !elem->attr) {
352 return;
353 }
Radek Krejci02117302015-04-13 16:32:44 +0200354
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200355 a = elem->attr;
356 do {
357 next = a->next;
Radek Krejci02117302015-04-13 16:32:44 +0200358
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200359 lydict_remove(ctx, a->name);
360 lydict_remove(ctx, a->value);
361 free(a);
Radek Krejci02117302015-04-13 16:32:44 +0200362
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200363 a = next;
364 } while (a);
Radek Krejci02117302015-04-13 16:32:44 +0200365}
366
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200367static void
368lyxml_free_elem_(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200369{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200370 struct lyxml_elem *e, *next;
Radek Krejci02117302015-04-13 16:32:44 +0200371
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200372 if (!elem) {
373 return;
374 }
Radek Krejci02117302015-04-13 16:32:44 +0200375
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200376 lyxml_free_attrs(ctx, elem);
377 LY_TREE_FOR_SAFE(elem->child, next, e) {
378 lyxml_free_elem_(ctx, e);
379 }
380 lydict_remove(ctx, elem->name);
381 lydict_remove(ctx, elem->content);
382 free(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200383}
384
Radek Krejcic6704c82015-10-06 11:12:45 +0200385API void
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200386lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200387{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200388 if (!elem) {
389 return;
390 }
Radek Krejci02117302015-04-13 16:32:44 +0200391
Michal Vasko61f7ccb2015-10-23 10:15:08 +0200392 lyxml_unlink_elem(ctx, elem, 2);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200393 lyxml_free_elem_(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200394}
395
Michal Vasko88c29542015-11-27 14:57:53 +0100396API const char *
Michal Vasko1e62a092015-12-01 12:27:20 +0100397lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200398{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200399 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200400
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200401 assert(elem);
402 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200403
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200404 for (a = elem->attr; a; a = a->next) {
405 if (a->type != LYXML_ATTR_STD) {
406 continue;
407 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200408
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200409 if (!strcmp(name, a->name)) {
410 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
411 return a->value;
412 }
413 }
414 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200415
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200416 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200417}
418
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200419int
Michal Vaskof8879c22015-08-21 09:07:36 +0200420lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200421{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200422 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200423
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200424 assert(parent);
425 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200426
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200427 /* (re)link element to parent */
428 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200429 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200430 }
431 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200432
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200433 /* link parent to element */
434 if (parent->child) {
435 e = parent->child;
436 elem->prev = e->prev;
437 elem->next = NULL;
438 elem->prev->next = elem;
439 e->prev = elem;
440 } else {
441 parent->child = elem;
442 elem->prev = elem;
443 elem->next = NULL;
444 }
Radek Krejci02117302015-04-13 16:32:44 +0200445
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200446 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200447}
448
Michal Vasko3b855722015-08-28 16:01:18 +0200449int
Michal Vasko076588e2015-10-06 15:39:57 +0200450lyxml_getutf8(const char *buf, unsigned int *read, unsigned int line)
Radek Krejci02117302015-04-13 16:32:44 +0200451{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200452 int c, aux;
453 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200454
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200455 c = buf[0];
456 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200457
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200458 /* buf is NULL terminated string, so 0 means EOF */
459 if (!c) {
Michal Vasko076588e2015-10-06 15:39:57 +0200460 LOGVAL(LYE_EOF, line);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200461 return 0;
462 }
463 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200464
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200465 /* process character byte(s) */
466 if ((c & 0xf8) == 0xf0) {
467 /* four bytes character */
468 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200469
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200470 c &= 0x07;
471 for (i = 1; i <= 3; i++) {
472 aux = buf[i];
473 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200474 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200475 return 0;
476 }
Radek Krejci02117302015-04-13 16:32:44 +0200477
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200478 c = (c << 6) | (aux & 0x3f);
479 }
Radek Krejci02117302015-04-13 16:32:44 +0200480
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200481 if (c < 0x1000 || c > 0x10ffff) {
Michal Vasko076588e2015-10-06 15:39:57 +0200482 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200483 return 0;
484 }
485 } else if ((c & 0xf0) == 0xe0) {
486 /* three bytes character */
487 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200488
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200489 c &= 0x0f;
490 for (i = 1; i <= 2; i++) {
491 aux = buf[i];
492 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200493 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200494 return 0;
495 }
Radek Krejci02117302015-04-13 16:32:44 +0200496
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200497 c = (c << 6) | (aux & 0x3f);
498 }
Radek Krejci02117302015-04-13 16:32:44 +0200499
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200500 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
Michal Vasko076588e2015-10-06 15:39:57 +0200501 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200502 return 0;
503 }
504 } else if ((c & 0xe0) == 0xc0) {
505 /* two bytes character */
506 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200507
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200508 aux = buf[1];
509 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200510 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200511 return 0;
512 }
513 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200514
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200515 if (c < 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200516 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200517 return 0;
518 }
519 } else if (!(c & 0x80)) {
520 /* one byte character */
521 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
522 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200523 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200524 return 0;
525 }
526 } else {
527 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200528 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200529 return 0;
530 }
Radek Krejci02117302015-04-13 16:32:44 +0200531
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200532 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200533}
534
Michal Vasko0d343d12015-08-24 14:57:36 +0200535/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200536static int
537parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200538{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200539 unsigned int slen;
540 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200541
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200542 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200543
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200544 while (*c && memcmp(c, endstr, slen)) {
545 COUNTLINE(*c);
546 c++;
547 }
548 if (!*c) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200549 LOGVAL(LYE_XML_MISS, lineno, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200550 return EXIT_FAILURE;
551 }
552 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200553
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200554 *len = c - data;
555 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200556}
557
Michal Vasko0d343d12015-08-24 14:57:36 +0200558/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200559static char *
560parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200561{
Radek Krejci709fee62015-04-15 13:56:19 +0200562#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200563
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200564 char buf[BUFSIZE];
565 char *result = NULL, *aux;
566 unsigned int r;
567 int o, size = 0;
568 int cdsect = 0;
569 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200570
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200571 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
572 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200573 LOGVAL(LYE_XML_INVAL, lineno, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200574 goto error;
575 }
Radek Krejci709fee62015-04-15 13:56:19 +0200576
Radek Krejcia4a84062015-04-16 13:00:10 +0200577loop:
578
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200579 if (o > BUFSIZE - 3) {
580 /* add buffer into the result */
581 if (result) {
582 size = size + o;
583 aux = realloc(result, size + 1);
584 result = aux;
585 } else {
586 size = o;
587 result = malloc((size + 1) * sizeof *result);
588 }
589 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200590
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200591 /* write again into the beginning of the buffer */
592 o = 0;
593 }
Radek Krejci709fee62015-04-15 13:56:19 +0200594
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200595 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
596 /* CDSect */
597 if (!cdsect) {
598 cdsect = 1;
599 *len += 9;
600 }
601 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
602 *len += 3;
603 cdsect = 0;
604 o--; /* we don't write any data in this iteration */
605 } else {
606 buf[o] = data[*len];
607 (*len)++;
608 }
609 } else if (data[*len] == '&') {
610 (*len)++;
611 if (data[*len] != '#') {
612 /* entity reference - only predefined refs are supported */
613 if (!memcmp(&data[*len], "lt;", 3)) {
614 buf[o] = '<';
615 *len += 3;
616 } else if (!memcmp(&data[*len], "gt;", 3)) {
617 buf[o] = '>';
618 *len += 3;
619 } else if (!memcmp(&data[*len], "amp;", 4)) {
620 buf[o] = '&';
621 *len += 4;
622 } else if (!memcmp(&data[*len], "apos;", 5)) {
623 buf[o] = '\'';
624 *len += 5;
625 } else if (!memcmp(&data[*len], "quot;", 5)) {
626 buf[o] = '\"';
627 *len += 5;
628 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200629 LOGVAL(LYE_XML_INVAL, lineno, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200630 goto error;
631 }
632 } else {
633 /* character reference */
634 (*len)++;
635 if (isdigit(data[*len])) {
636 for (n = 0; isdigit(data[*len]); (*len)++) {
637 n = (10 * n) + (data[*len] - '0');
638 }
639 if (data[*len] != ';') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200640 LOGVAL(LYE_XML_INVAL, lineno, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200641 goto error;
642 }
643 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
644 for (n = 0; isxdigit(data[*len]); (*len)++) {
645 if (isdigit(data[*len])) {
646 r = (data[*len] - '0');
647 } else if (data[*len] > 'F') {
648 r = 10 + (data[*len] - 'a');
649 } else {
650 r = 10 + (data[*len] - 'A');
651 }
652 n = (16 * n) + r;
653 }
654 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200655 LOGVAL(LYE_XML_INVAL, lineno, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200656 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200657
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200658 }
Radek Krejci5449d472015-10-26 14:35:56 +0100659 r = pututf8(&buf[o], n, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200660 if (!r) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200661 LOGVAL(LYE_XML_INVAL, lineno, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200662 goto error;
663 }
664 o += r - 1; /* o is ++ in for loop */
665 (*len)++;
666 }
667 } else {
668 buf[o] = data[*len];
669 COUNTLINE(buf[o]);
670 (*len)++;
671 }
672 }
Radek Krejci02117302015-04-13 16:32:44 +0200673
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200674 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
675 /* ignore loop's end condition on beginning of CDSect */
676 goto loop;
677 }
Radek Krejci709fee62015-04-15 13:56:19 +0200678#undef BUFSIZE
679
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200680 if (o) {
681 if (result) {
682 size = size + o;
683 aux = realloc(result, size + 1);
684 result = aux;
685 } else {
686 size = o;
687 result = malloc((size + 1) * sizeof *result);
688 }
689 memcpy(&result[size - o], buf, o);
690 }
691 if (result) {
692 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200693 } else {
694 size = 0;
695 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200696 }
Radek Krejci02117302015-04-13 16:32:44 +0200697
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200698 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200699
700error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200701 free(result);
702 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200703}
704
Michal Vasko0d343d12015-08-24 14:57:36 +0200705/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200706static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200707parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200708{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200709 const char *c = data, *start, *delim;
710 char prefix[32];
711 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200712 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200713 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200714
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200715 /* check if it is attribute or namespace */
716 if (!memcmp(c, "xmlns", 5)) {
717 /* namespace */
718 attr = calloc(1, sizeof (struct lyxml_ns));
719 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200720 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200721 c += 5;
722 if (*c != ':') {
723 /* default namespace, prefix will be empty */
724 goto equal;
725 }
726 c++; /* go after ':' to the prefix value */
727 } else {
728 /* attribute */
729 attr = calloc(1, sizeof *attr);
730 attr->type = LYXML_ATTR_STD;
731 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200732
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200733 /* process name part of the attribute */
734 start = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200735 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200736 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200737 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200738 free(attr);
739 return NULL;
740 }
741 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200742 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200743 while (is_xmlnamechar(uc)) {
744 if (attr->type == LYXML_ATTR_STD && *c == ':') {
745 /* attribute in a namespace */
746 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200747
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200748 /* look for the prefix in namespaces */
749 memcpy(prefix, data, c - data);
750 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200751 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200752 }
753 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200754 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200755 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200756
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200757 /* store the name */
758 size = c - start;
759 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200760
761equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200762 /* check Eq mark that can be surrounded by whitespaces */
763 ign_xmlws(c);
764 if (*c != '=') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200765 LOGVAL(LYE_XML_INVAL, lineno, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200766 goto error;
767 }
768 c++;
769 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200770
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200771 /* process value part of the attribute */
772 if (!*c || (*c != '"' && *c != '\'')) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200773 LOGVAL(LYE_XML_INVAL, lineno, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200774 goto error;
775 }
776 delim = c;
777 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
778 if (ly_errno) {
779 goto error;
780 }
Radek Krejci02117302015-04-13 16:32:44 +0200781
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200782 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200783
784 /* put attribute into the parent's attributes list */
785 if (parent->attr) {
786 /* go to the end of the list */
787 for (a = parent->attr; a->next; a = a->next);
788 /* and append new attribute */
789 a->next = attr;
790 } else {
791 /* add the first attribute in the list */
792 parent->attr = attr;
793 }
794
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200795 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200796
797error:
Radek Krejci00249f22015-07-07 13:43:28 +0200798 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200799 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200800}
801
Michal Vasko0d343d12015-08-24 14:57:36 +0200802/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200803static struct lyxml_elem *
804parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200805{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200806 const char *c = data, *start, *e;
807 const char *lws; /* leading white space for handling mixed content */
808 int uc;
809 char *str;
810 char prefix[32] = { 0 };
811 unsigned int prefix_len = 0;
812 struct lyxml_elem *elem = NULL, *child;
813 struct lyxml_attr *attr;
814 unsigned int size;
815 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200816
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200817 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200818
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200819 if (*c != '<') {
820 return NULL;
821 }
Radek Krejci02117302015-04-13 16:32:44 +0200822
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200823 /* locate element name */
824 c++;
825 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200826
Michal Vasko076588e2015-10-06 15:39:57 +0200827 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200828 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200829 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200830 return NULL;
831 }
832 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200833 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200834 while (is_xmlnamechar(uc)) {
835 if (*e == ':') {
836 if (prefix_len) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200837 LOGVAL(LYE_XML_INVAL, lineno, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200838 goto error;
839 }
840 /* element in a namespace */
841 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200842
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200843 /* look for the prefix in namespaces */
844 memcpy(prefix, c, prefix_len = e - c);
845 prefix[prefix_len] = '\0';
846 c = start;
847 }
848 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200849 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200850 }
851 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200852 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200853 return NULL;
854 }
Radek Krejci02117302015-04-13 16:32:44 +0200855
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200856 /* allocate element structure */
857 elem = calloc(1, sizeof *elem);
Radek Krejci3045cf32015-05-28 10:58:52 +0200858#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200859 elem->line = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +0200860#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200861 elem->next = NULL;
862 elem->prev = elem;
863 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200864 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200865 }
Radek Krejci02117302015-04-13 16:32:44 +0200866
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200867 /* store the name into the element structure */
868 elem->name = lydict_insert(ctx, c, e - c);
869 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200870
871process:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200872 ly_errno = 0;
873 ign_xmlws(c);
874 if (!memcmp("/>", c, 2)) {
875 /* we are done, it was EmptyElemTag */
876 c += 2;
877 closed_flag = 1;
878 } else if (*c == '>') {
879 /* process element content */
880 c++;
881 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200882
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200883 while (*c) {
884 if (!memcmp(c, "</", 2)) {
885 if (lws && !elem->child) {
886 /* leading white spaces were actually content */
887 goto store_content;
888 }
Radek Krejci02117302015-04-13 16:32:44 +0200889
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200890 /* Etag */
891 c += 2;
892 /* get name and check it */
893 e = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200894 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200895 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200896 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200897 goto error;
898 }
899 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200900 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200901 while (is_xmlnamechar(uc)) {
902 if (*e == ':') {
903 /* element in a namespace */
904 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200905
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200906 /* look for the prefix in namespaces */
907 if (memcmp(prefix, c, e - c)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200908 LOGVAL(LYE_SPEC, lineno,
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200909 "Mixed opening (%s) and closing element tags (different namespaces).", elem->name);
910 goto error;
911 }
912 c = start;
913 }
914 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200915 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200916 }
917 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200918 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200919 goto error;
920 }
Radek Krejci02117302015-04-13 16:32:44 +0200921
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200922 /* check that it corresponds to opening tag */
923 size = e - c;
924 str = malloc((size + 1) * sizeof *str);
925 memcpy(str, c, e - c);
926 str[e - c] = '\0';
927 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200928 LOGVAL(LYE_SPEC, lineno, "Mixed opening (%s) and closing (%s) element tags.", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200929 free(str);
930 goto error;
931 }
932 free(str);
933 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200934
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200935 ign_xmlws(c);
936 if (*c != '>') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200937 LOGVAL(LYE_SPEC, lineno, "Close element tag \"%s\" contain additional data.", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200938 goto error;
939 }
940 c++;
941 closed_flag = 1;
942 break;
Radek Krejci02117302015-04-13 16:32:44 +0200943
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200944 } else if (!memcmp(c, "<?", 2)) {
945 if (lws) {
946 /* leading white spaces were only formatting */
947 lws = NULL;
948 }
949 /* PI - ignore it */
950 c += 2;
951 if (parse_ignore(c, "?>", &size)) {
952 goto error;
953 }
954 c += size;
955 } else if (!memcmp(c, "<!--", 4)) {
956 if (lws) {
957 /* leading white spaces were only formatting */
958 lws = NULL;
959 }
960 /* Comment - ignore it */
961 c += 4;
962 if (parse_ignore(c, "-->", &size)) {
963 goto error;
964 }
965 c += size;
966 } else if (!memcmp(c, "<![CDATA[", 9)) {
967 /* CDSect */
968 goto store_content;
969 } else if (*c == '<') {
970 if (lws) {
971 if (elem->flags & LYXML_ELEM_MIXED) {
972 /* we have a mixed content */
973 goto store_content;
974 } else {
975 /* leading white spaces were only formatting */
976 lws = NULL;
977 }
978 }
979 if (elem->content) {
980 /* we have a mixed content */
981 child = calloc(1, sizeof *child);
982 child->content = elem->content;
983 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +0200984 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200985 elem->flags |= LYXML_ELEM_MIXED;
986 }
987 child = parse_elem(ctx, c, &size, elem);
988 if (!child) {
989 goto error;
990 }
991 c += size; /* move after processed child element */
992 } else if (is_xmlws(*c)) {
993 lws = c;
Radek Krejci3045cf32015-05-28 10:58:52 +0200994#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200995 lws_lineno = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +0200996#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200997 ign_xmlws(c);
998 } else {
Radek Krejci02117302015-04-13 16:32:44 +0200999store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001000 /* store text content */
1001 if (lws) {
1002 /* process content including the leading white spaces */
1003 c = lws;
Radek Krejci3045cf32015-05-28 10:58:52 +02001004#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001005 lineno = lws_lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001006#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001007 lws = NULL;
1008 }
1009 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1010 if (ly_errno) {
1011 goto error;
1012 }
1013 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001014
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001015 if (elem->child) {
1016 /* we have a mixed content */
1017 child = calloc(1, sizeof *child);
1018 child->content = elem->content;
1019 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001020 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001021 elem->flags |= LYXML_ELEM_MIXED;
1022 }
1023 }
1024 }
1025 } else {
1026 /* process attribute */
1027 attr = parse_attr(ctx, c, &size, elem);
1028 if (!attr) {
1029 goto error;
1030 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001031 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001032
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001033 /* check namespace */
1034 if (attr->type == LYXML_ATTR_NS) {
1035 if (!prefix[0] && !attr->name) {
1036 if (attr->value) {
1037 /* default prefix */
1038 elem->ns = (struct lyxml_ns *)attr;
1039 } else {
1040 /* xmlns="" -> no namespace */
1041 nons_flag = 1;
1042 }
1043 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1044 /* matching namespace with prefix */
1045 elem->ns = (struct lyxml_ns *)attr;
1046 }
1047 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001048
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001049 /* go back to finish element processing */
1050 goto process;
1051 }
Radek Krejci02117302015-04-13 16:32:44 +02001052
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001053 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001054
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001055 if (!closed_flag) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001056 LOGVAL(LYE_XML_MISS, lineno, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001057 goto error;
1058 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001059
Radek Krejci78a230a2015-07-07 17:04:40 +02001060 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001061 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001062 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001063
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001064 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001065
1066error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001067 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001068
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001069 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001070}
1071
Michal Vasko0d343d12015-08-24 14:57:36 +02001072/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001073API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001074lyxml_read(struct ly_ctx *ctx, const char *data, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001075{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001076 const char *c = data;
1077 unsigned int len;
1078 struct lyxml_elem *root = NULL;
Radek Krejci02117302015-04-13 16:32:44 +02001079
Radek Krejci3045cf32015-05-28 10:58:52 +02001080#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001081 /* TODO: threads support */
1082 lineno = 1;
Radek Krejci3045cf32015-05-28 10:58:52 +02001083#endif
1084
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001085 /* process document */
1086 while (*c) {
1087 if (is_xmlws(*c)) {
1088 /* skip whitespaces */
1089 ign_xmlws(c);
1090 } else if (!memcmp(c, "<?", 2)) {
1091 /* XMLDecl or PI - ignore it */
1092 c += 2;
1093 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001094 return NULL;
1095 }
1096 c += len;
1097 } else if (!memcmp(c, "<!--", 4)) {
1098 /* Comment - ignore it */
1099 c += 2;
1100 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001101 return NULL;
1102 }
1103 c += len;
1104 } else if (!memcmp(c, "<!", 2)) {
1105 /* DOCTYPE */
1106 /* TODO - standalone ignore counting < and > */
1107 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1108 return NULL;
1109 } else if (*c == '<') {
1110 /* element - process it in next loop to strictly follow XML
1111 * format
1112 */
1113 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001114 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001115 LOGVAL(LYE_XML_INCHAR, lineno, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001116 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001117 }
1118 }
Radek Krejci02117302015-04-13 16:32:44 +02001119
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001120 root = parse_elem(ctx, c, &len, NULL);
1121 if (!root) {
1122 return NULL;
1123 }
1124 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001125
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001126 /* ignore the rest of document where can be comments, PIs and whitespaces,
1127 * note that we are not detecting syntax errors in these parts
1128 */
1129 ign_xmlws(c);
1130 if (*c) {
1131 LOGWRN("There are some not parsed data:\n%s", c);
1132 }
Radek Krejci02117302015-04-13 16:32:44 +02001133
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001134 return root;
Radek Krejci02117302015-04-13 16:32:44 +02001135}
1136
Radek Krejcic6704c82015-10-06 11:12:45 +02001137API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001138lyxml_read_file(struct ly_ctx *ctx, const char *filename, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001139{
Pavol Vicanb2570c12015-11-12 13:50:20 +01001140 struct lyxml_elem *elem=NULL;
1141 struct stat sb;
1142 int fd;
1143 char *addr;
1144
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001145 if (!filename || !ctx) {
1146 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1147 return NULL;
1148 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001149
Pavol Vicanb2570c12015-11-12 13:50:20 +01001150 fd = open(filename, O_RDONLY);
1151 if (fd == -1) {
1152 LOGERR(LY_EINVAL,"Opening file \"%s\" failed.",filename);
1153 return NULL;
1154 }
1155 if (fstat(fd, &sb) == -1) {
1156 LOGERR(LY_EINVAL, "Unable to get file \"%s\" information.\n", filename);
1157 goto error;
1158 }
1159 if (!S_ISREG(sb.st_mode)) {
1160 fprintf(stderr, "File \"%s\" not a file.\n",filename);
1161 goto error;
1162 }
1163 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1164 if (addr == MAP_FAILED) {
1165 LOGERR(LY_EMEM,"Map file into memory failed (%s()).",__func__);
1166 goto error;
1167 }
1168 elem=lyxml_read(ctx,addr,0);
1169 munmap(addr, sb.st_size);
1170 return elem;
1171
1172error:
1173 if (fd!=-1) close(fd);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001174 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001175}
Radek Krejci02117302015-04-13 16:32:44 +02001176
Michal Vasko5db027d2015-10-09 14:38:50 +02001177int
1178lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001179{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001180 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001181
Michal Vasko5db027d2015-10-09 14:38:50 +02001182 if (!text) {
1183 return 0;
1184 }
1185
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001186 for (i = n = 0; text[i]; i++) {
1187 switch (text[i]) {
1188 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001189 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001190 break;
1191 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001192 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001193 break;
1194 case '>':
1195 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001196 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001197 break;
1198 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001199 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001200 n++;
1201 }
1202 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001203
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001204 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001205}
1206
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001207static int
Michal Vasko1e62a092015-12-01 12:27:20 +01001208dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001209{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001210 int size = 0;
1211 struct lyxml_attr *a;
1212 struct lyxml_elem *child;
1213 const char *delim, *delim_outer;
1214 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001215
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001216 if (!e->name) {
1217 /* mixed content */
1218 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001219 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001220 } else {
1221 return 0;
1222 }
1223 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001224
Pavol Vican4171e512015-10-22 13:30:53 +02001225 delim = delim_outer = (options & LYXML_DUMP_FORMAT) ? "\n" : "";
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001226 indent = 2 * level;
1227 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1228 delim = "";
1229 }
1230 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1231 delim_outer = "";
1232 indent = 0;
1233 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001234
Radek Krejci528b8ce2015-10-22 16:25:04 +02001235 if (!(options & (LYXML_DUMP_OPEN|LYXML_DUMP_CLOSE|LYXML_DUMP_ATTRS)) || (options & LYXML_DUMP_OPEN)) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001236 /* opening tag */
1237 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001238 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001239 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001240 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001241 }
1242 } else if (options & LYXML_DUMP_CLOSE) {
1243 indent = 0;
1244 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001245 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001246
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001247 /* attributes */
1248 for (a = e->attr; a; a = a->next) {
1249 if (a->type == LYXML_ATTR_NS) {
1250 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001251 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001252 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001253 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001254 }
1255 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001256 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001257 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001258 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001259 }
1260 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001261
Radek Krejcic6704c82015-10-06 11:12:45 +02001262 /* apply options */
Pavol Vican0308d0c2015-10-22 15:39:23 +02001263 if ((options & LYXML_DUMP_CLOSE) && (options & LYXML_DUMP_OPEN)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001264 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001265 return size;
1266 } else if (options & LYXML_DUMP_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001267 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001268 return ++size;
1269 } else if (options & LYXML_DUMP_ATTRS) {
1270 return size;
1271 }
1272
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001273 if (!e->child && !e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001274 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001275 return size;
1276 } else if (e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001277 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001278 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001279
Michal Vasko5db027d2015-10-09 14:38:50 +02001280 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001281
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001282 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001283 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001284 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001285 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001286 }
1287 return size;
1288 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001289 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001290 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001291
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001292 /* go recursively */
1293 LY_TREE_FOR(e->child, child) {
Pavol Vicanbe7eef52015-10-22 14:07:48 +02001294 if (options & LYXML_DUMP_FORMAT) {
1295 size += dump_elem(out, child, level + 1, LYXML_DUMP_FORMAT);
1296 } else {
1297 size += dump_elem(out, child, level, 0);
1298 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001299 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001300
Radek Krejcic6704c82015-10-06 11:12:45 +02001301close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001302 /* closing tag */
1303 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001304 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001305 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001306 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001307 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001308
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001310}
1311
Radek Krejcic6704c82015-10-06 11:12:45 +02001312API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001313lyxml_dump(FILE *stream, const struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001314{
Radek Krejci5248f132015-10-09 10:34:25 +02001315 struct lyout out;
1316
1317 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001318 return 0;
1319 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001320
Radek Krejci5248f132015-10-09 10:34:25 +02001321 out.type = LYOUT_STREAM;
1322 out.method.f = stream;
1323
1324 return dump_elem(&out, elem, 0, options);
1325}
1326
1327API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001328lyxml_dump_fd(int fd, const struct lyxml_elem *elem, int options)
Radek Krejci5248f132015-10-09 10:34:25 +02001329{
1330 struct lyout out;
1331
1332 if (fd < 0 || !elem) {
1333 return 0;
1334 }
1335
1336 out.type = LYOUT_FD;
1337 out.method.fd = fd;
1338
1339 return dump_elem(&out, elem, 0, options);
Radek Krejcif0023a92015-04-20 20:51:39 +02001340}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001341
1342API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001343lyxml_dump_mem(char **strp, const struct lyxml_elem *elem, int options)
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001344{
1345 struct lyout out;
1346 int r;
1347
1348 if (!strp || !elem) {
1349 return 0;
1350 }
1351
1352 out.type = LYOUT_MEMORY;
1353 out.method.mem.buf = NULL;
1354 out.method.mem.len = 0;
1355 out.method.mem.size = 0;
1356
1357 r = dump_elem(&out, elem, 0, options);
1358
1359 *strp = out.method.mem.buf;
1360 return r;
1361}
1362
1363API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001364lyxml_dump_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg, const struct lyxml_elem *elem, int options)
Radek Krejci6140e4e2015-10-09 15:50:55 +02001365{
1366 struct lyout out;
1367
1368 if (!writeclb || !elem) {
1369 return 0;
1370 }
1371
1372 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001373 out.method.clb.f = writeclb;
1374 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001375
1376 return dump_elem(&out, elem, 0, options);
1377}