blob: 2674fe0d63c6b0616b757988ca69ce867da9ccc6 [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? */
100 attr->ns = lyxml_get_ns(attr->ns->parent, tmp_ns->prefix);
101
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
Michal Vasko88c29542015-11-27 14:57:53 +0100401API const char *
Michal Vasko1e62a092015-12-01 12:27:20 +0100402lyxml_get_attr(const struct lyxml_elem *elem, const char *name, const char *ns)
Radek Krejcida04f4a2015-05-21 12:54:09 +0200403{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200404 struct lyxml_attr *a;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200405
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200406 assert(elem);
407 assert(name);
Radek Krejcida04f4a2015-05-21 12:54:09 +0200408
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200409 for (a = elem->attr; a; a = a->next) {
410 if (a->type != LYXML_ATTR_STD) {
411 continue;
412 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200413
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200414 if (!strcmp(name, a->name)) {
415 if ((!ns && !a->ns) || (ns && a->ns && !strcmp(ns, a->ns->value))) {
416 return a->value;
417 }
418 }
419 }
Radek Krejcida04f4a2015-05-21 12:54:09 +0200420
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200421 return NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +0200422}
423
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200424int
Michal Vaskof8879c22015-08-21 09:07:36 +0200425lyxml_add_child(struct ly_ctx *ctx, struct lyxml_elem *parent, struct lyxml_elem *elem)
Radek Krejci02117302015-04-13 16:32:44 +0200426{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200427 struct lyxml_elem *e;
Radek Krejci02117302015-04-13 16:32:44 +0200428
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200429 assert(parent);
430 assert(elem);
Radek Krejci02117302015-04-13 16:32:44 +0200431
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200432 /* (re)link element to parent */
433 if (elem->parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200434 lyxml_unlink_elem(ctx, elem, 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200435 }
436 elem->parent = parent;
Radek Krejci02117302015-04-13 16:32:44 +0200437
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200438 /* link parent to element */
439 if (parent->child) {
440 e = parent->child;
441 elem->prev = e->prev;
442 elem->next = NULL;
443 elem->prev->next = elem;
444 e->prev = elem;
445 } else {
446 parent->child = elem;
447 elem->prev = elem;
448 elem->next = NULL;
449 }
Radek Krejci02117302015-04-13 16:32:44 +0200450
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200451 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200452}
453
Michal Vasko3b855722015-08-28 16:01:18 +0200454int
Radek Krejci48464ed2016-03-17 15:44:09 +0100455lyxml_getutf8(const char *buf, unsigned int *read)
Radek Krejci02117302015-04-13 16:32:44 +0200456{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200457 int c, aux;
458 int i;
Radek Krejci02117302015-04-13 16:32:44 +0200459
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200460 c = buf[0];
461 *read = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200462
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200463 /* buf is NULL terminated string, so 0 means EOF */
464 if (!c) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100465 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200466 return 0;
467 }
468 *read = 1;
Radek Krejci02117302015-04-13 16:32:44 +0200469
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200470 /* process character byte(s) */
471 if ((c & 0xf8) == 0xf0) {
472 /* four bytes character */
473 *read = 4;
Radek Krejci02117302015-04-13 16:32:44 +0200474
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200475 c &= 0x07;
476 for (i = 1; i <= 3; i++) {
477 aux = buf[i];
478 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100479 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200480 return 0;
481 }
Radek Krejci02117302015-04-13 16:32:44 +0200482
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200483 c = (c << 6) | (aux & 0x3f);
484 }
Radek Krejci02117302015-04-13 16:32:44 +0200485
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200486 if (c < 0x1000 || c > 0x10ffff) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100487 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200488 return 0;
489 }
490 } else if ((c & 0xf0) == 0xe0) {
491 /* three bytes character */
492 *read = 3;
Radek Krejci02117302015-04-13 16:32:44 +0200493
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200494 c &= 0x0f;
495 for (i = 1; i <= 2; i++) {
496 aux = buf[i];
497 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100498 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200499 return 0;
500 }
Radek Krejci02117302015-04-13 16:32:44 +0200501
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200502 c = (c << 6) | (aux & 0x3f);
503 }
Radek Krejci02117302015-04-13 16:32:44 +0200504
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200505 if (c < 0x800 || (c > 0xd7ff && c < 0xe000) || c > 0xfffd) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100506 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200507 return 0;
508 }
509 } else if ((c & 0xe0) == 0xc0) {
510 /* two bytes character */
511 *read = 2;
Radek Krejci02117302015-04-13 16:32:44 +0200512
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200513 aux = buf[1];
514 if ((aux & 0xc0) != 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100515 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200516 return 0;
517 }
518 c = ((c & 0x1f) << 6) | (aux & 0x3f);
Radek Krejci02117302015-04-13 16:32:44 +0200519
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200520 if (c < 0x80) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100521 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200522 return 0;
523 }
524 } else if (!(c & 0x80)) {
525 /* one byte character */
526 if (c < 0x20 && c != 0x9 && c != 0xa && c != 0xd) {
527 /* invalid character */
Radek Krejci48464ed2016-03-17 15:44:09 +0100528 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200529 return 0;
530 }
531 } else {
532 /* invalid character */
Radek Krejci48464ed2016-03-17 15:44:09 +0100533 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "input character");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200534 return 0;
535 }
Radek Krejci02117302015-04-13 16:32:44 +0200536
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200537 return c;
Radek Krejci02117302015-04-13 16:32:44 +0200538}
539
Michal Vasko0d343d12015-08-24 14:57:36 +0200540/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200541static int
542parse_ignore(const char *data, const char *endstr, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200543{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200544 unsigned int slen;
545 const char *c = data;
Radek Krejci02117302015-04-13 16:32:44 +0200546
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200547 slen = strlen(endstr);
Radek Krejci02117302015-04-13 16:32:44 +0200548
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200549 while (*c && memcmp(c, endstr, slen)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200550 c++;
551 }
552 if (!*c) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100553 LOGVAL(LYE_XML_MISS, LY_VLOG_NONE, NULL, "closing sequence", endstr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200554 return EXIT_FAILURE;
555 }
556 c += slen;
Radek Krejci02117302015-04-13 16:32:44 +0200557
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200558 *len = c - data;
559 return EXIT_SUCCESS;
Radek Krejci02117302015-04-13 16:32:44 +0200560}
561
Michal Vasko0d343d12015-08-24 14:57:36 +0200562/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200563static char *
564parse_text(const char *data, char delim, unsigned int *len)
Radek Krejci02117302015-04-13 16:32:44 +0200565{
Radek Krejci709fee62015-04-15 13:56:19 +0200566#define BUFSIZE 1024
Radek Krejci02117302015-04-13 16:32:44 +0200567
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200568 char buf[BUFSIZE];
569 char *result = NULL, *aux;
570 unsigned int r;
571 int o, size = 0;
572 int cdsect = 0;
573 int32_t n;
Radek Krejci709fee62015-04-15 13:56:19 +0200574
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200575 for (*len = o = 0; cdsect || data[*len] != delim; o++) {
576 if (!data[*len] || (!cdsect && !memcmp(&data[*len], "]]>", 2))) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100577 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element content, \"]]>\" found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200578 goto error;
579 }
Radek Krejci709fee62015-04-15 13:56:19 +0200580
Radek Krejcia4a84062015-04-16 13:00:10 +0200581loop:
582
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200583 if (o > BUFSIZE - 3) {
584 /* add buffer into the result */
585 if (result) {
586 size = size + o;
Michal Vasko253035f2015-12-17 16:58:13 +0100587 aux = ly_realloc(result, size + 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200588 result = aux;
589 } else {
590 size = o;
591 result = malloc((size + 1) * sizeof *result);
592 }
Michal Vasko253035f2015-12-17 16:58:13 +0100593 if (!result) {
594 LOGMEM;
595 return NULL;
596 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200597 memcpy(&result[size - o], buf, o);
Radek Krejci709fee62015-04-15 13:56:19 +0200598
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200599 /* write again into the beginning of the buffer */
600 o = 0;
601 }
Radek Krejci709fee62015-04-15 13:56:19 +0200602
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200603 if (cdsect || !memcmp(&data[*len], "<![CDATA[", 9)) {
604 /* CDSect */
605 if (!cdsect) {
606 cdsect = 1;
607 *len += 9;
608 }
609 if (data[*len] && !memcmp(&data[*len], "]]>", 3)) {
610 *len += 3;
611 cdsect = 0;
612 o--; /* we don't write any data in this iteration */
613 } else {
614 buf[o] = data[*len];
615 (*len)++;
616 }
617 } else if (data[*len] == '&') {
618 (*len)++;
619 if (data[*len] != '#') {
620 /* entity reference - only predefined refs are supported */
621 if (!memcmp(&data[*len], "lt;", 3)) {
622 buf[o] = '<';
623 *len += 3;
624 } else if (!memcmp(&data[*len], "gt;", 3)) {
625 buf[o] = '>';
626 *len += 3;
627 } else if (!memcmp(&data[*len], "amp;", 4)) {
628 buf[o] = '&';
629 *len += 4;
630 } else if (!memcmp(&data[*len], "apos;", 5)) {
631 buf[o] = '\'';
632 *len += 5;
633 } else if (!memcmp(&data[*len], "quot;", 5)) {
634 buf[o] = '\"';
635 *len += 5;
636 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +0100637 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "entity reference (only predefined references are supported)");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200638 goto error;
639 }
640 } else {
641 /* character reference */
642 (*len)++;
643 if (isdigit(data[*len])) {
644 for (n = 0; isdigit(data[*len]); (*len)++) {
645 n = (10 * n) + (data[*len] - '0');
646 }
647 if (data[*len] != ';') {
Radek Krejci48464ed2016-03-17 15:44:09 +0100648 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference, missing semicolon");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200649 goto error;
650 }
651 } else if (data[(*len)++] == 'x' && isxdigit(data[*len])) {
652 for (n = 0; isxdigit(data[*len]); (*len)++) {
653 if (isdigit(data[*len])) {
654 r = (data[*len] - '0');
655 } else if (data[*len] > 'F') {
656 r = 10 + (data[*len] - 'a');
657 } else {
658 r = 10 + (data[*len] - 'A');
659 }
660 n = (16 * n) + r;
661 }
662 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +0100663 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200664 goto error;
Radek Krejci709fee62015-04-15 13:56:19 +0200665
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200666 }
Radek Krejci48464ed2016-03-17 15:44:09 +0100667 r = pututf8(&buf[o], n);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200668 if (!r) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100669 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "character reference value");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200670 goto error;
671 }
672 o += r - 1; /* o is ++ in for loop */
673 (*len)++;
674 }
675 } else {
676 buf[o] = data[*len];
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200677 (*len)++;
678 }
679 }
Radek Krejci02117302015-04-13 16:32:44 +0200680
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200681 if (delim == '<' && !memcmp(&data[*len], "<![CDATA[", 9)) {
682 /* ignore loop's end condition on beginning of CDSect */
683 goto loop;
684 }
Radek Krejci709fee62015-04-15 13:56:19 +0200685#undef BUFSIZE
686
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200687 if (o) {
688 if (result) {
689 size = size + o;
690 aux = realloc(result, size + 1);
691 result = aux;
692 } else {
693 size = o;
694 result = malloc((size + 1) * sizeof *result);
695 }
Michal Vasko253035f2015-12-17 16:58:13 +0100696 if (!result) {
697 LOGMEM;
698 return NULL;
699 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200700 memcpy(&result[size - o], buf, o);
701 }
702 if (result) {
703 result[size] = '\0';
Radek Krejcia5269642015-07-20 19:04:11 +0200704 } else {
705 size = 0;
706 result = strdup("");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200707 }
Radek Krejci02117302015-04-13 16:32:44 +0200708
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200709 return result;
Radek Krejci709fee62015-04-15 13:56:19 +0200710
711error:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200712 free(result);
713 return NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200714}
715
Michal Vasko0d343d12015-08-24 14:57:36 +0200716/* logs directly */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200717static struct lyxml_attr *
Radek Krejci00249f22015-07-07 13:43:28 +0200718parse_attr(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci674e1f82015-04-21 14:12:19 +0200719{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200720 const char *c = data, *start, *delim;
721 char prefix[32];
722 int uc;
Radek Krejci00249f22015-07-07 13:43:28 +0200723 struct lyxml_attr *attr = NULL, *a;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200724 unsigned int size;
Radek Krejci02117302015-04-13 16:32:44 +0200725
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200726 /* check if it is attribute or namespace */
727 if (!memcmp(c, "xmlns", 5)) {
728 /* namespace */
729 attr = calloc(1, sizeof (struct lyxml_ns));
Michal Vasko253035f2015-12-17 16:58:13 +0100730 if (!attr) {
731 LOGMEM;
732 return NULL;
733 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200734 attr->type = LYXML_ATTR_NS;
Radek Krejci00249f22015-07-07 13:43:28 +0200735 ((struct lyxml_ns *)attr)->parent = parent;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200736 c += 5;
737 if (*c != ':') {
738 /* default namespace, prefix will be empty */
739 goto equal;
740 }
741 c++; /* go after ':' to the prefix value */
742 } else {
743 /* attribute */
744 attr = calloc(1, sizeof *attr);
Michal Vasko253035f2015-12-17 16:58:13 +0100745 if (!attr) {
746 LOGMEM;
747 return NULL;
748 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200749 attr->type = LYXML_ATTR_STD;
750 }
Radek Krejci4ea08382015-04-21 09:41:40 +0200751
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200752 /* process name part of the attribute */
753 start = c;
Radek Krejci48464ed2016-03-17 15:44:09 +0100754 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200755 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100756 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the attribute");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200757 free(attr);
758 return NULL;
759 }
760 c += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100761 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200762 while (is_xmlnamechar(uc)) {
763 if (attr->type == LYXML_ATTR_STD && *c == ':') {
764 /* attribute in a namespace */
765 start = c + 1;
Radek Krejci4ea08382015-04-21 09:41:40 +0200766
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200767 /* look for the prefix in namespaces */
768 memcpy(prefix, data, c - data);
769 prefix[c - data] = '\0';
Radek Krejci4476d412015-07-10 15:35:01 +0200770 attr->ns = lyxml_get_ns(parent, prefix);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200771 }
772 c += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100773 uc = lyxml_getutf8(c, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200774 }
Radek Krejci674e1f82015-04-21 14:12:19 +0200775
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200776 /* store the name */
777 size = c - start;
778 attr->name = lydict_insert(ctx, start, size);
Radek Krejci674e1f82015-04-21 14:12:19 +0200779
780equal:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200781 /* check Eq mark that can be surrounded by whitespaces */
782 ign_xmlws(c);
783 if (*c != '=') {
Radek Krejci48464ed2016-03-17 15:44:09 +0100784 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute definition, \"=\" expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200785 goto error;
786 }
787 c++;
788 ign_xmlws(c);
Radek Krejci02117302015-04-13 16:32:44 +0200789
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200790 /* process value part of the attribute */
791 if (!*c || (*c != '"' && *c != '\'')) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100792 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "attribute value, \" or \' expected");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200793 goto error;
794 }
795 delim = c;
796 attr->value = lydict_insert_zc(ctx, parse_text(++c, *delim, &size));
797 if (ly_errno) {
798 goto error;
799 }
Radek Krejci02117302015-04-13 16:32:44 +0200800
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200801 *len = c + size + 1 - data; /* +1 is delimiter size */
Radek Krejci00249f22015-07-07 13:43:28 +0200802
803 /* put attribute into the parent's attributes list */
804 if (parent->attr) {
805 /* go to the end of the list */
806 for (a = parent->attr; a->next; a = a->next);
807 /* and append new attribute */
808 a->next = attr;
809 } else {
810 /* add the first attribute in the list */
811 parent->attr = attr;
812 }
813
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200814 return attr;
Radek Krejci02117302015-04-13 16:32:44 +0200815
816error:
Radek Krejci00249f22015-07-07 13:43:28 +0200817 lyxml_free_attr(ctx, NULL, attr);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200818 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +0200819}
820
Michal Vasko0d343d12015-08-24 14:57:36 +0200821/* logs directly */
Radek Krejci9a5daea2016-03-02 16:49:40 +0100822struct lyxml_elem *
823lyxml_parse_elem(struct ly_ctx *ctx, const char *data, unsigned int *len, struct lyxml_elem *parent)
Radek Krejci54ea8de2015-04-09 18:02:56 +0200824{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200825 const char *c = data, *start, *e;
826 const char *lws; /* leading white space for handling mixed content */
827 int uc;
828 char *str;
829 char prefix[32] = { 0 };
830 unsigned int prefix_len = 0;
831 struct lyxml_elem *elem = NULL, *child;
832 struct lyxml_attr *attr;
833 unsigned int size;
834 int nons_flag = 0, closed_flag = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200835
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200836 *len = 0;
Radek Krejci02117302015-04-13 16:32:44 +0200837
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200838 if (*c != '<') {
839 return NULL;
840 }
Radek Krejci02117302015-04-13 16:32:44 +0200841
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200842 /* locate element name */
843 c++;
844 e = c;
Radek Krejci02117302015-04-13 16:32:44 +0200845
Radek Krejci48464ed2016-03-17 15:44:09 +0100846 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200847 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100848 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200849 return NULL;
850 }
851 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100852 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200853 while (is_xmlnamechar(uc)) {
854 if (*e == ':') {
855 if (prefix_len) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100856 LOGVAL(LYE_XML_INVAL, LY_VLOG_NONE, NULL, "element name, multiple colons found");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200857 goto error;
858 }
859 /* element in a namespace */
860 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200861
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200862 /* look for the prefix in namespaces */
863 memcpy(prefix, c, prefix_len = e - c);
864 prefix[prefix_len] = '\0';
865 c = start;
866 }
867 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100868 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200869 }
870 if (!*e) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100871 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200872 return NULL;
873 }
Radek Krejci02117302015-04-13 16:32:44 +0200874
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200875 /* allocate element structure */
876 elem = calloc(1, sizeof *elem);
Michal Vasko253035f2015-12-17 16:58:13 +0100877 if (!elem) {
878 LOGMEM;
879 return NULL;
880 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200881 elem->next = NULL;
882 elem->prev = elem;
883 if (parent) {
Michal Vaskof8879c22015-08-21 09:07:36 +0200884 lyxml_add_child(ctx, parent, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200885 }
Radek Krejci02117302015-04-13 16:32:44 +0200886
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200887 /* store the name into the element structure */
888 elem->name = lydict_insert(ctx, c, e - c);
889 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200890
891process:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200892 ly_errno = 0;
893 ign_xmlws(c);
894 if (!memcmp("/>", c, 2)) {
895 /* we are done, it was EmptyElemTag */
896 c += 2;
Michal Vasko44913842016-04-13 14:20:41 +0200897 elem->content = lydict_insert(ctx, "", 0);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200898 closed_flag = 1;
899 } else if (*c == '>') {
900 /* process element content */
901 c++;
902 lws = NULL;
Radek Krejci02117302015-04-13 16:32:44 +0200903
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200904 while (*c) {
905 if (!memcmp(c, "</", 2)) {
906 if (lws && !elem->child) {
907 /* leading white spaces were actually content */
908 goto store_content;
909 }
Radek Krejci02117302015-04-13 16:32:44 +0200910
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200911 /* Etag */
912 c += 2;
913 /* get name and check it */
914 e = c;
Radek Krejci48464ed2016-03-17 15:44:09 +0100915 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200916 if (!is_xmlnamestartchar(uc)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100917 LOGVAL(LYE_XML_INVAL, LY_VLOG_XML, elem, "NameStartChar of the element");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200918 goto error;
919 }
920 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100921 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200922 while (is_xmlnamechar(uc)) {
923 if (*e == ':') {
924 /* element in a namespace */
925 start = e + 1;
Radek Krejci674e1f82015-04-21 14:12:19 +0200926
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200927 /* look for the prefix in namespaces */
928 if (memcmp(prefix, c, e - c)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100929 LOGVAL(LYE_XML_INVAL, LY_VLOG_XML, elem,
Michal Vasko6ea3e362016-03-11 10:25:36 +0100930 "(different namespaces) opening (%s) and closing element tags", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200931 goto error;
932 }
933 c = start;
934 }
935 e += size;
Radek Krejci48464ed2016-03-17 15:44:09 +0100936 uc = lyxml_getutf8(e, &size);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200937 }
938 if (!*e) {
Radek Krejci3cc10962016-04-13 15:03:27 +0200939 LOGVAL(LYE_EOF, LY_VLOG_NONE, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200940 goto error;
941 }
Radek Krejci02117302015-04-13 16:32:44 +0200942
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200943 /* check that it corresponds to opening tag */
944 size = e - c;
945 str = malloc((size + 1) * sizeof *str);
Michal Vasko253035f2015-12-17 16:58:13 +0100946 if (!str) {
947 LOGMEM;
948 goto error;
949 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200950 memcpy(str, c, e - c);
951 str[e - c] = '\0';
952 if (size != strlen(elem->name) || memcmp(str, elem->name, size)) {
Radek Krejci48464ed2016-03-17 15:44:09 +0100953 LOGVAL(LYE_XML_INVAL, LY_VLOG_XML, elem,
Michal Vasko6ea3e362016-03-11 10:25:36 +0100954 "(mixed names) opening (%s) and closing (%s) element tags", elem->name, str);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200955 free(str);
956 goto error;
957 }
958 free(str);
959 c = e;
Radek Krejci02117302015-04-13 16:32:44 +0200960
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200961 ign_xmlws(c);
962 if (*c != '>') {
Radek Krejci48464ed2016-03-17 15:44:09 +0100963 LOGVAL(LYE_XML_INVAL, LY_VLOG_XML, elem,
Michal Vasko6ea3e362016-03-11 10:25:36 +0100964 "data after closing element tag \"%s\"", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200965 goto error;
966 }
967 c++;
Michal Vaskoe00b7892016-04-14 10:12:18 +0200968 if (!(elem->flags & LYXML_ELEM_MIXED) && !elem->content) {
969 /* there was no content, but we don't want NULL (only if mixed content) */
970 elem->content = lydict_insert(ctx, "", 0);
971 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200972 closed_flag = 1;
973 break;
Radek Krejci02117302015-04-13 16:32:44 +0200974
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200975 } else if (!memcmp(c, "<?", 2)) {
976 if (lws) {
977 /* leading white spaces were only formatting */
978 lws = NULL;
979 }
980 /* PI - ignore it */
981 c += 2;
982 if (parse_ignore(c, "?>", &size)) {
983 goto error;
984 }
985 c += size;
986 } else if (!memcmp(c, "<!--", 4)) {
987 if (lws) {
988 /* leading white spaces were only formatting */
989 lws = NULL;
990 }
991 /* Comment - ignore it */
992 c += 4;
993 if (parse_ignore(c, "-->", &size)) {
994 goto error;
995 }
996 c += size;
997 } else if (!memcmp(c, "<![CDATA[", 9)) {
998 /* CDSect */
999 goto store_content;
1000 } else if (*c == '<') {
1001 if (lws) {
1002 if (elem->flags & LYXML_ELEM_MIXED) {
1003 /* we have a mixed content */
1004 goto store_content;
1005 } else {
1006 /* leading white spaces were only formatting */
1007 lws = NULL;
1008 }
1009 }
1010 if (elem->content) {
1011 /* we have a mixed content */
1012 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001013 if (!child) {
1014 LOGMEM;
1015 goto error;
1016 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001017 child->content = elem->content;
1018 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001019 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001020 elem->flags |= LYXML_ELEM_MIXED;
1021 }
Radek Krejci9a5daea2016-03-02 16:49:40 +01001022 child = lyxml_parse_elem(ctx, c, &size, elem);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001023 if (!child) {
1024 goto error;
1025 }
1026 c += size; /* move after processed child element */
1027 } else if (is_xmlws(*c)) {
1028 lws = c;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001029 ign_xmlws(c);
1030 } else {
Radek Krejci02117302015-04-13 16:32:44 +02001031store_content:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001032 /* store text content */
1033 if (lws) {
1034 /* process content including the leading white spaces */
1035 c = lws;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001036 lws = NULL;
1037 }
1038 elem->content = lydict_insert_zc(ctx, parse_text(c, '<', &size));
1039 if (ly_errno) {
1040 goto error;
1041 }
1042 c += size; /* move after processed text content */
Radek Krejci02117302015-04-13 16:32:44 +02001043
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001044 if (elem->child) {
1045 /* we have a mixed content */
1046 child = calloc(1, sizeof *child);
Michal Vasko253035f2015-12-17 16:58:13 +01001047 if (!child) {
1048 LOGMEM;
1049 goto error;
1050 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001051 child->content = elem->content;
1052 elem->content = NULL;
Michal Vaskof8879c22015-08-21 09:07:36 +02001053 lyxml_add_child(ctx, elem, child);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001054 elem->flags |= LYXML_ELEM_MIXED;
1055 }
1056 }
1057 }
1058 } else {
1059 /* process attribute */
1060 attr = parse_attr(ctx, c, &size, elem);
1061 if (!attr) {
1062 goto error;
1063 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001064 c += size; /* move after processed attribute */
Radek Krejci02117302015-04-13 16:32:44 +02001065
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001066 /* check namespace */
1067 if (attr->type == LYXML_ATTR_NS) {
1068 if (!prefix[0] && !attr->name) {
1069 if (attr->value) {
1070 /* default prefix */
1071 elem->ns = (struct lyxml_ns *)attr;
1072 } else {
1073 /* xmlns="" -> no namespace */
1074 nons_flag = 1;
1075 }
1076 } else if (prefix[0] && attr->name && !memcmp(attr->name, prefix, prefix_len + 1)) {
1077 /* matching namespace with prefix */
1078 elem->ns = (struct lyxml_ns *)attr;
1079 }
1080 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001081
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001082 /* go back to finish element processing */
1083 goto process;
1084 }
Radek Krejci02117302015-04-13 16:32:44 +02001085
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001086 *len = c - data;
Radek Krejci02117302015-04-13 16:32:44 +02001087
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001088 if (!closed_flag) {
Radek Krejci48464ed2016-03-17 15:44:09 +01001089 LOGVAL(LYE_XML_MISS, LY_VLOG_XML, elem, "closing element tag", elem->name);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001090 goto error;
1091 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001092
Radek Krejci78a230a2015-07-07 17:04:40 +02001093 if (!elem->ns && !nons_flag && parent) {
Radek Krejci4476d412015-07-10 15:35:01 +02001094 elem->ns = lyxml_get_ns(parent, prefix_len ? prefix : NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001095 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001096
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001097 return elem;
Radek Krejci02117302015-04-13 16:32:44 +02001098
1099error:
Michal Vasko345da0a2015-12-02 10:35:55 +01001100 lyxml_free(ctx, elem);
Radek Krejci02117302015-04-13 16:32:44 +02001101
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001102 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001103}
1104
Michal Vasko0d343d12015-08-24 14:57:36 +02001105/* logs directly */
Radek Krejcic6704c82015-10-06 11:12:45 +02001106API struct lyxml_elem *
Radek Krejci722b0072016-02-01 17:09:45 +01001107lyxml_parse_mem(struct ly_ctx *ctx, const char *data, int options)
Radek Krejci54ea8de2015-04-09 18:02:56 +02001108{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001109 const char *c = data;
1110 unsigned int len;
Radek Krejci851ea662016-01-08 09:30:53 +01001111 struct lyxml_elem *root, *first = NULL, *next;
Radek Krejci02117302015-04-13 16:32:44 +02001112
Radek Krejci2342cf62016-01-29 16:48:23 +01001113 ly_errno = LY_SUCCESS;
1114
Radek Krejci120f6242015-12-17 12:32:56 +01001115repeat:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001116 /* process document */
1117 while (*c) {
1118 if (is_xmlws(*c)) {
1119 /* skip whitespaces */
1120 ign_xmlws(c);
1121 } else if (!memcmp(c, "<?", 2)) {
1122 /* XMLDecl or PI - ignore it */
1123 c += 2;
1124 if (parse_ignore(c, "?>", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001125 return NULL;
1126 }
1127 c += len;
1128 } else if (!memcmp(c, "<!--", 4)) {
1129 /* Comment - ignore it */
1130 c += 2;
1131 if (parse_ignore(c, "-->", &len)) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001132 return NULL;
1133 }
1134 c += len;
1135 } else if (!memcmp(c, "<!", 2)) {
1136 /* DOCTYPE */
1137 /* TODO - standalone ignore counting < and > */
1138 LOGERR(LY_EINVAL, "DOCTYPE not supported in XML documents.");
1139 return NULL;
1140 } else if (*c == '<') {
1141 /* element - process it in next loop to strictly follow XML
1142 * format
1143 */
1144 break;
Michal Vaskoc2e80562015-07-27 11:31:41 +02001145 } else {
Radek Krejci48464ed2016-03-17 15:44:09 +01001146 LOGVAL(LYE_XML_INCHAR, LY_VLOG_NONE, NULL, c);
Michal Vaskoc2e80562015-07-27 11:31:41 +02001147 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001148 }
1149 }
Radek Krejci02117302015-04-13 16:32:44 +02001150
Radek Krejci9a5daea2016-03-02 16:49:40 +01001151 root = lyxml_parse_elem(ctx, c, &len, NULL);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001152 if (!root) {
Michal Vaskobc58b4a2016-01-07 14:42:31 +01001153 if (first) {
Radek Krejci851ea662016-01-08 09:30:53 +01001154 LY_TREE_FOR_SAFE(first, next, root) {
Michal Vaskobc58b4a2016-01-07 14:42:31 +01001155 lyxml_free(ctx, root);
1156 }
Radek Krejci120f6242015-12-17 12:32:56 +01001157 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001158 return NULL;
Radek Krejci120f6242015-12-17 12:32:56 +01001159 } else if (!first) {
1160 first = root;
1161 } else {
1162 first->prev->next = root;
1163 root->prev = first->prev;
1164 first->prev = root;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001165 }
1166 c += len;
Radek Krejci02117302015-04-13 16:32:44 +02001167
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001168 /* ignore the rest of document where can be comments, PIs and whitespaces,
1169 * note that we are not detecting syntax errors in these parts
1170 */
1171 ign_xmlws(c);
1172 if (*c) {
Radek Krejci722b0072016-02-01 17:09:45 +01001173 if (options & LYXML_PARSE_MULTIROOT) {
Radek Krejci120f6242015-12-17 12:32:56 +01001174 goto repeat;
1175 } else {
1176 LOGWRN("There are some not parsed data:\n%s", c);
1177 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001178 }
Radek Krejci02117302015-04-13 16:32:44 +02001179
Radek Krejci120f6242015-12-17 12:32:56 +01001180 return first;
Radek Krejci02117302015-04-13 16:32:44 +02001181}
1182
Radek Krejcic6704c82015-10-06 11:12:45 +02001183API struct lyxml_elem *
Radek Krejci722b0072016-02-01 17:09:45 +01001184lyxml_parse_path(struct ly_ctx *ctx, const char *filename, int options)
Radek Krejci54ea8de2015-04-09 18:02:56 +02001185{
Radek Krejci6b3d9262015-12-03 13:45:27 +01001186 struct lyxml_elem *elem = NULL;
Pavol Vicanb2570c12015-11-12 13:50:20 +01001187 struct stat sb;
1188 int fd;
1189 char *addr;
1190
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001191 if (!filename || !ctx) {
1192 LOGERR(LY_EINVAL, "%s: Invalid parameter.", __func__);
1193 return NULL;
1194 }
Radek Krejci54ea8de2015-04-09 18:02:56 +02001195
Pavol Vicanb2570c12015-11-12 13:50:20 +01001196 fd = open(filename, O_RDONLY);
1197 if (fd == -1) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001198 LOGERR(LY_EINVAL,"Opening file \"%s\" failed.", filename);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001199 return NULL;
1200 }
1201 if (fstat(fd, &sb) == -1) {
1202 LOGERR(LY_EINVAL, "Unable to get file \"%s\" information.\n", filename);
1203 goto error;
1204 }
1205 if (!S_ISREG(sb.st_mode)) {
Radek Krejcib051f722016-02-25 15:12:21 +01001206 LOGERR(LY_EINVAL, "%s: Invalid parameter, input file is not a regular file", __func__);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001207 goto error;
1208 }
Pavol Vicanf7cc2852016-03-22 23:27:35 +01001209 addr = mmap(NULL, sb.st_size + 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001210 if (addr == MAP_FAILED) {
Radek Krejci6b3d9262015-12-03 13:45:27 +01001211 LOGERR(LY_EMEM,"Map file into memory failed (%s()).", __func__);
Pavol Vicanb2570c12015-11-12 13:50:20 +01001212 goto error;
1213 }
Radek Krejci6b3d9262015-12-03 13:45:27 +01001214
Radek Krejci722b0072016-02-01 17:09:45 +01001215 elem = lyxml_parse_mem(ctx, addr, options);
Pavol Vicanf7cc2852016-03-22 23:27:35 +01001216 munmap(addr, sb.st_size +2);
Radek Krejci30793ab2015-12-03 13:45:45 +01001217 close(fd);
Radek Krejci6b3d9262015-12-03 13:45:27 +01001218
Pavol Vicanb2570c12015-11-12 13:50:20 +01001219 return elem;
1220
1221error:
Radek Krejci6b3d9262015-12-03 13:45:27 +01001222 if (fd != -1) {
1223 close(fd);
1224 }
1225
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001226 return NULL;
Radek Krejci54ea8de2015-04-09 18:02:56 +02001227}
Radek Krejci02117302015-04-13 16:32:44 +02001228
Michal Vasko5db027d2015-10-09 14:38:50 +02001229int
1230lyxml_dump_text(struct lyout *out, const char *text)
Radek Krejcif0023a92015-04-20 20:51:39 +02001231{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001232 unsigned int i, n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001233
Michal Vasko5db027d2015-10-09 14:38:50 +02001234 if (!text) {
1235 return 0;
1236 }
1237
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001238 for (i = n = 0; text[i]; i++) {
1239 switch (text[i]) {
1240 case '&':
Radek Krejci5248f132015-10-09 10:34:25 +02001241 n += ly_print(out, "&amp;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001242 break;
1243 case '<':
Radek Krejci5248f132015-10-09 10:34:25 +02001244 n += ly_print(out, "&lt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001245 break;
1246 case '>':
1247 /* not needed, just for readability */
Radek Krejci5248f132015-10-09 10:34:25 +02001248 n += ly_print(out, "&gt;");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001249 break;
1250 default:
Radek Krejci5248f132015-10-09 10:34:25 +02001251 ly_write(out, &text[i], 1);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001252 n++;
1253 }
1254 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001255
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001256 return n;
Radek Krejcif0023a92015-04-20 20:51:39 +02001257}
1258
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001259static int
Michal Vasko1e62a092015-12-01 12:27:20 +01001260dump_elem(struct lyout *out, const struct lyxml_elem *e, int level, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001261{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001262 int size = 0;
1263 struct lyxml_attr *a;
1264 struct lyxml_elem *child;
1265 const char *delim, *delim_outer;
1266 int indent;
Radek Krejcif0023a92015-04-20 20:51:39 +02001267
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001268 if (!e->name) {
1269 /* mixed content */
1270 if (e->content) {
Michal Vasko5db027d2015-10-09 14:38:50 +02001271 return lyxml_dump_text(out, e->content);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001272 } else {
1273 return 0;
1274 }
1275 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001276
Radek Krejci722b0072016-02-01 17:09:45 +01001277 delim = delim_outer = (options & LYXML_PRINT_FORMAT) ? "\n" : "";
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001278 indent = 2 * level;
1279 if ((e->flags & LYXML_ELEM_MIXED) || (e->parent && (e->parent->flags & LYXML_ELEM_MIXED))) {
1280 delim = "";
1281 }
1282 if (e->parent && (e->parent->flags & LYXML_ELEM_MIXED)) {
1283 delim_outer = "";
1284 indent = 0;
1285 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001286
Radek Krejci722b0072016-02-01 17:09:45 +01001287 if (!(options & (LYXML_PRINT_OPEN | LYXML_PRINT_CLOSE | LYXML_PRINT_ATTRS)) || (options & LYXML_PRINT_OPEN)) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001288 /* opening tag */
1289 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001290 size += ly_print(out, "%*s<%s:%s", indent, "", e->ns->prefix, e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001291 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001292 size += ly_print(out, "%*s<%s", indent, "", e->name);
Radek Krejcic6704c82015-10-06 11:12:45 +02001293 }
Radek Krejci722b0072016-02-01 17:09:45 +01001294 } else if (options & LYXML_PRINT_CLOSE) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001295 indent = 0;
1296 goto close;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001297 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001298
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001299 /* attributes */
1300 for (a = e->attr; a; a = a->next) {
1301 if (a->type == LYXML_ATTR_NS) {
1302 if (a->name) {
Radek Krejci5248f132015-10-09 10:34:25 +02001303 size += ly_print(out, " xmlns:%s=\"%s\"", a->name, a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001304 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001305 size += ly_print(out, " xmlns=\"%s\"", a->value ? a->value : "");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001306 }
1307 } else if (a->ns && a->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001308 size += ly_print(out, " %s:%s=\"%s\"", a->ns->prefix, a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001309 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001310 size += ly_print(out, " %s=\"%s\"", a->name, a->value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001311 }
1312 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001313
Radek Krejcic6704c82015-10-06 11:12:45 +02001314 /* apply options */
Radek Krejci722b0072016-02-01 17:09:45 +01001315 if ((options & LYXML_PRINT_CLOSE) && (options & LYXML_PRINT_OPEN)) {
Radek Krejci5248f132015-10-09 10:34:25 +02001316 size += ly_print(out, "/>%s", delim);
Radek Krejcic6704c82015-10-06 11:12:45 +02001317 return size;
Radek Krejci722b0072016-02-01 17:09:45 +01001318 } else if (options & LYXML_PRINT_OPEN) {
Radek Krejci5248f132015-10-09 10:34:25 +02001319 ly_print(out, ">");
Radek Krejcic6704c82015-10-06 11:12:45 +02001320 return ++size;
Radek Krejci722b0072016-02-01 17:09:45 +01001321 } else if (options & LYXML_PRINT_ATTRS) {
Radek Krejcic6704c82015-10-06 11:12:45 +02001322 return size;
1323 }
1324
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001325 if (!e->child && !e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001326 size += ly_print(out, "/>%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001327 return size;
1328 } else if (e->content) {
Radek Krejci5248f132015-10-09 10:34:25 +02001329 ly_print(out, ">");
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001330 size++;
Radek Krejcif0023a92015-04-20 20:51:39 +02001331
Michal Vasko5db027d2015-10-09 14:38:50 +02001332 size += lyxml_dump_text(out, e->content);
Radek Krejcif0023a92015-04-20 20:51:39 +02001333
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001334 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001335 size += ly_print(out, "</%s:%s>%s", e->ns->prefix, e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001336 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001337 size += ly_print(out, "</%s>%s", e->name, delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001338 }
1339 return size;
1340 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001341 size += ly_print(out, ">%s", delim);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001342 }
Radek Krejci674e1f82015-04-21 14:12:19 +02001343
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001344 /* go recursively */
1345 LY_TREE_FOR(e->child, child) {
Radek Krejci722b0072016-02-01 17:09:45 +01001346 if (options & LYXML_PRINT_FORMAT) {
1347 size += dump_elem(out, child, level + 1, LYXML_PRINT_FORMAT);
Pavol Vicanbe7eef52015-10-22 14:07:48 +02001348 } else {
1349 size += dump_elem(out, child, level, 0);
1350 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001351 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001352
Radek Krejcic6704c82015-10-06 11:12:45 +02001353close:
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001354 /* closing tag */
1355 if (e->ns && e->ns->prefix) {
Radek Krejci5248f132015-10-09 10:34:25 +02001356 size += ly_print(out, "%*s</%s:%s>%s", indent, "", e->ns->prefix, e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001357 } else {
Radek Krejci5248f132015-10-09 10:34:25 +02001358 size += ly_print(out, "%*s</%s>%s", indent, "", e->name, delim_outer);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001359 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001360
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001361 return size;
Radek Krejcif0023a92015-04-20 20:51:39 +02001362}
1363
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001364static int
1365dump_siblings(struct lyout *out, const struct lyxml_elem *e, int options)
1366{
1367 const struct lyxml_elem *start, *iter;
1368 int ret = 0;
1369
1370 if (e->parent) {
1371 start = e->parent->child;
1372 } else {
1373 start = e;
1374 while(start->prev && start->prev->next) {
1375 start = start->prev;
1376 }
1377 }
1378
1379 LY_TREE_FOR(start, iter) {
1380 ret += dump_elem(out, iter, 0, options);
1381 }
1382
1383 return ret;
1384}
1385
Radek Krejcic6704c82015-10-06 11:12:45 +02001386API int
Radek Krejci722b0072016-02-01 17:09:45 +01001387lyxml_print_file(FILE *stream, const struct lyxml_elem *elem, int options)
Radek Krejcif0023a92015-04-20 20:51:39 +02001388{
Radek Krejci5248f132015-10-09 10:34:25 +02001389 struct lyout out;
1390
1391 if (!stream || !elem) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +02001392 return 0;
1393 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001394
Radek Krejci5248f132015-10-09 10:34:25 +02001395 out.type = LYOUT_STREAM;
1396 out.method.f = stream;
1397
Radek Krejci722b0072016-02-01 17:09:45 +01001398 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001399 return dump_siblings(&out, elem, options);
1400 } else {
1401 return dump_elem(&out, elem, 0, options);
1402 }
Radek Krejci5248f132015-10-09 10:34:25 +02001403}
1404
1405API int
Radek Krejci722b0072016-02-01 17:09:45 +01001406lyxml_print_fd(int fd, const struct lyxml_elem *elem, int options)
Radek Krejci5248f132015-10-09 10:34:25 +02001407{
1408 struct lyout out;
1409
1410 if (fd < 0 || !elem) {
1411 return 0;
1412 }
1413
1414 out.type = LYOUT_FD;
1415 out.method.fd = fd;
1416
Radek Krejci722b0072016-02-01 17:09:45 +01001417 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001418 return dump_siblings(&out, elem, options);
1419 } else {
1420 return dump_elem(&out, elem, 0, options);
1421 }
Radek Krejcif0023a92015-04-20 20:51:39 +02001422}
Radek Krejci6140e4e2015-10-09 15:50:55 +02001423
1424API int
Radek Krejci722b0072016-02-01 17:09:45 +01001425lyxml_print_mem(char **strp, const struct lyxml_elem *elem, int options)
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001426{
1427 struct lyout out;
1428 int r;
1429
1430 if (!strp || !elem) {
1431 return 0;
1432 }
1433
1434 out.type = LYOUT_MEMORY;
1435 out.method.mem.buf = NULL;
1436 out.method.mem.len = 0;
1437 out.method.mem.size = 0;
1438
Radek Krejci722b0072016-02-01 17:09:45 +01001439 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001440 r = dump_siblings(&out, elem, options);
1441 } else {
1442 r = dump_elem(&out, elem, 0, options);
1443 }
Radek Krejci2fa0fc12015-10-14 18:14:29 +02001444
1445 *strp = out.method.mem.buf;
1446 return r;
1447}
1448
1449API int
Radek Krejci722b0072016-02-01 17:09:45 +01001450lyxml_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 +02001451{
1452 struct lyout out;
1453
1454 if (!writeclb || !elem) {
1455 return 0;
1456 }
1457
1458 out.type = LYOUT_CALLBACK;
Radek Krejci50929eb2015-10-09 18:14:15 +02001459 out.method.clb.f = writeclb;
1460 out.method.clb.arg = arg;
Radek Krejci6140e4e2015-10-09 15:50:55 +02001461
Radek Krejci722b0072016-02-01 17:09:45 +01001462 if (options & LYXML_PRINT_SIBLINGS) {
Radek Krejci8c56a5a2015-12-16 15:10:28 +01001463 return dump_siblings(&out, elem, options);
1464 } else {
1465 return dump_elem(&out, elem, 0, options);
1466 }
Radek Krejci6140e4e2015-10-09 15:50:55 +02001467}