<commit_before>/*
 * This conforms with the freedesktop.org Desktop Menu Specification version 0.92
 *
 * This is gonna be repetative and slow.  The idea is to first get it to correctly
 * follow the spec in a way that is easy to check.  Only then can we start to 
 * optomize into "ugly but fast".
 *
 * There are notes at the very end about all those nasty steps we need to do to 
 * follow the spec.
 */

#include <Ecore.h>

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

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

//#define DEBUG 1

struct _ecore_desktop_menu_expand_apps_data
{
   char               *path;
   Ecore_Hash         *pool;
   int                 length;
};

struct _ecore_desktop_menu_unxml_data
{
   char               *file;
   char               *base;
   char               *path;
   Ecore_Desktop_Tree *stack, *merge_stack;
   int                 unallocated, level;
};

struct _ecore_desktop_menu_generate_data
{
   char               *name, *path;
   Ecore_Desktop_Tree *rules;
   Ecore_Hash         *pool, *apps;
   int                 unallocated;

   Ecore_Desktop_Tree *rule;
   int                 include;
};

struct _ecore_desktop_menu_legacy_data
{
   Ecore_Desktop_Tree *merge;
   Ecore_Desktop_Tree *current;
   char               *menu;
   char               *prefix;
   char               *path;
   int                 length, menu_length, level;
};

static int          _ecore_desktop_menu_make_apps(const void *data,
						  Ecore_Desktop_Tree * tree,
						  int element, int level);
static Ecore_Desktop_Tree *_ecore_desktop_menu_get0(char *file,
						    Ecore_Desktop_Tree *
						    merge_stack, int level);
static Ecore_Desktop_Tree *_ecore_desktop_menu_create_menu(void);
static int          _ecore_desktop_menu_unxml(const void *data,
					      Ecore_Desktop_Tree * tree,
					      int element, int level);
static int          _ecore_desktop_menu_check_directory(void *data,
							const char *path);
static int          _ecore_desktop_menu_check_menu(void *data,
						   const char *path);
static int          _ecore_desktop_menu_legacy_menu_dir(void *data,
							const char *path);
static int          _ecore_desktop_menu_legacy_menu(void *data,
						    const char *path);
static void         _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
						    Ecore_Desktop_Tree * tree,
						    char type, char sub_type);
static void         _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
						    Ecore_Desktop_Tree * tree);
static void         _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree,
						 Ecore_List * paths, const char *pre,
						 const char *post, char *extra,
						 int element);
static int          _ecore_desktop_menu_expand_apps(struct
						    _ecore_desktop_menu_unxml_data
						    *unxml_data, char *app_dir,
						    Ecore_Hash * pool);
static int          _ecore_desktop_menu_check_app(void *data, const char *path);

static int          _ecore_desktop_menu_merge(const void *data,
					      Ecore_Desktop_Tree * tree,
					      int element, int level);
static int          _ecore_desktop_menu_expand_default_dirs(const void *data,
							    Ecore_Desktop_Tree
							    * tree,
							    int element,
							    int level);

static int          _ecore_desktop_menu_generate(const void *data,
						 Ecore_Desktop_Tree * tree,
						 int element, int level);
static int          _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__);
static void         _ecore_desktop_menu_inherit_apps(void *value,
						     void *user_data);
static void         _ecore_desktop_menu_select_app(void *value,
						   void *user_data);
static int          _ecore_desktop_menu_apply_rules(struct
						    _ecore_desktop_menu_generate_data
						    *generate_data,
						    Ecore_Desktop_Tree * rule,
						    char *key,
						    Ecore_Desktop * desktop);

/**
 * @defgroup Ecore_Desktop_Menu_Group menu Functions
 *
 * Functions that deal with freedesktop.org menus.
 */

EAPI void
ecore_desktop_menu_for_each(void (*func)
			    (const char *name, const char *path, const char *directory, Ecore_Hash * apps))
{
   char               *menu_file;

   /* Find the main menu file. */
   menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
					     "applications.menu", -1, NULL,
					     NULL);
   if (!menu_file)
     {
	/* Try various quirks of various systems and other wms. */
	menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
						  "kde-applications.menu", -1, NULL,
						  NULL);
        if (!menu_file)
          {
	     menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
						  "debian-menu.menu", -1, NULL,
						  NULL);
	     /* FIXME: If all else fails, run debians funky menu generator shit. */
	  }
     }

   if (menu_file)
     {
	Ecore_Desktop_Tree *menus;

	/* convert the xml into menus */
	menus = ecore_desktop_menu_get(menu_file);
	if (menus)
	  {
	     /* create the .desktop and order files from the menu */
	     ecore_desktop_tree_foreach(menus, 0, _ecore_desktop_menu_make_apps,
					func);
// FIXME: Can't free this just yet, causes major memory corruption.
// this leaks a LOT (428,200 bytes in one valgrind run...)
//               ecore_desktop_tree_del(menus);
	  }
	free(menu_file);
     }
}

static int
_ecore_desktop_menu_make_apps(const void *data, Ecore_Desktop_Tree * tree,
			      int element, int level __UNUSED__)
{
   if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
     {
	if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
	  {
	     int                 len;
	     char               *path;
	     char               *name;
	     char               *directory;
	     Ecore_Hash         *apps;
	     void                (*func) (char *name, char *path, char *directory,
					  Ecore_Hash * apps);

	     func = data;
	     name = (char *)tree->elements[element].element;
	     directory = name;
	     directory += 8;
	     path = strchr(directory, '<');
	     path++;
	     directory = strchr(path, '<');
	     directory++;
	     len = strlen(directory);
	     if (len > 2)
	       {
	          path = strdup(directory);
		  if (path)
		    {
	               path[len - 1] = '\0';
                       /* FIXME: Figure out what to do if it's just ".directory". */
	               directory = ecore_desktop_paths_file_find(ecore_desktop_paths_directories, path, 0, NULL, NULL);
		       free(path);
		    }
		  else
	             directory = NULL;
	       }
	     else
	        directory = NULL;
	     path = (char *)tree->elements[element + 1].element;
//             pool = (Ecore_Hash *) tree->elements[element + 2].element;
	     apps = (Ecore_Hash *) tree->elements[element + 4].element;
	     path = &path[11];
#ifdef DEBUG
	     printf("OUTPUTTING MENU - %s \t\t%s \t\t%s\n", path, name, directory);
#endif
	     if (func)
		func(name, path, directory, apps);
	     free(directory);
	  }
     }
   return 0;
}

/**
 * Decode a freedesktop.org menu XML jungle.
 *
 * Using the algorithm specified by freedesktop.org, fully decode
 * a menu based on an initial menu file.
 *
 * @param   file The base file for the menu.
 * @return  The resulting menu tree.
 * @ingroup Ecore_Desktop_Menu_Group
 */

Ecore_Desktop_Tree *
ecore_desktop_menu_get(char *file)
{
   return _ecore_desktop_menu_get0(file, NULL, 0);
}

static Ecore_Desktop_Tree *
_ecore_desktop_menu_get0(char *file, Ecore_Desktop_Tree * merge_stack,
			 int level)
{
   Ecore_Desktop_Tree *menu_xml;
   struct _ecore_desktop_menu_unxml_data data;
   int                 oops = 0;

   /* Preperation. */
   data.stack = ecore_desktop_tree_new(NULL);
   /* FIXME data.base and data.path leak */
   data.base = ecore_file_strip_ext(ecore_file_get_file(file));
   data.path = ecore_file_get_dir(file);
   if ((level == 0) && (merge_stack == NULL))
      merge_stack = ecore_desktop_tree_new(NULL);
#ifdef DEBUG
printf("MENU FILE %d - %s\n", level, file);
#endif
   menu_xml = ecore_desktop_xmlame_get(file);
   if ((data.stack) && (data.base) && (data.path) && (merge_stack)
       && (menu_xml))
     {
	int                 i;

	data.file = file;
	data.level = level;
	data.merge_stack = merge_stack;

	/* Setup the merge stack. */
	if (merge_stack->size <= level)
	  {
      /* XXX storing a pointer to a static string probably isn't a good idea. this is most likely the cause of the valgrind error mentioned below. (once this function has exited that memory will no longer be valid) */
	     while (merge_stack->size < level)
		ecore_desktop_tree_add(merge_stack, "");
	     ecore_desktop_tree_add(merge_stack, file);
	  }
	else
	   merge_stack->elements[level].element = file;

	/* Find out if we are looping. */
	for (i = 0; i < level; i++)
	  {
	     char               *text;

	     /* I can safely assume that they are all strings. */
	     text = (char *)merge_stack->elements[i].element;
/* printf("---  %s  ---  %s\n", text, file);
	     if (text)
	     if (file) */
	     if (strcmp(text, file) == 0)
	       {
/* FIXME: valgrind says -
==5761== Conditional jump or move depends on uninitialised value(s)
==5761==    at 0x1B90565C: strcmp (in /usr/lib/valgrind/vgpreload_memcheck.so)
==5761==    by 0x1BA3E984: _ecore_desktop_menu_get0 (ecore_desktop_menu.c:176)
==5761==    by 0x1BA3FE3F: _ecore_desktop_menu_merge (ecore_desktop_menu.c:1146)
==5761==    by 0x1BA3D5A2: ecore_desktop_tree_foreach (ecore_desktop_tree.c:267)
 * which is the strcmp just above.  But it doesn't complain about the first two if's, 
 * or the printf, which I inserted to try and track this down.
 * No idea what it actually is complaining about, so I'll comment it for future study.
 *
 * This is probably caused by the fact that a static string was passed into the
 * tree. See the comment above where this occurs.
 */
		  fprintf(stderr,
			  "\n### Oops, infinite menu merging loop detected at %s\n",
			  file);
		  oops++;
	       }
	  }

	if (oops == 0)
	  {
	     /* Get on with it. */
	     ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_unxml,
					&data);
	     ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_merge,
					&data);

	     /* The rest of this is only done after ALL the menus have been merged. */
	     if (level == 0)
	       {
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_expand_default_dirs,
					     &data);

#ifdef DEBUG
		  ecore_desktop_tree_dump(menu_xml, 0);
		  printf("\n\n");
#endif

		  data.unallocated = FALSE;
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_generate,
					     &data);

		  data.unallocated = TRUE;
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_generate,
					     &data);

#ifdef DEBUG
//		  ecore_desktop_tree_dump(menu_xml, 0);
//		  printf("\n\n");
#endif
	       }
	  }
     }
   else
      oops++;

   if (oops)
     {
	E_FN_DEL(ecore_desktop_tree_del, (menu_xml));
	if (level == 0)
	  {
	     E_FN_DEL(ecore_desktop_tree_del, (merge_stack));
	  }
   E_FREE(data.path);
   E_FREE(data.base);
   E_FN_DEL(ecore_desktop_tree_del, (data.stack));
     }


   return menu_xml;
}

static int
_ecore_desktop_menu_unxml(const void *data, Ecore_Desktop_Tree * tree,
			  int element, int level)
{
   struct _ecore_desktop_menu_unxml_data *unxml_data;

   unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
   if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
     {
	if (strncmp((char *)tree->elements[element].element, "<!", 2) == 0)
	  {
	     tree->elements[element].type =
		ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
	     tree->elements[element].element = NULL;
	  }
	else if (strcmp((char *)tree->elements[element].element, "<Menu") == 0)
	  {
	     Ecore_Desktop_Tree *menu;

	     menu = _ecore_desktop_menu_create_menu();
	     if (menu)
	       {
		  int                 i;
		  char               *flags = "    ", *name = "", *directory =
		     "", *menu_path = "";
		  char                temp[PATH_MAX];

		  flags = (char *)menu->elements[0].element;
		  flags += 7;
		  tree->elements[element].element = menu;
		  tree->elements[element].type =
		     ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
		  for (i = element + 1; i < tree->size; i++)
		    {
		       int                 result = 0;

		       if (tree->elements[i].type ==
			   ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
			 {
			    if (strncmp
				((char *)tree->elements[i].element, "<!",
				 2) == 0)
			      {
				 tree->elements[i].type =
				    ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
				 tree->elements[i].element = NULL;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<Deleted/") == 0)
			      {
				 flags[1] = 'D';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<NotDeleted/") == 0)
			      {
				 flags[1] = ' ';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<OnlyUnallocated/") == 0)
			      {
				 flags[2] = 'O';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<NotOnlyUnallocated/") == 0)
			      {
				 flags[2] = ' ';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "</Menu") == 0)
			      {
				 result = 1;
			      }
			    else
			      {
				 ecore_desktop_tree_extend(menu,
							   (char *)tree->
							   elements[i].element);
				 result = 1;
			      }
			 }
		       else if (tree->elements[i].type ==
				ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
			 {
			    Ecore_Desktop_Tree *sub;

			    sub =
			       (Ecore_Desktop_Tree *) tree->elements[i].element;
			    if ((sub) && (sub->size))
			      {
				 if (sub->elements[0].type ==
				     ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
				   {
				      if (strcmp
					  ((char *)sub->elements[0].element,
					   "<Name") == 0)
					{
					   int                 i, length = 0;

					   name =
					      strdup((char *)sub->elements[1].
						     element);
					   sprintf(temp,
						   "<MENU <%.4s> <%s> <%s>",
						   flags, name, directory);
					   menu->elements[0].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu, name);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[0].
								    element);
					   flags =
					      (char *)menu->elements[0].element;
					   flags += 7;
					   /* The rest of this is probably not needed, except to ease debugging. */
					   if (unxml_data->stack->size <= level)
					     {
						while (unxml_data->stack->size <
						       level)
						   ecore_desktop_tree_add
						      (unxml_data->stack, "");
						ecore_desktop_tree_add
						   (unxml_data->stack, name);
					     }
					   else
					      unxml_data->stack->
						 elements[level].element = name;
					   temp[0] = '\0';
					   for (i = 0; i <= level; i++)
					     {
						char               *text;

						/* I can safely assume that they are all strings. */
						text =
						   (char *)unxml_data->stack->
						   elements[i].element;
						if (text[0] != '\0')
						  {
						     sprintf(&temp[length],
							     "%s%s",
							     ((length) ? "/" :
							      ""), text);
						     length +=
							strlen(text) +
							((length) ? 1 : 0);
						  }
					     }
					   menu_path = strdup(temp);
					   sprintf(temp, "<MENU_PATH %s",
						   menu_path);
					   menu->elements[1].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu,
								    menu_path);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[1].
								    element);
					   result = 1;
					}
				      /* FIXME: Move this to later in the sequence. */
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Directory") == 0)
					{
					   directory =
					      strdup((char *)sub->elements[1].
						     element);
					   sprintf(temp,
						   "<MENU <%.4s> <%s> <%s>",
						   flags, name, directory);
					   menu->elements[0].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu,
								    directory);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[0].
								    element);
					   flags =
					      (char *)menu->elements[0].element;
					   flags += 7;
					   result = 1;
					}
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Menu") == 0)
					{
					   _ecore_desktop_menu_unxml(data, sub,
								     0,
								     level + 1);
					   ecore_desktop_tree_add_child(menu,
									(Ecore_Desktop_Tree
									 *)
									sub->
									elements
									[0].
									element);
					   /* FIXME: Dunno if this causes a memory leak, but for now we play it safe. */
					   tree->elements[i].type =
					      ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
					   tree->elements[i].element = NULL;
//                                                 result = 1;
					}
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Move") == 0)
					{
					   _ecore_desktop_menu_unxml_moves
					      (menu, sub);
					   result = 1;
					}
				      else
					{
					   if ((sub->size == 3)
					       && (sub->elements[1].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
					       && (((char *)sub->elements[1].element)[0] != '<'))
					     {
						char temp[PATH_MAX];

						snprintf(temp, PATH_MAX, "%s %s",
						        (char *)sub->elements[0].element,
							(char *)sub->elements[1].element);
						ecore_desktop_tree_extend(menu, temp);
						result = 1;
					     }
					   else
					     {
						ecore_desktop_tree_add_child
						   (menu, sub);
						tree->elements[i].type =
						   ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
						tree->elements[i].element =
						   NULL;
					     }
					}
				   }
			      }
			 }
		       if (result)
			 {
			    if (tree->elements[i].type ==
				ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
			       ecore_desktop_tree_del((Ecore_Desktop_Tree *)
						      tree->elements[i].
						      element);
			    tree->elements[i].type =
			       ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
			    tree->elements[i].element = NULL;
			 }
		    }

		  /* Add it if it has not been deleted. */
		  if (flags[1] != 'D')
		    {
		       tree->elements[element].element = menu;
		       tree->elements[element].type =
			  ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
		    }
	       }
	  }
     }
   return 0;
}

static Ecore_Desktop_Tree *
_ecore_desktop_menu_create_menu()
{
   Ecore_Desktop_Tree *menu, *rules;
   Ecore_Hash         *pool, *apps;

   menu = ecore_desktop_tree_new(NULL);
   rules = ecore_desktop_tree_new(NULL);
   pool = ecore_hash_new(ecore_str_hash, ecore_str_compare);
   apps = ecore_hash_new(ecore_str_hash, ecore_str_compare);
   if ((menu) && (rules) && (pool) && (apps))
     {
	ecore_hash_set_free_key(pool, free);
	ecore_hash_set_free_value(pool, free);
	ecore_hash_set_free_key(apps, free);
	ecore_hash_set_free_value(apps, free);
	ecore_desktop_tree_extend(menu, "<MENU <    > <> <>");
	ecore_desktop_tree_extend(menu, "<MENU_PATH ");
	ecore_desktop_tree_add_hash(menu, pool);
	ecore_desktop_tree_add_child(menu, rules);
	ecore_desktop_tree_add_hash(menu, apps);
     }
   else
     {
	if (apps)
	   ecore_hash_destroy(apps);
	if (pool)
	   ecore_hash_destroy(pool);
	if (rules)
	   ecore_desktop_tree_del(rules);
	if (menu)
	   ecore_desktop_tree_del(menu);
	menu = NULL;
     }

   return menu;
}

static int
_ecore_desktop_menu_check_directory(void *data, const char *path)
{
   char               *p;
   Ecore_Desktop_Tree *merge;

   merge = (Ecore_Desktop_Tree *) data;
   p = strrchr(path, '.');
   if (p)
     {
	if (strcmp(p, ".directory") == 0)
	  {
	     char                merge_file[PATH_MAX];

	     sprintf(merge_file, "<Directory %s", path);
	     ecore_desktop_tree_extend(merge, merge_file);
	  }
     }

   return 0;
}

static int
_ecore_desktop_menu_check_menu(void *data, const char *path)
{
   char               *p;
   Ecore_Desktop_Tree *merge;

   merge = (Ecore_Desktop_Tree *) data;
   p = strrchr(path, '.');
   if (p)
     {
	if (strcmp(p, ".menu") == 0)
	  {
	     char                merge_file[PATH_MAX];

	     sprintf(merge_file, "<MergeFile type=\"path\" %s", path);
	     ecore_desktop_tree_extend(merge, merge_file);
	  }
     }

   return 0;
}

static int
_ecore_desktop_menu_legacy_menu_dir(void *data, const char *path)
{
   struct _ecore_desktop_menu_legacy_data *legacy_data;
   Ecore_Desktop_Tree *menu;
   char               *temp_path;
   int                 start;
   int                 i, count = 0;

   legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
   for (i = legacy_data->length; path[i] != '\0'; i++)
     {
	if (path[i] == '/')
	   count++;
     }
   if (count == 1)
     {
	legacy_data->current = legacy_data->merge;
	legacy_data->level = 0;
     }
   else if (count <= legacy_data->level)
     {
	if ((legacy_data->current) && (legacy_data->current->parent))
	  {
	     legacy_data->current = legacy_data->current->parent;
	     legacy_data->level--;
	  }
     }

   start = legacy_data->length;
   count = 0;
   temp_path = strdup(path);
   if (temp_path)
     {
	for (i = legacy_data->length; temp_path[i] != '\0'; i++)
	  {
	     if (temp_path[i] == '/')
	       {
		  if (count >= legacy_data->level)
		    {
		       temp_path[i] = '\0';
		       menu = _ecore_desktop_menu_create_menu();
		       if (menu)
			 {
			    char                temp[PATH_MAX];

			    sprintf(temp, "<MENU <   L> <%s> <>",
				    &temp_path[start]);
			    menu->elements[0].element = strdup(temp);
			    ecore_desktop_tree_track(menu,
						     menu->elements[0].element);
			    sprintf(temp, "<MENU_PATH %s/%s", legacy_data->menu,
				    &temp_path[legacy_data->length]);
			    menu->elements[1].element = strdup(temp);
			    ecore_desktop_tree_track(menu,
						     menu->elements[1].element);

			    ecore_desktop_tree_add_child(legacy_data->current,
							 menu);
			    /*  This is not needed, but if it was, this is where it would go.
			     * sprintf(temp, "<AppDir %s/", temp_path);
			     * ecore_desktop_tree_extend(menu, temp);
			     */
			    sprintf(temp, "<DirectoryDir %s/", temp_path);
			    ecore_desktop_tree_extend(menu, temp);

			    legacy_data->current = menu;
			    legacy_data->level++;
			 }
		       temp_path[i] = '/';
		    }
		  start = i + 1;
		  count++;
	       }
	  }
	free(temp_path);
     }

   legacy_data->level = count;

   return 0;
}

static int
_ecore_desktop_menu_legacy_menu(void *data, const char *path)
{
   struct _ecore_desktop_menu_legacy_data *legacy_data;
   char               *menu_path;
   char               *file;
   char                temp[PATH_MAX];
   int                 i, count = 0, menu_count = 0;

   legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
   if ((legacy_data->current) && (legacy_data->current->size > 0) &&
       (legacy_data->current->elements[1].type ==
	ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
     {
	menu_path = (char *)legacy_data->current->elements[1].element;
	menu_path += 12 + legacy_data->menu_length;
     }
   else
     {
	menu_path = "";
	printf("  PROBLEM IN LEGACYDIR FILE - %s - %s  %s\n",
	       legacy_data->prefix, legacy_data->path,
	       &path[legacy_data->length]);
     }

   for (i = legacy_data->length; path[i] != '\0'; i++)
     {
	if (path[i] == '/')
	   count++;
     }
   for (i = 0; menu_path[i] != '\0'; i++)
     {
	if (menu_path[i] == '/')
	   menu_count++;
     }
   /* FIXME: The (legacy_data->current) test is just to patch a seggie, find out why. */
   while ((menu_count >= count) && (legacy_data->current))
     {
	legacy_data->current = legacy_data->current->parent;
	menu_count--;
     }
   if ((legacy_data->current) && (legacy_data->current->size > 0) &&
       (legacy_data->current->elements[1].type ==
	ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
     {
	menu_path = (char *)legacy_data->current->elements[1].element;
	menu_path += 12 + legacy_data->menu_length;
     }
   else
     {
	/* FIXME:  Bugger, a corner case.
	 * If this is .directory, then maybe ignore it?
	 * If this is a desktop, we need to put it into the pool of the menu that legacy_data->merge will get merged into.
	 */
	menu_path = "";
	printf("  LEGACYDIR FILE - %s - %s  %s\n", legacy_data->prefix,
	       legacy_data->path, &path[legacy_data->length]);
	return 0;
     }

   menu_count = strlen(menu_path);
   if (menu_count)
      menu_count++;
   file = (char *)&path[legacy_data->length + menu_count];
   count = strlen(file);

   if (strcmp(".directory", file) == 0)
     {
	menu_path = (char *)legacy_data->current->elements[0].element;
	menu_count = strlen(menu_path);
	menu_path[menu_count - 3] = '\0';
	sprintf(temp, "%s <.directory>", menu_path);
	legacy_data->current->elements[0].element = strdup(temp);
	ecore_desktop_tree_track(legacy_data->current,
				 legacy_data->current->elements[0].element);
     }
   else if (strcmp(".desktop", &file[count - 8]) == 0)
     {
	Ecore_Hash         *pool;
	Ecore_Desktop_Tree *rules;

	pool = (Ecore_Hash *) legacy_data->current->elements[2].element;
	rules =
	   (Ecore_Desktop_Tree *) legacy_data->current->elements[3].element;
	if (rules->size == 0)
	  {
	     Ecore_Desktop_Tree *new_rules;

	     new_rules = ecore_desktop_tree_new(NULL);
	     if (new_rules)
		ecore_desktop_tree_add_child(rules, new_rules);
	  }
	sprintf(temp, "%s%s", legacy_data->prefix, file);
	ecore_hash_set(pool, strdup(temp), strdup(path));
#ifdef DEBUG
//	printf
//	   ("POOLING - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s - %s\n",
//	    path, file, temp);
#endif
	if (rules->size > 0)
	  {
	     rules = (Ecore_Desktop_Tree *) rules->elements[0].element;
	     sprintf(temp, "IOF %s%s", legacy_data->prefix, file);
	     ecore_desktop_tree_extend(rules, temp);
	  }
     }
#ifdef DEBUG
   else
      printf("PROBLEM - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s\n",
	     path, file);
#endif

   return 0;
}

static void
_ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
				Ecore_Desktop_Tree * tree, char type,
				char sub_type)
{
   int                 i;
   char                temp[PATH_MAX];

   if (!tree) return;
   for (i = 0; i < tree->size; i++)
     {
	if (!tree->elements[i]) continue;
	if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
	  {
             /* FIXME <All /> or <All foo="bar"> would be valid input (which this won't catch) */
	     if (strcmp((char *)tree->elements[i].element, "<All/") == 0)
	       {
		  sprintf(temp, "%c%cA", type, sub_type);
		  ecore_desktop_tree_extend(rules, temp);
	       }
	     else if (strcmp((char *)tree->elements[i].element, "<Filename") ==
		      0)
	       {
		  sprintf(temp, "%c%cF %s", type, sub_type,
			  (char *)tree->elements[i + 1].element);
		  ecore_desktop_tree_extend(rules, temp);
	       }
	     else if (strcmp((char *)tree->elements[i].element, "<Category") ==
		      0)
	       {<commit_msg>Revert that recent trap commit.<commit_after>/*
 * This conforms with the freedesktop.org Desktop Menu Specification version 0.92
 *
 * This is gonna be repetative and slow.  The idea is to first get it to correctly
 * follow the spec in a way that is easy to check.  Only then can we start to 
 * optomize into "ugly but fast".
 *
 * There are notes at the very end about all those nasty steps we need to do to 
 * follow the spec.
 */

#include <Ecore.h>

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

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

//#define DEBUG 1

struct _ecore_desktop_menu_expand_apps_data
{
   char               *path;
   Ecore_Hash         *pool;
   int                 length;
};

struct _ecore_desktop_menu_unxml_data
{
   char               *file;
   char               *base;
   char               *path;
   Ecore_Desktop_Tree *stack, *merge_stack;
   int                 unallocated, level;
};

struct _ecore_desktop_menu_generate_data
{
   char               *name, *path;
   Ecore_Desktop_Tree *rules;
   Ecore_Hash         *pool, *apps;
   int                 unallocated;

   Ecore_Desktop_Tree *rule;
   int                 include;
};

struct _ecore_desktop_menu_legacy_data
{
   Ecore_Desktop_Tree *merge;
   Ecore_Desktop_Tree *current;
   char               *menu;
   char               *prefix;
   char               *path;
   int                 length, menu_length, level;
};

static int          _ecore_desktop_menu_make_apps(const void *data,
						  Ecore_Desktop_Tree * tree,
						  int element, int level);
static Ecore_Desktop_Tree *_ecore_desktop_menu_get0(char *file,
						    Ecore_Desktop_Tree *
						    merge_stack, int level);
static Ecore_Desktop_Tree *_ecore_desktop_menu_create_menu(void);
static int          _ecore_desktop_menu_unxml(const void *data,
					      Ecore_Desktop_Tree * tree,
					      int element, int level);
static int          _ecore_desktop_menu_check_directory(void *data,
							const char *path);
static int          _ecore_desktop_menu_check_menu(void *data,
						   const char *path);
static int          _ecore_desktop_menu_legacy_menu_dir(void *data,
							const char *path);
static int          _ecore_desktop_menu_legacy_menu(void *data,
						    const char *path);
static void         _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
						    Ecore_Desktop_Tree * tree,
						    char type, char sub_type);
static void         _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
						    Ecore_Desktop_Tree * tree);
static void         _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree,
						 Ecore_List * paths, const char *pre,
						 const char *post, char *extra,
						 int element);
static int          _ecore_desktop_menu_expand_apps(struct
						    _ecore_desktop_menu_unxml_data
						    *unxml_data, char *app_dir,
						    Ecore_Hash * pool);
static int          _ecore_desktop_menu_check_app(void *data, const char *path);

static int          _ecore_desktop_menu_merge(const void *data,
					      Ecore_Desktop_Tree * tree,
					      int element, int level);
static int          _ecore_desktop_menu_expand_default_dirs(const void *data,
							    Ecore_Desktop_Tree
							    * tree,
							    int element,
							    int level);

static int          _ecore_desktop_menu_generate(const void *data,
						 Ecore_Desktop_Tree * tree,
						 int element, int level);
static int          _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__);
static void         _ecore_desktop_menu_inherit_apps(void *value,
						     void *user_data);
static void         _ecore_desktop_menu_select_app(void *value,
						   void *user_data);
static int          _ecore_desktop_menu_apply_rules(struct
						    _ecore_desktop_menu_generate_data
						    *generate_data,
						    Ecore_Desktop_Tree * rule,
						    char *key,
						    Ecore_Desktop * desktop);

/**
 * @defgroup Ecore_Desktop_Menu_Group menu Functions
 *
 * Functions that deal with freedesktop.org menus.
 */

EAPI void
ecore_desktop_menu_for_each(void (*func)
			    (const char *name, const char *path, const char *directory, Ecore_Hash * apps))
{
   char               *menu_file;

   /* Find the main menu file. */
   menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
					     "applications.menu", -1, NULL,
					     NULL);
   if (!menu_file)
     {
	/* Try various quirks of various systems and other wms. */
	menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
						  "kde-applications.menu", -1, NULL,
						  NULL);
        if (!menu_file)
          {
	     menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
						  "debian-menu.menu", -1, NULL,
						  NULL);
	     /* FIXME: If all else fails, run debians funky menu generator shit. */
	  }
     }

   if (menu_file)
     {
	Ecore_Desktop_Tree *menus;

	/* convert the xml into menus */
	menus = ecore_desktop_menu_get(menu_file);
	if (menus)
	  {
	     /* create the .desktop and order files from the menu */
	     ecore_desktop_tree_foreach(menus, 0, _ecore_desktop_menu_make_apps,
					func);
// FIXME: Can't free this just yet, causes major memory corruption.
// this leaks a LOT (428,200 bytes in one valgrind run...)
//               ecore_desktop_tree_del(menus);
	  }
	free(menu_file);
     }
}

static int
_ecore_desktop_menu_make_apps(const void *data, Ecore_Desktop_Tree * tree,
			      int element, int level __UNUSED__)
{
   if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
     {
	if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
	  {
	     int                 len;
	     char               *path;
	     char               *name;
	     char               *directory;
	     Ecore_Hash         *apps;
	     void                (*func) (char *name, char *path, char *directory,
					  Ecore_Hash * apps);

	     func = data;
	     name = (char *)tree->elements[element].element;
	     directory = name;
	     directory += 8;
	     path = strchr(directory, '<');
	     path++;
	     directory = strchr(path, '<');
	     directory++;
	     len = strlen(directory);
	     if (len > 2)
	       {
	          path = strdup(directory);
		  if (path)
		    {
	               path[len - 1] = '\0';
                       /* FIXME: Figure out what to do if it's just ".directory". */
	               directory = ecore_desktop_paths_file_find(ecore_desktop_paths_directories, path, 0, NULL, NULL);
		       free(path);
		    }
		  else
	             directory = NULL;
	       }
	     else
	        directory = NULL;
	     path = (char *)tree->elements[element + 1].element;
//             pool = (Ecore_Hash *) tree->elements[element + 2].element;
	     apps = (Ecore_Hash *) tree->elements[element + 4].element;
	     path = &path[11];
#ifdef DEBUG
	     printf("OUTPUTTING MENU - %s \t\t%s \t\t%s\n", path, name, directory);
#endif
	     if (func)
		func(name, path, directory, apps);
	     free(directory);
	  }
     }
   return 0;
}

/**
 * Decode a freedesktop.org menu XML jungle.
 *
 * Using the algorithm specified by freedesktop.org, fully decode
 * a menu based on an initial menu file.
 *
 * @param   file The base file for the menu.
 * @return  The resulting menu tree.
 * @ingroup Ecore_Desktop_Menu_Group
 */

Ecore_Desktop_Tree *
ecore_desktop_menu_get(char *file)
{
   return _ecore_desktop_menu_get0(file, NULL, 0);
}

static Ecore_Desktop_Tree *
_ecore_desktop_menu_get0(char *file, Ecore_Desktop_Tree * merge_stack,
			 int level)
{
   Ecore_Desktop_Tree *menu_xml;
   struct _ecore_desktop_menu_unxml_data data;
   int                 oops = 0;

   /* Preperation. */
   data.stack = ecore_desktop_tree_new(NULL);
   /* FIXME data.base and data.path leak */
   data.base = ecore_file_strip_ext(ecore_file_get_file(file));
   data.path = ecore_file_get_dir(file);
   if ((level == 0) && (merge_stack == NULL))
      merge_stack = ecore_desktop_tree_new(NULL);
#ifdef DEBUG
printf("MENU FILE %d - %s\n", level, file);
#endif
   menu_xml = ecore_desktop_xmlame_get(file);
   if ((data.stack) && (data.base) && (data.path) && (merge_stack)
       && (menu_xml))
     {
	int                 i;

	data.file = file;
	data.level = level;
	data.merge_stack = merge_stack;

	/* Setup the merge stack. */
	if (merge_stack->size <= level)
	  {
      /* XXX storing a pointer to a static string probably isn't a good idea. this is most likely the cause of the valgrind error mentioned below. (once this function has exited that memory will no longer be valid) */
	     while (merge_stack->size < level)
		ecore_desktop_tree_add(merge_stack, "");
	     ecore_desktop_tree_add(merge_stack, file);
	  }
	else
	   merge_stack->elements[level].element = file;

	/* Find out if we are looping. */
	for (i = 0; i < level; i++)
	  {
	     char               *text;

	     /* I can safely assume that they are all strings. */
	     text = (char *)merge_stack->elements[i].element;
/* printf("---  %s  ---  %s\n", text, file);
	     if (text)
	     if (file) */
	     if (strcmp(text, file) == 0)
	       {
/* FIXME: valgrind says -
==5761== Conditional jump or move depends on uninitialised value(s)
==5761==    at 0x1B90565C: strcmp (in /usr/lib/valgrind/vgpreload_memcheck.so)
==5761==    by 0x1BA3E984: _ecore_desktop_menu_get0 (ecore_desktop_menu.c:176)
==5761==    by 0x1BA3FE3F: _ecore_desktop_menu_merge (ecore_desktop_menu.c:1146)
==5761==    by 0x1BA3D5A2: ecore_desktop_tree_foreach (ecore_desktop_tree.c:267)
 * which is the strcmp just above.  But it doesn't complain about the first two if's, 
 * or the printf, which I inserted to try and track this down.
 * No idea what it actually is complaining about, so I'll comment it for future study.
 *
 * This is probably caused by the fact that a static string was passed into the
 * tree. See the comment above where this occurs.
 */
		  fprintf(stderr,
			  "\n### Oops, infinite menu merging loop detected at %s\n",
			  file);
		  oops++;
	       }
	  }

	if (oops == 0)
	  {
	     /* Get on with it. */
	     ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_unxml,
					&data);
	     ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_merge,
					&data);

	     /* The rest of this is only done after ALL the menus have been merged. */
	     if (level == 0)
	       {
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_expand_default_dirs,
					     &data);

#ifdef DEBUG
		  ecore_desktop_tree_dump(menu_xml, 0);
		  printf("\n\n");
#endif

		  data.unallocated = FALSE;
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_generate,
					     &data);

		  data.unallocated = TRUE;
		  ecore_desktop_tree_foreach(menu_xml, 0,
					     _ecore_desktop_menu_generate,
					     &data);

#ifdef DEBUG
//		  ecore_desktop_tree_dump(menu_xml, 0);
//		  printf("\n\n");
#endif
	       }
	  }
     }
   else
      oops++;

   if (oops)
     {
	E_FN_DEL(ecore_desktop_tree_del, (menu_xml));
	if (level == 0)
	  {
	     E_FN_DEL(ecore_desktop_tree_del, (merge_stack));
	  }
   E_FREE(data.path);
   E_FREE(data.base);
   E_FN_DEL(ecore_desktop_tree_del, (data.stack));
     }


   return menu_xml;
}

static int
_ecore_desktop_menu_unxml(const void *data, Ecore_Desktop_Tree * tree,
			  int element, int level)
{
   struct _ecore_desktop_menu_unxml_data *unxml_data;

   unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
   if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
     {
	if (strncmp((char *)tree->elements[element].element, "<!", 2) == 0)
	  {
	     tree->elements[element].type =
		ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
	     tree->elements[element].element = NULL;
	  }
	else if (strcmp((char *)tree->elements[element].element, "<Menu") == 0)
	  {
	     Ecore_Desktop_Tree *menu;

	     menu = _ecore_desktop_menu_create_menu();
	     if (menu)
	       {
		  int                 i;
		  char               *flags = "    ", *name = "", *directory =
		     "", *menu_path = "";
		  char                temp[PATH_MAX];

		  flags = (char *)menu->elements[0].element;
		  flags += 7;
		  tree->elements[element].element = menu;
		  tree->elements[element].type =
		     ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
		  for (i = element + 1; i < tree->size; i++)
		    {
		       int                 result = 0;

		       if (tree->elements[i].type ==
			   ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
			 {
			    if (strncmp
				((char *)tree->elements[i].element, "<!",
				 2) == 0)
			      {
				 tree->elements[i].type =
				    ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
				 tree->elements[i].element = NULL;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<Deleted/") == 0)
			      {
				 flags[1] = 'D';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<NotDeleted/") == 0)
			      {
				 flags[1] = ' ';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<OnlyUnallocated/") == 0)
			      {
				 flags[2] = 'O';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "<NotOnlyUnallocated/") == 0)
			      {
				 flags[2] = ' ';
				 result = 1;
			      }
			    else
			       if (strcmp
				   ((char *)tree->elements[i].element,
				    "</Menu") == 0)
			      {
				 result = 1;
			      }
			    else
			      {
				 ecore_desktop_tree_extend(menu,
							   (char *)tree->
							   elements[i].element);
				 result = 1;
			      }
			 }
		       else if (tree->elements[i].type ==
				ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
			 {
			    Ecore_Desktop_Tree *sub;

			    sub =
			       (Ecore_Desktop_Tree *) tree->elements[i].element;
			    if ((sub) && (sub->size))
			      {
				 if (sub->elements[0].type ==
				     ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
				   {
				      if (strcmp
					  ((char *)sub->elements[0].element,
					   "<Name") == 0)
					{
					   int                 i, length = 0;

					   name =
					      strdup((char *)sub->elements[1].
						     element);
					   sprintf(temp,
						   "<MENU <%.4s> <%s> <%s>",
						   flags, name, directory);
					   menu->elements[0].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu, name);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[0].
								    element);
					   flags =
					      (char *)menu->elements[0].element;
					   flags += 7;
					   /* The rest of this is probably not needed, except to ease debugging. */
					   if (unxml_data->stack->size <= level)
					     {
						while (unxml_data->stack->size <
						       level)
						   ecore_desktop_tree_add
						      (unxml_data->stack, "");
						ecore_desktop_tree_add
						   (unxml_data->stack, name);
					     }
					   else
					      unxml_data->stack->
						 elements[level].element = name;
					   temp[0] = '\0';
					   for (i = 0; i <= level; i++)
					     {
						char               *text;

						/* I can safely assume that they are all strings. */
						text =
						   (char *)unxml_data->stack->
						   elements[i].element;
						if (text[0] != '\0')
						  {
						     sprintf(&temp[length],
							     "%s%s",
							     ((length) ? "/" :
							      ""), text);
						     length +=
							strlen(text) +
							((length) ? 1 : 0);
						  }
					     }
					   menu_path = strdup(temp);
					   sprintf(temp, "<MENU_PATH %s",
						   menu_path);
					   menu->elements[1].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu,
								    menu_path);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[1].
								    element);
					   result = 1;
					}
				      /* FIXME: Move this to later in the sequence. */
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Directory") == 0)
					{
					   directory =
					      strdup((char *)sub->elements[1].
						     element);
					   sprintf(temp,
						   "<MENU <%.4s> <%s> <%s>",
						   flags, name, directory);
					   menu->elements[0].element =
					      strdup(temp);
					   ecore_desktop_tree_track(menu,
								    directory);
					   ecore_desktop_tree_track(menu,
								    menu->
								    elements[0].
								    element);
					   flags =
					      (char *)menu->elements[0].element;
					   flags += 7;
					   result = 1;
					}
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Menu") == 0)
					{
					   _ecore_desktop_menu_unxml(data, sub,
								     0,
								     level + 1);
					   ecore_desktop_tree_add_child(menu,
									(Ecore_Desktop_Tree
									 *)
									sub->
									elements
									[0].
									element);
					   /* FIXME: Dunno if this causes a memory leak, but for now we play it safe. */
					   tree->elements[i].type =
					      ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
					   tree->elements[i].element = NULL;
//                                                 result = 1;
					}
				      else
					 if (strcmp
					     ((char *)sub->elements[0].element,
					      "<Move") == 0)
					{
					   _ecore_desktop_menu_unxml_moves
					      (menu, sub);
					   result = 1;
					}
				      else
					{
					   if ((sub->size == 3)
					       && (sub->elements[1].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
					       && (((char *)sub->elements[1].element)[0] != '<'))
					     {
						char temp[PATH_MAX];

						snprintf(temp, PATH_MAX, "%s %s",
						        (char *)sub->elements[0].element,
							(char *)sub->elements[1].element);
						ecore_desktop_tree_extend(menu, temp);
						result = 1;
					     }
					   else
					     {
						ecore_desktop_tree_add_child
						   (menu, sub);
						tree->elements[i].type =
						   ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
						tree->elements[i].element =
						   NULL;
					     }
					}
				   }
			      }
			 }
		       if (result)
			 {
			    if (tree->elements[i].type ==
				ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
			       ecore_desktop_tree_del((Ecore_Desktop_Tree *)
						      tree->elements[i].
						      element);
			    tree->elements[i].type =
			       ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
			    tree->elements[i].element = NULL;
			 }
		    }

		  /* Add it if it has not been deleted. */
		  if (flags[1] != 'D')
		    {
		       tree->elements[element].element = menu;
		       tree->elements[element].type =
			  ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
		    }
	       }
	  }
     }
   return 0;
}

static Ecore_Desktop_Tree *
_ecore_desktop_menu_create_menu()
{
   Ecore_Desktop_Tree *menu, *rules;
   Ecore_Hash         *pool, *apps;

   menu = ecore_desktop_tree_new(NULL);
   rules = ecore_desktop_tree_new(NULL);
   pool = ecore_hash_new(ecore_str_hash, ecore_str_compare);
   apps = ecore_hash_new(ecore_str_hash, ecore_str_compare);
   if ((menu) && (rules) && (pool) && (apps))
     {
	ecore_hash_set_free_key(pool, free);
	ecore_hash_set_free_value(pool, free);
	ecore_hash_set_free_key(apps, free);
	ecore_hash_set_free_value(apps, free);
	ecore_desktop_tree_extend(menu, "<MENU <    > <> <>");
	ecore_desktop_tree_extend(menu, "<MENU_PATH ");
	ecore_desktop_tree_add_hash(menu, pool);
	ecore_desktop_tree_add_child(menu, rules);
	ecore_desktop_tree_add_hash(menu, apps);
     }
   else
     {
	if (apps)
	   ecore_hash_destroy(apps);
	if (pool)
	   ecore_hash_destroy(pool);
	if (rules)
	   ecore_desktop_tree_del(rules);
	if (menu)
	   ecore_desktop_tree_del(menu);
	menu = NULL;
     }

   return menu;
}

static int
_ecore_desktop_menu_check_directory(void *data, const char *path)
{
   char               *p;
   Ecore_Desktop_Tree *merge;

   merge = (Ecore_Desktop_Tree *) data;
   p = strrchr(path, '.');
   if (p)
     {
	if (strcmp(p, ".directory") == 0)
	  {
	     char                merge_file[PATH_MAX];

	     sprintf(merge_file, "<Directory %s", path);
	     ecore_desktop_tree_extend(merge, merge_file);
	  }
     }

   return 0;
}

static int
_ecore_desktop_menu_check_menu(void *data, const char *path)
{
   char               *p;
   Ecore_Desktop_Tree *merge;

   merge = (Ecore_Desktop_Tree *) data;
   p = strrchr(path, '.');
   if (p)
     {
	if (strcmp(p, ".menu") == 0)
	  {
	     char                merge_file[PATH_MAX];

	     sprintf(merge_file, "<MergeFile type=\"path\" %s", path);
	     ecore_desktop_tree_extend(merge, merge_file);
	  }
     }

   return 0;
}

static int
_ecore_desktop_menu_legacy_menu_dir(void *data, const char *path)
{
   struct _ecore_desktop_menu_legacy_data *legacy_data;
   Ecore_Desktop_Tree *menu;
   char               *temp_path;
   int                 start;
   int                 i, count = 0;

   legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
   for (i = legacy_data->length; path[i] != '\0'; i++)
     {
	if (path[i] == '/')
	   count++;
     }
   if (count == 1)
     {
	legacy_data->current = legacy_data->merge;
	legacy_data->level = 0;
     }
   else if (count <= legacy_data->level)
     {
	if ((legacy_data->current) && (legacy_data->current->parent))
	  {
	     legacy_data->current = legacy_data->current->parent;
	     legacy_data->level--;
	  }
     }

   start = legacy_data->length;
   count = 0;
   temp_path = strdup(path);
   if (temp_path)
     {
	for (i = legacy_data->length; temp_path[i] != '\0'; i++)
	  {
	     if (temp_path[i] == '/')
	       {
		  if (count >= legacy_data->level)
		    {
		       temp_path[i] = '\0';
		       menu = _ecore_desktop_menu_create_menu();
		       if (menu)
			 {
			    char                temp[PATH_MAX];

			    sprintf(temp, "<MENU <   L> <%s> <>",
				    &temp_path[start]);
			    menu->elements[0].element = strdup(temp);
			    ecore_desktop_tree_track(menu,
						     menu->elements[0].element);
			    sprintf(temp, "<MENU_PATH %s/%s", legacy_data->menu,
				    &temp_path[legacy_data->length]);
			    menu->elements[1].element = strdup(temp);
			    ecore_desktop_tree_track(menu,
						     menu->elements[1].element);

			    ecore_desktop_tree_add_child(legacy_data->current,
							 menu);
			    /*  This is not needed, but if it was, this is where it would go.
			     * sprintf(temp, "<AppDir %s/", temp_path);
			     * ecore_desktop_tree_extend(menu, temp);
			     */
			    sprintf(temp, "<DirectoryDir %s/", temp_path);
			    ecore_desktop_tree_extend(menu, temp);

			    legacy_data->current = menu;
			    legacy_data->level++;
			 }
		       temp_path[i] = '/';
		    }
		  start = i + 1;
		  count++;
	       }
	  }
	free(temp_path);
     }

   legacy_data->level = count;

   return 0;
}

static int
_ecore_desktop_menu_legacy_menu(void *data, const char *path)
{
   struct _ecore_desktop_menu_legacy_data *legacy_data;
   char               *menu_path;
   char               *file;
   char                temp[PATH_MAX];
   int                 i, count = 0, menu_count = 0;

   legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
   if ((legacy_data->current) && (legacy_data->current->size > 0) &&
       (legacy_data->current->elements[1].type ==
	ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
     {
	menu_path = (char *)legacy_data->current->elements[1].element;
	menu_path += 12 + legacy_data->menu_length;
     }
   else
     {
	menu_path = "";
	printf("  PROBLEM IN LEGACYDIR FILE - %s - %s  %s\n",
	       legacy_data->prefix, legacy_data->path,
	       &path[legacy_data->length]);
     }

   for (i = legacy_data->length; path[i] != '\0'; i++)
     {
	if (path[i] == '/')
	   count++;
     }
   for (i = 0; menu_path[i] != '\0'; i++)
     {
	if (menu_path[i] == '/')
	   menu_count++;
     }
   /* FIXME: The (legacy_data->current) test is just to patch a seggie, find out why. */
   while ((menu_count >= count) && (legacy_data->current))
     {
	legacy_data->current = legacy_data->current->parent;
	menu_count--;
     }
   if ((legacy_data->current) && (legacy_data->current->size > 0) &&
       (legacy_data->current->elements[1].type ==
	ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
     {
	menu_path = (char *)legacy_data->current->elements[1].element;
	menu_path += 12 + legacy_data->menu_length;
     }
   else
     {
	/* FIXME:  Bugger, a corner case.
	 * If this is .directory, then maybe ignore it?
	 * If this is a desktop, we need to put it into the pool of the menu that legacy_data->merge will get merged into.
	 */
	menu_path = "";
	printf("  LEGACYDIR FILE - %s - %s  %s\n", legacy_data->prefix,
	       legacy_data->path, &path[legacy_data->length]);
	return 0;
     }

   menu_count = strlen(menu_path);
   if (menu_count)
      menu_count++;
   file = (char *)&path[legacy_data->length + menu_count];
   count = strlen(file);

   if (strcmp(".directory", file) == 0)
     {
	menu_path = (char *)legacy_data->current->elements[0].element;
	menu_count = strlen(menu_path);
	menu_path[menu_count - 3] = '\0';
	sprintf(temp, "%s <.directory>", menu_path);
	legacy_data->current->elements[0].element = strdup(temp);
	ecore_desktop_tree_track(legacy_data->current,
				 legacy_data->current->elements[0].element);
     }
   else if (strcmp(".desktop", &file[count - 8]) == 0)
     {
	Ecore_Hash         *pool;
	Ecore_Desktop_Tree *rules;

	pool = (Ecore_Hash *) legacy_data->current->elements[2].element;
	rules =
	   (Ecore_Desktop_Tree *) legacy_data->current->elements[3].element;
	if (rules->size == 0)
	  {
	     Ecore_Desktop_Tree *new_rules;

	     new_rules = ecore_desktop_tree_new(NULL);
	     if (new_rules)
		ecore_desktop_tree_add_child(rules, new_rules);
	  }
	sprintf(temp, "%s%s", legacy_data->prefix, file);
	ecore_hash_set(pool, strdup(temp), strdup(path));
#ifdef DEBUG
//	printf
//	   ("POOLING - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s - %s\n",
//	    path, file, temp);
#endif
	if (rules->size > 0)
	  {
	     rules = (Ecore_Desktop_Tree *) rules->elements[0].element;
	     sprintf(temp, "IOF %s%s", legacy_data->prefix, file);
	     ecore_desktop_tree_extend(rules, temp);
	  }
     }
#ifdef DEBUG
   else
      printf("PROBLEM - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s\n",
	     path, file);
#endif

   return 0;
}

static void
_ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
				Ecore_Desktop_Tree * tree, char type,
				char sub_type)
{
   int                 i;
   char                temp[PATH_MAX];

   for (i = 0; i < tree->size; i++)
     {
	if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
	  {
             /* FIXME <All /> or <All foo="bar"> would be valid input (which this won't catch) */
	     if (strcmp((char *)tree->elements[i].element, "<All/") == 0)
	       {
		  sprintf(temp, "%c%cA", type, sub_type);
		  ecore_desktop_tree_extend(rules, temp);
	       }
	     else if (strcmp((char *)tree->elements[i].element, "<Filename") ==
		      0)
	       {
		  sprintf(temp, "%c%cF %s", type, sub_type,
			  (char *)tree->elements[i + 1].element);
		  ecore_desktop_tree_extend(rules, temp);
	       }
	     else if (strcmp((char *)tree->elements[i].element, "<Category") ==
		      0)
	       {
{'fraction_non_alphanumeric': 0.12398877332012548, 'fraction_numerical': 0.008108157687157192, 'mean_word_length': 2.250104334346867, 'pattern_counts': {'":': 0, '<': 119, '<?xml version=': 0, '>': 354, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 72}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1203671', 'n_tokens_mistral': 22189, 'n_tokens_neox': 18166, 'n_words': 3912}