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