blob: 489701ea1500f2e03476c8814bce0908c61820e6 [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 *
Radek Krejci54f6fb32016-02-24 12:56:39 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vasko8de098c2016-02-26 10:00:25 +010011 *
Radek Krejci54f6fb32016-02-24 12:56:39 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci54ea8de2015-04-09 18:02:56 +020013 */
14
Radek Krejci812b10a2015-05-28 16:48:25 +020015#include <assert.h>
Radek Krejci563427e2016-02-08 16:26:34 +010016#include <errno.h>
Radek Krejci709fee62015-04-15 13:56:19 +020017#include <ctype.h>
18#include <stdint.h>
Radek Krejcif0023a92015-04-20 20:51:39 +020019#include <stdio.h>
Radek Krejci02117302015-04-13 16:32:44 +020020#include <stdlib.h>
21#include <string.h>
Radek Krejci54ea8de2015-04-09 18:02:56 +020022#include <unistd.h>
Radek Krejci563427e2016-02-08 16:26:34 +010023#include <pthread.h>
Pavol Vicanb2570c12015-11-12 13:50:20 +010024#include <sys/stat.h>
25#include <sys/mman.h>
Radek Krejci563427e2016-02-08 16:26:34 +010026#include <sys/syscall.h>
Pavol Vicanb2570c12015-11-12 13:50:20 +010027#include <fcntl.h>
Radek Krejci54ea8de2015-04-09 18:02:56 +020028
Radek Krejci06a704e2015-04-22 14:50:49 +020029#include "common.h"
Radek Krejci41912fe2015-10-22 10:22:12 +020030#include "dict_private.h"
Radek Krejci5248f132015-10-09 10:34:25 +020031#include "printer.h"
Radek Krejci5449d472015-10-26 14:35:56 +010032#include "parser.h"
Michal Vasko2d162e12015-09-24 14:33:29 +020033#include "tree_schema.h"
Michal Vaskofc5744d2015-10-22 12:09:34 +020034#include "xml_internal.h"
Radek Krejci54ea8de2015-04-09 18:02:56 +020035
Radek Krejci3045cf32015-05-28 10:58:52 +020036#define ign_xmlws(p) \
Radek Krejci563427e2016-02-08 16:26:34 +010037 while (is_xmlws(*p)) { \
Radek Krejci563427e2016-02-08 16:26:34 +010038 p++; \
39 }
Radek Krejci02117302015-04-13 16:32:44 +020040
Michal Vasko88c29542015-11-27 14:57:53 +010041static struct lyxml_attr *lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr);
42
Michal Vasko1e62a092015-12-01 12:27:20 +010043API const struct lyxml_ns *
44lyxml_get_ns(const struct lyxml_elem *elem, const char *prefix)
Michal Vaskof8879c22015-08-21 09:07:36 +020045{
46 struct lyxml_attr *attr;
47 int len;
48
49 if (!elem) {
50 return NULL;
51 }
52
53 if (!prefix) {
54 len = 0;
55 } else {
56 len = strlen(prefix) + 1;
57 }
58
59 for (attr = elem->attr; attr; attr = attr->next) {
60 if (attr->type != LYXML_ATTR_NS) {
61 continue;
62 }
63 if (!attr->name) {
64 if (!len) {
65 /* default namespace found */
66 if (!attr->value) {
67 /* empty default namespace -> no default namespace */
68 return NULL;
69 }
70 return (struct lyxml_ns *)attr;
71 }
72 } else if (len && !memcmp(attr->name, prefix, len)) {
73 /* prefix found */
74 return (struct lyxml_ns *)attr;
75 }
76 }
77
78 /* go recursively */
79 return lyxml_get_ns(elem->parent, prefix);
80}
81
Michal Vasko88c29542015-11-27 14:57:53 +010082static void
83lyxml_correct_attr_ns(struct ly_ctx *ctx, struct lyxml_attr *attr, struct lyxml_elem *attr_parent, int copy_ns)
84{
85 const struct lyxml_ns *tmp_ns;
Michal Vaskof6109112015-12-03 14:00:42 +010086 struct lyxml_elem *ns_root, *attr_root;
Michal Vasko88c29542015-11-27 14:57:53 +010087
88 if ((attr->type != LYXML_ATTR_NS) && attr->ns) {
Michal Vaskof6109112015-12-03 14:00:42 +010089 /* find the root of attr */
90 for (attr_root = attr_parent; attr_root->parent; attr_root = attr_root->parent);
Michal Vasko88c29542015-11-27 14:57:53 +010091
92 /* find the root of attr NS */
93 for (ns_root = attr->ns->parent; ns_root->parent; ns_root = ns_root->parent);
94
Michal Vaskof6109112015-12-03 14:00:42 +010095 /* attr NS is defined outside attr parent subtree */
96 if (ns_root != attr_root) {
Michal Vasko88c29542015-11-27 14:57:53 +010097 if (copy_ns) {
98 tmp_ns = attr->ns;
99 /* we may have already copied the NS over? */
Radek Krejci66aca402016-05-24 15:23:02 +0200100 attr->ns = lyxml_get_ns(attr_parent, tmp_ns->prefix);
Michal Vasko88c29542015-11-27 14:57:53 +0100101
102 /* we haven't copied it over, copy it now */
103 if (!attr->ns) {
Michal Vaskof6109112015-12-03 14:00:42 +0100104 attr->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, attr_parent, (struct lyxml_attr *)tmp_ns);
Michal Vasko88c29542015-11-27 14:57:53 +0100105 }
106 } else {
107 attr->ns = NULL;
108 }
109 }
110 }
111}
112
113static struct lyxml_attr *
Michal Vaskof8879c22015-08-21 09:07:36 +0200114lyxml_dup_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
115{
116 struct lyxml_attr *result, *a;
117
118 if (!attr || !parent) {
119 return NULL;
120 }
121
122 if (attr->type == LYXML_ATTR_NS) {
123 /* this is correct, despite that all attributes seems like a standard
124 * attributes (struct lyxml_attr), some of them can be namespace
125 * definitions (and in that case they are struct lyxml_ns).
126 */
127 result = (struct lyxml_attr *)calloc(1, sizeof (struct lyxml_ns));
128 } else {
129 result = calloc(1, sizeof (struct lyxml_attr));
130 }
Michal Vasko253035f2015-12-17 16:58:13 +0100131 if (!result) {
132 LOGMEM;
133 return NULL;
134 }
Michal Vaskof8879c22015-08-21 09:07:36 +0200135 result->value = lydict_insert(ctx, attr->value, 0);
136 result->name = lydict_insert(ctx, attr->name, 0);
137 result->type = attr->type;
138
139 /* set namespace in case of standard attributes */
140 if (result->type == LYXML_ATTR_STD && attr->ns) {
Michal Vasko88c29542015-11-27 14:57:53 +0100141 result->ns = attr->ns;
142 lyxml_correct_attr_ns(ctx, result, parent, 1);
Michal Vaskof8879c22015-08-21 09:07:36 +0200143 }
144
145 /* set parent pointer in case of namespace attribute */
146 if (result->type == LYXML_ATTR_NS) {
147 ((struct lyxml_ns *)result)->parent = parent;
148 }
149
150 /* put attribute into the parent's attributes list */
151 if (parent->attr) {
152 /* go to the end of the list */
153 for (a = parent->attr; a->next; a = a->next);
154 /* and append new attribute */
155 a->next = result;
156 } else {
157 /* add the first attribute in the list */
158 parent->attr = result;
159 }
160
161 return result;
162}
163
Michal Vaskof748dbc2016-04-05 11:27:47 +0200164void
Michal Vasko88c29542015-11-27 14:57:53 +0100165lyxml_correct_elem_ns(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns, int correct_attrs)
166{
167 const struct lyxml_ns *tmp_ns;
Radek Krejcid5be5682016-01-14 16:23:22 +0100168 struct lyxml_elem *elem_root, *ns_root, *tmp, *iter;
Michal Vasko88c29542015-11-27 14:57:53 +0100169 struct lyxml_attr *attr;
170
171 /* find the root of elem */
172 for (elem_root = elem; elem_root->parent; elem_root = elem_root->parent);
173
Radek Krejcid5be5682016-01-14 16:23:22 +0100174 LY_TREE_DFS_BEGIN(elem, tmp, iter) {
175 if (iter->ns) {
Michal Vasko88c29542015-11-27 14:57:53 +0100176 /* find the root of elem NS */
Radek Krejcic071c542016-01-27 14:57:51 +0100177 for (ns_root = iter->ns->parent; ns_root; ns_root = ns_root->parent);
Michal Vasko88c29542015-11-27 14:57:53 +0100178
179 /* elem NS is defined outside elem subtree */
180 if (ns_root != elem_root) {
181 if (copy_ns) {
Radek Krejcid5be5682016-01-14 16:23:22 +0100182 tmp_ns = iter->ns;
Michal Vasko88c29542015-11-27 14:57:53 +0100183 /* we may have already copied the NS over? */
Radek Krejcid5be5682016-01-14 16:23:22 +0100184 iter->ns = lyxml_get_ns(iter, tmp_ns->prefix);
Michal Vasko88c29542015-11-27 14:57:53 +0100185
186 /* we haven't copied it over, copy it now */
Radek Krejcid5be5682016-01-14 16:23:22 +0100187 if (!iter->ns) {
188 iter->ns = (struct lyxml_ns *)lyxml_dup_attr(ctx, iter, (struct lyxml_attr *)tmp_ns);
Michal Vasko88c29542015-11-27 14:57:53 +0100189 }
190 } else {
Radek Krejcid5be5682016-01-14 16:23:22 +0100191 iter->ns = NULL;
Michal Vasko88c29542015-11-27 14:57:53 +0100192 }
193 }
194 }
195 if (correct_attrs) {
Radek Krejcid5be5682016-01-14 16:23:22 +0100196 LY_TREE_FOR(iter->attr, attr) {
Michal Vasko88c29542015-11-27 14:57:53 +0100197 lyxml_correct_attr_ns(ctx, attr, elem_root, copy_ns);
198 }
199 }
Radek Krejcid5be5682016-01-14 16:23:22 +0100200 LY_TREE_DFS_END(elem, tmp, iter);
Michal Vasko88c29542015-11-27 14:57:53 +0100201 }
202}
203
Michal Vaskof8879c22015-08-21 09:07:36 +0200204struct lyxml_elem *
205lyxml_dup_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, struct lyxml_elem *parent, int recursive)
206{
207 struct lyxml_elem *result, *child;
208 struct lyxml_attr *attr;
209
210 if (!elem) {
211 return NULL;
212 }
213
214 result = calloc(1, sizeof *result);
Michal Vasko253035f2015-12-17 16:58:13 +0100215 if (!result) {
216 LOGMEM;
217 return NULL;
218 }
Michal Vaskof8879c22015-08-21 09:07:36 +0200219 result->content = lydict_insert(ctx, elem->content, 0);
220 result->name = lydict_insert(ctx, elem->name, 0);
221 result->flags = elem->flags;
Michal Vaskof8879c22015-08-21 09:07:36 +0200222 result->prev = result;
223
224 if (parent) {
225 lyxml_add_child(ctx, parent, result);
226 }
227
Michal Vasko88c29542015-11-27 14:57:53 +0100228 /* keep old namespace for now */
229 result->ns = elem->ns;
230
231 /* correct namespaces */
232 lyxml_correct_elem_ns(ctx, result, 1, 0);
Michal Vaskof8879c22015-08-21 09:07:36 +0200233
234 /* duplicate attributes */
235 for (attr = elem->attr; attr; attr = attr->next) {
236 lyxml_dup_attr(ctx, result, attr);
237 }
238
239 if (!recursive) {
240 return result;
241 }
242
243 /* duplicate children */
244 LY_TREE_FOR(elem->child, child) {
245 lyxml_dup_elem(ctx, child, result, 1);
246 }
247
248 return result;
249}
250
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200251void
Michal Vaskof8879c22015-08-21 09:07:36 +0200252lyxml_unlink_elem(struct ly_ctx *ctx, struct lyxml_elem *elem, int copy_ns)
Radek Krejci02117302015-04-13 16:32:44 +0200253{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200254 struct lyxml_elem *parent, *first;
Radek Krejci02117302015-04-13 16:32:44 +0200255
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200256 if (!elem) {
257 return;
258 }
Radek Krejci02117302015-04-13 16:32:44 +0200259
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200260 /* store pointers to important nodes */
261 parent = elem->parent;
Radek Krejcie1f13912015-05-26 15:17:38 +0200262
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200263 /* unlink from parent */
264 if (parent) {
265 if (parent->child == elem) {
266 /* we unlink the first child */
267 /* update the parent's link */
268 parent->child = elem->next;
269 }
270 /* forget about the parent */
271 elem->parent = NULL;
272 }
Radek Krejci02117302015-04-13 16:32:44 +0200273
Michal Vasko88c29542015-11-27 14:57:53 +0100274 if (copy_ns < 2) {
275 lyxml_correct_elem_ns(ctx, elem, copy_ns, 1);
276 }
277
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200278 /* unlink from siblings */
279 if (elem->prev == elem) {
280 /* there are no more siblings */
281 return;
282 }
283 if (elem->next) {
284 elem->next->prev = elem->prev;
285 } else {
286 /* unlinking the last element */
287 if (parent) {
288 first = parent->child;
289 } else {
290 first = elem;
Radek Krejcie4fffcf2016-02-23 16:06:25 +0100291 while (first->prev->next) {
292 first = first->prev;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200293 }
294 }
295 first->prev = elem->prev;
296 }
297 if (elem->prev->next) {
298 elem->prev->next = elem->next;
299 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200300
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200301 /* clean up the unlinked element */
302 elem->next = NULL;
303 elem->prev = elem;
Radek Krejci02117302015-04-13 16:32:44 +0200304}
305
Michal Vasko345da0a2015-12-02 10:35:55 +0100306API void
307lyxml_unlink(struct ly_ctx *ctx, struct lyxml_elem *elem)
308{
309 if (!elem) {
310 return;
311 }
312
313 lyxml_unlink_elem(ctx, elem, 1);
314}
315
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200316void
Radek Krejci00249f22015-07-07 13:43:28 +0200317lyxml_free_attr(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_attr *attr)
Radek Krejci02117302015-04-13 16:32:44 +0200318{
Radek Krejci00249f22015-07-07 13:43:28 +0200319 struct lyxml_attr *aiter, *aprev;
320
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200321 if (!attr) {
322 return;
323 }
Radek Krejci02117302015-04-13 16:32:44 +0200324
Radek Krejci00249f22015-07-07 13:43:28 +0200325 if (parent) {
326 /* unlink attribute from the parent's list of attributes */
327 aprev = NULL;
328 for (aiter = parent->attr; aiter; aiter = aiter->next) {
329 if (aiter == attr) {
330 break;
331 }
332 aprev = aiter;
333 }
334 if (!aiter) {
335 /* attribute to remove not found */
336 return;
337 }
338
339 if (!aprev) {
340 /* attribute is first in parent's list of attributes */
341 parent->attr = attr->next;
342 } else {
343 /* reconnect previous attribute to the next */
344 aprev->next = attr->next;
345 }
346 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200347 lydict_remove(ctx, attr->name);
348 lydict_remove(ctx, attr->value);
349 free(attr);
Radek Krejci02117302015-04-13 16:32:44 +0200350}
351
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200352void
353lyxml_free_attrs(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200354{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200355 struct lyxml_attr *a, *next;
356 if (!elem || !elem->attr) {
357 return;
358 }
Radek Krejci02117302015-04-13 16:32:44 +0200359
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200360 a = elem->attr;
361 do {
362 next = a->next;
Radek Krejci02117302015-04-13 16:32:44 +0200363
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200364 lydict_remove(ctx, a->name);
365 lydict_remove(ctx, a->value);
366 free(a);
Radek Krejci02117302015-04-13 16:32:44 +0200367
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200368 a = next;
369 } while (a);
Radek Krejci02117302015-04-13 16:32:44 +0200370}
371
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200372static void
Michal Vasko272e42f2015-12-02 12:20:37 +0100373lyxml_free_elem(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200374{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200375 struct lyxml_elem *e, *next;
Radek Krejci02117302015-04-13 16:32:44 +0200376
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200377 if (!elem) {
378 return;
379 }
Radek Krejci02117302015-04-13 16:32:44 +0200380
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200381 lyxml_free_attrs(ctx, elem);
382 LY_TREE_FOR_SAFE(elem->child, next, e) {
Michal Vasko272e42f2015-12-02 12:20:37 +0100383 lyxml_free_elem(ctx, e);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200384 }
385 lydict_remove(ctx, elem->name);
386 lydict_remove(ctx, elem->content);
387 free(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200388}
389
Radek Krejcic6704c82015-10-06 11:12:45 +0200390API void
Michal Vasko345da0a2015-12-02 10:35:55 +0100391lyxml_free(struct ly_ctx *ctx, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200392{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200393 if (!elem) {
394 return;
395 }
Radek Krejci02117302015-04-13 16:32:44 +0200396
Michal Vasko61f7ccb2015-10-23 10:15:08 +0200397 lyxml_unlink_elem(ctx, elem, 2);
Michal Vasko272e42f2015-12-02 12:20:37 +0100398 lyxml_free_elem(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +0200399}
400
Radek Krejci8f8db232016-05-23 16:48:21 +0200401API void
402lyxml_free_withsiblings(struct ly_ctx *ctx, struct lyxml_elem *elem)
403{
404 struct lyxml_elem *iter, *aux;
405
406 if (!elem) {
407 return;
408 }
409
410 /* optimization - avoid freeing (unlinking) the last node of the siblings list */
411 /* so, first, free the node's predecessors to the beginning of the list ... */
412 for(iter = elem->prev; iter->next; iter = aux) {
413 aux = iter->prev;
414 lyxml_free(ctx, iter);
415 }
416 /* ... then, the node is the first in the siblings list, so free them all */
417 LY_TREE_FOR_SAFE(elem, aux, iter) {
418 lyxml_free(ctx, iter);
419 }
420}
421
Michal Vasko88c29542015-11-27 14:57:53 +0100422API const char *
Michal Vasko1e62a092015-12-01 12:27:20 +0100423lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200424{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200425 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200426
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200427 assert(elem);
428 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200429
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200430 for (a = elem->attr; a; a = a->next) {
431 if (a->type != LYXML_ATTR_STD) {
432 continue;
433 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200434
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200435 if (!strcmp(name, a->name)) {
436 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
437 return a->value;
438 }
439 }
440 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200441
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200442 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200443}
444
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200445int
Michal Vaskof8879c22015-08-21 09:07:36 +0200446lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200447{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200448 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200449
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200450 assert(parent);
451 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200452
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200453 /* (re)link element to parent */
454 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200455 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200456 }
457 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200458
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200459 /* link parent to element */
460 if (parent->child) {
461 e = parent->child;
462 elem->prev = e->prev;
463 elem->next = NULL;
464 elem->prev->next = elem;
465 e->prev = elem;
466 } else {
467 parent->child = elem;
468 elem->prev = elem;
469 elem->next = NULL;
470 }
Radek Krejci02117302015-04-13 16:32:44 +0200471
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200472 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200473}
474
Michal Vasko3b855722015-08-28 16:01:18 +0200475int
Radek Krejci48464ed2016-03-17 15:44:09 +0100476lyxml_getutf8(const char *buf, unsigned int *read)
Radek Krejci02117302015-04-13 16:32:44 +0200477{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200478 int c, aux;
479 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200480
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200481 c = buf[0];
482 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200483
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200484 /* buf is NULL terminated string, so 0 means EOF */
485 if (!c) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100486 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200487 return 0;
488 }
489 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200490
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200491 /* process character byte(s) */
492 if ((c & 0xf8) == 0xf0) {
493 /* four bytes character */
494 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200495
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200496 c &= 0x07;
497 for (i = 1; i <= 3; i++) {
498 aux = buf[i];
499 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100500 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200501 return 0;
502 }
Radek Krejci02117302015-04-13 16:32:44 +0200503
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200504 c = (c << 6) | (aux & 0x3f);
505 }
Radek Krejci02117302015-04-13 16:32:44 +0200506
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200507 if (c < 0x1000 || c > 0x10ffff) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100508 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200509 return 0;
510 }
511 } else if ((c & 0xf0) == 0xe0) {
512 /* three bytes character */
513 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200514
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200515 c &= 0x0f;
516 for (i = 1; i <= 2; i++) {
517 aux = buf[i];
518 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100519 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200520 return 0;
521 }
Radek Krejci02117302015-04-13 16:32:44 +0200522
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200523 c = (c << 6) | (aux & 0x3f);
524 }
Radek Krejci02117302015-04-13 16:32:44 +0200525
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200526 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100527 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200528 return 0;
529 }
530 } else if ((c & 0xe0) == 0xc0) {
531 /* two bytes character */
532 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200533
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200534 aux = buf[1];
535 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100536 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200537 return 0;
538 }
539 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200540
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200541 if (c < 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100542 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200543 return 0;
544 }
545 } else if (!(c & 0x80)) {
546 /* one byte character */
547 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
548 /* invalid character */
Radek Krejci48464ed2016-03-17 15:44:09 +0100549 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200550 return 0;
551 }
552 } else {
553 /* invalid character */
Radek Krejci48464ed2016-03-17 15:44:09 +0100554 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200555 return 0;
556 }
Radek Krejci02117302015-04-13 16:32:44 +0200557
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200558 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200559}
560
Michal Vasko0d343d12015-08-24 14:57:36 +0200561/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200562static int
563parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200564{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200565 unsigned int slen;
566 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200567
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200568 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200569
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200570 while (*c && memcmp(c, endstr, slen)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200571 c++;
572 }
573 if (!*c) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100574 LOGVAL(LYE_XML_MISS, LY_VLOG_NONE, NULL, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200575 return EXIT_FAILURE;
576 }
577 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200578
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200579 *len = c - data;
580 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200581}
582
Michal Vasko0d343d12015-08-24 14:57:36 +0200583/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200584static char *
585parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200586{
Radek Krejci709fee62015-04-15 13:56:19 +0200587#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200588
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200589 char buf[BUFSIZE];
590 char *result = NULL, *aux;
591 unsigned int r;
592 int o, size = 0;
593 int cdsect = 0;
594 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200595
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200596 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
Radek Krejci62ea46b2016-06-16 11:18:52 +0200597 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 3))) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100598 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200599 goto error;
600 }
Radek Krejci709fee62015-04-15 13:56:19 +0200601
Radek Krejcia4a84062015-04-16 13:00:10 +0200602loop:
603
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200604 if (o > BUFSIZE - 3) {
605 /* add buffer into the result */
606 if (result) {
607 size = size + o;
Michal Vasko253035f2015-12-17 16:58:13 +0100608 aux = ly_realloc(result, size + 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200609 result = aux;
610 } else {
611 size = o;
612 result = malloc((size + 1) * sizeof *result);
613 }
Michal Vasko253035f2015-12-17 16:58:13 +0100614 if (!result) {
615 LOGMEM;
616 return NULL;
617 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200618 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200619
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200620 /* write again into the beginning of the buffer */
621 o = 0;
622 }
Radek Krejci709fee62015-04-15 13:56:19 +0200623
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200624 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
625 /* CDSect */
626 if (!cdsect) {
627 cdsect = 1;
628 *len += 9;
629 }
630 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
631 *len += 3;
632 cdsect = 0;
633 o--; /* we don't write any data in this iteration */
634 } else {
635 buf[o] = data[*len];
636 (*len)++;
637 }
638 } else if (data[*len] == '&') {
639 (*len)++;
640 if (data[*len] != '#') {
641 /* entity reference - only predefined refs are supported */
642 if (!memcmp(&data[*len], "lt;", 3)) {
643 buf[o] = '<';
644 *len += 3;
645 } else if (!memcmp(&data[*len], "gt;", 3)) {
646 buf[o] = '>';
647 *len += 3;
648 } else if (!memcmp(&data[*len], "amp;", 4)) {
649 buf[o] = '&';
650 *len += 4;
651 } else if (!memcmp(&data[*len], "apos;", 5)) {
652 buf[o] = '\'';
653 *len += 5;
654 } else if (!memcmp(&data[*len], "quot;", 5)) {
655 buf[o] = '\"';
656 *len += 5;
657 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +0100658 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200659 goto error;
660 }
661 } else {
662 /* character reference */
663 (*len)++;
664 if (isdigit(data[*len])) {
665 for (n = 0; isdigit(data[*len]); (*len)++) {
666 n = (10 * n) + (data[*len] - '0');
667 }
668 if (data[*len] != ';') {
Radek Krejci48464ed2016-03-17 15:44:09 +0100669 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200670 goto error;
671 }
672 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
673 for (n = 0; isxdigit(data[*len]); (*len)++) {
674 if (isdigit(data[*len])) {
675 r = (data[*len] - '0');
676 } else if (data[*len] > 'F') {
677 r = 10 + (data[*len] - 'a');
678 } else {
679 r = 10 + (data[*len] - 'A');
680 }
681 n = (16 * n) + r;
682 }
683 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +0100684 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200685 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200686
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200687 }
Radek Krejci48464ed2016-03-17 15:44:09 +0100688 r = pututf8(&buf[o], n);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200689 if (!r) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100690 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200691 goto error;
692 }
693 o += r - 1; /* o is ++ in for loop */
694 (*len)++;
695 }
696 } else {
Radek Krejcideee60e2016-09-23 15:21:14 +0200697 r = copyutf8(&buf[o], &data[*len]);
698 if (!r) {
699 goto error;
700 }
701
702 o += r - 1; /* o is ++ in for loop */
703 (*len) = (*len) + r;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200704 }
705 }
Radek Krejci02117302015-04-13 16:32:44 +0200706
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200707 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
708 /* ignore loop's end condition on beginning of CDSect */
709 goto loop;
710 }
Radek Krejci709fee62015-04-15 13:56:19 +0200711#undef BUFSIZE
712
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200713 if (o) {
714 if (result) {
715 size = size + o;
716 aux = realloc(result, size + 1);
717 result = aux;
718 } else {
719 size = o;
720 result = malloc((size + 1) * sizeof *result);
721 }
Michal Vasko253035f2015-12-17 16:58:13 +0100722 if (!result) {
723 LOGMEM;
724 return NULL;
725 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200726 memcpy(&result[size - o], buf, o);
727 }
728 if (result) {
729 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200730 } else {
731 size = 0;
732 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200733 }
Radek Krejci02117302015-04-13 16:32:44 +0200734
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200735 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200736
737error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200738 free(result);
739 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200740}
741
Michal Vasko0d343d12015-08-24 14:57:36 +0200742/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200743static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200744parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200745{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200746 const char *c = data, *start, *delim;
747 char prefix[32];
748 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200749 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200750 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200751
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200752 /* check if it is attribute or namespace */
753 if (!memcmp(c, "xmlns", 5)) {
754 /* namespace */
755 attr = calloc(1, sizeof (struct lyxml_ns));
Michal Vasko253035f2015-12-17 16:58:13 +0100756 if (!attr) {
757 LOGMEM;
758 return NULL;
759 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200760 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200761 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200762 c += 5;
763 if (*c != ':') {
764 /* default namespace, prefix will be empty */
765 goto equal;
766 }
767 c++; /* go after ':' to the prefix value */
768 } else {
769 /* attribute */
770 attr = calloc(1, sizeof *attr);
Michal Vasko253035f2015-12-17 16:58:13 +0100771 if (!attr) {
772 LOGMEM;
773 return NULL;
774 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200775 attr->type = LYXML_ATTR_STD;
776 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200777
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200778 /* process name part of the attribute */
779 start = c;
Radek Krejci48464ed2016-03-17 15:44:09 +0100780 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200781 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100782 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200783 free(attr);
784 return NULL;
785 }
786 c += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100787 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200788 while (is_xmlnamechar(uc)) {
789 if (attr->type == LYXML_ATTR_STD && *c == ':') {
790 /* attribute in a namespace */
791 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200792
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200793 /* look for the prefix in namespaces */
794 memcpy(prefix, data, c - data);
795 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200796 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200797 }
798 c += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100799 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200800 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200801
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200802 /* store the name */
803 size = c - start;
804 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200805
806equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200807 /* check Eq mark that can be surrounded by whitespaces */
808 ign_xmlws(c);
809 if (*c != '=') {
Radek Krejci48464ed2016-03-17 15:44:09 +0100810 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200811 goto error;
812 }
813 c++;
814 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200815
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200816 /* process value part of the attribute */
817 if (!*c || (*c != '"' && *c != '\'')) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100818 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200819 goto error;
820 }
821 delim = c;
822 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
823 if (ly_errno) {
824 goto error;
825 }
Radek Krejci02117302015-04-13 16:32:44 +0200826
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200827 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200828
829 /* put attribute into the parent's attributes list */
830 if (parent->attr) {
831 /* go to the end of the list */
832 for (a = parent->attr; a->next; a = a->next);
833 /* and append new attribute */
834 a->next = attr;
835 } else {
836 /* add the first attribute in the list */
837 parent->attr = attr;
838 }
839
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200840 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200841
842error:
Radek Krejci00249f22015-07-07 13:43:28 +0200843 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200844 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200845}
846
Michal Vasko0d343d12015-08-24 14:57:36 +0200847/* logs directly */
Radek Krejci9a5daea2016-03-02 16:49:40 +0100848struct lyxml_elem *
849lyxml_parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200850{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200851 const char *c = data, *start, *e;
852 const char *lws; /* leading white space for handling mixed content */
853 int uc;
854 char *str;
855 char prefix[32] = { 0 };
856 unsigned int prefix_len = 0;
857 struct lyxml_elem *elem = NULL, *child;
858 struct lyxml_attr *attr;
859 unsigned int size;
860 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200861
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200862 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200863
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200864 if (*c != '<') {
865 return NULL;
866 }
Radek Krejci02117302015-04-13 16:32:44 +0200867
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200868 /* locate element name */
869 c++;
870 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200871
Radek Krejci48464ed2016-03-17 15:44:09 +0100872 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200873 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100874 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200875 return NULL;
876 }
877 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100878 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200879 while (is_xmlnamechar(uc)) {
880 if (*e == ':') {
881 if (prefix_len) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100882 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200883 goto error;
884 }
885 /* element in a namespace */
886 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200887
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200888 /* look for the prefix in namespaces */
889 memcpy(prefix, c, prefix_len = e - c);
890 prefix[prefix_len] = '\0';
891 c = start;
892 }
893 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100894 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200895 }
896 if (!*e) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100897 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200898 return NULL;
899 }
Radek Krejci02117302015-04-13 16:32:44 +0200900
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200901 /* allocate element structure */
902 elem = calloc(1, sizeof *elem);
Michal Vasko253035f2015-12-17 16:58:13 +0100903 if (!elem) {
904 LOGMEM;
905 return NULL;
906 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200907 elem->next = NULL;
908 elem->prev = elem;
909 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200910 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200911 }
Radek Krejci02117302015-04-13 16:32:44 +0200912
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200913 /* store the name into the element structure */
914 elem->name = lydict_insert(ctx, c, e - c);
915 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200916
917process:
Radek Krejcidb4f0782016-05-03 12:07:16 +0200918 ly_errno = LY_SUCCESS;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200919 ign_xmlws(c);
920 if (!memcmp("/>", c, 2)) {
921 /* we are done, it was EmptyElemTag */
922 c += 2;
Michal Vasko44913842016-04-13 14:20:41 +0200923 elem->content = lydict_insert(ctx, "", 0);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200924 closed_flag = 1;
925 } else if (*c == '>') {
926 /* process element content */
927 c++;
928 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200929
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200930 while (*c) {
931 if (!memcmp(c, "</", 2)) {
932 if (lws && !elem->child) {
933 /* leading white spaces were actually content */
934 goto store_content;
935 }
Radek Krejci02117302015-04-13 16:32:44 +0200936
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200937 /* Etag */
938 c += 2;
939 /* get name and check it */
940 e = c;
Radek Krejci48464ed2016-03-17 15:44:09 +0100941 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200942 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100943 LOGVAL(LYE_XML_INVAL, LY_VLOG_XML, elem, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200944 goto error;
945 }
946 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100947 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200948 while (is_xmlnamechar(uc)) {
949 if (*e == ':') {
950 /* element in a namespace */
951 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200952
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200953 /* look for the prefix in namespaces */
954 if (memcmp(prefix, c, e - c)) {
Michal Vaskoff9336a2016-05-10 10:48:48 +0200955 LOGVAL(LYE_SPEC, LY_VLOG_XML, elem,
956 "Invalid (different namespaces) opening (%s) and closing element tags.", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200957 goto error;
958 }
959 c = start;
960 }
961 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100962 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200963 }
964 if (!*e) {
Radek Krejci3cc10962016-04-13 15:03:27 +0200965 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200966 goto error;
967 }
Radek Krejci02117302015-04-13 16:32:44 +0200968
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200969 /* check that it corresponds to opening tag */
970 size = e - c;
971 str = malloc((size + 1) * sizeof *str);
Michal Vasko253035f2015-12-17 16:58:13 +0100972 if (!str) {
973 LOGMEM;
974 goto error;
975 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200976 memcpy(str, c, e - c);
977 str[e - c] = '\0';
978 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Michal Vaskoff9336a2016-05-10 10:48:48 +0200979 LOGVAL(LYE_SPEC, LY_VLOG_XML, elem,
980 "Invalid (mixed names) opening (%s) and closing (%s) element tags.", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200981 free(str);
982 goto error;
983 }
984 free(str);
985 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200986
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200987 ign_xmlws(c);
988 if (*c != '>') {
Michal Vaskoff9336a2016-05-10 10:48:48 +0200989 LOGVAL(LYE_SPEC, LY_VLOG_XML, elem, "Data after closing element tag \"%s\".", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200990 goto error;
991 }
992 c++;
Michal Vaskoe00b7892016-04-14 10:12:18 +0200993 if (!(elem->flags & LYXML_ELEM_MIXED) && !elem->content) {
994 /* there was no content, but we don't want NULL (only if mixed content) */
995 elem->content = lydict_insert(ctx, "", 0);
996 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200997 closed_flag = 1;
998 break;
Radek Krejci02117302015-04-13 16:32:44 +0200999
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001000 } else if (!memcmp(c, "<?", 2)) {
1001 if (lws) {
1002 /* leading white spaces were only formatting */
1003 lws = NULL;
1004 }
1005 /* PI - ignore it */
1006 c += 2;
1007 if (parse_ignore(c, "?>", &size)) {
1008 goto error;
1009 }
1010 c += size;
1011 } else if (!memcmp(c, "<!--", 4)) {
1012 if (lws) {
1013 /* leading white spaces were only formatting */
1014 lws = NULL;
1015 }
1016 /* Comment - ignore it */
1017 c += 4;
1018 if (parse_ignore(c, "-->", &size)) {
1019 goto error;
1020 }
1021 c += size;
1022 } else if (!memcmp(c, "<![CDATA[", 9)) {
1023 /* CDSect */
1024 goto store_content;
1025 } else if (*c == '<') {
1026 if (lws) {
1027 if (elem->flags & LYXML_ELEM_MIXED) {
1028 /* we have a mixed content */
1029 goto store_content;
1030 } else {
1031 /* leading white spaces were only formatting */
1032 lws = NULL;
1033 }
1034 }
1035 if (elem->content) {
1036 /* we have a mixed content */
1037 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001038 if (!child) {
1039 LOGMEM;
1040 goto error;
1041 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001042 child->content = elem->content;
1043 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001044 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001045 elem->flags |= LYXML_ELEM_MIXED;
1046 }
Radek Krejci9a5daea2016-03-02 16:49:40 +01001047 child = lyxml_parse_elem(ctx, c, &size, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001048 if (!child) {
1049 goto error;
1050 }
1051 c += size; /* move after processed child element */
1052 } else if (is_xmlws(*c)) {
1053 lws = c;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001054 ign_xmlws(c);
1055 } else {
Radek Krejci02117302015-04-13 16:32:44 +02001056store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001057 /* store text content */
1058 if (lws) {
1059 /* process content including the leading white spaces */
1060 c = lws;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001061 lws = NULL;
1062 }
1063 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1064 if (ly_errno) {
1065 goto error;
1066 }
1067 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001068
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001069 if (elem->child) {
1070 /* we have a mixed content */
1071 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001072 if (!child) {
1073 LOGMEM;
1074 goto error;
1075 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001076 child->content = elem->content;
1077 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001078 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001079 elem->flags |= LYXML_ELEM_MIXED;
1080 }
1081 }
1082 }
1083 } else {
1084 /* process attribute */
1085 attr = parse_attr(ctx, c, &size, elem);
1086 if (!attr) {
1087 goto error;
1088 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001089 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001090
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001091 /* check namespace */
1092 if (attr->type == LYXML_ATTR_NS) {
1093 if (!prefix[0] && !attr->name) {
1094 if (attr->value) {
1095 /* default prefix */
1096 elem->ns = (struct lyxml_ns *)attr;
1097 } else {
1098 /* xmlns="" -> no namespace */
1099 nons_flag = 1;
1100 }
1101 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1102 /* matching namespace with prefix */
1103 elem->ns = (struct lyxml_ns *)attr;
1104 }
1105 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001106
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001107 /* go back to finish element processing */
1108 goto process;
1109 }
Radek Krejci02117302015-04-13 16:32:44 +02001110
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001111 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001112
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001113 if (!closed_flag) {
Radek Krejci48464ed2016-03-17 15:44:09 +01001114 LOGVAL(LYE_XML_MISS, LY_VLOG_XML, elem, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001115 goto error;
1116 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001117
Radek Krejci78a230a2015-07-07 17:04:40 +02001118 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001119 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001120 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001121
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001122 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001123
1124error:
Michal Vasko345da0a2015-12-02 10:35:55 +01001125 lyxml_free(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001126
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001127 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001128}
1129
Michal Vasko0d343d12015-08-24 14:57:36 +02001130/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001131API struct lyxml_elem *
Radek Krejci722b0072016-02-01 17:09:45 +01001132lyxml_parse_mem(struct ly_ctx *ctx, const char *data, int options)
Radek Krejci54ea8de2015-04-09 18:02:56 +02001133{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001134 const char *c = data;
1135 unsigned int len;
Radek Krejci851ea662016-01-08 09:30:53 +01001136 struct lyxml_elem *root, *first = NULL, *next;
Radek Krejci02117302015-04-13 16:32:44 +02001137
Radek Krejci2342cf62016-01-29 16:48:23 +01001138 ly_errno = LY_SUCCESS;
1139
Radek Krejci120f6242015-12-17 12:32:56 +01001140repeat:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001141 /* process document */
Radek Krejcif8ae23e2016-07-26 17:11:17 +02001142 while (1) {
1143 if (!*c) {
1144 /* eof */
1145 return first;
1146 } else if (is_xmlws(*c)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001147 /* skip whitespaces */
1148 ign_xmlws(c);
1149 } else if (!memcmp(c, "<?", 2)) {
1150 /* XMLDecl or PI - ignore it */
1151 c += 2;
1152 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001153 return NULL;
1154 }
1155 c += len;
1156 } else if (!memcmp(c, "<!--", 4)) {
1157 /* Comment - ignore it */
1158 c += 2;
1159 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001160 return NULL;
1161 }
1162 c += len;
1163 } else if (!memcmp(c, "<!", 2)) {
1164 /* DOCTYPE */
1165 /* TODO - standalone ignore counting < and > */
1166 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1167 return NULL;
1168 } else if (*c == '<') {
1169 /* element - process it in next loop to strictly follow XML
1170 * format
1171 */
1172 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001173 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +01001174 LOGVAL(LYE_XML_INCHAR, LY_VLOG_NONE, NULL, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001175 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001176 }
1177 }
Radek Krejci02117302015-04-13 16:32:44 +02001178
Radek Krejci9a5daea2016-03-02 16:49:40 +01001179 root = lyxml_parse_elem(ctx, c, &len, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001180 if (!root) {
Michal Vaskobc58b4a2016-01-07 14:42:31 +01001181 if (first) {
Radek Krejci851ea662016-01-08 09:30:53 +01001182 LY_TREE_FOR_SAFE(first, next, root) {
Michal Vaskobc58b4a2016-01-07 14:42:31 +01001183 lyxml_free(ctx, root);
1184 }
Radek Krejci120f6242015-12-17 12:32:56 +01001185 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001186 return NULL;
Radek Krejci120f6242015-12-17 12:32:56 +01001187 } else if (!first) {
1188 first = root;
1189 } else {
1190 first->prev->next = root;
1191 root->prev = first->prev;
1192 first->prev = root;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001193 }
1194 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001195
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001196 /* ignore the rest of document where can be comments, PIs and whitespaces,
1197 * note that we are not detecting syntax errors in these parts
1198 */
1199 ign_xmlws(c);
1200 if (*c) {
Radek Krejci722b0072016-02-01 17:09:45 +01001201 if (options & LYXML_PARSE_MULTIROOT) {
Radek Krejci120f6242015-12-17 12:32:56 +01001202 goto repeat;
1203 } else {
1204 LOGWRN("There are some not parsed data:\n%s", c);
1205 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001206 }
Radek Krejci02117302015-04-13 16:32:44 +02001207
Radek Krejci120f6242015-12-17 12:32:56 +01001208 return first;
Radek Krejci02117302015-04-13 16:32:44 +02001209}
1210
Radek Krejcic6704c82015-10-06 11:12:45 +02001211API struct lyxml_elem *
Radek Krejci722b0072016-02-01 17:09:45 +01001212lyxml_parse_path(struct ly_ctx *ctx, const char *filename, int options)
Radek Krejci54ea8de2015-04-09 18:02:56 +02001213{
Radek Krejci6b3d9262015-12-03 13:45:27 +01001214 struct lyxml_elem *elem = NULL;
Pavol Vicanb2570c12015-11-12 13:50:20 +01001215 struct stat sb;
1216 int fd;
1217 char *addr;
1218
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001219 if (!filename || !ctx) {
1220 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1221 return NULL;
1222 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001223
Pavol Vicanb2570c12015-11-12 13:50:20 +01001224 fd = open(filename, O_RDONLY);
1225 if (fd == -1) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001226 LOGERR(LY_EINVAL,"Opening file \"%s\" failed.", filename);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001227 return NULL;
1228 }
1229 if (fstat(fd, &sb) == -1) {
1230 LOGERR(LY_EINVAL, "Unable to get file \"%s\" information.\n", filename);
1231 goto error;
1232 }
1233 if (!S_ISREG(sb.st_mode)) {
Radek Krejcib051f722016-02-25 15:12:21 +01001234 LOGERR(LY_EINVAL, "%s: Invalid parameter, input file is not a regular file", __func__);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001235 goto error;
1236 }
Pavol Vicanf7cc2852016-03-22 23:27:35 +01001237 addr = mmap(NULL, sb.st_size + 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001238 if (addr == MAP_FAILED) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001239 LOGERR(LY_EMEM,"Map file into memory failed (%s()).", __func__);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001240 goto error;
1241 }
Radek Krejci6b3d9262015-12-03 13:45:27 +01001242
Radek Krejci722b0072016-02-01 17:09:45 +01001243 elem = lyxml_parse_mem(ctx, addr, options);
Pavol Vicanf7cc2852016-03-22 23:27:35 +01001244 munmap(addr, sb.st_size +2);
Radek Krejci30793ab2015-12-03 13:45:45 +01001245 close(fd);
Radek Krejci6b3d9262015-12-03 13:45:27 +01001246
Pavol Vicanb2570c12015-11-12 13:50:20 +01001247 return elem;
1248
1249error:
Radek Krejci6b3d9262015-12-03 13:45:27 +01001250 if (fd != -1) {
1251 close(fd);
1252 }
1253
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001254 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001255}
Radek Krejci02117302015-04-13 16:32:44 +02001256
Michal Vasko5db027d2015-10-09 14:38:50 +02001257int
1258lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001259{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001260 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001261
Michal Vasko5db027d2015-10-09 14:38:50 +02001262 if (!text) {
1263 return 0;
1264 }
1265
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001266 for (i = n = 0; text[i]; i++) {
1267 switch (text[i]) {
1268 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001269 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001270 break;
1271 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001272 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001273 break;
1274 case '>':
1275 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001276 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001277 break;
Radek Krejci952a7252016-07-16 20:52:43 +02001278 case '"':
1279 n += ly_print(out, "&quot;");
1280 break;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001281 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001282 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001283 n++;
1284 }
1285 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001286
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001287 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001288}
1289
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001290static int
Michal Vasko1e62a092015-12-01 12:27:20 +01001291dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001292{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001293 int size = 0;
1294 struct lyxml_attr *a;
1295 struct lyxml_elem *child;
1296 const char *delim, *delim_outer;
1297 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001298
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001299 if (!e->name) {
1300 /* mixed content */
1301 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001302 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001303 } else {
1304 return 0;
1305 }
1306 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001307
Radek Krejci722b0072016-02-01 17:09:45 +01001308 delim = delim_outer = (options & LYXML_PRINT_FORMAT) ? "\n" : "";
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 indent = 2 * level;
1310 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1311 delim = "";
1312 }
1313 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1314 delim_outer = "";
1315 indent = 0;
1316 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001317
Radek Krejci722b0072016-02-01 17:09:45 +01001318 if (!(options & (LYXML_PRINT_OPEN | LYXML_PRINT_CLOSE | LYXML_PRINT_ATTRS)) || (options & LYXML_PRINT_OPEN)) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001319 /* opening tag */
1320 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001321 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001322 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001323 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001324 }
Radek Krejci722b0072016-02-01 17:09:45 +01001325 } else if (options & LYXML_PRINT_CLOSE) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001326 indent = 0;
1327 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001328 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001329
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001330 /* attributes */
1331 for (a = e->attr; a; a = a->next) {
1332 if (a->type == LYXML_ATTR_NS) {
1333 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001334 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001335 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001336 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001337 }
1338 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001339 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001340 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001341 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001342 }
1343 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001344
Radek Krejcic6704c82015-10-06 11:12:45 +02001345 /* apply options */
Radek Krejci722b0072016-02-01 17:09:45 +01001346 if ((options & LYXML_PRINT_CLOSE) && (options & LYXML_PRINT_OPEN)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001347 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001348 return size;
Radek Krejci722b0072016-02-01 17:09:45 +01001349 } else if (options & LYXML_PRINT_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001350 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001351 return ++size;
Radek Krejci722b0072016-02-01 17:09:45 +01001352 } else if (options & LYXML_PRINT_ATTRS) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001353 return size;
1354 }
1355
Michal Vasko3a611612016-04-14 10:12:56 +02001356 if (!e->child && (!e->content || !e->content[0])) {
Radek Krejci5248f132015-10-09 10:34:25 +02001357 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001358 return size;
Michal Vasko3a611612016-04-14 10:12:56 +02001359 } else if (e->content && e->content[0]) {
Radek Krejci5248f132015-10-09 10:34:25 +02001360 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001361 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001362
Michal Vasko5db027d2015-10-09 14:38:50 +02001363 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001364
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001365 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001366 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001367 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001368 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001369 }
1370 return size;
1371 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001372 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001373 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001374
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001375 /* go recursively */
1376 LY_TREE_FOR(e->child, child) {
Radek Krejci722b0072016-02-01 17:09:45 +01001377 if (options & LYXML_PRINT_FORMAT) {
1378 size += dump_elem(out, child, level + 1, LYXML_PRINT_FORMAT);
Pavol Vicanbe7eef52015-10-22 14:07:48 +02001379 } else {
1380 size += dump_elem(out, child, level, 0);
1381 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001382 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001383
Radek Krejcic6704c82015-10-06 11:12:45 +02001384close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001385 /* closing tag */
1386 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001387 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001388 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001389 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001390 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001391
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001392 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001393}
1394
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001395static int
1396dump_siblings(struct lyout *out, const struct lyxml_elem *e, int options)
1397{
1398 const struct lyxml_elem *start, *iter;
1399 int ret = 0;
1400
1401 if (e->parent) {
1402 start = e->parent->child;
1403 } else {
1404 start = e;
1405 while(start->prev && start->prev->next) {
1406 start = start->prev;
1407 }
1408 }
1409
1410 LY_TREE_FOR(start, iter) {
1411 ret += dump_elem(out, iter, 0, options);
1412 }
1413
1414 return ret;
1415}
1416
Radek Krejcic6704c82015-10-06 11:12:45 +02001417API int
Radek Krejci722b0072016-02-01 17:09:45 +01001418lyxml_print_file(FILE *stream, const struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001419{
Radek Krejci5248f132015-10-09 10:34:25 +02001420 struct lyout out;
1421
1422 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001423 return 0;
1424 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001425
Radek Krejci5248f132015-10-09 10:34:25 +02001426 out.type = LYOUT_STREAM;
1427 out.method.f = stream;
1428
Radek Krejci722b0072016-02-01 17:09:45 +01001429 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001430 return dump_siblings(&out, elem, options);
1431 } else {
1432 return dump_elem(&out, elem, 0, options);
1433 }
Radek Krejci5248f132015-10-09 10:34:25 +02001434}
1435
1436API int
Radek Krejci722b0072016-02-01 17:09:45 +01001437lyxml_print_fd(int fd, const struct lyxml_elem *elem, int options)
Radek Krejci5248f132015-10-09 10:34:25 +02001438{
1439 struct lyout out;
1440
1441 if (fd < 0 || !elem) {
1442 return 0;
1443 }
1444
1445 out.type = LYOUT_FD;
1446 out.method.fd = fd;
1447
Radek Krejci722b0072016-02-01 17:09:45 +01001448 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001449 return dump_siblings(&out, elem, options);
1450 } else {
1451 return dump_elem(&out, elem, 0, options);
1452 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001453}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001454
1455API int
Radek Krejci722b0072016-02-01 17:09:45 +01001456lyxml_print_mem(char **strp, const struct lyxml_elem *elem, int options)
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001457{
1458 struct lyout out;
1459 int r;
1460
1461 if (!strp || !elem) {
1462 return 0;
1463 }
1464
1465 out.type = LYOUT_MEMORY;
1466 out.method.mem.buf = NULL;
1467 out.method.mem.len = 0;
1468 out.method.mem.size = 0;
1469
Radek Krejci722b0072016-02-01 17:09:45 +01001470 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001471 r = dump_siblings(&out, elem, options);
1472 } else {
1473 r = dump_elem(&out, elem, 0, options);
1474 }
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001475
1476 *strp = out.method.mem.buf;
1477 return r;
1478}
1479
1480API int
Radek Krejci722b0072016-02-01 17:09:45 +01001481lyxml_print_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 +02001482{
1483 struct lyout out;
1484
1485 if (!writeclb || !elem) {
1486 return 0;
1487 }
1488
1489 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001490 out.method.clb.f = writeclb;
1491 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001492
Radek Krejci722b0072016-02-01 17:09:45 +01001493 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001494 return dump_siblings(&out, elem, options);
1495 } else {
1496 return dump_elem(&out, elem, 0, options);
1497 }
Radek Krejci6140e4e2015-10-09 15:50:55 +02001498}