efreet: Improve language reset
[framework/uifw/efreet.git] / src / lib / efreet_cache.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 /* TODO: Consider flushing local icons cache after idling.
6  *       Icon requests will probably come in batches, f.ex. during menu
7  *       browsing.
8  */
9
10 #include <libgen.h>
11 #include <unistd.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14
15 #include <Eet.h>
16 #include <Ecore.h>
17 #include <Ecore_File.h>
18
19 /* define macros and variable for using the eina logging system  */
20 #define EFREET_MODULE_LOG_DOM _efreet_cache_log_dom
21 static int _efreet_cache_log_dom = -1;
22
23 #include "Efreet.h"
24 #include "efreet_private.h"
25 #include "efreet_cache_private.h"
26
27 #define NON_EXISTING (void *)-1
28
29 typedef struct _Efreet_Old_Cache Efreet_Old_Cache;
30
31 struct _Efreet_Old_Cache
32 {
33     Eina_Hash *hash;
34     Eet_File *ef;
35 };
36
37 /**
38  * Data for cache files
39  */
40 static Eet_Data_Descriptor *directory_edd = NULL;
41 static Eet_Data_Descriptor *icon_theme_edd = NULL;
42 static Eet_Data_Descriptor *icon_theme_directory_edd = NULL;
43
44 static Eet_Data_Descriptor *icon_fallback_edd = NULL;
45 static Eet_Data_Descriptor *icon_element_pointer_edd = NULL;
46 static Eet_Data_Descriptor *icon_element_edd = NULL;
47 static Eet_Data_Descriptor *icon_edd = NULL;
48
49 static Eet_File            *icon_cache = NULL;
50 static Eet_File            *fallback_cache = NULL;
51 static Eet_File            *icon_theme_cache = NULL;
52
53 static Eina_Hash           *themes = NULL;
54 static Eina_Hash           *icons = NULL;
55 static Eina_Hash           *fallbacks = NULL;
56
57 static const char          *icon_theme_cache_file = NULL;
58
59 static const char          *theme_name = NULL;
60
61 static Eet_Data_Descriptor *version_edd = NULL;
62 static Eet_Data_Descriptor *desktop_edd = NULL;
63 static Eet_Data_Descriptor *hash_array_string_edd = NULL;
64 static Eet_Data_Descriptor *array_string_edd = NULL;
65 static Eet_Data_Descriptor *hash_string_edd = NULL;
66
67 static Eina_Hash           *desktops = NULL;
68 static Eina_List           *desktop_dirs_add = NULL;
69 static Eet_File            *desktop_cache = NULL;
70 static const char          *desktop_cache_file = NULL;
71
72 static Ecore_File_Monitor  *cache_monitor = NULL;
73
74 static Ecore_Event_Handler *cache_exe_handler = NULL;
75 static Ecore_Timer         *icon_cache_timer = NULL;
76 static Ecore_Exe           *icon_cache_exe = NULL;
77 static int                  icon_cache_exe_lock = -1;
78 static Ecore_Timer         *desktop_cache_timer = NULL;
79 static Ecore_Exe           *desktop_cache_exe = NULL;
80 static int                  desktop_cache_exe_lock = -1;
81
82 static Eina_List           *old_desktop_caches = NULL;
83
84 static const char                *util_cache_file = NULL;
85 static Eet_File                  *util_cache = NULL;
86 static Efreet_Cache_Hash         *util_cache_hash = NULL;
87 static const char                *util_cache_hash_key = NULL;
88 static Efreet_Cache_Array_String *util_cache_names = NULL;
89 static const char                *util_cache_names_key = NULL;
90
91 static void efreet_cache_edd_shutdown(void);
92 static void efreet_cache_icon_free(Efreet_Cache_Icon *icon);
93 static void efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon);
94 static void efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme);
95
96 static Eina_Bool efreet_cache_check(Eet_File **ef, const char *path, int major);
97 static void *efreet_cache_close(Eet_File *ef);
98
99 static Eina_Bool cache_exe_cb(void *data, int type, void *event);
100 static Eina_Bool cache_check_change(const char *path);
101 static void cache_update_cb(void *data, Ecore_File_Monitor *em,
102                             Ecore_File_Event event, const char *path);
103
104 static Eina_Bool desktop_cache_update_cache_cb(void *data);
105 static Eina_Bool icon_cache_update_cache_cb(void *data);
106 static void desktop_cache_update_free(void *data, void *ev);
107 static void icon_cache_update_free(void *data, void *ev);
108
109 static void *hash_array_string_add(void *hash, const char *key, void *data);
110
111 EAPI int EFREET_EVENT_ICON_CACHE_UPDATE = 0;
112 EAPI int EFREET_EVENT_DESKTOP_CACHE_UPDATE = 0;
113 EAPI int EFREET_EVENT_DESKTOP_CACHE_BUILD = 0;
114
115 int
116 efreet_cache_init(void)
117 {
118     char buf[PATH_MAX];
119
120     _efreet_cache_log_dom = eina_log_domain_register("efreet_cache", EFREET_DEFAULT_LOG_COLOR);
121     if (_efreet_cache_log_dom < 0)
122         return 0;
123
124     EFREET_EVENT_ICON_CACHE_UPDATE = ecore_event_type_new();
125     EFREET_EVENT_DESKTOP_CACHE_UPDATE = ecore_event_type_new();
126     EFREET_EVENT_DESKTOP_CACHE_BUILD = ecore_event_type_new();
127
128     themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
129     icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
130     fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
131     desktops = eina_hash_string_superfast_new(NULL);
132
133     if (efreet_cache_update)
134     {
135         snprintf(buf, sizeof(buf), "%s/efreet", efreet_cache_home_get());
136         if (!ecore_file_exists(buf))
137         {
138             if (!ecore_file_mkpath(buf))
139             {
140                 ERR("Failed to create directory '%s'", buf);
141                 goto error;
142             }
143             efreet_setowner(buf);
144         }
145
146         cache_exe_handler = ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
147                                                     cache_exe_cb, NULL);
148         if (!cache_exe_handler)
149         {
150             ERR("Failed to add exe del handler");
151             goto error;
152         }
153
154         cache_monitor = ecore_file_monitor_add(buf,
155                                                cache_update_cb,
156                                                NULL);
157         if (!cache_monitor)
158         {
159             ERR("Failed to set up ecore file monitor for '%s'", buf);
160             goto error;
161         }
162
163         efreet_cache_icon_update();
164         efreet_cache_desktop_update();
165     }
166
167     return 1;
168 error:
169     if (themes) eina_hash_free(themes);
170     themes = NULL;
171     if (icons) eina_hash_free(icons);
172     icons = NULL;
173     if (fallbacks) eina_hash_free(fallbacks);
174     fallbacks = NULL;
175     if (desktops) eina_hash_free(desktops);
176     desktops = NULL;
177
178     if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler);
179     cache_exe_handler = NULL;
180     if (cache_monitor) ecore_file_monitor_del(cache_monitor);
181     cache_monitor = NULL;
182     efreet_cache_edd_shutdown();
183     return 0;
184 }
185
186 void
187 efreet_cache_shutdown(void)
188 {
189     Efreet_Old_Cache *d;
190     void *data;
191
192     IF_RELEASE(theme_name);
193
194     icon_cache = efreet_cache_close(icon_cache);
195     icon_theme_cache = efreet_cache_close(icon_theme_cache);
196
197     IF_FREE_HASH(themes);
198     IF_FREE_HASH(icons);
199     IF_FREE_HASH(fallbacks);
200
201     IF_FREE_HASH_CB(desktops, EINA_FREE_CB(efreet_cache_desktop_free));
202     EINA_LIST_FREE(desktop_dirs_add, data)
203         eina_stringshare_del(data);
204     desktop_cache = efreet_cache_close(desktop_cache);
205     IF_RELEASE(desktop_cache_file);
206
207     if (cache_exe_handler) ecore_event_handler_del(cache_exe_handler);
208     cache_exe_handler = NULL;
209     if (cache_monitor) ecore_file_monitor_del(cache_monitor);
210     cache_monitor = NULL;
211
212     efreet_cache_edd_shutdown();
213     if (desktop_cache_timer)
214     {
215         ecore_timer_del(desktop_cache_timer);
216         desktop_cache_timer = NULL;
217     }
218     IF_RELEASE(icon_theme_cache_file);
219     if (icon_cache_exe_lock > 0)
220     {
221         close(icon_cache_exe_lock);
222         icon_cache_exe_lock = -1;
223     }
224
225     if (desktop_cache_exe_lock > 0)
226     {
227         close(desktop_cache_exe_lock);
228         desktop_cache_exe_lock = -1;
229     }
230
231     if (old_desktop_caches)
232         ERR("This application has not properly closed all its desktop references!");
233     EINA_LIST_FREE(old_desktop_caches, d)
234     {
235         eina_hash_free(d->hash);
236         eet_close(d->ef);
237         free(d);
238     }
239
240     IF_RELEASE(util_cache_names_key);
241     efreet_cache_array_string_free(util_cache_names);
242     util_cache_names = NULL;
243
244     IF_RELEASE(util_cache_hash_key);
245     if (util_cache_hash)
246     {
247         eina_hash_free(util_cache_hash->hash);
248         free(util_cache_hash);
249         util_cache_hash = NULL;
250     }
251
252     util_cache = efreet_cache_close(util_cache);
253     IF_RELEASE(util_cache_file);
254
255     eina_log_domain_unregister(_efreet_cache_log_dom);
256     _efreet_cache_log_dom = -1;
257 }
258
259 /*
260  * Needs EAPI because of helper binaries
261  */
262 EAPI const char *
263 efreet_icon_cache_file(const char *theme)
264 {
265     static char cache_file[PATH_MAX] = { '\0' };
266     const char *cache;
267
268     EINA_SAFETY_ON_NULL_RETURN_VAL(theme, NULL);
269
270     cache = efreet_cache_home_get();
271
272     snprintf(cache_file, sizeof(cache_file), "%s/efreet/icons_%s_%s.eet", cache, theme, efreet_hostname_get());
273
274     return cache_file;
275 }
276
277 /*
278  * Needs EAPI because of helper binaries
279  */
280 EAPI const char *
281 efreet_icon_theme_cache_file(void)
282 {
283     char tmp[PATH_MAX] = { '\0' };
284
285     if (icon_theme_cache_file) return icon_theme_cache_file;
286
287     snprintf(tmp, sizeof(tmp), "%s/efreet/icon_themes_%s.eet",
288              efreet_cache_home_get(), efreet_hostname_get());
289     icon_theme_cache_file = eina_stringshare_add(tmp);
290
291     return icon_theme_cache_file;
292 }
293
294 /*
295  * Needs EAPI because of helper binaries
296  */
297 EAPI const char *
298 efreet_desktop_util_cache_file(void)
299 {
300     char tmp[PATH_MAX] = { '\0' };
301     const char *cache_dir, *lang, *country, *modifier;
302
303     if (util_cache_file) return util_cache_file;
304
305     cache_dir = efreet_cache_home_get();
306     lang = efreet_lang_get();
307     country = efreet_lang_country_get();
308     modifier = efreet_lang_modifier_get();
309
310     if (lang && country && modifier)
311         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s@%s.eet", cache_dir, efreet_hostname_get(), lang, country, modifier);
312     else if (lang && country)
313         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s_%s.eet", cache_dir, efreet_hostname_get(), lang, country);
314     else if (lang)
315         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s_%s.eet", cache_dir, efreet_hostname_get(), lang);
316     else
317         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_util_%s.eet", cache_dir, efreet_hostname_get());
318
319     util_cache_file = eina_stringshare_add(tmp);
320     return util_cache_file;
321 }
322
323 /*
324  * Needs EAPI because of helper binaries
325  */
326 EAPI Eet_Data_Descriptor *
327 efreet_version_edd(void)
328 {
329     Eet_Data_Descriptor_Class eddc;
330
331     if (version_edd) return version_edd;
332
333     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Version);
334     version_edd = eet_data_descriptor_file_new(&eddc);
335     if (!version_edd) return NULL;
336
337     EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
338                                   "minor", minor, EET_T_UCHAR);
339     EET_DATA_DESCRIPTOR_ADD_BASIC(version_edd, Efreet_Cache_Version,
340                                   "major", major, EET_T_UCHAR);
341
342     return version_edd;
343 }
344
345 /*
346  * Needs EAPI because of helper binaries
347  */
348 EAPI Eet_Data_Descriptor *
349 efreet_hash_array_string_edd(void)
350 {
351     Eet_Data_Descriptor_Class eddc;
352
353     if (hash_array_string_edd) return hash_array_string_edd;
354
355     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
356     eddc.func.hash_add = hash_array_string_add;
357     hash_array_string_edd = eet_data_descriptor_file_new(&eddc);
358     if (!hash_array_string_edd) return NULL;
359
360     EET_DATA_DESCRIPTOR_ADD_HASH(hash_array_string_edd, Efreet_Cache_Hash,
361                                   "hash", hash, efreet_array_string_edd());
362
363     return hash_array_string_edd;
364 }
365
366 /*
367  * Needs EAPI because of helper binaries
368  */
369 EAPI Eet_Data_Descriptor *
370 efreet_hash_string_edd(void)
371 {
372     Eet_Data_Descriptor_Class eddc;
373
374     if (hash_string_edd) return hash_string_edd;
375
376     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Hash);
377     hash_string_edd = eet_data_descriptor_file_new(&eddc);
378     if (!hash_string_edd) return NULL;
379
380     EET_DATA_DESCRIPTOR_ADD_HASH_STRING(hash_string_edd, Efreet_Cache_Hash,
381                                   "hash", hash);
382
383     return hash_string_edd;
384 }
385
386 /*
387  * Needs EAPI because of helper binaries
388  */
389 EAPI Eet_Data_Descriptor *
390 efreet_array_string_edd(void)
391 {
392     Eet_Data_Descriptor_Class eddc;
393
394     if (array_string_edd) return array_string_edd;
395
396     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Array_String);
397     array_string_edd = eet_data_descriptor_file_new(&eddc);
398     if (!array_string_edd) return NULL;
399     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(array_string_edd, Efreet_Cache_Array_String,
400                                              "array", array);
401
402     return array_string_edd;
403 }
404
405 /*
406  * Needs EAPI because of helper binaries
407  */
408 EAPI const char *
409 efreet_desktop_cache_file(void)
410 {
411     char tmp[PATH_MAX] = { '\0' };
412     const char *cache, *lang, *country, *modifier;
413
414     if (desktop_cache_file) return desktop_cache_file;
415
416     cache = efreet_cache_home_get();
417     lang = efreet_lang_get();
418     country = efreet_lang_country_get();
419     modifier = efreet_lang_modifier_get();
420
421     if (lang && country && modifier)
422         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s@%s.eet", cache, efreet_hostname_get(), lang, country, modifier);
423     else if (lang && country)
424         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s_%s.eet", cache, efreet_hostname_get(), lang, country);
425     else if (lang)
426         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s_%s.eet", cache, efreet_hostname_get(), lang);
427     else
428         snprintf(tmp, sizeof(tmp), "%s/efreet/desktop_%s.eet", cache, efreet_hostname_get());
429
430     desktop_cache_file = eina_stringshare_add(tmp);
431     return desktop_cache_file;
432 }
433
434 #define EDD_SHUTDOWN(Edd)                       \
435     if (Edd) eet_data_descriptor_free(Edd);       \
436 Edd = NULL;
437
438 static void
439 efreet_cache_edd_shutdown(void)
440 {
441     EDD_SHUTDOWN(version_edd);
442     EDD_SHUTDOWN(desktop_edd);
443     EDD_SHUTDOWN(hash_array_string_edd);
444     EDD_SHUTDOWN(array_string_edd);
445     EDD_SHUTDOWN(hash_string_edd);
446     EDD_SHUTDOWN(icon_theme_edd);
447     EDD_SHUTDOWN(icon_theme_directory_edd);
448     EDD_SHUTDOWN(directory_edd);
449     EDD_SHUTDOWN(icon_fallback_edd);
450     EDD_SHUTDOWN(icon_element_pointer_edd);
451     EDD_SHUTDOWN(icon_element_edd);
452     EDD_SHUTDOWN(icon_edd);
453 }
454
455 #define EFREET_POINTER_TYPE(Edd_Dest, Edd_Source, Type)   \
456 {                                                                     \
457     typedef struct _Efreet_##Type##_Pointer Efreet_##Type##_Pointer;   \
458     struct _Efreet_##Type##_Pointer                                    \
459     {                                                                  \
460         Efreet_##Type *pointer;                                         \
461     };                                                                 \
462     \
463     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_##Type##_Pointer); \
464     Edd_Dest = eet_data_descriptor_file_new(&eddc);                    \
465     EET_DATA_DESCRIPTOR_ADD_SUB(Edd_Dest, Efreet_##Type##_Pointer,     \
466                                 "pointer", pointer, Edd_Source);       \
467 }
468
469 static Eet_Data_Descriptor *
470 efreet_icon_directory_edd(void)
471 {
472     Eet_Data_Descriptor_Class eddc;
473
474     if (directory_edd) return directory_edd;
475
476     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Directory);
477     directory_edd = eet_data_descriptor_file_new(&eddc);
478     if (!directory_edd) return NULL;
479
480     EET_DATA_DESCRIPTOR_ADD_BASIC(directory_edd, Efreet_Cache_Directory,
481                                   "modified_time", modified_time, EET_T_LONG_LONG);
482
483     return directory_edd;
484 }
485
486 /*
487  * Needs EAPI because of helper binaries
488  */
489 EAPI Eet_Data_Descriptor *
490 efreet_icon_edd(void)
491 {
492     Eet_Data_Descriptor_Class eddc;
493
494     if (icon_edd) return icon_edd;
495
496     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Element);
497     icon_element_edd = eet_data_descriptor_file_new(&eddc);
498     if (!icon_element_edd) return NULL;
499
500     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
501                                   "type", type, EET_T_USHORT);
502     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
503                                   "normal", normal, EET_T_USHORT);
504     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
505                                   "normal", normal, EET_T_USHORT);
506     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
507                                   "min", min, EET_T_USHORT);
508     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_element_edd, Efreet_Cache_Icon_Element,
509                                   "max", max, EET_T_USHORT);
510     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_element_edd, Efreet_Cache_Icon_Element,
511                                              "paths", paths);
512
513     EFREET_POINTER_TYPE(icon_element_pointer_edd, icon_element_edd, Cache_Icon_Element);
514
515     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon);
516     icon_edd = eet_data_descriptor_file_new(&eddc);
517     if (!icon_edd) return NULL;
518
519     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_edd, Efreet_Cache_Icon,
520                                   "theme", theme, EET_T_STRING);
521     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY(icon_edd, Efreet_Cache_Icon,
522                                       "icons", icons, icon_element_pointer_edd);
523
524     return icon_edd;
525 }
526
527 /*
528  * Needs EAPI because of helper binaries
529  */
530 EAPI Eet_Data_Descriptor *
531 efreet_icon_theme_edd(Eina_Bool cache)
532 {
533     Eet_Data_Descriptor_Class eddc;
534
535     if (icon_theme_edd) return icon_theme_edd;
536
537     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Icon_Theme_Directory);
538     icon_theme_directory_edd = eet_data_descriptor_file_new(&eddc);
539     if (!icon_theme_directory_edd) return NULL;
540
541     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
542                                   "name", name, EET_T_STRING);
543     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
544                                   "context", context, EET_T_UCHAR);
545     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
546                                   "type", type, EET_T_UCHAR);
547     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
548                                   "size.normal", size.normal, EET_T_UINT);
549     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
550                                   "size.min", size.min, EET_T_UINT);
551     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
552                                   "size.max", size.max, EET_T_UINT);
553     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_directory_edd, Efreet_Icon_Theme_Directory,
554                                   "size.threshold", size.threshold, EET_T_UINT);
555
556     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Icon_Theme);
557     icon_theme_edd = eet_data_descriptor_file_new(&eddc);
558     if (!icon_theme_edd) return NULL;
559
560     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
561                                   "name.internal", theme.name.internal, EET_T_STRING);
562     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
563                                   "name.name", theme.name.name, EET_T_STRING);
564     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
565                                   "comment", theme.comment, EET_T_STRING);
566     EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
567                                   "example_icon", theme.example_icon, EET_T_STRING);
568
569     eet_data_descriptor_element_add(icon_theme_edd, "paths", EET_T_STRING, EET_G_LIST,
570                                     offsetof(Efreet_Cache_Icon_Theme, theme.paths), 0, NULL, NULL);
571     eet_data_descriptor_element_add(icon_theme_edd, "inherits", EET_T_STRING, EET_G_LIST,
572                                     offsetof(Efreet_Cache_Icon_Theme, theme.inherits), 0, NULL, NULL);
573     EET_DATA_DESCRIPTOR_ADD_LIST(icon_theme_edd, Efreet_Cache_Icon_Theme,
574                                   "directories", theme.directories, icon_theme_directory_edd);
575
576     if (cache)
577     {
578         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
579                                       "last_cache_check", last_cache_check, EET_T_LONG_LONG);
580
581         EET_DATA_DESCRIPTOR_ADD_BASIC(icon_theme_edd, Efreet_Cache_Icon_Theme,
582                                       "path", path, EET_T_STRING);
583
584         EET_DATA_DESCRIPTOR_ADD_HASH(icon_theme_edd, Efreet_Cache_Icon_Theme,
585                                      "dirs", dirs, efreet_icon_directory_edd());
586     }
587
588     return icon_theme_edd;
589 }
590
591 /*
592  * Needs EAPI because of helper binaries
593  */
594 EAPI Eet_Data_Descriptor *
595 efreet_icon_fallback_edd(void)
596 {
597     Eet_Data_Descriptor_Class eddc;
598
599     if (icon_fallback_edd) return icon_fallback_edd;
600
601     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Fallback_Icon);
602     icon_fallback_edd = eet_data_descriptor_file_new(&eddc);
603     if (!icon_fallback_edd) return NULL;
604
605     EET_DATA_DESCRIPTOR_ADD_VAR_ARRAY_STRING(icon_fallback_edd,
606                                              Efreet_Cache_Fallback_Icon, "icons", icons);
607
608     return icon_fallback_edd;
609 }
610
611 /*
612  * Needs EAPI because of helper binaries
613  */
614 EAPI Eet_Data_Descriptor *
615 efreet_desktop_edd(void)
616 {
617     Eet_Data_Descriptor_Class eddc;
618
619     if (desktop_edd) return desktop_edd;
620
621     EET_EINA_FILE_DATA_DESCRIPTOR_CLASS_SET(&eddc, Efreet_Cache_Desktop);
622     desktop_edd = eet_data_descriptor_file_new(&eddc);
623     if (!desktop_edd) return NULL;
624
625     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "type", desktop.type, EET_T_INT);
626     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "version", desktop.version, EET_T_STRING);
627     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "orig_path", desktop.orig_path, EET_T_STRING);
628     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "load_time", desktop.load_time, EET_T_LONG_LONG);
629     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "name", desktop.name, EET_T_STRING);
630     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "generic_name", desktop.generic_name, EET_T_STRING);
631     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "comment", desktop.comment, EET_T_STRING);
632     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "icon", desktop.icon, EET_T_STRING);
633     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "try_exec", desktop.try_exec, EET_T_STRING);
634     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "exec", desktop.exec, EET_T_STRING);
635     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "path", desktop.path, EET_T_STRING);
636     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_wm_class", desktop.startup_wm_class, EET_T_STRING);
637     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "url", desktop.url, EET_T_STRING);
638     eet_data_descriptor_element_add(desktop_edd, "only_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.only_show_in), 0, NULL, NULL);
639     eet_data_descriptor_element_add(desktop_edd, "not_show_in", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.not_show_in), 0, NULL, NULL);
640     eet_data_descriptor_element_add(desktop_edd, "categories", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.categories), 0, NULL, NULL);
641     eet_data_descriptor_element_add(desktop_edd, "mime_types", EET_T_STRING, EET_G_LIST, offsetof(Efreet_Cache_Desktop, desktop.mime_types), 0, NULL, NULL);
642     eet_data_descriptor_element_add(desktop_edd, "x", EET_T_STRING, EET_G_HASH, offsetof(Efreet_Cache_Desktop, desktop.x), 0, NULL, NULL);
643     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "no_display", desktop.no_display, EET_T_UCHAR);
644     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "hidden", desktop.hidden, EET_T_UCHAR);
645     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "terminal", desktop.terminal, EET_T_UCHAR);
646     EET_DATA_DESCRIPTOR_ADD_BASIC(desktop_edd, Efreet_Cache_Desktop, "startup_notify", desktop.startup_notify, EET_T_UCHAR);
647
648     return desktop_edd;
649 }
650
651 Efreet_Cache_Icon *
652 efreet_cache_icon_find(Efreet_Icon_Theme *theme, const char *icon)
653 {
654     Efreet_Cache_Icon *cache = NULL;
655
656     if (theme_name && strcmp(theme_name, theme->name.internal))
657     {
658         /* FIXME: this is bad if people have pointer to this cache, things will go wrong */
659         INF("theme_name change from `%s` to `%s`", theme_name, theme->name.internal);
660         IF_RELEASE(theme_name);
661         icon_cache = efreet_cache_close(icon_cache);
662         eina_hash_free(icons);
663         icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
664     }
665
666     if (!efreet_cache_check(&icon_cache, efreet_icon_cache_file(theme->name.internal), EFREET_ICON_CACHE_MAJOR)) return NULL;
667     if (!theme_name)
668         theme_name = eina_stringshare_add(theme->name.internal);
669
670     cache = eina_hash_find(icons, icon);
671     if (cache == NON_EXISTING) return NULL;
672     if (cache) return cache;
673
674     cache = eet_data_read(icon_cache, efreet_icon_edd(), icon);
675     if (cache)
676         eina_hash_add(icons, icon, cache);
677     else
678         eina_hash_add(icons, icon, NON_EXISTING);
679     return cache;
680 }
681
682 Efreet_Cache_Fallback_Icon *
683 efreet_cache_icon_fallback_find(const char *icon)
684 {
685     Efreet_Cache_Fallback_Icon *cache;
686
687     if (!efreet_cache_check(&fallback_cache, efreet_icon_cache_file(EFREET_CACHE_ICON_FALLBACK), EFREET_ICON_CACHE_MAJOR)) return NULL;
688
689     cache = eina_hash_find(fallbacks, icon);
690     if (cache == NON_EXISTING) return NULL;
691     if (cache) return cache;
692
693     cache = eet_data_read(fallback_cache, efreet_icon_fallback_edd(), icon);
694     if (cache)
695         eina_hash_add(fallbacks, icon, cache);
696     else
697         eina_hash_add(fallbacks, icon, NON_EXISTING);
698     return cache;
699 }
700
701 Efreet_Icon_Theme *
702 efreet_cache_icon_theme_find(const char *theme)
703 {
704     Efreet_Cache_Icon_Theme *cache;
705
706     if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
707
708     cache = eina_hash_find(themes, theme);
709     if (cache == NON_EXISTING) return NULL;
710     if (cache) return &(cache->theme);
711
712     cache = eet_data_read(icon_theme_cache, efreet_icon_theme_edd(EINA_FALSE), theme);
713     if (cache)
714     {
715         eina_hash_add(themes, theme, cache);
716         return &(cache->theme);
717     }
718     else
719         eina_hash_add(themes, theme, NON_EXISTING);
720     return NULL;
721 }
722
723 static void
724 efreet_cache_icon_free(Efreet_Cache_Icon *icon)
725 {
726     unsigned int i;
727
728     if (!icon) return;
729     if (icon == NON_EXISTING) return;
730
731     for (i = 0; i < icon->icons_count; ++i)
732     {
733         free(icon->icons[i]->paths);
734         free(icon->icons[i]);
735     }
736
737     free(icon->icons);
738     free(icon);
739 }
740
741 static void
742 efreet_cache_icon_fallback_free(Efreet_Cache_Fallback_Icon *icon)
743 {
744     if (!icon) return;
745     if (icon == NON_EXISTING) return;
746
747     free(icon->icons);
748     free(icon);
749 }
750
751 static void
752 efreet_cache_icon_theme_free(Efreet_Icon_Theme *theme)
753 {
754     void *data;
755
756     if (!theme) return;
757     if (theme == NON_EXISTING) return;
758
759     eina_list_free(theme->paths);
760     eina_list_free(theme->inherits);
761     EINA_LIST_FREE(theme->directories, data)
762         free(data);
763
764     free(theme);
765 }
766
767 Eina_List *
768 efreet_cache_icon_theme_list(void)
769 {
770     Eina_List *ret = NULL;
771     char **keys;
772     int i, num;
773
774     if (!efreet_cache_check(&icon_theme_cache, efreet_icon_theme_cache_file(), EFREET_ICON_CACHE_MAJOR)) return NULL;
775     keys = eet_list(icon_theme_cache, "*", &num);
776     for (i = 0; i < num; i++)
777     {
778         Efreet_Icon_Theme *theme;
779         if (!strncmp(keys[i], "__efreet", 8)) continue;
780
781         theme = eina_hash_find(themes, keys[i]);
782         if (!theme)
783             theme = efreet_cache_icon_theme_find(keys[i]);
784         if (theme && theme != NON_EXISTING)
785             ret = eina_list_append(ret, theme);
786     }
787     free(keys);
788     return ret;
789 }
790
791 /*
792  * Needs EAPI because of helper binaries
793  */
794 EAPI void
795 efreet_cache_array_string_free(Efreet_Cache_Array_String *array)
796 {
797     if (!array) return;
798     free(array->array);
799     free(array);
800 }
801
802 Efreet_Desktop *
803 efreet_cache_desktop_find(const char *file)
804 {
805     Efreet_Cache_Desktop *cache;
806     char rp[PATH_MAX];
807
808     if (!realpath(file, rp)) return NULL;
809
810     if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL;
811
812     cache = eina_hash_find(desktops, rp);
813     if (cache == NON_EXISTING) return NULL;
814     if (cache)
815     {
816         /* If less than one second since last stat, return desktop */
817         if ((ecore_time_get() - cache->check_time) < 1)
818         {
819             INF("Return without stat %f %f", ecore_time_get(), cache->check_time);
820             return &cache->desktop;
821         }
822         if (cache->desktop.load_time == ecore_file_mod_time(cache->desktop.orig_path))
823         {
824             INF("Return with stat %f %f", ecore_time_get(), cache->check_time);
825             cache->check_time = ecore_time_get();
826             return &cache->desktop;
827         }
828
829         /* We got stale data. The desktop will be free'd eventually as
830          * users will call efreet_desktop_free */
831         eina_hash_set(desktops, rp, NON_EXISTING);
832         cache = NULL;
833     }
834
835     cache = eet_data_read(desktop_cache, efreet_desktop_edd(), rp);
836     if (cache)
837     {
838         if (cache->desktop.load_time != ecore_file_mod_time(cache->desktop.orig_path))
839         {
840             /* Don't return stale data */
841             INF("We got stale data in the desktop cache");
842             efreet_cache_desktop_free(&cache->desktop);
843             eina_hash_set(desktops, rp, NON_EXISTING);
844         }
845         else
846         {
847             cache->desktop.eet = 1;
848             cache->check_time = ecore_time_get();
849             eina_hash_set(desktops, cache->desktop.orig_path, cache);
850             return &cache->desktop;
851         }
852     }
853     else
854         eina_hash_set(desktops, rp, NON_EXISTING);
855     return NULL;
856 }
857
858 void
859 efreet_cache_desktop_free(Efreet_Desktop *desktop)
860 {
861     Efreet_Old_Cache *d;
862     Efreet_Desktop *curr;
863     Eina_List *l;
864
865     if (!desktop ||
866         desktop == NON_EXISTING ||
867         !desktop->eet) return;
868
869     curr = eina_hash_find(desktops, desktop->orig_path);
870     if (curr == desktop)
871     {
872         INF("Found in current cache, purge\n");
873         eina_hash_del_by_key(desktops, desktop->orig_path);
874     }
875
876     EINA_LIST_FOREACH(old_desktop_caches, l, d)
877     {
878         curr = eina_hash_find(d->hash, desktop->orig_path);
879         if (curr == desktop)
880         {
881             INF("Found in old cache, purge\n");
882             eina_hash_del_by_key(d->hash, desktop->orig_path);
883             if (eina_hash_population(d->hash) == 0)
884             {
885                 INF("Cache empty, close file\n");
886                 eina_hash_free(d->hash);
887                 eet_close(d->ef);
888                 free(d);
889                 old_desktop_caches = eina_list_remove_list(old_desktop_caches, l);
890             }
891             break;
892         }
893     }
894
895     eina_list_free(desktop->only_show_in);
896     eina_list_free(desktop->not_show_in);
897     eina_list_free(desktop->categories);
898     eina_list_free(desktop->mime_types);
899     IF_FREE_HASH(desktop->x);
900     free(desktop);
901 }
902
903 void
904 efreet_cache_desktop_add(Efreet_Desktop *desktop)
905 {
906     char buf[PATH_MAX];
907     char *dir;
908     Efreet_Cache_Array_String *arr;
909
910     /*
911      * Read file from disk, save path in cache so it will be included in next
912      * cache update
913      */
914     strncpy(buf, desktop->orig_path, PATH_MAX);
915     buf[PATH_MAX - 1] = '\0';
916     dir = dirname(buf);
917     arr = efreet_cache_desktop_dirs();
918     if (arr)
919     {
920         unsigned int i;
921
922         for (i = 0; i < arr->array_count; i++)
923         {
924             /* Check if we already have this dir in cache */
925             if (!strcmp(dir, arr->array[i]))
926                 return;
927         }
928         efreet_cache_array_string_free(arr);
929     }
930     if (!eina_list_search_unsorted_list(desktop_dirs_add, EINA_COMPARE_CB(strcmp), dir))
931         desktop_dirs_add = eina_list_append(desktop_dirs_add, eina_stringshare_add(dir));
932
933     efreet_cache_desktop_update();
934 }
935
936 Efreet_Cache_Array_String *
937 efreet_cache_desktop_dirs(void)
938 {
939     if (!efreet_cache_check(&desktop_cache, efreet_desktop_cache_file(), EFREET_DESKTOP_CACHE_MAJOR)) return NULL;
940
941     return eet_data_read(desktop_cache, efreet_array_string_edd(), EFREET_CACHE_DESKTOP_DIRS);
942 }
943
944 void
945 efreet_cache_desktop_reset(void)
946 {
947     Eina_List *l = NULL;
948     Efreet_Event_Cache_Update *ev = NULL;
949     Efreet_Old_Cache *d = NULL;
950
951     IF_RELEASE(util_cache_names_key);
952     IF_RELEASE(util_cache_hash_key);
953
954     if ((desktop_cache) && (desktop_cache != NON_EXISTING))
955     {
956         d = NEW(Efreet_Old_Cache, 1);
957         if (!d) goto error;
958         d->hash = desktops;
959         d->ef = desktop_cache;
960         old_desktop_caches = eina_list_append(old_desktop_caches, d);
961
962         desktops = eina_hash_string_superfast_new(NULL);
963     }
964     desktop_cache = NULL;
965
966     efreet_cache_array_string_free(util_cache_names);
967     util_cache_names = NULL;
968
969     if (util_cache_hash)
970     {
971         eina_hash_free(util_cache_hash->hash);
972         free(util_cache_hash);
973         util_cache_hash = NULL;
974     }
975
976     util_cache = efreet_cache_close(util_cache);
977
978     ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_UPDATE, ev, desktop_cache_update_free, d);
979     return;
980 error:
981     IF_FREE(ev);
982     IF_FREE(d);
983     EINA_LIST_FREE(l, d)
984         free(d);
985 }
986
987 void
988 efreet_cache_desktop_update(void)
989 {
990     if (!efreet_cache_update) return;
991
992     if (desktop_cache_timer)
993         ecore_timer_delay(desktop_cache_timer, 0.2);
994     else
995         desktop_cache_timer = ecore_timer_add(0.2, desktop_cache_update_cache_cb, NULL);
996 }
997
998 void
999 efreet_cache_icon_update(void)
1000 {
1001     if (!efreet_cache_update) return;
1002
1003     if (icon_cache_timer)
1004         ecore_timer_delay(icon_cache_timer, 0.2);
1005     else
1006         icon_cache_timer = ecore_timer_add(0.2, icon_cache_update_cache_cb, NULL);
1007 }
1008
1009 static Eina_Bool
1010 efreet_cache_check(Eet_File **ef, const char *path, int major)
1011 {
1012     Efreet_Cache_Version *version;
1013
1014     if (*ef == NON_EXISTING) return EINA_FALSE;
1015     if (*ef) return EINA_TRUE;
1016     if (!*ef)
1017         *ef = eet_open(path, EET_FILE_MODE_READ);
1018     if (!*ef)
1019     {
1020         *ef = NON_EXISTING;
1021         return EINA_FALSE;
1022     }
1023
1024     version = eet_data_read(*ef, efreet_version_edd(), EFREET_CACHE_VERSION);
1025     if ((!version) || (version->major != major))
1026     {
1027         IF_FREE(version);
1028         eet_close(*ef);
1029         *ef = NON_EXISTING;
1030         return EINA_FALSE;
1031     }
1032     free(version);
1033     return EINA_TRUE;
1034 }
1035
1036 static void *
1037 efreet_cache_close(Eet_File *ef)
1038 {
1039     if (ef && ef != NON_EXISTING)
1040         eet_close(ef);
1041     return NULL;
1042 }
1043
1044 Efreet_Cache_Hash *
1045 efreet_cache_util_hash_string(const char *key)
1046 {
1047     if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
1048         return util_cache_hash;
1049     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1050
1051     if (util_cache_hash)
1052     {
1053         /* free previous util_cache */
1054         IF_RELEASE(util_cache_hash_key);
1055         eina_hash_free(util_cache_hash->hash);
1056         free(util_cache_hash);
1057     }
1058     util_cache_hash_key = eina_stringshare_add(key);
1059     util_cache_hash = eet_data_read(util_cache, efreet_hash_string_edd(), key);
1060     return util_cache_hash;
1061 }
1062
1063 Efreet_Cache_Hash *
1064 efreet_cache_util_hash_array_string(const char *key)
1065 {
1066     if (util_cache_hash_key && !strcmp(key, util_cache_hash_key))
1067         return util_cache_hash;
1068     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1069
1070     IF_RELEASE(util_cache_hash_key);
1071     if (util_cache_hash)
1072     {
1073         /* free previous cache */
1074         eina_hash_free(util_cache_hash->hash);
1075         free(util_cache_hash);
1076     }
1077     util_cache_hash_key = eina_stringshare_add(key);
1078     util_cache_hash = eet_data_read(util_cache, efreet_hash_array_string_edd(), key);
1079     return util_cache_hash;
1080 }
1081
1082 Efreet_Cache_Array_String *
1083 efreet_cache_util_names(const char *key)
1084 {
1085     if (util_cache_names_key && !strcmp(key, util_cache_names_key))
1086         return util_cache_names;
1087     if (!efreet_cache_check(&util_cache, efreet_desktop_util_cache_file(), EFREET_DESKTOP_UTILS_CACHE_MAJOR)) return NULL;
1088
1089     if (util_cache_names)
1090     {
1091         /* free previous util_cache */
1092         IF_RELEASE(util_cache_names_key);
1093         efreet_cache_array_string_free(util_cache_names);
1094     }
1095     util_cache_names_key = eina_stringshare_add(key);
1096     util_cache_names = eet_data_read(util_cache, efreet_array_string_edd(), key);
1097     return util_cache_names;
1098 }
1099
1100 static Eina_Bool
1101 cache_exe_cb(void *data __UNUSED__, int type __UNUSED__, void *event)
1102 {
1103     Ecore_Exe_Event_Del *ev;
1104
1105     ev = event;
1106     if (ev->exe == desktop_cache_exe)
1107     {
1108         if (desktop_cache_exe_lock > 0)
1109         {
1110             close(desktop_cache_exe_lock);
1111             desktop_cache_exe_lock = -1;
1112         }
1113         desktop_cache_exe = NULL;
1114     }
1115     else if (ev->exe == icon_cache_exe)
1116     {
1117         if (icon_cache_exe_lock > 0)
1118         {
1119             close(icon_cache_exe_lock);
1120             icon_cache_exe_lock = -1;
1121         }
1122         icon_cache_exe = NULL;
1123     }
1124     return ECORE_CALLBACK_RENEW;
1125 }
1126
1127 static Eina_Bool
1128 cache_check_change(const char *path)
1129 {
1130     const char *data;
1131     Eina_Bool changed = EINA_TRUE;
1132     Eina_File *f;
1133
1134     f = eina_file_open(path, EINA_FALSE);
1135     if (!f) return EINA_TRUE;
1136     if (eina_file_size_get(f) < 1) return EINA_TRUE;
1137     data = eina_file_map_all(f, EINA_FILE_SEQUENTIAL);
1138     if (*data == 'n') changed = EINA_FALSE;
1139     eina_file_close(f);
1140     return changed;
1141 }
1142
1143 static void
1144 cache_update_cb(void *data __UNUSED__, Ecore_File_Monitor *em __UNUSED__,
1145                 Ecore_File_Event event, const char *path)
1146 {
1147     const char *file;
1148     Efreet_Event_Cache_Update *ev = NULL;
1149     Efreet_Old_Cache *d = NULL;
1150     Eina_List *l = NULL;
1151
1152     if (event != ECORE_FILE_EVENT_CLOSED)
1153         return;
1154
1155     file = ecore_file_file_get(path);
1156     if (!file) return;
1157     if (!strcmp(file, "desktop_data.update"))
1158     {
1159         ev = NEW(Efreet_Event_Cache_Update, 1);
1160         if (!ev) goto error;
1161         if (cache_check_change(path))
1162         {
1163             efreet_cache_desktop_reset();
1164         }
1165         ecore_event_add(EFREET_EVENT_DESKTOP_CACHE_BUILD, NULL, NULL, NULL);
1166         /* TODO: Check if desktop_dirs_add exists, and rebuild cache if */
1167     }
1168     else if (!strcmp(file, "icon_data.update"))
1169     {
1170         if (cache_check_change(path))
1171         {
1172             ev = NEW(Efreet_Event_Cache_Update, 1);
1173             if (!ev) goto error;
1174
1175             IF_RELEASE(theme_name);
1176
1177             /* Save all old caches */
1178             d = NEW(Efreet_Old_Cache, 1);
1179             if (!d) goto error;
1180             d->hash = themes;
1181             d->ef = icon_theme_cache;
1182             l = eina_list_append(l, d);
1183
1184             d = NEW(Efreet_Old_Cache, 1);
1185             if (!d) goto error;
1186             d->hash = icons;
1187             d->ef = icon_cache;
1188             l = eina_list_append(l, d);
1189
1190             d = NEW(Efreet_Old_Cache, 1);
1191             if (!d) goto error;
1192             d->hash = fallbacks;
1193             d->ef = fallback_cache;
1194             l = eina_list_append(l, d);
1195
1196             /* Create new empty caches */
1197             themes = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_theme_free));
1198             icons = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_free));
1199             fallbacks = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_icon_fallback_free));
1200
1201             icon_theme_cache = NULL;
1202             icon_cache = NULL;
1203             fallback_cache = NULL;
1204
1205             /* Send event */
1206             ecore_event_add(EFREET_EVENT_ICON_CACHE_UPDATE, ev, icon_cache_update_free, l);
1207         }
1208     }
1209     return;
1210 error:
1211     IF_FREE(ev);
1212     IF_FREE(d);
1213     EINA_LIST_FREE(l, d)
1214         free(d);
1215 }
1216
1217 static Eina_Bool
1218 desktop_cache_update_cache_cb(void *data __UNUSED__)
1219 {
1220     char file[PATH_MAX];
1221     struct flock fl;
1222     int prio;
1223
1224     desktop_cache_timer = NULL;
1225
1226     /* TODO: Retry update cache later */
1227     if (desktop_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL;
1228
1229     snprintf(file, sizeof(file), "%s/efreet/desktop_exec.lock", efreet_cache_home_get());
1230
1231     desktop_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
1232     if (desktop_cache_exe_lock < 0) goto error;
1233     efreet_fsetowner(desktop_cache_exe_lock);
1234     memset(&fl, 0, sizeof(struct flock));
1235     fl.l_type = F_WRLCK;
1236     fl.l_whence = SEEK_SET;
1237     if (fcntl(desktop_cache_exe_lock, F_SETLK, &fl) < 0) goto error;
1238     prio = ecore_exe_run_priority_get();
1239     ecore_exe_run_priority_set(19);
1240     eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_desktop_cache_create", sizeof(file));
1241     if (desktop_dirs_add)
1242     {
1243         const char *str;
1244
1245         eina_strlcat(file, " -d", sizeof(file));
1246         EINA_LIST_FREE(desktop_dirs_add, str)
1247         {
1248             eina_strlcat(file, " ", sizeof(file));
1249             eina_strlcat(file, str, sizeof(file));
1250             eina_stringshare_del(str);
1251         }
1252     }
1253     INF("Run desktop cache creation: %s", file);
1254     desktop_cache_exe = ecore_exe_run(file, NULL);
1255     ecore_exe_run_priority_set(prio);
1256     if (!desktop_cache_exe) goto error;
1257
1258     return ECORE_CALLBACK_CANCEL;
1259 error:
1260     if (desktop_cache_exe_lock > 0)
1261     {
1262         close(desktop_cache_exe_lock);
1263         desktop_cache_exe_lock = -1;
1264     }
1265     return ECORE_CALLBACK_CANCEL;
1266 }
1267
1268 static Eina_Bool
1269 icon_cache_update_cache_cb(void *data __UNUSED__)
1270 {
1271     char file[PATH_MAX];
1272     struct flock fl;
1273     int prio;
1274     Eina_List **l, *l2;
1275
1276     icon_cache_timer = NULL;
1277
1278     /* TODO: Retry update cache later */
1279     if (icon_cache_exe_lock > 0) return ECORE_CALLBACK_CANCEL;
1280
1281     snprintf(file, sizeof(file), "%s/efreet/icon_exec.lock", efreet_cache_home_get());
1282
1283     icon_cache_exe_lock = open(file, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
1284     if (icon_cache_exe_lock < 0) goto error;
1285     efreet_fsetowner(icon_cache_exe_lock);
1286     memset(&fl, 0, sizeof(struct flock));
1287     fl.l_type = F_WRLCK;
1288     fl.l_whence = SEEK_SET;
1289     if (fcntl(icon_cache_exe_lock, F_SETLK, &fl) < 0) goto error;
1290     prio = ecore_exe_run_priority_get();
1291     ecore_exe_run_priority_set(19);
1292     eina_strlcpy(file, PACKAGE_LIB_DIR "/efreet/efreet_icon_cache_create", sizeof(file));
1293     l = efreet_icon_extra_list_get();
1294     if (l && eina_list_count(*l) > 0)
1295     {
1296         Eina_List *ll;
1297         char *p;
1298
1299         eina_strlcat(file, " -d", sizeof(file));
1300         EINA_LIST_FOREACH(*l, ll, p)
1301         {
1302             eina_strlcat(file, " ", sizeof(file));
1303             eina_strlcat(file, p, sizeof(file));
1304         }
1305     }
1306     l2 = efreet_icon_extensions_list_get();
1307     if (eina_list_count(l2) > 0)
1308     {
1309         Eina_List *ll;
1310         char *p;
1311
1312         eina_strlcat(file, " -e", sizeof(file));
1313         EINA_LIST_FOREACH(l2, ll, p)
1314         {
1315             eina_strlcat(file, " ", sizeof(file));
1316             eina_strlcat(file, p, sizeof(file));
1317         }
1318     }
1319     icon_cache_exe = ecore_exe_run(file, NULL);
1320     ecore_exe_run_priority_set(prio);
1321     if (!icon_cache_exe) goto error;
1322
1323     return ECORE_CALLBACK_CANCEL;
1324
1325 error:
1326     if (icon_cache_exe_lock > 0)
1327     {
1328         close(icon_cache_exe_lock);
1329         icon_cache_exe_lock = -1;
1330     }
1331     return ECORE_CALLBACK_CANCEL;
1332 }
1333
1334 static void
1335 desktop_cache_update_free(void *data, void *ev)
1336 {
1337     Efreet_Old_Cache *d;
1338     int dangling = 0;
1339
1340     d = data;
1341     if (d && (eina_list_data_find(old_desktop_caches, d) == d))
1342     {
1343         /*
1344          * All users should now had the chance to update their pointers.
1345          * Check whether we still have some dangling and print a warning.
1346          * Programs might close their pointers later.
1347          */
1348         if (d->hash)
1349         {
1350             Eina_Iterator *it;
1351             Eina_Hash_Tuple *tuple;
1352
1353             it = eina_hash_iterator_tuple_new(d->hash);
1354             EINA_ITERATOR_FOREACH(it, tuple)
1355             {
1356                 if (tuple->data == NON_EXISTING) continue;
1357                 WRN("%d:%s still in cache after update event!",
1358                     ((Efreet_Desktop *)tuple->data)->ref, (char *)tuple->key);
1359                 dangling++;
1360             }
1361             eina_iterator_free(it);
1362         }
1363         if (dangling != 0)
1364         {
1365             WRN("There are still %i desktop files with old\n"
1366                 "dangling references to desktop files. This application\n"
1367                 "has not handled the EFREET_EVENT_DESKTOP_CACHE_UPDATE\n"
1368                 "fully and released its references. Please fix the application\n"
1369                 "so it does this.",
1370                 dangling);
1371         }
1372     }
1373     free(ev);
1374 }
1375
1376 static void
1377 icon_cache_update_free(void *data, void *ev)
1378 {
1379     Efreet_Old_Cache *d;
1380     Eina_List *l;
1381
1382     l = data;
1383     EINA_LIST_FREE(l, d)
1384     {
1385         if (d->hash)
1386             eina_hash_free(d->hash);
1387         efreet_cache_close(d->ef);
1388         free(d);
1389     }
1390     free(ev);
1391 }
1392
1393 static void *
1394 hash_array_string_add(void *hash, const char *key, void *data)
1395 {
1396     if (!hash)
1397         hash = eina_hash_string_superfast_new(EINA_FREE_CB(efreet_cache_array_string_free));
1398     if (!hash)
1399         return NULL;
1400     eina_hash_add(hash, key, data);
1401     return hash;
1402 }