more leak fixes
[framework/uifw/efreet.git] / src / lib / efreet_utils.c
1 /* vim: set sw=4 ts=4 sts=4 et: */
2 #include "Efreet.h"
3 #include "efreet_private.h"
4
5 typedef struct Efreet_Cache_Fill        Efreet_Cache_Fill;
6 typedef struct Efreet_Cache_Fill_Dir    Efreet_Cache_Fill_Dir;
7 typedef struct Efreet_Cache_Search      Efreet_Cache_Search;
8 typedef struct Efreet_Cache_Search_List Efreet_Cache_Search_List;
9 typedef struct Efreet_Monitor           Efreet_Monitor;
10 typedef struct Efreet_Util_Desktop      Efreet_Util_Desktop;
11
12 struct Efreet_Cache_Fill
13 {
14     Eina_List *dirs;
15     Efreet_Cache_Fill_Dir *current;
16     DIR  *files;
17 };
18
19 struct Efreet_Cache_Fill_Dir
20 {
21     char *path;
22     char *file_id;
23     int   priority;
24 };
25
26 struct Efreet_Cache_Search
27 {
28     Efreet_Util_Desktop *result;
29     const char *what1;
30     const char *what2;
31 };
32
33 struct Efreet_Cache_Search_List
34 {
35     Eina_List *list;
36     const char *what;
37 };
38
39 struct Efreet_Monitor
40 {
41     char *file_id;
42     Ecore_File_Monitor *monitor;
43     int priority;
44 };
45
46 struct Efreet_Util_Desktop
47 {
48     Efreet_Desktop *desktop;
49     int priority;
50 };
51
52 static char *efreet_util_path_in_default(const char *section, const char *path);
53
54 static int  efreet_util_cache_fill(void *data);
55 static void efreet_util_cache_add(const char *path, const char *file_id, int priority, int event);
56 static void efreet_util_cache_remove(const char *path, const char *file_id, int priority);
57 static void efreet_util_cache_reload(const char *path, const char *file_id, int priority);
58 static void efreet_util_cache_dir_free(void *data);
59
60 static Eina_Bool efreet_util_cache_search_mime(const Eina_Hash *hash, void *value, void *fdata);
61 static Eina_Bool efreet_util_cache_search_wm_class(const Eina_Hash *hash, void *value, void *fdata);
62 static Eina_Bool efreet_util_cache_search_exec(const Eina_Hash *hash, void *value, void *fdata);
63 static Eina_Bool efreet_util_cache_search_name(const Eina_Hash *hash, void *value, void *fdata);
64 static Eina_Bool efreet_util_cache_search_generic_name(const Eina_Hash *hash, void *value, void *fdata);
65
66 static Eina_Bool efreet_util_cache_search_name_glob(const Eina_Hash *hash, void *value, void *fdata);
67 static Eina_Bool efreet_util_cache_search_exec_glob(const Eina_Hash *hash, void *value, void *fdata);
68 static Eina_Bool efreet_util_cache_search_generic_name_glob(const Eina_Hash *hash, void *value, void *fdata);
69 static Eina_Bool efreet_util_cache_search_comment_glob(const Eina_Hash *hash, void *value, void *fdata);
70
71 static int  efreet_util_glob_match(const char *str, const char *glob);
72
73 static void efreet_util_monitor(const char *path, const char *file_id, int priority);
74 static void efreet_util_monitor_cb(void *data, Ecore_File_Monitor *monitor,
75                                     Ecore_File_Event event, const char *path);
76 static void efreet_util_monitor_free(void *data);
77 static void efreet_util_menus_find_helper(Eina_List *menus, const char *config_dir);
78
79 static void efreet_util_desktops_by_category_add(Efreet_Desktop *desktop);
80 static void efreet_util_desktops_by_category_remove(Efreet_Desktop *desktop);
81
82 static void efreet_util_desktop_free(Efreet_Util_Desktop *ud);
83 static void efreet_event_desktop_change_free(void *data, void *ev);
84
85 static Eina_Hash *desktop_by_file_id = NULL;
86 static Eina_Hash *file_id_by_desktop_path = NULL;
87 static Eina_Hash *desktops_by_category = NULL;
88
89 static Ecore_Idler       *idler = NULL;
90 static Efreet_Cache_Fill *fill = NULL;
91
92 static Eina_List *monitors = NULL;
93
94 static int init = 0;
95
96 EAPI int EFREET_EVENT_DESKTOP_LIST_CHANGE = 0;
97 EAPI int EFREET_EVENT_DESKTOP_CHANGE = 0;
98
99 EAPI int
100 efreet_util_init(void)
101 {
102     Eina_List *dirs;
103
104     if (init++) return init;
105
106     if (!EFREET_EVENT_DESKTOP_LIST_CHANGE)
107         EFREET_EVENT_DESKTOP_LIST_CHANGE = ecore_event_type_new();
108     if (!EFREET_EVENT_DESKTOP_CHANGE)
109         EFREET_EVENT_DESKTOP_CHANGE = ecore_event_type_new();
110     desktop_by_file_id = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_util_desktop_free));
111     file_id_by_desktop_path = eina_hash_string_superfast_new(EINA_FREE_CB(eina_stringshare_del));
112     desktops_by_category = eina_hash_string_superfast_new(EINA_FREE_CB(eina_list_free));
113
114     monitors = NULL;
115
116     fill = NEW(Efreet_Cache_Fill, 1);
117     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
118                                                                     "applications");
119     if (dirs)
120     {
121         Efreet_Cache_Fill_Dir *dir;
122         char *path;
123         int priority = 0;
124
125         while (dirs)
126         {
127             path = eina_list_data_get(dirs);
128             dir = NEW(Efreet_Cache_Fill_Dir, 1);
129             dir->path = path;
130             dir->priority = priority++;
131             fill->dirs = eina_list_append(fill->dirs, dir);
132             dirs = eina_list_remove_list(dirs, dirs);
133         }
134     }
135     idler = ecore_idler_add(efreet_util_cache_fill, NULL);
136     return init;
137 }
138
139 EAPI int
140 efreet_util_shutdown(void)
141 {
142     Efreet_Monitor *em;
143     Efreet_Cache_Fill_Dir *dir;
144
145     if (--init) return init;
146
147     if (idler)
148     {
149         ecore_idler_del(idler);
150         while (fill->dirs)
151         {
152             dir = eina_list_data_get(fill->dirs);
153             efreet_util_cache_dir_free(dir);
154             fill->dirs = eina_list_remove_list(fill->dirs, fill->dirs);
155         }
156
157         if (fill->current) efreet_util_cache_dir_free(fill->current);
158         if (fill->files) closedir(fill->files);
159         free(fill);
160     }
161     idler = NULL;
162
163     IF_FREE_HASH(desktop_by_file_id);
164     IF_FREE_HASH(file_id_by_desktop_path);
165
166     while (monitors)
167     {
168         em = eina_list_data_get(monitors);
169         efreet_util_monitor_free(em);
170         monitors = eina_list_remove_list(monitors, monitors);
171     }
172
173     IF_FREE_HASH(desktops_by_category);
174
175     return init;
176 }
177
178 static char *
179 efreet_util_path_in_default(const char *section, const char *path)
180 {
181     Eina_List *dirs;
182     char *ret = NULL;
183     char *dir;
184
185     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
186                                                                     section);
187
188     EINA_LIST_FREE(dirs, dir)
189     {
190         size_t len;
191
192         len = strlen(dir);
193         if (!strncmp(path, dir, strlen(dir)))
194         {
195             ret = dir;
196             break;
197         }
198         free(dir);
199     }
200
201     EINA_LIST_FREE(dirs, dir)
202       if (ret != dir) free(dir);
203
204     return ret;
205 }
206
207 EAPI const char *
208 efreet_util_path_to_file_id(const char *path)
209 {
210     size_t len;
211     char *tmp, *p;
212     char *base;
213     const char *file_id = NULL;
214
215     if (!path) return NULL;
216     file_id = eina_hash_find(file_id_by_desktop_path, path);
217     if (file_id) return file_id;
218
219     base = efreet_util_path_in_default("applications", path);
220     if (!base) return NULL;
221
222     len = strlen(base);
223     if (strlen(path) <= len)
224     {
225         free(base);
226         return NULL;
227     }
228     if (strncmp(path, base, len))
229     {
230         free(base);
231         return NULL;
232     }
233
234     tmp = strdup(path + len + 1);
235     p = tmp;
236     while (*p)
237     {
238         if (*p == '/') *p = '-';
239         p++;
240     }
241     free(base);
242     file_id = eina_stringshare_add(tmp);
243     free(tmp);
244     eina_hash_del(file_id_by_desktop_path, path, NULL);
245     eina_hash_add(file_id_by_desktop_path, path, (void *)file_id);
246     return file_id;
247 }
248
249 EAPI Eina_List *
250 efreet_util_desktop_mime_list(const char *mime)
251 {
252     Efreet_Cache_Search_List search;
253     Eina_Iterator *it;
254
255     search.list = NULL;
256     search.what = eina_stringshare_add(mime);
257
258     it = eina_hash_iterator_data_new(desktop_by_file_id);
259     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_mime), &search);
260     eina_iterator_free(it);
261
262     eina_stringshare_del(search.what);
263
264     return search.list;
265 }
266
267 EAPI Efreet_Desktop *
268 efreet_util_desktop_wm_class_find(const char *wmname, const char *wmclass)
269 {
270     Efreet_Cache_Search search;
271     Efreet_Util_Desktop *ud;
272     Eina_Iterator *it;
273
274     if ((!wmname) && (!wmclass)) return NULL;
275     search.what1 = wmname;
276     search.what2 = wmclass;
277     search.result = NULL;
278
279     it = eina_hash_iterator_data_new(desktop_by_file_id);
280     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_wm_class), &search);
281     eina_iterator_free(it);
282     ud = search.result;
283
284     if (ud)
285       {
286          efreet_desktop_ref(ud->desktop);
287          return ud->desktop;
288       }
289     return NULL;
290 }
291
292 EAPI Efreet_Desktop *
293 efreet_util_desktop_file_id_find(const char *file_id)
294 {
295     Efreet_Desktop *desktop = NULL;
296     Efreet_Util_Desktop *ud = NULL;
297     Eina_List *dirs, *l;
298     const char *dir;
299     int priority = 0;
300
301     if (!file_id) return NULL;
302     ud = eina_hash_find(desktop_by_file_id, file_id);
303     if (ud) return ud->desktop;
304
305     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
306                                                                     "applications");
307     if (!dirs) return NULL;
308
309     EINA_LIST_FOREACH(dirs, l, dir)
310     {
311         char *tmp, *p;
312         char buf[PATH_MAX];
313
314         tmp = strdup(file_id);
315         p = tmp;
316
317         while (p)
318         {
319             snprintf(buf, sizeof(buf), "%s/%s", dir, tmp);
320             desktop = efreet_desktop_get(buf);
321             if (desktop) break;
322             p = strchr(p, '-');
323             if (p) *p = '/';
324         }
325         free(tmp);
326         if (desktop) break;
327         priority++;
328     }
329     while (dirs)
330     {
331         free(eina_list_data_get(dirs));
332         dirs = eina_list_remove_list(dirs, dirs);
333     }
334     if (desktop)
335     {
336         Efreet_Event_Desktop_Change *ev;
337
338         ud = NEW(Efreet_Util_Desktop, 1);
339         ud->priority = priority;
340         ud->desktop = desktop;
341
342         eina_hash_del(desktop_by_file_id, file_id, NULL);
343         eina_hash_add(desktop_by_file_id, file_id, ud);
344         efreet_util_desktops_by_category_add(desktop);
345
346         ev = NEW(Efreet_Event_Desktop_Change, 1);
347         ev->current = desktop;
348         efreet_desktop_ref(ev->current);
349         ev->change = EFREET_DESKTOP_CHANGE_ADD;
350         ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
351     }
352     return desktop;
353 }
354
355 EAPI Efreet_Desktop *
356 efreet_util_desktop_exec_find(const char *exec)
357 {
358     Efreet_Cache_Search search;
359     Eina_Iterator *it;
360
361     if (!exec) return NULL;
362     search.what1 = exec;
363     search.what2 = NULL;
364     search.result = NULL;
365
366     it = eina_hash_iterator_data_new(desktop_by_file_id);
367     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_exec), &search);
368     eina_iterator_free(it);
369
370     if (!search.result) return NULL;
371
372     efreet_desktop_ref(search.result->desktop);
373     return search.result->desktop;
374 }
375
376 EAPI Efreet_Desktop *
377 efreet_util_desktop_name_find(const char *name)
378 {
379     Efreet_Cache_Search search;
380     Eina_Iterator *it;
381
382     if (!name) return NULL;
383     search.what1 = name;
384     search.what2 = NULL;
385     search.result = NULL;
386
387     it = eina_hash_iterator_data_new(desktop_by_file_id);
388     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_name), &search);
389     eina_iterator_free(it);
390
391     if (!search.result) return NULL;
392
393     efreet_desktop_ref(search.result->desktop);
394     return search.result->desktop;
395 }
396
397 EAPI Efreet_Desktop *
398 efreet_util_desktop_generic_name_find(const char *generic_name)
399 {
400     Efreet_Cache_Search search;
401     Eina_Iterator *it;
402
403     if (!generic_name) return NULL;
404     search.what1 = generic_name;
405     search.what2 = NULL;
406     search.result = NULL;
407
408     it = eina_hash_iterator_data_new(desktop_by_file_id);
409     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_generic_name), &search);
410     eina_iterator_free(it);
411
412     if (!search.result) return NULL;
413
414     efreet_desktop_ref(search.result->desktop);
415     return search.result->desktop;
416 }
417
418 EAPI Eina_List *
419 efreet_util_desktop_name_glob_list(const char *glob)
420 {
421     Efreet_Cache_Search_List search;
422     Eina_Iterator *it;
423
424     search.list = NULL;
425     search.what = glob;
426
427     it = eina_hash_iterator_data_new(desktop_by_file_id);
428     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_name_glob), &search);
429     eina_iterator_free(it);
430
431     return search.list;
432 }
433
434 EAPI Eina_List *
435 efreet_util_desktop_exec_glob_list(const char *glob)
436 {
437     Efreet_Cache_Search_List search;
438     Eina_Iterator *it;
439
440     search.list = NULL;
441     search.what = glob;
442
443     it = eina_hash_iterator_data_new(desktop_by_file_id);
444     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_exec_glob), &search);
445     eina_iterator_free(it);
446
447     return search.list;
448 }
449
450 EAPI Eina_List *
451 efreet_util_desktop_generic_name_glob_list(const char *glob)
452 {
453     Efreet_Cache_Search_List search;
454     Eina_Iterator *it;
455
456     search.list = NULL;
457     search.what = glob;
458
459     it = eina_hash_iterator_data_new(desktop_by_file_id);
460     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_generic_name_glob), &search);
461     eina_iterator_free(it);
462
463     return search.list;
464 }
465
466 EAPI Eina_List *
467 efreet_util_desktop_comment_glob_list(const char *glob)
468 {
469     Efreet_Cache_Search_List search;
470     Eina_Iterator *it;
471
472     search.list = NULL;
473     search.what = glob;
474
475     it = eina_hash_iterator_data_new(desktop_by_file_id);
476     eina_iterator_foreach(it, EINA_EACH(efreet_util_cache_search_comment_glob), &search);
477     eina_iterator_free(it);
478
479     return search.list;
480 }
481
482 static Eina_Bool
483 _hash_keys(Eina_Hash *hash __UNUSED__, const void *key, void *fdata)
484 {
485     Eina_List **l = fdata;
486
487     *l = eina_list_append(*l, key);
488     return EINA_TRUE;
489 }
490
491 /**
492  * Find all desktop categories
493  * This list must be freed using ecore_list_destroy()
494  *
495  * @return an Eina_List of category names (const char *)
496  */
497 EAPI Eina_List *
498 efreet_util_desktop_categories_list(void)
499 {
500    Eina_Iterator *it;
501    Eina_List *list = NULL;
502    
503    it = eina_hash_iterator_key_new(desktops_by_category);
504    if (it)
505      {
506         eina_iterator_foreach(it, EINA_EACH(_hash_keys), &list);
507         eina_iterator_free(it);
508      }
509    
510    return list;
511 }
512
513 /**
514  * Find all desktops in a given category
515  *
516  * Note: this returns a pointer to an internal list. Do not add to, remove from, or delete this list.
517  *
518  * @param category the category name
519  * @return a list of desktops
520  */
521 EAPI Eina_List *
522 efreet_util_desktop_category_list(const char *category)
523 {
524     return eina_hash_find(desktops_by_category, category);
525 }
526
527 #if 0
528 static Eina_Bool
529 dump(Eina_Hash *hash, const char *key, void *value, __UNUSED__ void *data)
530 {
531     printf("%s -> %p\n", (char *)key, value);
532 }
533 #endif
534
535 static int
536 efreet_util_cache_fill(__UNUSED__ void *data)
537 {
538     Efreet_Cache_Fill_Dir *dir;
539     struct dirent *file = NULL;
540     double start;
541     char buf[PATH_MAX];
542
543     if ((!fill->dirs) && (!fill->files))
544     {
545         free(fill);
546         idler = NULL;
547         fill = NULL;
548         efreet_cache_clear();
549         ecore_event_add(EFREET_EVENT_DESKTOP_LIST_CHANGE, NULL, NULL, NULL);
550
551         return 0;
552     }
553     if (!fill->current)
554     {
555         fill->current = eina_list_data_get(fill->dirs);
556         fill->dirs = eina_list_remove_list(fill->dirs, fill->dirs);
557         if (!fill->current)
558         {
559             while (fill->dirs)
560             {
561                 dir = eina_list_data_get(fill->dirs);
562                 efreet_util_cache_dir_free(dir);
563                 fill->dirs = eina_list_remove_list(fill->dirs, fill->dirs);
564             }
565
566             free(fill);
567             idler = NULL;
568             fill = NULL;
569 #if 0
570             eina_hash_foreach(desktop_by_file_id, dump, NULL);
571             eina_hash_foreach(file_id_by_desktop_path, dump, NULL);
572             printf("%d\n", eina_hash_population(desktop_by_file_id));
573 #endif
574             efreet_cache_clear();
575             ecore_event_add(EFREET_EVENT_DESKTOP_LIST_CHANGE, NULL, NULL, NULL);
576
577             return 0;
578         }
579     }
580
581     start = ecore_time_get();
582     if (!fill->files)
583     {
584         fill->files = opendir(fill->current->path);
585     }
586     if (!fill->files)
587     {
588         /* Couldn't open this dir, continue to next */
589         efreet_util_cache_dir_free(fill->current);
590         fill->current = NULL;
591     }
592     else
593     {
594         do
595         {
596             char file_id[PATH_MAX];
597
598             file = readdir(fill->files);
599             if (!file) break;
600             if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
601
602             snprintf(buf, PATH_MAX, "%s/%s", fill->current->path, file->d_name);
603             if (fill->current->file_id)
604                 snprintf(file_id, PATH_MAX, "%s-%s", fill->current->file_id, file->d_name);
605             else
606                 strcpy(file_id, file->d_name);
607
608             if (ecore_file_is_dir(buf))
609             {
610                 Efreet_Cache_Fill_Dir *dir;
611
612                 dir = NEW(Efreet_Cache_Fill_Dir, 1);
613                 dir->path = strdup(buf);
614                 dir->file_id = strdup(file_id);
615                 dir->priority = fill->current->priority;
616                 fill->dirs = eina_list_append(fill->dirs, dir);
617             }
618             else
619                 efreet_util_cache_add(buf, file_id, fill->current->priority, 0);
620         } while ((ecore_time_get() - start) < 0.01);
621
622         if (!file)
623         {
624             /* This dir has been search through */
625             efreet_util_monitor(fill->current->path, fill->current->file_id,
626                                 fill->current->priority);
627             efreet_util_cache_dir_free(fill->current);
628             fill->current = NULL;
629             closedir(fill->files);
630             fill->files = NULL;
631         }
632     }
633
634     return 1;
635 }
636
637 static void
638 efreet_util_cache_add(const char *path, const char *file_id, int priority, int event)
639 {
640     Efreet_Desktop *desktop;
641     Efreet_Util_Desktop *ud;
642     char *ext;
643
644     ext = strrchr(path, '.');
645     if (!ext || strcmp(ext, ".desktop")) return;
646     desktop = efreet_desktop_get(path);
647
648     if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
649     {
650         if (desktop) efreet_desktop_free(desktop);
651         return;
652     }
653
654     if (!eina_hash_find(file_id_by_desktop_path, desktop->orig_path))
655       {
656          eina_hash_add(file_id_by_desktop_path,
657                desktop->orig_path,
658                (void *)eina_stringshare_add(file_id));
659
660       }
661
662     ud = eina_hash_find(desktop_by_file_id, file_id);
663     if (!ud)
664     {
665         ud = NEW(Efreet_Util_Desktop, 1);
666         ud->priority = priority;
667         ud->desktop = desktop;
668
669         eina_hash_add(desktop_by_file_id, file_id, ud);
670         efreet_util_desktops_by_category_add(desktop);
671
672         if (event)
673         {
674             Efreet_Event_Desktop_Change *ev;
675
676             ev = NEW(Efreet_Event_Desktop_Change, 1);
677             ev->current = desktop;
678             efreet_desktop_ref(ev->current);
679             ev->change = EFREET_DESKTOP_CHANGE_ADD;
680             efreet_cache_clear();
681             ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
682         }
683     }
684     else if (priority < ud->priority)
685     {
686         efreet_util_desktops_by_category_remove(ud->desktop);
687         efreet_util_desktops_by_category_add(desktop);
688
689         if (event)
690         {
691             Efreet_Event_Desktop_Change *ev;
692
693             ev = NEW(Efreet_Event_Desktop_Change, 1);
694             ev->current = desktop;
695             efreet_desktop_ref(ev->current);
696             ev->previous = ud->desktop;
697             efreet_desktop_ref(ev->previous);
698             ev->change = EFREET_DESKTOP_CHANGE_UPDATE;
699             efreet_desktop_free(ud->desktop);
700             ud->desktop = desktop;
701             efreet_cache_clear();
702             ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
703         }
704         else
705         {
706             efreet_desktop_free(ud->desktop);
707             ud->desktop = desktop;
708         }
709     }
710     else
711         efreet_desktop_free(desktop);
712 }
713
714 static void
715 efreet_util_cache_remove(const char *path, const char *file_id, int priority)
716 {
717     Efreet_Util_Desktop *ud;
718     char *ext;
719
720     ext = strrchr(path, '.');
721     if (!ext || strcmp(ext, ".desktop")) return;
722     ud = eina_hash_find(desktop_by_file_id, file_id);
723     if (ud && (ud->priority >= priority))
724     {
725         Efreet_Event_Desktop_Change *ev;
726
727         efreet_util_desktops_by_category_remove(ud->desktop);
728
729         ev = NEW(Efreet_Event_Desktop_Change, 1);
730         ev->current = ud->desktop;
731         efreet_desktop_ref(ev->current);
732         ev->change = EFREET_DESKTOP_CHANGE_REMOVE;
733         efreet_cache_clear();
734         ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
735
736         eina_hash_del(desktop_by_file_id, file_id, ud);
737
738         /* This call will search application dirs and add the file to cache if it
739          * exists. */
740         efreet_util_desktop_file_id_find(file_id);
741     }
742     eina_hash_del(file_id_by_desktop_path, path, eina_hash_find(file_id_by_desktop_path, path));
743 }
744
745 static void
746 efreet_util_cache_reload(const char *path, const char *file_id, int priority)
747 {
748     Efreet_Desktop *desktop;
749     Efreet_Util_Desktop *ud;
750     char *ext;
751
752     ext = strrchr(path, '.');
753     if (!ext || strcmp(ext, ".desktop")) return;
754     desktop = efreet_desktop_get(path);
755
756     if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
757     {
758         if (desktop) efreet_desktop_free(desktop);
759         return;
760     }
761
762     ud = eina_hash_find(desktop_by_file_id, file_id);
763     if (ud)
764     {
765         Efreet_Event_Desktop_Change *ev;
766
767         if (ud->priority < priority)
768         {
769             efreet_desktop_free(desktop);
770             return;
771         }
772
773         efreet_util_desktops_by_category_remove(ud->desktop);
774         efreet_util_desktops_by_category_add(desktop);
775
776         ev = NEW(Efreet_Event_Desktop_Change, 1);
777         ev->current = desktop;
778         efreet_desktop_ref(ev->current);
779         ev->previous = ud->desktop;
780         efreet_desktop_ref(ev->previous);
781         ev->change = EFREET_DESKTOP_CHANGE_UPDATE;
782         efreet_cache_clear();
783         ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
784
785         efreet_desktop_free(ud->desktop);
786         ud->desktop = desktop;
787     }
788     else
789     {
790         Efreet_Event_Desktop_Change *ev;
791
792         ud = NEW(Efreet_Util_Desktop, 1);
793         ud->priority = priority;
794         ud->desktop = desktop;
795
796         eina_hash_add(desktop_by_file_id, file_id, ud);
797         efreet_util_desktops_by_category_remove(ud->desktop);
798         efreet_util_desktops_by_category_add(desktop);
799
800         ev = NEW(Efreet_Event_Desktop_Change, 1);
801         ev->current = desktop;
802         efreet_desktop_ref(ev->current);
803         ev->change = EFREET_DESKTOP_CHANGE_ADD;
804         efreet_cache_clear();
805         ecore_event_add(EFREET_EVENT_DESKTOP_CHANGE, ev, efreet_event_desktop_change_free, NULL);
806     }
807 }
808
809 static void
810 efreet_util_cache_dir_free(void *data)
811 {
812     Efreet_Cache_Fill_Dir *dir;
813
814     dir = data;
815     IF_FREE(dir->path);
816     IF_FREE(dir->file_id);
817     free(dir);
818 }
819
820 static Eina_Bool
821 efreet_util_cache_search_mime(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
822 {
823     Efreet_Cache_Search_List *search;
824     Efreet_Util_Desktop      *ud;
825     Eina_List                *l;
826     const char               *mime;
827
828     search = fdata;
829     ud = value;
830
831     if (!ud->desktop->mime_types) return EINA_TRUE;
832     EINA_LIST_FOREACH(ud->desktop->mime_types, l, mime)
833       if (search->what == mime)
834         {
835             efreet_desktop_ref(ud->desktop);
836             search->list = eina_list_append(search->list, ud->desktop);
837             break;
838         }
839     return EINA_TRUE;
840 }
841
842 static Eina_Bool
843 efreet_util_cache_search_wm_class(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
844 {
845     Efreet_Cache_Search *search;
846     Efreet_Util_Desktop *ud;
847
848     ud = value;
849     search = fdata;
850
851     if (!ud->desktop) return EINA_TRUE;
852     if (!ud->desktop->startup_wm_class) return EINA_TRUE;
853     if ((search->what2) && (!strcmp(ud->desktop->startup_wm_class, search->what2)))
854       {
855           search->result = ud;
856           return EINA_FALSE;
857       }
858     else if ((search->what1) && (!strcmp(ud->desktop->startup_wm_class, search->what1)))
859       {
860           search->result = ud;
861           return EINA_FALSE;
862       }
863     return EINA_TRUE;
864 }
865
866 static Eina_Bool
867 efreet_util_cache_search_exec(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
868 {
869     Efreet_Cache_Search *search;
870     Efreet_Util_Desktop *ud;
871     char                      *exec;
872     const char                *file;
873
874     ud = value;
875     search = fdata;
876
877     if (!ud->desktop->exec) return EINA_TRUE;
878     exec = ecore_file_app_exe_get(ud->desktop->exec);
879     if (!exec) return EINA_TRUE;
880     if (!strcmp(exec, search->what1))
881     {
882        free(exec);
883        search->result = ud;
884        return EINA_FALSE;
885     }
886
887     file = ecore_file_file_get(exec);
888     if (file && !strcmp(file, search->what1))
889     {
890        free(exec);
891        search->result = ud;
892        return EINA_FALSE;
893     }
894     free(exec);
895     return EINA_TRUE;
896 }
897
898 static Eina_Bool
899 efreet_util_cache_search_name(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
900 {
901     Efreet_Cache_Search *search;
902     Efreet_Util_Desktop *ud;
903
904     ud = value;
905     search = fdata;
906
907     if (!ud->desktop->name) return EINA_TRUE;
908     if (!strcmp(ud->desktop->name, search->what1))
909       {
910          search->result = ud;
911          return EINA_FALSE;
912       }
913     return EINA_TRUE;
914 }
915
916 static Eina_Bool
917 efreet_util_cache_search_generic_name(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
918 {
919     Efreet_Cache_Search *search;
920     Efreet_Util_Desktop *ud;
921
922     ud = value;
923     search = fdata;
924
925     if (!ud->desktop->generic_name) return EINA_TRUE;
926     if (!strcmp(ud->desktop->generic_name, search->what1))
927       {
928          search->result = ud;
929          return EINA_FALSE;
930       }
931     return EINA_TRUE;
932 }
933
934 static Eina_Bool
935 efreet_util_cache_search_name_glob(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
936 {
937     Efreet_Cache_Search_List *search;
938     Efreet_Util_Desktop      *ud;
939
940     search = fdata;
941     ud = value;
942
943     if (!ud->desktop) return EINA_TRUE;
944     if (efreet_util_glob_match(ud->desktop->name, search->what))
945       {
946          efreet_desktop_ref(ud->desktop);
947          search->list = eina_list_append(search->list, ud->desktop);
948       }
949     return EINA_TRUE;
950 }
951
952 static Eina_Bool
953 efreet_util_cache_search_exec_glob(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
954 {
955     Efreet_Cache_Search_List *search;
956     Efreet_Util_Desktop      *ud;
957     char                     *exec;
958
959     search = fdata;
960     ud = value;
961
962     if (!ud->desktop->exec) return EINA_TRUE;
963     exec = ecore_file_app_exe_get(ud->desktop->exec);
964     if (!exec) return EINA_TRUE;
965
966         if (efreet_util_glob_match(exec, search->what))
967       {
968          efreet_desktop_ref(ud->desktop);
969          search->list = eina_list_append(search->list, ud->desktop);
970     }
971     free(exec);
972     return EINA_TRUE;
973 }
974
975 static Eina_Bool
976 efreet_util_cache_search_generic_name_glob(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
977 {
978     Efreet_Cache_Search_List *search;
979     Efreet_Util_Desktop      *ud;
980
981     search = fdata;
982     ud = value;
983
984     if (efreet_util_glob_match(ud->desktop->generic_name, search->what))
985       {
986          efreet_desktop_ref(ud->desktop);
987          search->list = eina_list_append(search->list, ud->desktop);
988       }
989     return EINA_TRUE;
990 }
991
992 static Eina_Bool
993 efreet_util_cache_search_comment_glob(__UNUSED__ const Eina_Hash *hash, void *value, void *fdata)
994 {
995     Efreet_Cache_Search_List *search;
996     Efreet_Util_Desktop      *ud;
997
998     search = fdata;
999     ud = value;
1000
1001     if (efreet_util_glob_match(ud->desktop->comment, search->what))
1002       {
1003          efreet_desktop_ref(ud->desktop);
1004          search->list = eina_list_append(search->list, ud->desktop);
1005       }
1006     return EINA_TRUE;
1007 }
1008
1009 static int
1010 efreet_util_glob_match(const char *str, const char *glob)
1011 {
1012     if (!str || !glob)
1013         return 0;
1014     if (glob[0] == '\0')
1015     {
1016         if (str[0] == '\0') return 1;
1017         return 0;
1018     }
1019     if (!strcmp(glob, "*")) return 1;
1020     if (!fnmatch(glob, str, 0)) return 1;
1021     return 0;
1022 }
1023
1024 static void
1025 efreet_util_monitor(const char *path, const char *file_id, int priority)
1026 {
1027     Efreet_Monitor *em;
1028
1029     em = NEW(Efreet_Monitor, 1);
1030     em->monitor = ecore_file_monitor_add(path, efreet_util_monitor_cb, em);
1031     if (file_id) em->file_id = strdup(file_id);
1032     em->priority = priority;
1033     monitors = eina_list_append(monitors, em);
1034 }
1035
1036 static void
1037 efreet_util_monitor_cb(void *data, Ecore_File_Monitor *monitor __UNUSED__,
1038                         Ecore_File_Event event, const char *path)
1039 {
1040     Efreet_Monitor *em;
1041     char file_id[PATH_MAX];
1042
1043     em = data;
1044     if (em->file_id)
1045         snprintf(file_id, sizeof(file_id), "%s-%s", em->file_id, ecore_file_file_get(path));
1046     else
1047         strcpy(file_id, ecore_file_file_get(path));
1048     switch (event)
1049     {
1050         case ECORE_FILE_EVENT_NONE:
1051             /* Ignore */
1052             break;
1053         case ECORE_FILE_EVENT_CREATED_FILE:
1054             efreet_util_cache_add(path, file_id, em->priority, 1);
1055             break;
1056         case ECORE_FILE_EVENT_CREATED_DIRECTORY:
1057             {
1058                 Efreet_Cache_Fill_Dir *dir;
1059
1060                 if (!fill)
1061                 {
1062                     fill = NEW(Efreet_Cache_Fill, 1);
1063                 }
1064
1065                 dir = NEW(Efreet_Cache_Fill_Dir, 1);
1066                 dir->path = strdup(path);
1067                 dir->file_id = strdup(file_id);
1068                 dir->priority = em->priority;
1069                 fill->dirs = eina_list_append(fill->dirs, dir);
1070
1071                 if (!idler)
1072                     idler = ecore_idler_add(efreet_util_cache_fill, NULL);
1073             }
1074             break;
1075         case ECORE_FILE_EVENT_DELETED_FILE:
1076             efreet_util_cache_remove(path, file_id, em->priority);
1077             break;
1078         case ECORE_FILE_EVENT_DELETED_DIRECTORY:
1079             /* Ignore, we should already have a monitor on any subdir */
1080             break;
1081         case ECORE_FILE_EVENT_DELETED_SELF:
1082             if (eina_list_data_find(monitors, em))
1083                 eina_list_remove(monitors, em);
1084             efreet_util_monitor_free(em);
1085             break;
1086         case ECORE_FILE_EVENT_MODIFIED:
1087             efreet_util_cache_reload(path, file_id, em->priority);
1088             break;
1089     }
1090 }
1091
1092 static void
1093 efreet_util_monitor_free(void *data)
1094 {
1095     Efreet_Monitor *em;
1096
1097     em = data;
1098     if (em->monitor) ecore_file_monitor_del(em->monitor);
1099     IF_FREE(em->file_id);
1100     free(em);
1101 }
1102
1103 /**
1104  * Returns a list of .menu files found in the various config dirs.
1105  * @return An ecore list of menu file paths (const char *). This must be freed with ecore_list_destroy().
1106  */
1107 EAPI Eina_List *
1108 efreet_util_menus_find(void)
1109 {
1110     Eina_List *menus = NULL;
1111     Eina_List *dirs, *l;
1112     const char *dir;
1113
1114     efreet_util_menus_find_helper(menus, efreet_config_home_get());
1115
1116     dirs = efreet_config_dirs_get();
1117     EINA_LIST_FOREACH(dirs, l, dir)
1118         efreet_util_menus_find_helper(menus, dir);
1119
1120     return menus;
1121 }
1122
1123 static void
1124 efreet_util_menus_find_helper(Eina_List *menus, const char *config_dir)
1125 {
1126     DIR *files = NULL;
1127     struct dirent *file = NULL;
1128     char dbuf[PATH_MAX], fbuf[PATH_MAX];
1129
1130     snprintf(dbuf, sizeof(dbuf), "%s/menus", config_dir);
1131     files = opendir(dbuf);
1132     if (!files) return;
1133     while ((file = readdir(files))) {
1134         const char *exten;
1135         exten = strrchr(file->d_name, '.');
1136         if (!exten) continue;
1137         if (strcmp(".menu", exten)) continue;
1138
1139         snprintf(fbuf, PATH_MAX, "%s/%s", dbuf, file->d_name);
1140         if (ecore_file_is_dir(fbuf)) continue;
1141
1142         menus = eina_list_append(menus, strdup(fbuf));
1143     }
1144     closedir(files);
1145 }
1146
1147 static void
1148 efreet_util_desktops_by_category_add(Efreet_Desktop *desktop)
1149 {
1150    Eina_List *l;
1151    const char *category;
1152    
1153    if (!desktop->categories) return;
1154    
1155    EINA_LIST_FOREACH(desktop->categories, l, category)
1156      {
1157         Eina_List *list;
1158         int newlist = 0;
1159         list = eina_hash_find(desktops_by_category, category);
1160         if (!list) newlist = 1;
1161         if (!eina_list_data_find(list, desktop))
1162           list = eina_list_append(list, desktop);
1163         if (newlist)
1164           eina_hash_add(desktops_by_category, category, list);
1165         else
1166           eina_hash_modify(desktops_by_category, category, list);
1167      }
1168 }
1169
1170 static void
1171 efreet_util_desktops_by_category_remove(Efreet_Desktop *desktop)
1172 {
1173    Eina_List *l;
1174    const char *category;
1175    
1176    if (!desktop->categories) return;
1177    
1178    EINA_LIST_FOREACH(desktop->categories, l, category)
1179      {
1180         Eina_List *list;
1181         list = eina_hash_find(desktops_by_category, category);
1182         if (!list) continue;
1183         if (eina_list_data_find(list, desktop))
1184           list = eina_list_remove(list, desktop);
1185         if (!list)
1186           eina_hash_del(desktops_by_category, category, list);
1187         else
1188           eina_hash_modify(desktops_by_category, category, list);
1189      }
1190 }
1191
1192 static void
1193 efreet_util_desktop_free(Efreet_Util_Desktop *ud)
1194 {
1195     if (!ud) return;
1196
1197     efreet_desktop_free(ud->desktop);
1198     free(ud);
1199 }
1200
1201 static void
1202 efreet_event_desktop_change_free(void *data __UNUSED__, void *ev)
1203 {
1204     Efreet_Event_Desktop_Change *event;
1205
1206     event = ev;
1207     if (event->current) efreet_desktop_free(event->current);
1208     if (event->previous) efreet_desktop_free(event->previous);
1209
1210     free(event);
1211 }