1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 
5 #include <ctype.h>
6 #include <Eina.h>
7 #include "eo_parser.h"
8 #include "eolian_database.h"
9 #include "eolian_priv.h"
10 
11 void
database_object_add(Eolian_Unit * unit,const Eolian_Object * obj)12 database_object_add(Eolian_Unit *unit, const Eolian_Object *obj)
13 {
14    /* object storage */
15    eina_hash_add(unit->objects, obj->name, obj);
16    eina_hash_add(unit->state->staging.unit.objects, obj->name, obj);
17    eina_hash_set(unit->state->staging.objects_f, obj->file, eina_list_append
18                  ((Eina_List *)eina_hash_find(unit->state->staging.objects_f, obj->file), obj));
19 }
20 
21 EAPI Eolian_Object_Type
eolian_object_type_get(const Eolian_Object * obj)22 eolian_object_type_get(const Eolian_Object *obj)
23 {
24    if (!obj) return EOLIAN_OBJECT_UNKNOWN;
25    return obj->type;
26 }
27 
28 EAPI const Eolian_Unit *
eolian_object_unit_get(const Eolian_Object * obj)29 eolian_object_unit_get(const Eolian_Object *obj)
30 {
31    if (!obj) return NULL;
32    return obj->unit;
33 }
34 
35 EAPI const char *
eolian_object_file_get(const Eolian_Object * obj)36 eolian_object_file_get(const Eolian_Object *obj)
37 {
38    if (!obj) return NULL;
39    return obj->file;
40 }
41 
42 EAPI int
eolian_object_line_get(const Eolian_Object * obj)43 eolian_object_line_get(const Eolian_Object *obj)
44 {
45    if (!obj) return 0;
46    return obj->line;
47 }
48 
49 EAPI int
eolian_object_column_get(const Eolian_Object * obj)50 eolian_object_column_get(const Eolian_Object *obj)
51 {
52    if (!obj) return 0;
53    return obj->column;
54 }
55 
56 EAPI const char *
eolian_object_name_get(const Eolian_Object * obj)57 eolian_object_name_get(const Eolian_Object *obj)
58 {
59    if (!obj) return NULL;
60    return obj->name;
61 }
62 
63 EAPI const char *
eolian_object_c_name_get(const Eolian_Object * obj)64 eolian_object_c_name_get(const Eolian_Object *obj)
65 {
66    if (!obj) return NULL;
67    return obj->c_name;
68 }
69 
70 typedef struct _Eolian_Namespace_List
71 {
72    Eina_Iterator itr;
73    char *curp;
74 } Eolian_Namespace_List;
75 
76 static Eina_Bool
_nmsp_iterator_next(Eolian_Namespace_List * it,void ** data)77 _nmsp_iterator_next(Eolian_Namespace_List *it, void **data)
78 {
79    if (!it || !it->curp)
80      return EINA_FALSE;
81 
82    char *ndot = strchr(it->curp, '.');
83    if (!ndot)
84      return EINA_FALSE;
85 
86    *ndot = '\0';
87    if (data) *data = it->curp;
88    it->curp = ndot + 1;
89    return EINA_TRUE;
90 }
91 
92 static void *
_nmsp_container_get(Eina_Iterator * it EINA_UNUSED)93 _nmsp_container_get(Eina_Iterator *it EINA_UNUSED)
94 {
95    return NULL;
96 }
97 
98 EAPI const char *
eolian_object_short_name_get(const Eolian_Object * obj)99 eolian_object_short_name_get(const Eolian_Object *obj)
100 {
101    if (!obj || !obj->name) return NULL;
102    const char *ldot = strrchr(obj->name, '.');
103    if (ldot)
104      return ldot + 1;
105    return obj->name;
106 }
107 
108 EAPI Eina_Iterator *
eolian_object_namespaces_get(const Eolian_Object * obj)109 eolian_object_namespaces_get(const Eolian_Object *obj)
110 {
111    if (!obj || !obj->name || !strchr(obj->name, '.')) return NULL;
112 
113    size_t nstrl = strlen(obj->name) + 1;
114    size_t anstrl = nstrl - 1 - (nstrl - 1) % sizeof(void *) + sizeof(void *);
115 
116    Eolian_Namespace_List *it = malloc(sizeof(Eolian_Namespace_List) + anstrl);
117    memset(&it->itr, 0, sizeof(Eina_Iterator));
118    it->curp = (char *)(it + 1);
119    memcpy(it->curp, obj->name, nstrl);
120 
121    EINA_MAGIC_SET(&it->itr, EINA_MAGIC_ITERATOR);
122    it->itr.version = EINA_ITERATOR_VERSION;
123    it->itr.next = FUNC_ITERATOR_NEXT(_nmsp_iterator_next);
124    it->itr.get_container = _nmsp_container_get;
125    it->itr.free = FUNC_ITERATOR_FREE(free);
126    return &it->itr;
127 }
128 
129 EAPI Eina_Bool
eolian_object_is_beta(const Eolian_Object * obj)130 eolian_object_is_beta(const Eolian_Object *obj)
131 {
132    if (!obj) return EINA_FALSE;
133    return obj->is_beta;
134 }
135 
database_doc_del(Eolian_Documentation * doc)136 void database_doc_del(Eolian_Documentation *doc)
137 {
138    if (!doc) return;
139    eina_stringshare_del(doc->summary);
140    eina_stringshare_del(doc->description);
141    eina_stringshare_del(doc->since);
142    eina_list_free(doc->ref_dbg);
143    free(doc);
144 }
145 
146 EAPI const char *
eolian_documentation_summary_get(const Eolian_Documentation * doc)147 eolian_documentation_summary_get(const Eolian_Documentation *doc)
148 {
149    EINA_SAFETY_ON_NULL_RETURN_VAL(doc, NULL);
150    return doc->summary;
151 }
152 
153 EAPI const char *
eolian_documentation_description_get(const Eolian_Documentation * doc)154 eolian_documentation_description_get(const Eolian_Documentation *doc)
155 {
156    EINA_SAFETY_ON_NULL_RETURN_VAL(doc, NULL);
157    return doc->description;
158 }
159 
160 EAPI const char *
eolian_documentation_since_get(const Eolian_Documentation * doc)161 eolian_documentation_since_get(const Eolian_Documentation *doc)
162 {
163    EINA_SAFETY_ON_NULL_RETURN_VAL(doc, NULL);
164    return doc->since;
165 }
166 
167 EAPI Eina_List *
eolian_documentation_string_split(const char * doc)168 eolian_documentation_string_split(const char *doc)
169 {
170    EINA_SAFETY_ON_NULL_RETURN_VAL(doc, NULL);
171    if (!doc[0])
172      return NULL;
173    const char *sep = strstr(doc, "\n\n");
174    Eina_List *ret = NULL;
175    for (;;)
176      {
177         Eina_Strbuf *buf = eina_strbuf_new();
178         if (sep)
179           eina_strbuf_append_length(buf, doc, sep - doc);
180         else
181           eina_strbuf_append(buf, doc);
182         eina_strbuf_trim(buf);
183         if (eina_strbuf_length_get(buf))
184           ret = eina_list_append(ret, eina_strbuf_string_steal(buf));
185         eina_strbuf_free(buf);
186         if (!sep)
187           break;
188         doc = sep + 2;
189         sep = strstr(doc, "\n\n");
190      }
191    return ret;
192 }
193 
194 static Eina_Bool
_skip_ref_word(const char ** doc)195 _skip_ref_word(const char **doc)
196 {
197    if (((*doc)[0] != '_') && !isalpha((*doc)[0]))
198      return EINA_FALSE;
199 
200    while (((*doc)[0] == '_') || isalnum((*doc)[0]))
201      ++*doc;
202 
203    return EINA_TRUE;
204 }
205 
206 /* this make sure the format is correct at least, it cannot verify the
207  * correctness of the reference itself (but Eolian will do it in its
208  * lexer, so there is nothing to worry about; all references are guaranteed
209  * to be right
210  */
211 static Eolian_Doc_Token_Type
_get_ref_token(const char * doc,const char ** doc_end)212 _get_ref_token(const char *doc, const char **doc_end)
213 {
214    /* not a ref at all, for convenience */
215    if (doc[0] != '@')
216      return EOLIAN_DOC_TOKEN_UNKNOWN;
217 
218    ++doc;
219 
220    Eina_Bool is_event = (doc[0] == '[');
221    if (is_event)
222      ++doc;
223 
224    if (_skip_ref_word(&doc))
225      {
226         while (doc[0] == '.')
227           {
228              ++doc;
229              if (!_skip_ref_word(&doc))
230                {
231                   --doc;
232                   break;
233                }
234           }
235         if (is_event) while (doc[0] == ',')
236           {
237              ++doc;
238              if (!_skip_ref_word(&doc))
239                {
240                   --doc;
241                   break;
242                }
243           }
244      }
245    else
246      return EOLIAN_DOC_TOKEN_UNKNOWN;
247 
248    if (is_event)
249      {
250         if (doc[0] != ']')
251           return EOLIAN_DOC_TOKEN_UNKNOWN;
252         ++doc;
253      }
254 
255    if (doc_end)
256      *doc_end = doc;
257 
258    /* got a reference */
259    return EOLIAN_DOC_TOKEN_REF;
260 }
261 
262 EAPI const char *
eolian_documentation_tokenize(const char * doc,Eolian_Doc_Token * ret)263 eolian_documentation_tokenize(const char *doc, Eolian_Doc_Token *ret)
264 {
265    /* token is used for statekeeping, so force it */
266    EINA_SAFETY_ON_NULL_RETURN_VAL(ret, NULL);
267 
268    /* we've reached the end or invalid input */
269    if (!doc || !doc[0])
270      {
271         ret->text = ret->text_end = NULL;
272         ret->type = EOLIAN_DOC_TOKEN_UNKNOWN;
273         return NULL;
274      }
275 
276    Eina_Bool cont = (ret->type != EOLIAN_DOC_TOKEN_UNKNOWN);
277 
278    /* we can only check notes etc at beginning of parsing */
279    if (cont)
280      goto mloop;
281 
282 #define CMP_MARK_NOTE(doc, note) !strncmp(doc, note ": ", sizeof(note) + 1)
283 
284    /* different types of notes */
285    if (CMP_MARK_NOTE(doc, "Note"))
286      {
287         ret->text = doc;
288         ret->text_end = doc + sizeof("Note:");
289         ret->type = EOLIAN_DOC_TOKEN_MARK_NOTE;
290         return ret->text_end;
291      }
292    else if (CMP_MARK_NOTE(doc, "Warning"))
293      {
294         ret->text = doc;
295         ret->text_end = doc + sizeof("Warning:");
296         ret->type = EOLIAN_DOC_TOKEN_MARK_WARNING;
297         return ret->text_end;
298      }
299    else if (CMP_MARK_NOTE(doc, "Remark"))
300      {
301         ret->text = doc;
302         ret->text_end = doc + sizeof("Remark:");
303         ret->type = EOLIAN_DOC_TOKEN_MARK_REMARK;
304         return ret->text_end;
305      }
306    else if (CMP_MARK_NOTE(doc, "TODO"))
307      {
308         ret->text = doc;
309         ret->text_end = doc + sizeof("TODO:");
310         ret->type = EOLIAN_DOC_TOKEN_MARK_TODO;
311         return ret->text_end;
312      }
313 
314 #undef CMP_MARK_NOTE
315 
316 mloop:
317    /* monospace markup ($foo) */
318    if ((doc[0] == '$') && ((doc[1] == '_') || isalpha(doc[1])))
319      {
320         ret->text = ++doc;
321         ret->text_end = ret->text;
322         while ((ret->text_end[0] == '_') || isalnum(ret->text_end[0]))
323           ++ret->text_end;
324         ret->type = EOLIAN_DOC_TOKEN_MARKUP_MONOSPACE;
325         return ret->text_end;
326      }
327 
328    /* complex monospace markup ($[...]) */
329    if ((doc[0] == '$') && (doc[1] == '['))
330      {
331         doc += 2;
332         ret->text = doc;
333         ret->text_end = ret->text;
334         while ((ret->text_end[0] != '\0') && (ret->text_end[0] != ']') &&
335                (ret->text_end[0] != '\n'))
336           {
337              /* escape: skip backslash */
338              if ((ret->text_end[0] == '\\') && (ret->text_end[1] != '\0') &&
339                  (ret->text_end[1] != '\n'))
340                ++ret->text_end;
341              ++ret->text_end;
342           }
343         ret->type = EOLIAN_DOC_TOKEN_MARKUP_MONOSPACE;
344         /* return past the ending bracket as that's markup syntax */
345         if (ret->text_end[0] == ']')
346           return ret->text_end + 1;
347         return ret->text_end;
348      }
349 
350    /* references */
351    Eolian_Doc_Token_Type rtp = _get_ref_token(doc, &ret->text_end);
352    if (rtp != EOLIAN_DOC_TOKEN_UNKNOWN)
353      {
354         ret->text = doc + 1;
355         ret->type = rtp;
356         return ret->text_end;
357      }
358 
359    const char *schr = doc, *pschr = NULL;
360    /* keep finding potential tokens until a suitable one is found
361     * terminate text token there (it also means next token can directly
362     * be tested for event/monospace)
363     */
364    while ((schr = strpbrk(schr, "@$")))
365      {
366         /* escape sequences */
367         if ((schr != doc) && (schr[-1] == '\\'))
368           {
369              schr += 1;
370              continue;
371           }
372         /* monospace markup */
373         if ((schr[0] == '$') && (
374             (schr[1] == '_') || (schr[1] == '[') || isalpha(schr[1])))
375           {
376              pschr = schr;
377              break;
378           }
379         /* references */
380         if (_get_ref_token(schr, NULL) != EOLIAN_DOC_TOKEN_UNKNOWN)
381           {
382              pschr = schr;
383              break;
384           }
385         /* nothing, keep matching text from next char on */
386         schr += 1;
387      }
388 
389    /* figure out where we actually end */
390    ret->text = doc;
391    ret->text_end = pschr ? pschr : (doc + strlen(doc));
392    ret->type = EOLIAN_DOC_TOKEN_TEXT;
393    return ret->text_end;
394 }
395 
396 EAPI void
eolian_doc_token_init(Eolian_Doc_Token * tok)397 eolian_doc_token_init(Eolian_Doc_Token *tok)
398 {
399    if (!tok)
400      return;
401    tok->type = EOLIAN_DOC_TOKEN_UNKNOWN;
402    tok->text = tok->text_end = NULL;
403 }
404 
405 EAPI Eolian_Doc_Token_Type
eolian_doc_token_type_get(const Eolian_Doc_Token * tok)406 eolian_doc_token_type_get(const Eolian_Doc_Token *tok)
407 {
408    EINA_SAFETY_ON_NULL_RETURN_VAL(tok, EOLIAN_DOC_TOKEN_UNKNOWN);
409    return tok->type;
410 }
411 
412 EAPI char *
eolian_doc_token_text_get(const Eolian_Doc_Token * tok)413 eolian_doc_token_text_get(const Eolian_Doc_Token *tok)
414 {
415    EINA_SAFETY_ON_NULL_RETURN_VAL(tok, NULL);
416    if (tok->type == EOLIAN_DOC_TOKEN_UNKNOWN)
417      return NULL;
418    Eina_Strbuf *buf = eina_strbuf_new();
419    for (const char *p = tok->text; p != tok->text_end; ++p)
420      {
421         if (*p == '\\') ++p;
422         if (p != tok->text_end)
423           eina_strbuf_append_char(buf, *p);
424      }
425    char *ptr = eina_strbuf_string_steal(buf);
426    eina_strbuf_free(buf);
427    return ptr;
428 }
429 
430 static Eolian_Object_Type
_resolve_event(char * name,const Eolian_Unit * unit1,const Eolian_Unit * unit2,const Eolian_Object ** data1,const Eolian_Object ** data2)431 _resolve_event(char *name, const Eolian_Unit *unit1, const Eolian_Unit *unit2,
432                const Eolian_Object **data1, const Eolian_Object **data2)
433 {
434    /* never trust the user */
435    if (name[0] == ',')
436      return EOLIAN_OBJECT_UNKNOWN;
437 
438    char *evname = strrchr(name, '.');
439    if (!evname)
440      return EOLIAN_OBJECT_UNKNOWN;
441 
442    *evname++ = '\0';
443    const Eolian_Class *cl = eolian_unit_class_by_name_get(unit1, name);
444    if (!cl && unit2)
445      cl = eolian_unit_class_by_name_get(unit2, name);
446    if (!cl)
447      return EOLIAN_OBJECT_UNKNOWN;
448 
449    const Eolian_Event *ev = eolian_class_event_by_name_get(cl, evname);
450    if (!ev)
451      return EOLIAN_OBJECT_UNKNOWN;
452 
453    if (data1) *data1 = &cl->base;
454    if (data2) *data2 = &ev->base;
455    return EOLIAN_OBJECT_EVENT;
456 }
457 
458 Eolian_Object_Type
database_doc_token_ref_resolve(const Eolian_Doc_Token * tok,const Eolian_Unit * unit1,const Eolian_Unit * unit2,const Eolian_Object ** data1,const Eolian_Object ** data2)459 database_doc_token_ref_resolve(const Eolian_Doc_Token *tok,
460                                const Eolian_Unit *unit1, const Eolian_Unit *unit2,
461                                const Eolian_Object **data1, const Eolian_Object **data2)
462 {
463    if (tok->type != EOLIAN_DOC_TOKEN_REF)
464      return EOLIAN_OBJECT_UNKNOWN;
465 
466    size_t nlen = tok->text_end - tok->text;
467 
468    /* events are handled separately */
469    if (tok->text[0] == '[')
470      {
471         /* strip brackets */
472         size_t elen = nlen - 2;
473         char *ename = alloca(elen + 1);
474         memcpy(ename, tok->text + 1, elen);
475         ename[elen] = '\0';
476         return _resolve_event(ename, unit1, unit2, data1, data2);
477      }
478 
479    char *name = alloca(nlen + 1);
480    memcpy(name, tok->text, nlen);
481    name[nlen] = '\0';
482 
483    const Eolian_Object *decl = eolian_unit_object_by_name_get(unit1, name);
484    if (!decl && unit2)
485      decl = eolian_unit_object_by_name_get(unit2, name);
486    if (decl)
487      {
488        if (data1) *data1 = decl;
489        Eolian_Object_Type tp = eolian_object_type_get(decl);
490        switch (tp)
491          {
492           case EOLIAN_OBJECT_CLASS:
493           case EOLIAN_OBJECT_TYPEDECL:
494           case EOLIAN_OBJECT_CONSTANT:
495           case EOLIAN_OBJECT_ERROR:
496             /* we only allow certain types to be referenced */
497             return tp;
498           default:
499             return EOLIAN_OBJECT_UNKNOWN;
500          }
501      }
502 
503    /* from here it can only be a function, a struct field or an enum field */
504 
505    char *suffix = strrchr(name, '.');
506    /* no suffix, therefore invalid */
507    if (!suffix)
508      return EOLIAN_OBJECT_UNKNOWN;
509 
510    /* name will terminate before suffix, suffix will be standalone */
511    *suffix++ = '\0';
512 
513    /* try a struct field */
514    const Eolian_Typedecl *tpd = eolian_unit_struct_by_name_get(unit1, name);
515    if (!tpd && unit2)
516      tpd = eolian_unit_struct_by_name_get(unit2, name);
517    if (tpd)
518      {
519         const Eolian_Struct_Type_Field *fld = eolian_typedecl_struct_field_get(tpd, suffix);
520         /* field itself is invalid */
521         if (!fld)
522           return EOLIAN_OBJECT_UNKNOWN;
523         if (data1) *data1 = &tpd->base;
524         if (data2) *data2 = &fld->base;
525         return EOLIAN_OBJECT_STRUCT_FIELD;
526      }
527 
528    /* try an enum field */
529    tpd = eolian_unit_enum_by_name_get(unit1, name);
530    if (!tpd && unit2)
531      tpd = eolian_unit_enum_by_name_get(unit2, name);
532    if (tpd)
533      {
534         const Eolian_Enum_Type_Field *fld = eolian_typedecl_enum_field_get(tpd, suffix);
535         /* field itself is invalid */
536         if (!fld)
537           return EOLIAN_OBJECT_UNKNOWN;
538         if (data1) *data1 = &tpd->base;
539         if (data2) *data2 = &fld->base;
540         return EOLIAN_OBJECT_ENUM_FIELD;
541      }
542 
543    /* now it can only be a function or invalid */
544 
545    Eolian_Function_Type ftype = EOLIAN_UNRESOLVED;
546    if (!strcmp(suffix, "get"))
547      ftype = EOLIAN_PROP_GET;
548    else if (!strcmp(suffix, "set"))
549      ftype = EOLIAN_PROP_SET;
550 
551    if (ftype != EOLIAN_UNRESOLVED)
552      {
553         suffix = strrchr(name, '.');
554         /* wrong suffix, therefore invalid */
555         if (!suffix)
556           return EOLIAN_OBJECT_UNKNOWN;
557         /* re-terminate */
558         *suffix++ = '\0';
559      }
560 
561    const Eolian_Class *cl = eolian_unit_class_by_name_get(unit1, name);
562    if (!cl && unit2)
563      cl = eolian_unit_class_by_name_get(unit2, name);
564    if (!cl)
565      return EOLIAN_OBJECT_UNKNOWN;
566 
567    const Eolian_Function *fid = eolian_class_function_by_name_get(cl, suffix, ftype);
568    if (!fid)
569      return EOLIAN_OBJECT_UNKNOWN;
570 
571    /* got a func */
572    if (data1) *data1 = &cl->base;
573    if (data2) *data2 = &fid->base;
574    return EOLIAN_OBJECT_FUNCTION;
575 }
576 
577 EAPI Eolian_Object_Type
eolian_doc_token_ref_resolve(const Eolian_Doc_Token * tok,const Eolian_State * state,const Eolian_Object ** data,const Eolian_Object ** data2)578 eolian_doc_token_ref_resolve(const Eolian_Doc_Token *tok, const Eolian_State *state,
579                              const Eolian_Object **data, const Eolian_Object **data2)
580 {
581    return database_doc_token_ref_resolve(tok, (const Eolian_Unit *)state, NULL, data, data2);
582 }
583 
584 void
database_unit_init(Eolian_State * state,Eolian_Unit * unit,const char * file)585 database_unit_init(Eolian_State *state, Eolian_Unit *unit, const char *file)
586 {
587    unit->file  = eina_stringshare_ref(file);
588    unit->state = state;
589 
590    unit->children   = eina_hash_stringshared_new(NULL);
591    unit->classes    = eina_hash_stringshared_new(EINA_FREE_CB(database_class_del));
592    unit->constants  = eina_hash_stringshared_new(EINA_FREE_CB(database_constant_del));
593    unit->errors     = eina_hash_stringshared_new(EINA_FREE_CB(database_error_del));
594    unit->aliases    = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
595    unit->structs    = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
596    unit->enums      = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
597    unit->objects    = eina_hash_stringshared_new(NULL);
598 
599    /* baseline version; support for higher featurelevel must be specified explicitly */
600    unit->version    = 1;
601 }
602 
603 static void
_unit_contents_del(Eolian_Unit * unit)604 _unit_contents_del(Eolian_Unit *unit)
605 {
606    eina_stringshare_del(unit->file);
607    eina_hash_free(unit->children);
608    eina_hash_free(unit->classes);
609    eina_hash_free(unit->constants);
610    eina_hash_free(unit->errors);
611    eina_hash_free(unit->aliases);
612    eina_hash_free(unit->structs);
613    eina_hash_free(unit->enums);
614    eina_hash_free(unit->objects);
615 }
616 
617 void
database_unit_del(Eolian_Unit * unit)618 database_unit_del(Eolian_Unit *unit)
619 {
620    if (!unit)
621      return;
622 
623    _unit_contents_del(unit);
624    free(unit);
625 }
626 
627 static Eina_Bool
_hashlist_free_cb(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata EINA_UNUSED)628 _hashlist_free_cb(const Eina_Hash *hash EINA_UNUSED,
629                   const void *key EINA_UNUSED,
630                   void *data, void *fdata EINA_UNUSED)
631 {
632    eina_list_free((Eina_List *)data);
633    return EINA_TRUE;
634 }
635 
636 static void
_hashlist_free(Eina_Hash * h)637 _hashlist_free(Eina_Hash *h)
638 {
639    eina_hash_foreach(h, _hashlist_free_cb, NULL);
640    eina_hash_free(h);
641 }
642 
643 static void
_hashlist_free_buckets(Eina_Hash * h)644 _hashlist_free_buckets(Eina_Hash *h)
645 {
646    eina_hash_foreach(h, _hashlist_free_cb, NULL);
647    eina_hash_free_buckets(h);
648 }
649 
650 static void
_state_area_init(Eolian_State * state,Eolian_State_Area * a)651 _state_area_init(Eolian_State *state, Eolian_State_Area *a)
652 {
653    database_unit_init(state, &a->unit, NULL);
654 
655    a->units = eina_hash_stringshared_new(NULL);
656 
657    a->classes_f   = eina_hash_stringshared_new(NULL);
658    a->aliases_f   = eina_hash_stringshared_new(NULL);
659    a->structs_f   = eina_hash_stringshared_new(NULL);
660    a->enums_f     = eina_hash_stringshared_new(NULL);
661    a->constants_f = eina_hash_stringshared_new(NULL);
662    a->errors_f    = eina_hash_stringshared_new(NULL);
663    a->objects_f   = eina_hash_stringshared_new(NULL);
664 }
665 
666 static Eina_Bool
_ulist_free_cb(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata EINA_UNUSED)667 _ulist_free_cb(const Eina_Hash *hash EINA_UNUSED,
668                const void *key EINA_UNUSED,
669                void *data, void *fdata EINA_UNUSED)
670 {
671    database_unit_del((Eolian_Unit *)data);
672    return EINA_TRUE;
673 }
674 
675 static void
_state_area_contents_del(Eolian_State_Area * a)676 _state_area_contents_del(Eolian_State_Area *a)
677 {
678    _unit_contents_del(&a->unit);
679 
680    eina_hash_foreach(a->units, _ulist_free_cb, NULL);
681    eina_hash_free(a->units);
682 
683    eina_hash_free(a->classes_f);
684    _hashlist_free(a->aliases_f);
685    _hashlist_free(a->structs_f);
686    _hashlist_free(a->enums_f);
687    _hashlist_free(a->constants_f);
688    _hashlist_free(a->errors_f);
689    _hashlist_free(a->objects_f);
690 }
691 
692 static void
_default_panic_cb(const Eolian_State * state EINA_UNUSED,const char * msg)693 _default_panic_cb(const Eolian_State *state EINA_UNUSED, const char *msg)
694 {
695    _eolian_log(msg);
696 }
697 
698 static void
_default_error_cb(const Eolian_Object * obj,const char * msg,void * data EINA_UNUSED)699 _default_error_cb(const Eolian_Object *obj, const char *msg, void *data EINA_UNUSED)
700 {
701    if (obj)
702      _eolian_log_line(obj->file, obj->line, obj->column, msg);
703    else
704      _eolian_log(msg);
705 }
706 
707 EAPI Eolian_State *
eolian_state_new(void)708 eolian_state_new(void)
709 {
710    Eolian_State *state = calloc(1, sizeof(Eolian_State));
711    if (!state)
712      return NULL;
713 
714    state->panic = _default_panic_cb;
715 
716    if (setjmp(state->jmp_env))
717      {
718         state->panic(state, state->panic_msg);
719         eina_stringshare_del(state->panic_msg);
720         exit(EXIT_FAILURE);
721      }
722 
723    state->error = _default_error_cb;
724 
725    _state_area_init(state, &state->main);
726    _state_area_init(state, &state->staging);
727 
728    state->filenames_eo  = eina_hash_string_small_new(free);
729    state->filenames_eot = eina_hash_string_small_new(free);
730 
731    state->defer = eina_hash_string_small_new(NULL);
732 
733    return state;
734 }
735 
736 EAPI void
eolian_state_free(Eolian_State * state)737 eolian_state_free(Eolian_State *state)
738 {
739    if (!state)
740      return;
741 
742    _state_area_contents_del(&state->main);
743    _state_area_contents_del(&state->staging);
744 
745    eina_hash_free(state->filenames_eo);
746    eina_hash_free(state->filenames_eot);
747 
748    eina_hash_free(state->defer);
749 
750    free(state);
751 }
752 
753 EAPI Eolian_Panic_Cb
eolian_state_panic_cb_set(Eolian_State * state,Eolian_Panic_Cb cb)754 eolian_state_panic_cb_set(Eolian_State *state, Eolian_Panic_Cb cb)
755 {
756    Eolian_Panic_Cb old_cb = state->panic;
757    state->panic = cb ? cb : _default_panic_cb;
758    return old_cb;
759 }
760 
761 EAPI Eolian_Error_Cb
eolian_state_error_cb_set(Eolian_State * state,Eolian_Error_Cb cb)762 eolian_state_error_cb_set(Eolian_State *state, Eolian_Error_Cb cb)
763 {
764    Eolian_Error_Cb old_cb = state->error;
765    state->error = cb ? cb : _default_error_cb;
766    return old_cb;
767 }
768 
769 EAPI void *
eolian_state_error_data_set(Eolian_State * state,void * data)770 eolian_state_error_data_set(Eolian_State *state, void *data)
771 {
772    void *old_data = state->error_data;
773    state->error_data = data;
774    return old_data;
775 }
776 
777 #define EO_SUFFIX ".eo"
778 #define EOT_SUFFIX ".eot"
779 
780 static char *
join_path(const char * path,const char * file)781 join_path(const char *path, const char *file)
782 {
783    Eina_Strbuf *buf = eina_strbuf_new();
784    char *ret;
785 
786    eina_strbuf_append(buf, path);
787    eina_strbuf_append_char(buf, '/');
788    eina_strbuf_append(buf, file);
789 
790    ret = eina_file_path_sanitize(eina_strbuf_string_get(buf));
791    eina_strbuf_free(buf);
792    return ret;
793 }
794 
795 typedef struct _Scan_State
796 {
797    Eolian_State *eos;
798    Eina_Bool succ;
799 } Scan_State;
800 
801 static void
_scan_cb(const char * name,const char * path,void * data)802 _scan_cb(const char *name, const char *path, void *data)
803 {
804    Scan_State *sst = data;
805    Eina_Bool is_eo = eina_str_has_suffix(name, EO_SUFFIX);
806    if (!is_eo && !eina_str_has_suffix(name, EOT_SUFFIX)) return;
807    Eina_Hash *fh = is_eo ? sst->eos->filenames_eo : sst->eos->filenames_eot;
808    const char *origpath = eina_hash_find(fh, name);
809    char *newpath = join_path(path, name);
810    if (origpath)
811      {
812         if (strcmp(origpath, newpath))
813           {
814             eolian_state_log(sst->eos,
815                              "conflicting paths for '%s': '%s' -> '%s'",
816                              name, origpath, newpath);
817             sst->succ = EINA_FALSE;
818           }
819         free(newpath);
820      }
821    else
822      eina_hash_add(fh, name, newpath);
823 }
824 
825 EAPI Eina_Bool
eolian_state_directory_add(Eolian_State * state,const char * dir)826 eolian_state_directory_add(Eolian_State *state, const char *dir)
827 {
828    if (!dir || !state) return EINA_FALSE;
829    Scan_State sst = { state, EINA_TRUE };
830    return eina_file_dir_list(dir, EINA_TRUE, _scan_cb, &sst) && sst.succ;
831 }
832 
833 EAPI Eina_Bool
eolian_state_system_directory_add(Eolian_State * state)834 eolian_state_system_directory_add(Eolian_State *state)
835 {
836    Eina_Bool ret;
837    Eina_Strbuf *buf = eina_strbuf_new();
838    eina_strbuf_append(buf, eina_prefix_data_get(_eolian_prefix));
839    eina_strbuf_append(buf, "/include");
840    ret = eolian_state_directory_add(state, eina_strbuf_string_get(buf));
841    eina_strbuf_free(buf);
842    return ret;
843 }
844 
845 EAPI Eina_Iterator *
eolian_state_eot_files_get(const Eolian_State * state)846 eolian_state_eot_files_get(const Eolian_State *state)
847 {
848    if (!state) return NULL;
849    return eina_hash_iterator_key_new(state->filenames_eot);
850 }
851 
852 EAPI Eina_Iterator *
eolian_state_eo_files_get(const Eolian_State * state)853 eolian_state_eo_files_get(const Eolian_State *state)
854 {
855    if (!state) return NULL;
856    return eina_hash_iterator_key_new(state->filenames_eo);
857 }
858 
859 EAPI Eina_Iterator *
eolian_state_eot_file_paths_get(const Eolian_State * state)860 eolian_state_eot_file_paths_get(const Eolian_State *state)
861 {
862    if (!state) return NULL;
863    return eina_hash_iterator_data_new(state->filenames_eot);
864 }
865 
866 EAPI Eina_Iterator *
eolian_state_eo_file_paths_get(const Eolian_State * state)867 eolian_state_eo_file_paths_get(const Eolian_State *state)
868 {
869    if (!state) return NULL;
870    return eina_hash_iterator_data_new(state->filenames_eo);
871 }
872 
873 static Eolian_Unit *
_eolian_file_parse_nodep(Eolian_Unit * parent,const char * filename)874 _eolian_file_parse_nodep(Eolian_Unit *parent, const char *filename)
875 {
876    Eina_Bool is_eo;
877    is_eo = eina_str_has_suffix(filename, EO_SUFFIX);
878    if (!is_eo && !eina_str_has_suffix(filename, EOT_SUFFIX))
879      {
880         eolian_state_log(parent->state,
881                          "file '%s' doesn't have a correct extension",
882                          filename);
883         return NULL;
884      }
885    const char *eopath = eina_hash_find(is_eo ? parent->state->filenames_eo : parent->state->filenames_eot, filename);
886    if (!eopath)
887      {
888         eolian_state_log(parent->state,
889                          "file '%s' is not registered in the database",
890                          filename);
891         return NULL;
892      }
893    return eo_parser_database_fill(parent, eopath, !is_eo);
894 }
895 
896 static void
_state_clean(Eolian_State * state)897 _state_clean(Eolian_State *state)
898 {
899    eina_hash_free_buckets(state->defer);
900 
901    Eolian_State_Area *st = &state->staging;
902 
903    Eolian_Unit *stu = &st->unit;
904    eina_hash_free_buckets(stu->classes);
905    eina_hash_free_buckets(stu->constants);
906    eina_hash_free_buckets(stu->aliases);
907    eina_hash_free_buckets(stu->structs);
908    eina_hash_free_buckets(stu->enums);
909    eina_hash_free_buckets(stu->objects);
910 
911    eina_hash_foreach(st->units, _ulist_free_cb, NULL);
912    eina_hash_free_buckets(st->units);
913 
914    eina_hash_free_buckets(st->classes_f);
915 
916    _hashlist_free_buckets(st->aliases_f);
917    _hashlist_free_buckets(st->structs_f);
918    _hashlist_free_buckets(st->enums_f);
919    _hashlist_free_buckets(st->constants_f);
920    _hashlist_free_buckets(st->objects_f);
921 }
922 
923 void
database_defer(Eolian_State * state,const char * fname,Eina_Bool isdep)924 database_defer(Eolian_State *state, const char *fname, Eina_Bool isdep)
925 {
926    void *nval = (void *)((size_t)isdep + 1);
927    size_t found = (size_t)eina_hash_find(state->defer, fname);
928    /* add if not found or upgrade to dep if requested */
929    if (!found)
930      eina_hash_add(state->defer, fname, nval);
931    else if ((found <= 1) && isdep)
932      eina_hash_set(state->defer, fname, nval);
933 }
934 
935 static Eina_Bool _parse_deferred(Eolian_Unit *parent);
936 static void _merge_units(Eolian_Unit *unit);
937 
938 typedef struct _Defer_Data
939 {
940    Eolian_Unit *parent;
941    Eina_Bool succ;
942 } Defer_Data;
943 
944 static Eina_Bool
_defer_hash_cb(const Eina_Hash * hash EINA_UNUSED,const void * key,void * data,void * fdata)945 _defer_hash_cb(const Eina_Hash *hash EINA_UNUSED, const void *key,
946                void *data, void *fdata)
947 {
948    Defer_Data *d = fdata;
949    Eina_Bool alone = ((size_t)data <= 1);
950    Eolian_Unit *parent = d->parent;
951    /* not a dependency; parse standalone */
952    if (alone)
953      parent = &parent->state->staging.unit;
954    Eolian_Unit *pdep = _eolian_file_parse_nodep(parent, key);
955    d->succ = (pdep && _parse_deferred(pdep));
956    /* standalone-parsed stuff forms its own dependency trees,
957     * so we have to merge the units manually and separately
958     */
959    if (d->succ && alone)
960      _merge_units(pdep);
961    return d->succ;
962 }
963 
964 static Eina_Bool
_parse_deferred(Eolian_Unit * parent)965 _parse_deferred(Eolian_Unit *parent)
966 {
967    Eina_Hash *defer = parent->state->defer;
968    if (!eina_hash_population(defer))
969      return EINA_TRUE;
970    /* clean room for more deps for later parsing */
971    parent->state->defer = eina_hash_string_small_new(NULL);
972    Defer_Data d = { parent, EINA_FALSE };
973    eina_hash_foreach(defer, _defer_hash_cb, &d);
974    if (!d.succ)
975      eina_hash_free_buckets(parent->state->defer);
976    eina_hash_free(defer);
977    return d.succ;
978 }
979 
980 static Eina_Bool
_merge_unit_cb(const Eina_Hash * hash EINA_UNUSED,const void * key,void * data,void * fdata)981 _merge_unit_cb(const Eina_Hash *hash EINA_UNUSED,
982                const void *key, void *data, void *fdata)
983 {
984    Eina_Hash *dest = fdata;
985    if (!eina_hash_find(dest, key))
986      {
987         eina_hash_add(dest, key, data);
988         eolian_object_ref((Eolian_Object *)data);
989      }
990    return EINA_TRUE;
991 }
992 
993 static Eina_Bool
_merge_unit_cb_noref(const Eina_Hash * hash EINA_UNUSED,const void * key,void * data,void * fdata)994 _merge_unit_cb_noref(const Eina_Hash *hash EINA_UNUSED,
995                const void *key, void *data, void *fdata)
996 {
997    Eina_Hash *dest = fdata;
998    if (!eina_hash_find(dest, key))
999      eina_hash_add(dest, key, data);
1000    return EINA_TRUE;
1001 }
1002 
1003 static void
_merge_unit(Eolian_Unit * dest,Eolian_Unit * src)1004 _merge_unit(Eolian_Unit *dest, Eolian_Unit *src)
1005 {
1006    eina_hash_foreach(src->classes, _merge_unit_cb, dest->classes);
1007    eina_hash_foreach(src->constants, _merge_unit_cb, dest->constants);
1008    eina_hash_foreach(src->aliases, _merge_unit_cb, dest->aliases);
1009    eina_hash_foreach(src->structs, _merge_unit_cb, dest->structs);
1010    eina_hash_foreach(src->enums, _merge_unit_cb, dest->enums);
1011    eina_hash_foreach(src->objects, _merge_unit_cb_noref, dest->objects);
1012 }
1013 
1014 typedef struct _Merge_Data
1015 {
1016    Eina_Hash *cycles;
1017    Eolian_Unit *unit;
1018 } Merge_Data;
1019 
1020 static Eina_Bool
_merge_units_cb(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata)1021 _merge_units_cb(const Eina_Hash *hash EINA_UNUSED,
1022                 const void *key EINA_UNUSED, void *data, void *fdata)
1023 {
1024    Merge_Data *mdata = fdata;
1025    Merge_Data imdata = { mdata->cycles, data };
1026    if (!eina_hash_find(imdata.cycles, &imdata.unit))
1027      {
1028         eina_hash_add(imdata.cycles, &imdata.unit, imdata.unit);
1029         eina_hash_foreach(imdata.unit->children, _merge_units_cb, &imdata);
1030      }
1031    _merge_unit(mdata->unit, imdata.unit);
1032    return EINA_TRUE;
1033 }
1034 
1035 
1036 static void
_merge_units(Eolian_Unit * unit)1037 _merge_units(Eolian_Unit *unit)
1038 {
1039    Merge_Data mdata = { eina_hash_pointer_new(NULL), unit };
1040    eina_hash_foreach(unit->children, _merge_units_cb, &mdata);
1041    eina_hash_free(mdata.cycles);
1042 }
1043 
1044 static Eina_Bool
_merge_staging_cb(const Eina_Hash * hash EINA_UNUSED,const void * key,void * data,void * fdata)1045 _merge_staging_cb(const Eina_Hash *hash EINA_UNUSED,
1046                   const void *key, void *data, void *fdata)
1047 {
1048    eina_hash_add((Eina_Hash *)fdata, key, data);
1049    return EINA_TRUE;
1050 }
1051 
1052 static void
_merge_staging(Eolian_State * state)1053 _merge_staging(Eolian_State *state)
1054 {
1055    Eolian_State_Area *amain = &state->main, *staging = &state->staging;
1056    _merge_unit(&amain->unit, &staging->unit);
1057 
1058    eina_hash_foreach(staging->units, _merge_staging_cb, amain->units);
1059    eina_hash_free_buckets(staging->units);
1060 
1061 #define EOLIAN_STAGING_MERGE_LIST(name) \
1062    eina_hash_foreach(staging->name##_f, _merge_staging_cb, amain->name##_f); \
1063    eina_hash_free_buckets(staging->name##_f);
1064 
1065    EOLIAN_STAGING_MERGE_LIST(classes);
1066    EOLIAN_STAGING_MERGE_LIST(aliases);
1067    EOLIAN_STAGING_MERGE_LIST(structs);
1068    EOLIAN_STAGING_MERGE_LIST(enums);
1069    EOLIAN_STAGING_MERGE_LIST(constants);
1070    EOLIAN_STAGING_MERGE_LIST(objects);
1071 
1072 #undef EOLIAN_STAGING_MERGE_LIST
1073 
1074    _state_clean(state);
1075 }
1076 
1077 EAPI const Eolian_Unit *
eolian_state_file_parse(Eolian_State * state,const char * filename)1078 eolian_state_file_parse(Eolian_State *state, const char *filename)
1079 {
1080    if (!state)
1081      return NULL;
1082 
1083    _state_clean(state);
1084    Eolian_Unit *ret = _eolian_file_parse_nodep(&state->staging.unit, filename);
1085    if (!ret)
1086      return NULL;
1087    if (!_parse_deferred(ret))
1088      return NULL;
1089    _merge_units(ret);
1090    if (!database_validate(&state->staging.unit))
1091      return NULL;
1092    _merge_staging(state);
1093    return ret;
1094 }
1095 
1096 EAPI const Eolian_Unit *
eolian_state_file_path_parse(Eolian_State * state,const char * filepath)1097 eolian_state_file_path_parse(Eolian_State *state, const char *filepath)
1098 {
1099    const Eolian_Unit *unit;
1100    if (!state)
1101      return NULL;
1102 
1103    char *mpath = strdup(filepath);
1104    if (!mpath)
1105      return NULL;
1106 
1107    char *fname = strrchr(mpath, '/');
1108    if (fname && strrchr(fname, '\\'))
1109      fname = strrchr(fname, '\\');
1110 
1111    const char *toscan = mpath;
1112    if (!fname)
1113      {
1114         toscan = ".";
1115         fname = mpath;
1116      }
1117    else
1118      *fname++ = '\0';
1119 
1120    if (!eolian_state_directory_add(state, toscan))
1121      {
1122         eolian_state_log(state, "could not scan directory '%s'", toscan);
1123         free(mpath);
1124         return NULL;
1125      }
1126    unit = eolian_state_file_parse(state, fname);
1127    free(mpath);
1128    return unit;
1129 }
1130 
1131 typedef struct _Parse_Data
1132 {
1133    Eolian_State *state;
1134    Eina_Bool ret;
1135 } Parse_Data;
1136 
_tfile_parse(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata)1137 static Eina_Bool _tfile_parse(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata)
1138 {
1139    Parse_Data *pd = fdata;
1140    Eolian_Unit *unit = NULL;
1141    if (pd->ret)
1142      unit = eo_parser_database_fill(&pd->state->staging.unit, data, EINA_TRUE);
1143    pd->ret = !!unit;
1144    if (pd->ret) pd->ret = _parse_deferred(unit);
1145    if (pd->ret) _merge_units(unit);
1146    return pd->ret;
1147 }
1148 
1149 EAPI Eina_Bool
eolian_state_all_eot_files_parse(Eolian_State * state)1150 eolian_state_all_eot_files_parse(Eolian_State *state)
1151 {
1152    Parse_Data pd = { state, EINA_TRUE };
1153 
1154    if (!state)
1155      return EINA_FALSE;
1156 
1157    _state_clean(state);
1158    eina_hash_foreach(state->filenames_eot, _tfile_parse, &pd);
1159 
1160    if (pd.ret && !database_validate(&state->staging.unit))
1161      return EINA_FALSE;
1162 
1163    _merge_staging(state);
1164 
1165    return pd.ret;
1166 }
1167 
_file_parse(const Eina_Hash * hash EINA_UNUSED,const void * key EINA_UNUSED,void * data,void * fdata)1168 static Eina_Bool _file_parse(const Eina_Hash *hash EINA_UNUSED, const void *key EINA_UNUSED, void *data, void *fdata)
1169 {
1170    Parse_Data *pd = fdata;
1171    Eolian_Unit *unit = NULL;
1172    if (pd->ret)
1173      unit = eo_parser_database_fill(&pd->state->staging.unit, data, EINA_FALSE);
1174    pd->ret = !!unit;
1175    if (pd->ret) pd->ret = _parse_deferred(unit);
1176    if (pd->ret) _merge_units(unit);
1177    return pd->ret;
1178 }
1179 
1180 EAPI Eina_Bool
eolian_state_all_eo_files_parse(Eolian_State * state)1181 eolian_state_all_eo_files_parse(Eolian_State *state)
1182 {
1183    Parse_Data pd = { state, EINA_TRUE };
1184 
1185    if (!state)
1186      return EINA_FALSE;
1187 
1188    _state_clean(state);
1189    eina_hash_foreach(state->filenames_eo, _file_parse, &pd);
1190 
1191    if (pd.ret && !database_validate(&state->staging.unit))
1192      return EINA_FALSE;
1193 
1194    _merge_staging(state);
1195 
1196    return pd.ret;
1197 }
1198 
1199 EAPI Eina_Bool
eolian_state_check(const Eolian_State * state)1200 eolian_state_check(const Eolian_State *state)
1201 {
1202    return database_check(state);
1203 }
1204 
1205 EAPI const Eolian_Unit *
eolian_state_unit_by_file_get(const Eolian_State * state,const char * file_name)1206 eolian_state_unit_by_file_get(const Eolian_State *state, const char *file_name)
1207 {
1208    if (!state) return NULL;
1209    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1210    Eolian_Unit *unit = eina_hash_find(state->main.units, shr);
1211    eina_stringshare_del(shr);
1212    return unit;
1213 }
1214 
1215 EAPI Eina_Iterator *
eolian_state_units_get(const Eolian_State * state)1216 eolian_state_units_get(const Eolian_State *state)
1217 {
1218    if (!state) return NULL;
1219    return eina_hash_iterator_data_new(state->main.units);
1220 }
1221 
1222 EAPI Eina_Iterator *
eolian_state_objects_by_file_get(const Eolian_State * state,const char * file_name)1223 eolian_state_objects_by_file_get(const Eolian_State *state, const char *file_name)
1224 {
1225    if (!state) return NULL;
1226    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1227    Eina_List *l = eina_hash_find(state->main.objects_f, shr);
1228    eina_stringshare_del(shr);
1229    if (!l) return NULL;
1230    return eina_list_iterator_new(l);
1231 }
1232 
1233 EAPI const Eolian_Class *
eolian_state_class_by_file_get(const Eolian_State * state,const char * file_name)1234 eolian_state_class_by_file_get(const Eolian_State *state, const char *file_name)
1235 {
1236    if (!state) return NULL;
1237    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1238    Eolian_Class *cl = eina_hash_find(state->main.classes_f, shr);
1239    eina_stringshare_del(shr);
1240    return cl;
1241 }
1242 
1243 EAPI Eina_Iterator *
eolian_state_constants_by_file_get(const Eolian_State * state,const char * file_name)1244 eolian_state_constants_by_file_get(const Eolian_State *state, const char *file_name)
1245 {
1246    if (!state) return NULL;
1247    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1248    Eina_List *l = eina_hash_find(state->main.constants_f, shr);
1249    eina_stringshare_del(shr);
1250    if (!l) return NULL;
1251    return eina_list_iterator_new(l);
1252 }
1253 
1254 EAPI Eina_Iterator *
eolian_state_errors_by_file_get(const Eolian_State * state,const char * file_name)1255 eolian_state_errors_by_file_get(const Eolian_State *state, const char *file_name)
1256 {
1257    if (!state) return NULL;
1258    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1259    Eina_List *l = eina_hash_find(state->main.errors_f, shr);
1260    eina_stringshare_del(shr);
1261    if (!l) return NULL;
1262    return eina_list_iterator_new(l);
1263 }
1264 
1265 EAPI Eina_Iterator *
eolian_state_aliases_by_file_get(const Eolian_State * state,const char * file_name)1266 eolian_state_aliases_by_file_get(const Eolian_State *state, const char *file_name)
1267 {
1268    if (!state) return NULL;
1269    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1270    Eina_List *l = eina_hash_find(state->main.aliases_f, shr);
1271    eina_stringshare_del(shr);
1272    if (!l) return NULL;
1273    return eina_list_iterator_new(l);
1274 }
1275 
1276 EAPI Eina_Iterator *
eolian_state_structs_by_file_get(const Eolian_State * state,const char * file_name)1277 eolian_state_structs_by_file_get(const Eolian_State *state, const char *file_name)
1278 {
1279    if (!state) return NULL;
1280    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1281    Eina_List *l = eina_hash_find(state->main.structs_f, shr);
1282    eina_stringshare_del(shr);
1283    if (!l) return NULL;
1284    return eina_list_iterator_new(l);
1285 }
1286 
1287 EAPI Eina_Iterator *
eolian_state_enums_by_file_get(const Eolian_State * state,const char * file_name)1288 eolian_state_enums_by_file_get(const Eolian_State *state, const char *file_name)
1289 {
1290    if (!state) return NULL;
1291    Eina_Stringshare *shr = eina_stringshare_add(file_name);
1292    Eina_List *l = eina_hash_find(state->main.enums_f, shr);
1293    eina_stringshare_del(shr);
1294    if (!l) return NULL;
1295    return eina_list_iterator_new(l);
1296 }
1297 
1298 EAPI const Eolian_State *
eolian_unit_state_get(const Eolian_Unit * unit)1299 eolian_unit_state_get(const Eolian_Unit *unit)
1300 {
1301    if (!unit) return NULL;
1302    return unit->state;
1303 }
1304 
1305 EAPI Eina_Iterator *
eolian_unit_children_get(const Eolian_Unit * unit)1306 eolian_unit_children_get(const Eolian_Unit *unit)
1307 {
1308    if (!unit) return NULL;
1309    return eina_hash_iterator_data_new(unit->children);
1310 }
1311 
1312 EAPI const char *
eolian_unit_file_get(const Eolian_Unit * unit)1313 eolian_unit_file_get(const Eolian_Unit *unit)
1314 {
1315    if (!unit) return NULL;
1316    return unit->file;
1317 }
1318 
1319 EAPI const char *
eolian_unit_file_path_get(const Eolian_Unit * unit)1320 eolian_unit_file_path_get(const Eolian_Unit *unit)
1321 {
1322    if (!unit || !unit->file) return NULL;
1323    Eina_Bool is_eo = eina_str_has_suffix(unit->file, EO_SUFFIX);
1324    return eina_hash_find(is_eo
1325      ? unit->state->filenames_eo
1326      : unit->state->filenames_eot, unit->file);
1327 }
1328 
1329 EAPI unsigned short
eolian_unit_version_get(const Eolian_Unit * unit)1330 eolian_unit_version_get(const Eolian_Unit *unit)
1331 {
1332    if (!unit) return 0;
1333    return unit->version;
1334 }
1335 
1336 EAPI const Eolian_Object *
eolian_unit_object_by_name_get(const Eolian_Unit * unit,const char * name)1337 eolian_unit_object_by_name_get(const Eolian_Unit *unit, const char *name)
1338 {
1339    if (!unit) return NULL;
1340    Eina_Stringshare *shr = eina_stringshare_add(name);
1341    Eolian_Object *o = eina_hash_find(unit->objects, shr);
1342    eina_stringshare_del(shr);
1343    return o;
1344 }
1345 
eolian_unit_objects_get(const Eolian_Unit * unit)1346 EAPI Eina_Iterator *eolian_unit_objects_get(const Eolian_Unit *unit)
1347 {
1348    return (unit ? eina_hash_iterator_data_new(unit->objects) : NULL);
1349 }
1350 
1351 EAPI const Eolian_Class *
eolian_unit_class_by_name_get(const Eolian_Unit * unit,const char * class_name)1352 eolian_unit_class_by_name_get(const Eolian_Unit *unit, const char *class_name)
1353 {
1354    if (!unit) return NULL;
1355    Eina_Stringshare *shr = eina_stringshare_add(class_name);
1356    Eolian_Class *cl = eina_hash_find(unit->classes, shr);
1357    eina_stringshare_del(shr);
1358    return cl;
1359 }
1360 
1361 EAPI Eina_Iterator *
eolian_unit_classes_get(const Eolian_Unit * unit)1362 eolian_unit_classes_get(const Eolian_Unit *unit)
1363 {
1364    return (unit ? eina_hash_iterator_data_new(unit->classes) : NULL);
1365 }
1366 
1367 EAPI const Eolian_Constant *
eolian_unit_constant_by_name_get(const Eolian_Unit * unit,const char * name)1368 eolian_unit_constant_by_name_get(const Eolian_Unit *unit, const char *name)
1369 {
1370    if (!unit) return NULL;
1371    Eina_Stringshare *shr = eina_stringshare_add(name);
1372    Eolian_Constant *v = eina_hash_find(unit->constants, shr);
1373    eina_stringshare_del(shr);
1374    return v;
1375 }
1376 
1377 EAPI const Eolian_Error *
eolian_unit_error_by_name_get(const Eolian_Unit * unit,const char * name)1378 eolian_unit_error_by_name_get(const Eolian_Unit *unit, const char *name)
1379 {
1380    if (!unit) return NULL;
1381    Eina_Stringshare *shr = eina_stringshare_add(name);
1382    Eolian_Error *v = eina_hash_find(unit->errors, shr);
1383    eina_stringshare_del(shr);
1384    return v;
1385 }
1386 
1387 EAPI Eina_Iterator *
eolian_unit_constants_get(const Eolian_Unit * unit)1388 eolian_unit_constants_get(const Eolian_Unit *unit)
1389 {
1390    return (unit ? eina_hash_iterator_data_new(unit->constants) : NULL);
1391 }
1392 
1393 EAPI Eina_Iterator *
eolian_unit_errors_get(const Eolian_Unit * unit)1394 eolian_unit_errors_get(const Eolian_Unit *unit)
1395 {
1396    return (unit ? eina_hash_iterator_data_new(unit->errors) : NULL);
1397 }
1398 
1399 EAPI const Eolian_Typedecl *
eolian_unit_alias_by_name_get(const Eolian_Unit * unit,const char * name)1400 eolian_unit_alias_by_name_get(const Eolian_Unit *unit, const char *name)
1401 {
1402    if (!unit) return NULL;
1403    Eina_Stringshare *shr = eina_stringshare_add(name);
1404    Eolian_Typedecl *tp = eina_hash_find(unit->aliases, shr);
1405    eina_stringshare_del(shr);
1406    if (!tp) return NULL;
1407    return tp;
1408 }
1409 
1410 EAPI const Eolian_Typedecl *
eolian_unit_struct_by_name_get(const Eolian_Unit * unit,const char * name)1411 eolian_unit_struct_by_name_get(const Eolian_Unit *unit, const char *name)
1412 {
1413    if (!unit) return NULL;
1414    Eina_Stringshare *shr = eina_stringshare_add(name);
1415    Eolian_Typedecl *tp = eina_hash_find(unit->structs, shr);
1416    eina_stringshare_del(shr);
1417    if (!tp) return NULL;
1418    return tp;
1419 }
1420 
1421 EAPI const Eolian_Typedecl *
eolian_unit_enum_by_name_get(const Eolian_Unit * unit,const char * name)1422 eolian_unit_enum_by_name_get(const Eolian_Unit *unit, const char *name)
1423 {
1424    if (!unit) return NULL;
1425    Eina_Stringshare *shr = eina_stringshare_add(name);
1426    Eolian_Typedecl *tp = eina_hash_find(unit->enums, shr);
1427    eina_stringshare_del(shr);
1428    if (!tp) return NULL;
1429    return tp;
1430 }
1431 
1432 EAPI Eina_Iterator *
eolian_unit_aliases_get(const Eolian_Unit * unit)1433 eolian_unit_aliases_get(const Eolian_Unit *unit)
1434 {
1435    return (unit ? eina_hash_iterator_data_new(unit->aliases) : NULL);
1436 }
1437 
1438 EAPI Eina_Iterator *
eolian_unit_structs_get(const Eolian_Unit * unit)1439 eolian_unit_structs_get(const Eolian_Unit *unit)
1440 {
1441    return (unit ? eina_hash_iterator_data_new(unit->structs) : NULL);
1442 }
1443 
1444 EAPI Eina_Iterator *
eolian_unit_enums_get(const Eolian_Unit * unit)1445 eolian_unit_enums_get(const Eolian_Unit *unit)
1446 {
1447    return (unit ? eina_hash_iterator_data_new(unit->enums) : NULL);
1448 }
1449 
1450 EAPI const char *
eolian_error_message_get(const Eolian_Error * err)1451 eolian_error_message_get(const Eolian_Error *err)
1452 {
1453    if (!err) return NULL;
1454    return err->msg;
1455 }
1456 
1457 EAPI Eina_Bool
eolian_error_is_extern(const Eolian_Error * err)1458 eolian_error_is_extern(const Eolian_Error *err)
1459 {
1460    if (!err) return EINA_FALSE;
1461    return err->is_extern;
1462 }
1463 
1464 EAPI const Eolian_Documentation *
eolian_error_documentation_get(const Eolian_Error * err)1465 eolian_error_documentation_get(const Eolian_Error *err)
1466 {
1467    EINA_SAFETY_ON_NULL_RETURN_VAL(err, NULL);
1468    return err->doc;
1469 }
1470 
1471 void
database_error_del(Eolian_Error * err)1472 database_error_del(Eolian_Error *err)
1473 {
1474    if (!err || eolian_object_unref(&err->base)) return;
1475    eina_stringshare_del(err->msg);
1476    database_doc_del(err->doc);
1477    free(err);
1478 }
1479 
1480 void
database_error_add(Eolian_Unit * unit,Eolian_Error * err)1481 database_error_add(Eolian_Unit *unit, Eolian_Error *err)
1482 {
1483    EOLIAN_OBJECT_ADD(unit, err->base.name, err, errors);
1484    eina_hash_set(unit->state->staging.errors_f, err->base.file, eina_list_append
1485                 ((Eina_List*)eina_hash_find(unit->state->staging.errors_f, err->base.file),
1486                 err));
1487    database_object_add(unit, &err->base);
1488 }
1489 
1490 char *
database_class_to_filename(const char * cname)1491 database_class_to_filename(const char *cname)
1492 {
1493    char *ret;
1494    Eina_Strbuf *strbuf = eina_strbuf_new();
1495    eina_strbuf_append(strbuf, cname);
1496    eina_strbuf_replace_all(strbuf, ".", "_");
1497    eina_strbuf_append(strbuf, ".eo");
1498 
1499    ret = eina_strbuf_string_steal(strbuf);
1500    eina_strbuf_free(strbuf);
1501 
1502    eina_str_tolower(&ret);
1503 
1504    return ret;
1505 }
1506