aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/libraries/eina/src/lib/eina_simple_xml_parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/eina/src/lib/eina_simple_xml_parser.c')
-rw-r--r--libraries/eina/src/lib/eina_simple_xml_parser.c1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/libraries/eina/src/lib/eina_simple_xml_parser.c b/libraries/eina/src/lib/eina_simple_xml_parser.c
new file mode 100644
index 0000000..08a8259
--- /dev/null
+++ b/libraries/eina/src/lib/eina_simple_xml_parser.c
@@ -0,0 +1,1070 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2011 Gustavo Sverzut Barbieri
3 * Cedric Bail
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library;
17 * if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#ifdef HAVE_ALLOCA_H
25# include <alloca.h>
26#elif defined __GNUC__
27# define alloca __builtin_alloca
28#elif defined _AIX
29# define alloca __alloca
30#elif defined _MSC_VER
31# include <malloc.h>
32# define alloca _alloca
33#else
34# include <stddef.h>
35# ifdef __cplusplus
36extern "C"
37# endif
38void *alloca (size_t);
39#endif
40
41#include <strings.h>
42#include <string.h>
43#include <ctype.h>
44
45#ifdef HAVE_EVIL
46# include <Evil.h>
47#endif
48
49#include "eina_private.h"
50#include "eina_log.h"
51#include "eina_mempool.h"
52#include "eina_stringshare.h"
53#include "eina_strbuf.h"
54#include "eina_simple_xml_parser.h"
55
56/*============================================================================*
57 * Local *
58 *============================================================================*/
59
60/**
61 * @cond LOCAL
62 */
63
64static Eina_Mempool *_eina_simple_xml_tag_mp = NULL;
65static Eina_Mempool *_eina_simple_xml_attribute_mp = NULL;
66static int _eina_simple_xml_log_dom = -1;
67
68static const char EINA_MAGIC_SIMPLE_XML_TAG_STR[] = "Eina Simple XML Tag";
69static const char EINA_MAGIC_SIMPLE_XML_DATA_STR[] = "Eina Simple XML Data";
70static const char EINA_MAGIC_SIMPLE_XML_ATTRIBUTE_STR[] = "Eina Simple XML Attribute";
71
72#define EINA_MAGIC_CHECK_TAG(d, ...) \
73 do { \
74 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_TAG)) \
75 { \
76 EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_TAG); \
77 return __VA_ARGS__; \
78 } \
79 } while(0)
80
81#define EINA_MAGIC_CHECK_DATA(d, ...) \
82 do { \
83 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_DATA)) \
84 { \
85 EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_DATA); \
86 return __VA_ARGS__; \
87 } \
88 } while(0)
89
90#define EINA_MAGIC_CHECK_ATTRIBUTE(d, ...) \
91 do { \
92 if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE)) \
93 { \
94 EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE); \
95 return __VA_ARGS__; \
96 } \
97 } while(0)
98
99
100#ifndef EINA_LOG_COLOR_DEFAULT
101#define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
102#endif
103
104#ifdef ERR
105#undef ERR
106#endif
107#define ERR(...) EINA_LOG_DOM_ERR(_eina_simple_xml_log_dom, __VA_ARGS__)
108
109#ifdef WRN
110#undef WRN
111#endif
112#define WRN(...) EINA_LOG_DOM_WARN(_eina_simple_xml_log_dom, __VA_ARGS__)
113
114#ifdef DBG
115#undef DBG
116#endif
117#define DBG(...) EINA_LOG_DOM_DBG(_eina_simple_xml_log_dom, __VA_ARGS__)
118
119
120static inline const char *
121_eina_simple_xml_whitespace_find(const char *itr, const char *itr_end)
122{
123 for (; itr < itr_end; itr++)
124 if (isspace(*itr)) break;
125 return itr;
126}
127
128static inline const char *
129_eina_simple_xml_whitespace_skip(const char *itr, const char *itr_end)
130{
131 for (; itr < itr_end; itr++)
132 if (!isspace(*itr)) break;
133 return itr;
134}
135
136static inline const char *
137_eina_simple_xml_whitespace_unskip(const char *itr, const char *itr_start)
138{
139 for (itr--; itr > itr_start; itr--)
140 if (!isspace(*itr)) break;
141 return itr + 1;
142}
143
144static inline const char *
145_eina_simple_xml_tag_start_find(const char *itr, const char *itr_end)
146{
147 return memchr(itr, '<', itr_end - itr);
148}
149
150static inline const char *
151_eina_simple_xml_tag_end_find(const char *itr, const char *itr_end)
152{
153 for (; itr < itr_end; itr++)
154 if ((*itr == '>') || (*itr == '<')) /* consider < also ends a tag */
155 return itr;
156 return NULL;
157}
158
159/**
160 * @endcond
161 */
162
163/*============================================================================*
164 * Global *
165 *============================================================================*/
166
167
168/**
169 * @internal
170 * @brief Initialize the simple xml parser module.
171 *
172 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
173 *
174 * This function sets up the simple xml parser module of Eina. It is called by
175 * eina_init().
176 *
177 * @see eina_init()
178 */
179Eina_Bool
180eina_simple_xml_init(void)
181{
182 const char *choice, *tmp;
183
184 _eina_simple_xml_log_dom = eina_log_domain_register("eina_simple_xml",
185 EINA_LOG_COLOR_DEFAULT);
186 if (_eina_simple_xml_log_dom < 0)
187 {
188 EINA_LOG_ERR("Could not register log domain: eina_simple_xml");
189 return EINA_FALSE;
190 }
191
192#ifdef EINA_DEFAULT_MEMPOOL
193 choice = "pass_through";
194#else
195 choice = "chained_mempool";
196#endif
197 tmp = getenv("EINA_MEMPOOL");
198 if (tmp && tmp[0])
199 choice = tmp;
200
201 _eina_simple_xml_tag_mp = eina_mempool_add
202 (choice, "simple_xml_tag", NULL,
203 sizeof(Eina_Simple_XML_Node_Tag), 320);
204 if (!_eina_simple_xml_tag_mp)
205 {
206 ERR("Mempool for simple_xml_tag cannot be allocated in init.");
207 goto on_init_fail;
208 }
209
210 _eina_simple_xml_attribute_mp = eina_mempool_add
211 (choice, "simple_xml_attribute", NULL,
212 sizeof(Eina_Simple_XML_Attribute), 80);
213 if (!_eina_simple_xml_attribute_mp)
214 {
215 ERR("Mempool for simple_xml_attribute cannot be allocated in init.");
216 eina_mempool_del(_eina_simple_xml_tag_mp);
217 goto on_init_fail;
218 }
219
220#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
221 EMS(EINA_MAGIC_SIMPLE_XML_TAG);
222 EMS(EINA_MAGIC_SIMPLE_XML_DATA);
223 EMS(EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
224#undef EMS
225
226 return EINA_TRUE;
227
228on_init_fail:
229 eina_log_domain_unregister(_eina_simple_xml_log_dom);
230 _eina_simple_xml_log_dom = -1;
231 return EINA_FALSE;
232}
233
234/**
235 * @internal
236 * @brief Shut down the simple xml parser module.
237 *
238 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
239 *
240 * This function shuts down the simple xml parser module set
241 * up by eina_simple_xml_init(). It is called by
242 * eina_shutdown().
243 *
244 * @see eina_shutdown()
245 */
246Eina_Bool
247eina_simple_xml_shutdown(void)
248{
249 eina_mempool_del(_eina_simple_xml_attribute_mp);
250 eina_mempool_del(_eina_simple_xml_tag_mp);
251
252 eina_log_domain_unregister(_eina_simple_xml_log_dom);
253 _eina_simple_xml_log_dom = -1;
254 return EINA_TRUE;
255}
256
257
258/*============================================================================*
259 * API *
260 *============================================================================*/
261
262
263EAPI Eina_Bool
264eina_simple_xml_parse(const char *buf, unsigned buflen, Eina_Bool strip, Eina_Simple_XML_Cb func, const void *data)
265{
266 const char *itr = buf, *itr_end = buf + buflen;
267
268 if (!buf) return EINA_FALSE;
269 if (!func) return EINA_FALSE;
270
271#define CB(type, start, end) \
272 do \
273 { \
274 size_t _sz = end - start; \
275 Eina_Bool _ret; \
276 _ret = func((void*)data, type, start, start - buf, _sz); \
277 if (!_ret) return EINA_FALSE; \
278 } \
279 while (0)
280
281 while (itr < itr_end)
282 {
283 if (itr[0] == '<')
284 {
285 if (itr + 1 >= itr_end)
286 {
287 CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
288 return EINA_FALSE;
289 }
290 else
291 {
292 Eina_Simple_XML_Type type;
293 size_t toff;
294 const char *p;
295
296 if (itr[1] == '/')
297 {
298 type = EINA_SIMPLE_XML_CLOSE;
299 toff = 1;
300 }
301 else if (itr[1] == '?')
302 {
303 type = EINA_SIMPLE_XML_PROCESSING;
304 toff = 1;
305 }
306 else if (itr[1] == '!')
307 {
308 if ((itr + sizeof("<!DOCTYPE>") - 1 < itr_end) &&
309 (!memcmp(itr + 2, "DOCTYPE",
310 sizeof("DOCTYPE") - 1)) &&
311 ((itr[2 + sizeof("DOCTYPE") - 1] == '>') ||
312 (isspace(itr[2 + sizeof("DOCTYPE") - 1]))))
313 {
314 type = EINA_SIMPLE_XML_DOCTYPE;
315 toff = sizeof("!DOCTYPE") - 1;
316 }
317 else if ((itr + sizeof("<!---->") - 1 < itr_end) &&
318 (!memcmp(itr + 2, "--", sizeof("--") - 1)))
319 {
320 type = EINA_SIMPLE_XML_COMMENT;
321 toff = sizeof("!--") - 1;
322 }
323 else if ((itr + sizeof("<![CDATA[]]>") - 1 < itr_end) &&
324 (!memcmp(itr + 2, "[CDATA[",
325 sizeof("[CDATA[") - 1)))
326 {
327 type = EINA_SIMPLE_XML_CDATA;
328 toff = sizeof("![CDATA[") - 1;
329 }
330 else
331 {
332 type = EINA_SIMPLE_XML_OPEN;
333 toff = 0;
334 }
335 }
336 else
337 {
338 type = EINA_SIMPLE_XML_OPEN;
339 toff = 0;
340 }
341
342 p = _eina_simple_xml_tag_end_find(itr + 1 + toff, itr_end);
343 if (p)
344 {
345 if (type == EINA_SIMPLE_XML_CDATA)
346 {
347 /* must end with ]]> */
348 while ((p) && (memcmp(p - 2, "]]>", 3)))
349 p = _eina_simple_xml_tag_end_find(p + 1, itr_end);
350 }
351
352 if ((p) && (*p == '<'))
353 {
354 type = EINA_SIMPLE_XML_ERROR;
355 toff = 0;
356 }
357 }
358
359 if (p)
360 {
361 const char *start, *end;
362
363 start = itr + 1 + toff;
364 end = p;
365
366 switch (type)
367 {
368 case EINA_SIMPLE_XML_OPEN:
369 if (p[-1] == '/')
370 {
371 type = EINA_SIMPLE_XML_OPEN_EMPTY;
372 end--;
373 }
374 break;
375 case EINA_SIMPLE_XML_CDATA:
376 if (!memcmp(p - 2, "]]", 2)) end -= 2;
377 break;
378 case EINA_SIMPLE_XML_PROCESSING:
379 if (p[-1] == '?') end--;
380 break;
381 case EINA_SIMPLE_XML_COMMENT:
382 if (!memcmp(p - 2, "--", 2)) end -= 2;
383 break;
384 case EINA_SIMPLE_XML_OPEN_EMPTY:
385 case EINA_SIMPLE_XML_CLOSE:
386 case EINA_SIMPLE_XML_DATA:
387 case EINA_SIMPLE_XML_ERROR:
388 case EINA_SIMPLE_XML_DOCTYPE:
389 case EINA_SIMPLE_XML_IGNORED:
390 break;
391 }
392
393 if ((strip) && (type != EINA_SIMPLE_XML_ERROR))
394 {
395 start = _eina_simple_xml_whitespace_skip
396 (start, end);
397 end = _eina_simple_xml_whitespace_unskip
398 (end, start + 1);
399 }
400
401 CB(type, start, end);
402
403 if (type != EINA_SIMPLE_XML_ERROR)
404 itr = p + 1;
405 else
406 itr = p;
407 }
408 else
409 {
410 CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
411 return EINA_FALSE;
412 }
413 }
414 }
415 else
416 {
417 const char *p, *end;
418
419 if (strip)
420 {
421 p = _eina_simple_xml_whitespace_skip(itr, itr_end);
422 if (p)
423 {
424 CB(EINA_SIMPLE_XML_IGNORED, itr, p);
425 itr = p;
426 }
427 }
428
429 p = _eina_simple_xml_tag_start_find(itr, itr_end);
430 if (!p) p = itr_end;
431
432 end = p;
433 if (strip)
434 end = _eina_simple_xml_whitespace_unskip(end, itr);
435
436 if (itr != end)
437 CB(EINA_SIMPLE_XML_DATA, itr, end);
438
439 if ((strip) && (end < p))
440 CB(EINA_SIMPLE_XML_IGNORED, end, p);
441
442 itr = p;
443 }
444 }
445
446#undef CB
447
448 return EINA_TRUE;
449}
450
451EAPI const char *
452eina_simple_xml_tag_attributes_find(const char *buf, unsigned buflen)
453{
454 const char *itr = buf, *itr_end = buf + buflen;
455
456 for (; itr < itr_end; itr++)
457 {
458 if (!isspace(*itr))
459 {
460 /* user skip tagname and already gave it the attributes */
461 if (*itr == '=')
462 return buf;
463 }
464 else
465 {
466 itr = _eina_simple_xml_whitespace_skip(itr + 1, itr_end);
467 if (itr == itr_end)
468 return NULL;
469 return itr;
470 }
471 }
472
473 return NULL;
474}
475
476EAPI Eina_Bool
477eina_simple_xml_attributes_parse(const char *buf, unsigned buflen, Eina_Simple_XML_Attribute_Cb func, const void *data)
478{
479 const char *itr = buf, *itr_end = buf + buflen;
480 char *tmpbuf = alloca(buflen + 1);
481
482 if (!buf) return EINA_FALSE;
483 if (!func) return EINA_FALSE;
484
485 while (itr < itr_end)
486 {
487 const char *p = _eina_simple_xml_whitespace_skip(itr, itr_end);
488 const char *key, *key_end, *value, *value_end;
489 char *tval;
490
491 if (p == itr_end) return EINA_TRUE;
492
493 key = p;
494 for (key_end = key; key_end < itr_end; key_end++)
495 if ((*key_end == '=') || (isspace(*key_end))) break;
496 if (key_end == itr_end) return EINA_FALSE;
497 if (key_end == key) continue;
498
499 if (*key_end == '=') value = key_end + 1;
500 else
501 {
502 value = memchr(key_end, '=', itr_end - key_end);
503 if (!value) return EINA_FALSE;
504 value++;
505 }
506 for (; value < itr_end; value++)
507 if (!isspace(*value)) break;
508 if (value == itr_end) return EINA_FALSE;
509
510 if ((*value == '"') || (*value == '\''))
511 {
512 value_end = memchr(value + 1, *value, itr_end - value);
513 if (!value_end) return EINA_FALSE;
514 value++;
515 }
516 else
517 {
518 value_end = _eina_simple_xml_whitespace_find(value, itr_end);
519 }
520
521 memcpy(tmpbuf, key, key_end - key);
522 tmpbuf[key_end - key] = '\0';
523
524 tval = tmpbuf + (key_end - key) + 1;
525 memcpy(tval, value, value_end - value);
526 tval[value_end - value] = '\0';
527
528 if (!func((void*)data, tmpbuf, tval))
529 return EINA_FALSE;
530
531 itr = value_end + 1;
532 }
533 return EINA_TRUE;
534}
535
536/* Node loader *************************************************************/
537
538EAPI Eina_Simple_XML_Attribute *
539eina_simple_xml_attribute_new(Eina_Simple_XML_Node_Tag *parent, const char *key, const char *value)
540{
541 Eina_Simple_XML_Attribute *attr;
542
543 if (!key) return NULL;
544
545 attr = eina_mempool_malloc(_eina_simple_xml_attribute_mp, sizeof(*attr));
546 if (!attr)
547 {
548 ERR("could not allocate memory for attribute from mempool");
549 return NULL;
550 }
551
552 EINA_MAGIC_SET(attr, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
553 attr->parent = parent;
554 attr->key = eina_stringshare_add(key);
555 attr->value = eina_stringshare_add(value ? value : "");
556
557 if (parent)
558 parent->attributes = eina_inlist_append
559 (parent->attributes, EINA_INLIST_GET(attr));
560
561 return attr;
562}
563
564EAPI void
565eina_simple_xml_attribute_free(Eina_Simple_XML_Attribute *attr)
566{
567 EINA_MAGIC_CHECK_ATTRIBUTE(attr);
568
569 if (attr->parent)
570 attr->parent->attributes = eina_inlist_remove
571 (attr->parent->attributes, EINA_INLIST_GET(attr));
572
573 eina_stringshare_del(attr->key);
574 eina_stringshare_del(attr->value);
575 EINA_MAGIC_SET(attr, EINA_MAGIC_NONE);
576 eina_mempool_free(_eina_simple_xml_attribute_mp, attr);
577}
578
579static void
580_eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node)
581{
582 if (node->base.parent)
583 node->base.parent->children = eina_inlist_remove
584 (node->base.parent->children, EINA_INLIST_GET(&node->base));
585
586 EINA_MAGIC_SET(&node->base, EINA_MAGIC_NONE);
587 free(node);
588}
589
590EAPI Eina_Simple_XML_Node_Tag *
591eina_simple_xml_node_tag_new(Eina_Simple_XML_Node_Tag *parent, const char *name)
592{
593 Eina_Simple_XML_Node_Tag *n;
594
595 if (!name) return NULL;
596
597 n = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*n));
598 if (!n)
599 {
600 ERR("could not allocate memory for node from mempool");
601 return NULL;
602 }
603
604 memset(n, 0, sizeof(*n));
605
606 EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_TAG);
607
608 n->base.type = EINA_SIMPLE_XML_NODE_TAG;
609 n->base.parent = parent;
610 n->name = eina_stringshare_add(name);
611
612 if (parent)
613 parent->children = eina_inlist_append
614 (parent->children, EINA_INLIST_GET(&n->base));
615
616 return n;
617}
618
619void
620_eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
621{
622 while (tag->children)
623 {
624 Eina_Simple_XML_Node *n = EINA_INLIST_CONTAINER_GET
625 (tag->children, Eina_Simple_XML_Node);
626 if (n->type == EINA_SIMPLE_XML_NODE_TAG)
627 _eina_simple_xml_node_tag_free((Eina_Simple_XML_Node_Tag *)n);
628 else
629 _eina_simple_xml_node_data_free((Eina_Simple_XML_Node_Data *)n);
630 }
631
632 while (tag->attributes)
633 {
634 Eina_Simple_XML_Attribute *a = EINA_INLIST_CONTAINER_GET
635 (tag->attributes, Eina_Simple_XML_Attribute);
636 eina_simple_xml_attribute_free(a);
637 }
638
639 if (tag->base.parent)
640 tag->base.parent->children = eina_inlist_remove
641 (tag->base.parent->children, EINA_INLIST_GET(&tag->base));
642
643 eina_stringshare_del(tag->name);
644 EINA_MAGIC_SET(&tag->base, EINA_MAGIC_NONE);
645 eina_mempool_free(_eina_simple_xml_tag_mp, tag);
646}
647
648EAPI void
649eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
650{
651 EINA_MAGIC_CHECK_TAG(&tag->base);
652 if (tag->base.type != EINA_SIMPLE_XML_NODE_TAG)
653 {
654 ERR("expected tag node!");
655 return;
656 }
657 _eina_simple_xml_node_tag_free(tag);
658}
659
660static Eina_Simple_XML_Node_Data *
661_eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, Eina_Simple_XML_Node_Type type, const char *content, unsigned length)
662{
663 Eina_Simple_XML_Node_Data *n = malloc(sizeof(*n) + length + 1);
664
665 if (!content) return NULL;
666
667 if (!n)
668 {
669 ERR("could not allocate memory for node");
670 return NULL;
671 }
672
673 EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_DATA);
674 n->base.type = type;
675 n->base.parent = parent;
676
677 n->length = length;
678 memcpy(n->data, content, length);
679 n->data[length] = '\0';
680
681 if (parent)
682 parent->children = eina_inlist_append
683 (parent->children, EINA_INLIST_GET(&n->base));
684
685 return n;
686}
687
688EAPI Eina_Simple_XML_Node_Data *
689eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
690{
691 return _eina_simple_xml_node_data_new
692 (parent, EINA_SIMPLE_XML_NODE_DATA, contents, length);
693}
694
695EAPI void
696eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node)
697{
698 EINA_MAGIC_CHECK_DATA(&node->base);
699 if (node->base.type != EINA_SIMPLE_XML_NODE_DATA)
700 {
701 ERR("expected node of type: data!");
702 return;
703 }
704 _eina_simple_xml_node_data_free(node);
705}
706
707EAPI Eina_Simple_XML_Node_CData *
708eina_simple_xml_node_cdata_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
709{
710 return _eina_simple_xml_node_data_new
711 (parent, EINA_SIMPLE_XML_NODE_CDATA, contents, length);
712}
713
714EAPI void
715eina_simple_xml_node_cdata_free(Eina_Simple_XML_Node_Data *node)
716{
717 EINA_MAGIC_CHECK_DATA(&node->base);
718 if (node->base.type != EINA_SIMPLE_XML_NODE_CDATA)
719 {
720 ERR("expected node of type: cdata!");
721 return;
722 }
723 _eina_simple_xml_node_data_free(node);
724}
725
726EAPI Eina_Simple_XML_Node_Processing *
727eina_simple_xml_node_processing_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
728{
729 return _eina_simple_xml_node_data_new
730 (parent, EINA_SIMPLE_XML_NODE_PROCESSING, contents, length);
731}
732
733EAPI void
734eina_simple_xml_node_processing_free(Eina_Simple_XML_Node_Data *node)
735{
736 EINA_MAGIC_CHECK_DATA(&node->base);
737 if (node->base.type != EINA_SIMPLE_XML_NODE_PROCESSING)
738 {
739 ERR("expected node of type: processing!");
740 return;
741 }
742 _eina_simple_xml_node_data_free(node);
743}
744
745EAPI Eina_Simple_XML_Node_Doctype *
746eina_simple_xml_node_doctype_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
747{
748 return _eina_simple_xml_node_data_new
749 (parent, EINA_SIMPLE_XML_NODE_DOCTYPE, contents, length);
750}
751
752EAPI void
753eina_simple_xml_node_doctype_free(Eina_Simple_XML_Node_Data *node)
754{
755 EINA_MAGIC_CHECK_DATA(&node->base);
756 if (node->base.type != EINA_SIMPLE_XML_NODE_DOCTYPE)
757 {
758 ERR("expected node of type: doctype!");
759 return;
760 }
761 _eina_simple_xml_node_data_free(node);
762}
763
764EAPI Eina_Simple_XML_Node_Comment *
765eina_simple_xml_node_comment_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
766{
767 return _eina_simple_xml_node_data_new
768 (parent, EINA_SIMPLE_XML_NODE_COMMENT, contents, length);
769}
770
771EAPI void
772eina_simple_xml_node_comment_free(Eina_Simple_XML_Node_Data *node)
773{
774 EINA_MAGIC_CHECK_DATA(&node->base);
775 if (node->base.type != EINA_SIMPLE_XML_NODE_COMMENT)
776 {
777 ERR("expected node of type: comment!");
778 return;
779 }
780 _eina_simple_xml_node_data_free(node);
781}
782
783struct eina_simple_xml_node_load_ctxt
784{
785 Eina_Simple_XML_Node_Root *root;
786 Eina_Simple_XML_Node_Tag *current;
787};
788
789static Eina_Bool
790_eina_simple_xml_attrs_parse(void *data, const char *key, const char *value)
791{
792 Eina_Simple_XML_Node_Tag *n = data;
793 Eina_Simple_XML_Attribute *attr;
794
795 attr = eina_simple_xml_attribute_new(n, key, value);
796 return !!attr;
797}
798
799static Eina_Bool
800_eina_simple_xml_node_parse(void *data, Eina_Simple_XML_Type type, const char *content, unsigned offset, unsigned length)
801{
802 struct eina_simple_xml_node_load_ctxt *ctx = data;
803
804 switch (type)
805 {
806 case EINA_SIMPLE_XML_OPEN:
807 case EINA_SIMPLE_XML_OPEN_EMPTY:
808 {
809 Eina_Simple_XML_Node_Tag *n;
810 const char *name, *name_end, *attrs;
811
812 attrs = eina_simple_xml_tag_attributes_find(content, length);
813 if (!attrs)
814 name_end = content + length;
815 else
816 name_end = attrs;
817
818 name_end = _eina_simple_xml_whitespace_unskip(name_end, content);
819
820 name = eina_stringshare_add_length(content, name_end - content);
821 n = eina_simple_xml_node_tag_new(ctx->current, name);
822 eina_stringshare_del(name);
823 if (!n) return EINA_FALSE;
824
825 if (attrs)
826 eina_simple_xml_attributes_parse
827 (attrs, length - (attrs - content),
828 _eina_simple_xml_attrs_parse, n);
829
830 if (type == EINA_SIMPLE_XML_OPEN)
831 ctx->current = n;
832 }
833 break;
834
835 case EINA_SIMPLE_XML_CLOSE:
836 if (ctx->current->base.parent)
837 {
838 const char *end = _eina_simple_xml_whitespace_unskip
839 (content + length, content);
840 int len;
841 len = end - content;
842 if ((len == 0) /* </> closes the tag for us. */ ||
843 ((eina_stringshare_strlen(ctx->current->name) == len) &&
844 (memcmp(ctx->current->name, content, len) == 0)))
845 ctx->current = ctx->current->base.parent;
846 else
847 WRN("closed incorrect tag: '%.*s', '%s' was expected!",
848 len, content, ctx->current->name);
849 }
850 else
851 WRN("closed tag '%.*s' but already at document root!",
852 length, content);
853 break;
854
855 case EINA_SIMPLE_XML_DATA:
856 return !!eina_simple_xml_node_data_new
857 (ctx->current, content, length);
858 case EINA_SIMPLE_XML_CDATA:
859 return !!eina_simple_xml_node_cdata_new
860 (ctx->current, content, length);
861 case EINA_SIMPLE_XML_PROCESSING:
862 return !!eina_simple_xml_node_processing_new
863 (ctx->current, content, length);
864 case EINA_SIMPLE_XML_DOCTYPE:
865 return !!eina_simple_xml_node_doctype_new
866 (ctx->current, content, length);
867 case EINA_SIMPLE_XML_COMMENT:
868 return !!eina_simple_xml_node_comment_new
869 (ctx->current, content, length);
870
871 case EINA_SIMPLE_XML_ERROR:
872 ERR("parser error at offset %u-%u: %.*s",
873 offset, length, length, content);
874 break;
875 case EINA_SIMPLE_XML_IGNORED:
876 DBG("ignored contents at offset %u-%u: %.*s",
877 offset, length, length, content);
878 break;
879 }
880
881 return EINA_TRUE;
882}
883
884EAPI Eina_Simple_XML_Node_Root *
885eina_simple_xml_node_load(const char *buf, unsigned buflen, Eina_Bool strip)
886{
887 Eina_Simple_XML_Node_Root *root;
888 struct eina_simple_xml_node_load_ctxt ctx;
889
890 if (!buf) return NULL;
891
892 root = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*root));
893 if (!root) return NULL;
894
895 memset(root, 0, sizeof(*root));
896 EINA_MAGIC_SET(&root->base, EINA_MAGIC_SIMPLE_XML_TAG);
897 root->base.type = EINA_SIMPLE_XML_NODE_ROOT;
898
899 ctx.root = root;
900 ctx.current = root;
901 eina_simple_xml_parse(buf, buflen, strip, _eina_simple_xml_node_parse, &ctx);
902
903 return root;
904}
905
906EAPI void
907eina_simple_xml_node_root_free(Eina_Simple_XML_Node_Root *root)
908{
909 if (!root) return;
910 EINA_MAGIC_CHECK_TAG(&root->base);
911 if (root->base.type != EINA_SIMPLE_XML_NODE_ROOT)
912 {
913 ERR("expected root node!");
914 return;
915 }
916 _eina_simple_xml_node_tag_free(root);
917}
918
919static inline void
920_eina_simple_xml_node_dump_indent(Eina_Strbuf *buf, const char *indent, unsigned level)
921{
922 unsigned i, indent_len = strlen(indent);
923 for (i = 0; i < level; i++)
924 eina_strbuf_append_length(buf, indent, indent_len);
925}
926
927static void
928_eina_simple_xml_node_tag_attributes_append(Eina_Strbuf *buf, Eina_Simple_XML_Node_Tag *tag)
929{
930 Eina_Simple_XML_Attribute *a;
931
932 EINA_INLIST_FOREACH(tag->attributes, a)
933 eina_strbuf_append_printf(buf, " %s=\"%s\"", a->key, a->value);
934}
935
936static void _eina_simple_xml_node_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node *node, const char *indent, unsigned level);
937
938static void
939_eina_simple_xml_node_children_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node_Tag *tag, const char *indent, unsigned level)
940{
941 Eina_Simple_XML_Node *node;
942
943 EINA_INLIST_FOREACH(tag->children, node)
944 _eina_simple_xml_node_dump(buf, node, indent, level);
945}
946
947static void
948_eina_simple_xml_node_dump(Eina_Strbuf *buf, Eina_Simple_XML_Node *node, const char *indent, unsigned level)
949{
950 switch (node->type)
951 {
952 case EINA_SIMPLE_XML_NODE_ROOT:
953 _eina_simple_xml_node_children_dump
954 (buf, (Eina_Simple_XML_Node_Tag *)node, indent, level);
955 break;
956
957 case EINA_SIMPLE_XML_NODE_TAG:
958 {
959 Eina_Simple_XML_Node_Tag *n = (Eina_Simple_XML_Node_Tag *)node;
960
961 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
962
963 eina_strbuf_append_char(buf, '<');
964 eina_strbuf_append_length
965 (buf, n->name, eina_stringshare_strlen(n->name));
966
967 if (n->attributes)
968 _eina_simple_xml_node_tag_attributes_append(buf, n);
969
970 if (n->children)
971 eina_strbuf_append_char(buf, '>');
972 else
973 eina_strbuf_append_length(buf, "/>", sizeof("/>") - 1);
974
975 if (indent) eina_strbuf_append_char(buf, '\n');
976
977 if (n->children)
978 {
979 _eina_simple_xml_node_children_dump(buf, n, indent, level + 1);
980
981 if (indent)
982 _eina_simple_xml_node_dump_indent(buf, indent, level);
983
984 eina_strbuf_append_length(buf, "</", sizeof("</") - 1);
985 eina_strbuf_append_length
986 (buf, n->name, eina_stringshare_strlen(n->name));
987 eina_strbuf_append_char(buf, '>');
988
989 if (indent) eina_strbuf_append_char(buf, '\n');
990 }
991 }
992 break;
993 case EINA_SIMPLE_XML_NODE_DATA:
994 {
995 Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node;
996
997 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
998 eina_strbuf_append_length(buf, n->data, n->length);
999 if (indent) eina_strbuf_append_char(buf, '\n');
1000 }
1001 break;
1002
1003 case EINA_SIMPLE_XML_NODE_CDATA:
1004 {
1005 Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node;
1006
1007 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
1008 eina_strbuf_append_length(buf, "<![CDATA[", sizeof("<![CDATA[") - 1);
1009 eina_strbuf_append_length(buf, n->data, n->length);
1010 eina_strbuf_append_length(buf, "]]>", sizeof("]]>") - 1);
1011 if (indent) eina_strbuf_append_char(buf, '\n');
1012 }
1013 break;
1014
1015 case EINA_SIMPLE_XML_NODE_PROCESSING:
1016 {
1017 Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node;
1018
1019 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
1020 eina_strbuf_append_length(buf, "<?", sizeof("<?") - 1);
1021 eina_strbuf_append_length(buf, n->data, n->length);
1022 eina_strbuf_append_length(buf, " ?>", sizeof(" ?>") - 1);
1023 if (indent) eina_strbuf_append_char(buf, '\n');
1024 }
1025 break;
1026
1027 case EINA_SIMPLE_XML_NODE_DOCTYPE:
1028 {
1029 Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node;
1030
1031 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
1032 eina_strbuf_append_length
1033 (buf, "<!DOCTYPE ", sizeof("<!DOCTYPE ") - 1);
1034 eina_strbuf_append_length(buf, n->data, n->length);
1035 eina_strbuf_append_char(buf, '>');
1036 if (indent) eina_strbuf_append_char(buf, '\n');
1037 }
1038 break;
1039
1040 case EINA_SIMPLE_XML_NODE_COMMENT:
1041 {
1042 Eina_Simple_XML_Node_Data *n = (Eina_Simple_XML_Node_Data *)node;
1043
1044 if (indent) _eina_simple_xml_node_dump_indent(buf, indent, level);
1045 eina_strbuf_append_length(buf, "<!-- ", sizeof("<!-- ") - 1);
1046 eina_strbuf_append_length(buf, n->data, n->length);
1047 eina_strbuf_append_length(buf, " -->", sizeof(" -->") - 1);
1048 if (indent) eina_strbuf_append_char(buf, '\n');
1049 }
1050 break;
1051 }
1052}
1053
1054EAPI char *
1055eina_simple_xml_node_dump(Eina_Simple_XML_Node *node, const char *indent)
1056{
1057 Eina_Strbuf *buf;
1058 char *ret;
1059
1060 if (!node) return NULL;
1061
1062 buf = eina_strbuf_new();
1063 if (!buf) return NULL;
1064
1065 _eina_simple_xml_node_dump(buf, node, indent, 0);
1066
1067 ret = eina_strbuf_string_steal(buf);
1068 eina_strbuf_free(buf);
1069 return ret;
1070}