blob: c41aa5fb4675924ca8a84ae137d7673f862f400a [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
Michal Vasko345da0a2015-12-02 10:35:55 +0100311API void
312lyxml_unlink(struct ly_ctx *ctx, struct lyxml_elem *elem)
313{
314 if (!elem) {
315 return;
316 }
317
318 lyxml_unlink_elem(ctx, elem, 1);
319}
320
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200321void
Radek Krejci00249f22015-07-07 13:43:28 +0200322lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200323{
Radek Krejci00249f22015-07-07 13:43:28 +0200324 struct lyxml_attr *aiter, *aprev;
325
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200326 if (!attr) {
327 return;
328 }
Radek Krejci02117302015-04-13 16:32:44 +0200329
Radek Krejci00249f22015-07-07 13:43:28 +0200330 if (parent) {
331 /* unlink attribute from the parent's list of attributes */
332 aprev = NULL;
333 for (aiter = parent->attr; aiter; aiter = aiter->next) {
334 if (aiter == attr) {
335 break;
336 }
337 aprev = aiter;
338 }
339 if (!aiter) {
340 /* attribute to remove not found */
341 return;
342 }
343
344 if (!aprev) {
345 /* attribute is first in parent's list of attributes */
346 parent->attr = attr->next;
347 } else {
348 /* reconnect previous attribute to the next */
349 aprev->next = attr->next;
350 }
351 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200352 lydict_remove(ctx, attr->name);
353 lydict_remove(ctx, attr->value);
354 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200355}
356
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200357void
358lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200359{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200360 struct lyxml_attr *a, *next;
361 if (!elem || !elem->attr) {
362 return;
363 }
Radek Krejci02117302015-04-13 16:32:44 +0200364
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200365 a = elem->attr;
366 do {
367 next = a->next;
Radek Krejci02117302015-04-13 16:32:44 +0200368
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200369 lydict_remove(ctx, a->name);
370 lydict_remove(ctx, a->value);
371 free(a);
Radek Krejci02117302015-04-13 16:32:44 +0200372
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200373 a = next;
374 } while (a);
Radek Krejci02117302015-04-13 16:32:44 +0200375}
376
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200377static void
Michal Vasko272e42f2015-12-02 12:20:37 +0100378lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200379{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200380 struct lyxml_elem *e, *next;
Radek Krejci02117302015-04-13 16:32:44 +0200381
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200382 if (!elem) {
383 return;
384 }
Radek Krejci02117302015-04-13 16:32:44 +0200385
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200386 lyxml_free_attrs(ctx, elem);
387 LY_TREE_FOR_SAFE(elem->child, next, e) {
Michal Vasko272e42f2015-12-02 12:20:37 +0100388 lyxml_free_elem(ctx, e);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200389 }
390 lydict_remove(ctx, elem->name);
391 lydict_remove(ctx, elem->content);
392 free(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200393}
394
Radek Krejcic6704c82015-10-06 11:12:45 +0200395API void
Michal Vasko345da0a2015-12-02 10:35:55 +0100396lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200397{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200398 if (!elem) {
399 return;
400 }
Radek Krejci02117302015-04-13 16:32:44 +0200401
Michal Vasko61f7ccb2015-10-23 10:15:08 +0200402 lyxml_unlink_elem(ctx, elem, 2);
Michal Vasko272e42f2015-12-02 12:20:37 +0100403 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200404}
405
Michal Vasko88c29542015-11-27 14:57:53 +0100406API const char *
Michal Vasko1e62a092015-12-01 12:27:20 +0100407lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200408{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200409 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200410
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200411 assert(elem);
412 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200413
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200414 for (a = elem->attr; a; a = a->next) {
415 if (a->type != LYXML_ATTR_STD) {
416 continue;
417 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200418
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200419 if (!strcmp(name, a->name)) {
420 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
421 return a->value;
422 }
423 }
424 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200425
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200426 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200427}
428
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200429int
Michal Vaskof8879c22015-08-21 09:07:36 +0200430lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200431{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200432 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200433
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200434 assert(parent);
435 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200436
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200437 /* (re)link element to parent */
438 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200439 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200440 }
441 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200442
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200443 /* link parent to element */
444 if (parent->child) {
445 e = parent->child;
446 elem->prev = e->prev;
447 elem->next = NULL;
448 elem->prev->next = elem;
449 e->prev = elem;
450 } else {
451 parent->child = elem;
452 elem->prev = elem;
453 elem->next = NULL;
454 }
Radek Krejci02117302015-04-13 16:32:44 +0200455
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200456 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200457}
458
Michal Vasko3b855722015-08-28 16:01:18 +0200459int
Michal Vasko076588e2015-10-06 15:39:57 +0200460lyxml_getutf8(const char *buf, unsigned int *read, unsigned int line)
Radek Krejci02117302015-04-13 16:32:44 +0200461{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200462 int c, aux;
463 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200464
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200465 c = buf[0];
466 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200467
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200468 /* buf is NULL terminated string, so 0 means EOF */
469 if (!c) {
Michal Vasko076588e2015-10-06 15:39:57 +0200470 LOGVAL(LYE_EOF, line);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200471 return 0;
472 }
473 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200474
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200475 /* process character byte(s) */
476 if ((c & 0xf8) == 0xf0) {
477 /* four bytes character */
478 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200479
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200480 c &= 0x07;
481 for (i = 1; i <= 3; i++) {
482 aux = buf[i];
483 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200484 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200485 return 0;
486 }
Radek Krejci02117302015-04-13 16:32:44 +0200487
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200488 c = (c << 6) | (aux & 0x3f);
489 }
Radek Krejci02117302015-04-13 16:32:44 +0200490
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200491 if (c < 0x1000 || c > 0x10ffff) {
Michal Vasko076588e2015-10-06 15:39:57 +0200492 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200493 return 0;
494 }
495 } else if ((c & 0xf0) == 0xe0) {
496 /* three bytes character */
497 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200498
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200499 c &= 0x0f;
500 for (i = 1; i <= 2; i++) {
501 aux = buf[i];
502 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200503 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200504 return 0;
505 }
Radek Krejci02117302015-04-13 16:32:44 +0200506
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200507 c = (c << 6) | (aux & 0x3f);
508 }
Radek Krejci02117302015-04-13 16:32:44 +0200509
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200510 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
Michal Vasko076588e2015-10-06 15:39:57 +0200511 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200512 return 0;
513 }
514 } else if ((c & 0xe0) == 0xc0) {
515 /* two bytes character */
516 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200517
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200518 aux = buf[1];
519 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200520 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200521 return 0;
522 }
523 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200524
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200525 if (c < 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200526 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200527 return 0;
528 }
529 } else if (!(c & 0x80)) {
530 /* one byte character */
531 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
532 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200533 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200534 return 0;
535 }
536 } else {
537 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200538 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200539 return 0;
540 }
Radek Krejci02117302015-04-13 16:32:44 +0200541
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200542 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200543}
544
Michal Vasko0d343d12015-08-24 14:57:36 +0200545/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200546static int
547parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200548{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200549 unsigned int slen;
550 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200551
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200552 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200553
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200554 while (*c && memcmp(c, endstr, slen)) {
555 COUNTLINE(*c);
556 c++;
557 }
558 if (!*c) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200559 LOGVAL(LYE_XML_MISS, lineno, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200560 return EXIT_FAILURE;
561 }
562 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200563
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200564 *len = c - data;
565 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200566}
567
Michal Vasko0d343d12015-08-24 14:57:36 +0200568/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200569static char *
570parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200571{
Radek Krejci709fee62015-04-15 13:56:19 +0200572#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200573
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200574 char buf[BUFSIZE];
575 char *result = NULL, *aux;
576 unsigned int r;
577 int o, size = 0;
578 int cdsect = 0;
579 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200580
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200581 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
582 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200583 LOGVAL(LYE_XML_INVAL, lineno, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200584 goto error;
585 }
Radek Krejci709fee62015-04-15 13:56:19 +0200586
Radek Krejcia4a84062015-04-16 13:00:10 +0200587loop:
588
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200589 if (o > BUFSIZE - 3) {
590 /* add buffer into the result */
591 if (result) {
592 size = size + o;
593 aux = realloc(result, size + 1);
594 result = aux;
595 } else {
596 size = o;
597 result = malloc((size + 1) * sizeof *result);
598 }
599 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200600
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200601 /* write again into the beginning of the buffer */
602 o = 0;
603 }
Radek Krejci709fee62015-04-15 13:56:19 +0200604
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200605 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
606 /* CDSect */
607 if (!cdsect) {
608 cdsect = 1;
609 *len += 9;
610 }
611 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
612 *len += 3;
613 cdsect = 0;
614 o--; /* we don't write any data in this iteration */
615 } else {
616 buf[o] = data[*len];
617 (*len)++;
618 }
619 } else if (data[*len] == '&') {
620 (*len)++;
621 if (data[*len] != '#') {
622 /* entity reference - only predefined refs are supported */
623 if (!memcmp(&data[*len], "lt;", 3)) {
624 buf[o] = '<';
625 *len += 3;
626 } else if (!memcmp(&data[*len], "gt;", 3)) {
627 buf[o] = '>';
628 *len += 3;
629 } else if (!memcmp(&data[*len], "amp;", 4)) {
630 buf[o] = '&';
631 *len += 4;
632 } else if (!memcmp(&data[*len], "apos;", 5)) {
633 buf[o] = '\'';
634 *len += 5;
635 } else if (!memcmp(&data[*len], "quot;", 5)) {
636 buf[o] = '\"';
637 *len += 5;
638 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200639 LOGVAL(LYE_XML_INVAL, lineno, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200640 goto error;
641 }
642 } else {
643 /* character reference */
644 (*len)++;
645 if (isdigit(data[*len])) {
646 for (n = 0; isdigit(data[*len]); (*len)++) {
647 n = (10 * n) + (data[*len] - '0');
648 }
649 if (data[*len] != ';') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200650 LOGVAL(LYE_XML_INVAL, lineno, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200651 goto error;
652 }
653 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
654 for (n = 0; isxdigit(data[*len]); (*len)++) {
655 if (isdigit(data[*len])) {
656 r = (data[*len] - '0');
657 } else if (data[*len] > 'F') {
658 r = 10 + (data[*len] - 'a');
659 } else {
660 r = 10 + (data[*len] - 'A');
661 }
662 n = (16 * n) + r;
663 }
664 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200665 LOGVAL(LYE_XML_INVAL, lineno, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200666 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200667
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200668 }
Radek Krejci5449d472015-10-26 14:35:56 +0100669 r = pututf8(&buf[o], n, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200670 if (!r) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200671 LOGVAL(LYE_XML_INVAL, lineno, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200672 goto error;
673 }
674 o += r - 1; /* o is ++ in for loop */
675 (*len)++;
676 }
677 } else {
678 buf[o] = data[*len];
679 COUNTLINE(buf[o]);
680 (*len)++;
681 }
682 }
Radek Krejci02117302015-04-13 16:32:44 +0200683
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200684 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
685 /* ignore loop's end condition on beginning of CDSect */
686 goto loop;
687 }
Radek Krejci709fee62015-04-15 13:56:19 +0200688#undef BUFSIZE
689
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200690 if (o) {
691 if (result) {
692 size = size + o;
693 aux = realloc(result, size + 1);
694 result = aux;
695 } else {
696 size = o;
697 result = malloc((size + 1) * sizeof *result);
698 }
699 memcpy(&result[size - o], buf, o);
700 }
701 if (result) {
702 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200703 } else {
704 size = 0;
705 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200706 }
Radek Krejci02117302015-04-13 16:32:44 +0200707
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200708 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200709
710error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200711 free(result);
712 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200713}
714
Michal Vasko0d343d12015-08-24 14:57:36 +0200715/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200716static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200717parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200718{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200719 const char *c = data, *start, *delim;
720 char prefix[32];
721 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200722 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200723 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200724
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200725 /* check if it is attribute or namespace */
726 if (!memcmp(c, "xmlns", 5)) {
727 /* namespace */
728 attr = calloc(1, sizeof (struct lyxml_ns));
729 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200730 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200731 c += 5;
732 if (*c != ':') {
733 /* default namespace, prefix will be empty */
734 goto equal;
735 }
736 c++; /* go after ':' to the prefix value */
737 } else {
738 /* attribute */
739 attr = calloc(1, sizeof *attr);
740 attr->type = LYXML_ATTR_STD;
741 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200742
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200743 /* process name part of the attribute */
744 start = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200745 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200746 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200747 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200748 free(attr);
749 return NULL;
750 }
751 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200752 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200753 while (is_xmlnamechar(uc)) {
754 if (attr->type == LYXML_ATTR_STD && *c == ':') {
755 /* attribute in a namespace */
756 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200757
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200758 /* look for the prefix in namespaces */
759 memcpy(prefix, data, c - data);
760 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200761 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200762 }
763 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200764 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200765 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200766
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200767 /* store the name */
768 size = c - start;
769 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200770
771equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200772 /* check Eq mark that can be surrounded by whitespaces */
773 ign_xmlws(c);
774 if (*c != '=') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200775 LOGVAL(LYE_XML_INVAL, lineno, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200776 goto error;
777 }
778 c++;
779 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200780
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200781 /* process value part of the attribute */
782 if (!*c || (*c != '"' && *c != '\'')) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200783 LOGVAL(LYE_XML_INVAL, lineno, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200784 goto error;
785 }
786 delim = c;
787 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
788 if (ly_errno) {
789 goto error;
790 }
Radek Krejci02117302015-04-13 16:32:44 +0200791
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200792 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200793
794 /* put attribute into the parent's attributes list */
795 if (parent->attr) {
796 /* go to the end of the list */
797 for (a = parent->attr; a->next; a = a->next);
798 /* and append new attribute */
799 a->next = attr;
800 } else {
801 /* add the first attribute in the list */
802 parent->attr = attr;
803 }
804
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200805 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200806
807error:
Radek Krejci00249f22015-07-07 13:43:28 +0200808 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200809 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200810}
811
Michal Vasko0d343d12015-08-24 14:57:36 +0200812/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200813static struct lyxml_elem *
814parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200815{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200816 const char *c = data, *start, *e;
817 const char *lws; /* leading white space for handling mixed content */
818 int uc;
819 char *str;
820 char prefix[32] = { 0 };
821 unsigned int prefix_len = 0;
822 struct lyxml_elem *elem = NULL, *child;
823 struct lyxml_attr *attr;
824 unsigned int size;
825 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200826
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200827 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200828
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200829 if (*c != '<') {
830 return NULL;
831 }
Radek Krejci02117302015-04-13 16:32:44 +0200832
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200833 /* locate element name */
834 c++;
835 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200836
Michal Vasko076588e2015-10-06 15:39:57 +0200837 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200838 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200839 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200840 return NULL;
841 }
842 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200843 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200844 while (is_xmlnamechar(uc)) {
845 if (*e == ':') {
846 if (prefix_len) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200847 LOGVAL(LYE_XML_INVAL, lineno, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200848 goto error;
849 }
850 /* element in a namespace */
851 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200852
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200853 /* look for the prefix in namespaces */
854 memcpy(prefix, c, prefix_len = e - c);
855 prefix[prefix_len] = '\0';
856 c = start;
857 }
858 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200859 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200860 }
861 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200862 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200863 return NULL;
864 }
Radek Krejci02117302015-04-13 16:32:44 +0200865
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200866 /* allocate element structure */
867 elem = calloc(1, sizeof *elem);
Radek Krejci3045cf32015-05-28 10:58:52 +0200868#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200869 elem->line = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +0200870#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200871 elem->next = NULL;
872 elem->prev = elem;
873 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200874 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200875 }
Radek Krejci02117302015-04-13 16:32:44 +0200876
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200877 /* store the name into the element structure */
878 elem->name = lydict_insert(ctx, c, e - c);
879 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200880
881process:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200882 ly_errno = 0;
883 ign_xmlws(c);
884 if (!memcmp("/>", c, 2)) {
885 /* we are done, it was EmptyElemTag */
886 c += 2;
887 closed_flag = 1;
888 } else if (*c == '>') {
889 /* process element content */
890 c++;
891 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200892
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200893 while (*c) {
894 if (!memcmp(c, "</", 2)) {
895 if (lws && !elem->child) {
896 /* leading white spaces were actually content */
897 goto store_content;
898 }
Radek Krejci02117302015-04-13 16:32:44 +0200899
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200900 /* Etag */
901 c += 2;
902 /* get name and check it */
903 e = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200904 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200905 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200906 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200907 goto error;
908 }
909 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200910 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200911 while (is_xmlnamechar(uc)) {
912 if (*e == ':') {
913 /* element in a namespace */
914 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200915
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200916 /* look for the prefix in namespaces */
917 if (memcmp(prefix, c, e - c)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200918 LOGVAL(LYE_SPEC, lineno,
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200919 "Mixed opening (%s) and closing element tags (different namespaces).", elem->name);
920 goto error;
921 }
922 c = start;
923 }
924 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200925 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200926 }
927 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200928 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200929 goto error;
930 }
Radek Krejci02117302015-04-13 16:32:44 +0200931
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200932 /* check that it corresponds to opening tag */
933 size = e - c;
934 str = malloc((size + 1) * sizeof *str);
935 memcpy(str, c, e - c);
936 str[e - c] = '\0';
937 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200938 LOGVAL(LYE_SPEC, lineno, "Mixed opening (%s) and closing (%s) element tags.", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200939 free(str);
940 goto error;
941 }
942 free(str);
943 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200944
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200945 ign_xmlws(c);
946 if (*c != '>') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200947 LOGVAL(LYE_SPEC, lineno, "Close element tag \"%s\" contain additional data.", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200948 goto error;
949 }
950 c++;
951 closed_flag = 1;
952 break;
Radek Krejci02117302015-04-13 16:32:44 +0200953
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200954 } else if (!memcmp(c, "<?", 2)) {
955 if (lws) {
956 /* leading white spaces were only formatting */
957 lws = NULL;
958 }
959 /* PI - ignore it */
960 c += 2;
961 if (parse_ignore(c, "?>", &size)) {
962 goto error;
963 }
964 c += size;
965 } else if (!memcmp(c, "<!--", 4)) {
966 if (lws) {
967 /* leading white spaces were only formatting */
968 lws = NULL;
969 }
970 /* Comment - ignore it */
971 c += 4;
972 if (parse_ignore(c, "-->", &size)) {
973 goto error;
974 }
975 c += size;
976 } else if (!memcmp(c, "<![CDATA[", 9)) {
977 /* CDSect */
978 goto store_content;
979 } else if (*c == '<') {
980 if (lws) {
981 if (elem->flags & LYXML_ELEM_MIXED) {
982 /* we have a mixed content */
983 goto store_content;
984 } else {
985 /* leading white spaces were only formatting */
986 lws = NULL;
987 }
988 }
989 if (elem->content) {
990 /* we have a mixed content */
991 child = calloc(1, sizeof *child);
992 child->content = elem->content;
993 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +0200994 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200995 elem->flags |= LYXML_ELEM_MIXED;
996 }
997 child = parse_elem(ctx, c, &size, elem);
998 if (!child) {
999 goto error;
1000 }
1001 c += size; /* move after processed child element */
1002 } else if (is_xmlws(*c)) {
1003 lws = c;
Radek Krejci3045cf32015-05-28 10:58:52 +02001004#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001005 lws_lineno = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001006#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001007 ign_xmlws(c);
1008 } else {
Radek Krejci02117302015-04-13 16:32:44 +02001009store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001010 /* store text content */
1011 if (lws) {
1012 /* process content including the leading white spaces */
1013 c = lws;
Radek Krejci3045cf32015-05-28 10:58:52 +02001014#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001015 lineno = lws_lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001016#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001017 lws = NULL;
1018 }
1019 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1020 if (ly_errno) {
1021 goto error;
1022 }
1023 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001024
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001025 if (elem->child) {
1026 /* we have a mixed content */
1027 child = calloc(1, sizeof *child);
1028 child->content = elem->content;
1029 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001030 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001031 elem->flags |= LYXML_ELEM_MIXED;
1032 }
1033 }
1034 }
1035 } else {
1036 /* process attribute */
1037 attr = parse_attr(ctx, c, &size, elem);
1038 if (!attr) {
1039 goto error;
1040 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001041 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001042
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001043 /* check namespace */
1044 if (attr->type == LYXML_ATTR_NS) {
1045 if (!prefix[0] && !attr->name) {
1046 if (attr->value) {
1047 /* default prefix */
1048 elem->ns = (struct lyxml_ns *)attr;
1049 } else {
1050 /* xmlns="" -> no namespace */
1051 nons_flag = 1;
1052 }
1053 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1054 /* matching namespace with prefix */
1055 elem->ns = (struct lyxml_ns *)attr;
1056 }
1057 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001058
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001059 /* go back to finish element processing */
1060 goto process;
1061 }
Radek Krejci02117302015-04-13 16:32:44 +02001062
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001063 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001064
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001065 if (!closed_flag) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001066 LOGVAL(LYE_XML_MISS, lineno, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001067 goto error;
1068 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001069
Radek Krejci78a230a2015-07-07 17:04:40 +02001070 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001071 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001072 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001073
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001074 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001075
1076error:
Michal Vasko345da0a2015-12-02 10:35:55 +01001077 lyxml_free(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001078
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001079 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001080}
1081
Michal Vasko0d343d12015-08-24 14:57:36 +02001082/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001083API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001084lyxml_read(struct ly_ctx *ctx, const char *data, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001085{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001086 const char *c = data;
1087 unsigned int len;
1088 struct lyxml_elem *root = NULL;
Radek Krejci02117302015-04-13 16:32:44 +02001089
Radek Krejci3045cf32015-05-28 10:58:52 +02001090#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001091 /* TODO: threads support */
1092 lineno = 1;
Radek Krejci3045cf32015-05-28 10:58:52 +02001093#endif
1094
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001095 /* process document */
1096 while (*c) {
1097 if (is_xmlws(*c)) {
1098 /* skip whitespaces */
1099 ign_xmlws(c);
1100 } else if (!memcmp(c, "<?", 2)) {
1101 /* XMLDecl or PI - ignore it */
1102 c += 2;
1103 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001104 return NULL;
1105 }
1106 c += len;
1107 } else if (!memcmp(c, "<!--", 4)) {
1108 /* Comment - ignore it */
1109 c += 2;
1110 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001111 return NULL;
1112 }
1113 c += len;
1114 } else if (!memcmp(c, "<!", 2)) {
1115 /* DOCTYPE */
1116 /* TODO - standalone ignore counting < and > */
1117 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1118 return NULL;
1119 } else if (*c == '<') {
1120 /* element - process it in next loop to strictly follow XML
1121 * format
1122 */
1123 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001124 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001125 LOGVAL(LYE_XML_INCHAR, lineno, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001126 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001127 }
1128 }
Radek Krejci02117302015-04-13 16:32:44 +02001129
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001130 root = parse_elem(ctx, c, &len, NULL);
1131 if (!root) {
1132 return NULL;
1133 }
1134 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001135
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001136 /* ignore the rest of document where can be comments, PIs and whitespaces,
1137 * note that we are not detecting syntax errors in these parts
1138 */
1139 ign_xmlws(c);
1140 if (*c) {
1141 LOGWRN("There are some not parsed data:\n%s", c);
1142 }
Radek Krejci02117302015-04-13 16:32:44 +02001143
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001144 return root;
Radek Krejci02117302015-04-13 16:32:44 +02001145}
1146
Radek Krejcic6704c82015-10-06 11:12:45 +02001147API struct lyxml_elem *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001148lyxml_read_file(struct ly_ctx *ctx, const char *filename, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001149{
Pavol Vicanb2570c12015-11-12 13:50:20 +01001150 struct lyxml_elem *elem=NULL;
1151 struct stat sb;
1152 int fd;
1153 char *addr;
1154
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001155 if (!filename || !ctx) {
1156 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1157 return NULL;
1158 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001159
Pavol Vicanb2570c12015-11-12 13:50:20 +01001160 fd = open(filename, O_RDONLY);
1161 if (fd == -1) {
1162 LOGERR(LY_EINVAL,"Opening file \"%s\" failed.",filename);
1163 return NULL;
1164 }
1165 if (fstat(fd, &sb) == -1) {
1166 LOGERR(LY_EINVAL, "Unable to get file \"%s\" information.\n", filename);
1167 goto error;
1168 }
1169 if (!S_ISREG(sb.st_mode)) {
1170 fprintf(stderr, "File \"%s\" not a file.\n",filename);
1171 goto error;
1172 }
1173 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1174 if (addr == MAP_FAILED) {
1175 LOGERR(LY_EMEM,"Map file into memory failed (%s()).",__func__);
1176 goto error;
1177 }
1178 elem=lyxml_read(ctx,addr,0);
1179 munmap(addr, sb.st_size);
1180 return elem;
1181
1182error:
1183 if (fd!=-1) close(fd);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001184 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001185}
Radek Krejci02117302015-04-13 16:32:44 +02001186
Michal Vasko5db027d2015-10-09 14:38:50 +02001187int
1188lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001189{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001190 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001191
Michal Vasko5db027d2015-10-09 14:38:50 +02001192 if (!text) {
1193 return 0;
1194 }
1195
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001196 for (i = n = 0; text[i]; i++) {
1197 switch (text[i]) {
1198 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001199 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001200 break;
1201 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001202 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001203 break;
1204 case '>':
1205 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001206 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001207 break;
1208 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001209 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001210 n++;
1211 }
1212 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001213
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001214 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001215}
1216
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001217static int
Michal Vasko1e62a092015-12-01 12:27:20 +01001218dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001219{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001220 int size = 0;
1221 struct lyxml_attr *a;
1222 struct lyxml_elem *child;
1223 const char *delim, *delim_outer;
1224 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001225
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001226 if (!e->name) {
1227 /* mixed content */
1228 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001229 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001230 } else {
1231 return 0;
1232 }
1233 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001234
Pavol Vican4171e512015-10-22 13:30:53 +02001235 delim = delim_outer = (options & LYXML_DUMP_FORMAT) ? "\n" : "";
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001236 indent = 2 * level;
1237 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1238 delim = "";
1239 }
1240 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1241 delim_outer = "";
1242 indent = 0;
1243 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001244
Radek Krejci528b8ce2015-10-22 16:25:04 +02001245 if (!(options & (LYXML_DUMP_OPEN|LYXML_DUMP_CLOSE|LYXML_DUMP_ATTRS)) || (options & LYXML_DUMP_OPEN)) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001246 /* opening tag */
1247 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001248 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001249 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001250 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001251 }
1252 } else if (options & LYXML_DUMP_CLOSE) {
1253 indent = 0;
1254 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001255 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001256
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001257 /* attributes */
1258 for (a = e->attr; a; a = a->next) {
1259 if (a->type == LYXML_ATTR_NS) {
1260 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001261 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001262 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001263 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001264 }
1265 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001266 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001267 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001268 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001269 }
1270 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001271
Radek Krejcic6704c82015-10-06 11:12:45 +02001272 /* apply options */
Pavol Vican0308d0c2015-10-22 15:39:23 +02001273 if ((options & LYXML_DUMP_CLOSE) && (options & LYXML_DUMP_OPEN)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001274 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001275 return size;
1276 } else if (options & LYXML_DUMP_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001277 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001278 return ++size;
1279 } else if (options & LYXML_DUMP_ATTRS) {
1280 return size;
1281 }
1282
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001283 if (!e->child && !e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001284 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001285 return size;
1286 } else if (e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001287 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001288 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001289
Michal Vasko5db027d2015-10-09 14:38:50 +02001290 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001291
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001292 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001293 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001294 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001295 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001296 }
1297 return size;
1298 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001299 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001300 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001301
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001302 /* go recursively */
1303 LY_TREE_FOR(e->child, child) {
Pavol Vicanbe7eef52015-10-22 14:07:48 +02001304 if (options & LYXML_DUMP_FORMAT) {
1305 size += dump_elem(out, child, level + 1, LYXML_DUMP_FORMAT);
1306 } else {
1307 size += dump_elem(out, child, level, 0);
1308 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001310
Radek Krejcic6704c82015-10-06 11:12:45 +02001311close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001312 /* closing tag */
1313 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001314 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001315 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001316 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001317 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001318
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001319 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001320}
1321
Radek Krejcic6704c82015-10-06 11:12:45 +02001322API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001323lyxml_dump(FILE *stream, const struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001324{
Radek Krejci5248f132015-10-09 10:34:25 +02001325 struct lyout out;
1326
1327 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001328 return 0;
1329 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001330
Radek Krejci5248f132015-10-09 10:34:25 +02001331 out.type = LYOUT_STREAM;
1332 out.method.f = stream;
1333
1334 return dump_elem(&out, elem, 0, options);
1335}
1336
1337API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001338lyxml_dump_fd(int fd, const struct lyxml_elem *elem, int options)
Radek Krejci5248f132015-10-09 10:34:25 +02001339{
1340 struct lyout out;
1341
1342 if (fd < 0 || !elem) {
1343 return 0;
1344 }
1345
1346 out.type = LYOUT_FD;
1347 out.method.fd = fd;
1348
1349 return dump_elem(&out, elem, 0, options);
Radek Krejcif0023a92015-04-20 20:51:39 +02001350}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001351
1352API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001353lyxml_dump_mem(char **strp, const struct lyxml_elem *elem, int options)
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001354{
1355 struct lyout out;
1356 int r;
1357
1358 if (!strp || !elem) {
1359 return 0;
1360 }
1361
1362 out.type = LYOUT_MEMORY;
1363 out.method.mem.buf = NULL;
1364 out.method.mem.len = 0;
1365 out.method.mem.size = 0;
1366
1367 r = dump_elem(&out, elem, 0, options);
1368
1369 *strp = out.method.mem.buf;
1370 return r;
1371}
1372
1373API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001374lyxml_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 +02001375{
1376 struct lyout out;
1377
1378 if (!writeclb || !elem) {
1379 return 0;
1380 }
1381
1382 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001383 out.method.clb.f = writeclb;
1384 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001385
1386 return dump_elem(&out, elem, 0, options);
1387}