<commit_before>/* EINA - EFL data type library
 * Copyright (C) 2011 <NAME>
 *                    <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#elif defined __GNUC__
# define alloca __builtin_alloca
#elif defined _AIX
# define alloca __alloca
#elif defined _MSC_VER
# include <malloc.h>
# define alloca _alloca
#else
# include <stddef.h>
# ifdef  __cplusplus
extern "C"
# endif
void *alloca (size_t);
#endif

#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef HAVE_EVIL
# include <Evil.h>
#endif

#include "eina_private.h"
#include "eina_log.h"
#include "eina_mempool.h"
#include "eina_stringshare.h"
#include "eina_strbuf.h"
#include "eina_simple_xml_parser.h"

/*============================================================================*
 *                                  Local                                     *
 *============================================================================*/

/**
 * @cond LOCAL
 */

static Eina_Mempool *_eina_simple_xml_tag_mp = NULL;
static Eina_Mempool *_eina_simple_xml_attribute_mp = NULL;
static int _eina_simple_xml_log_dom = -1;

static const char EINA_MAGIC_SIMPLE_XML_TAG_STR[] = "Eina Simple XML Tag";
static const char EINA_MAGIC_SIMPLE_XML_DATA_STR[] = "Eina Simple XML Data";
static const char EINA_MAGIC_SIMPLE_XML_ATTRIBUTE_STR[] = "Eina Simple XML Attribute";

#define EINA_MAGIC_CHECK_TAG(d, ...)                            \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_TAG))       \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_TAG);        \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)

#define EINA_MAGIC_CHECK_DATA(d, ...)                           \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_DATA))      \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_DATA);       \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)

#define EINA_MAGIC_CHECK_ATTRIBUTE(d, ...)                      \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE)) \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);  \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)


#ifndef EINA_LOG_COLOR_DEFAULT
#define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
#endif

#ifdef ERR
#undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_eina_simple_xml_log_dom, __VA_ARGS__)

#ifdef WRN
#undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_eina_simple_xml_log_dom, __VA_ARGS__)

#ifdef DBG
#undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_eina_simple_xml_log_dom, __VA_ARGS__)


static inline const char *
_eina_simple_xml_whitespace_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if (isspace((unsigned char)*itr)) break;
   return itr;
}

static inline const char *
_eina_simple_xml_whitespace_skip(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if (!isspace((unsigned char)*itr)) break;
   return itr;
}

static inline const char *
_eina_simple_xml_whitespace_unskip(const char *itr, const char *itr_start)
{
   for (itr--; itr > itr_start; itr--)
     if (!isspace((unsigned char)*itr)) break;
   return itr + 1;
}

static inline const char *
_eina_simple_xml_tag_start_find(const char *itr, const char *itr_end)
{
   return memchr(itr, '<', itr_end - itr);
}

static inline const char *
_eina_simple_xml_tag_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == '>') || (*itr == '<')) /* consider < also ends a tag */
       return itr;
   return NULL;
}

static inline const char *
_eina_simple_xml_tag_comment_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == '-') &&
         ((itr + 1 < itr_end) && (*(itr + 1) == '-')) &&
         ((itr + 2 < itr_end) && (*(itr + 2) == '>')))
       return itr + 2;
   return NULL;
}

static inline const char *
_eina_simple_xml_tag_cdata_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == ']') &&
         ((itr + 1 < itr_end) && (*(itr + 1) == ']')) &&
         ((itr + 2 < itr_end) && (*(itr + 2) == '>')))
       return itr + 2;
   return NULL;
}

/**
 * @endcond
 */

/*============================================================================*
 *                                 Global                                     *
 *============================================================================*/


/**
 * @internal
 * @brief Initialize the simple xml parser module.
 *
 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
 *
 * This function sets up the simple xml parser module of Eina. It is called by
 * eina_init().
 *
 * @see eina_init()
 */
Eina_Bool
eina_simple_xml_init(void)
{
   const char *choice, *tmp;

   _eina_simple_xml_log_dom = eina_log_domain_register("eina_simple_xml",
                                                       EINA_LOG_COLOR_DEFAULT);
   if (_eina_simple_xml_log_dom < 0)
     {
        EINA_LOG_ERR("Could not register log domain: eina_simple_xml");
        return EINA_FALSE;
     }

#ifdef EINA_DEFAULT_MEMPOOL
   choice = "pass_through";
#else
   choice = "chained_mempool";
#endif
   tmp = getenv("EINA_MEMPOOL");
   if (tmp && tmp[0])
      choice = tmp;

   _eina_simple_xml_tag_mp = eina_mempool_add
         (choice, "simple_xml_tag", NULL,
          sizeof(Eina_Simple_XML_Node_Tag), 32);
   if (!_eina_simple_xml_tag_mp)
     {
        ERR("Mempool for simple_xml_tag cannot be allocated in init.");
        goto on_init_fail;
     }

   _eina_simple_xml_attribute_mp = eina_mempool_add
         (choice, "simple_xml_attribute", NULL,
          sizeof(Eina_Simple_XML_Attribute), 8);
   if (!_eina_simple_xml_attribute_mp)
     {
        ERR("Mempool for simple_xml_attribute cannot be allocated in init.");
        eina_mempool_del(_eina_simple_xml_tag_mp);
        goto on_init_fail;
     }

#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
   EMS(EINA_MAGIC_SIMPLE_XML_TAG);
   EMS(EINA_MAGIC_SIMPLE_XML_DATA);
   EMS(EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
#undef EMS

   return EINA_TRUE;

on_init_fail:
   eina_log_domain_unregister(_eina_simple_xml_log_dom);
   _eina_simple_xml_log_dom = -1;
   return EINA_FALSE;
}

/**
 * @internal
 * @brief Shut down the simple xml parser module.
 *
 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
 *
 * This function shuts down the simple xml parser module set
 * up by eina_simple_xml_init(). It is called by
 * eina_shutdown().
 *
 * @see eina_shutdown()
 */
Eina_Bool
eina_simple_xml_shutdown(void)
{
   eina_mempool_del(_eina_simple_xml_attribute_mp);
   eina_mempool_del(_eina_simple_xml_tag_mp);

   eina_log_domain_unregister(_eina_simple_xml_log_dom);
   _eina_simple_xml_log_dom = -1;
   return EINA_TRUE;
}


/*============================================================================*
 *                                   API                                      *
 *============================================================================*/


EAPI Eina_Bool
eina_simple_xml_parse(const char *buf, unsigned buflen, Eina_Bool strip, Eina_Simple_XML_Cb func, const void *data)
{
   const char *itr = buf, *itr_end = buf + buflen;

   if (!buf) return EINA_FALSE;
   if (!func) return EINA_FALSE;

#define CB(type, start, end)                                            \
   do                                                                   \
     {                                                                  \
        size_t _sz = end - start;                                       \
        Eina_Bool _ret;                                                 \
        _ret = func((void*)data, type, start, start - buf, _sz);        \
        if (!_ret) return EINA_FALSE;                                   \
     }                                                                  \
   while (0)

   while (itr < itr_end)
     {
        if (itr[0] == '<')
          {
             if (itr + 1 >= itr_end)
               {
                  CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
                  return EINA_FALSE;
               }
             else
               {
                  Eina_Simple_XML_Type type;
                  size_t toff;
                  const char *p;

                  if (itr[1] == '/')
                    {
                       type = EINA_SIMPLE_XML_CLOSE;
                       toff = 1;
                    }
                  else if (itr[1] == '?')
                    {
                       type = EINA_SIMPLE_XML_PROCESSING;
                       toff = 1;
                    }
                  else if (itr[1] == '!')
                    {
                       if ((itr + sizeof("<!DOCTYPE>") - 1 < itr_end) &&
                           (!memcmp(itr + 2, "DOCTYPE",
                                    sizeof("DOCTYPE") - 1)) &&
                           ((itr[2 + sizeof("DOCTYPE") - 1] == '>') ||
                            (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1]))))
                         {
                            type = EINA_SIMPLE_XML_DOCTYPE;
                            toff = sizeof("!DOCTYPE") - 1;
                         }
                       else if ((itr + sizeof("<!---->") - 1 < itr_end) &&
                                (!memcmp(itr + 2, "--", sizeof("--") - 1)))
                         {
                            type = EINA_SIMPLE_XML_COMMENT;
                            toff = sizeof("!--") - 1;
                         }
                       else if ((itr + sizeof("<![CDATA[]]>") - 1 < itr_end) &&
                                (!memcmp(itr + 2, "[CDATA[",
                                         sizeof("[CDATA[") - 1)))
                         {
                            type = EINA_SIMPLE_XML_CDATA;
                            toff = sizeof("![CDATA[") - 1;
                         }
                       else
                         {
                            type = EINA_SIMPLE_XML_OPEN;
                            toff = 0;
                         }
                    }
                  else
                    {
                       type = EINA_SIMPLE_XML_OPEN;
                       toff = 0;
                    }

                  if (type == EINA_SIMPLE_XML_CDATA)
                    p = _eina_simple_xml_tag_cdata_end_find(itr + 1 + toff, itr_end);
                  else if (type == EINA_SIMPLE_XML_COMMENT)
                    p = _eina_simple_xml_tag_comment_end_find(itr + 1 + toff, itr_end);
                  else
                    p = _eina_simple_xml_tag_end_find(itr + 1 + toff, itr_end);

                  if ((p) && (*p == '<'))
                    {
                       type = EINA_SIMPLE_XML_ERROR;
                       toff = 0;
                    }

                  if (p)
                    {
                       const char *start, *end;

                       start = itr + 1 + toff;
                       end = p;

                       switch (type)
                         {
                          case EINA_SIMPLE_XML_OPEN:
                             if (p[-1] == '/')
                               {
                                  type = EINA_SIMPLE_XML_OPEN_EMPTY;
                                  end--;
                               }
                             break;
                          case EINA_SIMPLE_XML_CDATA:
                             if (!memcmp(p - 2, "]]", 2)) end -= 2;
                             break;
                          case EINA_SIMPLE_XML_PROCESSING:
                             if (p[-1] == '?') end--;
                             break;
                          case EINA_SIMPLE_XML_COMMENT:
                             if (!memcmp(p - 2, "--", 2)) end -= 2;
                             break;
                          case EINA_SIMPLE_XML_OPEN_EMPTY:
                          case EINA_SIMPLE_XML_CLOSE:
                          case EINA_SIMPLE_XML_DATA:
                          case EINA_SIMPLE_XML_ERROR:
                          case EINA_SIMPLE_XML_DOCTYPE:
                          case EINA_SIMPLE_XML_IGNORED:
                             break;
                         }

                       if ((strip) && (type != EINA_SIMPLE_XML_ERROR))
                         {
                            start = _eina_simple_xml_whitespace_skip
                              (start, end);
                            end = _eina_simple_xml_whitespace_unskip
                              (end, start + 1);
                         }

                       CB(type, start, end);

                       if (type != EINA_SIMPLE_XML_ERROR)
                         itr = p + 1;
                       else
                         itr = p;
                    }
                  else
                    {
                       CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
                       return EINA_FALSE;
                    }
               }
          }
        else
          {
             const char *p, *end;

             if (strip)
               {
                  p = _eina_simple_xml_whitespace_skip(itr, itr_end);
                  if (p)
                    {
                       CB(EINA_SIMPLE_XML_IGNORED, itr, p);
                       itr = p;
                    }
               }

             p = _eina_simple_xml_tag_start_find(itr, itr_end);
             if (!p) p = itr_end;

             end = p;
             if (strip)
               end = _eina_simple_xml_whitespace_unskip(end, itr);

             if (itr != end)
               CB(EINA_SIMPLE_XML_DATA, itr, end);

             if ((strip) && (end < p))
               CB(EINA_SIMPLE_XML_IGNORED, end, p);

             itr = p;
          }
     }

#undef CB

   return EINA_TRUE;
}

EAPI const char *
eina_simple_xml_tag_attributes_find(const char *buf, unsigned buflen)
{
   const char *itr = buf, *itr_end = buf + buflen;

   for (; itr < itr_end; itr++)
     {
        if (!isspace((unsigned char)*itr))
          {
             /* user skip tagname and already gave it the attributes */
             if (*itr == '=')
               return buf;
          }
        else
          {
             itr = _eina_simple_xml_whitespace_skip(itr + 1, itr_end);
             if (itr == itr_end)
               return NULL;
             return itr;
          }
     }

   return NULL;
}

EAPI Eina_Bool
eina_simple_xml_attributes_parse(const char *buf, unsigned buflen, Eina_Simple_XML_Attribute_Cb func, const void *data)
{
   const char *itr = buf, *itr_end = buf + buflen;
   char *tmpbuf = alloca(buflen + 1);

   if (!buf) return EINA_FALSE;
   if (!func) return EINA_FALSE;

   while (itr < itr_end)
     {
        const char *p = _eina_simple_xml_whitespace_skip(itr, itr_end);
        const char *key, *key_end, *value, *value_end;
        char *tval;

        if (p == itr_end) return EINA_TRUE;

        key = p;
        for (key_end = key; key_end < itr_end; key_end++)
          if ((*key_end == '=') || (isspace((unsigned char)*key_end))) break;
        if (key_end == itr_end) return EINA_FALSE;
        if (key_end == key) continue;

        if (*key_end == '=') value = key_end + 1;
        else
          {
             value = memchr(key_end, '=', itr_end - key_end);
             if (!value) return EINA_FALSE;
             value++;
          }
        for (; value < itr_end; value++)
          if (!isspace((unsigned char)*value)) break;
        if (value == itr_end) return EINA_FALSE;

        if ((*value == '"') || (*value == '\''))
          {
             value_end = memchr(value + 1, *value, itr_end - value);
             if (!value_end) return EINA_FALSE;
             value++;
          }
        else
          {
             value_end = _eina_simple_xml_whitespace_find(value, itr_end);
          }

        memcpy(tmpbuf, key, key_end - key);
        tmpbuf[key_end - key] = '\0';

        tval = tmpbuf + (key_end - key) + 1;
        memcpy(tval, value, value_end - value);
        tval[value_end - value] = '\0';

        if (!func((void*)data, tmpbuf, tval))
          return EINA_FALSE;

        itr = value_end + 1;
     }
   return EINA_TRUE;
}

/* Node loader *************************************************************/

EAPI Eina_Simple_XML_Attribute *
eina_simple_xml_attribute_new(Eina_Simple_XML_Node_Tag *parent, const char *key, const char *value)
{
   Eina_Simple_XML_Attribute *attr;

   if (!key) return NULL;

   attr = eina_mempool_malloc(_eina_simple_xml_attribute_mp, sizeof(*attr));
   if (!attr)
     {
        ERR("could not allocate memory for attribute from mempool");
        return NULL;
     }

   EINA_MAGIC_SET(attr, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
   attr->parent = parent;
   attr->key = eina_stringshare_add(key);
   attr->value = eina_stringshare_add(value ? value : "");

   if (parent)
     parent->attributes = eina_inlist_append
       (parent->attributes, EINA_INLIST_GET(attr));

   return attr;
}

EAPI void
eina_simple_xml_attribute_free(Eina_Simple_XML_Attribute *attr)
{
   if (!attr)
     return;

   EINA_MAGIC_CHECK_ATTRIBUTE(attr);

   if (attr->parent)
     attr->parent->attributes = eina_inlist_remove
          (attr->parent->attributes, EINA_INLIST_GET(attr));

   eina_stringshare_del(attr->key);
   eina_stringshare_del(attr->value);
   EINA_MAGIC_SET(attr, EINA_MAGIC_NONE);
   eina_mempool_free(_eina_simple_xml_attribute_mp, attr);
}

static void
_eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node)
{
   if (node->base.parent)
     node->base.parent->children = eina_inlist_remove
          (node->base.parent->children, EINA_INLIST_GET(&node->base));

   EINA_MAGIC_SET(&node->base, EINA_MAGIC_NONE);
   free(node);
}

EAPI Eina_Simple_XML_Node_Tag *
eina_simple_xml_node_tag_new(Eina_Simple_XML_Node_Tag *parent, const char *name)
{
   Eina_Simple_XML_Node_Tag *n;

   if (!name) return NULL;

   n = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*n));
   if (!n)
     {
        ERR("could not allocate memory for node from mempool");
        return NULL;
     }

   memset(n, 0, sizeof(*n));

   EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_TAG);

   n->base.type = EINA_SIMPLE_XML_NODE_TAG;
   n->base.parent = parent;
   n->name = eina_stringshare_add(name);

   if (parent)
     parent->children = eina_inlist_append
       (parent->children, EINA_INLIST_GET(&n->base));

   return n;
}

void
_eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
{
   while (tag->children)
     {
        Eina_Simple_XML_Node *n = EINA_INLIST_CONTAINER_GET
          (tag->children, Eina_Simple_XML_Node);
        if (n->type == EINA_SIMPLE_XML_NODE_TAG)
          _eina_simple_xml_node_tag_free((Eina_Simple_XML_Node_Tag *)n);
        else
          _eina_simple_xml_node_data_free((Eina_Simple_XML_Node_Data *)n);
     }

   while (tag->attributes)
     {
        Eina_Simple_XML_Attribute *a = EINA_INLIST_CONTAINER_GET
          (tag->attributes, Eina_Simple_XML_Attribute);
        eina_simple_xml_attribute_free(a);
     }

   if (tag->base.parent)
     tag->base.parent->children = eina_inlist_remove
          (tag->base.parent->children, EINA_INLIST_GET(&tag->base));

   eina_stringshare_del(tag->name);
   EINA_MAGIC_SET(&tag->base, EINA_MAGIC_NONE);
   eina_mempool_free(_eina_simple_xml_tag_mp, tag);
}

EAPI void
eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
{
   if (!tag)
     return;

   EINA_MAGIC_CHECK_TAG(&tag->base);
   if (tag->base.type != EINA_SIMPLE_XML_NODE_TAG)
     {
        ERR("expected tag node!");
        return;
     }
   _eina_simple_xml_node_tag_free(tag);
}

static Eina_Simple_XML_Node_Data *
_eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, Eina_Simple_XML_Node_Type type, const char *content, unsigned length)
{
   Eina_Simple_XML_Node_Data *n = malloc(sizeof(*n) + length + 1);

   if (!content) return NULL;

   if (!n)
     {
        ERR("could not allocate memory for node");
        return NULL;
     }

   EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_DATA);
   n->base.type = type;
   n->base.parent = parent;

   n->length = length;
   memcpy(n->data, content, length);
   n->data[length] = '\0';

   if (parent)
     parent->children = eina_inlist_append
       (parent->children, EINA_INLIST_GET(&n->base));

   return n;
}

EAPI Eina_Simple_XML_Node_Data *
eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
{<commit_msg>Eina: fix possible mem leak<commit_after>/* EINA - EFL data type library
 * Copyright (C) 2011 <NAME>
 *                    <NAME>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#ifdef HAVE_ALLOCA_H
# include <alloca.h>
#elif defined __GNUC__
# define alloca __builtin_alloca
#elif defined _AIX
# define alloca __alloca
#elif defined _MSC_VER
# include <malloc.h>
# define alloca _alloca
#else
# include <stddef.h>
# ifdef  __cplusplus
extern "C"
# endif
void *alloca (size_t);
#endif

#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef HAVE_EVIL
# include <Evil.h>
#endif

#include "eina_private.h"
#include "eina_log.h"
#include "eina_mempool.h"
#include "eina_stringshare.h"
#include "eina_strbuf.h"
#include "eina_simple_xml_parser.h"

/*============================================================================*
 *                                  Local                                     *
 *============================================================================*/

/**
 * @cond LOCAL
 */

static Eina_Mempool *_eina_simple_xml_tag_mp = NULL;
static Eina_Mempool *_eina_simple_xml_attribute_mp = NULL;
static int _eina_simple_xml_log_dom = -1;

static const char EINA_MAGIC_SIMPLE_XML_TAG_STR[] = "Eina Simple XML Tag";
static const char EINA_MAGIC_SIMPLE_XML_DATA_STR[] = "Eina Simple XML Data";
static const char EINA_MAGIC_SIMPLE_XML_ATTRIBUTE_STR[] = "Eina Simple XML Attribute";

#define EINA_MAGIC_CHECK_TAG(d, ...)                            \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_TAG))       \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_TAG);        \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)

#define EINA_MAGIC_CHECK_DATA(d, ...)                           \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_DATA))      \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_DATA);       \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)

#define EINA_MAGIC_CHECK_ATTRIBUTE(d, ...)                      \
  do {                                                          \
     if (!EINA_MAGIC_CHECK(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE)) \
       {                                                        \
          EINA_MAGIC_FAIL(d, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);  \
          return __VA_ARGS__;                                   \
       }                                                        \
  } while(0)


#ifndef EINA_LOG_COLOR_DEFAULT
#define EINA_LOG_COLOR_DEFAULT EINA_COLOR_CYAN
#endif

#ifdef ERR
#undef ERR
#endif
#define ERR(...) EINA_LOG_DOM_ERR(_eina_simple_xml_log_dom, __VA_ARGS__)

#ifdef WRN
#undef WRN
#endif
#define WRN(...) EINA_LOG_DOM_WARN(_eina_simple_xml_log_dom, __VA_ARGS__)

#ifdef DBG
#undef DBG
#endif
#define DBG(...) EINA_LOG_DOM_DBG(_eina_simple_xml_log_dom, __VA_ARGS__)


static inline const char *
_eina_simple_xml_whitespace_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if (isspace((unsigned char)*itr)) break;
   return itr;
}

static inline const char *
_eina_simple_xml_whitespace_skip(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if (!isspace((unsigned char)*itr)) break;
   return itr;
}

static inline const char *
_eina_simple_xml_whitespace_unskip(const char *itr, const char *itr_start)
{
   for (itr--; itr > itr_start; itr--)
     if (!isspace((unsigned char)*itr)) break;
   return itr + 1;
}

static inline const char *
_eina_simple_xml_tag_start_find(const char *itr, const char *itr_end)
{
   return memchr(itr, '<', itr_end - itr);
}

static inline const char *
_eina_simple_xml_tag_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == '>') || (*itr == '<')) /* consider < also ends a tag */
       return itr;
   return NULL;
}

static inline const char *
_eina_simple_xml_tag_comment_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == '-') &&
         ((itr + 1 < itr_end) && (*(itr + 1) == '-')) &&
         ((itr + 2 < itr_end) && (*(itr + 2) == '>')))
       return itr + 2;
   return NULL;
}

static inline const char *
_eina_simple_xml_tag_cdata_end_find(const char *itr, const char *itr_end)
{
   for (; itr < itr_end; itr++)
     if ((*itr == ']') &&
         ((itr + 1 < itr_end) && (*(itr + 1) == ']')) &&
         ((itr + 2 < itr_end) && (*(itr + 2) == '>')))
       return itr + 2;
   return NULL;
}

/**
 * @endcond
 */

/*============================================================================*
 *                                 Global                                     *
 *============================================================================*/


/**
 * @internal
 * @brief Initialize the simple xml parser module.
 *
 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
 *
 * This function sets up the simple xml parser module of Eina. It is called by
 * eina_init().
 *
 * @see eina_init()
 */
Eina_Bool
eina_simple_xml_init(void)
{
   const char *choice, *tmp;

   _eina_simple_xml_log_dom = eina_log_domain_register("eina_simple_xml",
                                                       EINA_LOG_COLOR_DEFAULT);
   if (_eina_simple_xml_log_dom < 0)
     {
        EINA_LOG_ERR("Could not register log domain: eina_simple_xml");
        return EINA_FALSE;
     }

#ifdef EINA_DEFAULT_MEMPOOL
   choice = "pass_through";
#else
   choice = "chained_mempool";
#endif
   tmp = getenv("EINA_MEMPOOL");
   if (tmp && tmp[0])
      choice = tmp;

   _eina_simple_xml_tag_mp = eina_mempool_add
         (choice, "simple_xml_tag", NULL,
          sizeof(Eina_Simple_XML_Node_Tag), 32);
   if (!_eina_simple_xml_tag_mp)
     {
        ERR("Mempool for simple_xml_tag cannot be allocated in init.");
        goto on_init_fail;
     }

   _eina_simple_xml_attribute_mp = eina_mempool_add
         (choice, "simple_xml_attribute", NULL,
          sizeof(Eina_Simple_XML_Attribute), 8);
   if (!_eina_simple_xml_attribute_mp)
     {
        ERR("Mempool for simple_xml_attribute cannot be allocated in init.");
        eina_mempool_del(_eina_simple_xml_tag_mp);
        goto on_init_fail;
     }

#define EMS(n) eina_magic_string_static_set(n, n ## _STR)
   EMS(EINA_MAGIC_SIMPLE_XML_TAG);
   EMS(EINA_MAGIC_SIMPLE_XML_DATA);
   EMS(EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
#undef EMS

   return EINA_TRUE;

on_init_fail:
   eina_log_domain_unregister(_eina_simple_xml_log_dom);
   _eina_simple_xml_log_dom = -1;
   return EINA_FALSE;
}

/**
 * @internal
 * @brief Shut down the simple xml parser module.
 *
 * @return #EINA_TRUE on success, #EINA_FALSE on failure.
 *
 * This function shuts down the simple xml parser module set
 * up by eina_simple_xml_init(). It is called by
 * eina_shutdown().
 *
 * @see eina_shutdown()
 */
Eina_Bool
eina_simple_xml_shutdown(void)
{
   eina_mempool_del(_eina_simple_xml_attribute_mp);
   eina_mempool_del(_eina_simple_xml_tag_mp);

   eina_log_domain_unregister(_eina_simple_xml_log_dom);
   _eina_simple_xml_log_dom = -1;
   return EINA_TRUE;
}


/*============================================================================*
 *                                   API                                      *
 *============================================================================*/


EAPI Eina_Bool
eina_simple_xml_parse(const char *buf, unsigned buflen, Eina_Bool strip, Eina_Simple_XML_Cb func, const void *data)
{
   const char *itr = buf, *itr_end = buf + buflen;

   if (!buf) return EINA_FALSE;
   if (!func) return EINA_FALSE;

#define CB(type, start, end)                                            \
   do                                                                   \
     {                                                                  \
        size_t _sz = end - start;                                       \
        Eina_Bool _ret;                                                 \
        _ret = func((void*)data, type, start, start - buf, _sz);        \
        if (!_ret) return EINA_FALSE;                                   \
     }                                                                  \
   while (0)

   while (itr < itr_end)
     {
        if (itr[0] == '<')
          {
             if (itr + 1 >= itr_end)
               {
                  CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
                  return EINA_FALSE;
               }
             else
               {
                  Eina_Simple_XML_Type type;
                  size_t toff;
                  const char *p;

                  if (itr[1] == '/')
                    {
                       type = EINA_SIMPLE_XML_CLOSE;
                       toff = 1;
                    }
                  else if (itr[1] == '?')
                    {
                       type = EINA_SIMPLE_XML_PROCESSING;
                       toff = 1;
                    }
                  else if (itr[1] == '!')
                    {
                       if ((itr + sizeof("<!DOCTYPE>") - 1 < itr_end) &&
                           (!memcmp(itr + 2, "DOCTYPE",
                                    sizeof("DOCTYPE") - 1)) &&
                           ((itr[2 + sizeof("DOCTYPE") - 1] == '>') ||
                            (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1]))))
                         {
                            type = EINA_SIMPLE_XML_DOCTYPE;
                            toff = sizeof("!DOCTYPE") - 1;
                         }
                       else if ((itr + sizeof("<!---->") - 1 < itr_end) &&
                                (!memcmp(itr + 2, "--", sizeof("--") - 1)))
                         {
                            type = EINA_SIMPLE_XML_COMMENT;
                            toff = sizeof("!--") - 1;
                         }
                       else if ((itr + sizeof("<![CDATA[]]>") - 1 < itr_end) &&
                                (!memcmp(itr + 2, "[CDATA[",
                                         sizeof("[CDATA[") - 1)))
                         {
                            type = EINA_SIMPLE_XML_CDATA;
                            toff = sizeof("![CDATA[") - 1;
                         }
                       else
                         {
                            type = EINA_SIMPLE_XML_OPEN;
                            toff = 0;
                         }
                    }
                  else
                    {
                       type = EINA_SIMPLE_XML_OPEN;
                       toff = 0;
                    }

                  if (type == EINA_SIMPLE_XML_CDATA)
                    p = _eina_simple_xml_tag_cdata_end_find(itr + 1 + toff, itr_end);
                  else if (type == EINA_SIMPLE_XML_COMMENT)
                    p = _eina_simple_xml_tag_comment_end_find(itr + 1 + toff, itr_end);
                  else
                    p = _eina_simple_xml_tag_end_find(itr + 1 + toff, itr_end);

                  if ((p) && (*p == '<'))
                    {
                       type = EINA_SIMPLE_XML_ERROR;
                       toff = 0;
                    }

                  if (p)
                    {
                       const char *start, *end;

                       start = itr + 1 + toff;
                       end = p;

                       switch (type)
                         {
                          case EINA_SIMPLE_XML_OPEN:
                             if (p[-1] == '/')
                               {
                                  type = EINA_SIMPLE_XML_OPEN_EMPTY;
                                  end--;
                               }
                             break;
                          case EINA_SIMPLE_XML_CDATA:
                             if (!memcmp(p - 2, "]]", 2)) end -= 2;
                             break;
                          case EINA_SIMPLE_XML_PROCESSING:
                             if (p[-1] == '?') end--;
                             break;
                          case EINA_SIMPLE_XML_COMMENT:
                             if (!memcmp(p - 2, "--", 2)) end -= 2;
                             break;
                          case EINA_SIMPLE_XML_OPEN_EMPTY:
                          case EINA_SIMPLE_XML_CLOSE:
                          case EINA_SIMPLE_XML_DATA:
                          case EINA_SIMPLE_XML_ERROR:
                          case EINA_SIMPLE_XML_DOCTYPE:
                          case EINA_SIMPLE_XML_IGNORED:
                             break;
                         }

                       if ((strip) && (type != EINA_SIMPLE_XML_ERROR))
                         {
                            start = _eina_simple_xml_whitespace_skip
                              (start, end);
                            end = _eina_simple_xml_whitespace_unskip
                              (end, start + 1);
                         }

                       CB(type, start, end);

                       if (type != EINA_SIMPLE_XML_ERROR)
                         itr = p + 1;
                       else
                         itr = p;
                    }
                  else
                    {
                       CB(EINA_SIMPLE_XML_ERROR, itr, itr_end);
                       return EINA_FALSE;
                    }
               }
          }
        else
          {
             const char *p, *end;

             if (strip)
               {
                  p = _eina_simple_xml_whitespace_skip(itr, itr_end);
                  if (p)
                    {
                       CB(EINA_SIMPLE_XML_IGNORED, itr, p);
                       itr = p;
                    }
               }

             p = _eina_simple_xml_tag_start_find(itr, itr_end);
             if (!p) p = itr_end;

             end = p;
             if (strip)
               end = _eina_simple_xml_whitespace_unskip(end, itr);

             if (itr != end)
               CB(EINA_SIMPLE_XML_DATA, itr, end);

             if ((strip) && (end < p))
               CB(EINA_SIMPLE_XML_IGNORED, end, p);

             itr = p;
          }
     }

#undef CB

   return EINA_TRUE;
}

EAPI const char *
eina_simple_xml_tag_attributes_find(const char *buf, unsigned buflen)
{
   const char *itr = buf, *itr_end = buf + buflen;

   for (; itr < itr_end; itr++)
     {
        if (!isspace((unsigned char)*itr))
          {
             /* user skip tagname and already gave it the attributes */
             if (*itr == '=')
               return buf;
          }
        else
          {
             itr = _eina_simple_xml_whitespace_skip(itr + 1, itr_end);
             if (itr == itr_end)
               return NULL;
             return itr;
          }
     }

   return NULL;
}

EAPI Eina_Bool
eina_simple_xml_attributes_parse(const char *buf, unsigned buflen, Eina_Simple_XML_Attribute_Cb func, const void *data)
{
   const char *itr = buf, *itr_end = buf + buflen;
   char *tmpbuf = alloca(buflen + 1);

   if (!buf) return EINA_FALSE;
   if (!func) return EINA_FALSE;

   while (itr < itr_end)
     {
        const char *p = _eina_simple_xml_whitespace_skip(itr, itr_end);
        const char *key, *key_end, *value, *value_end;
        char *tval;

        if (p == itr_end) return EINA_TRUE;

        key = p;
        for (key_end = key; key_end < itr_end; key_end++)
          if ((*key_end == '=') || (isspace((unsigned char)*key_end))) break;
        if (key_end == itr_end) return EINA_FALSE;
        if (key_end == key) continue;

        if (*key_end == '=') value = key_end + 1;
        else
          {
             value = memchr(key_end, '=', itr_end - key_end);
             if (!value) return EINA_FALSE;
             value++;
          }
        for (; value < itr_end; value++)
          if (!isspace((unsigned char)*value)) break;
        if (value == itr_end) return EINA_FALSE;

        if ((*value == '"') || (*value == '\''))
          {
             value_end = memchr(value + 1, *value, itr_end - value);
             if (!value_end) return EINA_FALSE;
             value++;
          }
        else
          {
             value_end = _eina_simple_xml_whitespace_find(value, itr_end);
          }

        memcpy(tmpbuf, key, key_end - key);
        tmpbuf[key_end - key] = '\0';

        tval = tmpbuf + (key_end - key) + 1;
        memcpy(tval, value, value_end - value);
        tval[value_end - value] = '\0';

        if (!func((void*)data, tmpbuf, tval))
          return EINA_FALSE;

        itr = value_end + 1;
     }
   return EINA_TRUE;
}

/* Node loader *************************************************************/

EAPI Eina_Simple_XML_Attribute *
eina_simple_xml_attribute_new(Eina_Simple_XML_Node_Tag *parent, const char *key, const char *value)
{
   Eina_Simple_XML_Attribute *attr;

   if (!key) return NULL;

   attr = eina_mempool_malloc(_eina_simple_xml_attribute_mp, sizeof(*attr));
   if (!attr)
     {
        ERR("could not allocate memory for attribute from mempool");
        return NULL;
     }

   EINA_MAGIC_SET(attr, EINA_MAGIC_SIMPLE_XML_ATTRIBUTE);
   attr->parent = parent;
   attr->key = eina_stringshare_add(key);
   attr->value = eina_stringshare_add(value ? value : "");

   if (parent)
     parent->attributes = eina_inlist_append
       (parent->attributes, EINA_INLIST_GET(attr));

   return attr;
}

EAPI void
eina_simple_xml_attribute_free(Eina_Simple_XML_Attribute *attr)
{
   if (!attr)
     return;

   EINA_MAGIC_CHECK_ATTRIBUTE(attr);

   if (attr->parent)
     attr->parent->attributes = eina_inlist_remove
          (attr->parent->attributes, EINA_INLIST_GET(attr));

   eina_stringshare_del(attr->key);
   eina_stringshare_del(attr->value);
   EINA_MAGIC_SET(attr, EINA_MAGIC_NONE);
   eina_mempool_free(_eina_simple_xml_attribute_mp, attr);
}

static void
_eina_simple_xml_node_data_free(Eina_Simple_XML_Node_Data *node)
{
   if (node->base.parent)
     node->base.parent->children = eina_inlist_remove
          (node->base.parent->children, EINA_INLIST_GET(&node->base));

   EINA_MAGIC_SET(&node->base, EINA_MAGIC_NONE);
   free(node);
}

EAPI Eina_Simple_XML_Node_Tag *
eina_simple_xml_node_tag_new(Eina_Simple_XML_Node_Tag *parent, const char *name)
{
   Eina_Simple_XML_Node_Tag *n;

   if (!name) return NULL;

   n = eina_mempool_malloc(_eina_simple_xml_tag_mp, sizeof(*n));
   if (!n)
     {
        ERR("could not allocate memory for node from mempool");
        return NULL;
     }

   memset(n, 0, sizeof(*n));

   EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_TAG);

   n->base.type = EINA_SIMPLE_XML_NODE_TAG;
   n->base.parent = parent;
   n->name = eina_stringshare_add(name);

   if (parent)
     parent->children = eina_inlist_append
       (parent->children, EINA_INLIST_GET(&n->base));

   return n;
}

void
_eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
{
   while (tag->children)
     {
        Eina_Simple_XML_Node *n = EINA_INLIST_CONTAINER_GET
          (tag->children, Eina_Simple_XML_Node);
        if (n->type == EINA_SIMPLE_XML_NODE_TAG)
          _eina_simple_xml_node_tag_free((Eina_Simple_XML_Node_Tag *)n);
        else
          _eina_simple_xml_node_data_free((Eina_Simple_XML_Node_Data *)n);
     }

   while (tag->attributes)
     {
        Eina_Simple_XML_Attribute *a = EINA_INLIST_CONTAINER_GET
          (tag->attributes, Eina_Simple_XML_Attribute);
        eina_simple_xml_attribute_free(a);
     }

   if (tag->base.parent)
     tag->base.parent->children = eina_inlist_remove
          (tag->base.parent->children, EINA_INLIST_GET(&tag->base));

   eina_stringshare_del(tag->name);
   EINA_MAGIC_SET(&tag->base, EINA_MAGIC_NONE);
   eina_mempool_free(_eina_simple_xml_tag_mp, tag);
}

EAPI void
eina_simple_xml_node_tag_free(Eina_Simple_XML_Node_Tag *tag)
{
   if (!tag)
     return;

   EINA_MAGIC_CHECK_TAG(&tag->base);
   if (tag->base.type != EINA_SIMPLE_XML_NODE_TAG)
     {
        ERR("expected tag node!");
        return;
     }
   _eina_simple_xml_node_tag_free(tag);
}

static Eina_Simple_XML_Node_Data *
_eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, Eina_Simple_XML_Node_Type type, const char *content, unsigned length)
{
   Eina_Simple_XML_Node_Data *n;

   if (!content) return NULL;

   n = malloc(sizeof(*n) + length + 1);

   if (!n)
     {
        ERR("could not allocate memory for node");
        return NULL;
     }

   EINA_MAGIC_SET(&n->base, EINA_MAGIC_SIMPLE_XML_DATA);
   n->base.type = type;
   n->base.parent = parent;

   n->length = length;
   memcpy(n->data, content, length);
   n->data[length] = '\0';

   if (parent)
     parent->children = eina_inlist_append
       (parent->children, EINA_INLIST_GET(&n->base));

   return n;
}

EAPI Eina_Simple_XML_Node_Data *
eina_simple_xml_node_data_new(Eina_Simple_XML_Node_Tag *parent, const char *contents, size_t length)
{
{'fraction_non_alphanumeric': 0.1235131617999589, 'fraction_numerical': 0.003652884637337047, 'mean_word_length': 1.6361338468945594, 'pattern_counts': {'":': 0, '<': 79, '<?xml version=': 0, '>': 143, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 140}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 102, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3846311', 'n_tokens_mistral': 14284, 'n_tokens_neox': 13566, 'n_words': 2789}