1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif /* ifdef HAVE_CONFIG_H */
4 
5 #include <string.h>
6 #include <stdio.h>
7 
8 #include <Eina.h>
9 
10 #include "Eet.h"
11 #include "Eet_private.h"
12 
13 static Eina_Mempool *_eet_node_mp = NULL;
14 
15 Eet_Node *
eet_node_new(void)16 eet_node_new(void)
17 {
18    Eet_Node *result;
19 
20    result = eina_mempool_malloc(_eet_node_mp, sizeof (Eet_Node));
21    if (!result)
22      return NULL;
23 
24    memset(result, 0, sizeof (Eet_Node));
25    return result;
26 }
27 
28 void
eet_node_free(Eet_Node * node)29 eet_node_free(Eet_Node *node)
30 {
31    eina_mempool_free(_eet_node_mp, node);
32 }
33 
34 static Eet_Node *
_eet_node_new(const char * name,int type)35 _eet_node_new(const char *name,
36               int         type)
37 {
38    Eet_Node *n;
39 
40    n = eet_node_new();
41    if (!n)
42      return NULL;
43 
44    n->type = type;
45    n->name = eina_stringshare_add(name);
46 
47    return n;
48 }
49 
50 static void
_eet_node_append(Eet_Node * n,Eina_List * nodes)51 _eet_node_append(Eet_Node  *n,
52                  Eina_List *nodes)
53 {
54    Eet_Node *value;
55    Eina_List *l;
56 
57    EINA_LIST_REVERSE_FOREACH(nodes, l, value)
58      {
59         value->next = n->values;
60         n->values = value;
61      }
62 }
63 
64 #define EET_NODE_NEW(Eet_type, Name, Value, Type)         \
65   EAPI Eet_Node *                                         \
66   eet_node_ ## Name ## _new(const char *name, Type Value) \
67   {                                                       \
68      Eet_Node *n;                                         \
69                                                           \
70      n = _eet_node_new(name, Eet_type);                   \
71      if (!n) { return NULL; }                             \
72                                                           \
73      n->data.value.Value = Value;                         \
74                                                           \
75      return n;                                            \
76   }
77 
78 #define EET_NODE_STR_NEW(Eet_type, Name, Value, Type)     \
79   EAPI Eet_Node *                                         \
80   eet_node_ ## Name ## _new(const char *name, Type Value) \
81   {                                                       \
82      Eet_Node *n;                                         \
83                                                           \
84      n = _eet_node_new(name, Eet_type);                   \
85      if (!n) { return NULL; }                             \
86                                                           \
87      n->data.value.Value = eina_stringshare_add(Value);   \
88                                                           \
89      return n;                                            \
90   }
91 
EET_NODE_NEW(EET_T_CHAR,char,c,char)92 EET_NODE_NEW(EET_T_CHAR, char, c, char)
93 EET_NODE_NEW(EET_T_SHORT, short, s, short)
94 EET_NODE_NEW(EET_T_INT, int, i, int)
95 EET_NODE_NEW(EET_T_LONG_LONG, long_long, l, long long)
96 EET_NODE_NEW(EET_T_FLOAT, float, f, float)
97 EET_NODE_NEW(EET_T_DOUBLE, double, d, double)
98 EET_NODE_NEW(EET_T_UCHAR, unsigned_char, uc, unsigned char)
99 EET_NODE_NEW(EET_T_USHORT, unsigned_short, us, unsigned short)
100 EET_NODE_NEW(EET_T_UINT, unsigned_int, ui, unsigned int)
101 EET_NODE_NEW(EET_T_ULONG_LONG, unsigned_long_long, ul, unsigned long long)
102 EET_NODE_STR_NEW(EET_T_STRING, string, str, const char *)
103 EET_NODE_STR_NEW(EET_T_INLINED_STRING, inlined_string, str, const char *)
104 
105 Eet_Node *
106 eet_node_null_new(const char *name)
107 {
108    Eet_Node *n;
109 
110    n = _eet_node_new(name, EET_T_NULL);
111    if (!n)
112      return NULL;
113 
114    n->data.value.str = NULL;
115 
116    return n;
117 }
118 
119 Eet_Node *
eet_node_list_new(const char * name,Eina_List * nodes)120 eet_node_list_new(const char *name,
121                   Eina_List  *nodes)
122 {
123    Eet_Node *n;
124 
125    n = _eet_node_new(name, EET_G_LIST);
126    if (!n)
127      return NULL;
128 
129    _eet_node_append(n, nodes);
130 
131    return n;
132 }
133 
134 Eet_Node *
eet_node_array_new(const char * name,int count,Eina_List * nodes)135 eet_node_array_new(const char *name,
136                    int         count,
137                    Eina_List  *nodes)
138 {
139    Eet_Node *n;
140 
141    n = _eet_node_new(name, EET_G_ARRAY);
142    if (!n)
143      return NULL;
144 
145    n->count = count;
146 
147    _eet_node_append(n, nodes);
148 
149    return n;
150 }
151 
152 Eet_Node *
eet_node_var_array_new(const char * name,Eina_List * nodes)153 eet_node_var_array_new(const char *name,
154                        Eina_List  *nodes)
155 {
156    Eet_Node *n;
157 
158    n = _eet_node_new(name, EET_G_VAR_ARRAY);
159    if (!n)
160      return NULL;
161 
162    n->count = eina_list_count(nodes);
163 
164    _eet_node_append(n, nodes);
165 
166    return n;
167 }
168 
169 Eet_Node *
eet_node_hash_new(const char * name,const char * key,Eet_Node * node)170 eet_node_hash_new(const char *name,
171                   const char *key,
172                   Eet_Node   *node)
173 {
174    Eina_List *nodes;
175    Eet_Node *n;
176 
177    if (!node)
178      return NULL;
179 
180    n = _eet_node_new(name, EET_G_HASH);
181    if (!n)
182      return NULL;
183 
184    n->key = eina_stringshare_add(key);
185    nodes = eina_list_append(NULL, node);
186 
187    _eet_node_append(n, nodes);
188 
189    return n;
190 }
191 
192 Eet_Node *
eet_node_struct_new(const char * name,Eina_List * nodes)193 eet_node_struct_new(const char *name,
194                     Eina_List  *nodes)
195 {
196    Eet_Node *n;
197 
198    n = _eet_node_new(name, EET_G_UNKNOWN);
199    if (!n)
200      return NULL;
201 
202    _eet_node_append(n, nodes);
203 
204    return n;
205 }
206 
207 Eet_Node *
eet_node_struct_child_new(const char * parent,Eet_Node * child)208 eet_node_struct_child_new(const char *parent,
209                           Eet_Node   *child)
210 {
211    Eet_Node *n;
212 
213    if (!child) return NULL;
214 
215    if (child->type != EET_G_UNKNOWN)
216      return child;
217 
218    n = _eet_node_new(parent, EET_G_UNKNOWN);
219    if (!n)
220      return NULL;
221 
222    _eet_node_append(n, eina_list_prepend(NULL, child));
223 
224    return n;
225 }
226 
227 Eet_Node *
eet_node_children_get(Eet_Node * node)228 eet_node_children_get(Eet_Node *node)
229 {
230    if (!node) return NULL;
231    return node->values;
232 }
233 
234 Eet_Node *
eet_node_next_get(Eet_Node * node)235 eet_node_next_get(Eet_Node *node)
236 {
237    if (!node) return NULL;
238    return node->next;
239 }
240 
241 Eet_Node *
eet_node_parent_get(Eet_Node * node)242 eet_node_parent_get(Eet_Node *node)
243 {
244    if (!node) return NULL;
245    return node->parent;
246 }
247 
248 void
eet_node_list_append(Eet_Node * parent,const char * name,Eet_Node * child)249 eet_node_list_append(Eet_Node   *parent,
250                      const char *name,
251                      Eet_Node   *child)
252 {
253    const char *tmp;
254    Eet_Node *nn;
255 
256    if ((!parent) || (!child)) return;
257    tmp = eina_stringshare_add(name);
258 
259    for (nn = parent->values; nn; nn = nn->next)
260      if (nn->name == tmp && nn->type == EET_G_LIST)
261        {
262           Eet_Node *n;
263 
264           if (!nn->values)
265             nn->values = child;
266           else
267             {
268                for (n = nn->values; n->next; n = n->next)
269                  ;
270                n->next = child;
271             }
272 
273           child->next = NULL;
274           child->parent = parent;
275 
276           eina_stringshare_del(tmp);
277 
278           return;
279        }
280 
281    /* No list found, so create it. */
282    nn = eet_node_list_new(tmp, eina_list_append(NULL, child));
283 
284    /* And add it to the parent. */
285    nn->next = parent->values;
286    parent->values = nn;
287    child->parent = parent;
288 
289    eina_stringshare_del(tmp);
290 }
291 
292 void
eet_node_struct_append(Eet_Node * parent,const char * name,Eet_Node * child)293 eet_node_struct_append(Eet_Node   *parent,
294                        const char *name,
295                        Eet_Node   *child)
296 {
297    const char *tmp;
298    Eet_Node *prev;
299    Eet_Node *nn;
300 
301    if ((!parent) || (!child)) return;
302    if (parent->type != EET_G_UNKNOWN)
303      {
304         ERR("[%s] is not a structure. Will not insert [%s] in it",
305             parent->name,
306             name);
307         eet_node_del(child);
308         return;
309      }
310 
311    tmp = eina_stringshare_add(name);
312 
313    for (prev = NULL, nn = parent->values; nn; prev = nn, nn = nn->next)
314      if (nn->name == tmp && nn->type == child->type)
315        {
316           if (prev)
317             prev->next = nn->next;
318           else
319             parent->values = nn->next;
320 
321           nn->next = NULL;
322           eet_node_del(nn);
323 
324           break;
325        }
326 
327    if (prev)
328      {
329         prev->next = child;
330         child->next = NULL;
331      }
332    else
333      {
334         child->next = NULL;
335         parent->values = child;
336      }
337    child->parent = parent;
338 
339    eina_stringshare_del(tmp);
340 }
341 
342 void
eet_node_hash_add(Eet_Node * parent,const char * name,const char * key,Eet_Node * child)343 eet_node_hash_add(Eet_Node   *parent,
344                   const char *name,
345                   const char *key,
346                   Eet_Node   *child)
347 {
348    Eet_Node *nn;
349 
350    if ((!parent) || (!child)) return;
351 
352    /* No list found, so create it. */
353    nn = eet_node_hash_new(name, key, child);
354 
355    /* And add it to the parent. */
356    nn->next = parent->values;
357    parent->values = nn;
358    child->parent = parent;
359 }
360 
361 int
eet_node_type_get(Eet_Node * node)362 eet_node_type_get(Eet_Node *node)
363 {
364    if (!node) return EET_T_UNKNOW;
365    return node->type;
366 }
367 
368 Eet_Node_Data *
eet_node_value_get(Eet_Node * node)369 eet_node_value_get(Eet_Node *node)
370 {
371    if (!node) return NULL;
372    return &node->data;
373 }
374 
375 const char *
eet_node_name_get(Eet_Node * node)376 eet_node_name_get(Eet_Node *node)
377 {
378    if (!node) return NULL;
379    return node->name;
380 }
381 
382 void
eet_node_del(Eet_Node * n)383 eet_node_del(Eet_Node *n)
384 {
385    Eet_Node *nn;
386    Eet_Node *tmp;
387 
388    if (!n)
389      return;
390 
391    switch (n->type)
392      {
393       case EET_G_HASH:
394         eina_stringshare_del(n->key);
395         /* No break here as we want it to fall through and free the resources */
396         EINA_FALLTHROUGH;
397 
398       case EET_G_UNKNOWN:
399       case EET_G_VAR_ARRAY:
400       case EET_G_ARRAY:
401       case EET_G_LIST:
402         for (nn = n->values; nn; )
403           {
404              tmp = nn;
405              nn = nn->next;
406              eet_node_del(tmp);
407           }
408         break;
409 
410       case EET_T_STRING:
411       case EET_T_INLINED_STRING:
412         eina_stringshare_del(n->data.value.str);
413         break;
414 
415       case EET_T_CHAR:
416       case EET_T_SHORT:
417       case EET_T_INT:
418       case EET_T_LONG_LONG:
419       case EET_T_FLOAT:
420       case EET_T_DOUBLE:
421       case EET_T_UCHAR:
422       case EET_T_USHORT:
423       case EET_T_UINT:
424         break;
425      }
426 
427    eina_stringshare_del(n->name);
428    eet_node_free(n);
429 }
430 
431 static const char *eet_node_dump_g_name[6] = {
432    "struct",
433    "array",
434    "var_array",
435    "list",
436    "hash",
437    "???"
438 };
439 
440 static const char *eet_node_dump_t_name[14][2] = {
441    { "???: ", "???" },
442    { "char: ", "%hhi" },
443    { "short: ", "%hi" },
444    { "int: ", "%i" },
445    { "long_long: ", "%lli" },
446    { "float: ", "%1.25f" },
447    { "double: ", "%1.25f" },
448    { "uchar: ", "%hhu" },
449    { "ushort: ", "%i" },
450    { "uint: ", "%u" },
451    { "ulong_long: ", "%llu" },
452    { "null", "" }
453 };
454 
455 static void
eet_node_dump_level(int level,Eet_Dump_Callback dumpfunc,void * dumpdata)456 eet_node_dump_level(int               level,
457                     Eet_Dump_Callback dumpfunc,
458                     void             *dumpdata)
459 {
460    int i;
461 
462    for (i = 0; i < level; i++) dumpfunc(dumpdata, "  ");
463 }
464 
465 static char *
eet_node_string_escape(const char * str)466 eet_node_string_escape(const char *str)
467 {
468    char *s, *sp;
469    const char *strp;
470    int sz = 0;
471 
472    for (strp = str; *strp; strp++)
473      {
474         if (*strp == '\"')
475           sz += 2;
476         else if (*strp == '\\')
477           sz += 2;
478         else if (*strp == '\n')
479           sz += 2;
480         else
481           sz += 1;
482      }
483    s = malloc(sz + 1);
484    if (!s)
485      return NULL;
486 
487    for (strp = str, sp = s; *strp; strp++, sp++)
488      {
489         if (*strp == '\"'
490             || *strp == '\\'
491             || *strp == '\n')
492           {
493              *sp = '\\';
494              sp++;
495           }
496 
497         if (*strp == '\n')
498           *sp = 'n';
499         else
500           *sp = *strp;
501      }
502    *sp = 0;
503    return s;
504 }
505 
506 static void
eet_node_dump_string_escape(void * dumpdata,Eet_Dump_Callback dumpfunc,const char * str)507 eet_node_dump_string_escape(void             *dumpdata,
508                             Eet_Dump_Callback dumpfunc,
509                             const char       *str)
510 {
511    char *s;
512 
513    s = eet_node_string_escape(str);
514    if (!s)
515      return;
516 
517    dumpfunc(dumpdata, s);
518    free(s);
519 }
520 
521 static void
eet_node_dump_simple_type(Eet_Node * n,int level,Eet_Dump_Callback dumpfunc,void * dumpdata)522 eet_node_dump_simple_type(Eet_Node         *n,
523                           int               level,
524                           Eet_Dump_Callback dumpfunc,
525                           void             *dumpdata)
526 {
527    const char *type_name = NULL;
528    char tbuf[256];
529 
530    eet_node_dump_level(level, dumpfunc, dumpdata);
531    dumpfunc(dumpdata, "value \"");
532    eet_node_dump_string_escape(dumpdata, dumpfunc, n->name);
533    dumpfunc(dumpdata, "\" ");
534 
535 #ifdef EET_T_TYPE
536 # undef EET_T_TYPE
537 #endif /* ifdef EET_T_TYPE */
538 
539 #define EET_T_TYPE(Eet_Type, Type)                        \
540 case Eet_Type:                                            \
541 {                                                         \
542    dumpfunc(dumpdata, eet_node_dump_t_name[Eet_Type][0]); \
543    snprintf(tbuf,                                         \
544             sizeof (tbuf),                                \
545             eet_node_dump_t_name[Eet_Type][1],            \
546             n->data.value.Type);                          \
547    dumpfunc(dumpdata, tbuf);                              \
548    break;                                                 \
549 }
550 
551    switch (n->type)
552      {
553         EET_T_TYPE(EET_T_CHAR, c);
554         EET_T_TYPE(EET_T_SHORT, s);
555         EET_T_TYPE(EET_T_INT, i);
556         EET_T_TYPE(EET_T_LONG_LONG, l);
557         EET_T_TYPE(EET_T_FLOAT, f);
558         EET_T_TYPE(EET_T_DOUBLE, d);
559         EET_T_TYPE(EET_T_UCHAR, uc);
560         EET_T_TYPE(EET_T_USHORT, us);
561         EET_T_TYPE(EET_T_UINT, ui);
562         EET_T_TYPE(EET_T_ULONG_LONG, ul);
563 
564       case EET_T_INLINED_STRING:
565         type_name = "inlined: \"";
566         /* inlined string are just like a string, but not inside the general
567          * dictionnary. No need to duplicate code. */
568         EINA_FALLTHROUGH;
569 
570       case EET_T_STRING:
571         if (!type_name)
572           type_name = "string: \"";
573 
574         dumpfunc(dumpdata, type_name);
575         eet_node_dump_string_escape(dumpdata, dumpfunc, n->data.value.str);
576         dumpfunc(dumpdata, "\"");
577         break;
578 
579       case EET_T_NULL:
580         dumpfunc(dumpdata, "null");
581         break;
582 
583       default:
584         dumpfunc(dumpdata, "???: ???");
585         break;
586      }
587 
588    dumpfunc(dumpdata, ";\n");
589 }
590 
591 static void
eet_node_dump_group_start(int level,Eet_Dump_Callback dumpfunc,void * dumpdata,int group_type,const char * name)592 eet_node_dump_group_start(int               level,
593                           Eet_Dump_Callback dumpfunc,
594                           void             *dumpdata,
595                           int               group_type,
596                           const char       *name)
597 {
598    int chnk_type;
599 
600    chnk_type = (group_type >= EET_G_UNKNOWN && group_type <= EET_G_HASH) ?
601      group_type : EET_G_UNKNOWN;
602 
603    eet_node_dump_level(level, dumpfunc, dumpdata);
604    dumpfunc(dumpdata, "group \"");
605    eet_node_dump_string_escape(dumpdata, dumpfunc, name);
606    dumpfunc(dumpdata, "\" ");
607 
608    dumpfunc(dumpdata, eet_node_dump_g_name[chnk_type - EET_G_UNKNOWN]);
609    dumpfunc(dumpdata, " {\n");
610 }
611 
612 static void
eet_node_dump_group_end(int level,Eet_Dump_Callback dumpfunc,void * dumpdata)613 eet_node_dump_group_end(int               level,
614                         Eet_Dump_Callback dumpfunc,
615                         void             *dumpdata)
616 {
617    eet_node_dump_level(level, dumpfunc, dumpdata);
618    dumpfunc(dumpdata, "}\n");
619 }
620 
621 void
eet_node_dump(Eet_Node * n,int dumplevel,Eet_Dump_Callback dumpfunc,void * dumpdata)622 eet_node_dump(Eet_Node         *n,
623               int               dumplevel,
624               Eet_Dump_Callback dumpfunc,
625               void             *dumpdata)
626 {
627    Eet_Node *it;
628 
629    if (!n)
630      return;
631 
632    switch (n->type)
633      {
634       case EET_G_VAR_ARRAY:
635       case EET_G_ARRAY:
636       case EET_G_UNKNOWN:
637       case EET_G_HASH:
638       case EET_G_LIST:
639         eet_node_dump_group_start(dumplevel,
640                                   dumpfunc,
641                                   dumpdata,
642                                   n->type,
643                                   n->name);
644 
645         if (n->type == EET_G_VAR_ARRAY
646             || n->type == EET_G_ARRAY)
647           {
648              char tbuf[256];
649 
650              eet_node_dump_level(dumplevel, dumpfunc, dumpdata);
651              dumpfunc(dumpdata, "    count ");
652              eina_convert_itoa(n->count, tbuf);
653              dumpfunc(dumpdata, tbuf);
654              dumpfunc(dumpdata, ";\n");
655           }
656         else if (n->type == EET_G_HASH)
657           {
658              eet_node_dump_level(dumplevel, dumpfunc, dumpdata);
659              dumpfunc(dumpdata, "    key \"");
660              eet_node_dump_string_escape(dumpdata, dumpfunc, n->key);
661              dumpfunc(dumpdata, "\";\n");
662           }
663 
664         for (it = n->values; it; it = it->next)
665           eet_node_dump(it, dumplevel + 2, dumpfunc, dumpdata);
666 
667         eet_node_dump_group_end(dumplevel, dumpfunc, dumpdata);
668         break;
669 
670       case EET_T_STRING:
671       case EET_T_INLINED_STRING:
672       case EET_T_CHAR:
673       case EET_T_SHORT:
674       case EET_T_INT:
675       case EET_T_LONG_LONG:
676       case EET_T_FLOAT:
677       case EET_T_DOUBLE:
678       case EET_T_UCHAR:
679       case EET_T_USHORT:
680       case EET_T_UINT:
681       case EET_T_ULONG_LONG:
682         eet_node_dump_simple_type(n, dumplevel, dumpfunc, dumpdata);
683         break;
684      }
685 }
686 
687 void *
eet_node_walk(void * parent,const char * name,Eet_Node * root,Eet_Node_Walk * cb,void * user_data)688 eet_node_walk(void          *parent,
689               const char    *name,
690               Eet_Node      *root,
691               Eet_Node_Walk *cb,
692               void          *user_data)
693 {
694    Eet_Node *it;
695    void *me = NULL;
696    int i;
697 
698    if (!root)
699      {
700         if (parent)
701           cb->struct_add(parent, name, NULL, user_data);
702 
703         return NULL;
704      }
705 
706    switch (root->type)
707      {
708       case EET_G_UNKNOWN:
709         me = cb->struct_alloc(root->name, user_data);
710 
711         for (it = root->values; it; it = it->next)
712           eet_node_walk(me, it->name, it, cb, user_data);
713 
714         break;
715 
716       case EET_G_VAR_ARRAY:
717       case EET_G_ARRAY:
718         me = cb->array(root->type == EET_G_VAR_ARRAY ? EINA_TRUE : EINA_FALSE,
719                        root->name, root->count, user_data);
720 
721         for (i = 0, it = root->values; it; it = it->next)
722           cb->insert(me, i++, eet_node_walk(NULL,
723                                             NULL,
724                                             it,
725                                             cb,
726                                             user_data), user_data);
727 
728         break;
729 
730       case EET_G_LIST:
731         me = cb->list(root->name, user_data);
732 
733         for (it = root->values; it; it = it->next)
734           cb->append(me, eet_node_walk(NULL,
735                                        NULL,
736                                        it,
737                                        cb,
738                                        user_data), user_data);
739 
740         break;
741 
742       case EET_G_HASH:
743         if (!parent)
744           return NULL;
745 
746         return cb->hash(parent, root->name, root->key,
747                         eet_node_walk(NULL,
748                                       NULL,
749                                       root->values,
750                                       cb,
751                                       user_data), user_data);
752 
753       case EET_T_STRING:
754       case EET_T_INLINED_STRING:
755       case EET_T_CHAR:
756       case EET_T_SHORT:
757       case EET_T_INT:
758       case EET_T_LONG_LONG:
759       case EET_T_FLOAT:
760       case EET_T_DOUBLE:
761       case EET_T_UCHAR:
762       case EET_T_USHORT:
763       case EET_T_UINT:
764       case EET_T_ULONG_LONG:
765         me = cb->simple(root->type, &root->data, user_data);
766         break;
767      }
768 
769    if (parent)
770      cb->struct_add(parent, name, me, user_data);
771 
772    return me;
773 }
774 
775 int
eet_node_init(void)776 eet_node_init(void)
777 {
778    const char *choice;
779    const char *tmp;
780 
781 #ifdef EINA_DEFAULT_MEMPOOL
782    choice = "pass_through";
783 #else
784    choice = "chained_mempool";
785 #endif
786    tmp = getenv("EINA_MEMPOOL");
787    if (tmp && tmp[0])
788      choice = tmp;
789 
790    _eet_node_mp =
791      eina_mempool_add(choice, "eet-node-alloc", NULL, sizeof(Eet_Node), 32);
792 
793    return _eet_node_mp ? 1 : 0;
794 }
795 
796 void
eet_node_shutdown(void)797 eet_node_shutdown(void)
798 {
799    eina_mempool_del(_eet_node_mp);
800    _eet_node_mp = NULL;
801 }
802 
803