Tizen 2.1 base
[framework/uifw/ecore.git] / src / lib / ecore_desktop / ecore_desktop_paths.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4 /*
5  * This conforms with the freedesktop.org XDG Base Directory Specification version 0.6
6  *
7  * The policy here is to add extra directories to the possible search paths to 
8  * cater for quirks of different OS's.  It doesn't take long to eliminate an
9  * excess directory from the paths.
10  */
11
12 #include <Ecore.h>
13
14 #include "Ecore_Desktop.h"
15 #include "ecore_desktop_private.h"
16
17 #include <dirent.h>
18 #include <sys/stat.h>
19 #include <libgen.h>
20 #include <strings.h>
21
22 /* This really slows things down and no doubt drags in some KDE bloat at start up.
23  * To work around this, I add a few extra things to try in the _ecore_desktop_paths_get()
24  * calls below.
25  *
26 #define KDE_SUPPORT 1
27  */
28
29 /* This is nowhere near as bloated and slow as the KDE stuff, but worthwhile 
30  * making it optional anyway.
31  *
32 #define GNOME_SUPPORT 1
33  */
34
35 /* FIXME: remove those two defines, preload a cache with the guesses, whenever 
36  * we have a few seconds of idle time, run the gnome and kde config proggies 
37  * and correct those guesses.
38  */
39
40 Ecore_List         *ecore_desktop_paths_config = NULL;
41 Ecore_List         *ecore_desktop_paths_menus = NULL;
42 Ecore_List         *ecore_desktop_paths_directories = NULL;
43 Ecore_List         *ecore_desktop_paths_desktops = NULL;
44 Ecore_List         *ecore_desktop_paths_icons = NULL;
45 Ecore_List         *ecore_desktop_paths_kde_legacy = NULL;
46 Ecore_List         *ecore_desktop_paths_xsessions = NULL;
47
48
49 static Ecore_List  *_ecore_desktop_paths_get(Ecore_Desktop_Paths_Type path_type,
50                                              const char *before, const char *env_home,
51                                              const char *env, const char *env_home_default,
52                                              const char *env_default, const char *type,
53                                              const char *gnome_extra, const char *kde);
54 static void         _ecore_desktop_paths_massage_path(char *path, char *home,
55                                                       char *first,
56                                                       char *second);
57 static void         _ecore_desktop_paths_check_and_add(Ecore_List * paths,
58                                                        const char *path);
59 static void         _ecore_desktop_paths_create(void);
60 static void         _ecore_desktop_paths_destroy(void);
61
62 static Ecore_List  *gnome_data = NULL;
63 static Ecore_List  *prepend_user_paths[ECORE_DESKTOP_PATHS_MAX];
64 static Ecore_List  *prepend_system_paths[ECORE_DESKTOP_PATHS_MAX];
65 static Ecore_List  *append_user_paths[ECORE_DESKTOP_PATHS_MAX];
66 static Ecore_List  *append_system_paths[ECORE_DESKTOP_PATHS_MAX];
67 static char        *home;
68 static int          init_count = 0;
69
70 #if defined GNOME_SUPPORT || defined KDE_SUPPORT
71 struct _config_exe_data
72 {
73    char               *home;
74    Ecore_List         *paths, *types;
75    int                 done;
76 };
77
78 static void         _ecore_desktop_paths_exec_config(Ecore_List * paths,
79                                                      char *home,
80                                                      Ecore_List * extras,
81                                                      char *cmd);
82
83 static int          _ecore_desktop_paths_cb_exe_exit(void *data, int type,
84                                                      void *event);
85
86 static Ecore_Event_Handler *exit_handler = NULL;
87 #endif
88
89 EAPI int
90 ecore_desktop_paths_init(void)
91 {
92    if (++init_count != 1)
93       return init_count;
94
95 #if defined GNOME_SUPPORT || defined KDE_SUPPORT
96    exit_handler =
97       ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
98                               _ecore_desktop_paths_cb_exe_exit, NULL);
99 #endif
100
101    ecore_desktop_paths_extras_clear();
102    gnome_data = ecore_list_new();
103    home = ecore_desktop_home_get();
104
105    if (gnome_data)
106      {
107 #ifdef GNOME_SUPPORT
108         if (exit_handler)
109           {
110              ecore_list_free_cb_set(gnome_data, free);
111              _ecore_desktop_paths_exec_config(gnome_data, home, NULL,
112                                               "gnome-config --datadir");
113           }
114 #else
115         Ecore_List         *config_list;
116
117         config_list = ecore_desktop_paths_to_list("/opt/gnome/share");
118         if (config_list)
119           {
120              char               *this_config;
121              char                path[PATH_MAX];
122
123              ecore_list_first_goto(config_list);
124              while ((this_config = ecore_list_next(config_list)) != NULL)
125                {
126
127                   _ecore_desktop_paths_massage_path(path, home,
128                                                     this_config, NULL);
129                   _ecore_desktop_paths_check_and_add(gnome_data, path);
130                }
131              E_FN_DEL(ecore_list_destroy, config_list);
132           }
133 #endif
134      }
135
136    _ecore_desktop_paths_create();
137    return init_count;
138 }
139
140 EAPI int
141 ecore_desktop_paths_shutdown(void)
142 {
143    int i;
144
145    if (--init_count != 0)
146       return init_count;
147
148    for (i = 0; i < ECORE_DESKTOP_PATHS_MAX; i++)
149      {
150         E_FN_DEL(ecore_list_destroy, prepend_user_paths[i]);
151         E_FN_DEL(ecore_list_destroy, prepend_system_paths[i]);
152         E_FN_DEL(ecore_list_destroy, append_user_paths[i]);
153         E_FN_DEL(ecore_list_destroy, append_system_paths[i]);
154      }
155
156    _ecore_desktop_paths_destroy();
157    E_FN_DEL(ecore_list_destroy, gnome_data);
158
159 #if defined GNOME_SUPPORT || defined KDE_SUPPORT
160    if (exit_handler)
161       ecore_event_handler_del(exit_handler);
162 #endif
163    free(home);
164    return init_count;
165 }
166
167 EAPI void
168 ecore_desktop_paths_regen(void)
169 {
170    _ecore_desktop_paths_destroy();
171    _ecore_desktop_paths_create();
172 }
173
174 static void
175 _ecore_desktop_paths_create(void)
176 {
177    if (!ecore_desktop_paths_desktops)
178      {
179         ecore_desktop_paths_desktops =
180            _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_DESKTOPS, NULL,
181                                     "XDG_DATA_HOME", "XDG_DATA_DIRS",
182                                     "~/.local/share:~/.kde/share",
183                                     "/usr/local/share:/usr/share",
184 //                                  "applications:applnk:applications/kde",
185                                     "applications:applnk",
186                                     "dist/desktop-files:dist/short-menu:gnome/apps",
187                                     "xdgdata-apps:apps");
188         _ecore_desktop_paths_check_and_add(ecore_desktop_paths_desktops,
189                                            "/usr/local/share/update-desktop-files/templates");
190         _ecore_desktop_paths_check_and_add(ecore_desktop_paths_desktops,
191                                            "/usr/share/update-desktop-files/templates");
192      }
193    if (!ecore_desktop_paths_kde_legacy)
194      {
195 #ifdef KDE_SUPPORT
196         ecore_desktop_paths_kde_legacy =
197            _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_KDE_LEGACY, NULL, NULL,
198                                     NULL, NULL, NULL, NULL, NULL, "apps");
199 #else
200         ecore_desktop_paths_kde_legacy =
201            _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_KDE_LEGACY, NULL,
202                                     "XDG_DATA_HOME", "XDG_DATA_DIRS",
203                                     "~/.local/share:~/.kde/share",
204                                     "/usr/local/share:/usr/share", "applnk",
205                                     NULL, "apps");
206 #endif
207         if (ecore_desktop_paths_kde_legacy)
208           {
209              char                temp[PATH_MAX], *path;
210              Ecore_List         *temp_list;
211
212              /* Copy it, cause Ecore_List walks can not be nested. */
213              temp_list = ecore_list_new();
214              if (temp_list)
215                {
216                   ecore_list_first_goto(ecore_desktop_paths_kde_legacy);
217                   while ((path =
218                           ecore_list_next(ecore_desktop_paths_kde_legacy)) !=
219                          NULL)
220                      ecore_list_append(temp_list, path);
221
222                   ecore_list_first_goto(temp_list);
223                   while ((path = ecore_list_next(temp_list)) != NULL)
224                     {
225                        char               *t1, *t2;
226
227                        t1 = rindex(path, '/');
228                        *t1 = '\0';
229                        t2 = rindex(path, '/');
230                        *t2 = '\0';
231                        sprintf(temp, "%s/apps/kappfinder/apps/", path);
232                        *t2 = '/';
233                        *t1 = '/';
234                        _ecore_desktop_paths_check_and_add
235                           (ecore_desktop_paths_kde_legacy, temp);
236                     }
237                }
238              ecore_list_destroy(temp_list);
239           }
240      }
241    if (!ecore_desktop_paths_icons)
242      {
243         char               *gnome;
244
245         ecore_desktop_paths_icons =
246            _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_ICONS, "~/.icons",
247                                     "XDG_DATA_HOME", "XDG_DATA_DIRS",
248                                     "~/.local/share:~/.kde/share",
249                                     "/usr/local/share:/usr/share:/usr/X11R6/share",
250                                     "icons:pixmaps", "dist/icons",
251                                     "icon:pixmap");
252         _ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
253                                            "/usr/local/share/pixmaps/");
254         _ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
255                                            "/usr/share/pixmaps/");
256         _ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
257                                            "/usr/share/update-desktop-files/kappfinder-icons/");
258         gnome = getenv("GNOME_ICON_PATH");
259         if (gnome)
260            _ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons, gnome);
261      }
262    if (!ecore_desktop_paths_menus)
263       ecore_desktop_paths_menus =
264          _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_MENUS, NULL,
265                                   "XDG_CONFIG_HOME", "XDG_CONFIG_DIRS",
266                                   "~/.config", "/etc/xdg:/var/lib/menu-xdg",
267                                   "menus", NULL, "xdgconf-menu");
268    if (!ecore_desktop_paths_directories)
269       ecore_desktop_paths_directories =
270          _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_DIRECTORIES, NULL,
271                                   "XDG_DATA_HOME", "XDG_DATA_DIRS",
272                                   "~/.local/share:~/.kde/share",
273                                   "/usr/local/share:/usr/share",
274                                   "desktop-directories", "gnome/vfolders",
275                                   "xdgdata-dirs");
276    if (!ecore_desktop_paths_config)
277       ecore_desktop_paths_config =
278          _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_CONFIG, NULL,
279                                   "XDG_CONFIG_HOME", "XDG_CONFIG_DIRS",
280                                   "~/.config", "/etc/xdg", NULL, NULL, NULL);
281    if (!ecore_desktop_paths_xsessions)
282       ecore_desktop_paths_xsessions =
283          _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_XSESSIONS, NULL,
284                                   "XDG_DATA_HOME", "XDG_DATA_DIRS",
285                                   "~/.local/share:~/.kde/share",
286                                   "/usr/local/share:/usr/share", "xsessions",
287                                   NULL, NULL);
288 }
289
290 static void
291 _ecore_desktop_paths_destroy(void)
292 {
293    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_xsessions);
294    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_config);
295    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_directories);
296    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_menus);
297    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_icons);
298    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_kde_legacy);
299    E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_desktops);
300 }
301
302 EAPI void
303 ecore_desktop_paths_extras_clear(void)
304 {
305    int                 i;
306
307    for (i = 0; i < ECORE_DESKTOP_PATHS_MAX; i++)
308      {
309         E_FN_DEL(ecore_list_destroy, prepend_user_paths[i]);
310         E_FN_DEL(ecore_list_destroy, prepend_system_paths[i]);
311         E_FN_DEL(ecore_list_destroy, append_user_paths[i]);
312         E_FN_DEL(ecore_list_destroy, append_system_paths[i]);
313         prepend_user_paths[i] = ecore_list_new();
314         if (prepend_user_paths[i])
315            ecore_list_free_cb_set(prepend_user_paths[i], free);
316         prepend_system_paths[i] = ecore_list_new();
317         if (prepend_system_paths[i])
318            ecore_list_free_cb_set(prepend_system_paths[i], free);
319         append_user_paths[i] = ecore_list_new();
320         if (append_user_paths[i])
321            ecore_list_free_cb_set(append_user_paths[i], free);
322         append_system_paths[i] = ecore_list_new();
323         if (append_system_paths[i])
324            ecore_list_free_cb_set(append_system_paths[i], free);
325      }
326 }
327
328 EAPI void
329 ecore_desktop_paths_prepend_user(Ecore_Desktop_Paths_Type type, const char *paths)
330 {
331    if (prepend_user_paths[type])
332       ecore_list_append(prepend_user_paths[type], strdup(paths));
333 }
334
335 EAPI void
336 ecore_desktop_paths_prepend_system(Ecore_Desktop_Paths_Type type, const char *paths)
337 {
338    if (prepend_system_paths[type])
339       ecore_list_append(prepend_system_paths[type], strdup(paths));
340 }
341
342 EAPI void
343 ecore_desktop_paths_append_user(Ecore_Desktop_Paths_Type type, const char *paths)
344 {
345    if (append_user_paths[type])
346       ecore_list_append(append_user_paths[type], strdup(paths));
347 }
348
349 EAPI void
350 ecore_desktop_paths_append_system(Ecore_Desktop_Paths_Type type, const char *paths)
351 {
352    if (append_system_paths[type])
353       ecore_list_append(append_system_paths[type], strdup(paths));
354 }
355
356 /** Search for a file in fdo compatible locations.
357  *
358  * This will search through all the diretories of a particular type, looking 
359  * for the file.  It will recurse into subdirectories.  If func is NULL, then
360  * only the first file found will be returned.  If func is defined, then each
361  * file found will be passed to func, until func returns 1.
362  *
363  * The returned string will have to be freed eventually.
364  *
365  * @param   type The type of directories to search.
366  * @param   file The file to search for.
367  * @param   sub Levels of sub directories to search, -1 = all, 0 = none.
368  * @param   func A function to call for each file found.
369  * @param   data A pointer to pass on to func.
370  */
371 char               *
372 ecore_desktop_paths_file_find(Ecore_List * paths, const char *file, int sub,
373                               int (*func) (void *data, const char *path),
374                               void *data)
375 {
376    char               *path = NULL, *this_path;
377    char                temp[PATH_MAX];
378    struct stat         path_stat;
379
380    if (!paths) return NULL;
381    ecore_list_first_goto(paths);
382    while ((this_path = ecore_list_next(paths)) != NULL)
383      {
384         if (path)
385           {
386              free(path);
387              path = NULL;
388           }
389         snprintf(temp, PATH_MAX, "%s%s", this_path, file);
390         if (stat(temp, &path_stat) == 0)
391           {
392              path = strdup(temp);
393              if ((func) && (func(data, temp)))
394                break;
395           }
396         else if (sub != 0)
397           path =
398              ecore_desktop_paths_recursive_search(this_path, file, sub,
399                                                   NULL, func, data);
400         if (path && (!func))
401           break;
402      }
403
404    return path;
405 }
406
407 /*  We need -
408 config file full of paths
409 menus=pathlist
410 desktops=pathlist
411 directories=pathlist
412 icons=pathlist
413 */
414
415 static Ecore_List  *
416 _ecore_desktop_paths_get(Ecore_Desktop_Paths_Type path_type,
417                          const char *before, const char *env_home, const char *env,
418                          const char *env_home_default, const char *env_default, const char *type,
419                          const char *gnome_extra, const char *kde)
420 {
421    Ecore_List         *paths = NULL;
422    Ecore_List         *types = NULL;
423    Ecore_List         *gnome_extras = NULL;
424    char                path[PATH_MAX];
425    Ecore_List         *env_list;
426
427
428 #ifdef KDE_SUPPORT
429    Ecore_List         *kdes = NULL;
430 #endif
431
432    paths = ecore_list_new();
433    if (!paths) return NULL;
434    ecore_list_free_cb_set(paths, free);
435    /* Don't sort them, as they are in preferred order from each source. */
436    /* Merge the results, there are probably some duplicates. */
437
438    if (type)
439       types = ecore_desktop_paths_to_list(type);
440    if (gnome_extra)
441       gnome_extras = ecore_desktop_paths_to_list(gnome_extra);
442 #ifdef KDE_SUPPORT
443    if (kde)
444       kdes = ecore_desktop_paths_to_list(kde);
445 #else
446    kde = NULL;
447 #endif
448
449    if (before)
450      {
451         Ecore_List         *befores;
452
453         befores = ecore_desktop_paths_to_list(before);
454         if (befores)
455           {
456              char               *this_before;
457
458              ecore_list_first_goto(befores);
459              while ((this_before = ecore_list_next(befores)) != NULL)
460                {
461                   _ecore_desktop_paths_massage_path(path, home,
462                                                     this_before, NULL);
463                   _ecore_desktop_paths_check_and_add(paths, path);
464                }
465              E_FN_DEL(ecore_list_destroy, befores);
466           }
467      }
468
469    if (prepend_user_paths[path_type])
470      {
471         char               *this_path;
472
473         ecore_list_first_goto(prepend_user_paths[path_type]);
474         while ((this_path = ecore_list_next(prepend_user_paths[path_type])) != NULL)
475           {
476              _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
477              _ecore_desktop_paths_check_and_add(paths, path);
478           }
479      }
480
481    if (env_home)
482      {
483         const char         *value;
484
485         value = getenv(env_home);
486         if ((value == NULL) || (value[0] == '\0'))
487           value = env_home_default;
488         env_list = ecore_desktop_paths_to_list(value);
489         if (env_list)
490           {
491              char               *this_env;
492
493              ecore_list_first_goto(env_list);
494              while ((this_env = ecore_list_next(env_list)) != NULL)
495                {
496                   if (types)
497                     {
498                        char *this_type;
499
500                        ecore_list_first_goto(types);
501                        while ((this_type = ecore_list_next(types)) != NULL)
502                          {
503                             _ecore_desktop_paths_massage_path(path, home,
504                                                          this_env, this_type);
505                             _ecore_desktop_paths_check_and_add(paths, path);
506                          }
507                     }
508                   else
509                     {
510                        _ecore_desktop_paths_massage_path(path, home, this_env, NULL);
511                        _ecore_desktop_paths_check_and_add(paths, path);
512                     }
513                }
514              E_FN_DEL(ecore_list_destroy, env_list);
515           }
516      }
517
518    if (append_user_paths[path_type])
519      {
520         char               *this_path;
521
522         ecore_list_first_goto(append_user_paths[path_type]);
523         while ((this_path = ecore_list_next(append_user_paths[path_type])) != NULL)
524           {
525              _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
526              _ecore_desktop_paths_check_and_add(paths, path);
527           }
528      }
529
530    if (prepend_system_paths[path_type])
531      {
532         char               *this_path;
533
534         ecore_list_first_goto(prepend_system_paths[path_type]);
535         while ((this_path = ecore_list_next(prepend_system_paths[path_type])) != NULL)
536           {
537              _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
538              _ecore_desktop_paths_check_and_add(paths, path);
539           }
540      }
541
542    if (env)
543      {
544         const char         *value;
545
546         value = getenv(env);
547         if ((value == NULL) || (value[0] == '\0'))
548           value = env_default;
549         env_list = ecore_desktop_paths_to_list(value);
550         if (env_list)
551           {
552              char               *this_env;
553
554              ecore_list_first_goto(env_list);
555              while ((this_env = ecore_list_next(env_list)) != NULL)
556                {
557                   if (types)
558                     {
559                        char *this_type;
560
561                        ecore_list_first_goto(types);
562                        while ((this_type = ecore_list_next(types)) != NULL)
563                          {
564                             _ecore_desktop_paths_massage_path(path, home,
565                                                          this_env, this_type);
566                             _ecore_desktop_paths_check_and_add(paths, path);
567                          }
568                     }
569                   else
570                     {
571                        _ecore_desktop_paths_massage_path(path, home, this_env, NULL);
572                        _ecore_desktop_paths_check_and_add(paths, path);
573                     }
574                }
575              E_FN_DEL(ecore_list_destroy, env_list);
576           }
577      }
578
579    if (append_system_paths[path_type])
580      {
581         char               *this_path;
582
583         ecore_list_first_goto(append_system_paths[path_type]);
584         while ((this_path = ecore_list_next(append_system_paths[path_type])) != NULL)
585           {
586              _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
587              _ecore_desktop_paths_check_and_add(paths, path);
588           }
589      }
590
591    /*
592     * Get the pathlist from the config file - type=pathlist
593     *   for each path in config
594     *      if it is already in paths, skip it
595     *      if it exists, add it to end of paths
596     */
597
598    if (gnome_data)
599      {
600         char               *this_gnome;
601
602         ecore_list_first_goto(gnome_data);
603         while ((this_gnome = ecore_list_next(gnome_data)) != NULL)
604           {
605              if (types)
606                {
607                   char *this_type;
608
609                   ecore_list_first_goto(types);
610                   while ((this_type = ecore_list_next(types)) != NULL)
611                      {
612                         _ecore_desktop_paths_massage_path(path, home,
613                                                          this_gnome, this_type);
614                         _ecore_desktop_paths_check_and_add(paths, path);
615                      }
616                }
617              else
618                {
619                   _ecore_desktop_paths_massage_path(path, home, this_gnome, NULL);
620                   _ecore_desktop_paths_check_and_add(paths, path);
621                }
622           }
623      }
624    if (gnome_data && gnome_extras)
625      {
626         char               *this_gnome, *this_type;
627
628         ecore_list_first_goto(gnome_data);
629         while ((this_gnome = ecore_list_next(gnome_data)) != NULL)
630           {
631              ecore_list_first_goto(gnome_extras);
632              while ((this_type = ecore_list_next(gnome_extras)) != NULL)
633                {
634                   _ecore_desktop_paths_massage_path(path, home,
635                                                     this_gnome, this_type);
636                   _ecore_desktop_paths_check_and_add(paths, path);
637                }
638           }
639      }
640
641 #ifdef KDE_SUPPORT
642    if ((exit_handler != NULL) && (kdes != NULL))
643      {
644         char               *this_kde;
645
646         ecore_list_first_goto(kdes);
647         while ((this_kde = ecore_list_next(kdes)) != NULL)
648           {
649              char                cmd[128];
650
651              sprintf(cmd, "kde-config --path %s", this_kde);
652              _ecore_desktop_paths_exec_config(paths, home, NULL, cmd);
653           }
654      }
655 #endif
656
657 #ifdef KDE_SUPPORT
658    E_FN_DEL(ecore_list_destroy, kdes);
659 #endif
660    E_FN_DEL(ecore_list_destroy, gnome_extras);
661    E_FN_DEL(ecore_list_destroy, types);
662
663    return paths;
664 }
665
666 static void
667 _ecore_desktop_paths_massage_path(char *path, char *home, char *first,
668                                   char *second)
669 {
670    int                 last;
671
672    /* Strip traling slash of first. */
673    last = strlen(first) - 1;
674    while ((last >= 0) && (first[last] == '/'))
675      {
676         first[last] = '\0';
677         last--;
678      }
679
680    if (second)
681      {
682         /* Strip traling slash of second. */
683         last = strlen(second) - 1;
684         while ((last >= 0) && (second[last] == '/'))
685           {
686              second[last] = '\0';
687              last--;
688           }
689      }
690
691    if ((second) && (second[0] != '\0'))
692      {
693         if (first[0] == '~')
694           sprintf(path, "%s%s/%s/", home, &first[1],
695                   &second[(second[0] == '/') ? 1 : 0]);
696         else
697           sprintf(path, "%s/%s/", first, &second[(second[0] == '/') ? 1 : 0]);
698      }
699    else
700      {
701         if (first[0] == '~')
702           sprintf(path, "%s%s/", home, &first[1]);
703         else
704           sprintf(path, "%s/", first);
705      }
706 }
707
708 static void
709 _ecore_desktop_paths_check_and_add(Ecore_List * paths, const char *path)
710 {
711    struct stat         path_stat;
712    char               *this_path;
713
714    if (!paths) return;
715
716    /* Check if we have it already. */
717    ecore_list_first_goto(paths);
718    while ((this_path = ecore_list_next(paths)) != NULL)
719      {
720         if (strcmp(path, this_path) == 0)
721            return;
722      }
723
724    /* Check if the path exists. */
725    if ((stat(path, &path_stat) == 0) && (S_ISDIR(path_stat.st_mode)))
726       ecore_list_append(paths, strdup(path));
727 }
728
729 char               *
730 ecore_desktop_paths_recursive_search(const char *path, const char *file,
731                                      int sub, int (*dir_func) (void *data,
732                                                                const char
733                                                                *path),
734                                      int (*func) (void *data, const char *path),
735                                      void *data)
736 {
737    char               *fpath = NULL;
738    DIR                *dir = NULL;
739    struct dirent      *script;
740
741    if ((sub != 0) && (sub != -1))
742       sub -= 1;
743
744    dir = opendir(path);
745    if (!dir) return NULL;
746
747    while ((script = readdir(dir)) != NULL)
748      {
749         struct stat         script_stat;
750         char                info_text[PATH_MAX];
751
752         sprintf(info_text, "%s%s", path, script->d_name);
753         if ((stat(info_text, &script_stat) == 0))
754           {
755              if (S_ISDIR(script_stat.st_mode))
756                {
757                   if ((strcmp(basename(info_text), ".") != 0) &&
758                       (strcmp(basename(info_text), "..") != 0))
759                     {
760                        snprintf(info_text, PATH_MAX, "%s%s/", path, script->d_name);
761                        if ((dir_func) && (dir_func(data, info_text)))
762                            break;
763                        if (sub != 0)
764                          {
765                             if (fpath) free(fpath);
766                             fpath =
767                                ecore_desktop_paths_recursive_search(info_text, file, sub,
768                                                                     dir_func, func, data);
769                          }
770                     }
771                }
772              else
773                {
774                   if (file)
775                     {
776                        if (strcmp(basename(info_text), file) == 0)
777                          {
778                             if (fpath)
779                               free(fpath);
780                             fpath = strdup(info_text);
781                             if ((func) && (func(data, path)))
782                                 break;
783                          }
784                     }
785                   else
786                     {
787                        if ((func) && (func(data, info_text)))
788                            break;
789                     }
790                }
791              if (fpath && (!func))
792                break;
793           }
794      }
795    closedir(dir);
796
797    return fpath;
798 }
799
800 #if defined GNOME_SUPPORT || defined KDE_SUPPORT
801 static void
802 _ecore_desktop_paths_exec_config(Ecore_List * paths, char *home,
803                                  Ecore_List * extras, char *cmd)
804 {
805    Ecore_Exe          *exe;
806    struct _config_exe_data ced;
807
808    ced.home = home;
809    ced.paths = paths;
810    ced.types = extras;
811    ced.done = 0;
812    exe =
813       ecore_exe_pipe_run(cmd,
814                          ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ |
815                          ECORE_EXE_PIPE_READ_LINE_BUFFERED, &ced);
816    if (exe)
817      {
818         ecore_exe_tag_set(exe, "genmenu/fdo");
819         while (ced.done == 0)
820           {
821              /* FIXME: raster is paranoid.  If too much time passes, give up.
822               * Or find a way to let the usual event loop shit do this without 
823               * spinning our wheels.  On the other hand, these are quick 
824               * commands, and we NEED this data before we can continue.  On 
825               * the gripping hand, some tweaking of the stuff searched for not 
826               * only gets rid of the need for this, but also speeds things up 
827               * drastically.
828               */
829              ecore_main_loop_iterate();
830              usleep(10);
831           }
832      }
833 }
834
835 static int
836 _ecore_desktop_paths_cb_exe_exit(void *data, int type, void *event)
837 {
838    Ecore_Exe_Event_Del *ev;
839    Ecore_List         *paths;
840    Ecore_List         *config_list;
841    Ecore_Exe_Event_Data *read;
842    struct _config_exe_data *ced;
843    char               *value;
844    char                path[PATH_MAX];
845
846    ev = event;
847    if (!ev->exe)
848       return 1;
849    value = ecore_exe_tag_get(ev->exe);
850    if ((!value) || (strcmp(value, "genmenu/fdo")) != 0)
851       return 1;
852    ced = ecore_exe_data_get(ev->exe);
853    if (!ced)
854       return 1;
855    paths = ced->paths;
856    if (!paths)
857       return 1;
858
859    read = ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_READ);
860    if ((read) && (read->lines[0].line))
861      {
862         value = read->lines[0].line;
863         config_list = ecore_desktop_paths_to_list(value);
864         if (config_list)
865           {
866              char               *this_config, *this_type;
867
868              ecore_list_first_goto(config_list);
869              while ((this_config = ecore_list_next(config_list)) != NULL)
870                {
871                   if (ced->types)
872                     {
873                        ecore_list_first_goto(ced->types);
874                        while ((this_type =
875                                 ecore_list_next(ced->types)) != NULL)
876                          {
877                             _ecore_desktop_paths_massage_path(path,
878                                   ced->home,
879                                   this_config,
880                                   this_type);
881                             _ecore_desktop_paths_check_and_add(paths,
882                                   path);
883                          }
884                     }
885                   else
886                     {
887                        _ecore_desktop_paths_massage_path(path, ced->home,
888                              this_config,
889                              NULL);
890                        _ecore_desktop_paths_check_and_add(paths, path);
891                     }
892                }
893              E_FN_DEL(ecore_list_destroy, config_list);
894           }
895      }
896    ced->done = 1;
897    return 1;
898 }
899 #endif
900
901 /** Split a list of paths into an Ecore_Hash.
902  *
903  * The list of paths can use any one of ;:, to seperate the paths.
904  * You can also escape the :;, with \.
905  *
906  * @param   paths A list of paths.
907  */
908 Ecore_Hash         *
909 ecore_desktop_paths_to_hash(const char *paths)
910 {
911    Ecore_Hash         *result;
912    char               *path;
913    char                buf[PATH_MAX], *p, *pp;
914
915    if (!paths) return NULL;
916    result = ecore_hash_new(ecore_str_hash, ecore_str_compare);
917    if (!result) return NULL;
918    ecore_hash_free_key_cb_set(result, free);
919    ecore_hash_free_value_cb_set(result, free);
920
921    path = strdup(paths);
922    if (path)
923      {
924         p = path;
925         while (p)
926           {
927              pp = buf;
928              while (*p)
929                {
930                   /* Check for escape */
931                   if (*p == '\\')
932                     {
933                        /* If separator, skip escape */
934                        if ((*(p + 1) == ';') || (*(p + 1) == ':') || (*(p + 1) == ','))
935                          p++;
936                     }
937                   /* Check for separator */
938                   else if ((*p == ';') || (*p == ':') || (*p == ','))
939                     break;
940                   *pp = *p;
941                   pp++;
942                   p++;
943                }
944              *pp = '\0';
945              if (*buf) ecore_hash_set(result, strdup(buf), strdup(buf));
946              if (*p) p++;
947              else p = NULL;
948           }
949         free(path);
950      }
951    return result;
952 }
953
954 /** Split a list of paths into an Ecore_Hash.
955  *
956  * The list of paths can use any one of ;:, to seperate the paths.
957  * You can also escape the :;, with \.
958  *
959  * @param   paths A list of paths.
960  */
961 Ecore_List         *
962 ecore_desktop_paths_to_list(const char *paths)
963 {
964    Ecore_List         *result;
965    char               *path;
966    char                buf[PATH_MAX], *p, *pp;
967
968    if (!paths) return NULL;
969    result = ecore_list_new();
970    if (!result) return NULL;
971    ecore_list_free_cb_set(result, free);
972
973    path = strdup(paths);
974    if (path)
975      {
976         p = path;
977         while (p)
978           {
979              pp = buf;
980              while (*p)
981                {
982                   /* Check for escape */
983                   if (*p == '\\')
984                     {
985                        /* If separator, skip escape */
986                        if ((*(p + 1) == ';') || (*(p + 1) == ':') || (*(p + 1) == ','))
987                          p++;
988                     }
989                   /* Check for separator */
990                   else if ((*p == ';') || (*p == ':') || (*p == ','))
991                     break;
992                   *pp = *p;
993                   pp++;
994                   p++;
995                }
996              *pp = '\0';
997              if (*buf) ecore_list_append(result, strdup(buf));
998              if (*p) p++;
999              else p = NULL;
1000           }
1001         free(path);
1002      }
1003    return result;
1004 }
1005
1006 EAPI int
1007 ecore_desktop_paths_for_each(Ecore_Desktop_Paths_Type type,
1008                              Ecore_For_Each function, void *user_data)
1009 {
1010    Ecore_List         *list = NULL;
1011
1012    switch (type)
1013      {
1014      case ECORE_DESKTOP_PATHS_CONFIG:
1015         list = ecore_desktop_paths_config;
1016         break;
1017      case ECORE_DESKTOP_PATHS_MENUS:
1018         list = ecore_desktop_paths_menus;
1019         break;
1020      case ECORE_DESKTOP_PATHS_DIRECTORIES:
1021         list = ecore_desktop_paths_directories;
1022         break;
1023      case ECORE_DESKTOP_PATHS_DESKTOPS:
1024         list = ecore_desktop_paths_desktops;
1025         break;
1026      case ECORE_DESKTOP_PATHS_ICONS:
1027         list = ecore_desktop_paths_icons;
1028         break;
1029      case ECORE_DESKTOP_PATHS_KDE_LEGACY:
1030         list = ecore_desktop_paths_kde_legacy;
1031         break;
1032      case ECORE_DESKTOP_PATHS_XSESSIONS:
1033         list = ecore_desktop_paths_xsessions;
1034         break;
1035      case ECORE_DESKTOP_PATHS_MAX:
1036         break;
1037      }
1038    if (list)
1039       return ecore_list_for_each(list, function, user_data);
1040    return 0;
1041 }