1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 
5 #include <locale.h>
6 #include <fnmatch.h>
7 
8 #include <Ecore.h>
9 #include <Ecore_Getopt.h>
10 
11 #include "Edje.h"
12 
13 static int _log_dom;
14 #define DBG(...) EINA_LOG_DOM_DBG(_log_dom, __VA_ARGS__)
15 #define INF(...) EINA_LOG_DOM_INFO(_log_dom, __VA_ARGS__)
16 #define WRN(...) EINA_LOG_DOM_WARN(_log_dom, __VA_ARGS__)
17 #define ERR(...) EINA_LOG_DOM_ERR(_log_dom, __VA_ARGS__)
18 #define CRI(...) EINA_LOG_DOM_CRIT(_log_dom, __VA_ARGS__)
19 
20 #define INDENT  "   "
21 #define INDENT2 INDENT INDENT
22 #define INDENT3 INDENT2 INDENT
23 #define INDENT4 INDENT3 INDENT
24 
25 static char *module_patterns_str = NULL;
26 
27 static int detail = 1;
28 static Eina_Bool machine = EINA_FALSE;
29 static char *type_glob = NULL;
30 static char *const *module_patterns;
31 static const Eina_List *modules;
32 
33 static char *
_module_patterns_str_new(void)34 _module_patterns_str_new(void)
35 {
36    Eina_Strbuf *buf;
37    char *const *itr;
38    char *ret;
39    if (!module_patterns) return strdup("*");
40 
41    buf = eina_strbuf_new();
42    for (itr = module_patterns; *itr != NULL; itr++)
43      {
44         eina_strbuf_append(buf, *itr);
45         if (itr[1]) eina_strbuf_append(buf, ", ");
46      }
47    ret = eina_strbuf_string_steal(buf);
48    eina_strbuf_free(buf);
49    return ret;
50 }
51 
52 static Eina_Bool
module_matches(const char * name)53 module_matches(const char *name)
54 {
55    char *const *itr;
56    if (!module_patterns) return EINA_TRUE;
57 
58    for (itr = module_patterns; *itr != NULL; itr++)
59      if (fnmatch(*itr, name, 0) == 0) return EINA_TRUE;
60 
61    return EINA_FALSE;
62 }
63 
64 static inline Eina_Bool
type_matches(const char * name)65 type_matches(const char *name)
66 {
67    if (!type_glob) return EINA_TRUE;
68    return fnmatch(type_glob, name, 0) == 0;
69 }
70 
71 static int
_types_sort(const void * pa,const void * pb)72 _types_sort(const void *pa, const void *pb)
73 {
74    const Eina_Hash_Tuple *ha = pa, *hb = pb;
75    const Edje_External_Type *ta = ha->data, *tb = hb->data;
76    const char *na = ha->key, *nb = hb->key;
77    int r;
78 
79    if (!ta->module) return -1;
80    if (!tb->module) return 1;
81    r = strcmp(ta->module, tb->module);
82    if (r != 0) return r;
83 
84    if (!na) return -1;
85    if (!nb) return 1;
86    return strcmp(na, nb);
87 }
88 
89 static const char *
_param_type_str_get(const Edje_External_Param_Info * param)90 _param_type_str_get(const Edje_External_Param_Info *param)
91 {
92    switch (param->type)
93      {
94       case EDJE_EXTERNAL_PARAM_TYPE_INT: return "int";
95 
96       case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE: return "double";
97 
98       case EDJE_EXTERNAL_PARAM_TYPE_STRING: return "string";
99 
100       case EDJE_EXTERNAL_PARAM_TYPE_BOOL: return "bool";
101 
102       case EDJE_EXTERNAL_PARAM_TYPE_CHOICE: return "choice";
103 
104       default:
105         ERR("Unknown parameter type %d", param->type);
106         return "???";
107      }
108 }
109 
110 static const char *
_param_value_str_get(const Edje_External_Type * type,const Edje_External_Param_Info * param,char * buf,size_t buflen)111 _param_value_str_get(const Edje_External_Type *type, const Edje_External_Param_Info *param, char *buf, size_t buflen)
112 {
113    switch (param->type)
114      {
115       case EDJE_EXTERNAL_PARAM_TYPE_INT:
116         if (param->info.i.def == EDJE_EXTERNAL_INT_UNSET) return NULL;
117         snprintf(buf, buflen, "%d", param->info.i.def);
118         return buf;
119 
120       case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
121         if (EINA_DBL_EQ(param->info.d.def, EDJE_EXTERNAL_DOUBLE_UNSET)) return NULL;
122         snprintf(buf, buflen, "%g", param->info.d.def);
123         return buf;
124 
125       case EDJE_EXTERNAL_PARAM_TYPE_STRING:
126         return param->info.s.def;
127 
128       case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
129         if (param->info.b.def == 0) return "0";
130         else if (param->info.b.def == 1)
131           return "1";
132         return NULL;
133 
134       case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
135       {
136          char *def;
137          if (param->info.c.def) return param->info.c.def;
138          if (!param->info.c.def_get) return NULL;
139          def = param->info.c.def_get(type->data, param);
140          if (!def) return NULL;
141          eina_strlcpy(buf, def, buflen);
142          free(def);
143          return buf;
144       }
145 
146       default:
147         ERR("Unknown parameter type %d", param->type);
148         return NULL;
149      }
150 }
151 
152 static const char *
_param_flags_str_get(const Edje_External_Param_Info * param)153 _param_flags_str_get(const Edje_External_Param_Info *param)
154 {
155    static char buf[] = "GET|SET|STATE|CONSTRUCTOR";
156 
157    if (param->flags == EDJE_EXTERNAL_PARAM_FLAGS_NONE) return "NONE";
158    if (param->flags == EDJE_EXTERNAL_PARAM_FLAGS_REGULAR) return "REGULAR";
159 
160    buf[0] = '\0';
161 
162    if (param->flags & EDJE_EXTERNAL_PARAM_FLAGS_GET)
163      strcat(buf, "GET");
164 
165    if (param->flags & EDJE_EXTERNAL_PARAM_FLAGS_SET)
166      {
167         if (buf[0] != '\0') strcat(buf, "|");
168         strcat(buf, "SET");
169      }
170 
171    if (param->flags & EDJE_EXTERNAL_PARAM_FLAGS_STATE)
172      {
173         if (buf[0] != '\0') strcat(buf, "|");
174         strcat(buf, "STATE");
175      }
176 
177    if (param->flags & EDJE_EXTERNAL_PARAM_FLAGS_CONSTRUCTOR)
178      {
179         if (buf[0] != '\0') strcat(buf, "|");
180         strcat(buf, "CONSTRUCTOR");
181      }
182 
183    return buf;
184 }
185 
186 static void
_param_choices_print(const char * const * choices)187 _param_choices_print(const char *const *choices)
188 {
189    if (machine) puts("CHOICES-BEGIN");
190    else fputs(", choices:", stdout);
191    for (; *choices != NULL; choices++)
192      {
193         if (machine) puts(*choices);
194         else printf(" \"%s\"", *choices);
195      }
196    if (machine) puts("CHOICES-END");
197 }
198 
199 static void
_param_extra_details(const Edje_External_Type * type,const Edje_External_Param_Info * param)200 _param_extra_details(const Edje_External_Type *type, const Edje_External_Param_Info *param)
201 {
202    const char *str = _param_flags_str_get(param);
203    if (machine) printf("FLAGS: %s\n", str);
204    else printf(" /* flags: %s", str);
205 
206    switch (param->type)
207      {
208       case EDJE_EXTERNAL_PARAM_TYPE_INT:
209         if (param->info.i.min != EDJE_EXTERNAL_INT_UNSET)
210           {
211              if (machine) printf("MIN: %d\n", param->info.i.min);
212              else printf(", min: %d", param->info.i.min);
213           }
214         if (param->info.i.max != EDJE_EXTERNAL_INT_UNSET)
215           {
216              if (machine) printf("MAX: %d\n", param->info.i.max);
217              else printf(", max: %d", param->info.i.max);
218           }
219         if (param->info.i.step != EDJE_EXTERNAL_INT_UNSET)
220           {
221              if (machine) printf("STEP: %d\n", param->info.i.step);
222              else printf(", step: %d", param->info.i.step);
223           }
224         break;
225 
226       case EDJE_EXTERNAL_PARAM_TYPE_DOUBLE:
227         if (EINA_DBL_EQ(param->info.d.min, EDJE_EXTERNAL_DOUBLE_UNSET))
228           {
229              if (machine) printf("MIN: %g\n", param->info.d.min);
230              else printf(", min: %g", param->info.d.min);
231           }
232         if (EINA_DBL_EQ(param->info.d.max, EDJE_EXTERNAL_DOUBLE_UNSET))
233           {
234              if (machine) printf("MAX: %g\n", param->info.d.max);
235              else printf(", max: %g", param->info.d.max);
236           }
237         if (EINA_DBL_EQ(param->info.d.step, EDJE_EXTERNAL_DOUBLE_UNSET))
238           {
239              if (machine) printf("STEP: %g\n", param->info.d.step);
240              else printf(", step: %g", param->info.d.step);
241           }
242         break;
243 
244       case EDJE_EXTERNAL_PARAM_TYPE_STRING:
245         if (param->info.s.accept_fmt)
246           {
247              if (machine) printf("ACCEPT_FMT: %s\n", param->info.s.accept_fmt);
248              else printf(", accept_fmt: \"%s\"", param->info.s.accept_fmt);
249           }
250         if (param->info.s.deny_fmt)
251           {
252              if (machine) printf("DENY_FMT: %s\n", param->info.s.deny_fmt);
253              else printf(", deny_fmt: \"%s\"", param->info.s.deny_fmt);
254           }
255         break;
256 
257       case EDJE_EXTERNAL_PARAM_TYPE_BOOL:
258         if (param->info.b.false_str)
259           {
260              if (machine) printf("FALSE_STR: %s\n", param->info.b.false_str);
261              else printf(", false_str: \"%s\"", param->info.b.false_str);
262           }
263         if (param->info.b.true_str)
264           {
265              if (machine) printf("TRUE_STR: %s\n", param->info.b.true_str);
266              else printf(", true_str: \"%s\"", param->info.b.true_str);
267           }
268         break;
269 
270       case EDJE_EXTERNAL_PARAM_TYPE_CHOICE:
271       {
272          if (param->info.c.choices)
273            _param_choices_print(param->info.c.choices);
274          else if (param->info.c.query)
275            {
276               char **choices = param->info.c.query(type->data, param);
277               if (choices)
278                 {
279                    char **itr;
280                    _param_choices_print((const char *const *)choices);
281                    for (itr = choices; *itr; itr++)
282                      free(*itr);
283                    free(choices);
284                 }
285            }
286       }
287       break;
288 
289       default:
290         ERR("Unknown parameter type %d", param->type);
291      }
292 
293    if (!machine) fputs(" */", stdout);  /* \n not desired */
294 }
295 
296 static int
_info_list(void)297 _info_list(void)
298 {
299    Eina_Iterator *itr;
300    Eina_List *types;
301    const Eina_Hash_Tuple *tuple;
302    const Eina_List *l;
303    const char *name, *last_module;
304    Eina_Bool module_found = EINA_FALSE, type_found = EINA_FALSE;
305    Eina_Bool in_module = EINA_FALSE;
306 
307    EINA_LIST_FOREACH(modules, l, name)
308      {
309         if (!module_matches(name))
310           {
311              DBG("filter out module '%s': does not match '%s'",
312                  name, module_patterns_str);
313              continue;
314           }
315 
316         if (!edje_module_load(name))
317           {
318              ERR("error loading external '%s'", name);
319              continue;
320           }
321 
322         module_found = EINA_TRUE;
323      }
324 
325    itr = edje_external_iterator_get();
326    types = NULL;
327    EINA_ITERATOR_FOREACH(itr, tuple)
328      {
329         const Edje_External_Type *type = tuple->data;
330         name = tuple->key;
331 
332         if (!type)
333           {
334              ERR("no type value for '%s'", name);
335              continue;
336           }
337         else if (type->abi_version != edje_external_type_abi_version_get())
338           {
339              ERR("type '%s' with incorrect abi_version %u (expected %u)",
340                  name, type->abi_version, edje_external_type_abi_version_get());
341              continue;
342           }
343 
344         if (!type_matches(name))
345           {
346              DBG("filter out type '%s': does not match '%s'", name, type_glob);
347              continue;
348           }
349 
350         types = eina_list_append(types, tuple);
351         type_found = EINA_TRUE;
352      }
353    eina_iterator_free(itr);
354 
355    last_module = NULL;
356    types = eina_list_sort(types, 0, _types_sort);
357    EINA_LIST_FREE(types, tuple)
358      {
359         Eina_Bool changed_module = EINA_FALSE;
360         const Edje_External_Type *type = tuple->data;
361         const Edje_External_Param_Info *param;
362         name = tuple->key;
363 
364         if ((last_module) && (type->module))
365           {
366              changed_module = ((last_module != type->module) &&
367                                (!strcmp(last_module, type->module)));
368           }
369         else if ((!last_module) && (type->module))
370           changed_module = EINA_TRUE;
371 
372         if (changed_module)
373           {
374              if (in_module)
375                {
376                   if (machine) puts("TYPES-END\nMODULE-END");
377                   else puts(INDENT "}\n}");
378                }
379 
380              if (machine)
381                printf("MODULE-BEGIN\n"
382                       "NAME: %s\n"
383                       "FRIENDLY-NAME: %s\n"
384                       "TYPES-BEGIN\n",
385                       type->module, type->module_name);
386              else
387                printf("module {\n"
388                       INDENT "name: \"%s\";\n"
389                       INDENT "friendly_name: \"%s\";\n"
390                       INDENT "types {\n",
391                       type->module, type->module_name);
392 
393              in_module = EINA_TRUE;
394           }
395 
396         if (machine) printf("TYPE-BEGIN\nNAME: %s\n", name);
397         else printf(INDENT2 "type {\n" INDENT3 "name: \"%s\";\n", name);
398 
399         if (detail > 1)
400           {
401              const char *str;
402 
403              if (!type->label_get) str = NULL;
404              else str = type->label_get(type->data);
405              if (machine) printf("LABEL: %s\n", str ? str : "");
406              else if (str)
407                printf(INDENT3 "label: \"%s\";\n", str);
408 
409              if (!type->description_get) str = NULL;
410              else str = type->description_get(type->data);
411              if (machine) printf("DESCRIPTION: %s\n", str ? str : "");
412              else if (str)
413                printf(INDENT3 "description: \"%s\";\n", str);
414           }
415 
416         if (machine) puts("PARAMS-BEGIN");
417         else puts(INDENT3 "params {");
418 
419         for (param = type->parameters_info; param->name != NULL; param++)
420           {
421              const char *pt = _param_type_str_get(param);
422              char buf[128];
423 
424              if (machine)
425                printf("PARAM-BEGIN\nNAME: %s\nTYPE: %s\n", param->name, pt);
426              else printf(INDENT4 "%s: \"%s\"", pt, param->name);
427 
428              if (detail > 0)
429                {
430                   const char *str = _param_value_str_get
431                       (type, param, buf, sizeof(buf));
432                   if (machine) printf("DEFAULT: %s\n", str ? str : "");
433                   else if (str)
434                     printf(" \"%s\"", str);
435 
436                   if (detail > 1)
437                     {
438                        if (!machine) putchar(';');
439                        _param_extra_details(type, param);
440                     }
441                }
442 
443              if (machine) puts("PARAM-END");
444              else if (detail > 1)
445                putchar('\n');
446              else puts(";");
447           }
448 
449         if (machine) puts("PARAMS-END\nTYPE-END");
450         else puts(INDENT3 "}\n" INDENT2 "}");
451 
452         last_module = type->module;
453      }
454 
455    if (in_module)
456      {
457         if (machine) puts("MODULE-END");
458         else puts(INDENT "}\n}");
459      }
460 
461    if (!module_found) WRN("no modules match '%s'", module_patterns_str);
462    if (!type_found) WRN("no types match '%s'", type_glob);
463    return (!module_found) || (!type_found);
464 }
465 
466 static int
_types_names_list(void)467 _types_names_list(void)
468 {
469    Eina_Iterator *itr;
470    Eina_List *types;
471    const Eina_Hash_Tuple *tuple;
472    const Eina_List *l;
473    const char *name;
474    Eina_Bool module_found = EINA_FALSE, type_found = EINA_FALSE;
475 
476    EINA_LIST_FOREACH(modules, l, name)
477      {
478         if (!module_matches(name))
479           {
480              DBG("filter out module '%s': does not match '%s'",
481                  name, module_patterns_str);
482              continue;
483           }
484 
485         if (!edje_module_load(name))
486           {
487              ERR("error loading external '%s'", name);
488              continue;
489           }
490 
491         module_found = EINA_TRUE;
492      }
493 
494    itr = edje_external_iterator_get();
495    types = NULL;
496    EINA_ITERATOR_FOREACH(itr, tuple)
497      {
498         const Edje_External_Type *type = tuple->data;
499         name = tuple->key;
500 
501         if (!type)
502           {
503              ERR("no type value for '%s'", name);
504              continue;
505           }
506         else if (type->abi_version != edje_external_type_abi_version_get())
507           {
508              ERR("type '%s' with incorrect abi_version %u (expected %u)",
509                  name, type->abi_version, edje_external_type_abi_version_get());
510              continue;
511           }
512 
513         if (!type_matches(name))
514           {
515              DBG("filter out type '%s': does not match '%s'", name, type_glob);
516              continue;
517           }
518 
519         types = eina_list_append(types, tuple);
520         type_found = EINA_TRUE;
521      }
522    eina_iterator_free(itr);
523 
524    types = eina_list_sort(types, 0, _types_sort);
525    EINA_LIST_FREE(types, tuple)
526      puts(tuple->key);
527 
528    if (!module_found) WRN("no modules match '%s'", module_patterns_str);
529    if (!type_found) WRN("no types match '%s'", type_glob);
530    return (!module_found) || (!type_found);
531 }
532 
533 static int
_modules_names_list(void)534 _modules_names_list(void)
535 {
536    const Eina_List *l;
537    const char *name;
538    Eina_Bool found = EINA_FALSE;
539 
540    EINA_LIST_FOREACH(modules, l, name)
541      {
542         if (!module_matches(name))
543           {
544              DBG("filter out module '%s': does not match '%s'",
545                  name, module_patterns_str);
546              continue;
547           }
548         found = EINA_TRUE;
549         puts(name);
550      }
551 
552    if (!found) WRN("no modules match '%s'", module_patterns_str);
553    return !found;
554 }
555 
556 static const char *mode_choices[] = {
557    "info",
558    "modules-names",
559    "types-names",
560    NULL,
561 };
562 
563 static const char *detail_choices[] = {
564    "none",
565    "terse",
566    "all",
567    NULL
568 };
569 
570 const Ecore_Getopt optdesc = {
571    "edje_external_inspector",
572    "%prog [options] [module|module-glob] ... [module|module-glob]",
573    PACKAGE_VERSION,
574    "(C) 2010 - The Enlightenment Project",
575    "BSD",
576    "Edje external module inspector.",
577    0,
578    {
579       ECORE_GETOPT_CHOICE('m', "mode", "Choose which mode to operate.",
580                           mode_choices),
581       ECORE_GETOPT_STORE_STR('t', "type", "Limit output to type (or glob)."),
582       ECORE_GETOPT_CHOICE('d', "detail", "Choose detail level (default=terse)",
583                           detail_choices),
584       ECORE_GETOPT_STORE_TRUE('M', "machine", "Produce machine readable output."),
585       ECORE_GETOPT_LICENSE('L', "license"),
586       ECORE_GETOPT_COPYRIGHT('C', "copyright"),
587       ECORE_GETOPT_VERSION('V', "version"),
588       ECORE_GETOPT_HELP('h', "help"),
589       ECORE_GETOPT_SENTINEL
590    }
591 };
592 
593 int
main(int argc,char ** argv)594 main(int argc, char **argv)
595 {
596    Eina_Bool quit_option = EINA_FALSE;
597    char *mode = NULL;
598    char *detail_name = NULL;
599    int arg_index;
600    int ret = 0;
601    Ecore_Getopt_Value values[] = {
602       ECORE_GETOPT_VALUE_STR(mode),
603       ECORE_GETOPT_VALUE_STR(type_glob),
604       ECORE_GETOPT_VALUE_STR(detail_name),
605       ECORE_GETOPT_VALUE_BOOL(machine),
606       ECORE_GETOPT_VALUE_BOOL(quit_option),
607       ECORE_GETOPT_VALUE_BOOL(quit_option),
608       ECORE_GETOPT_VALUE_BOOL(quit_option),
609       ECORE_GETOPT_VALUE_BOOL(quit_option),
610       ECORE_GETOPT_VALUE_NONE
611    };
612 
613    setlocale(LC_NUMERIC, "C");
614 
615    ecore_app_no_system_modules();
616 
617    eina_init();
618 
619    _log_dom = eina_log_domain_register
620        ("edje_external_inspector", EINA_COLOR_YELLOW);
621    if (_log_dom < 0)
622      {
623         EINA_LOG_CRIT
624           ("could not register log domain 'edje_external_inspector'");
625         ret = 1;
626         goto error_log;
627      }
628 
629    edje_init();
630 
631    arg_index = ecore_getopt_parse(&optdesc, values, argc, argv);
632    if (arg_index < 0)
633      {
634         ERR("could not parse arguments.");
635         ret = 1;
636         goto error_getopt;
637      }
638    else if (quit_option)
639      goto error_getopt;
640 
641    if (!mode) mode = (char *)mode_choices[0];
642 
643    if (detail_name)
644      {
645         if (!strcmp(detail_name, "none")) detail = 0;
646         else if (!strcmp(detail_name, "terse"))
647           detail = 1;
648         else if (!strcmp(detail_name, "all"))
649           detail = 2;
650         else ERR("Unknown detail level: '%s'", detail_name);
651      }
652 
653    if (arg_index < argc) module_patterns = argv + arg_index;
654    else module_patterns = NULL;
655 
656    modules = edje_available_modules_get();
657    module_patterns_str = _module_patterns_str_new();
658 
659    if (!strcmp(mode, "info")) ret = _info_list();
660    else if (!strcmp(mode, "modules-names"))
661      ret = _modules_names_list();
662    else if (!strcmp(mode, "types-names"))
663      ret = _types_names_list();
664    else
665      {
666         ERR("Unknown mode: %s", mode);
667         ret = 1;
668      }
669 
670    free(module_patterns_str);
671 
672 error_getopt:
673    edje_shutdown();
674    eina_log_domain_unregister(_log_dom);
675 error_log:
676    eina_shutdown();
677 
678    return ret;
679 }
680 
681