blob: d0e56e50cd49817f0a1841a37b32103589896b07 [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;
Michal Vaskof6109112015-12-03 14:00:42 +010096 struct lyxml_elem *ns_root, *attr_root;
Michal Vasko88c29542015-11-27 14:57:53 +010097
98 if ((attr->type != LYXML_ATTR_NS) && attr->ns) {
Michal Vaskof6109112015-12-03 14:00:42 +010099 /* find the root of attr */
100 for (attr_root = attr_parent; attr_root->parent; attr_root = attr_root->parent);
Michal Vasko88c29542015-11-27 14:57:53 +0100101
102 /* find the root of attr NS */
103 for (ns_root = attr->ns->parent; ns_root->parent; ns_root = ns_root->parent);
104
Michal Vaskof6109112015-12-03 14:00:42 +0100105 /* attr NS is defined outside attr parent subtree */
106 if (ns_root != attr_root) {
Michal Vasko88c29542015-11-27 14:57:53 +0100107 if (copy_ns) {
108 tmp_ns = attr->ns;
109 /* we may have already copied the NS over? */
110 attr->ns = lyxml_get_ns(attr->ns->parent, tmp_ns->prefix);
111
112 /* we haven't copied it over, copy it now */
113 if (!attr->ns) {
Michal Vaskof6109112015-12-03 14:00:42 +0100114 attr->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, attr_parent, (struct lyxml_attr *)tmp_ns);
Michal Vasko88c29542015-11-27 14:57:53 +0100115 }
116 } else {
117 attr->ns = NULL;
118 }
119 }
120 }
121}
122
123static struct lyxml_attr *
Michal Vaskof8879c22015-08-21 09:07:36 +0200124lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
125{
126 struct lyxml_attr *result, *a;
127
128 if (!attr || !parent) {
129 return NULL;
130 }
131
132 if (attr->type == LYXML_ATTR_NS) {
133 /* this is correct, despite that all attributes seems like a standard
134 * attributes (struct lyxml_attr), some of them can be namespace
135 * definitions (and in that case they are struct lyxml_ns).
136 */
137 result = (struct lyxml_attr *)calloc(1, sizeof (struct lyxml_ns));
138 } else {
139 result = calloc(1, sizeof (struct lyxml_attr));
140 }
Michal Vasko253035f2015-12-17 16:58:13 +0100141 if (!result) {
142 LOGMEM;
143 return NULL;
144 }
Michal Vaskof8879c22015-08-21 09:07:36 +0200145 result->value = lydict_insert(ctx, attr->value, 0);
146 result->name = lydict_insert(ctx, attr->name, 0);
147 result->type = attr->type;
148
149 /* set namespace in case of standard attributes */
150 if (result->type == LYXML_ATTR_STD && attr->ns) {
Michal Vasko88c29542015-11-27 14:57:53 +0100151 result->ns = attr->ns;
152 lyxml_correct_attr_ns(ctx, result, parent, 1);
Michal Vaskof8879c22015-08-21 09:07:36 +0200153 }
154
155 /* set parent pointer in case of namespace attribute */
156 if (result->type == LYXML_ATTR_NS) {
157 ((struct lyxml_ns *)result)->parent = parent;
158 }
159
160 /* put attribute into the parent's attributes list */
161 if (parent->attr) {
162 /* go to the end of the list */
163 for (a = parent->attr; a->next; a = a->next);
164 /* and append new attribute */
165 a->next = result;
166 } else {
167 /* add the first attribute in the list */
168 parent->attr = result;
169 }
170
171 return result;
172}
173
Michal Vasko88c29542015-11-27 14:57:53 +0100174/* copy_ns: 0 - set invalid namespaces to NULL, 1 - copy them into this subtree */
175static void
176lyxml_correct_elem_ns(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns, int correct_attrs)
177{
178 const struct lyxml_ns *tmp_ns;
179 struct lyxml_elem *elem_root, *ns_root, *tmp;
180 struct lyxml_attr *attr;
181
182 /* find the root of elem */
183 for (elem_root = elem; elem_root->parent; elem_root = elem_root->parent);
184
185 LY_TREE_DFS_BEGIN(elem, tmp, elem) {
186 if (elem->ns) {
187 /* find the root of elem NS */
188 for (ns_root = elem->ns->parent; ns_root->parent; ns_root = ns_root->parent);
189
190 /* elem NS is defined outside elem subtree */
191 if (ns_root != elem_root) {
192 if (copy_ns) {
193 tmp_ns = elem->ns;
194 /* we may have already copied the NS over? */
195 elem->ns = lyxml_get_ns(elem, tmp_ns->prefix);
196
197 /* we haven't copied it over, copy it now */
198 if (!elem->ns) {
199 elem->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, elem, (struct lyxml_attr *)tmp_ns);
200 }
201 } else {
202 elem->ns = NULL;
203 }
204 }
205 }
206 if (correct_attrs) {
207 LY_TREE_FOR(elem->attr, attr) {
208 lyxml_correct_attr_ns(ctx, attr, elem_root, copy_ns);
209 }
210 }
211 LY_TREE_DFS_END(elem, tmp, elem);
212 }
213}
214
Michal Vaskof8879c22015-08-21 09:07:36 +0200215struct lyxml_elem *
216lyxml_dup_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, struct lyxml_elem *parent, int recursive)
217{
218 struct lyxml_elem *result, *child;
219 struct lyxml_attr *attr;
220
221 if (!elem) {
222 return NULL;
223 }
224
225 result = calloc(1, sizeof *result);
Michal Vasko253035f2015-12-17 16:58:13 +0100226 if (!result) {
227 LOGMEM;
228 return NULL;
229 }
Michal Vaskof8879c22015-08-21 09:07:36 +0200230 result->content = lydict_insert(ctx, elem->content, 0);
231 result->name = lydict_insert(ctx, elem->name, 0);
232 result->flags = elem->flags;
233#ifndef NDEBUG
234 result->line = elem->line;
235#endif
236 result->prev = result;
237
238 if (parent) {
239 lyxml_add_child(ctx, parent, result);
240 }
241
Michal Vasko88c29542015-11-27 14:57:53 +0100242 /* keep old namespace for now */
243 result->ns = elem->ns;
244
245 /* correct namespaces */
246 lyxml_correct_elem_ns(ctx, result, 1, 0);
Michal Vaskof8879c22015-08-21 09:07:36 +0200247
248 /* duplicate attributes */
249 for (attr = elem->attr; attr; attr = attr->next) {
250 lyxml_dup_attr(ctx, result, attr);
251 }
252
253 if (!recursive) {
254 return result;
255 }
256
257 /* duplicate children */
258 LY_TREE_FOR(elem->child, child) {
259 lyxml_dup_elem(ctx, child, result, 1);
260 }
261
262 return result;
263}
264
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200265void
Michal Vaskof8879c22015-08-21 09:07:36 +0200266lyxml_unlink_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
Radek Krejci02117302015-04-13 16:32:44 +0200267{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200268 struct lyxml_elem *parent, *first;
Radek Krejci02117302015-04-13 16:32:44 +0200269
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200270 if (!elem) {
271 return;
272 }
Radek Krejci02117302015-04-13 16:32:44 +0200273
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200274 /* store pointers to important nodes */
275 parent = elem->parent;
Radek Krejcie1f13912015-05-26 15:17:38 +0200276
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200277 /* unlink from parent */
278 if (parent) {
279 if (parent->child == elem) {
280 /* we unlink the first child */
281 /* update the parent's link */
282 parent->child = elem->next;
283 }
284 /* forget about the parent */
285 elem->parent = NULL;
286 }
Radek Krejci02117302015-04-13 16:32:44 +0200287
Michal Vasko88c29542015-11-27 14:57:53 +0100288 if (copy_ns < 2) {
289 lyxml_correct_elem_ns(ctx, elem, copy_ns, 1);
290 }
291
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200292 /* unlink from siblings */
293 if (elem->prev == elem) {
294 /* there are no more siblings */
295 return;
296 }
297 if (elem->next) {
298 elem->next->prev = elem->prev;
299 } else {
300 /* unlinking the last element */
301 if (parent) {
302 first = parent->child;
303 } else {
304 first = elem;
305 while (elem->prev->next) {
306 first = elem->prev;
307 }
308 }
309 first->prev = elem->prev;
310 }
311 if (elem->prev->next) {
312 elem->prev->next = elem->next;
313 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200314
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200315 /* clean up the unlinked element */
316 elem->next = NULL;
317 elem->prev = elem;
Radek Krejci02117302015-04-13 16:32:44 +0200318}
319
Michal Vasko345da0a2015-12-02 10:35:55 +0100320API void
321lyxml_unlink(struct ly_ctx *ctx, struct lyxml_elem *elem)
322{
323 if (!elem) {
324 return;
325 }
326
327 lyxml_unlink_elem(ctx, elem, 1);
328}
329
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200330void
Radek Krejci00249f22015-07-07 13:43:28 +0200331lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200332{
Radek Krejci00249f22015-07-07 13:43:28 +0200333 struct lyxml_attr *aiter, *aprev;
334
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200335 if (!attr) {
336 return;
337 }
Radek Krejci02117302015-04-13 16:32:44 +0200338
Radek Krejci00249f22015-07-07 13:43:28 +0200339 if (parent) {
340 /* unlink attribute from the parent's list of attributes */
341 aprev = NULL;
342 for (aiter = parent->attr; aiter; aiter = aiter->next) {
343 if (aiter == attr) {
344 break;
345 }
346 aprev = aiter;
347 }
348 if (!aiter) {
349 /* attribute to remove not found */
350 return;
351 }
352
353 if (!aprev) {
354 /* attribute is first in parent's list of attributes */
355 parent->attr = attr->next;
356 } else {
357 /* reconnect previous attribute to the next */
358 aprev->next = attr->next;
359 }
360 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200361 lydict_remove(ctx, attr->name);
362 lydict_remove(ctx, attr->value);
363 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200364}
365
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200366void
367lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200368{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200369 struct lyxml_attr *a, *next;
370 if (!elem || !elem->attr) {
371 return;
372 }
Radek Krejci02117302015-04-13 16:32:44 +0200373
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200374 a = elem->attr;
375 do {
376 next = a->next;
Radek Krejci02117302015-04-13 16:32:44 +0200377
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200378 lydict_remove(ctx, a->name);
379 lydict_remove(ctx, a->value);
380 free(a);
Radek Krejci02117302015-04-13 16:32:44 +0200381
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200382 a = next;
383 } while (a);
Radek Krejci02117302015-04-13 16:32:44 +0200384}
385
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200386static void
Michal Vasko272e42f2015-12-02 12:20:37 +0100387lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200388{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200389 struct lyxml_elem *e, *next;
Radek Krejci02117302015-04-13 16:32:44 +0200390
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200391 if (!elem) {
392 return;
393 }
Radek Krejci02117302015-04-13 16:32:44 +0200394
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200395 lyxml_free_attrs(ctx, elem);
396 LY_TREE_FOR_SAFE(elem->child, next, e) {
Michal Vasko272e42f2015-12-02 12:20:37 +0100397 lyxml_free_elem(ctx, e);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200398 }
399 lydict_remove(ctx, elem->name);
400 lydict_remove(ctx, elem->content);
401 free(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200402}
403
Radek Krejcic6704c82015-10-06 11:12:45 +0200404API void
Michal Vasko345da0a2015-12-02 10:35:55 +0100405lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200406{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200407 if (!elem) {
408 return;
409 }
Radek Krejci02117302015-04-13 16:32:44 +0200410
Michal Vasko61f7ccb2015-10-23 10:15:08 +0200411 lyxml_unlink_elem(ctx, elem, 2);
Michal Vasko272e42f2015-12-02 12:20:37 +0100412 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200413}
414
Michal Vasko88c29542015-11-27 14:57:53 +0100415API const char *
Michal Vasko1e62a092015-12-01 12:27:20 +0100416lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200417{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200418 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200419
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200420 assert(elem);
421 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200422
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200423 for (a = elem->attr; a; a = a->next) {
424 if (a->type != LYXML_ATTR_STD) {
425 continue;
426 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200427
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200428 if (!strcmp(name, a->name)) {
429 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
430 return a->value;
431 }
432 }
433 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200434
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200435 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200436}
437
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200438int
Michal Vaskof8879c22015-08-21 09:07:36 +0200439lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200440{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200441 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200442
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200443 assert(parent);
444 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200445
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200446 /* (re)link element to parent */
447 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200448 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200449 }
450 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200451
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200452 /* link parent to element */
453 if (parent->child) {
454 e = parent->child;
455 elem->prev = e->prev;
456 elem->next = NULL;
457 elem->prev->next = elem;
458 e->prev = elem;
459 } else {
460 parent->child = elem;
461 elem->prev = elem;
462 elem->next = NULL;
463 }
Radek Krejci02117302015-04-13 16:32:44 +0200464
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200465 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200466}
467
Michal Vasko3b855722015-08-28 16:01:18 +0200468int
Michal Vasko076588e2015-10-06 15:39:57 +0200469lyxml_getutf8(const char *buf, unsigned int *read, unsigned int line)
Radek Krejci02117302015-04-13 16:32:44 +0200470{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200471 int c, aux;
472 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200473
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200474 c = buf[0];
475 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200476
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200477 /* buf is NULL terminated string, so 0 means EOF */
478 if (!c) {
Michal Vasko076588e2015-10-06 15:39:57 +0200479 LOGVAL(LYE_EOF, line);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200480 return 0;
481 }
482 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200483
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200484 /* process character byte(s) */
485 if ((c & 0xf8) == 0xf0) {
486 /* four bytes character */
487 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200488
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200489 c &= 0x07;
490 for (i = 1; i <= 3; 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 < 0x1000 || c > 0x10ffff) {
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 & 0xf0) == 0xe0) {
505 /* three bytes character */
506 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200507
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200508 c &= 0x0f;
509 for (i = 1; i <= 2; i++) {
510 aux = buf[i];
511 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200512 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200513 return 0;
514 }
Radek Krejci02117302015-04-13 16:32:44 +0200515
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200516 c = (c << 6) | (aux & 0x3f);
517 }
Radek Krejci02117302015-04-13 16:32:44 +0200518
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200519 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
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 } else if ((c & 0xe0) == 0xc0) {
524 /* two bytes character */
525 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200526
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200527 aux = buf[1];
528 if ((aux & 0xc0) != 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200529 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200530 return 0;
531 }
532 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200533
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200534 if (c < 0x80) {
Michal Vasko076588e2015-10-06 15:39:57 +0200535 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200536 return 0;
537 }
538 } else if (!(c & 0x80)) {
539 /* one byte character */
540 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
541 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200542 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200543 return 0;
544 }
545 } else {
546 /* invalid character */
Michal Vasko076588e2015-10-06 15:39:57 +0200547 LOGVAL(LYE_XML_INVAL, line, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200548 return 0;
549 }
Radek Krejci02117302015-04-13 16:32:44 +0200550
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200551 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200552}
553
Michal Vasko0d343d12015-08-24 14:57:36 +0200554/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200555static int
556parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200557{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200558 unsigned int slen;
559 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200560
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200561 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200562
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200563 while (*c && memcmp(c, endstr, slen)) {
564 COUNTLINE(*c);
565 c++;
566 }
567 if (!*c) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200568 LOGVAL(LYE_XML_MISS, lineno, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200569 return EXIT_FAILURE;
570 }
571 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200572
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200573 *len = c - data;
574 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200575}
576
Michal Vasko0d343d12015-08-24 14:57:36 +0200577/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200578static char *
579parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200580{
Radek Krejci709fee62015-04-15 13:56:19 +0200581#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200582
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200583 char buf[BUFSIZE];
584 char *result = NULL, *aux;
585 unsigned int r;
586 int o, size = 0;
587 int cdsect = 0;
588 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200589
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200590 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
591 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200592 LOGVAL(LYE_XML_INVAL, lineno, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200593 goto error;
594 }
Radek Krejci709fee62015-04-15 13:56:19 +0200595
Radek Krejcia4a84062015-04-16 13:00:10 +0200596loop:
597
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200598 if (o > BUFSIZE - 3) {
599 /* add buffer into the result */
600 if (result) {
601 size = size + o;
Michal Vasko253035f2015-12-17 16:58:13 +0100602 aux = ly_realloc(result, size + 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200603 result = aux;
604 } else {
605 size = o;
606 result = malloc((size + 1) * sizeof *result);
607 }
Michal Vasko253035f2015-12-17 16:58:13 +0100608 if (!result) {
609 LOGMEM;
610 return NULL;
611 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200612 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200613
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200614 /* write again into the beginning of the buffer */
615 o = 0;
616 }
Radek Krejci709fee62015-04-15 13:56:19 +0200617
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200618 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
619 /* CDSect */
620 if (!cdsect) {
621 cdsect = 1;
622 *len += 9;
623 }
624 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
625 *len += 3;
626 cdsect = 0;
627 o--; /* we don't write any data in this iteration */
628 } else {
629 buf[o] = data[*len];
630 (*len)++;
631 }
632 } else if (data[*len] == '&') {
633 (*len)++;
634 if (data[*len] != '#') {
635 /* entity reference - only predefined refs are supported */
636 if (!memcmp(&data[*len], "lt;", 3)) {
637 buf[o] = '<';
638 *len += 3;
639 } else if (!memcmp(&data[*len], "gt;", 3)) {
640 buf[o] = '>';
641 *len += 3;
642 } else if (!memcmp(&data[*len], "amp;", 4)) {
643 buf[o] = '&';
644 *len += 4;
645 } else if (!memcmp(&data[*len], "apos;", 5)) {
646 buf[o] = '\'';
647 *len += 5;
648 } else if (!memcmp(&data[*len], "quot;", 5)) {
649 buf[o] = '\"';
650 *len += 5;
651 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200652 LOGVAL(LYE_XML_INVAL, lineno, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200653 goto error;
654 }
655 } else {
656 /* character reference */
657 (*len)++;
658 if (isdigit(data[*len])) {
659 for (n = 0; isdigit(data[*len]); (*len)++) {
660 n = (10 * n) + (data[*len] - '0');
661 }
662 if (data[*len] != ';') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200663 LOGVAL(LYE_XML_INVAL, lineno, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200664 goto error;
665 }
666 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
667 for (n = 0; isxdigit(data[*len]); (*len)++) {
668 if (isdigit(data[*len])) {
669 r = (data[*len] - '0');
670 } else if (data[*len] > 'F') {
671 r = 10 + (data[*len] - 'a');
672 } else {
673 r = 10 + (data[*len] - 'A');
674 }
675 n = (16 * n) + r;
676 }
677 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200678 LOGVAL(LYE_XML_INVAL, lineno, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200679 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200680
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200681 }
Radek Krejci5449d472015-10-26 14:35:56 +0100682 r = pututf8(&buf[o], n, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200683 if (!r) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200684 LOGVAL(LYE_XML_INVAL, lineno, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200685 goto error;
686 }
687 o += r - 1; /* o is ++ in for loop */
688 (*len)++;
689 }
690 } else {
691 buf[o] = data[*len];
692 COUNTLINE(buf[o]);
693 (*len)++;
694 }
695 }
Radek Krejci02117302015-04-13 16:32:44 +0200696
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200697 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
698 /* ignore loop's end condition on beginning of CDSect */
699 goto loop;
700 }
Radek Krejci709fee62015-04-15 13:56:19 +0200701#undef BUFSIZE
702
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200703 if (o) {
704 if (result) {
705 size = size + o;
706 aux = realloc(result, size + 1);
707 result = aux;
708 } else {
709 size = o;
710 result = malloc((size + 1) * sizeof *result);
711 }
Michal Vasko253035f2015-12-17 16:58:13 +0100712 if (!result) {
713 LOGMEM;
714 return NULL;
715 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200716 memcpy(&result[size - o], buf, o);
717 }
718 if (result) {
719 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200720 } else {
721 size = 0;
722 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200723 }
Radek Krejci02117302015-04-13 16:32:44 +0200724
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200725 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200726
727error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200728 free(result);
729 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200730}
731
Michal Vasko0d343d12015-08-24 14:57:36 +0200732/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200733static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200734parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200735{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200736 const char *c = data, *start, *delim;
737 char prefix[32];
738 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200739 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200740 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200741
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200742 /* check if it is attribute or namespace */
743 if (!memcmp(c, "xmlns", 5)) {
744 /* namespace */
745 attr = calloc(1, sizeof (struct lyxml_ns));
Michal Vasko253035f2015-12-17 16:58:13 +0100746 if (!attr) {
747 LOGMEM;
748 return NULL;
749 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200750 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200751 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200752 c += 5;
753 if (*c != ':') {
754 /* default namespace, prefix will be empty */
755 goto equal;
756 }
757 c++; /* go after ':' to the prefix value */
758 } else {
759 /* attribute */
760 attr = calloc(1, sizeof *attr);
Michal Vasko253035f2015-12-17 16:58:13 +0100761 if (!attr) {
762 LOGMEM;
763 return NULL;
764 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200765 attr->type = LYXML_ATTR_STD;
766 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200767
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200768 /* process name part of the attribute */
769 start = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200770 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200771 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200772 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200773 free(attr);
774 return NULL;
775 }
776 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200777 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200778 while (is_xmlnamechar(uc)) {
779 if (attr->type == LYXML_ATTR_STD && *c == ':') {
780 /* attribute in a namespace */
781 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200782
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200783 /* look for the prefix in namespaces */
784 memcpy(prefix, data, c - data);
785 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200786 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200787 }
788 c += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200789 uc = lyxml_getutf8(c, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200790 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200791
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200792 /* store the name */
793 size = c - start;
794 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200795
796equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200797 /* check Eq mark that can be surrounded by whitespaces */
798 ign_xmlws(c);
799 if (*c != '=') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200800 LOGVAL(LYE_XML_INVAL, lineno, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200801 goto error;
802 }
803 c++;
804 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200805
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200806 /* process value part of the attribute */
807 if (!*c || (*c != '"' && *c != '\'')) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200808 LOGVAL(LYE_XML_INVAL, lineno, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200809 goto error;
810 }
811 delim = c;
812 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
813 if (ly_errno) {
814 goto error;
815 }
Radek Krejci02117302015-04-13 16:32:44 +0200816
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200817 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200818
819 /* put attribute into the parent's attributes list */
820 if (parent->attr) {
821 /* go to the end of the list */
822 for (a = parent->attr; a->next; a = a->next);
823 /* and append new attribute */
824 a->next = attr;
825 } else {
826 /* add the first attribute in the list */
827 parent->attr = attr;
828 }
829
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200830 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200831
832error:
Radek Krejci00249f22015-07-07 13:43:28 +0200833 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200834 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200835}
836
Michal Vasko0d343d12015-08-24 14:57:36 +0200837/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200838static struct lyxml_elem *
839parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200840{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200841 const char *c = data, *start, *e;
842 const char *lws; /* leading white space for handling mixed content */
843 int uc;
844 char *str;
845 char prefix[32] = { 0 };
846 unsigned int prefix_len = 0;
847 struct lyxml_elem *elem = NULL, *child;
848 struct lyxml_attr *attr;
849 unsigned int size;
850 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200851
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200852 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200853
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200854 if (*c != '<') {
855 return NULL;
856 }
Radek Krejci02117302015-04-13 16:32:44 +0200857
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200858 /* locate element name */
859 c++;
860 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200861
Michal Vasko076588e2015-10-06 15:39:57 +0200862 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200863 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200864 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200865 return NULL;
866 }
867 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200868 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200869 while (is_xmlnamechar(uc)) {
870 if (*e == ':') {
871 if (prefix_len) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200872 LOGVAL(LYE_XML_INVAL, lineno, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200873 goto error;
874 }
875 /* element in a namespace */
876 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200877
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200878 /* look for the prefix in namespaces */
879 memcpy(prefix, c, prefix_len = e - c);
880 prefix[prefix_len] = '\0';
881 c = start;
882 }
883 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200884 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200885 }
886 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200887 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200888 return NULL;
889 }
Radek Krejci02117302015-04-13 16:32:44 +0200890
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200891 /* allocate element structure */
892 elem = calloc(1, sizeof *elem);
Michal Vasko253035f2015-12-17 16:58:13 +0100893 if (!elem) {
894 LOGMEM;
895 return NULL;
896 }
Radek Krejci3045cf32015-05-28 10:58:52 +0200897#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200898 elem->line = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +0200899#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200900 elem->next = NULL;
901 elem->prev = elem;
902 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200903 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200904 }
Radek Krejci02117302015-04-13 16:32:44 +0200905
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200906 /* store the name into the element structure */
907 elem->name = lydict_insert(ctx, c, e - c);
908 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200909
910process:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200911 ly_errno = 0;
912 ign_xmlws(c);
913 if (!memcmp("/>", c, 2)) {
914 /* we are done, it was EmptyElemTag */
915 c += 2;
916 closed_flag = 1;
917 } else if (*c == '>') {
918 /* process element content */
919 c++;
920 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200921
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200922 while (*c) {
923 if (!memcmp(c, "</", 2)) {
924 if (lws && !elem->child) {
925 /* leading white spaces were actually content */
926 goto store_content;
927 }
Radek Krejci02117302015-04-13 16:32:44 +0200928
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200929 /* Etag */
930 c += 2;
931 /* get name and check it */
932 e = c;
Michal Vasko076588e2015-10-06 15:39:57 +0200933 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200934 if (!is_xmlnamestartchar(uc)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200935 LOGVAL(LYE_XML_INVAL, lineno, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200936 goto error;
937 }
938 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200939 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200940 while (is_xmlnamechar(uc)) {
941 if (*e == ':') {
942 /* element in a namespace */
943 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200944
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200945 /* look for the prefix in namespaces */
946 if (memcmp(prefix, c, e - c)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200947 LOGVAL(LYE_SPEC, lineno,
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200948 "Mixed opening (%s) and closing element tags (different namespaces).", elem->name);
949 goto error;
950 }
951 c = start;
952 }
953 e += size;
Michal Vasko076588e2015-10-06 15:39:57 +0200954 uc = lyxml_getutf8(e, &size, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200955 }
956 if (!*e) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200957 LOGVAL(LYE_EOF, lineno);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200958 goto error;
959 }
Radek Krejci02117302015-04-13 16:32:44 +0200960
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200961 /* check that it corresponds to opening tag */
962 size = e - c;
963 str = malloc((size + 1) * sizeof *str);
Michal Vasko253035f2015-12-17 16:58:13 +0100964 if (!str) {
965 LOGMEM;
966 goto error;
967 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200968 memcpy(str, c, e - c);
969 str[e - c] = '\0';
970 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200971 LOGVAL(LYE_SPEC, lineno, "Mixed opening (%s) and closing (%s) element tags.", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200972 free(str);
973 goto error;
974 }
975 free(str);
976 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200977
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200978 ign_xmlws(c);
979 if (*c != '>') {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +0200980 LOGVAL(LYE_SPEC, lineno, "Close element tag \"%s\" contain additional data.", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200981 goto error;
982 }
983 c++;
984 closed_flag = 1;
985 break;
Radek Krejci02117302015-04-13 16:32:44 +0200986
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200987 } else if (!memcmp(c, "<?", 2)) {
988 if (lws) {
989 /* leading white spaces were only formatting */
990 lws = NULL;
991 }
992 /* PI - ignore it */
993 c += 2;
994 if (parse_ignore(c, "?>", &size)) {
995 goto error;
996 }
997 c += size;
998 } else if (!memcmp(c, "<!--", 4)) {
999 if (lws) {
1000 /* leading white spaces were only formatting */
1001 lws = NULL;
1002 }
1003 /* Comment - ignore it */
1004 c += 4;
1005 if (parse_ignore(c, "-->", &size)) {
1006 goto error;
1007 }
1008 c += size;
1009 } else if (!memcmp(c, "<![CDATA[", 9)) {
1010 /* CDSect */
1011 goto store_content;
1012 } else if (*c == '<') {
1013 if (lws) {
1014 if (elem->flags & LYXML_ELEM_MIXED) {
1015 /* we have a mixed content */
1016 goto store_content;
1017 } else {
1018 /* leading white spaces were only formatting */
1019 lws = NULL;
1020 }
1021 }
1022 if (elem->content) {
1023 /* we have a mixed content */
1024 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001025 if (!child) {
1026 LOGMEM;
1027 goto error;
1028 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001029 child->content = elem->content;
1030 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001031 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001032 elem->flags |= LYXML_ELEM_MIXED;
1033 }
1034 child = parse_elem(ctx, c, &size, elem);
1035 if (!child) {
1036 goto error;
1037 }
1038 c += size; /* move after processed child element */
1039 } else if (is_xmlws(*c)) {
1040 lws = c;
Radek Krejci3045cf32015-05-28 10:58:52 +02001041#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001042 lws_lineno = lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001043#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001044 ign_xmlws(c);
1045 } else {
Radek Krejci02117302015-04-13 16:32:44 +02001046store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001047 /* store text content */
1048 if (lws) {
1049 /* process content including the leading white spaces */
1050 c = lws;
Radek Krejci3045cf32015-05-28 10:58:52 +02001051#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001052 lineno = lws_lineno;
Radek Krejci3045cf32015-05-28 10:58:52 +02001053#endif
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001054 lws = NULL;
1055 }
1056 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1057 if (ly_errno) {
1058 goto error;
1059 }
1060 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001061
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001062 if (elem->child) {
1063 /* we have a mixed content */
1064 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001065 if (!child) {
1066 LOGMEM;
1067 goto error;
1068 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001069 child->content = elem->content;
1070 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001071 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001072 elem->flags |= LYXML_ELEM_MIXED;
1073 }
1074 }
1075 }
1076 } else {
1077 /* process attribute */
1078 attr = parse_attr(ctx, c, &size, elem);
1079 if (!attr) {
1080 goto error;
1081 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001082 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001083
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001084 /* check namespace */
1085 if (attr->type == LYXML_ATTR_NS) {
1086 if (!prefix[0] && !attr->name) {
1087 if (attr->value) {
1088 /* default prefix */
1089 elem->ns = (struct lyxml_ns *)attr;
1090 } else {
1091 /* xmlns="" -> no namespace */
1092 nons_flag = 1;
1093 }
1094 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1095 /* matching namespace with prefix */
1096 elem->ns = (struct lyxml_ns *)attr;
1097 }
1098 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001099
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001100 /* go back to finish element processing */
1101 goto process;
1102 }
Radek Krejci02117302015-04-13 16:32:44 +02001103
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001104 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001105
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001106 if (!closed_flag) {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001107 LOGVAL(LYE_XML_MISS, lineno, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001108 goto error;
1109 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001110
Radek Krejci78a230a2015-07-07 17:04:40 +02001111 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001112 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001113 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001114
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001115 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001116
1117error:
Michal Vasko345da0a2015-12-02 10:35:55 +01001118 lyxml_free(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001119
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001120 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001121}
1122
Michal Vasko0d343d12015-08-24 14:57:36 +02001123/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001124API struct lyxml_elem *
Michal Vasko70f92c32015-12-10 09:57:24 +01001125lyxml_read_data(struct ly_ctx *ctx, const char *data, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001126{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001127 const char *c = data;
1128 unsigned int len;
1129 struct lyxml_elem *root = NULL;
Radek Krejci02117302015-04-13 16:32:44 +02001130
Radek Krejci3045cf32015-05-28 10:58:52 +02001131#ifndef NDEBUG
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001132 /* TODO: threads support */
1133 lineno = 1;
Radek Krejci3045cf32015-05-28 10:58:52 +02001134#endif
1135
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001136 /* process document */
1137 while (*c) {
1138 if (is_xmlws(*c)) {
1139 /* skip whitespaces */
1140 ign_xmlws(c);
1141 } else if (!memcmp(c, "<?", 2)) {
1142 /* XMLDecl or PI - ignore it */
1143 c += 2;
1144 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001145 return NULL;
1146 }
1147 c += len;
1148 } else if (!memcmp(c, "<!--", 4)) {
1149 /* Comment - ignore it */
1150 c += 2;
1151 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001152 return NULL;
1153 }
1154 c += len;
1155 } else if (!memcmp(c, "<!", 2)) {
1156 /* DOCTYPE */
1157 /* TODO - standalone ignore counting < and > */
1158 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1159 return NULL;
1160 } else if (*c == '<') {
1161 /* element - process it in next loop to strictly follow XML
1162 * format
1163 */
1164 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001165 } else {
Michal Vaskoe7fc19c2015-08-05 16:24:39 +02001166 LOGVAL(LYE_XML_INCHAR, lineno, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001167 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001168 }
1169 }
Radek Krejci02117302015-04-13 16:32:44 +02001170
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001171 root = parse_elem(ctx, c, &len, NULL);
1172 if (!root) {
1173 return NULL;
1174 }
1175 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001176
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001177 /* ignore the rest of document where can be comments, PIs and whitespaces,
1178 * note that we are not detecting syntax errors in these parts
1179 */
1180 ign_xmlws(c);
1181 if (*c) {
1182 LOGWRN("There are some not parsed data:\n%s", c);
1183 }
Radek Krejci02117302015-04-13 16:32:44 +02001184
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001185 return root;
Radek Krejci02117302015-04-13 16:32:44 +02001186}
1187
Radek Krejcic6704c82015-10-06 11:12:45 +02001188API struct lyxml_elem *
Michal Vasko70f92c32015-12-10 09:57:24 +01001189lyxml_read_path(struct ly_ctx *ctx, const char *filename, int UNUSED(options))
Radek Krejci54ea8de2015-04-09 18:02:56 +02001190{
Radek Krejci6b3d9262015-12-03 13:45:27 +01001191 struct lyxml_elem *elem = NULL;
Pavol Vicanb2570c12015-11-12 13:50:20 +01001192 struct stat sb;
1193 int fd;
1194 char *addr;
1195
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001196 if (!filename || !ctx) {
1197 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1198 return NULL;
1199 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001200
Pavol Vicanb2570c12015-11-12 13:50:20 +01001201 fd = open(filename, O_RDONLY);
1202 if (fd == -1) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001203 LOGERR(LY_EINVAL,"Opening file \"%s\" failed.", filename);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001204 return NULL;
1205 }
1206 if (fstat(fd, &sb) == -1) {
1207 LOGERR(LY_EINVAL, "Unable to get file \"%s\" information.\n", filename);
1208 goto error;
1209 }
1210 if (!S_ISREG(sb.st_mode)) {
Radek Krejci7e793ed2015-12-17 09:16:38 +01001211 LOGERR(LY_EINVAL, "File \"%s\" not a file.\n", filename);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001212 goto error;
1213 }
1214 addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
1215 if (addr == MAP_FAILED) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001216 LOGERR(LY_EMEM,"Map file into memory failed (%s()).", __func__);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001217 goto error;
1218 }
Radek Krejci6b3d9262015-12-03 13:45:27 +01001219
Michal Vasko70f92c32015-12-10 09:57:24 +01001220 elem = lyxml_read_data(ctx, addr, 0);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001221 munmap(addr, sb.st_size);
Radek Krejci30793ab2015-12-03 13:45:45 +01001222 close(fd);
Radek Krejci6b3d9262015-12-03 13:45:27 +01001223
Pavol Vicanb2570c12015-11-12 13:50:20 +01001224 return elem;
1225
1226error:
Radek Krejci6b3d9262015-12-03 13:45:27 +01001227 if (fd != -1) {
1228 close(fd);
1229 }
1230
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001231 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001232}
Radek Krejci02117302015-04-13 16:32:44 +02001233
Michal Vasko5db027d2015-10-09 14:38:50 +02001234int
1235lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001236{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001237 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001238
Michal Vasko5db027d2015-10-09 14:38:50 +02001239 if (!text) {
1240 return 0;
1241 }
1242
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001243 for (i = n = 0; text[i]; i++) {
1244 switch (text[i]) {
1245 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001246 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001247 break;
1248 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001249 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001250 break;
1251 case '>':
1252 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001253 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001254 break;
1255 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001256 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001257 n++;
1258 }
1259 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001260
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001261 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001262}
1263
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001264static int
Michal Vasko1e62a092015-12-01 12:27:20 +01001265dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001266{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001267 int size = 0;
1268 struct lyxml_attr *a;
1269 struct lyxml_elem *child;
1270 const char *delim, *delim_outer;
1271 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001272
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001273 if (!e->name) {
1274 /* mixed content */
1275 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001276 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001277 } else {
1278 return 0;
1279 }
1280 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001281
Pavol Vican4171e512015-10-22 13:30:53 +02001282 delim = delim_outer = (options & LYXML_DUMP_FORMAT) ? "\n" : "";
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001283 indent = 2 * level;
1284 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1285 delim = "";
1286 }
1287 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1288 delim_outer = "";
1289 indent = 0;
1290 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001291
Radek Krejci528b8ce2015-10-22 16:25:04 +02001292 if (!(options & (LYXML_DUMP_OPEN|LYXML_DUMP_CLOSE|LYXML_DUMP_ATTRS)) || (options & LYXML_DUMP_OPEN)) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001293 /* opening tag */
1294 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001295 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001296 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001297 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001298 }
1299 } else if (options & LYXML_DUMP_CLOSE) {
1300 indent = 0;
1301 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001302 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001303
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001304 /* attributes */
1305 for (a = e->attr; a; a = a->next) {
1306 if (a->type == LYXML_ATTR_NS) {
1307 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001308 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001310 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001311 }
1312 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001313 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001314 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001315 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001316 }
1317 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001318
Radek Krejcic6704c82015-10-06 11:12:45 +02001319 /* apply options */
Pavol Vican0308d0c2015-10-22 15:39:23 +02001320 if ((options & LYXML_DUMP_CLOSE) && (options & LYXML_DUMP_OPEN)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001321 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001322 return size;
1323 } else if (options & LYXML_DUMP_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001324 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001325 return ++size;
1326 } else if (options & LYXML_DUMP_ATTRS) {
1327 return size;
1328 }
1329
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001330 if (!e->child && !e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001331 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001332 return size;
1333 } else if (e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001334 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001335 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001336
Michal Vasko5db027d2015-10-09 14:38:50 +02001337 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001338
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001339 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001340 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001341 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001342 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001343 }
1344 return size;
1345 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001346 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001347 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001348
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001349 /* go recursively */
1350 LY_TREE_FOR(e->child, child) {
Pavol Vicanbe7eef52015-10-22 14:07:48 +02001351 if (options & LYXML_DUMP_FORMAT) {
1352 size += dump_elem(out, child, level + 1, LYXML_DUMP_FORMAT);
1353 } else {
1354 size += dump_elem(out, child, level, 0);
1355 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001356 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001357
Radek Krejcic6704c82015-10-06 11:12:45 +02001358close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001359 /* closing tag */
1360 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001361 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001362 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001363 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001364 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001365
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001366 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001367}
1368
Radek Krejcic6704c82015-10-06 11:12:45 +02001369API int
Michal Vasko70f92c32015-12-10 09:57:24 +01001370lyxml_dump_file(FILE *stream, const struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001371{
Radek Krejci5248f132015-10-09 10:34:25 +02001372 struct lyout out;
1373
1374 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001375 return 0;
1376 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001377
Radek Krejci5248f132015-10-09 10:34:25 +02001378 out.type = LYOUT_STREAM;
1379 out.method.f = stream;
1380
1381 return dump_elem(&out, elem, 0, options);
1382}
1383
1384API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001385lyxml_dump_fd(int fd, const struct lyxml_elem *elem, int options)
Radek Krejci5248f132015-10-09 10:34:25 +02001386{
1387 struct lyout out;
1388
1389 if (fd < 0 || !elem) {
1390 return 0;
1391 }
1392
1393 out.type = LYOUT_FD;
1394 out.method.fd = fd;
1395
1396 return dump_elem(&out, elem, 0, options);
Radek Krejcif0023a92015-04-20 20:51:39 +02001397}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001398
1399API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001400lyxml_dump_mem(char **strp, const struct lyxml_elem *elem, int options)
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001401{
1402 struct lyout out;
1403 int r;
1404
1405 if (!strp || !elem) {
1406 return 0;
1407 }
1408
1409 out.type = LYOUT_MEMORY;
1410 out.method.mem.buf = NULL;
1411 out.method.mem.len = 0;
1412 out.method.mem.size = 0;
1413
1414 r = dump_elem(&out, elem, 0, options);
1415
1416 *strp = out.method.mem.buf;
1417 return r;
1418}
1419
1420API int
Michal Vasko1e62a092015-12-01 12:27:20 +01001421lyxml_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 +02001422{
1423 struct lyout out;
1424
1425 if (!writeclb || !elem) {
1426 return 0;
1427 }
1428
1429 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001430 out.method.clb.f = writeclb;
1431 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001432
1433 return dump_elem(&out, elem, 0, options);
1434}