/*
 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
 */
/*
 * This conforms with the freedesktop.org XDG Base Directory Specification version 0.6
 *
 * The policy here is to add extra directories to the possible search paths to 
 * cater for quirks of different OS's.  It doesn't take long to eliminate an
 * excess directory from the paths.
 */

#include <Ecore.h>

#include "Ecore_Desktop.h"
#include "ecore_desktop_private.h"

#include <dirent.h>
#include <sys/stat.h>
#include <libgen.h>
#include <strings.h>

/* FIXME: Add functions to add paths before the user paths, after the user but before the system paths, and at the end. */

/* This really slows things down and no doubt drags in some KDE bloat at start up.
 * To work around this, I add a few extra things to try in the _ecore_desktop_paths_get()
 * calls below.
 *
#define KDE_SUPPORT 1
 */

/* This is nowhere near as bloated and slow as the KDE stuff, but worthwhile 
 * making it optional anyway.
 *
#define GNOME_SUPPORT 1
 */

/* FIXME: remove those two defines, preload a cache with the guesses, whenever 
 * we have a few seconds of idle time, run the gnome and kde config proggies 
 * and correct those guesses.
 */

Ecore_List         *ecore_desktop_paths_config = NULL;
Ecore_List         *ecore_desktop_paths_menus = NULL;
Ecore_List         *ecore_desktop_paths_directories = NULL;
Ecore_List         *ecore_desktop_paths_desktops = NULL;
Ecore_List         *ecore_desktop_paths_icons = NULL;
Ecore_List         *ecore_desktop_paths_kde_legacy = NULL;
Ecore_List         *ecore_desktop_paths_xsessions = NULL;


static Ecore_List  *_ecore_desktop_paths_get(Ecore_Desktop_Paths_Type path_type,
					     char *before, char *env_home,
					     char *env, char *env_home_default,
					     char *env_default, char *type,
					     char *gnome_extra, char *kde);
static void         _ecore_desktop_paths_massage_path(char *path, char *home,
						      char *first,
						      char *second);
static void         _ecore_desktop_paths_check_and_add(Ecore_List * paths,
						       char *path);
static void         _ecore_desktop_paths_create();
static void         _ecore_desktop_paths_destroy();

static Ecore_List  *gnome_data = NULL;
static Ecore_List  *prepend_user_paths[ECORE_DESKTOP_PATHS_MAX];
static Ecore_List  *prepend_system_paths[ECORE_DESKTOP_PATHS_MAX];
static Ecore_List  *append_user_paths[ECORE_DESKTOP_PATHS_MAX];
static Ecore_List  *append_system_paths[ECORE_DESKTOP_PATHS_MAX];
static char        *home;
static int          init_count = 0;

#if defined GNOME_SUPPORT || defined KDE_SUPPORT
struct _config_exe_data
{
   char               *home;
   Ecore_List         *paths, *types;
   int                 done;
};

static void         _ecore_desktop_paths_exec_config(Ecore_List * paths,
						     char *home,
						     Ecore_List * extras,
						     char *cmd);

static int          _ecore_desktop_paths_cb_exe_exit(void *data, int type,
						     void *event);

static Ecore_Event_Handler *exit_handler = NULL;
#endif

EAPI int
ecore_desktop_paths_init(void)
{
   if (++init_count != 1)
      return init_count;

#if defined GNOME_SUPPORT || defined KDE_SUPPORT
   exit_handler =
      ecore_event_handler_add(ECORE_EXE_EVENT_DEL,
			      _ecore_desktop_paths_cb_exe_exit, NULL);
#endif

   ecore_desktop_paths_extras_clear();
   gnome_data = ecore_list_new();
   home = ecore_desktop_home_get();

   if (gnome_data)
     {
#ifdef GNOME_SUPPORT
	if (exit_handler)
	  {
	     ecore_list_set_free_cb(gnome_data, free);
	     _ecore_desktop_paths_exec_config(gnome_data, home, NULL,
					      "gnome-config --datadir");
	  }
#else
	Ecore_List         *config_list;

	config_list = ecore_desktop_paths_to_list("/opt/gnome/share");
	if (config_list)
	  {
	     char               *this_config;
	     char                path[PATH_MAX];

	     ecore_list_goto_first(config_list);
	     while ((this_config = ecore_list_next(config_list)) != NULL)
	       {

		  _ecore_desktop_paths_massage_path(path, home,
						    this_config, NULL);
		  _ecore_desktop_paths_check_and_add(gnome_data, path);
	       }
	     E_FN_DEL(ecore_list_destroy, config_list);
	  }
#endif
     }

   _ecore_desktop_paths_create();
   return init_count;
}

EAPI int
ecore_desktop_paths_shutdown(void)
{
   int i;

   if (--init_count != 0)
      return init_count;

   for (i = 0; i < ECORE_DESKTOP_PATHS_MAX; i++)
     {
	E_FN_DEL(ecore_list_destroy, prepend_user_paths[i]);
	E_FN_DEL(ecore_list_destroy, prepend_system_paths[i]);
	E_FN_DEL(ecore_list_destroy, append_user_paths[i]);
	E_FN_DEL(ecore_list_destroy, append_system_paths[i]);
     }

   _ecore_desktop_paths_destroy();
   E_FN_DEL(ecore_list_destroy, gnome_data);

#if defined GNOME_SUPPORT || defined KDE_SUPPORT
   if (exit_handler)
      ecore_event_handler_del(exit_handler);
#endif
   free(home);
   return init_count;
}

EAPI void
ecore_desktop_paths_regen(void)
{
   _ecore_desktop_paths_destroy();
   _ecore_desktop_paths_create();
}

static void
_ecore_desktop_paths_create(void)
{
   if (!ecore_desktop_paths_desktops)
     {
	ecore_desktop_paths_desktops =
	   _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_DESKTOPS, NULL,
				    "XDG_DATA_HOME", "XDG_DATA_DIRS",
				    "~/.local/share:~/.kde/share",
				    "/usr/local/share:/usr/share",
				    "applications:applnk:applications/kde",
				    "dist/desktop-files:dist/short-menu:gnome/apps",
				    "xdgdata-apps:apps");
	_ecore_desktop_paths_check_and_add(ecore_desktop_paths_desktops,
					   "/usr/local/share/update-desktop-files/templates");
	_ecore_desktop_paths_check_and_add(ecore_desktop_paths_desktops,
					   "/usr/share/update-desktop-files/templates");
     }
   if (!ecore_desktop_paths_kde_legacy)
     {
#ifdef KDE_SUPPORT
	ecore_desktop_paths_kde_legacy =
	   _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_KDE_LEGACY, NULL, NULL,
				    NULL, NULL, NULL, NULL, NULL, "apps");
#else
	ecore_desktop_paths_kde_legacy =
	   _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_KDE_LEGACY, NULL,
				    "XDG_DATA_HOME", "XDG_DATA_DIRS",
				    "~/.local/share:~/.kde/share",
				    "/usr/local/share:/usr/share", "applnk",
				    NULL, "apps");
#endif
	if (ecore_desktop_paths_kde_legacy)
	  {
	     char                temp[PATH_MAX], *path;
	     Ecore_List         *temp_list;

	     /* Copy it, cause Ecore_List walks can not be nested. */
	     temp_list = ecore_list_new();
	     if (temp_list)
	       {
		  ecore_list_goto_first(ecore_desktop_paths_kde_legacy);
		  while ((path =
			  ecore_list_next(ecore_desktop_paths_kde_legacy)) !=
			 NULL)
		     ecore_list_append(temp_list, path);

		  ecore_list_goto_first(temp_list);
		  while ((path = ecore_list_next(temp_list)) != NULL)
		    {
		       char               *t1, *t2;

		       t1 = rindex(path, '/');
		       *t1 = '\0';
		       t2 = rindex(path, '/');
		       *t2 = '\0';
		       sprintf(temp, "%s/apps/kappfinder/apps/", path);
		       *t2 = '/';
		       *t1 = '/';
		       _ecore_desktop_paths_check_and_add
			  (ecore_desktop_paths_kde_legacy, temp);
		    }
	       }
	     ecore_list_destroy(temp_list);
	  }
     }
   if (!ecore_desktop_paths_icons)
     {
	char               *gnome;

	ecore_desktop_paths_icons =
	   _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_ICONS, "~/.icons",
				    "XDG_DATA_HOME", "XDG_DATA_DIRS",
				    "~/.local/share:~/.kde/share",
				    "/usr/local/share:/usr/share:/usr/X11R6/share",
				    "icons:pixmaps", "dist/icons",
				    "icon:pixmap");
	_ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
					   "/usr/local/share/pixmaps/");
	_ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
					   "/usr/share/pixmaps/");
	_ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons,
					   "/usr/share/update-desktop-files/kappfinder-icons/");
	gnome = getenv("GNOME_ICON_PATH");
	if (gnome)
	   _ecore_desktop_paths_check_and_add(ecore_desktop_paths_icons, gnome);
     }
   if (!ecore_desktop_paths_menus)
      ecore_desktop_paths_menus =
	 _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_MENUS, NULL,
				  "XDG_CONFIG_HOME", "XDG_CONFIG_DIRS",
				  "~/.config", "/etc/xdg:/var/lib/menu-xdg",
				  "menus", NULL, "xdgconf-menu");
   if (!ecore_desktop_paths_directories)
      ecore_desktop_paths_directories =
	 _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_DIRECTORIES, NULL,
				  "XDG_DATA_HOME", "XDG_DATA_DIRS",
				  "~/.local/share:~/.kde/share",
				  "/usr/local/share:/usr/share",
				  "desktop-directories", "gnome/vfolders",
				  "xdgdata-dirs");
   if (!ecore_desktop_paths_config)
      ecore_desktop_paths_config =
	 _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_CONFIG, NULL,
				  "XDG_CONFIG_HOME", "XDG_CONFIG_DIRS",
				  "~/.config", "/etc/xdg", "", NULL, NULL);
   if (!ecore_desktop_paths_xsessions)
      ecore_desktop_paths_xsessions =
	 _ecore_desktop_paths_get(ECORE_DESKTOP_PATHS_XSESSIONS, NULL,
				  "XDG_DATA_HOME", "XDG_DATA_DIRS",
				  "~/.local/share:~/.kde/share",
				  "/usr/local/share:/usr/share", "xsessions",
				  NULL, NULL);
}

static void
_ecore_desktop_paths_destroy(void)
{
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_xsessions);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_config);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_directories);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_menus);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_icons);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_kde_legacy);
   E_FN_DEL(ecore_list_destroy, ecore_desktop_paths_desktops);
}

EAPI void
ecore_desktop_paths_extras_clear(void)
{
   int                 i;

   for (i = 0; i < ECORE_DESKTOP_PATHS_MAX; i++)
     {
	E_FN_DEL(ecore_list_destroy, prepend_user_paths[i]);
	E_FN_DEL(ecore_list_destroy, prepend_system_paths[i]);
	E_FN_DEL(ecore_list_destroy, append_user_paths[i]);
	E_FN_DEL(ecore_list_destroy, append_system_paths[i]);
	prepend_user_paths[i] = ecore_list_new();
	if (prepend_user_paths[i])
	   ecore_list_set_free_cb(prepend_user_paths[i], free);
	prepend_system_paths[i] = ecore_list_new();
	if (prepend_system_paths[i])
	   ecore_list_set_free_cb(prepend_system_paths[i], free);
	append_user_paths[i] = ecore_list_new();
	if (append_user_paths[i])
	   ecore_list_set_free_cb(append_user_paths[i], free);
	append_system_paths[i] = ecore_list_new();
	if (append_system_paths[i])
	   ecore_list_set_free_cb(append_system_paths[i], free);
     }
}

EAPI void
ecore_desktop_paths_prepend_user(Ecore_Desktop_Paths_Type type, char *paths)
{
   if (prepend_user_paths[type])
      ecore_list_append(prepend_user_paths[type], strdup(paths));
}

EAPI void
ecore_desktop_paths_prepend_system(Ecore_Desktop_Paths_Type type, char *paths)
{
   if (prepend_system_paths[type])
      ecore_list_append(prepend_system_paths[type], strdup(paths));
}

EAPI void
ecore_desktop_paths_append_user(Ecore_Desktop_Paths_Type type, char *paths)
{
   if (append_user_paths[type])
      ecore_list_append(append_user_paths[type], strdup(paths));
}

EAPI void
ecore_desktop_paths_append_system(Ecore_Desktop_Paths_Type type, char *paths)
{
   if (append_system_paths[type])
      ecore_list_append(append_system_paths[type], strdup(paths));
}

/** Search for a file in fdo compatible locations.
 *
 * This will search through all the diretories of a particular type, looking 
 * for the file.  It will recurse into subdirectories.  If func is NULL, then
 * only the first file found will be returned.  If func is defined, then each
 * file found will be passed to func, until func returns 1.
 *
 * The returned string will have to be freed eventually.
 *
 * @param   type The type of directories to search.
 * @param   file The file to search for.
 * @param   sub Levels of sub directories to search, -1 = all, 0 = none.
 * @param   func A function to call for each file found.
 * @param   data A pointer to pass on to func.
 */
char               *
ecore_desktop_paths_file_find(Ecore_List * paths, const char *file, int sub,
			      int (*func) (void *data, const char *path),
			      void *data)
{
   char               *path = NULL, *this_path;
   char                temp[PATH_MAX];
   struct stat         path_stat;

   if (!paths) return NULL;
   ecore_list_goto_first(paths);
   while ((this_path = ecore_list_next(paths)) != NULL)
     {
	if (path)
	  {
	     free(path);
	     path = NULL;
	  }
	snprintf(temp, PATH_MAX, "%s%s", this_path, file);
	if (stat(temp, &path_stat) == 0)
	  {
	     path = strdup(temp);
	     if ((func) && (func(data, temp)))
	       break;
	  }
	else if (sub != 0)
	  path =
	     ecore_desktop_paths_recursive_search(this_path, file, sub,
						  NULL, func, data);
	if (path && (!func))
	  break;
     }

   return path;
}

/*  We need -
config file full of paths
menus=pathlist
desktops=pathlist
directories=pathlist
icons=pathlist
*/

static Ecore_List  *
_ecore_desktop_paths_get(Ecore_Desktop_Paths_Type path_type,
			 char *before, char *env_home, char *env,
			 char *env_home_default, char *env_default, char *type,
			 char *gnome_extra, char *kde)
{
   Ecore_List         *paths = NULL;
   Ecore_List         *types = NULL;
   Ecore_List         *gnome_extras = NULL;
   char                path[PATH_MAX];
   Ecore_List         *env_list;


#ifdef KDE_SUPPORT
   Ecore_List         *kdes = NULL;
#endif

   paths = ecore_list_new();
   if (!paths) return NULL;
   ecore_list_set_free_cb(paths, free);
   /* Don't sort them, as they are in preferred order from each source. */
   /* Merge the results, there are probably some duplicates. */

   if (type)
      types = ecore_desktop_paths_to_list(type);
   if (gnome_extra)
      gnome_extras = ecore_desktop_paths_to_list(gnome_extra);
#ifdef KDE_SUPPORT
   if (kde)
      kdes = ecore_desktop_paths_to_list(kde);
#endif

   if (before)
     {
	Ecore_List         *befores;

	befores = ecore_desktop_paths_to_list(before);
	if (befores)
	  {
	     char               *this_before;

	     ecore_list_goto_first(befores);
	     while ((this_before = ecore_list_next(befores)) != NULL)
	       {
		  _ecore_desktop_paths_massage_path(path, home,
						    this_before, NULL);
		  _ecore_desktop_paths_check_and_add(paths, path);
	       }
	     E_FN_DEL(ecore_list_destroy, befores);
	  }
     }

   if (prepend_user_paths[path_type])
     {
	char               *this_path;

	ecore_list_goto_first(prepend_user_paths[path_type]);
	while ((this_path = ecore_list_next(prepend_user_paths[path_type])) != NULL)
	  {
	     _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
	     _ecore_desktop_paths_check_and_add(paths, path);
	  }
     }

   if (env_home)
     {
	char               *value;

	value = getenv(env_home);
	if ((value == NULL) || (value[0] == '\0'))
	  value = env_home_default;
	env_list = ecore_desktop_paths_to_list(value);
	if (env_list && types)
	  {
	     char               *this_env, *this_type;

	     ecore_list_goto_first(env_list);
	     while ((this_env = ecore_list_next(env_list)) != NULL)
	       {
		  ecore_list_goto_first(types);
		  while ((this_type = ecore_list_next(types)) != NULL)
		    {
		       _ecore_desktop_paths_massage_path(path, home,
							 this_env, this_type);
		       _ecore_desktop_paths_check_and_add(paths, path);
		    }
	       }
	     E_FN_DEL(ecore_list_destroy, env_list);
	  }
     }

   if (append_user_paths[path_type])
     {
	char               *this_path;

	ecore_list_goto_first(append_user_paths[path_type]);
	while ((this_path = ecore_list_next(append_user_paths[path_type])) != NULL)
	  {
	     _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
	     _ecore_desktop_paths_check_and_add(paths, path);
	  }
     }

   if (prepend_system_paths[path_type])
     {
	char               *this_path;

	ecore_list_goto_first(prepend_system_paths[path_type]);
	while ((this_path = ecore_list_next(prepend_system_paths[path_type])) != NULL)
	  {
	     _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
	     _ecore_desktop_paths_check_and_add(paths, path);
	  }
     }

   if (env)
     {
	char               *value;

	value = getenv(env);
	if ((value == NULL) || (value[0] == '\0'))
	  value = env_default;
	env_list = ecore_desktop_paths_to_list(value);
	if (env_list && types)
	  {
	     char               *this_env, *this_type;

	     ecore_list_goto_first(env_list);
	     while ((this_env = ecore_list_next(env_list)) != NULL)
	       {
		  ecore_list_goto_first(types);
		  while ((this_type = ecore_list_next(types)) != NULL)
		    {
		       _ecore_desktop_paths_massage_path(path, home,
							 this_env, this_type);
		       _ecore_desktop_paths_check_and_add(paths, path);
		    }
	       }
	     E_FN_DEL(ecore_list_destroy, env_list);
	  }
     }

   if (append_system_paths[path_type])
     {
	char               *this_path;

	ecore_list_goto_first(append_system_paths[path_type]);
	while ((this_path = ecore_list_next(append_system_paths[path_type])) != NULL)
	  {
	     _ecore_desktop_paths_massage_path(path, home, this_path, NULL);
	     _ecore_desktop_paths_check_and_add(paths, path);
	  }
     }

   /*
    * Get the pathlist from the config file - type=pathlist
    *   for each path in config
    *      if it is already in paths, skip it
    *      if it exists, add it to end of paths
    */

   if (gnome_data && types)
     {
	char               *this_gnome, *this_type;

	ecore_list_goto_first(gnome_data);
	while ((this_gnome = ecore_list_next(gnome_data)) != NULL)
	  {
	     ecore_list_goto_first(types);
	     while ((this_type = ecore_list_next(types)) != NULL)
	       {
		  _ecore_desktop_paths_massage_path(path, home,
						    this_gnome, this_type);
		  _ecore_desktop_paths_check_and_add(paths, path);
	       }
	  }
     }
   if (gnome_data && gnome_extras)
     {
	char               *this_gnome, *this_type;

	ecore_list_goto_first(gnome_data);
	while ((this_gnome = ecore_list_next(gnome_data)) != NULL)
	  {
	     ecore_list_goto_first(gnome_extras);
	     while ((this_type = ecore_list_next(gnome_extras)) != NULL)
	       {
		  _ecore_desktop_paths_massage_path(path, home,
						    this_gnome, this_type);
		  _ecore_desktop_paths_check_and_add(paths, path);
	       }
	  }
     }

#ifdef KDE_SUPPORT
   if ((exit_handler != NULL) && (kdes != NULL))
     {
	char               *this_kde;

	ecore_list_goto_first(kdes);
	while ((this_kde = ecore_list_next(kdes)) != NULL)
	  {
	     char                cmd[128];

	     sprintf(cmd, "kde-config --path %s", this_kde);
	     _ecore_desktop_paths_exec_config(paths, home, NULL, cmd);
	  }
     }
#endif

#ifdef KDE_SUPPORT
   E_FN_DEL(ecore_list_destroy, kdes);
#endif
   E_FN_DEL(ecore_list_destroy, gnome_extras);
   E_FN_DEL(ecore_list_destroy, types);

   return paths;
}

static void
_ecore_desktop_paths_massage_path(char *path, char *home, char *first,
				  char *second)
{
   int                 last;

   /* Strip traling slash of first. */
   last = strlen(first) - 1;
   while ((last >= 0) && (first[last] == '/'))
     {
	first[last] = '\0';
	last--;
     }

   if (second)
     {
	/* Strip traling slash of second. */
	last = strlen(second) - 1;
	while ((last >= 0) && (second[last] == '/'))
	  {
	     second[last] = '\0';
	     last--;
	  }
     }

   if ((second) && (second[0] != '\0'))
     {
	if (first[0] == '~')
	  sprintf(path, "%s%s/%s/", home, &first[1],
		  &second[(second[0] == '/') ? 1 : 0]);
	else
	  sprintf(path, "%s/%s/", first, &second[(second[0] == '/') ? 1 : 0]);
     }
   else
     {
	if (first[0] == '~')
	  sprintf(path, "%s%s/", home, &first[1]);
	else
	  sprintf(path, "%s/", first);
     }
}

static void
_ecore_desktop_paths_check_and_add(Ecore_List * paths, char *path)
{
   struct stat         path_stat;
   char               *this_path;

   if (!paths) return;

   /* Check if we have it already. */
   ecore_list_goto_first(paths);
   while ((this_path = ecore_list_next(paths)) != NULL)
     {
	if (strcmp(path, this_path) == 0)
	   return;
     }

   /* Check if the path exists. */
   if ((stat(path, &path_stat) == 0) && (S_ISDIR(path_stat.st_mode)))
      ecore_list_append(paths, strdup(path));
}

char               *
ecore_desktop_paths_recursive_search(const char *path, const char *file,
				     int sub, int (*dir_func) (void *data,
							       const char
							       *path),
				     int (*func) (void *data, const char *path),
				     void *data)
{
   char               *fpath = NULL;
   DIR                *dir = NULL;
   struct dirent      *script;

   if ((sub != 0) && (sub != -1))
      sub -= 1;

   dir = opendir(path);
   if (!dir) return NULL;

   while ((script = readdir(dir)) != NULL)
     {
	struct stat         script_stat;
	char                info_text[PATH_MAX];

	sprintf(info_text, "%s%s", path, script->d_name);
	if ((stat(info_text, &script_stat) == 0))
	  {
	     if (S_ISDIR(script_stat.st_mode))
	       {
		  if ((strcmp(basename(info_text), ".") != 0) &&
		      (strcmp(basename(info_text), "..") != 0))
		    {
		       snprintf(info_text, PATH_MAX, "%s%s/", path, script->d_name);
		       if ((dir_func) && (dir_func(data, info_text)))
			   break;
		       if (sub != 0)
			 {
			    if (fpath) free(fpath);
			    fpath =
			       ecore_desktop_paths_recursive_search(info_text, file, sub,
								    dir_func, func, data);
			 }
		    }
	       }
	     else
	       {
		  if (file)
		    {
		       if (strcmp(basename(info_text), file) == 0)
			 {
			    if (fpath)
			      free(fpath);
			    fpath = strdup(info_text);
			    if ((func) && (func(data, path)))
				break;
			 }
		    }
		  else
		    {
		       if ((func) && (func(data, info_text)))
			   break;
		    }
	       }
	     if (fpath && (!func))
	       break;
	  }
     }
   closedir(dir);

   return fpath;
}

#if defined GNOME_SUPPORT || defined KDE_SUPPORT
static void
_ecore_desktop_paths_exec_config(Ecore_List * paths, char *home,
				 Ecore_List * extras, char *cmd)
{
   Ecore_Exe          *exe;
   struct _config_exe_data ced;

   ced.home = home;
   ced.paths = paths;
   ced.types = extras;
   ced.done = 0;
   exe =
      ecore_exe_pipe_run(cmd,
			 ECORE_EXE_PIPE_AUTO | ECORE_EXE_PIPE_READ |
			 ECORE_EXE_PIPE_READ_LINE_BUFFERED, &ced);
   if (exe)
     {
	ecore_exe_tag_set(exe, "genmenu/fdo");
	while (ced.done == 0)
	  {
	     /* FIXME: raster is paranoid.  If too much time passes, give up.
	      * Or find a way to let the usual event loop shit do this without 
	      * spinning our wheels.  On the other hand, these are quick 
	      * commands, and we NEED this data before we can continue.  On 
	      * the gripping hand, some tweaking of the stuff searched for not 
	      * only gets rid of the need for this, but also speeds things up 
	      * drastically.
	      */
	     ecore_main_loop_iterate();
	     usleep(10);
	  }
     }
}

static int
_ecore_desktop_paths_cb_exe_exit(void *data, int type, void *event)
{
   Ecore_Exe_Event_Del *ev;
   Ecore_List         *paths;
   Ecore_List         *config_list;
   Ecore_Exe_Event_Data *read;
   struct _config_exe_data *ced;
   char               *value;
   char                path[PATH_MAX];

   ev = event;
   if (!ev->exe)
      return 1;
   value = ecore_exe_tag_get(ev->exe);
   if ((!value) || (strcmp(value, "genmenu/fdo")) != 0)
      return 1;
   ced = ecore_exe_data_get(ev->exe);
   if (!ced)
      return 1;
   paths = ced->paths;
   if (!paths)
      return 1;

   read = ecore_exe_event_data_get(ev->exe, ECORE_EXE_PIPE_READ);
   if ((read) && (read->lines[0].line))
     {
	value = read->lines[0].line;
	config_list = ecore_desktop_paths_to_list(value);
	if (config_list)
	  {
	     char               *this_config, *this_type;

	     ecore_list_goto_first(config_list);
	     while ((this_config = ecore_list_next(config_list)) != NULL)
	       {
		  if (ced->types)
		    {
		       ecore_list_goto_first(ced->types);
		       while ((this_type =
				ecore_list_next(ced->types)) != NULL)
			 {
			    _ecore_desktop_paths_massage_path(path,
				  ced->home,
				  this_config,
				  this_type);
			    _ecore_desktop_paths_check_and_add(paths,
				  path);
			 }
		    }
		  else
		    {
		       _ecore_desktop_paths_massage_path(path, ced->home,
			     this_config,
			     NULL);
		       _ecore_desktop_paths_check_and_add(paths, path);
		    }
	       }
	     E_FN_DEL(ecore_list_destroy, config_list);
	  }
     }
   ced->done = 1;
   return 1;
}
#endif

/** Split a list of paths into an Ecore_Hash.
 *
 * The list of paths can use any one of ;:, to seperate the paths.
 * You can also escape the :;, with \.
 *
 * @param   paths A list of paths.
 */
Ecore_Hash         *
ecore_desktop_paths_to_hash(const char *paths)
{
   Ecore_Hash         *result;
   char               *path;
   char                buf[PATH_MAX], *p, *pp;

   if (!paths) return NULL;
   result = ecore_hash_new(ecore_str_hash, ecore_str_compare);
   if (!result) return NULL;
   ecore_hash_set_free_key(result, free);
   ecore_hash_set_free_value(result, free);

   path = strdup(paths);
   if (path)
     {
	p = path;
	while (p)
	  {
	     pp = buf;
	     while (*p)
	       {
		  /* Check for escape */
		  if (*p == '\\')
		    {
		       /* If separator, skip escape */
		       if ((*(p + 1) == ';') || (*(p + 1) == ':') || (*(p + 1) == ','))
			 p++;
		    }
		  /* Check for separator */
		  else if ((*p == ';') || (*p == ':') || (*p == ','))
		    break;
		  *pp = *p;
		  pp++;
		  p++;
	       }
	     *pp = '\0';
	     if (*buf) ecore_hash_set(result, strdup(buf), strdup(buf));
	     if (*p) p++;
	     else p = NULL;
	  }
	free(path);
     }
   return result;
}

/** Split a list of paths into an Ecore_Hash.
 *
 * The list of paths can use any one of ;:, to seperate the paths.
 * You can also escape the :;, with \.
 *
 * @param   paths A list of paths.
 */
Ecore_List         *
ecore_desktop_paths_to_list(const char *paths)
{
   Ecore_List         *result;
   char               *path;
   char                buf[PATH_MAX], *p, *pp;

   if (!paths) return NULL;
   result = ecore_list_new();
   if (!result) return NULL;
   ecore_list_set_free_cb(result, free);

   path = strdup(paths);
   if (path)
     {
	p = path;
	while (p)
	  {
	     pp = buf;
	     while (*p)
	       {
		  /* Check for escape */
		  if (*p == '\\')
		    {
		       /* If separator, skip escape */
		       if ((*(p + 1) == ';') || (*(p + 1) == ':') || (*(p + 1) == ','))
			 p++;
		    }
		  /* Check for separator */
		  else if ((*p == ';') || (*p == ':') || (*p == ','))
		    break;
		  *pp = *p;
		  pp++;
		  p++;
	       }
	     *pp = '\0';
	     if (*buf) ecore_list_append(result, strdup(buf));
	     if (*p) p++;
	     else p = NULL;
	  }
	free(path);
     }
   return result;
}

EAPI int
ecore_desktop_paths_for_each(Ecore_Desktop_Paths_Type type,
			     Ecore_For_Each function, void *user_data)
{
   Ecore_List         *list = NULL;

   switch (type)
     {
     case ECORE_DESKTOP_PATHS_CONFIG:
	list = ecore_desktop_paths_config;
	break;
     case ECORE_DESKTOP_PATHS_MENUS:
	list = ecore_desktop_paths_menus;
	break;
     case ECORE_DESKTOP_PATHS_DIRECTORIES:
	list = ecore_desktop_paths_directories;
	break;
     case ECORE_DESKTOP_PATHS_DESKTOPS:
	list = ecore_desktop_paths_desktops;
	break;
     case ECORE_DESKTOP_PATHS_ICONS:
	list = ecore_desktop_paths_icons;
	break;
     case ECORE_DESKTOP_PATHS_KDE_LEGACY:
	list = ecore_desktop_paths_kde_legacy;
	break;
     case ECORE_DESKTOP_PATHS_XSESSIONS:
	list = ecore_desktop_paths_xsessions;
	break;
     case ECORE_DESKTOP_PATHS_MAX:
	break;
     }
   if (list)
      return ecore_list_for_each(list, function, user_data);
   return 0;
}
