1845 lines
53 KiB
C
1845 lines
53 KiB
C
/*
|
|
* 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)
|
|
{
|
|
sprintf(temp, "%c%cC %s", type, sub_type,
|
|
(char *)tree->elements[i + 1].element);
|
|
ecore_desktop_tree_extend(rules, temp);
|
|
}
|
|
else if (strcmp((char *)tree->elements[i].element, "<Or") == 0)
|
|
{
|
|
_ecore_desktop_menu_unxml_rules(rules,
|
|
(Ecore_Desktop_Tree *) tree->
|
|
elements[i + 1].element,
|
|
type, sub_type);
|
|
}
|
|
else if ((strcmp((char *)tree->elements[i].element, "<And") == 0)
|
|
|| (strcmp((char *)tree->elements[i].element, "<Not") ==
|
|
0))
|
|
{
|
|
char this_type;
|
|
Ecore_Desktop_Tree *sub;
|
|
|
|
this_type = ((char *)tree->elements[i].element)[1];
|
|
sub = ecore_desktop_tree_new(NULL);
|
|
if (sub)
|
|
{
|
|
ecore_desktop_tree_add_child(rules, sub);
|
|
for (i++; i < tree->size; i++)
|
|
{
|
|
if (tree->elements[i].type ==
|
|
ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
_ecore_desktop_menu_unxml_rules(sub,
|
|
(Ecore_Desktop_Tree
|
|
*) tree->
|
|
elements[i].
|
|
element, type,
|
|
this_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
{
|
|
_ecore_desktop_menu_unxml_rules(rules,
|
|
(Ecore_Desktop_Tree *) tree->
|
|
elements[i].element, type,
|
|
sub_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
|
|
Ecore_Desktop_Tree * tree)
|
|
{
|
|
int i;
|
|
char *old = NULL;
|
|
char *new = NULL;
|
|
|
|
for (i = 0; i < tree->size; i++)
|
|
{
|
|
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, "<Old") ==
|
|
0)
|
|
old = strdup((char *)sub->elements[1].element);
|
|
if (strcmp((char *)sub->elements[0].element, "<New") ==
|
|
0)
|
|
new = strdup((char *)sub->elements[1].element);
|
|
}
|
|
}
|
|
}
|
|
if ((old) && (new))
|
|
{
|
|
char temp[PATH_MAX * 2];
|
|
|
|
sprintf(temp, "<MOVE <%s> <%s>", old, new);
|
|
ecore_desktop_tree_extend(menu, temp);
|
|
}
|
|
|
|
if (old) free(old);
|
|
old = NULL;
|
|
if (new) free(new);
|
|
new = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
_ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree, Ecore_List * paths,
|
|
const char *pre, const char *post __UNUSED__, char *extra, int element __UNUSED__)
|
|
{
|
|
char t[PATH_MAX], *this_path;
|
|
|
|
/* reverse the order of the dirs. */
|
|
ecore_list_goto_first(paths);
|
|
while ((this_path = ecore_list_next(paths)) != NULL)
|
|
{
|
|
if (extra)
|
|
sprintf(t, "%s %s%s-merged/", pre, this_path, extra);
|
|
else
|
|
sprintf(t, "%s %s", pre, this_path);
|
|
if (tree)
|
|
ecore_desktop_tree_extend(tree, t);
|
|
}
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_expand_apps(struct _ecore_desktop_menu_unxml_data
|
|
*unxml_data, char *app_dir, Ecore_Hash * pool)
|
|
{
|
|
if (pool)
|
|
{
|
|
struct _ecore_desktop_menu_expand_apps_data our_data;
|
|
char dir[PATH_MAX];
|
|
|
|
our_data.pool = pool;
|
|
sprintf(dir, "%s", app_dir);
|
|
if (dir[0] != '/')
|
|
sprintf(dir, "%s/%s", unxml_data->path, app_dir);
|
|
our_data.path = dir;
|
|
our_data.length = strlen(dir);
|
|
#ifdef DEBUG
|
|
printf
|
|
("EXPANDING - _ecore_desktop_menu_expand_apps(unxml_data, %s) - %s\n",
|
|
app_dir, dir);
|
|
#endif
|
|
ecore_desktop_paths_recursive_search(dir, NULL, -1, NULL,
|
|
_ecore_desktop_menu_check_app,
|
|
&our_data);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_check_app(void *data, const char *path)
|
|
{
|
|
char *p;
|
|
struct _ecore_desktop_menu_expand_apps_data *our_data;
|
|
|
|
our_data = (struct _ecore_desktop_menu_expand_apps_data *)data;
|
|
p = strrchr(path, '.');
|
|
if (p)
|
|
{
|
|
if (strcmp(p, ".desktop") == 0)
|
|
{
|
|
int i;
|
|
char *file;
|
|
|
|
file = strdup(path + our_data->length);
|
|
if ((file) && (path))
|
|
{
|
|
for (i = 0; file[i] != '\0'; i++)
|
|
if (file[i] == '/')
|
|
file[i] = '-';
|
|
ecore_hash_set(our_data->pool, file, strdup(path));
|
|
#ifdef DEBUG
|
|
// printf
|
|
// ("POOLING - _ecore_desktop_menu_check_app(void *data, %s) - %s\n",
|
|
// path, file);
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
printf("PROBLEM - _ecore_desktop_menu_check_app(void *data, %s)\n",
|
|
path);
|
|
#endif
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_merge(const void *data, Ecore_Desktop_Tree * tree,
|
|
int element, int level)
|
|
{
|
|
struct _ecore_desktop_menu_unxml_data *unxml_data;
|
|
Ecore_Desktop_Tree *merge;
|
|
int result = 0;
|
|
|
|
unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
|
|
merge = ecore_desktop_tree_new(NULL);
|
|
if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
|
|
{
|
|
char *string;
|
|
|
|
string = (char *)tree->elements[element].element;
|
|
if (strcmp(string, "<DefaultMergeDirs/") == 0)
|
|
{
|
|
if (unxml_data->base)
|
|
_ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_menus,
|
|
"<MergeDir", "</MergeDir",
|
|
unxml_data->base, element);
|
|
result = 1;
|
|
}
|
|
else if (strcmp(string, "<KDELegacyDirs/") == 0)
|
|
{
|
|
_ecore_desktop_menu_add_dirs(merge,
|
|
ecore_desktop_paths_kde_legacy,
|
|
"<LegacyDir prefix=\"kde-\"",
|
|
"</LegacyDir", NULL, element);
|
|
result = 1;
|
|
}
|
|
else if (strncmp(string, "<MergeDir ", 10) == 0)
|
|
{
|
|
char merge_path[PATH_MAX];
|
|
|
|
if (string[10] == '/')
|
|
sprintf(merge_path, "%s", &string[10]);
|
|
else
|
|
sprintf(merge_path, "%s%s", unxml_data->path, &string[10]);
|
|
ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL,
|
|
_ecore_desktop_menu_check_menu,
|
|
merge);
|
|
result = 1;
|
|
}
|
|
else if (strncmp(string, "<LegacyDir ", 11) == 0)
|
|
{
|
|
char merge_path[PATH_MAX];
|
|
struct _ecore_desktop_menu_legacy_data legacy_data;
|
|
|
|
string += 11;
|
|
legacy_data.prefix = NULL;
|
|
legacy_data.merge = merge;
|
|
legacy_data.current = merge;
|
|
legacy_data.level = 0;
|
|
legacy_data.menu = (char *)tree->elements[0].element;
|
|
legacy_data.menu += 14;
|
|
legacy_data.menu_length =
|
|
index(legacy_data.menu, '>') - legacy_data.menu;
|
|
legacy_data.menu[legacy_data.menu_length] = '\0';
|
|
if (strncmp(string, "prefix=\"", 8) == 0)
|
|
{
|
|
string += 8;
|
|
legacy_data.prefix = string;
|
|
while ((*string != '"') && (*string != '\0'))
|
|
string++;
|
|
if (*string != '\0')
|
|
*string++ = '\0';
|
|
while ((*string == ' ') && (*string != '\0'))
|
|
string++;
|
|
}
|
|
if (string[0] == '/')
|
|
sprintf(merge_path, "%s", string);
|
|
else
|
|
sprintf(merge_path, "%s%s", unxml_data->path, string);
|
|
legacy_data.path = merge_path;
|
|
legacy_data.length = strlen(merge_path);
|
|
#ifdef DEBUG
|
|
printf("<LEGACYDIR> - %s - %s\n", legacy_data.prefix, merge_path);
|
|
#endif
|
|
ecore_desktop_paths_recursive_search(merge_path, NULL, -1,
|
|
_ecore_desktop_menu_legacy_menu_dir,
|
|
_ecore_desktop_menu_legacy_menu,
|
|
&legacy_data);
|
|
legacy_data.menu[legacy_data.menu_length] = '>';
|
|
result = 1;
|
|
}
|
|
else if (strncmp(string, "<MergeFile ", 11) == 0)
|
|
{
|
|
char merge_path[PATH_MAX];
|
|
int path_type = 1;
|
|
|
|
/* FIXME: need to weed out duplicate <MergeFile's, use the last one. */
|
|
string += 11;
|
|
if (strncmp(string, "type=\"", 6) == 0)
|
|
{
|
|
string += 6;
|
|
if (strncmp(string, "parent\"", 7) == 0)
|
|
path_type = 0;
|
|
while ((*string != '"') && (*string != '\0'))
|
|
string++;
|
|
if (*string != '\0')
|
|
string++;
|
|
while ((*string == ' ') && (*string != '\0'))
|
|
string++;
|
|
}
|
|
if (path_type)
|
|
{
|
|
if (string[0] == '/')
|
|
snprintf(merge_path, PATH_MAX, "%s", string);
|
|
else
|
|
snprintf(merge_path, PATH_MAX, "%s/%s", unxml_data->path, string);
|
|
}
|
|
else /* This is a parent type MergeFile. */
|
|
{
|
|
char *xdg_path;
|
|
int found = -1;
|
|
|
|
/* The spec is a little unclear, and the examples may look like they
|
|
* contradict the description, but it all makes sense if you cross
|
|
* reference it with the XDG Base Directory Specification (version 0.6).
|
|
* To make things harder, parent type MergeFiles never appear on my box.
|
|
*
|
|
* What you do is this.
|
|
*
|
|
* Just plain ignore the specified path in the MergeFile element, it's for
|
|
* legacy apps that don't understand parent types.
|
|
*
|
|
* Take the XDG_CONFIG_DIRS stuff as a whole ($XDG_CONFIG_HOME, then
|
|
* $XDG_CONFIG_DIRS), in this code that will be ecore_desktop_paths_config.
|
|
*
|
|
* If this menu file is from one of the directories in ecore_desktop_paths_config,
|
|
* scan the rest of ecore_desktop_paths_config looking for the new menu. In other
|
|
* words start searching in the next ecore_desktop_paths_config entry after the one
|
|
* that this menu is in.
|
|
*
|
|
* The file to look for is the path to this menu with the portion from
|
|
* ecore_desktop_paths_config stripped off the beginning. For instance, the top level
|
|
* menu file is typically /etc/xdg/menus/applications.menu, and /etc/xdg is
|
|
* typically in ecore_desktop_paths_config, so search for menus/applications.menu.
|
|
*
|
|
* If this menu file is NOT from one of the directories in ecore_desktop_paths_menus,
|
|
* insert nothing.
|
|
*
|
|
* The first one found wins, if none are found, don't merge anything.
|
|
*/
|
|
|
|
merge_path[0] = '\0';
|
|
ecore_list_goto_first(ecore_desktop_paths_config);
|
|
while ((xdg_path = ecore_list_next(ecore_desktop_paths_config)) != NULL)
|
|
{
|
|
if (found < 0)
|
|
{
|
|
int length = strlen(xdg_path);
|
|
|
|
if (strncmp(xdg_path, unxml_data->file, length) == 0)
|
|
found = length;
|
|
}
|
|
else
|
|
{
|
|
snprintf(merge_path, PATH_MAX, "%s%s", xdg_path, &(unxml_data->file)[found]);
|
|
if (ecore_file_exists(merge_path))
|
|
break;
|
|
merge_path[0] = '\0';
|
|
}
|
|
}
|
|
|
|
}
|
|
if (merge_path[0] != '\0')
|
|
{
|
|
Ecore_Desktop_Tree *new_menu;
|
|
|
|
new_menu =
|
|
_ecore_desktop_menu_get0(merge_path,
|
|
unxml_data->merge_stack,
|
|
level + 1);
|
|
if (new_menu)
|
|
{
|
|
if (new_menu->size > 1)
|
|
{
|
|
if (new_menu->elements[1].type ==
|
|
ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
{
|
|
new_menu =
|
|
(Ecore_Desktop_Tree *) new_menu->
|
|
elements[1].element;
|
|
if (new_menu->size > 0)
|
|
{
|
|
if (new_menu->elements[0].type ==
|
|
ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
{
|
|
merge =
|
|
(Ecore_Desktop_Tree *) new_menu->
|
|
elements[0].element;
|
|
ecore_desktop_tree_remove(merge, 0);
|
|
ecore_desktop_tree_remove(merge, 1);
|
|
ecore_desktop_tree_remove(merge, 2);
|
|
ecore_desktop_tree_remove(merge, 3);
|
|
ecore_desktop_tree_remove(merge, 4);
|
|
/* FIXME: The MENU_PATHs need to be prefixed. */
|
|
}
|
|
else
|
|
printf
|
|
("FUCK an error in _ecore_desktop_menu_merge(%s)\n",
|
|
merge_path);
|
|
}
|
|
else
|
|
printf
|
|
("FUCK another error in _ecore_desktop_menu_merge(%s)\n",
|
|
merge_path);
|
|
}
|
|
else
|
|
printf
|
|
("FUCK ME! An error in _ecore_desktop_menu_merge(%s)\n",
|
|
merge_path);
|
|
}
|
|
}
|
|
}
|
|
result = 1;
|
|
}
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
if ((merge) && (merge->size))
|
|
ecore_desktop_tree_merge(tree, element + 1, merge);
|
|
|
|
tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
|
|
tree->elements[element].element = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_expand_default_dirs(const void *data,
|
|
Ecore_Desktop_Tree * tree, int element,
|
|
int level __UNUSED__)
|
|
{
|
|
struct _ecore_desktop_menu_unxml_data *unxml_data;
|
|
Ecore_Desktop_Tree *merge;
|
|
int result = 0;
|
|
|
|
unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
|
|
merge = ecore_desktop_tree_new(NULL);
|
|
if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
|
|
{
|
|
char *string;
|
|
|
|
string = (char *)tree->elements[element].element;
|
|
if (strcmp(string, "<DefaultAppDirs/") == 0)
|
|
{
|
|
_ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_desktops,
|
|
"<AppDir", "</AppDir", NULL, element);
|
|
result = 1;
|
|
}
|
|
else if (strcmp(string, "<DefaultDirectoryDirs/") == 0)
|
|
{
|
|
_ecore_desktop_menu_add_dirs(merge,
|
|
ecore_desktop_paths_directories,
|
|
"<DirectoryDir", "</DirectoryDir",
|
|
NULL, element);
|
|
result = 1;
|
|
}
|
|
}
|
|
if (result)
|
|
{
|
|
if ((merge) && (merge->size))
|
|
ecore_desktop_tree_merge(tree, element + 1, merge);
|
|
|
|
tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
|
|
tree->elements[element].element = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_generate(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, "<MENU ", 6) == 0)
|
|
{
|
|
int i;
|
|
struct _ecore_desktop_menu_generate_data generate_data;
|
|
|
|
generate_data.unallocated = unxml_data->unallocated;
|
|
generate_data.name = (char *)tree->elements[element].element;
|
|
generate_data.path = (char *)tree->elements[element + 1].element;
|
|
generate_data.pool = (Ecore_Hash *) tree->elements[element + 2].element;
|
|
generate_data.rules = (Ecore_Desktop_Tree *) tree->elements[element + 3].element;
|
|
generate_data.apps = (Ecore_Hash *) tree->elements[element + 4].element;
|
|
|
|
/* generate and inherit the pools on the first pass, and preparse the include/exclude logic. */
|
|
if (!generate_data.unallocated)
|
|
{
|
|
int i;
|
|
|
|
for (i = element + 5; i < tree->size; i++)
|
|
{
|
|
int result = 0;
|
|
char *string;
|
|
|
|
if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
|
|
{
|
|
string = (char *)tree->elements[i].element;
|
|
if (strncmp(string, "<AppDir ", 8) == 0)
|
|
{
|
|
_ecore_desktop_menu_expand_apps(unxml_data, &string[8], generate_data.pool);
|
|
result = 1;
|
|
}
|
|
else if (strncmp(string, "<DirectoryDir ", 14) == 0)
|
|
{
|
|
char merge_path[PATH_MAX];
|
|
Ecore_Desktop_Tree *merge;
|
|
|
|
if (string[14] == '/')
|
|
sprintf(merge_path, "%s", &string[14]);
|
|
else
|
|
sprintf(merge_path, "%s%s", unxml_data->path, &string[14]);
|
|
merge = ecore_desktop_tree_new(NULL);
|
|
if (merge)
|
|
{
|
|
ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL, _ecore_desktop_menu_check_directory, merge);
|
|
ecore_desktop_tree_merge(tree, i + 1, merge);
|
|
}
|
|
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)
|
|
{
|
|
string = (char *)sub->elements[0].element;
|
|
if ((strcmp(string, "<Include") == 0) || (strcmp(string, "<Exclude") == 0))
|
|
{
|
|
Ecore_Desktop_Tree *new_sub;
|
|
|
|
new_sub = ecore_desktop_tree_new(NULL);
|
|
if (new_sub)
|
|
{
|
|
ecore_desktop_tree_add_child(generate_data.rules, new_sub);
|
|
_ecore_desktop_menu_unxml_rules(new_sub, sub, string[1], 'O');
|
|
}
|
|
result = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
tree->elements[i].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
|
|
tree->elements[i].element = NULL;
|
|
}
|
|
}
|
|
|
|
if (unxml_data->stack->size <= level)
|
|
{
|
|
while (unxml_data->stack->size < level)
|
|
ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
|
|
ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
|
|
}
|
|
else
|
|
{
|
|
unxml_data->stack->elements[level].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH;
|
|
unxml_data->stack->elements[level].element = generate_data.pool;
|
|
}
|
|
for (i = level - 1; i >= 0; i--)
|
|
{
|
|
if (unxml_data->stack->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH)
|
|
{
|
|
Ecore_Hash *ancestor;
|
|
|
|
ancestor = (Ecore_Hash *) unxml_data->stack->elements[i].element;
|
|
ecore_hash_for_each_node(ancestor, _ecore_desktop_menu_inherit_apps, generate_data.pool);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Process the rules. */
|
|
if (generate_data.name[9] == (generate_data.unallocated ? 'O' : ' '))
|
|
{
|
|
#ifdef DEBUG
|
|
printf("MAKING MENU - %s \t\t%s\n", generate_data.path, generate_data.name);
|
|
ecore_desktop_tree_dump(generate_data.rules, 0);
|
|
printf("\n\n");
|
|
#endif
|
|
for (i = 0; i < generate_data.rules->size; i++)
|
|
{
|
|
if (generate_data.rules->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
{
|
|
generate_data.rule = (Ecore_Desktop_Tree *) generate_data.rules->elements[i].element;
|
|
if (generate_data.rule->size > 0)
|
|
{
|
|
char type = 'I';
|
|
|
|
/* Find out if this is an include or an exclude. This info may be way down in the tree. */
|
|
if (generate_data.rule->elements[0].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
ecore_desktop_tree_foreach(generate_data.rule, 0, _ecore_desktop_menu_is_include, &type);
|
|
if (type == 'I')
|
|
{
|
|
generate_data.include = TRUE;
|
|
ecore_hash_for_each_node(generate_data.pool, _ecore_desktop_menu_select_app, &generate_data);
|
|
}
|
|
else
|
|
{
|
|
generate_data.include = FALSE;
|
|
ecore_hash_for_each_node(generate_data.apps, _ecore_desktop_menu_select_app, &generate_data);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
printf("Fuck, a bug in _ecore_desktop_menus.\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__)
|
|
{
|
|
char *result;
|
|
|
|
result = (char *)data;
|
|
if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
|
|
{
|
|
*result = ((char *)tree->elements[element].element)[0];
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
_ecore_desktop_menu_inherit_apps(void *value, void *user_data)
|
|
{
|
|
Ecore_Hash_Node *node;
|
|
Ecore_Hash *pool;
|
|
char *key, *app;
|
|
|
|
pool = (Ecore_Hash *) user_data;
|
|
node = (Ecore_Hash_Node *) value;
|
|
key = (char *)node->key;
|
|
app = (char *)node->value;
|
|
#ifdef DEBUG
|
|
// printf("CHECKING %s - %s\n", app, key);
|
|
#endif
|
|
if (!ecore_hash_get(pool, key))
|
|
ecore_hash_set(pool, strdup(key), strdup(app));
|
|
}
|
|
|
|
static void
|
|
_ecore_desktop_menu_select_app(void *value, void *user_data)
|
|
{
|
|
Ecore_Hash_Node *node;
|
|
Ecore_Desktop *desktop;
|
|
struct _ecore_desktop_menu_generate_data *generate_data;
|
|
char *key, *app;
|
|
|
|
node = (Ecore_Hash_Node *) value;
|
|
generate_data = (struct _ecore_desktop_menu_generate_data *)user_data;
|
|
key = (char *)node->key;
|
|
app = (char *)node->value;
|
|
|
|
/* FIXME: pass an actuall language parameter. */
|
|
desktop = ecore_desktop_get(app, NULL);
|
|
|
|
if (desktop)
|
|
{
|
|
if ((generate_data->unallocated) && (desktop->allocated))
|
|
return;
|
|
|
|
if (_ecore_desktop_menu_apply_rules(generate_data, generate_data->rule, key, desktop))
|
|
{
|
|
desktop->allocated = TRUE;
|
|
if (generate_data->include)
|
|
{
|
|
ecore_hash_set(generate_data->apps, key, strdup(app));
|
|
#ifdef DEBUG
|
|
printf("INCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ecore_hash_remove(generate_data->apps, key);
|
|
#ifdef DEBUG
|
|
printf("EXCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
_ecore_desktop_menu_apply_rules(struct _ecore_desktop_menu_generate_data *generate_data, Ecore_Desktop_Tree * rule, char *key, Ecore_Desktop * desktop)
|
|
{
|
|
char type = 'O';
|
|
int result = FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < rule->size; i++)
|
|
{
|
|
if (rule->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
|
|
{
|
|
result = _ecore_desktop_menu_apply_rules(generate_data, (Ecore_Desktop_Tree *) rule->elements[i].element, key, desktop);
|
|
}
|
|
else
|
|
{
|
|
char *rul;
|
|
char match;
|
|
int sub_result = FALSE;
|
|
|
|
rul = (char *)rule->elements[i].element;
|
|
type = rul[1];
|
|
match = rul[2];
|
|
switch (match)
|
|
{
|
|
case 'A':
|
|
{
|
|
sub_result = TRUE;
|
|
break;
|
|
}
|
|
|
|
case 'F':
|
|
{
|
|
if (strcmp(key, &rul[4]) == 0)
|
|
sub_result = TRUE;
|
|
break;
|
|
}
|
|
|
|
case 'C':
|
|
{
|
|
/* Try to match a category. */
|
|
if ((desktop->Categories) && (ecore_hash_get(desktop->Categories, &rul[4]) != NULL))
|
|
sub_result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
switch (type)
|
|
{
|
|
case 'A': result = TRUE; if (!sub_result) return FALSE; break;
|
|
case 'N': result = TRUE; if (sub_result) return FALSE; break;
|
|
default: if (sub_result) return TRUE; break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
|
|
<Menu (tree)
|
|
name
|
|
flags = " " or "MDO" the first letter of - Marked, Deleted, OnlyUnallocated
|
|
pool (hash)
|
|
id = path
|
|
id = path
|
|
rules (tree)
|
|
rule
|
|
rule
|
|
menu (hash)
|
|
id = path
|
|
id = path
|
|
<Menu (tree)
|
|
<Menu (tree)
|
|
|
|
rules (tree)
|
|
include/exclude or all/file/category x
|
|
and/not (tree)
|
|
include/exclude and/not all/file/category x
|
|
|
|
ALL
|
|
return true
|
|
|
|
FILENAME
|
|
if the rule string matches the desktop id return true
|
|
otherwise return false
|
|
|
|
CATEGORY
|
|
loop through the apps categories
|
|
as soon as one matches the rule string, return true
|
|
otherwise return false.
|
|
|
|
OR (implied)
|
|
loop through the rules
|
|
as soon as one matches, return true
|
|
otherwise return false.
|
|
|
|
SUB RULES
|
|
process the sub rules, return the result
|
|
|
|
AND
|
|
loop through the rules
|
|
as soon as one doesn't match, return false
|
|
otherwise return true.
|
|
|
|
NOT (implied OR)
|
|
loop through the rules
|
|
as soon as one matches, return false
|
|
otherwise return true.
|
|
*/
|
|
|
|
/* FDO PARSING STEPS.
|
|
|
|
An asterisk in the first column marks those bits that are done.
|
|
The spec is not numbered, so I have invented a numbering to help match the
|
|
steps to the code.
|
|
|
|
10000000 merge menus
|
|
*11000000 expand <KDELegacyDir>'s to <LegacyDir>.
|
|
*12000000 expand <LegacyDir>'s
|
|
*12100000 for each dir (recursive)
|
|
*12110000 create recursively nested <MENU < L> <dirname> <> element
|
|
*12120000 // <AppDir>dirpath</AppDir>
|
|
*12130000 <DirectoryDir>dirpath</DirectoryDir>
|
|
*12140000 if exist .directory
|
|
*12141000 add <.directory> to name
|
|
*12150000 <Include>
|
|
*12160000 for each *.desktop
|
|
12161000 if no categories in bar.desktop
|
|
*12161100 <Filename>prefix-bar.desktop</Filename>
|
|
12162000 add "Legacy" to categories
|
|
*12163000 add any prefix to the desktop ID.
|
|
*12164000 add it to the pool
|
|
*12170000 </Include>
|
|
*13000000 for each <MergeFile> and <MergeDir> element
|
|
*13100000 get the root <Menu> elements from that elements file/s.
|
|
*13200000 remove the <Name> element from those root <Menu> elements.
|
|
*13300000 replace that element with the child elements of those root <Menu> elements.
|
|
*13400000? expand the <DefaultMergeDirs> to <MergeDir>'s $XDG_CONFIG_DIRS/menus/foo-merged/ for the menu file foo.menu
|
|
FIXME: The above should probably move to before 13000000
|
|
*14000000 loop until all <MergeFile> and <MergeDir> elements are done,
|
|
*14000000 careful to avoid infinite loops in files that reference each other.
|
|
*15000000 for each <Menu> recursively
|
|
15100000 consolidate duplicate child <Menu>s.
|
|
*15200000 expand <DefaultAppDir>s and <DefaultDirectoryDir>s to <AppDir>s and <DirectoryDir>s.
|
|
15300000 consolidate duplicate child <AppDir>s, <DirectoryDir>s, and <Directory>s.
|
|
16000000 for each <Menu> recursively (but from the deepest level out)
|
|
16100000 for each <Move>
|
|
16110000 do the move.
|
|
16120000 mark any <Menu> that has been affected.
|
|
17000000 for each marked <Menu> recursively
|
|
17100000 consolidate duplicate child <Menu>s.
|
|
*18000000 for each <Menu> recursively
|
|
*18100000 if there are <Deleted> elements that are not ovreridden by a <NotDelete> element
|
|
*18110000 remove this <Menu> element and all it's children.
|
|
|
|
*20000000 generate menus
|
|
*21000000 for each <Menu> that is <NotOnlyUnallocated> (which is the default)
|
|
*21100000 for each <AppDir>
|
|
*21110000 for each .desktop
|
|
*21111000 if it exists in the pool, replace it.
|
|
*21112000 else add it to the pool.
|
|
*21200000 for each parent <Menu>
|
|
*21210000 for each .desktop in the pool
|
|
*21211000 if it doesn't exist in the child <Menu> pool
|
|
*21211100 add it to the pool.
|
|
*21300000 for each <Include> and <Exclude>
|
|
*21310000 if rule is an <Include>
|
|
*21311000 for each .desktop in pool
|
|
*21311100 for each rule
|
|
*21311110 if rule matches .desktop in pool
|
|
*21311111 add .desktop to menu.
|
|
*21311112 mark it as allocated
|
|
*21320000 if rule is an <Exclude>
|
|
*21321000 for each .desktop in menu
|
|
*21321100 for each rule
|
|
*21321110 if rule matches .desktop in menu
|
|
*21321111 remove .desktop from menu.
|
|
*21321112 leave it as allocated.
|
|
|
|
*30000000 generate unallocated menus
|
|
*31000000 Same as for generate menus, but only the <OnlyUnallocated> ones.
|
|
*32000000 Only the unallocated .desktop entries can be used.
|
|
|
|
40000000 generate menu layout
|
|
This part of the spec is a whole other rabbit hole, and optional. B-)
|
|
*/
|
|
|
|
|
|
/* THINGS TAHT CAN BE DUPLICATED
|
|
|
|
AppDir last dup is used
|
|
.desktop last dup is used
|
|
DirectoryDir last dup is used
|
|
.directory last dup is used
|
|
Directory last dup is used, unless it points to a non existant entry, then work your way backwards
|
|
LegacyDir last dup is used
|
|
MergeDir last dup is used
|
|
MergeFile last dup is used
|
|
|
|
DefaultMergeDirs dirs that are earlier in the path go later in the menu, so they have priority
|
|
KDELegacyDirs dirs that are earlier in the path go later in the menu, so they have priority
|
|
|
|
OnlyUnallocated & NotOnlyUnallocated last one wins, default is NotOnlyUnallocated
|
|
Deleted & NotDeleted last one wins
|
|
|
|
Include & Exclude done in order
|
|
*/
|