Adding upstream version 0.7.0. upstream/0.7.0
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Sat, 6 Sep 2014 16:33:38 +0000 (19:33 +0300)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Sat, 6 Sep 2014 16:33:38 +0000 (19:33 +0300)
37 files changed:
AUTHORS
Makefile.in
NEWS
README
configure
configure.ac
docs/Makefile.in
docs/reference/Makefile.in
docs/reference/libmenu-cache/Makefile.in
libmenu-cache/Makefile.am
libmenu-cache/Makefile.in
libmenu-cache/menu-cache.c
libmenu-cache/version.h
menu-cache-daemon/Makefile.in
menu-cache-daemon/menu-cached.c
menu-cache-gen/Makefile.am
menu-cache-gen/Makefile.in
menu-cache-gen/canonicalize.c [deleted file]
menu-cache-gen/canonicalize.h [deleted file]
menu-cache-gen/desktop-entries.c [deleted file]
menu-cache-gen/desktop-entries.h [deleted file]
menu-cache-gen/entry-directories.c [deleted file]
menu-cache-gen/entry-directories.h [deleted file]
menu-cache-gen/gmenu-tree.c [deleted file]
menu-cache-gen/gmenu-tree.h [deleted file]
menu-cache-gen/main.c [new file with mode: 0644]
menu-cache-gen/menu-cache-gen.c [deleted file]
menu-cache-gen/menu-cache-gen.h [deleted file]
menu-cache-gen/menu-compose.c [new file with mode: 0644]
menu-cache-gen/menu-layout.c [deleted file]
menu-cache-gen/menu-layout.h [deleted file]
menu-cache-gen/menu-merge.c [new file with mode: 0644]
menu-cache-gen/menu-monitor.c [deleted file]
menu-cache-gen/menu-monitor.h [deleted file]
menu-cache-gen/menu-tags.h [new file with mode: 0644]
menu-cache-gen/menu-util.c [deleted file]
menu-cache-gen/menu-util.h [deleted file]

diff --git a/AUTHORS b/AUTHORS
index a13b96c..19367d4 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,11 +1,10 @@
 Upstream Authors:
     LXDE team: http://lxde.org
     Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
+    Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
 
 Copyright:
     Copyright (c) 2012-2014 LXQt team
 
 License: GPL-2 and LGPL-2.1+
 The full text of the licenses can be found in the 'COPYING' file.
-
-Based on source code from libmenu of the GNOME project <http://gnome.org/>
index b13f4c4..075de28 100644 (file)
@@ -85,7 +85,7 @@ DIST_COMMON = INSTALL NEWS README AUTHORS ChangeLog \
        $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
        $(top_srcdir)/configure $(am__configure_deps) \
        $(srcdir)/config.h.in COPYING compile config.guess config.sub \
-       install-sh missing ltmain.sh
+       depcomp install-sh missing ltmain.sh
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
@@ -238,6 +238,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
diff --git a/NEWS b/NEWS
index af302ea..2683914 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,22 @@
+Changes in 0.7.0 since 0.6.1:
+
+* Added bit of support for multiple supported cache file versions, using
+    CACHE_GEN_VERSION environment variable to the generator. This may be
+    useful in future when 1.2 cache file version will be implemented.
+
+* The menu-cache-gen libexec binary is rewritten from scratch. No that
+    Red Hat / GNOME code anymore. New menu-cache-gen uses libfm-extra XML
+    manipulation functions therefore it is required now for build.
+
+* Added a parameter for menu-cached to specify socket path instead of
+    calculating one, that is definitely more safe.
+
+* Libmenu-cache handles menu-cached failure more gracefully now, don't
+    tries to restart it so fast that it clones many times.
+
+* Fixed menu-cached crash after menu-cache-gen failure.
+
+
 Changes in 0.6.1 since 0.6.0:
 
 * Fixed invalid memory access after cache reload.
diff --git a/README b/README
index ada2f51..1026e02 100644 (file)
--- a/README
+++ b/README
@@ -10,6 +10,31 @@ Advantages:
 4. Less unnecessary and complicated file monitoring.
 5. Heavily reduced disk I/O.
 
+Installing:
+
+Since version 0.7.0 the Libmenu-cache requires Libfm-extra for the
+menu-cache-gen menu cache generation binary. Since Libfm depends on
+Libmenu-cache, there is some hint for bootstrapers how to build those
+libraries together: you need create Libfm-extra first, you can easily
+do this by passing '--with-extra-only' option to configure script and
+installing Libfm-extra. Then you can succesfully build Libmenu-cache
+and therefore build full version of Libfm.
+
+Diagnostics:
+
+Libmenu-cache uses cache generation in "fail-proof" mode when it will
+ignore improper tags in XML menu file but fail only if that menu file
+doesn't exist at all, or has invalid XML structure, or has no root menu
+with name 'Applications'. If you want to explore how it is generated
+then you can start menu-cache-gen manually in verbose mode, for example:
+
+G_MESSAGES_DEBUG=all \
+  /usr/lib/menu-cache/menu-cache-gen -v -i applications.menu -o /dev/null
+
+so you can see all the processing of your XML file and it will fail on
+any broken tag or at least show you a warning and you can inspect that
+log.
+
 Spec:
 
 Cached menus are localized and stored in ~/.cache/menus/file_name.
index 6f8de61..ae278e7 100755 (executable)
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for menu-cache 0.6.1.
+# Generated by GNU Autoconf 2.69 for menu-cache 0.7.0.
 #
 # Report bugs to <http://lxde.org/>.
 #
@@ -590,12 +590,11 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='menu-cache'
 PACKAGE_TARNAME='menu-cache'
-PACKAGE_VERSION='0.6.1'
-PACKAGE_STRING='menu-cache 0.6.1'
+PACKAGE_VERSION='0.7.0'
+PACKAGE_STRING='menu-cache 0.7.0'
 PACKAGE_BUGREPORT='http://lxde.org/'
 PACKAGE_URL=''
 
-ac_unique_file="menu-cache-gen/gmenu-tree.h"
 # Factoring default headers for most tests.
 ac_includes_default="\
 #include <stdio.h>
@@ -658,6 +657,8 @@ GTKDOC_REBASE
 GTKDOC_CHECK
 DEBUG_CFLAGS
 ADDITIONAL_FLAGS
+LIBFM_EXTRA_LIBS
+LIBFM_EXTRA_CFLAGS
 GLIB_LIBS
 GLIB_CFLAGS
 PKG_CONFIG_LIBDIR
@@ -813,6 +814,8 @@ PKG_CONFIG_PATH
 PKG_CONFIG_LIBDIR
 GLIB_CFLAGS
 GLIB_LIBS
+LIBFM_EXTRA_CFLAGS
+LIBFM_EXTRA_LIBS
 GTKDOC_DEPS_CFLAGS
 GTKDOC_DEPS_LIBS'
 
@@ -1355,7 +1358,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures menu-cache 0.6.1 to adapt to many kinds of systems.
+\`configure' configures menu-cache 0.7.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1425,7 +1428,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of menu-cache 0.6.1:";;
+     short | recursive ) echo "Configuration of menu-cache 0.7.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1448,7 +1451,7 @@ Optional Features:
                           optimize for fast installation [default=yes]
   --disable-libtool-lock  avoid locking (might break parallel builds)
   --enable-more-warnings  Add more warnings [default=no]
-  --enable-debug=no/yes turn on debugging default=no
+  --enable-debug=no/yes   turn on debugging [default=no]
   --enable-gtk-doc        use gtk-doc to build documentation [[default=no]]
   --enable-gtk-doc-html   build documentation in html format [[default=yes]]
   --enable-gtk-doc-pdf    build documentation in pdf format [[default=no]]
@@ -1479,6 +1482,10 @@ Some influential environment variables:
               path overriding pkg-config's built-in search path
   GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config
   GLIB_LIBS   linker flags for GLIB, overriding pkg-config
+  LIBFM_EXTRA_CFLAGS
+              C compiler flags for LIBFM_EXTRA, overriding pkg-config
+  LIBFM_EXTRA_LIBS
+              linker flags for LIBFM_EXTRA, overriding pkg-config
   GTKDOC_DEPS_CFLAGS
               C compiler flags for GTKDOC_DEPS, overriding pkg-config
   GTKDOC_DEPS_LIBS
@@ -1550,7 +1557,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-menu-cache configure 0.6.1
+menu-cache configure 0.7.0
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1828,7 +1835,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by menu-cache $as_me 0.6.1, which was
+It was created by menu-cache $as_me 0.7.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2177,7 +2184,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
-
 am__api_version='1.14'
 
 ac_aux_dir=
@@ -2693,7 +2699,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='menu-cache'
- VERSION='0.6.1'
+ VERSION='0.7.0'
 
 
 cat >>confdefs.h <<_ACEOF
 
 
 
+
+pkg_failed=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for LIBFM_EXTRA" >&5
+$as_echo_n "checking for LIBFM_EXTRA... " >&6; }
+
+if test -n "$LIBFM_EXTRA_CFLAGS"; then
+    pkg_cv_LIBFM_EXTRA_CFLAGS="$LIBFM_EXTRA_CFLAGS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libfm-extra\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libfm-extra") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBFM_EXTRA_CFLAGS=`$PKG_CONFIG --cflags "libfm-extra" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+if test -n "$LIBFM_EXTRA_LIBS"; then
+    pkg_cv_LIBFM_EXTRA_LIBS="$LIBFM_EXTRA_LIBS"
+ elif test -n "$PKG_CONFIG"; then
+    if test -n "$PKG_CONFIG" && \
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libfm-extra\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "libfm-extra") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then
+  pkg_cv_LIBFM_EXTRA_LIBS=`$PKG_CONFIG --libs "libfm-extra" 2>/dev/null`
+                     test "x$?" != "x0" && pkg_failed=yes
+else
+  pkg_failed=yes
+fi
+ else
+    pkg_failed=untried
+fi
+
+
+
+if test $pkg_failed = yes; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
+        _pkg_short_errors_supported=yes
+else
+        _pkg_short_errors_supported=no
+fi
+        if test $_pkg_short_errors_supported = yes; then
+               LIBFM_EXTRA_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "libfm-extra" 2>&1`
+        else
+               LIBFM_EXTRA_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "libfm-extra" 2>&1`
+        fi
+       # Put the nasty error message in config.log where it belongs
+       echo "$LIBFM_EXTRA_PKG_ERRORS" >&5
+
+       as_fn_error $? "Package requirements (libfm-extra) were not met:
+
+$LIBFM_EXTRA_PKG_ERRORS
+
+Consider adjusting the PKG_CONFIG_PATH environment variable if you
+installed software in a non-standard prefix.
+
+Alternatively, you may set the environment variables LIBFM_EXTRA_CFLAGS
+and LIBFM_EXTRA_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details." "$LINENO" 5
+elif test $pkg_failed = untried; then
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "The pkg-config script could not be found or is too old.  Make sure it
+is in your PATH or set the PKG_CONFIG environment variable to the full
+path to pkg-config.
+
+Alternatively, you may set the environment variables LIBFM_EXTRA_CFLAGS
+and LIBFM_EXTRA_LIBS to avoid the need to call pkg-config.
+See the pkg-config man page for more details.
+
+To get pkg-config, see <http://pkg-config.freedesktop.org/>.
+See \`config.log' for more details" "$LINENO" 5; }
+else
+       LIBFM_EXTRA_CFLAGS=$pkg_cv_LIBFM_EXTRA_CFLAGS
+       LIBFM_EXTRA_LIBS=$pkg_cv_LIBFM_EXTRA_LIBS
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+fi
+
+
+
 # Check whether --enable-more_warnings was given.
 if test "${enable_more_warnings+set}" = set; then :
   enableval=$enable_more_warnings; enable_more_warnings="${enableval}"
@@ -13420,7 +13520,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by menu-cache $as_me 0.6.1, which was
+This file was extended by menu-cache $as_me 0.7.0, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -13486,7 +13586,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-menu-cache config.status 0.6.1
+menu-cache config.status 0.7.0
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
index 793a36a..4c81765 100644 (file)
@@ -1,6 +1,5 @@
-AC_INIT([menu-cache], [0.6.1],
+AC_INIT([menu-cache], [0.7.0],
         [http://lxde.org/])
-AC_CONFIG_SRCDIR(menu-cache-gen/gmenu-tree.h)
 
 AM_INIT_AUTOMAKE([no-dist-gzip dist-xz])
 AM_CONFIG_HEADER(config.h)
@@ -25,6 +24,10 @@ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16.0 gio-2.0 >= 2.15.2)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
+PKG_CHECK_MODULES(LIBFM_EXTRA, libfm-extra)
+AC_SUBST(LIBFM_EXTRA_CFLAGS)
+AC_SUBST(LIBFM_EXTRA_LIBS)
+
 AC_ARG_ENABLE(more_warnings,
        [AC_HELP_STRING([--enable-more-warnings],
                [Add more warnings @<:@default=no@:>@])],
@@ -38,7 +41,7 @@ fi
 AC_SUBST(ADDITIONAL_FLAGS)
 
 dnl --enable-debug=(yes|minimum|no)
-AC_ARG_ENABLE(debug, [  --enable-debug=[no/yes] turn on debugging [default=no]],,enable_debug=yes)
+AC_ARG_ENABLE(debug, [  --enable-debug=[no/yes]   turn on debugging [[default=no]]],,enable_debug=yes)
 if test "$enable_debug" = "yes"; then
   DEBUG_CFLAGS="-DG_ENABLE_DEBUG"
 else
index d4af9e7..72f9dd4 100644 (file)
@@ -212,6 +212,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
index fe71fae..0ee9356 100644 (file)
@@ -212,6 +212,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
index a8189f3..d197d2c 100644 (file)
@@ -159,6 +159,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
index 85e9427..2a4aa43 100644 (file)
@@ -24,7 +24,7 @@ libmenu_cache_la_LIBADD =             \
 libmenu_cache_la_LDFLAGS =                     \
        -no-undefined                           \
        -export-symbols-regex menu_cache        \
-       -version-info 3:3:0 \
+       -version-info 3:4:0 \
        $(NULL)
 
 lib_menu_cache_includedir = $(includedir)/menu-cache
index 2deb556..13e0196 100644 (file)
@@ -243,6 +243,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -357,7 +359,7 @@ libmenu_cache_la_LIBADD = \
 libmenu_cache_la_LDFLAGS = \
        -no-undefined                           \
        -export-symbols-regex menu_cache        \
-       -version-info 3:3:0 \
+       -version-info 3:4:0 \
        $(NULL)
 
 lib_menu_cache_includedir = $(includedir)/menu-cache
index a95fe40..bc56b24 100644 (file)
@@ -124,6 +124,7 @@ struct _MenuCache
     GSList* notifiers;
     GThread* thr;
     GCancellable* cancellable;
+    guint version;
     gboolean ready : 1; /* used for sync access */
 };
 
@@ -631,6 +632,7 @@ gboolean menu_cache_reload( MenuCache* cache )
     GDataInputStream* f;
     MenuCacheFileDir** all_used_files;
     int i, n;
+    int ver_maj, ver_min;
 
     file = g_file_new_for_path(cache->cache_file);
     if(!file)
@@ -648,12 +650,12 @@ gboolean menu_cache_reload( MenuCache* cache )
     line = g_data_input_stream_read_line(f, &len, cache->cancellable, NULL);
     if(G_LIKELY(line))
     {
-        int ver_maj, ver_min;
         len = sscanf(line, "%d.%d", &ver_maj, &ver_min);
         g_free(line);
         if(len < 2)
             goto _fail;
-        if( ver_maj != VER_MAJOR || ver_min != VER_MINOR )
+        if( ver_maj != VER_MAJOR ||
+            ver_min > VER_MINOR || ver_min < VER_MINOR_SUPPORTED )
             goto _fail;
     }
     else
@@ -695,6 +697,7 @@ _fail:
         g_object_unref(f);
         return FALSE;
     }
+    cache->version = ver_min;
 
     if(cache->root_dir)
         menu_cache_item_unref( MENU_CACHE_ITEM(cache->root_dir) );
@@ -1349,7 +1352,7 @@ static void get_socket_name( char* buf, int len )
 
 #define MAX_RETRIES 25
 
-static gboolean fork_server()
+static gboolean fork_server(const char *path)
 {
     int ret, pid, status;
 
@@ -1362,8 +1365,9 @@ static gboolean fork_server()
     pid = fork();
     if (pid == 0)
     {
-        execl( MENUCACHE_LIBEXECDIR "/menu-cached", MENUCACHE_LIBEXECDIR "/menu-cached", NULL);
-        g_print("failed to exec %s\n", MENUCACHE_LIBEXECDIR "/menu-cached");
+        execl(MENUCACHE_LIBEXECDIR "/menu-cached", MENUCACHE_LIBEXECDIR "/menu-cached",
+              path, NULL);
+        g_print("failed to exec %s %s\n", MENUCACHE_LIBEXECDIR "/menu-cached", path);
     }
 
     /*
@@ -1378,24 +1382,27 @@ retry_wait:
     return TRUE;
 }
 
-static gpointer server_io_thread(gpointer _unused)
+/* this thread is started by connect_server() */
+static gpointer server_io_thread(gpointer data)
 {
     char buf[1024]; /* protocol has a lot shorter strings */
     ssize_t sz;
     size_t ptr = 0;
-    int fd;
+    int fd = GPOINTER_TO_INT(data);
     GHashTableIter it;
     char* menu_name;
     MenuCache* cache;
 
-    G_LOCK(connect);
-    fd = server_fd;
-    G_UNLOCK(connect);
     while(fd >= 0)
     {
         sz = read(fd, &buf[ptr], sizeof(buf) - ptr);
         if(sz <= 0) /* socket error or EOF */
         {
+            MENU_CACHE_LOCK;
+            ptr = hash ? g_hash_table_size(hash) : 0;
+            MENU_CACHE_UNLOCK;
+            if (ptr == 0) /* don't need it anymore */
+                break;
             G_LOCK(connect);
             if(fd != server_fd) /* someone replaced us?! go out immediately! */
             {
@@ -1405,9 +1412,10 @@ static gpointer server_io_thread(gpointer _unused)
             server_fd = -1;
             G_UNLOCK(connect);
             DEBUG("connect failed, trying reconnect");
+            sleep(1);
             if( ! connect_server(NULL) )
             {
-                g_print("fail to re-connect to the server.\n");
+                g_critical("fail to re-connect to the server.");
                 MENU_CACHE_LOCK;
                 if(hash)
                 {
@@ -1418,7 +1426,7 @@ static gpointer server_io_thread(gpointer _unused)
                 MENU_CACHE_UNLOCK;
                 break;
             }
-            DEBUG("successfully restart server.\nre-register menus.");
+            DEBUG("successfully reconnected server, re-register menus.");
             /* re-register all menu caches */
             MENU_CACHE_LOCK;
             if(hash)
@@ -1429,7 +1437,7 @@ static gpointer server_io_thread(gpointer _unused)
                     /* FIXME: need we remove it from hash if failed? */
             }
             MENU_CACHE_UNLOCK;
-            continue;
+            break; /* next thread will do it */
         }
         while(sz > 0)
         {
@@ -1480,6 +1488,11 @@ static gpointer server_io_thread(gpointer _unused)
             ptr = 0;
         }
     }
+    G_LOCK(connect);
+    if (fd == server_fd)
+        server_fd = -1;
+    G_UNLOCK(connect);
+    close(fd);
     /* DEBUG("server io thread terminated"); */
 #if GLIB_CHECK_VERSION(2, 32, 0)
     g_thread_unref(g_thread_self());
@@ -1525,7 +1538,7 @@ retry:
         if((rc == ECONNREFUSED || rc == ENOENT) && retries == 0)
         {
             DEBUG("no running server found, starting it");
-            fork_server();
+            fork_server(addr.sun_path);
             ++retries;
             goto retry;
         }
@@ -1542,13 +1555,17 @@ retry:
     server_fd = fd;
     G_UNLOCK(connect);
 #if GLIB_CHECK_VERSION(2, 32, 0)
-    g_thread_new("menu-cache-io", server_io_thread, NULL);
+    g_thread_new("menu-cache-io", server_io_thread, GINT_TO_POINTER(fd));
 #else
-    g_thread_create(server_io_thread, NULL, FALSE, NULL);
+    g_thread_create(server_io_thread, GINT_TO_POINTER(fd), FALSE, NULL);
 #endif
     return TRUE;
 }
 
+#define CACHE_VERSION __num2str(VER_MAJOR) "." __num2str(VER_MINOR)
+#define __num2str(s) __def2str(s)
+#define __def2str(s) #s
+
 static MenuCache* menu_cache_create(const char* menu_name)
 {
     MenuCache* cache;
@@ -1564,6 +1581,7 @@ static MenuCache* menu_cache_create(const char* menu_name)
     char* file_name;
     int len = 0;
     GChecksum *sum;
+    char *langs_list;
 
     if( !xdg_cfg )
         xdg_cfg = "";
@@ -1578,13 +1596,13 @@ static MenuCache* menu_cache_create(const char* menu_name)
     if( ! xdg_cache_home )
         xdg_cache_home = "";
 
-    /* get rid of the encoding part of locale name. */
-    while( strchr(langs[0], '.') )
-        ++langs;
+    /* reconstruct languages list in form as it should be in $LANGUAGES */
+    langs_list = g_strjoinv(":", (char **)langs);
 
-    buf = g_strdup_printf( "REG:%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t00000000000000000000000000000000\n",
+    buf = g_strdup_printf( "REG:%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t" CACHE_VERSION
+                           "\t00000000000000000000000000000000\n",
                             menu_name,
-                            *langs,
+                            langs_list,
                             xdg_cache_home,
                             xdg_cfg,
                             xdg_prefix,
@@ -1605,6 +1623,7 @@ static MenuCache* menu_cache_create(const char* menu_name)
     memcpy( cache->md5, md5, 32 );
     cache->menu_name = g_strdup(menu_name);
     g_free( file_name );
+    g_free(langs_list);
 
     g_checksum_free(sum); /* md5 is also freed here */
 
@@ -1650,9 +1669,8 @@ static gpointer menu_cache_loader_thread(gpointer data)
         return NULL;
     }
     /* and request update from server */
-    if(!cache->cancellable || !g_cancellable_is_cancelled(cache->cancellable))
-        register_menu_to_server(cache);
-    else
+    if ((cache->cancellable && g_cancellable_is_cancelled(cache->cancellable)) ||
+        !register_menu_to_server(cache))
         SET_CACHE_READY(cache);
     return NULL;
 }
@@ -1899,3 +1917,6 @@ MenuCacheItem *menu_cache_find_item_by_id(MenuCache *cache, const char *id)
     MENU_CACHE_UNLOCK;
     return item;
 }
+
+/* TODO:
+const char **menu_cache_app_get_categories(MenuCacheApp *app) */
\ No newline at end of file
index 20bbbab..f87c46f 100644 (file)
@@ -5,5 +5,7 @@
    note that will break compatibility for generated cache */
 #define VER_MAJOR      1
 #define VER_MINOR      1
+/* This is for backward compatibility on transitions */
+#define VER_MINOR_SUPPORTED 1
 
 #endif
index f9a8f4e..91de34d 100644 (file)
@@ -209,6 +209,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
index b8c7ca3..27623d1 100644 (file)
@@ -73,10 +73,12 @@ typedef struct _Cache
     char *cache_file;
 }Cache;
 
-GMainLoop* main_loop = NULL;
+static GMainLoop* main_loop = NULL;
 
 static GHashTable* hash = NULL;
 
+static char *socket_file = NULL;
+
 static void on_file_changed( GFileMonitor* mon, GFile* gf, GFile* other,
                              GFileMonitorEvent evt, Cache* cache );
 
@@ -181,7 +183,7 @@ static gboolean read_all_used_files( FILE* f, int* n_files, char*** used_files )
 
 static void set_env( char* penv, const char* name )
 {
-    if( *penv )
+    if( penv && *penv )
         g_setenv( name, penv, TRUE);
     else
         g_unsetenv( name );
@@ -196,6 +198,7 @@ static void pre_exec( gpointer user_data )
     set_env(*++env, "XDG_DATA_DIRS");
     set_env(*++env, "XDG_CONFIG_HOME");
     set_env(*++env, "XDG_DATA_HOME");
+    set_env(*++env, "CACHE_GEN_VERSION");
 }
 
 static gboolean regenerate_cache( const char* menu_name,
@@ -440,10 +443,9 @@ static void get_socket_name( char* buf, int len )
     g_free(dpy);
 }
 
-static int create_socket()
+static int create_socket(struct sockaddr_un *addr)
 {
     int fd = -1;
-    struct sockaddr_un addr;
 
     fd = socket(PF_UNIX, SOCK_STREAM, 0);
     if (fd < 0)
@@ -451,20 +453,17 @@ static int create_socket()
         DEBUG("Failed to create socket");
         return -1;
     }
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_UNIX;
-    get_socket_name( addr.sun_path, sizeof( addr.sun_path ) );
 
     /* remove previous socket file */
-    if (unlink(addr.sun_path) < 0) {
+    if (unlink(addr->sun_path) < 0) {
         if (errno != ENOENT)
-            g_error("Couldn't remove previous socket file %s", addr.sun_path);
+            g_error("Couldn't remove previous socket file %s", addr->sun_path);
     }
     /* remove of previous socket file successful */
     else
-        g_warning("removed previous socket file %s", addr.sun_path);
+        g_warning("removed previous socket file %s", addr->sun_path);
 
-    if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+    if(bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0)
     {
         DEBUG("Failed to bind to socket");
         close(fd);
@@ -539,10 +538,10 @@ retry:
 
     if( memcmp(line, "REG:", 4) == 0 )
     {
-        int n_files, i;
+        int n_files = 0, i;
         char *pline = line + 4;
         char *sep, *menu_name, *lang_name, *cache_dir;
-        char **files;
+        char **files = NULL;
         char **env;
         char reload_cmd[38] = "REL:";
 
@@ -555,7 +554,9 @@ retry:
          * XDG_MENU_PREFIX
          * XDG_DATA_DIRS
          * XDG_CONFIG_HOME
-         * XDG_DATA_HOME */
+         * XDG_DATA_HOME
+         * (optional) CACHE_GEN_VERSION
+         * md5 hash */
 
         md5 = pline + len - 32; /* the md5 hash of this menu */
 
@@ -572,6 +573,7 @@ retry:
             lang_name = pline;
             pline = sep + 1;
 
+            ((char *)md5)[-1] = '\0';
             env = g_strsplit(pline, "\t", 0);
 
             cache_dir = env[0]; /* XDG_CACHE_HOME */
@@ -695,9 +697,7 @@ static gboolean on_new_conn_incoming(GIOChannel* ch, GIOCondition cond, gpointer
 static void terminate(int sig)
 {
 /* #ifndef HAVE_ABSTRACT_SOCKETS */
-    char path[1024];
-    get_socket_name(path, sizeof(path));
-    unlink(path);
+    unlink(socket_file);
     exit(0);
 /* #endif */
 }
@@ -710,11 +710,11 @@ static gboolean on_server_conn_close(GIOChannel* ch, GIOCondition cond, gpointer
     return TRUE;
 }
 
-
 int main(int argc, char** argv)
 {
     GIOChannel* ch;
     int server_fd;
+    struct sockaddr_un addr;
 #ifndef DISABLE_DAEMONIZE
     int fd, pid;
 
@@ -722,7 +722,21 @@ int main(int argc, char** argv)
     long i;
 #endif
 
-    server_fd = create_socket();
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    if (argc < 2)
+        /* old way, not recommended! */
+        get_socket_name( addr.sun_path, sizeof( addr.sun_path ) );
+    else if (strlen(argv[1]) >= sizeof(addr.sun_path))
+        /* ouch, it's too big! */
+        return 1;
+    else
+        /* this is a good way! */
+        strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);
+
+    socket_file = addr.sun_path;
+
+    server_fd = create_socket(&addr);
 
     if( server_fd < 0 )
         return 1;
@@ -794,11 +808,7 @@ int main(int argc, char** argv)
     g_main_loop_run( main_loop );
     g_main_loop_unref( main_loop );
 
-    {
-        char path[256];
-        get_socket_name(path, 256 );
-        unlink(path);
-    }
+    unlink(addr.sun_path);
 
     g_hash_table_destroy(hash);
     return 0;
index f794e5c..7c3c86a 100644 (file)
@@ -1,43 +1,27 @@
 NULL =
 
-INCLUDES =                             \
-       -I$(top_srcdir)/libmenu-cache   \
-       -DGMENU_I_KNOW_THIS_IS_UNSTABLE \
-       $(GLIB_CFLAGS)                  \
-       $(MONITOR_CFLAGS)               \
-       $(DEBUG_CFLAGS)                 \
-       $(WARN_CFLAGS)                  \
-    $(ADDITIONAL_FLAGS) \
+INCLUDES = \
+       -I$(top_srcdir)/libmenu-cache \
+       $(GLIB_CFLAGS) \
+       $(LIBFM_EXTRA_CFLAGS) \
+       $(DEBUG_CFLAGS) \
+       $(ADDITIONAL_FLAGS) \
        -Werror-implicit-function-declaration \
        $(NULL)
 
 pkglibexec_PROGRAMS = menu-cache-gen
 
-menu_cache_gen_SOURCES =               \
-       menu-cache-gen.c                \
-       menu-cache-gen.h                \
-       canonicalize.c                  \
-       canonicalize.h                  \
-       desktop-entries.c               \
-       desktop-entries.h               \
-       entry-directories.c             \
-       entry-directories.h             \
-       gmenu-tree.c                    \
-       gmenu-tree.h                    \
-       menu-layout.c                   \
-       menu-layout.h                   \
-       menu-monitor.c                  \
-       menu-monitor.h                  \
-       menu-util.c                     \
-       menu-util.h                     \
+menu_cache_gen_SOURCES = \
+       main.c \
+       menu-merge.c \
+       menu-compose.c \
        $(NULL)
 
-menu_cache_gen_LDADD =                 \
-       $(GLIB_LIBS)                                    \
-       $(MONITOR_LIBS)                                 \
+menu_cache_gen_LDADD = \
+       $(GLIB_LIBS) \
+       $(LIBFM_EXTRA_LIBS) \
        $(NULL)
-menu_cache_gen_LDFLAGS =                       \
-       -no-undefined                           \
-       $(NULL)
-EXTRA_DIST =                           \
+
+EXTRA_DIST = \
+       menu-tags.h \
        $(NULL)
index c24b07e..256a693 100644 (file)
@@ -93,23 +93,16 @@ CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(pkglibexecdir)"
 PROGRAMS = $(pkglibexec_PROGRAMS)
 am__objects_1 =
-am_menu_cache_gen_OBJECTS = menu-cache-gen.$(OBJEXT) \
-       canonicalize.$(OBJEXT) desktop-entries.$(OBJEXT) \
-       entry-directories.$(OBJEXT) gmenu-tree.$(OBJEXT) \
-       menu-layout.$(OBJEXT) menu-monitor.$(OBJEXT) \
-       menu-util.$(OBJEXT) $(am__objects_1)
+am_menu_cache_gen_OBJECTS = main.$(OBJEXT) menu-merge.$(OBJEXT) \
+       menu-compose.$(OBJEXT) $(am__objects_1)
 menu_cache_gen_OBJECTS = $(am_menu_cache_gen_OBJECTS)
 am__DEPENDENCIES_1 =
 menu_cache_gen_DEPENDENCIES = $(am__DEPENDENCIES_1) \
-       $(am__DEPENDENCIES_1)
+       $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
 AM_V_lt = $(am__v_lt_@AM_V@)
 am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
 am__v_lt_0 = --silent
 am__v_lt_1 = 
-menu_cache_gen_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
-       $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
-       $(AM_CFLAGS) $(CFLAGS) $(menu_cache_gen_LDFLAGS) $(LDFLAGS) -o \
-       $@
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -215,6 +208,8 @@ INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
 LD = @LD@
 LDFLAGS = @LDFLAGS@
+LIBFM_EXTRA_CFLAGS = @LIBFM_EXTRA_CFLAGS@
+LIBFM_EXTRA_LIBS = @LIBFM_EXTRA_LIBS@
 LIBOBJS = @LIBOBJS@
 LIBS = @LIBS@
 LIBTOOL = @LIBTOOL@
@@ -306,45 +301,27 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 NULL = 
 INCLUDES = \
-       -I$(top_srcdir)/libmenu-cache   \
-       -DGMENU_I_KNOW_THIS_IS_UNSTABLE \
-       $(GLIB_CFLAGS)                  \
-       $(MONITOR_CFLAGS)               \
-       $(DEBUG_CFLAGS)                 \
-       $(WARN_CFLAGS)                  \
-    $(ADDITIONAL_FLAGS) \
+       -I$(top_srcdir)/libmenu-cache \
+       $(GLIB_CFLAGS) \
+       $(LIBFM_EXTRA_CFLAGS) \
+       $(DEBUG_CFLAGS) \
+       $(ADDITIONAL_FLAGS) \
        -Werror-implicit-function-declaration \
        $(NULL)
 
 menu_cache_gen_SOURCES = \
-       menu-cache-gen.c                \
-       menu-cache-gen.h                \
-       canonicalize.c                  \
-       canonicalize.h                  \
-       desktop-entries.c               \
-       desktop-entries.h               \
-       entry-directories.c             \
-       entry-directories.h             \
-       gmenu-tree.c                    \
-       gmenu-tree.h                    \
-       menu-layout.c                   \
-       menu-layout.h                   \
-       menu-monitor.c                  \
-       menu-monitor.h                  \
-       menu-util.c                     \
-       menu-util.h                     \
+       main.c \
+       menu-merge.c \
+       menu-compose.c \
        $(NULL)
 
 menu_cache_gen_LDADD = \
-       $(GLIB_LIBS)                                    \
-       $(MONITOR_LIBS)                                 \
-       $(NULL)
-
-menu_cache_gen_LDFLAGS = \
-       -no-undefined                           \
+       $(GLIB_LIBS) \
+       $(LIBFM_EXTRA_LIBS) \
        $(NULL)
 
 EXTRA_DIST = \
+       menu-tags.h \
        $(NULL)
 
 all: all-am
@@ -433,7 +410,7 @@ clean-pkglibexecPROGRAMS:
 
 menu-cache-gen$(EXEEXT): $(menu_cache_gen_OBJECTS) $(menu_cache_gen_DEPENDENCIES) $(EXTRA_menu_cache_gen_DEPENDENCIES) 
        @rm -f menu-cache-gen$(EXEEXT)
-       $(AM_V_CCLD)$(menu_cache_gen_LINK) $(menu_cache_gen_OBJECTS) $(menu_cache_gen_LDADD) $(LIBS)
+       $(AM_V_CCLD)$(LINK) $(menu_cache_gen_OBJECTS) $(menu_cache_gen_LDADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
@@ -441,14 +418,9 @@ mostlyclean-compile:
 distclean-compile:
        -rm -f *.tab.c
 
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/canonicalize.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/desktop-entries.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/entry-directories.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gmenu-tree.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-cache-gen.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-layout.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-monitor.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-compose.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/menu-merge.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@   $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/menu-cache-gen/canonicalize.c b/menu-cache-gen/canonicalize.c
deleted file mode 100644 (file)
index 257f3bb..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/* Return the canonical absolute name of a given file.
-   Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib)
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-#include <config.h>
-
-#include "canonicalize.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <stddef.h>
-
-/* Return the canonical absolute name of file NAME.  A canonical name
-   does not contain any `.', `..' components nor any repeated path
-   separators ('/') or symlinks.  All path components must exist.  If
-   RESOLVED is null, the result is malloc'd; otherwise, if the
-   canonical name is PATH_MAX chars or more, returns null with `errno'
-   set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
-   returns the name in RESOLVED.  If the name cannot be resolved and
-   RESOLVED is non-NULL, it contains the path of the first component
-   that cannot be resolved.  If the path can be resolved, RESOLVED
-   holds the same value as the value returned.  */
-
-static char*
-menu_realpath (const char *name, char *resolved)
-{
-  char *rpath, *dest, *extra_buf = NULL;
-  const char *start, *end, *rpath_limit;
-  long int path_max;
-  int num_links = 0;
-
-  if (name == NULL)
-    {
-      /* As per Single Unix Specification V2 we must return an error if
-         either parameter is a null pointer.  We extend this to allow
-         the RESOLVED parameter to be NULL in case the we are expected to
-         allocate the room for the return value.  */
-      errno = EINVAL;
-      return NULL;
-    }
-
-  if (name[0] == '\0')
-    {
-      /* As per Single Unix Specification V2 we must return an error if
-         the name argument points to an empty string.  */
-      errno = ENOENT;
-      return NULL;
-    }
-
-#ifdef PATH_MAX
-  path_max = PATH_MAX;
-#else
-  path_max = pathconf (name, _PC_PATH_MAX);
-  if (path_max <= 0)
-    path_max = 1024;
-#endif
-
-  rpath = resolved ? g_alloca (path_max) : g_malloc (path_max);
-  rpath_limit = rpath + path_max;
-
-  if (name[0] != G_DIR_SEPARATOR)
-    {
-      if (!getcwd (rpath, path_max))
-        {
-          rpath[0] = '\0';
-          goto error;
-        }
-      dest = strchr (rpath, '\0');
-    }
-  else
-    {
-      rpath[0] = G_DIR_SEPARATOR;
-      dest = rpath + 1;
-    }
-
-  for (start = end = name; *start; start = end)
-    {
-      struct stat st;
-      int n;
-
-      /* Skip sequence of multiple path-separators.  */
-      while (*start == G_DIR_SEPARATOR)
-        ++start;
-
-      /* Find end of path component.  */
-      for (end = start; *end && *end != G_DIR_SEPARATOR; ++end)
-        /* Nothing.  */;
-
-      if (end - start == 0)
-        break;
-      else if (end - start == 1 && start[0] == '.')
-        /* nothing */;
-      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
-        {
-          /* Back up to previous component, ignore if at root already.  */
-          if (dest > rpath + 1)
-            while ((--dest)[-1] != G_DIR_SEPARATOR);
-        }
-      else
-        {
-          size_t new_size;
-
-          if (dest[-1] != G_DIR_SEPARATOR)
-            *dest++ = G_DIR_SEPARATOR;
-
-          if (dest + (end - start) >= rpath_limit)
-            {
-              ptrdiff_t dest_offset = dest - rpath;
-              char *new_rpath;
-
-              if (resolved)
-                {
-#ifdef ENAMETOOLONG
-                  errno = ENAMETOOLONG;
-#else
-                  /* Uh... just pick something */
-                  errno = EINVAL;
-#endif
-                  if (dest > rpath + 1)
-                    dest--;
-                  *dest = '\0';
-                  goto error;
-                }
-              new_size = rpath_limit - rpath;
-              if (end - start + 1 > path_max)
-                new_size += end - start + 1;
-              else
-                new_size += path_max;
-              new_rpath = (char *) realloc (rpath, new_size);
-              if (new_rpath == NULL)
-                goto error;
-              rpath = new_rpath;
-              rpath_limit = rpath + new_size;
-
-              dest = rpath + dest_offset;
-            }
-
-          memcpy (dest, start, end - start);
-          dest = dest + (end - start);
-          *dest = '\0';
-
-          if (stat (rpath, &st) < 0)
-            goto error;
-
-          if (S_ISLNK (st.st_mode))
-            {
-              char *buf = alloca (path_max);
-              size_t len;
-
-              /* Get the maximum number of symlinks to follow. */
-              int symlinksmax = sysconf(_SC_SYMLOOP_MAX);
-#ifdef MAXSYMLINKS
-              if (symlinksmax == -1)
-                symlinksmax = MAXSYMLINKS;
-#endif
-
-              if (++num_links > symlinksmax)
-                {
-                  errno = ELOOP;
-                  goto error;
-                }
-
-              n = readlink (rpath, buf, path_max);
-              if (n < 0)
-                goto error;
-              buf[n] = '\0';
-
-              if (!extra_buf)
-                extra_buf = g_alloca (path_max);
-
-              len = strlen (end);
-              if ((long int) (n + len) >= path_max)
-                {
-#ifdef ENAMETOOLONG
-                  errno = ENAMETOOLONG;
-#else
-                  /* Uh... just pick something */
-                  errno = EINVAL;
-#endif
-                  goto error;
-                }
-
-              /* Careful here, end may be a pointer into extra_buf... */
-              g_memmove (&extra_buf[n], end, len + 1);
-              name = end = memcpy (extra_buf, buf, n);
-
-              if (buf[0] == G_DIR_SEPARATOR)
-                dest = rpath + 1;       /* It's an absolute symlink */
-              else
-                /* Back up to previous component, ignore if at root already: */
-                if (dest > rpath + 1)
-                  while ((--dest)[-1] != G_DIR_SEPARATOR);
-            }
-        }
-    }
-  if (dest > rpath + 1 && dest[-1] == G_DIR_SEPARATOR)
-    --dest;
-  *dest = '\0';
-
-  return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath;
-
-error:
-  if (resolved)
-    strcpy (resolved, rpath);
-  else
-    g_free (rpath);
-  return NULL;
-}
-
-char *
-menu_canonicalize_file_name (const char *name,
-                             gboolean    allow_missing_basename)
-{
-  char *retval;
-
-  retval = menu_realpath (name, NULL);
-
-  /* We could avoid some system calls by using the second
-   * argument to realpath() instead of doing realpath
-   * all over again, but who cares really. we'll see if
-   * it's ever in a profile.
-   */
-  if (allow_missing_basename && retval == NULL)
-    {
-      char *dirname;
-      char *canonical_dirname;
-      dirname = g_path_get_dirname (name);
-      canonical_dirname = menu_realpath (dirname, NULL);
-      g_free (dirname);
-      if (canonical_dirname)
-        {
-          char *basename;
-          basename = g_path_get_basename (name);
-          retval = g_build_filename (canonical_dirname, basename, NULL);
-          g_free (basename);
-          g_free (canonical_dirname);
-        }
-    }
-
-  return retval;
-}
diff --git a/menu-cache-gen/canonicalize.h b/menu-cache-gen/canonicalize.h
deleted file mode 100644 (file)
index e3ef502..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Return the canonical absolute name of a given file.
-   Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc.
-   This file is part of the GNU C Library.
-
-   Copyright (C) 2002 Red Hat, Inc. (trivial port to GLib)
-
-   The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   The GNU C Library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
-
-#ifndef G_CANONICALIZE_H
-#define G_CANONICALIZE_H
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-char* menu_canonicalize_file_name (const char *name,
-                                   gboolean    allow_missing_basename);
-
-G_END_DECLS
-
-#endif /* G_CANONICALIZE_H */
diff --git a/menu-cache-gen/desktop-entries.c b/menu-cache-gen/desktop-entries.c
deleted file mode 100644 (file)
index a9fda7f..0000000
+++ /dev/null
@@ -1,922 +0,0 @@
-/*
- * Copyright (C) 2002 - 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <config.h>
-
-#include "desktop-entries.h"
-
-#include <string.h>
-
-#include "menu-util.h"
-#include "menu-cache-gen.h"
-
-#define DESKTOP_ENTRY_GROUP     "Desktop Entry"
-#define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry"
-
-enum
-{
-  DESKTOP_ENTRY_NO_DISPLAY     = 1 << 0,
-  DESKTOP_ENTRY_HIDDEN         = 1 << 1,
-  DESKTOP_ENTRY_SHOW_IN_GNOME  = 1 << 2,
-  DESKTOP_ENTRY_TRYEXEC_FAILED = 1 << 3
-};
-
-struct DesktopEntry
-{
-  char *path;
-  char *basename;
-
-  GQuark *categories;
-
-  char     *name;
-  char     *generic_name;
-  char     *full_name;
-  char     *comment;
-  char     *icon;
-  char     *exec;
-  gboolean terminal;
-  gboolean startup_notify : 1;
-
-  guint32 show_in_flags;
-
-  guint type : 2;
-  guint flags : 4;
-  guint refcount : 24;
-};
-
-struct DesktopEntrySet
-{
-  int         refcount;
-  GHashTable *hash;
-};
-
-/*
- * Desktop entries
- */
-
-static guint
-get_flags_from_key_file (DesktopEntry *entry,
-                         GKeyFile     *key_file,
-                         const char   *desktop_entry_group)
-{
-  GError    *error;
-  char     **strv;
-  gboolean   no_display;
-  gboolean   hidden;
-  gboolean   show_in_gnome;
-  gboolean   tryexec_failed;
-  char      *tryexec;
-  guint      flags;
-  int        i;
-
-  error = NULL;
-  no_display = g_key_file_get_boolean (key_file,
-                                       desktop_entry_group,
-                                       "NoDisplay",
-                                       &error);
-  if (error)
-    {
-      no_display = FALSE;
-      g_error_free (error);
-    }
-
-  error = NULL;
-  hidden = g_key_file_get_boolean (key_file,
-                                   desktop_entry_group,
-                                   "Hidden",
-                                   &error);
-  if (error)
-    {
-      hidden = FALSE;
-      g_error_free (error);
-    }
-
-  show_in_gnome = TRUE;
-  strv = g_key_file_get_string_list (key_file,
-                                     desktop_entry_group,
-                                     "OnlyShowIn",
-                                     NULL,
-                                     NULL);
-  if (strv)
-    {
-/*      show_in_gnome = FALSE; */
-      for (i = 0; strv[i]; i++)
-        {
-          guint32 de_flag = menu_cache_get_de_flag(strv[i]);
-          entry->show_in_flags |= de_flag; /* add the DE */
-/*
-          if (!strcmp (strv[i], "GNOME"))
-            {
-              show_in_gnome = TRUE;
-              break;
-            }
-*/
-        }
-    }
-  else
-    {
-      strv = g_key_file_get_string_list (key_file,
-                                         desktop_entry_group,
-                                         "NotShowIn",
-                                         NULL,
-                                         NULL);
-      if (strv)
-        {
-          /* show in all DEs by default */
-          entry->show_in_flags = (guint32)-1;
-/*          show_in_gnome = TRUE; */
-          for (i = 0; strv[i]; i++)
-            {
-              guint32 de_flag = menu_cache_get_de_flag(strv[i]);
-              entry->show_in_flags &= (~de_flag); /* remove the DE */
-/*
-              if (!strcmp (strv[i], "GNOME"))
-                {
-                  show_in_gnome = FALSE;
-                }
-*/
-            }
-        }
-    }
-  g_strfreev (strv);
-
-  tryexec_failed = FALSE;
-  tryexec = g_key_file_get_string (key_file,
-                                   desktop_entry_group,
-                                   "TryExec",
-                                   NULL);
-  if (tryexec)
-    {
-      char *path;
-
-      path = g_find_program_in_path (g_strstrip (tryexec));
-
-      tryexec_failed = (path == NULL);
-
-      g_free (path);
-      g_free (tryexec);
-    }
-
-  flags = 0;
-  if (no_display)
-    flags |= DESKTOP_ENTRY_NO_DISPLAY;
-  if (hidden)
-    flags |= DESKTOP_ENTRY_HIDDEN;
-  if (show_in_gnome)
-    flags |= DESKTOP_ENTRY_SHOW_IN_GNOME;
-  if (tryexec_failed)
-    flags |= DESKTOP_ENTRY_TRYEXEC_FAILED;
-
-  return flags;
-}
-
-static GQuark *
-get_categories_from_key_file (DesktopEntry *entry,
-                              GKeyFile     *key_file,
-                              const char   *desktop_entry_group)
-{
-  GQuark  *retval;
-  char   **strv;
-  gsize    len;
-  int      i;
-
-  strv = g_key_file_get_string_list (key_file,
-                                     desktop_entry_group,
-                                     "Categories",
-                                     &len,
-                                     NULL);
-  if (!strv)
-    return NULL;
-
-  retval = g_new0 (GQuark, len + 1);
-
-  for (i = 0; strv[i]; i++)
-    retval[i] = g_quark_from_string (strv[i]);
-
-  g_strfreev (strv);
-
-  return retval;
-}
-
-static DesktopEntry *
-desktop_entry_load (DesktopEntry *entry)
-{
-  DesktopEntry *retval = NULL;
-  GKeyFile     *key_file;
-  GError       *error;
-  const char   *desktop_entry_group;
-  char         *name_str;
-  char         *type_str;
-
-  key_file = g_key_file_new ();
-
-  error = NULL;
-  if (!g_key_file_load_from_file (key_file, entry->path, 0, &error))
-    {
-      menu_verbose ("Failed to load \"%s\": %s\n",
-                    entry->path, error->message);
-      g_error_free (error);
-      goto out;
-    }
-
-  if (g_key_file_has_group (key_file, DESKTOP_ENTRY_GROUP))
-    {
-      desktop_entry_group = DESKTOP_ENTRY_GROUP;
-    }
-  else
-    {
-      menu_verbose ("\"%s\" contains no \"" DESKTOP_ENTRY_GROUP "\" group\n",
-                    entry->path);
-
-      if (g_key_file_has_group (key_file, KDE_DESKTOP_ENTRY_GROUP))
-        {
-          desktop_entry_group = KDE_DESKTOP_ENTRY_GROUP;
-          menu_verbose ("\"%s\" contains deprecated \"" KDE_DESKTOP_ENTRY_GROUP "\" group\n",
-                        entry->path);
-        }
-      else
-        {
-          goto out;
-        }
-    }
-
-  if (!g_key_file_has_key (key_file, desktop_entry_group, "Name", NULL))
-    {
-      menu_verbose ("\"%s\" contains no \"Name\" key\n", entry->path);
-      goto out;
-    }
-
-  name_str = g_key_file_get_locale_string (key_file, desktop_entry_group, "Name", NULL, NULL);
-  if (!name_str)
-    {
-      menu_verbose ("\"%s\" contains an invalid \"Name\" key\n", entry->path);
-      goto out;
-    }
-
-  g_free (name_str);
-
-  type_str = g_key_file_get_string (key_file, desktop_entry_group, "Type", NULL);
-  if (!type_str)
-    {
-      menu_verbose ("\"%s\" contains no \"Type\" key\n", entry->path);
-      goto out;
-    }
-
-  if ((entry->type == DESKTOP_ENTRY_DESKTOP && strcmp (type_str, "Application") != 0) ||
-      (entry->type == DESKTOP_ENTRY_DIRECTORY && strcmp (type_str, "Directory") != 0))
-    {
-      menu_verbose ("\"%s\" does not contain the correct \"Type\" value\n", entry->path);
-      g_free (type_str);
-      goto out;
-    }
-
-  g_free (type_str);
-
-  if (entry->type == DESKTOP_ENTRY_DESKTOP &&
-      !g_key_file_has_key (key_file, desktop_entry_group, "Exec", NULL))
-    {
-      menu_verbose ("\"%s\" does not contain an \"Exec\" key\n", entry->path);
-      goto out;
-    }
-
-  retval = entry;
-
-#define GET_LOCALE_STRING(n) g_key_file_get_locale_string (key_file, desktop_entry_group, (n), NULL, NULL)
-
-  retval->name         = GET_LOCALE_STRING ("Name");
-  retval->generic_name = GET_LOCALE_STRING ("GenericName");
-  retval->full_name    = GET_LOCALE_STRING ("X-GNOME-FullName");
-/*
-  GError *err;
-  err = NULL;
-  g_print("Display : %s\n", g_key_file_get_locale_string (key_file, desktop_entry_group, "X-GNOME-FullName", NULL, &err));
-  if (err) {
-    g_print("Error : %s\n", err->message);
-    g_error_free (err);
-  }
-*/
-  retval->comment      = GET_LOCALE_STRING ("Comment");
-  retval->icon         = GET_LOCALE_STRING ("Icon");
-  retval->flags        = get_flags_from_key_file (retval, key_file, desktop_entry_group);
-  retval->categories   = get_categories_from_key_file (retval, key_file, desktop_entry_group);
-
-  if (entry->type == DESKTOP_ENTRY_DESKTOP)
-    {
-      retval->exec = g_key_file_get_string (key_file, desktop_entry_group, "Exec", NULL);
-      retval->terminal = g_key_file_get_boolean (key_file, desktop_entry_group, "Terminal", NULL);
-         retval->startup_notify = g_key_file_get_boolean (key_file, desktop_entry_group, "StartupNotify", NULL);
-    }
-  
-#undef GET_LOCALE_STRING
-
-  menu_verbose ("Desktop entry \"%s\" (%s, %s, %s, %s, %s) flags: NoDisplay=%s, Hidden=%s, ShowInGNOME=%s, TryExecFailed=%s\n",
-                retval->basename,
-                retval->name,
-                retval->generic_name ? retval->generic_name : "(null)",
-                retval->full_name ? retval->full_name : "(null)",
-                retval->comment ? retval->comment : "(null)",
-                retval->icon ? retval->icon : "(null)",
-                retval->flags & DESKTOP_ENTRY_NO_DISPLAY     ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_HIDDEN         ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_SHOW_IN_GNOME  ? "(true)" : "(false)",
-                retval->flags & DESKTOP_ENTRY_TRYEXEC_FAILED ? "(true)" : "(false)");
-
- out:
-  g_key_file_free (key_file);
-
-  if (!retval)
-    desktop_entry_unref (entry);
-
-  return retval;
-}
-
-DesktopEntry *
-desktop_entry_new (const char *path)
-{
-  DesktopEntryType  type;
-  DesktopEntry     *retval;
-
-  menu_verbose ("Loading desktop entry \"%s\"\n", path);
-
-  if (g_str_has_suffix (path, ".desktop"))
-    {
-      type = DESKTOP_ENTRY_DESKTOP;
-    }
-  else if (g_str_has_suffix (path, ".directory"))
-    {
-      type = DESKTOP_ENTRY_DIRECTORY;
-    }
-  else
-    {
-      menu_verbose ("Unknown desktop entry suffix in \"%s\"\n",
-                    path);
-      return NULL;
-    }
-
-  retval = g_new0 (DesktopEntry, 1);
-
-  retval->refcount = 1;
-  retval->type     = type;
-  retval->basename = g_path_get_basename (path);
-  retval->path     = g_strdup (path);
-
-  return desktop_entry_load (retval);
-}
-
-DesktopEntry *
-desktop_entry_reload (DesktopEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path);
-
-  g_free (entry->categories);
-  entry->categories = NULL;
-
-  g_free (entry->name);
-  entry->name = NULL;
-
-  g_free (entry->generic_name);
-  entry->generic_name = NULL;
-
-  g_free (entry->full_name);
-  entry->full_name = NULL;
-
-  g_free (entry->comment);
-  entry->comment = NULL;
-
-  g_free (entry->icon);
-  entry->icon = NULL;
-
-  g_free (entry->exec);
-  entry->exec = NULL;
-
-  entry->terminal = 0;
-  entry->startup_notify = 0;
-  entry->flags = 0;
-
-  return desktop_entry_load (entry);
-}
-
-DesktopEntry *
-desktop_entry_ref (DesktopEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-  g_return_val_if_fail (entry->refcount > 0, NULL);
-
-  entry->refcount += 1;
-
-  return entry;
-}
-
-DesktopEntry *
-desktop_entry_copy (DesktopEntry *entry)
-{
-  DesktopEntry *retval;
-  int           i;
-
-  menu_verbose ("Copying desktop entry \"%s\"\n",
-                entry->basename);
-
-  retval = g_new0 (DesktopEntry, 1);
-
-  retval->refcount     = 1;
-  retval->type         = entry->type;
-  retval->basename     = g_strdup (entry->basename);
-  retval->path         = g_strdup (entry->path);
-  retval->name         = g_strdup (entry->name);
-  retval->generic_name = g_strdup (entry->generic_name);
-  retval->full_name    = g_strdup (entry->full_name);
-  retval->comment      = g_strdup (entry->comment);
-  retval->icon         = g_strdup (entry->icon);
-  retval->exec         = g_strdup (entry->exec);
-  retval->terminal     = entry->terminal;
-  retval->startup_notify = entry->startup_notify;
-  retval->flags    = entry->flags;
-
-  i = 0;
-  if (entry->categories != NULL)
-    {
-      for (; entry->categories[i]; i++);
-    }
-
-  retval->categories = g_new0 (GQuark, i + 1);
-
-  i = 0;
-  if (entry->categories != NULL)
-    {
-      for (; entry->categories[i]; i++)
-        retval->categories[i] = entry->categories[i];
-    }
-
-  return retval;
-}
-
-void
-desktop_entry_unref (DesktopEntry *entry)
-{
-  g_return_if_fail (entry != NULL);
-  g_return_if_fail (entry->refcount > 0);
-
-  entry->refcount -= 1;
-  if (entry->refcount == 0)
-    {
-      g_free (entry->categories);
-      entry->categories = NULL;
-
-      g_free (entry->name);
-      entry->name = NULL;
-
-      g_free (entry->generic_name);
-      entry->generic_name = NULL;
-
-      g_free (entry->full_name);
-      entry->full_name = NULL;
-
-      g_free (entry->comment);
-      entry->comment = NULL;
-
-      g_free (entry->icon);
-      entry->icon = NULL;
-
-      g_free (entry->exec);
-      entry->exec = NULL;
-
-      g_free (entry->basename);
-      entry->basename = NULL;
-
-      g_free (entry->path);
-      entry->path = NULL;
-
-      g_free (entry);
-    }
-}
-
-DesktopEntryType
-desktop_entry_get_type (DesktopEntry *entry)
-{
-  return entry->type;
-}
-
-const char *
-desktop_entry_get_path (DesktopEntry *entry)
-{
-  return entry->path;
-}
-
-const char *
-desktop_entry_get_basename (DesktopEntry *entry)
-{
-  return entry->basename;
-}
-
-const char *
-desktop_entry_get_name (DesktopEntry *entry)
-{
-  return entry->name;
-}
-
-const char *
-desktop_entry_get_generic_name (DesktopEntry *entry)
-{
-  return entry->generic_name;
-}
-
-const char *
-desktop_entry_get_full_name (DesktopEntry *entry)
-{
-  return entry->full_name;
-}
-
-const char *
-desktop_entry_get_comment (DesktopEntry *entry)
-{
-  return entry->comment;
-}
-
-const char *
-desktop_entry_get_icon (DesktopEntry *entry)
-{
-  return entry->icon;
-}
-
-const char *
-desktop_entry_get_exec (DesktopEntry *entry)
-{
-  return entry->exec;
-}
-
-gboolean
-desktop_entry_get_launch_in_terminal (DesktopEntry *entry)
-{
-  return entry->terminal;
-}
-
-gboolean
-desktop_entry_get_use_startup_notify (DesktopEntry *entry)
-{
-  return entry->startup_notify;
-}
-
-gboolean
-desktop_entry_get_hidden (DesktopEntry *entry)
-{
-  return (entry->flags & DESKTOP_ENTRY_HIDDEN) != 0;
-}
-
-gboolean
-desktop_entry_get_no_display (DesktopEntry *entry)
-{
-  return (entry->flags & DESKTOP_ENTRY_NO_DISPLAY) != 0;
-}
-
-gboolean
-desktop_entry_get_show_in_gnome (DesktopEntry *entry)
-{
-  return (entry->flags & DESKTOP_ENTRY_SHOW_IN_GNOME) != 0;
-}
-
-gboolean
-desktop_entry_get_show_in_flags (DesktopEntry *entry)
-{
-  return entry->show_in_flags;
-}
-
-gboolean
-desktop_entry_get_tryexec_failed (DesktopEntry *entry)
-{
-  return (entry->flags & DESKTOP_ENTRY_TRYEXEC_FAILED) != 0;
-}
-
-gboolean
-desktop_entry_has_categories (DesktopEntry *entry)
-{
-  return (entry->categories != NULL && entry->categories[0] != 0);
-}
-
-gboolean
-desktop_entry_has_category (DesktopEntry *entry,
-                            const char   *category)
-{
-  GQuark quark;
-  int    i;
-
-  if (entry->categories == NULL)
-    return FALSE;
-
-  if (!(quark = g_quark_try_string (category)))
-    return FALSE;
-
-  for (i = 0; entry->categories[i]; i++)
-    {
-      if (quark == entry->categories[i])
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
-void
-desktop_entry_add_legacy_category (DesktopEntry *entry)
-{
-  GQuark *categories;
-  int     i;
-
-  menu_verbose ("Adding Legacy category to \"%s\"\n",
-                entry->basename);
-
-  i = 0;
-  if (entry->categories != NULL)
-    {
-      for (; entry->categories[i]; i++);
-    }
-
-  categories = g_new0 (GQuark, i + 2);
-
-  i = 0;
-  if (entry->categories != NULL)
-    {
-      for (; entry->categories[i]; i++)
-        categories[i] = entry->categories[i];
-    }
-
-  categories[i] = g_quark_from_string ("Legacy");
-
-  g_free (entry->categories);
-  entry->categories = categories;
-}
-
-/*
- * Entry sets
- */
-
-DesktopEntrySet *
-desktop_entry_set_new (void)
-{
-  DesktopEntrySet *set;
-
-  set = g_new0 (DesktopEntrySet, 1);
-  set->refcount = 1;
-
-  menu_verbose (" New entry set %p\n", set);
-
-  return set;
-}
-
-DesktopEntrySet *
-desktop_entry_set_ref (DesktopEntrySet *set)
-{
-  g_return_val_if_fail (set != NULL, NULL);
-  g_return_val_if_fail (set->refcount > 0, NULL);
-
-  set->refcount += 1;
-
-  return set;
-}
-
-void
-desktop_entry_set_unref (DesktopEntrySet *set)
-{
-  g_return_if_fail (set != NULL);
-  g_return_if_fail (set->refcount > 0);
-
-  set->refcount -= 1;
-  if (set->refcount == 0)
-    {
-      menu_verbose (" Deleting entry set %p\n", set);
-
-      if (set->hash)
-        g_hash_table_destroy (set->hash);
-      set->hash = NULL;
-
-      g_free (set);
-    }
-}
-
-void
-desktop_entry_set_add_entry (DesktopEntrySet *set,
-                             DesktopEntry    *entry,
-                             const char      *file_id)
-{
-  menu_verbose (" Adding to set %p entry %s\n", set, file_id);
-
-  if (set->hash == NULL)
-    {
-      set->hash = g_hash_table_new_full (g_str_hash,
-                                         g_str_equal,
-                                         g_free,
-                                         (GDestroyNotify) desktop_entry_unref);
-    }
-
-  g_hash_table_replace (set->hash,
-                        g_strdup (file_id),
-                        desktop_entry_ref (entry));
-}
-
-DesktopEntry *
-desktop_entry_set_lookup (DesktopEntrySet *set,
-                          const char      *file_id)
-{
-  if (set->hash == NULL)
-    return NULL;
-
-  return g_hash_table_lookup (set->hash, file_id);
-}
-
-typedef struct
-{
-  DesktopEntrySetForeachFunc func;
-  gpointer                   user_data;
-} EntryHashForeachData;
-
-static void
-entry_hash_foreach (const char           *file_id,
-                    DesktopEntry         *entry,
-                    EntryHashForeachData *fd)
-{
-  fd->func (file_id, entry, fd->user_data);
-}
-
-void
-desktop_entry_set_foreach (DesktopEntrySet            *set,
-                           DesktopEntrySetForeachFunc  func,
-                           gpointer                    user_data)
-{
-  g_return_if_fail (set != NULL);
-  g_return_if_fail (func != NULL);
-
-  if (set->hash != NULL)
-    {
-      EntryHashForeachData fd;
-
-      fd.func      = func;
-      fd.user_data = user_data;
-
-      g_hash_table_foreach (set->hash,
-                            (GHFunc) entry_hash_foreach,
-                            &fd);
-    }
-}
-
-static void
-desktop_entry_set_clear (DesktopEntrySet *set)
-{
-  menu_verbose (" Clearing set %p\n", set);
-
-  if (set->hash != NULL)
-    {
-      g_hash_table_destroy (set->hash);
-      set->hash = NULL;
-    }
-}
-
-int
-desktop_entry_set_get_count (DesktopEntrySet *set)
-{
-  if (set->hash == NULL)
-    return 0;
-
-  return g_hash_table_size (set->hash);
-}
-
-static void
-union_foreach (const char      *file_id,
-               DesktopEntry    *entry,
-               DesktopEntrySet *set)
-{
-  /* we are iterating over "with" adding anything not
-   * already in "set". We unconditionally overwrite
-   * the stuff in "set" because we can assume
-   * two entries with the same name are equivalent.
-   */
-  desktop_entry_set_add_entry (set, entry, file_id);
-}
-
-void
-desktop_entry_set_union (DesktopEntrySet *set,
-                         DesktopEntrySet *with)
-{
-  menu_verbose (" Union of %p and %p\n", set, with);
-
-  if (desktop_entry_set_get_count (with) == 0)
-    return; /* A fast simple case */
-
-  g_hash_table_foreach (with->hash,
-                        (GHFunc) union_foreach,
-                        set);
-}
-
-typedef struct
-{
-  DesktopEntrySet *set;
-  DesktopEntrySet *with;
-} IntersectData;
-
-static gboolean
-intersect_foreach_remove (const char    *file_id,
-                          DesktopEntry  *entry,
-                          IntersectData *id)
-{
-  /* Remove everything in "set" which is not in "with" */
-
-  if (g_hash_table_lookup (id->with->hash, file_id) != NULL)
-    return FALSE;
-
-  menu_verbose (" Removing from %p entry %s\n", id->set, file_id);
-
-  return TRUE; /* return TRUE to remove */
-}
-
-void
-desktop_entry_set_intersection (DesktopEntrySet *set,
-                                DesktopEntrySet *with)
-{
-  IntersectData id;
-
-  menu_verbose (" Intersection of %p and %p\n", set, with);
-
-  if (desktop_entry_set_get_count (set) == 0 ||
-      desktop_entry_set_get_count (with) == 0)
-    {
-      /* A fast simple case */
-      desktop_entry_set_clear (set);
-      return;
-    }
-
-  id.set  = set;
-  id.with = with;
-
-  g_hash_table_foreach_remove (set->hash,
-                               (GHRFunc) intersect_foreach_remove,
-                               &id);
-}
-
-typedef struct
-{
-  DesktopEntrySet *set;
-  DesktopEntrySet *other;
-} SubtractData;
-
-static gboolean
-subtract_foreach_remove (const char   *file_id,
-                         DesktopEntry *entry,
-                         SubtractData *sd)
-{
-  /* Remove everything in "set" which is not in "other" */
-
-  if (g_hash_table_lookup (sd->other->hash, file_id) == NULL)
-    return FALSE;
-
-  menu_verbose (" Removing from %p entry %s\n", sd->set, file_id);
-
-  return TRUE; /* return TRUE to remove */
-}
-
-void
-desktop_entry_set_subtract (DesktopEntrySet *set,
-                            DesktopEntrySet *other)
-{
-  SubtractData sd;
-
-  menu_verbose (" Subtract from %p set %p\n", set, other);
-
-  if (desktop_entry_set_get_count (set) == 0 ||
-      desktop_entry_set_get_count (other) == 0)
-    return; /* A fast simple case */
-
-  sd.set   = set;
-  sd.other = other;
-
-  g_hash_table_foreach_remove (set->hash,
-                               (GHRFunc) subtract_foreach_remove,
-                               &sd);
-}
-
-void
-desktop_entry_set_swap_contents (DesktopEntrySet *a,
-                                 DesktopEntrySet *b)
-{
-  GHashTable *tmp;
-
-  menu_verbose (" Swap contents of %p and %p\n", a, b);
-
-  tmp = a->hash;
-  a->hash = b->hash;
-  b->hash = tmp;
-}
diff --git a/menu-cache-gen/desktop-entries.h b/menu-cache-gen/desktop-entries.h
deleted file mode 100644 (file)
index 66ed638..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2002 - 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __DESKTOP_ENTRIES_H__
-#define __DESKTOP_ENTRIES_H__
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-typedef enum
-{
-  DESKTOP_ENTRY_INVALID = 0,
-  DESKTOP_ENTRY_DESKTOP,
-  DESKTOP_ENTRY_DIRECTORY
-} DesktopEntryType;
-
-typedef struct DesktopEntry DesktopEntry;
-
-DesktopEntry *desktop_entry_new (const char   *path);
-
-DesktopEntry *desktop_entry_ref    (DesktopEntry *entry);
-DesktopEntry *desktop_entry_copy   (DesktopEntry *entry);
-DesktopEntry *desktop_entry_reload (DesktopEntry *entry);
-void          desktop_entry_unref  (DesktopEntry *entry);
-
-DesktopEntryType  desktop_entry_get_type     (DesktopEntry *entry);
-const char       *desktop_entry_get_path     (DesktopEntry *entry);
-const char       *desktop_entry_get_basename (DesktopEntry *entry);
-
-const char *desktop_entry_get_name               (DesktopEntry *entry);
-const char *desktop_entry_get_generic_name       (DesktopEntry *entry);
-const char *desktop_entry_get_full_name          (DesktopEntry *entry);
-const char *desktop_entry_get_comment            (DesktopEntry *entry);
-const char *desktop_entry_get_icon               (DesktopEntry *entry);
-const char *desktop_entry_get_exec               (DesktopEntry *entry);
-gboolean    desktop_entry_get_launch_in_terminal (DesktopEntry *entry);
-gboolean    desktop_entry_get_use_startup_notify (DesktopEntry *entry);
-
-gboolean    desktop_entry_get_show_in_flags      (DesktopEntry *entry);
-
-gboolean desktop_entry_get_hidden         (DesktopEntry *entry);
-gboolean desktop_entry_get_no_display     (DesktopEntry *entry);
-gboolean desktop_entry_get_show_in_gnome  (DesktopEntry *entry);
-gboolean desktop_entry_get_tryexec_failed (DesktopEntry *entry);
-
-gboolean desktop_entry_has_categories (DesktopEntry *entry);
-gboolean desktop_entry_has_category   (DesktopEntry *entry,
-                                       const char   *category);
-
-void desktop_entry_add_legacy_category (DesktopEntry *src);
-
-
-typedef struct DesktopEntrySet DesktopEntrySet;
-
-DesktopEntrySet *desktop_entry_set_new   (void);
-DesktopEntrySet *desktop_entry_set_ref   (DesktopEntrySet *set);
-void             desktop_entry_set_unref (DesktopEntrySet *set);
-
-void          desktop_entry_set_add_entry (DesktopEntrySet *set,
-                                           DesktopEntry    *entry,
-                                           const char      *file_id);
-DesktopEntry* desktop_entry_set_lookup    (DesktopEntrySet *set,
-                                           const char      *file_id);
-int           desktop_entry_set_get_count (DesktopEntrySet *set);
-
-void desktop_entry_set_union         (DesktopEntrySet *set,
-                                      DesktopEntrySet *with);
-void desktop_entry_set_intersection  (DesktopEntrySet *set,
-                                      DesktopEntrySet *with);
-void desktop_entry_set_subtract      (DesktopEntrySet *set,
-                                      DesktopEntrySet *other);
-void desktop_entry_set_swap_contents (DesktopEntrySet *a,
-                                      DesktopEntrySet *b);
-
-typedef void (*DesktopEntrySetForeachFunc) (const char   *file_id,
-                                            DesktopEntry *entry,
-                                            gpointer      user_data);
-
-void desktop_entry_set_foreach (DesktopEntrySet            *set,
-                                DesktopEntrySetForeachFunc  func,
-                                gpointer                    user_data);
-
-G_END_DECLS
-
-#endif /* __DESKTOP_ENTRIES_H__ */
diff --git a/menu-cache-gen/entry-directories.c b/menu-cache-gen/entry-directories.c
deleted file mode 100644 (file)
index 989e22d..0000000
+++ /dev/null
@@ -1,1227 +0,0 @@
-/*
- * Copyright (C) 2002 - 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <config.h>
-
-#include "entry-directories.h"
-
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <dirent.h>
-
-#include "menu-util.h"
-#include "menu-monitor.h"
-#include "canonicalize.h"
-
-#include "gmenu-tree.h"
-
-typedef struct CachedDir CachedDir;
-typedef struct CachedDirMonitor CachedDirMonitor;
-
-struct EntryDirectory
-{
-  CachedDir *dir;
-  char      *legacy_prefix;
-
-  guint entry_type : 2;
-  guint is_legacy : 1;
-  guint refcount : 24;
-};
-
-struct EntryDirectoryList
-{
-  int    refcount;
-  int    length;
-  GList *dirs;
-};
-
-struct CachedDir
-{
-  CachedDir *parent;
-  char      *name;
-
-  GSList *entries;
-  GSList *subdirs;
-
-  MenuMonitor *dir_monitor;
-  GSList      *monitors;
-
-  guint have_read_entries : 1;
-  guint deleted : 1;
-
-  guint references : 28;
-};
-
-struct CachedDirMonitor
-{
-  EntryDirectory            *ed;
-  EntryDirectoryChangedFunc  callback;
-  gpointer                   user_data;
-};
-
-static void     cached_dir_free                   (CachedDir  *dir);
-static gboolean cached_dir_load_entries_recursive (CachedDir  *dir,
-                                                   const char *dirname);
-
-static void handle_cached_dir_changed (MenuMonitor      *monitor,
-                                      MenuMonitorEvent  event,
-                                      const char       *path,
-                                      CachedDir        *dir);
-
-/*
- * Entry directory cache
- */
-
-static CachedDir *dir_cache = NULL;
-
-static CachedDir *
-cached_dir_new (const char *name)
-{
-  CachedDir *dir;
-
-  dir = g_new0 (CachedDir, 1);
-
-  dir->name = g_strdup (name);
-
-  return dir;
-}
-
-static void
-cached_dir_free (CachedDir *dir)
-{
-  if (dir->dir_monitor)
-    {
-      menu_monitor_remove_notify (dir->dir_monitor,
-                                 (MenuMonitorNotifyFunc) handle_cached_dir_changed,
-                                 dir);
-      menu_monitor_unref (dir->dir_monitor);
-      dir->dir_monitor = NULL;
-    }
-
-  g_slist_foreach (dir->monitors, (GFunc) g_free, NULL);
-  g_slist_free (dir->monitors);
-  dir->monitors = NULL;
-
-  g_slist_foreach (dir->entries,
-                   (GFunc) desktop_entry_unref,
-                   NULL);
-  g_slist_free (dir->entries);
-  dir->entries = NULL;
-
-  g_slist_foreach (dir->subdirs,
-                   (GFunc) cached_dir_free,
-                   NULL);
-  g_slist_free (dir->subdirs);
-  dir->subdirs = NULL;
-
-  g_free (dir->name);
-  g_free (dir);
-}
-
-static inline CachedDir *
-find_subdir (CachedDir  *dir,
-             const char *subdir)
-{
-  GSList *tmp;
-
-  tmp = dir->subdirs;
-  while (tmp != NULL)
-    {
-      CachedDir *sub = tmp->data;
-
-      if (strcmp (sub->name, subdir) == 0)
-        return sub;
-
-      tmp = tmp->next;
-    }
-
-  return NULL;
-}
-
-static DesktopEntry *
-find_entry (CachedDir  *dir,
-            const char *basename)
-{
-  GSList *tmp;
-
-  tmp = dir->entries;
-  while (tmp != NULL)
-    {
-      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
-        return tmp->data;
-
-      tmp = tmp->next;
-    }
-
-  return NULL;
-}
-
-static DesktopEntry *
-cached_dir_find_relative_path (CachedDir  *dir,
-                               const char *relative_path)
-{
-  DesktopEntry  *retval = NULL;
-  char         **split;
-  int            i;
-
-  split = g_strsplit (relative_path, "/", -1);
-
-  i = 0;
-  while (split[i] != NULL)
-    {
-      if (split[i + 1] != NULL)
-        {
-          if ((dir = find_subdir (dir, split[i])) == NULL)
-            break;
-        }
-      else
-        {
-          retval = find_entry (dir, split[i]);
-          break;
-        }
-
-      ++i;
-    }
-
-  g_strfreev (split);
-
-  return retval;
-}
-
-static CachedDir *
-cached_dir_lookup (const char *canonical)
-{
-  CachedDir  *dir;
-  char      **split;
-  int         i;
-
-  if (dir_cache == NULL)
-    dir_cache = cached_dir_new ("/");
-  dir = dir_cache;
-
-  g_assert (canonical != NULL && canonical[0] == G_DIR_SEPARATOR);
-
-  menu_verbose ("Looking up cached dir \"%s\"\n", canonical);
-
-  split = g_strsplit (canonical + 1, "/", -1);
-
-  i = 0;
-  while (split[i] != NULL)
-    {
-      CachedDir *subdir;
-
-      if ((subdir = find_subdir (dir, split[i])) == NULL)
-        {
-          subdir = cached_dir_new (split[i]);
-          dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
-          subdir->parent = dir;
-        }
-
-      dir = subdir;
-
-      ++i;
-    }
-
-  g_strfreev (split);
-
-  g_assert (dir != NULL);
-
-  return dir;
-}
-
-static gboolean
-cached_dir_add_entry (CachedDir  *dir,
-                      const char *basename,
-                      const char *path)
-{
-  DesktopEntry *entry;
-
-  entry = desktop_entry_new (path);
-  if (entry == NULL)
-    return FALSE;
-
-  dir->entries = g_slist_prepend (dir->entries, entry);
-
-  return TRUE;
-}
-
-static gboolean
-cached_dir_update_entry (CachedDir  *dir,
-                         const char *basename,
-                         const char *path)
-{
-  GSList *tmp;
-
-  tmp = dir->entries;
-  while (tmp != NULL)
-    {
-      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
-        {
-          if (!desktop_entry_reload (tmp->data))
-           {
-             dir->entries = g_slist_delete_link (dir->entries, tmp);
-           }
-
-          return TRUE;
-        }
-
-      tmp = tmp->next;
-    }
-
-  return cached_dir_add_entry (dir, basename, path);
-}
-
-static gboolean
-cached_dir_remove_entry (CachedDir  *dir,
-                         const char *basename)
-{
-  GSList *tmp;
-
-  tmp = dir->entries;
-  while (tmp != NULL)
-    {
-      if (strcmp (desktop_entry_get_basename (tmp->data), basename) == 0)
-        {
-          desktop_entry_unref (tmp->data);
-          dir->entries = g_slist_delete_link (dir->entries, tmp);
-          return TRUE;
-        }
-
-      tmp = tmp->next;
-    }
-
-  return FALSE;
-}
-
-static gboolean
-cached_dir_add_subdir (CachedDir  *dir,
-                       const char *basename,
-                       const char *path)
-{
-  CachedDir *subdir;
-
-  subdir = find_subdir (dir, basename);
-
-  if (subdir != NULL)
-    {
-      subdir->deleted = FALSE;
-      return TRUE;
-    }
-
-  subdir = cached_dir_new (basename);
-
-  if (!cached_dir_load_entries_recursive (subdir, path))
-    {
-      cached_dir_free (subdir);
-      return FALSE;
-    }
-
-  menu_verbose ("Caching dir \"%s\"\n", basename);
-
-  subdir->parent = dir;
-  dir->subdirs = g_slist_prepend (dir->subdirs, subdir);
-
-  return TRUE;
-}
-
-static gboolean
-cached_dir_remove_subdir (CachedDir  *dir,
-                          const char *basename)
-{
-  CachedDir *subdir;
-
-  subdir = find_subdir (dir, basename);
-
-  if (subdir != NULL)
-    {
-      subdir->deleted = TRUE;
-
-      if (subdir->references == 0)
-        {
-          cached_dir_free (subdir);
-          dir->subdirs = g_slist_remove (dir->subdirs, subdir);
-        }
-
-      return TRUE;
-    }
-
-  return FALSE;
-}
-
-static void
-cached_dir_invoke_monitors (CachedDir *dir)
-{
-  GSList *tmp;
-
-  tmp = dir->monitors;
-  while (tmp != NULL)
-    {
-      CachedDirMonitor *monitor = tmp->data;
-      GSList           *next    = tmp->next;
-
-      monitor->callback (monitor->ed, monitor->user_data);
-
-      tmp = next;
-    }
-
-  if (dir->parent)
-    {
-      cached_dir_invoke_monitors (dir->parent);
-    }
-}
-
-static void
-handle_cached_dir_changed (MenuMonitor      *monitor,
-                          MenuMonitorEvent  event,
-                          const char       *path,
-                           CachedDir        *dir)
-{
-  gboolean  handled = FALSE;
-  char     *basename;
-  char     *dirname;
-
-  menu_verbose ("'%s' notified of '%s' %s - invalidating cache\n",
-               dir->name,
-                path,
-                event == MENU_MONITOR_EVENT_CREATED ? ("created") :
-                event == MENU_MONITOR_EVENT_DELETED ? ("deleted") : ("changed"));
-
-  dirname  = g_path_get_dirname  (path);
-  basename = g_path_get_basename (path);
-
-  dir = cached_dir_lookup (dirname);
-
-  if (g_str_has_suffix (basename, ".desktop") ||
-      g_str_has_suffix (basename, ".directory"))
-    {
-      switch (event)
-        {
-        case MENU_MONITOR_EVENT_CREATED:
-        case MENU_MONITOR_EVENT_CHANGED:
-          handled = cached_dir_update_entry (dir, basename, path);
-          break;
-
-        case MENU_MONITOR_EVENT_DELETED:
-          handled = cached_dir_remove_entry (dir, basename);
-          break;
-
-        default:
-          g_assert_not_reached ();
-          break;
-        }
-    }
-  else /* Try recursing */
-    {
-      switch (event)
-        {
-        case MENU_MONITOR_EVENT_CREATED:
-          handled = cached_dir_add_subdir (dir, basename, path);
-          break;
-
-        case MENU_MONITOR_EVENT_CHANGED:
-          break;
-
-        case MENU_MONITOR_EVENT_DELETED:
-          handled = cached_dir_remove_subdir (dir, basename);
-          break;
-
-        default:
-          g_assert_not_reached ();
-          break;
-        }
-    }
-
-  g_free (basename);
-  g_free (dirname);
-
-  if (handled)
-    {
-      /* CHANGED events don't change the set of desktop entries */
-      if (event == MENU_MONITOR_EVENT_CREATED || event == MENU_MONITOR_EVENT_DELETED)
-        {
-          _entry_directory_list_empty_desktop_cache ();
-        }
-
-      cached_dir_invoke_monitors (dir);
-    }
-}
-
-static void
-cached_dir_ensure_monitor (CachedDir  *dir,
-                           const char *dirname)
-{
-  if (dir->dir_monitor == NULL)
-    {
-      dir->dir_monitor = menu_get_directory_monitor (dirname);
-      menu_monitor_add_notify (dir->dir_monitor,
-                              (MenuMonitorNotifyFunc) handle_cached_dir_changed,
-                              dir);
-    }
-}
-
-static gboolean
-cached_dir_load_entries_recursive (CachedDir  *dir,
-                                   const char *dirname)
-{
-  DIR           *dp;
-  struct dirent *dent;
-  GString       *fullpath;
-  gsize          fullpath_len;
-
-  g_assert (dir != NULL);
-
-  if (dir->have_read_entries)
-    return TRUE;
-
-  menu_verbose ("Attempting to read entries from %s (full path %s)\n",
-                dir->name, dirname);
-
-  dp = opendir (dirname);
-  if (dp == NULL)
-    {
-      menu_verbose ("Unable to list directory \"%s\"\n",
-                    dirname);
-      return FALSE;
-    }
-
-  /* add the dir to files to monitor */
-  if( ! g_slist_find_custom(all_used_dirs, dirname, (GCompareFunc)strcmp ) )
-    all_used_dirs = g_slist_prepend(all_used_dirs, g_strdup(dirname));
-
-  cached_dir_ensure_monitor (dir, dirname);
-
-  fullpath = g_string_new (dirname);
-  if (fullpath->str[fullpath->len - 1] != G_DIR_SEPARATOR)
-    g_string_append_c (fullpath, G_DIR_SEPARATOR);
-
-  fullpath_len = fullpath->len;
-
-  while ((dent = readdir (dp)) != NULL)
-    {
-      /* ignore . and .. */
-      if (dent->d_name[0] == '.' &&
-          (dent->d_name[1] == '\0' ||
-           (dent->d_name[1] == '.' &&
-            dent->d_name[2] == '\0')))
-        continue;
-
-      g_string_append (fullpath, dent->d_name);
-
-      if (g_str_has_suffix (dent->d_name, ".desktop") ||
-          g_str_has_suffix (dent->d_name, ".directory"))
-        {
-          cached_dir_add_entry (dir, dent->d_name, fullpath->str);
-        }
-      else /* Try recursing */
-        {
-          cached_dir_add_subdir (dir, dent->d_name, fullpath->str);
-        }
-
-      g_string_truncate (fullpath, fullpath_len);
-    }
-
-  closedir (dp);
-
-  g_string_free (fullpath, TRUE);
-
-  dir->have_read_entries = TRUE;
-
-  return TRUE;
-}
-
-static void
-cached_dir_add_monitor (CachedDir                 *dir,
-                        EntryDirectory            *ed,
-                        EntryDirectoryChangedFunc  callback,
-                        gpointer                   user_data)
-{
-  CachedDirMonitor *monitor;
-  GSList           *tmp;
-
-  tmp = dir->monitors;
-  while (tmp != NULL)
-    {
-      monitor = tmp->data;
-
-      if (monitor->ed == ed &&
-          monitor->callback == callback &&
-          monitor->user_data == user_data)
-        break;
-
-      tmp = tmp->next;
-    }
-
-  if (tmp == NULL)
-    {
-      monitor            = g_new0 (CachedDirMonitor, 1);
-      monitor->ed        = ed;
-      monitor->callback  = callback;
-      monitor->user_data = user_data;
-
-      dir->monitors = g_slist_append (dir->monitors, monitor);
-    }
-}
-
-static void
-cached_dir_remove_monitor (CachedDir                 *dir,
-                           EntryDirectory            *ed,
-                           EntryDirectoryChangedFunc  callback,
-                           gpointer                   user_data)
-{
-  GSList *tmp;
-
-  tmp = dir->monitors;
-  while (tmp != NULL)
-    {
-      CachedDirMonitor *monitor = tmp->data;
-      GSList           *next = tmp->next;
-
-      if (monitor->ed == ed &&
-          monitor->callback == callback &&
-          monitor->user_data == user_data)
-        {
-          dir->monitors = g_slist_delete_link (dir->monitors, tmp);
-          g_free (monitor);
-        }
-
-      tmp = next;
-    }
-}
-
-static void
-cached_dir_add_reference (CachedDir *dir)
-{
-  dir->references++;
-
-  if (dir->parent != NULL)
-    {
-      cached_dir_add_reference (dir->parent);
-    }
-}
-
-static void
-cached_dir_remove_reference (CachedDir *dir)
-{
-  CachedDir *parent;
-
-  parent = dir->parent;
-
-  if (--dir->references == 0 && dir->deleted)
-    {
-      if (dir->parent != NULL)
-       {
-         GSList *tmp;
-
-         tmp = parent->subdirs;
-         while (tmp != NULL)
-           {
-             CachedDir *subdir = tmp->data;
-
-             if (!strcmp (subdir->name, dir->name))
-               {
-                 parent->subdirs = g_slist_delete_link (parent->subdirs, tmp);
-                 break;
-               }
-
-             tmp = tmp->next;
-           }
-       }
-
-      cached_dir_free (dir);
-    }
-
-  if (parent != NULL)
-    {
-      cached_dir_remove_reference (parent);
-    }
-}
-
-/*
- * Entry directories
- */
-
-static EntryDirectory *
-entry_directory_new_full (DesktopEntryType  entry_type,
-                          const char       *path,
-                          gboolean          is_legacy,
-                          const char       *legacy_prefix)
-{
-  EntryDirectory *ed;
-  char           *canonical;
-
-  menu_verbose ("Loading entry directory \"%s\" (legacy %s)\n",
-                path,
-                is_legacy ? "<yes>" : "<no>");
-
-  canonical = menu_canonicalize_file_name (path, FALSE);
-  if (canonical == NULL)
-    {
-      menu_verbose ("Failed to canonicalize \"%s\": %s\n",
-                    path, g_strerror (errno));
-      return NULL;
-    }
-
-  ed = g_new0 (EntryDirectory, 1);
-
-  ed->dir = cached_dir_lookup (canonical);
-  g_assert (ed->dir != NULL);
-
-  cached_dir_add_reference (ed->dir);
-  cached_dir_load_entries_recursive (ed->dir, canonical);
-
-  ed->legacy_prefix = g_strdup (legacy_prefix);
-  ed->entry_type    = entry_type;
-  ed->is_legacy     = is_legacy != FALSE;
-  ed->refcount      = 1;
-
-  g_free (canonical);
-
-  return ed;
-}
-
-EntryDirectory *
-entry_directory_new (DesktopEntryType  entry_type,
-                     const char       *path)
-{
-  if( ! g_slist_find_custom(all_used_dirs, path, (GCompareFunc)strcmp ) )
-      all_used_dirs = g_slist_prepend( all_used_dirs, g_strdup( path ) );
-  return entry_directory_new_full (entry_type, path, FALSE, NULL);
-}
-
-EntryDirectory *
-entry_directory_new_legacy (DesktopEntryType  entry_type,
-                            const char       *path,
-                            const char       *legacy_prefix)
-{
-  return entry_directory_new_full (entry_type, path, TRUE, legacy_prefix);
-}
-
-EntryDirectory *
-entry_directory_ref (EntryDirectory *ed)
-{
-  g_return_val_if_fail (ed != NULL, NULL);
-  g_return_val_if_fail (ed->refcount > 0, NULL);
-
-  ed->refcount++;
-
-  return ed;
-}
-
-void
-entry_directory_unref (EntryDirectory *ed)
-{
-  g_return_if_fail (ed != NULL);
-  g_return_if_fail (ed->refcount > 0);
-
-  if (--ed->refcount == 0)
-    {
-      cached_dir_remove_reference (ed->dir);
-
-      ed->dir        = NULL;
-      ed->entry_type = DESKTOP_ENTRY_INVALID;
-      ed->is_legacy  = FALSE;
-
-      g_free (ed->legacy_prefix);
-      ed->legacy_prefix = NULL;
-
-      g_free (ed);
-    }
-}
-
-static void
-entry_directory_add_monitor (EntryDirectory            *ed,
-                             EntryDirectoryChangedFunc  callback,
-                             gpointer                   user_data)
-{
-  cached_dir_add_monitor (ed->dir, ed, callback, user_data);
-}
-
-static void
-entry_directory_remove_monitor (EntryDirectory            *ed,
-                                EntryDirectoryChangedFunc  callback,
-                                gpointer                   user_data)
-{
-  cached_dir_remove_monitor (ed->dir, ed, callback, user_data);
-}
-
-static DesktopEntry *
-entry_directory_get_directory (EntryDirectory *ed,
-                               const char     *relative_path)
-{
-  DesktopEntry *entry;
-
-  if (ed->entry_type != DESKTOP_ENTRY_DIRECTORY)
-    return NULL;
-
-  entry = cached_dir_find_relative_path (ed->dir, relative_path);
-  if (entry == NULL || desktop_entry_get_type (entry) != DESKTOP_ENTRY_DIRECTORY)
-    return NULL;
-
-  return desktop_entry_ref (entry);
-}
-
-static char *
-get_desktop_file_id_from_path (EntryDirectory   *ed,
-                              DesktopEntryType  entry_type,
-                              const char       *relative_path)
-{
-  char *retval;
-  
-  retval = NULL;
-
-  if (entry_type == DESKTOP_ENTRY_DESKTOP)
-    {
-      if (!ed->is_legacy)
-       {
-         retval = g_strdelimit (g_strdup (relative_path), "/", '-');
-       }
-      else
-       {
-         char *basename;
-
-         basename = g_path_get_basename (relative_path);
-
-         if (ed->legacy_prefix)
-           {
-             retval = g_strjoin ("-", ed->legacy_prefix, basename, NULL);
-             g_free (basename);
-           }
-         else
-           {
-             retval = basename;
-           }
-       }
-    }
-  else
-    {
-      retval = g_strdup (relative_path);
-    }
-
-  return retval;
-}
-
-typedef gboolean (* EntryDirectoryForeachFunc) (EntryDirectory  *ed,
-                                                DesktopEntry    *entry,
-                                                const char      *file_id,
-                                                DesktopEntrySet *set,
-                                                gpointer         user_data);
-
-static gboolean
-entry_directory_foreach_recursive (EntryDirectory            *ed,
-                                   CachedDir                 *cd,
-                                   GString                   *relative_path,
-                                   EntryDirectoryForeachFunc  func,
-                                   DesktopEntrySet           *set,
-                                   gpointer                   user_data)
-{
-  GSList *tmp;
-  int     relative_path_len;
-
-  if (cd->deleted)
-    return TRUE;
-
-  relative_path_len = relative_path->len;
-
-  tmp = cd->entries;
-  while (tmp != NULL)
-    {
-      DesktopEntry *entry = tmp->data;
-
-      if (desktop_entry_get_type (entry) == ed->entry_type)
-        {
-          gboolean  ret;
-          char     *file_id;
-
-          g_string_append (relative_path,
-                           desktop_entry_get_basename (entry));
-
-         file_id = get_desktop_file_id_from_path (ed,
-                                                  ed->entry_type,
-                                                  relative_path->str);
-
-          ret = func (ed, entry, file_id, set, user_data);
-
-          g_free (file_id);
-
-          g_string_truncate (relative_path, relative_path_len);
-
-          if (!ret)
-            return FALSE;
-        }
-
-      tmp = tmp->next;
-    }
-
-  tmp = cd->subdirs;
-  while (tmp != NULL)
-    {
-      CachedDir *subdir = tmp->data;
-
-      g_string_append (relative_path, subdir->name);
-      g_string_append_c (relative_path, G_DIR_SEPARATOR);
-
-      if (!entry_directory_foreach_recursive (ed,
-                                              subdir,
-                                              relative_path,
-                                              func,
-                                              set,
-                                              user_data))
-        return FALSE;
-
-      g_string_truncate (relative_path, relative_path_len);
-
-      tmp = tmp->next;
-    }
-
-  return TRUE;
-}
-
-static void
-entry_directory_foreach (EntryDirectory            *ed,
-                         EntryDirectoryForeachFunc  func,
-                         DesktopEntrySet           *set,
-                         gpointer                   user_data)
-{
-  GString *path;
-
-  path = g_string_new (NULL);
-
-  entry_directory_foreach_recursive (ed,
-                                     ed->dir,
-                                     path,
-                                     func,
-                                     set,
-                                     user_data);
-
-  g_string_free (path, TRUE);
-}
-
-void
-entry_directory_get_flat_contents (EntryDirectory   *ed,
-                                   DesktopEntrySet  *desktop_entries,
-                                   DesktopEntrySet  *directory_entries,
-                                   GSList          **subdirs)
-{
-  GSList *tmp;
-
-  if (subdirs)
-    *subdirs = NULL;
-
-  tmp = ed->dir->entries;
-  while (tmp != NULL)
-    {
-      DesktopEntry *entry = tmp->data;
-      const char   *basename;
-
-      basename = desktop_entry_get_basename (entry);
-
-      if (desktop_entries &&
-          desktop_entry_get_type (entry) == DESKTOP_ENTRY_DESKTOP)
-        {
-          char *file_id;
-
-          file_id = get_desktop_file_id_from_path (ed,
-                                                  DESKTOP_ENTRY_DESKTOP,
-                                                  basename);
-
-          desktop_entry_set_add_entry (desktop_entries,
-                                       entry,
-                                       file_id);
-
-          g_free (file_id);
-        }
-
-      if (directory_entries &&
-          desktop_entry_get_type (entry) == DESKTOP_ENTRY_DIRECTORY)
-        {
-          desktop_entry_set_add_entry (directory_entries,
-                                      entry,
-                                      basename);
-        }
-
-      tmp = tmp->next;
-    }
-
-  if (subdirs)
-    {
-      tmp = ed->dir->subdirs;
-      while (tmp != NULL)
-        {
-          CachedDir *cd = tmp->data;
-
-         if (!cd->deleted)
-           {
-             *subdirs = g_slist_prepend (*subdirs, g_strdup (cd->name));
-           }
-
-          tmp = tmp->next;
-        }
-    }
-
-  if (subdirs)
-    *subdirs = g_slist_reverse (*subdirs);
-}
-
-/*
- * Entry directory lists
- */
-
-EntryDirectoryList *
-entry_directory_list_new (void)
-{
-  EntryDirectoryList *list;
-
-  list = g_new0 (EntryDirectoryList, 1);
-
-  list->refcount = 1;
-  list->dirs = NULL;
-  list->length = 0;
-
-  return list;
-}
-
-EntryDirectoryList *
-entry_directory_list_ref (EntryDirectoryList *list)
-{
-  g_return_val_if_fail (list != NULL, NULL);
-  g_return_val_if_fail (list->refcount > 0, NULL);
-
-  list->refcount += 1;
-
-  return list;
-}
-
-void
-entry_directory_list_unref (EntryDirectoryList *list)
-{
-  g_return_if_fail (list != NULL);
-  g_return_if_fail (list->refcount > 0);
-
-  list->refcount -= 1;
-  if (list->refcount == 0)
-    {
-      g_list_foreach (list->dirs, (GFunc) entry_directory_unref, NULL);
-      g_list_free (list->dirs);
-      list->dirs = NULL;
-      list->length = 0;
-      g_free (list);
-    }
-}
-
-void
-entry_directory_list_prepend  (EntryDirectoryList *list,
-                               EntryDirectory     *ed)
-{
-  list->length += 1;
-  list->dirs = g_list_prepend (list->dirs,
-                               entry_directory_ref (ed));
-}
-
-int
-entry_directory_list_get_length (EntryDirectoryList *list)
-{
-  return list->length;
-}
-
-void
-entry_directory_list_append_list (EntryDirectoryList *list,
-                                  EntryDirectoryList *to_append)
-{
-  GList *tmp;
-  GList *new_dirs = NULL;
-
-  if (to_append->length == 0)
-    return;
-
-  tmp = to_append->dirs;
-  while (tmp != NULL)
-    {
-      list->length += 1;
-      new_dirs = g_list_prepend (new_dirs,
-                                 entry_directory_ref (tmp->data));
-
-      tmp = tmp->next;
-    }
-
-  new_dirs   = g_list_reverse (new_dirs);
-  list->dirs = g_list_concat (list->dirs, new_dirs);
-}
-
-DesktopEntry *
-entry_directory_list_get_directory (EntryDirectoryList *list,
-                                    const char         *relative_path)
-{
-  DesktopEntry *retval = NULL;
-  GList        *tmp;
-
-  tmp = list->dirs;
-  while (tmp != NULL)
-    {
-      if ((retval = entry_directory_get_directory (tmp->data, relative_path)) != NULL)
-        break;
-
-      tmp = tmp->next;
-    }
-
-  return retval;
-}
-
-gboolean
-_entry_directory_list_compare (const EntryDirectoryList *a,
-                               const EntryDirectoryList *b)
-{
-  GList *al, *bl;
-
-  if (a == NULL && b == NULL)
-    return TRUE;
-
-  if ((a == NULL || b == NULL))
-    return FALSE;
-
-  if (a->length != b->length)
-    return FALSE;
-
-  al = a->dirs; bl = b->dirs;
-  while (al && bl && al->data == bl->data)
-    {
-      al = al->next;
-      bl = bl->next;
-    }
-
-  return (al == NULL && bl == NULL);
-}
-
-static gboolean
-get_all_func (EntryDirectory   *ed,
-              DesktopEntry     *entry,
-              const char       *file_id,
-              DesktopEntrySet  *set,
-              gpointer          user_data)
-{
-  if (ed->is_legacy && !desktop_entry_has_categories (entry))
-    {
-      entry = desktop_entry_copy (entry);
-      desktop_entry_add_legacy_category (entry);
-    }
-  else
-    {
-      entry = desktop_entry_ref (entry);
-    }
-
-  desktop_entry_set_add_entry (set, entry, file_id);
-  desktop_entry_unref (entry);
-
-  return TRUE;
-}
-
-static DesktopEntrySet    *entry_directory_last_set = NULL;
-static EntryDirectoryList *entry_directory_last_list = NULL;
-
-void
-_entry_directory_list_empty_desktop_cache (void)
-{
-  if (entry_directory_last_set != NULL)
-    desktop_entry_set_unref (entry_directory_last_set);
-  entry_directory_last_set = NULL;
-
-  if (entry_directory_last_list != NULL)
-    entry_directory_list_unref (entry_directory_last_list);
-  entry_directory_last_list = NULL;
-}
-
-DesktopEntrySet *
-_entry_directory_list_get_all_desktops (EntryDirectoryList *list)
-{
-  GList *tmp;
-  DesktopEntrySet *set;
-
-  /* The only tricky thing here is that desktop files later
-   * in the search list with the same relative path
-   * are "hidden" by desktop files earlier in the path,
-   * so we have to do the earlier files first causing
-   * the later files to replace the earlier files
-   * in the DesktopEntrySet
-   *
-   * We go from the end of the list so we can just
-   * g_hash_table_replace and not have to do two
-   * hash lookups (check for existing entry, then insert new
-   * entry)
-   */
-
-  /* This method is -extremely- slow, so we have a simple
-     one-entry cache here */
-  if (_entry_directory_list_compare (list, entry_directory_last_list))
-    {
-      menu_verbose (" Hit desktop list (%p) cache\n", list);
-      return desktop_entry_set_ref (entry_directory_last_set);
-    }
-
-  if (entry_directory_last_set != NULL)
-    desktop_entry_set_unref (entry_directory_last_set);
-  if (entry_directory_last_list != NULL)
-    entry_directory_list_unref (entry_directory_last_list);
-
-  set = desktop_entry_set_new ();
-  menu_verbose (" Storing all of list %p in set %p\n",
-                list, set);
-
-  tmp = g_list_last (list->dirs);
-  while (tmp != NULL)
-    {
-      entry_directory_foreach (tmp->data, get_all_func, set, NULL);
-
-      tmp = tmp->prev;
-    }
-
-  entry_directory_last_list = entry_directory_list_ref (list);
-  entry_directory_last_set = desktop_entry_set_ref (set);
-
-  return set;
-}
-
-void
-entry_directory_list_add_monitors (EntryDirectoryList        *list,
-                                   EntryDirectoryChangedFunc  callback,
-                                   gpointer                   user_data)
-{
-  GList *tmp;
-
-  tmp = list->dirs;
-  while (tmp != NULL)
-    {
-      entry_directory_add_monitor (tmp->data, callback, user_data);
-      tmp = tmp->next;
-    }
-}
-
-void
-entry_directory_list_remove_monitors (EntryDirectoryList        *list,
-                                      EntryDirectoryChangedFunc  callback,
-                                      gpointer                   user_data)
-{
-  GList *tmp;
-
-  tmp = list->dirs;
-  while (tmp != NULL)
-    {
-      entry_directory_remove_monitor (tmp->data, callback, user_data);
-      tmp = tmp->next;
-    }
-}
diff --git a/menu-cache-gen/entry-directories.h b/menu-cache-gen/entry-directories.h
deleted file mode 100644 (file)
index 4b1f5fb..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2002 - 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __ENTRY_DIRECTORIES_H__
-#define __ENTRY_DIRECTORIES_H__
-
-#include <glib.h>
-#include "desktop-entries.h"
-
-G_BEGIN_DECLS
-
-typedef struct EntryDirectory EntryDirectory;
-
-typedef void (*EntryDirectoryChangedFunc) (EntryDirectory *ed,
-                                           gpointer        user_data);
-
-EntryDirectory *entry_directory_new        (DesktopEntryType  entry_type,
-                                            const char       *path);
-EntryDirectory *entry_directory_new_legacy (DesktopEntryType  entry_type,
-                                            const char       *path,
-                                            const char       *legacy_prefix);
-
-EntryDirectory *entry_directory_ref   (EntryDirectory *ed);
-void            entry_directory_unref (EntryDirectory *ed);
-
-void entry_directory_get_flat_contents (EntryDirectory   *ed,
-                                        DesktopEntrySet  *desktop_entries,
-                                        DesktopEntrySet  *directory_entries,
-                                        GSList          **subdirs);
-
-
-typedef struct EntryDirectoryList EntryDirectoryList;
-
-EntryDirectoryList *entry_directory_list_new   (void);
-EntryDirectoryList *entry_directory_list_ref   (EntryDirectoryList *list);
-void                entry_directory_list_unref (EntryDirectoryList *list);
-
-int  entry_directory_list_get_length  (EntryDirectoryList *list);
-gboolean _entry_directory_list_compare (const EntryDirectoryList *a,
-                                        const EntryDirectoryList *b);
-
-void entry_directory_list_prepend     (EntryDirectoryList *list,
-                                       EntryDirectory     *ed);
-void entry_directory_list_append_list (EntryDirectoryList *list,
-                                       EntryDirectoryList *to_append);
-
-void entry_directory_list_add_monitors    (EntryDirectoryList        *list,
-                                           EntryDirectoryChangedFunc  callback,
-                                           gpointer                   user_data);
-void entry_directory_list_remove_monitors (EntryDirectoryList        *list,
-                                           EntryDirectoryChangedFunc  callback,
-                                           gpointer                   user_data);
-
-DesktopEntry* entry_directory_list_get_directory (EntryDirectoryList *list,
-                                                  const char         *relative_path);
-
-DesktopEntrySet *_entry_directory_list_get_all_desktops (EntryDirectoryList *list);
-void             _entry_directory_list_empty_desktop_cache (void);
-
-G_END_DECLS
-
-#endif /* __ENTRY_DIRECTORIES_H__ */
diff --git a/menu-cache-gen/gmenu-tree.c b/menu-cache-gen/gmenu-tree.c
deleted file mode 100644 (file)
index 1369b5d..0000000
+++ /dev/null
@@ -1,4591 +0,0 @@
-/*
- * Copyright (C) 2003, 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <config.h>
-
-#include "gmenu-tree.h"
-
-#include <string.h>
-#include <errno.h>
-
-#include "menu-layout.h"
-#include "menu-monitor.h"
-#include "menu-util.h"
-#include "canonicalize.h"
-
-/*
- * FIXME: it might be useful to be able to construct a menu
- * tree from a traditional directory based menu hierarchy
- * too.
- */
-
-typedef enum
-{
-  GMENU_TREE_ABSOLUTE = 0,
-  GMENU_TREE_BASENAME = 1
-} GMenuTreeType;
-
-struct GMenuTree
-{
-  GMenuTreeType type;
-  guint         refcount;
-
-  char *basename;
-  char *absolute_path;
-  char *canonical_path;
-
-  GMenuTreeFlags flags;
-  GMenuTreeSortKey sort_key;
-
-  GSList *menu_file_monitors;
-
-  MenuLayoutNode *layout;
-  GMenuTreeDirectory *root;
-
-  GSList *monitors;
-
-  gpointer       user_data;
-  GDestroyNotify dnotify;
-
-  guint canonical : 1;
-};
-
-typedef struct
-{
-  GMenuTreeChangedFunc callback;
-  gpointer             user_data;
-} GMenuTreeMonitor;
-
-struct GMenuTreeItem
-{
-  GMenuTreeItemType type;
-
-  GMenuTreeDirectory *parent;
-  
-  gpointer       user_data;
-  GDestroyNotify dnotify;
-
-  guint refcount;
-};
-
-struct GMenuTreeDirectory
-{
-  GMenuTreeItem item;
-
-  DesktopEntry *directory_entry;
-  char         *name;
-
-  GSList *entries;
-  GSList *subdirs;
-
-  MenuLayoutValues  default_layout_values;
-  GSList           *default_layout_info;
-  GSList           *layout_info;
-  GSList           *contents;
-
-  guint only_unallocated : 1;
-  guint is_root : 1;
-  guint is_nodisplay : 1;
-  guint layout_pending_separator : 1;
-  guint preprocessed : 1;
-
-  /* 16 bits should be more than enough; G_MAXUINT16 means no inline header */
-  guint will_inline_header : 16;
-};
-
-typedef struct
-{
-  GMenuTreeDirectory directory;
-
-  GMenuTree *tree;
-} GMenuTreeDirectoryRoot;
-
-struct GMenuTreeEntry
-{
-  GMenuTreeItem item;
-
-  DesktopEntry *desktop_entry;
-  char         *desktop_file_id;
-
-  guint is_excluded : 1;
-  guint is_nodisplay : 1;
-};
-
-struct GMenuTreeSeparator
-{
-  GMenuTreeItem item;
-};
-
-struct GMenuTreeHeader
-{
-  GMenuTreeItem item;
-
-  GMenuTreeDirectory *directory;
-};
-
-struct GMenuTreeAlias
-{
-  GMenuTreeItem item;
-
-  GMenuTreeDirectory *directory;
-  GMenuTreeItem      *aliased_item;
-};
-
-static GMenuTree *gmenu_tree_new                 (GMenuTreeType    type,
-                                                 const char      *menu_file,
-                                                 gboolean         canonical,
-                                                 GMenuTreeFlags   flags);
-static void      gmenu_tree_load_layout          (GMenuTree       *tree);
-static void      gmenu_tree_force_reload         (GMenuTree       *tree);
-static void      gmenu_tree_build_from_layout    (GMenuTree       *tree);
-static void      gmenu_tree_force_rebuild        (GMenuTree       *tree);
-static void      gmenu_tree_resolve_files        (GMenuTree       *tree,
-                                                 GHashTable      *loaded_menu_files,
-                                                 MenuLayoutNode  *layout);
-static void      gmenu_tree_force_recanonicalize (GMenuTree       *tree);
-static void      gmenu_tree_invoke_monitors      (GMenuTree       *tree);
-     
-static void gmenu_tree_item_unref_and_unset_parent (gpointer itemp);
-
-/*
- * The idea is that we cache the menu tree for either a given
- * menu basename or an absolute menu path.
- * If no files exist in $XDG_DATA_DIRS for the basename or the
- * absolute path doesn't exist we just return (and cache) the
- * empty menu tree.
- * We also add a file monitor for the basename in each dir in
- * $XDG_DATA_DIRS, or the absolute path to the menu file, and
- * re-compute if there are any changes.
- */
-
-static GHashTable *gmenu_tree_cache = NULL;
-
-/* all used app dirs.
- */
-/* all used app dirs, required by menu-cache-gen */
-GSList* all_used_dirs = NULL;
-GSList* all_used_files = NULL;
-
-static inline char *
-get_cache_key (GMenuTree      *tree,
-              GMenuTreeFlags  flags)
-{
-  const char *tree_name;
-
-  switch (tree->type)
-    {
-    case GMENU_TREE_ABSOLUTE:
-      tree_name = tree->canonical ? tree->canonical_path : tree->absolute_path;
-      break;
-
-    case GMENU_TREE_BASENAME:
-      tree_name = tree->basename;
-      break;
-
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-  return g_strdup_printf ("%s:0x%x", tree_name, flags);
-}
-
-static void
-gmenu_tree_add_to_cache (GMenuTree      *tree,
-                        GMenuTreeFlags  flags)
-{
-  char *cache_key;
-
-  if (gmenu_tree_cache == NULL)
-    {
-      gmenu_tree_cache =
-        g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-    }
-
-  cache_key = get_cache_key (tree, flags);
-
-  menu_verbose ("Adding menu tree to cache: %s\n", cache_key);
-
-  g_hash_table_replace (gmenu_tree_cache, cache_key, tree);
-}
-
-static void
-gmenu_tree_remove_from_cache (GMenuTree      *tree,
-                             GMenuTreeFlags  flags)
-{
-  char *cache_key;
-
-  cache_key = get_cache_key (tree, flags);
-
-  menu_verbose ("Removing menu tree from cache: %s\n", cache_key);
-
-  g_hash_table_remove (gmenu_tree_cache, cache_key);
-
-  g_free (cache_key);
-
-  if (g_hash_table_size (gmenu_tree_cache) == 0)
-    {
-      g_hash_table_destroy (gmenu_tree_cache);
-      gmenu_tree_cache = NULL;
-
-      _entry_directory_list_empty_desktop_cache ();
-    }
-}
-
-static GMenuTree *
-gmenu_tree_lookup_from_cache (const char    *tree_name,
-                             GMenuTreeFlags  flags)
-{
-  GMenuTree *retval;
-  char     *cache_key;
-
-  if (gmenu_tree_cache == NULL)
-    return NULL;
-
-  cache_key = g_strdup_printf ("%s:0x%x", tree_name, flags);
-
-  menu_verbose ("Looking up '%s' from menu cache\n", cache_key);
-
-  retval = g_hash_table_lookup (gmenu_tree_cache, cache_key);
-
-  g_free (cache_key);
-
-  return retval ? gmenu_tree_ref (retval) : NULL;
-}
-
-typedef enum
-{
-  MENU_FILE_MONITOR_INVALID = 0,
-  MENU_FILE_MONITOR_FILE,
-  MENU_FILE_MONITOR_NONEXISTENT_FILE,
-  MENU_FILE_MONITOR_DIRECTORY
-} MenuFileMonitorType;
-
-typedef struct
-{
-  MenuFileMonitorType  type;
-  MenuMonitor         *monitor;
-} MenuFileMonitor;
-
-static void
-handle_nonexistent_menu_file_changed (MenuMonitor      *monitor,
-                                     MenuMonitorEvent  event,
-                                     const char       *path,
-                                     GMenuTree        *tree)
-{
-  if (event == MENU_MONITOR_EVENT_CHANGED ||
-      event == MENU_MONITOR_EVENT_CREATED)
-    {
-      menu_verbose ("\"%s\" %s, marking tree for recanonicalization\n",
-                    path,
-                    event == MENU_MONITOR_EVENT_CREATED ? "created" : "changed");
-
-      gmenu_tree_force_recanonicalize (tree);
-      gmenu_tree_invoke_monitors (tree);
-    }
-}
-
-static void
-handle_menu_file_changed (MenuMonitor      *monitor,
-                         MenuMonitorEvent  event,
-                         const char       *path,
-                          GMenuTree        *tree)
-{
-  menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
-               path,
-               event == MENU_MONITOR_EVENT_CREATED ? "created" :
-               event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
-
-  gmenu_tree_force_recanonicalize (tree);
-  gmenu_tree_invoke_monitors (tree);
-}
-
-static void
-handle_menu_file_directory_changed (MenuMonitor      *monitor,
-                                   MenuMonitorEvent  event,
-                                   const char       *path,
-                                   GMenuTree        *tree)
-{
-  if (!g_str_has_suffix (path, ".menu"))
-    return;
-
-  menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
-               path,
-               event == MENU_MONITOR_EVENT_CREATED ? "created" :
-               event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
-
-  gmenu_tree_force_recanonicalize (tree);
-  gmenu_tree_invoke_monitors (tree);
-}
-
-static void
-gmenu_tree_add_menu_file_monitor (GMenuTree           *tree,
-                                 const char          *path,
-                                 MenuFileMonitorType  type)
-{
-  MenuFileMonitor *monitor;
-
-  monitor = g_new0 (MenuFileMonitor, 1);
-
-  monitor->type = type;
-
-  switch (type)
-    {
-    case MENU_FILE_MONITOR_FILE:
-      menu_verbose ("Adding a menu file monitor for \"%s\"\n", path);
-
-      monitor->monitor = menu_get_file_monitor (path);
-      menu_monitor_add_notify (monitor->monitor,
-                              (MenuMonitorNotifyFunc) handle_menu_file_changed,
-                              tree);
-      break;
-
-    case MENU_FILE_MONITOR_NONEXISTENT_FILE:
-      menu_verbose ("Adding a menu file monitor for non-existent \"%s\"\n", path);
-
-      monitor->monitor = menu_get_file_monitor (path);
-      menu_monitor_add_notify (monitor->monitor,
-                              (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
-                              tree);
-      break;
-
-    case MENU_FILE_MONITOR_DIRECTORY:
-      menu_verbose ("Adding a menu directory monitor for \"%s\"\n", path);
-
-      monitor->monitor = menu_get_directory_monitor (path);
-      menu_monitor_add_notify (monitor->monitor,
-                              (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
-                              tree);
-      break;
-
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-  tree->menu_file_monitors = g_slist_prepend (tree->menu_file_monitors, monitor);
-}
-
-static void
-remove_menu_file_monitor (MenuFileMonitor *monitor,
-                         GMenuTree       *tree)
-{
-  switch (monitor->type)
-    {
-    case MENU_FILE_MONITOR_FILE:
-      menu_monitor_remove_notify (monitor->monitor,
-                                 (MenuMonitorNotifyFunc) handle_menu_file_changed,
-                                 tree);
-      break;
-
-    case MENU_FILE_MONITOR_NONEXISTENT_FILE:
-      menu_monitor_remove_notify (monitor->monitor,
-                                 (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
-                                 tree);
-      break;
-
-    case MENU_FILE_MONITOR_DIRECTORY:
-      menu_monitor_remove_notify (monitor->monitor,
-                                 (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
-                                 tree);
-      break;
-
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-  menu_monitor_unref (monitor->monitor);
-  monitor->monitor = NULL;
-
-  monitor->type = MENU_FILE_MONITOR_INVALID;
-
-  g_free (monitor);
-}
-
-static void
-gmenu_tree_remove_menu_file_monitors (GMenuTree *tree)
-{
-  menu_verbose ("Removing all menu file monitors\n");
-
-  g_slist_foreach (tree->menu_file_monitors,
-                   (GFunc) remove_menu_file_monitor,
-                   tree);
-  g_slist_free (tree->menu_file_monitors);
-  tree->menu_file_monitors = NULL;
-}
-
-static GMenuTree *
-gmenu_tree_lookup_absolute (const char    *absolute,
-                           GMenuTreeFlags  flags)
-{
-  GMenuTree  *tree;
-  gboolean    canonical;
-  const char *canonical_path;
-  char       *freeme;
-
-  menu_verbose ("Looking up absolute path in tree cache: \"%s\"\n", absolute);
-
-  if ((tree = gmenu_tree_lookup_from_cache (absolute, flags)) != NULL)
-    return tree;
-
-  canonical = TRUE;
-  canonical_path = freeme = menu_canonicalize_file_name (absolute, FALSE);
-  if (canonical_path == NULL)
-    {
-      menu_verbose ("Failed to canonicalize absolute menu path \"%s\": %s\n",
-                    absolute, g_strerror (errno));
-      canonical = FALSE;
-      canonical_path = absolute;
-    }
-
-  if ((tree = gmenu_tree_lookup_from_cache (canonical_path, flags)) != NULL)
-    return tree;
-
-  tree = gmenu_tree_new (GMENU_TREE_ABSOLUTE, canonical_path, canonical, flags);
-
-  g_free (freeme);
-
-  return tree;
-}
-
-static GMenuTree *
-gmenu_tree_lookup_basename (const char    *basename,
-                           GMenuTreeFlags  flags)
-{
-  GMenuTree *tree;
-
-  menu_verbose ("Looking up menu file in tree cache: \"%s\"\n", basename);
-
-  if ((tree = gmenu_tree_lookup_from_cache (basename, flags)) != NULL)
-    return tree;
-
-  return gmenu_tree_new (GMENU_TREE_BASENAME, basename, FALSE, flags);
-}
-
-static gboolean
-canonicalize_basename_with_config_dir (GMenuTree   *tree,
-                                       const char *basename,
-                                       const char *config_dir)
-{
-  char *path;
-
-  path = g_build_filename (config_dir, "menus",  basename,  NULL);
-
-  tree->canonical_path = menu_canonicalize_file_name (path, FALSE);
-  if (tree->canonical_path)
-    {
-      tree->canonical = TRUE;
-      gmenu_tree_add_menu_file_monitor (tree,
-                                       tree->canonical_path,
-                                       MENU_FILE_MONITOR_FILE);
-    }
-  else
-    {
-      gmenu_tree_add_menu_file_monitor (tree,
-                                       path,
-                                       MENU_FILE_MONITOR_NONEXISTENT_FILE);
-    }
-
-  g_free (path);
-
-  return tree->canonical;
-}
-
-static void
-canonicalize_basename (GMenuTree  *tree,
-                       const char *basename)
-{
-  if (!canonicalize_basename_with_config_dir (tree,
-                                              basename,
-                                              g_get_user_config_dir ()))
-    {
-      const char * const *system_config_dirs;
-      int                 i;
-
-      system_config_dirs = g_get_system_config_dirs ();
-
-      i = 0;
-      while (system_config_dirs[i] != NULL)
-        {
-          if (canonicalize_basename_with_config_dir (tree,
-                                                     basename,
-                                                     system_config_dirs[i]))
-            break;
-
-          ++i;
-        }
-    }
-}
-
-static gboolean
-gmenu_tree_canonicalize_path (GMenuTree *tree)
-{
-  if (tree->canonical)
-    return TRUE;
-
-  g_assert (tree->canonical_path == NULL);
-
-  if (tree->type == GMENU_TREE_BASENAME)
-    {
-      gmenu_tree_remove_menu_file_monitors (tree);
-
-      if (strcmp (tree->basename, "applications.menu") == 0 &&
-          g_getenv ("XDG_MENU_PREFIX"))
-        {
-          char *prefixed_basename;
-          prefixed_basename = g_strdup_printf ("%s%s",
-                                               g_getenv ("XDG_MENU_PREFIX"),
-                                               tree->basename);
-          canonicalize_basename (tree, prefixed_basename);
-          g_free (prefixed_basename);
-        }
-
-      if (!tree->canonical)
-        canonicalize_basename (tree, tree->basename);
-
-      if (tree->canonical)
-        menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
-                      tree->basename, tree->canonical_path);
-      else
-        menu_verbose ("Failed to look up menu_file for \"%s\"\n",
-                      tree->basename);
-    }
-  else /* if (tree->type == GMENU_TREE_ABSOLUTE) */
-    {
-      tree->canonical_path =
-        menu_canonicalize_file_name (tree->absolute_path, FALSE);
-      if (tree->canonical_path != NULL)
-        {
-          menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
-                        tree->absolute_path, tree->canonical_path);
-
-         /*
-          * Replace the cache entry with the canonicalized version
-          */
-          gmenu_tree_remove_from_cache (tree, tree->flags);
-
-          gmenu_tree_remove_menu_file_monitors (tree);
-          gmenu_tree_add_menu_file_monitor (tree,
-                                           tree->canonical_path,
-                                           MENU_FILE_MONITOR_FILE);
-
-          tree->canonical = TRUE;
-
-          gmenu_tree_add_to_cache (tree, tree->flags);
-        }
-      else
-        {
-          menu_verbose ("Failed to look up menu_file for \"%s\"\n",
-                        tree->absolute_path);
-        }
-    }
-
-  return tree->canonical;
-}
-
-static void
-gmenu_tree_force_recanonicalize (GMenuTree *tree)
-{
-  gmenu_tree_remove_menu_file_monitors (tree);
-
-  if (tree->canonical)
-    {
-      gmenu_tree_force_reload (tree);
-
-      g_free (tree->canonical_path);
-      tree->canonical_path = NULL;
-
-      tree->canonical = FALSE;
-    }
-}
-
-GMenuTree *
-gmenu_tree_lookup (const char     *menu_file,
-                  GMenuTreeFlags  flags)
-{
-  GMenuTree *retval;
-
-  g_return_val_if_fail (menu_file != NULL, NULL);
-
-  flags &= GMENU_TREE_FLAGS_MASK;
-
-  if (g_path_is_absolute (menu_file))
-    retval = gmenu_tree_lookup_absolute (menu_file, flags);
-  else
-    retval = gmenu_tree_lookup_basename (menu_file, flags);
-
-  g_assert (retval != NULL);
-
-  return retval;
-}
-
-static GMenuTree *
-gmenu_tree_new (GMenuTreeType   type,
-               const char     *menu_file,
-               gboolean        canonical,
-               GMenuTreeFlags  flags)
-{
-  GMenuTree *tree;
-
-  tree = g_new0 (GMenuTree, 1);
-
-  tree->type     = type;
-  tree->flags    = flags;
-  tree->refcount = 1;
-
-  tree->sort_key = GMENU_TREE_SORT_NAME;
-
-  if (tree->type == GMENU_TREE_BASENAME)
-    {
-      g_assert (canonical == FALSE);
-      tree->basename = g_strdup (menu_file);
-    }
-  else
-    {
-      tree->canonical     = canonical != FALSE;
-      tree->absolute_path = g_strdup (menu_file);
-
-      if (tree->canonical)
-       {
-         tree->canonical_path = g_strdup (menu_file);
-         gmenu_tree_add_menu_file_monitor (tree,
-                                           tree->canonical_path,
-                                           MENU_FILE_MONITOR_FILE);
-       }
-      else
-       {
-         gmenu_tree_add_menu_file_monitor (tree,
-                                           tree->absolute_path,
-                                           MENU_FILE_MONITOR_NONEXISTENT_FILE);
-       }
-    }
-
-  gmenu_tree_add_to_cache (tree, tree->flags);
-
-  return tree;
-}
-
-GMenuTree *
-gmenu_tree_ref (GMenuTree *tree)
-{
-  g_return_val_if_fail (tree != NULL, NULL);
-  g_return_val_if_fail (tree->refcount > 0, NULL);
-
-  tree->refcount++;
-
-  return tree;
-}
-
-void
-gmenu_tree_unref (GMenuTree *tree)
-{
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (tree->refcount >= 1);
-
-  if (--tree->refcount > 0)
-    return;
-
-  if (tree->dnotify)
-    tree->dnotify (tree->user_data);
-  tree->user_data = NULL;
-  tree->dnotify   = NULL;
-
-  gmenu_tree_remove_from_cache (tree, tree->flags);
-
-  gmenu_tree_force_recanonicalize (tree);
-
-  if (tree->basename != NULL)
-    g_free (tree->basename);
-  tree->basename = NULL;
-
-  if (tree->absolute_path != NULL)
-    g_free (tree->absolute_path);
-  tree->absolute_path = NULL;
-
-  g_slist_foreach (tree->monitors, (GFunc) g_free, NULL);
-  g_slist_free (tree->monitors);
-  tree->monitors = NULL;
-
-  g_free (tree);
-}
-
-void
-gmenu_tree_set_user_data (GMenuTree       *tree,
-                         gpointer        user_data,
-                         GDestroyNotify  dnotify)
-{
-  g_return_if_fail (tree != NULL);
-
-  if (tree->dnotify != NULL)
-    tree->dnotify (tree->user_data);
-
-  tree->dnotify   = dnotify;
-  tree->user_data = user_data;
-}
-
-gpointer
-gmenu_tree_get_user_data (GMenuTree *tree)
-{
-  g_return_val_if_fail (tree != NULL, NULL);
-
-  return tree->user_data;
-}
-
-const char *
-gmenu_tree_get_menu_file (GMenuTree *tree)
-{
-  /* FIXME: this is horribly ugly. But it's done to keep the API. Would be bad
-   * to break the API only for a "const char *" => "char *" change. The other
-   * alternative is to leak the memory, which is bad too. */
-  static char *ugly_result_cache = NULL;
-
-  g_return_val_if_fail (tree != NULL, NULL);
-
-  /* we need to canonicalize the path so we actually find out the real menu
-   * file that is being used -- and take into account XDG_MENU_PREFIX */
-  if (!gmenu_tree_canonicalize_path (tree))
-    return NULL;
-
-  if (ugly_result_cache != NULL)
-    {
-      g_free (ugly_result_cache);
-      ugly_result_cache = NULL;
-    }
-
-  if (tree->type == GMENU_TREE_BASENAME)
-    {
-      ugly_result_cache = g_path_get_basename (tree->canonical_path);
-      return ugly_result_cache;
-    }
-  else
-    return tree->absolute_path;
-}
-
-GMenuTreeDirectory *
-gmenu_tree_get_root_directory (GMenuTree *tree)
-{
-  g_return_val_if_fail (tree != NULL, NULL);
-
-  if (!tree->root)
-    {
-      gmenu_tree_build_from_layout (tree);
-
-      if (!tree->root)
-        return NULL;
-    }
-
-  return gmenu_tree_item_ref (tree->root);
-}
-
-static GMenuTreeDirectory *
-find_path (GMenuTreeDirectory *directory,
-          const char         *path)
-{
-  const char *name;
-  char       *slash;
-  char       *freeme;
-  GSList     *tmp;
-
-  while (path[0] == G_DIR_SEPARATOR) path++;
-
-  if (path[0] == '\0')
-    return directory;
-
-  freeme = NULL;
-  slash = strchr (path, G_DIR_SEPARATOR);
-  if (slash)
-    {
-      name = freeme = g_strndup (path, slash - path);
-      path = slash + 1;
-    }
-  else
-    {
-      name = path;
-      path = NULL;
-    }
-
-  tmp = directory->contents;
-  while (tmp != NULL)
-    {
-      GMenuTreeItem *item = tmp->data;
-
-      if (gmenu_tree_item_get_type (item) != GMENU_TREE_ITEM_DIRECTORY)
-        {
-          tmp = tmp->next;
-          continue;
-        }
-
-      if (!strcmp (name, GMENU_TREE_DIRECTORY (item)->name))
-       {
-         g_free (freeme);
-
-         if (path)
-           return find_path (GMENU_TREE_DIRECTORY (item), path);
-         else
-           return GMENU_TREE_DIRECTORY (item);
-       }
-
-      tmp = tmp->next;
-    }
-
-  g_free (freeme);
-
-  return NULL;
-}
-
-GMenuTreeDirectory *
-gmenu_tree_get_directory_from_path (GMenuTree  *tree,
-                                   const char *path)
-{
-  GMenuTreeDirectory *root;
-  GMenuTreeDirectory *directory;
-
-  g_return_val_if_fail (tree != NULL, NULL);
-  g_return_val_if_fail (path != NULL, NULL);
-
-  if (path[0] != G_DIR_SEPARATOR)
-    return NULL;
-
-  if (!(root = gmenu_tree_get_root_directory (tree)))
-    return NULL;
-
-  directory = find_path (root, path);
-
-  gmenu_tree_item_unref (root);
-
-  return directory ? gmenu_tree_item_ref (directory) : NULL;
-}
-
-GMenuTreeSortKey
-gmenu_tree_get_sort_key (GMenuTree *tree)
-{
-  g_return_val_if_fail (tree != NULL, GMENU_TREE_SORT_NAME);
-  g_return_val_if_fail (tree->refcount > 0, GMENU_TREE_SORT_NAME);
-
-  return tree->sort_key;
-}
-
-void
-gmenu_tree_set_sort_key (GMenuTree        *tree,
-                        GMenuTreeSortKey  sort_key)
-{
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (tree->refcount > 0);
-  /* g_return_if_fail (sort_key >= GMENU_TREE_SORT_FIRST); */
-  g_return_if_fail (sort_key <= GMENU_TREE_SORT_LAST);
-
-  if (sort_key == tree->sort_key)
-    return;
-
-  tree->sort_key = sort_key;
-  gmenu_tree_force_rebuild (tree);
-}
-
-void
-gmenu_tree_add_monitor (GMenuTree            *tree,
-                       GMenuTreeChangedFunc   callback,
-                       gpointer               user_data)
-{
-  GMenuTreeMonitor *monitor;
-  GSList           *tmp;
-
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (callback != NULL);
-
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      monitor = tmp->data;
-
-      if (monitor->callback  == callback &&
-          monitor->user_data == user_data)
-        break;
-
-      tmp = tmp->next;
-    }
-
-  if (tmp == NULL)
-    {
-      monitor = g_new0 (GMenuTreeMonitor, 1);
-
-      monitor->callback  = callback;
-      monitor->user_data = user_data;
-
-      tree->monitors = g_slist_append (tree->monitors, monitor);
-    }
-}
-
-void
-gmenu_tree_remove_monitor (GMenuTree            *tree,
-                          GMenuTreeChangedFunc  callback,
-                          gpointer              user_data)
-{
-  GSList *tmp;
-
-  g_return_if_fail (tree != NULL);
-  g_return_if_fail (callback != NULL);
-
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      GMenuTreeMonitor *monitor = tmp->data;
-      GSList          *next = tmp->next;
-
-      if (monitor->callback  == callback &&
-          monitor->user_data == user_data)
-        {
-          tree->monitors = g_slist_delete_link (tree->monitors, tmp);
-          g_free (monitor);
-        }
-
-      tmp = next;
-    }
-}
-
-static void
-gmenu_tree_invoke_monitors (GMenuTree *tree)
-{
-  GSList *tmp;
-
-  tmp = tree->monitors;
-  while (tmp != NULL)
-    {
-      GMenuTreeMonitor *monitor = tmp->data;
-      GSList           *next    = tmp->next;
-
-      monitor->callback (tree, monitor->user_data);
-
-      tmp = next;
-    }
-}
-
-GMenuTreeItemType
-gmenu_tree_item_get_type (GMenuTreeItem *item)
-{
-  g_return_val_if_fail (item != NULL, 0);
-
-  return item->type;
-}
-
-GMenuTreeDirectory *
-gmenu_tree_item_get_parent (GMenuTreeItem *item)
-{
-  g_return_val_if_fail (item != NULL, NULL);
-
-  return item->parent ? gmenu_tree_item_ref (item->parent) : NULL;
-}
-
-static void
-gmenu_tree_item_set_parent (GMenuTreeItem      *item,
-                           GMenuTreeDirectory *parent)
-{
-  g_return_if_fail (item != NULL);
-
-  item->parent = parent;
-}
-
-GSList *
-gmenu_tree_directory_get_contents (GMenuTreeDirectory *directory)
-{
-  GSList *retval;
-  GSList *tmp;
-
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  retval = NULL;
-
-  tmp = directory->contents;
-  while (tmp != NULL)
-    {
-      retval = g_slist_prepend (retval,
-                                gmenu_tree_item_ref (tmp->data));
-
-      tmp = tmp->next;
-    }
-
-  return g_slist_reverse (retval);
-}
-
-const char *
-gmenu_tree_directory_get_name (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  if (!directory->directory_entry)
-    return directory->name;
-
-  return desktop_entry_get_name (directory->directory_entry);
-}
-
-const char *
-gmenu_tree_directory_get_comment (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  if (!directory->directory_entry)
-    return NULL;
-
-  return desktop_entry_get_comment (directory->directory_entry);
-}
-
-const char *
-gmenu_tree_directory_get_icon (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  if (!directory->directory_entry)
-    return NULL;
-
-  return desktop_entry_get_icon (directory->directory_entry);
-}
-
-const char *
-gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  if (!directory->directory_entry)
-    return NULL;
-
-  return desktop_entry_get_path (directory->directory_entry);
-}
-
-const char *
-gmenu_tree_directory_get_menu_id (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  return directory->name;
-}
-
-static void
-gmenu_tree_directory_set_tree (GMenuTreeDirectory *directory,
-                              GMenuTree          *tree)
-{
-  GMenuTreeDirectoryRoot *root;
-
-  g_assert (directory != NULL);
-  g_assert (directory->is_root);
-
-  root = (GMenuTreeDirectoryRoot *) directory;
-
-  root->tree = tree;
-}
-
-GMenuTree *
-gmenu_tree_directory_get_tree (GMenuTreeDirectory *directory)
-{
-  GMenuTreeDirectoryRoot *root;
-
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  while (GMENU_TREE_ITEM (directory)->parent != NULL)
-    directory = GMENU_TREE_DIRECTORY (GMENU_TREE_ITEM (directory)->parent);
-
-  if (!directory->is_root)
-    return NULL;
-
-  root = (GMenuTreeDirectoryRoot *) directory;
-
-  if (root->tree)
-    gmenu_tree_ref (root->tree);
-
-  return root->tree;
-}
-
-gboolean
-gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory)
-{
-  g_return_val_if_fail (directory != NULL, FALSE);
-
-  return directory->is_nodisplay;
-}
-
-static void
-append_directory_path (GMenuTreeDirectory *directory,
-                      GString            *path)
-{
-
-  if (!directory->item.parent)
-    {
-      g_string_append_c (path, G_DIR_SEPARATOR);
-      return;
-    }
-
-  append_directory_path (directory->item.parent, path);
-
-  g_string_append (path, directory->name);
-  g_string_append_c (path, G_DIR_SEPARATOR);
-}
-
-char *
-gmenu_tree_directory_make_path (GMenuTreeDirectory *directory,
-                               GMenuTreeEntry     *entry)
-{
-  GString *path;
-
-  g_return_val_if_fail (directory != NULL, NULL);
-
-  path = g_string_new (NULL);
-
-  append_directory_path (directory, path);
-
-  if (entry != NULL)
-    g_string_append (path,
-                    desktop_entry_get_basename (entry->desktop_entry));
-
-  return g_string_free (path, FALSE);
-}
-
-const char *
-gmenu_tree_entry_get_name (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_name (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_generic_name (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_generic_name (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_display_name (GMenuTreeEntry *entry)
-{
-  const char *display_name;
-
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  display_name = desktop_entry_get_full_name (entry->desktop_entry);
-  if (!display_name || display_name[0] == '\0')
-    display_name = desktop_entry_get_name (entry->desktop_entry);
-
-  return display_name;
-}
-
-const char *
-gmenu_tree_entry_get_comment (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_comment (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_icon (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_icon (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_exec (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_exec (entry->desktop_entry);
-}
-
-gboolean
-gmenu_tree_entry_get_launch_in_terminal (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, FALSE);
-
-  return desktop_entry_get_launch_in_terminal (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return desktop_entry_get_path (entry->desktop_entry);
-}
-
-const char *
-gmenu_tree_entry_get_desktop_file_id (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, NULL);
-
-  return entry->desktop_file_id;
-}
-
-gboolean
-gmenu_tree_entry_get_is_excluded (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, FALSE);
-
-  return entry->is_excluded;
-}
-
-gboolean
-gmenu_tree_entry_get_is_nodisplay (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, FALSE);
-
-  return entry->is_nodisplay;
-}
-
-gboolean
-gmenu_tree_entry_get_use_startup_notify (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, FALSE);
-
-  return desktop_entry_get_use_startup_notify (entry->desktop_entry);
-}
-
-guint32
-gmenu_tree_entry_get_show_in_flags (GMenuTreeEntry *entry)
-{
-  g_return_val_if_fail (entry != NULL, FALSE);
-
-  return desktop_entry_get_show_in_flags (entry->desktop_entry);
-}
-
-GMenuTreeDirectory *
-gmenu_tree_header_get_directory (GMenuTreeHeader *header)
-{
-  g_return_val_if_fail (header != NULL, NULL);
-
-  return gmenu_tree_item_ref (header->directory);
-}
-
-GMenuTreeDirectory *
-gmenu_tree_alias_get_directory (GMenuTreeAlias *alias)
-{
-  g_return_val_if_fail (alias != NULL, NULL);
-
-  return gmenu_tree_item_ref (alias->directory);
-}
-
-GMenuTreeItem *
-gmenu_tree_alias_get_item (GMenuTreeAlias *alias)
-{
-  g_return_val_if_fail (alias != NULL, NULL);
-
-  return gmenu_tree_item_ref (alias->aliased_item);
-}
-
-static GMenuTreeDirectory *
-gmenu_tree_directory_new (GMenuTreeDirectory *parent,
-                         const char         *name,
-                         gboolean            is_root)
-{
-  GMenuTreeDirectory *retval;
-
-  if (!is_root)
-    {
-      retval = g_new0 (GMenuTreeDirectory, 1);
-    }
-  else
-    {
-      GMenuTreeDirectoryRoot *root;
-
-      root = g_new0 (GMenuTreeDirectoryRoot, 1);
-
-      retval = GMENU_TREE_DIRECTORY (root);
-
-      retval->is_root = TRUE;
-    }
-
-
-  retval->item.type     = GMENU_TREE_ITEM_DIRECTORY;
-  retval->item.parent   = parent;
-  retval->item.refcount = 1;
-
-  retval->name                = g_strdup (name);
-  retval->directory_entry     = NULL;
-  retval->entries             = NULL;
-  retval->subdirs             = NULL;
-  retval->default_layout_info = NULL;
-  retval->layout_info         = NULL;
-  retval->contents            = NULL;
-  retval->only_unallocated    = FALSE;
-  retval->is_nodisplay        = FALSE;
-  retval->layout_pending_separator = FALSE;
-  retval->preprocessed        = FALSE;
-  retval->will_inline_header  = G_MAXUINT16;
-
-  retval->default_layout_values.mask          = MENU_LAYOUT_VALUES_NONE;
-  retval->default_layout_values.show_empty    = FALSE;
-  retval->default_layout_values.inline_menus  = FALSE;
-  retval->default_layout_values.inline_limit  = 4;
-  retval->default_layout_values.inline_header = FALSE;
-  retval->default_layout_values.inline_alias  = FALSE;
-
-  return retval;
-}
-
-static void
-gmenu_tree_directory_finalize (GMenuTreeDirectory *directory)
-{
-  g_assert (directory->item.refcount == 0);
-
-  g_slist_foreach (directory->contents,
-                  (GFunc) gmenu_tree_item_unref_and_unset_parent,
-                  NULL);
-  g_slist_free (directory->contents);
-  directory->contents = NULL;
-  
-  g_slist_foreach (directory->default_layout_info,
-                  (GFunc) menu_layout_node_unref,
-                  NULL);
-  g_slist_free (directory->default_layout_info);
-  directory->default_layout_info = NULL;
-
-  g_slist_foreach (directory->layout_info,
-                  (GFunc) menu_layout_node_unref,
-                  NULL);
-  g_slist_free (directory->layout_info);
-  directory->layout_info = NULL;
-
-  g_slist_foreach (directory->subdirs,
-                  (GFunc) gmenu_tree_item_unref_and_unset_parent,
-                  NULL);
-  g_slist_free (directory->subdirs);
-  directory->subdirs = NULL;
-
-  g_slist_foreach (directory->entries,
-                  (GFunc) gmenu_tree_item_unref_and_unset_parent,
-                  NULL);
-  g_slist_free (directory->entries);
-  directory->entries = NULL;
-
-  if (directory->directory_entry)
-    desktop_entry_unref (directory->directory_entry);
-  directory->directory_entry = NULL;
-
-  g_free (directory->name);
-  directory->name = NULL;
-}
-
-static GMenuTreeSeparator *
-gmenu_tree_separator_new (GMenuTreeDirectory *parent)
-{
-  GMenuTreeSeparator *retval;
-
-  retval = g_new0 (GMenuTreeSeparator, 1);
-
-  retval->item.type     = GMENU_TREE_ITEM_SEPARATOR;
-  retval->item.parent   = parent;
-  retval->item.refcount = 1;
-
-  return retval;
-}
-
-static GMenuTreeHeader *
-gmenu_tree_header_new (GMenuTreeDirectory *parent,
-                      GMenuTreeDirectory *directory)
-{
-  GMenuTreeHeader *retval;
-
-  retval = g_new0 (GMenuTreeHeader, 1);
-
-  retval->item.type     = GMENU_TREE_ITEM_HEADER;
-  retval->item.parent   = parent;
-  retval->item.refcount = 1;
-
-  retval->directory = gmenu_tree_item_ref (directory);
-
-  gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL);
-
-  return retval;
-}
-
-static void
-gmenu_tree_header_finalize (GMenuTreeHeader *header)
-{
-  g_assert (header->item.refcount == 0);
-
-  if (header->directory != NULL)
-    gmenu_tree_item_unref (header->directory);
-  header->directory = NULL;
-}
-
-static GMenuTreeAlias *
-gmenu_tree_alias_new (GMenuTreeDirectory *parent,
-                     GMenuTreeDirectory *directory,
-                     GMenuTreeItem      *item)
-{
-  GMenuTreeAlias *retval;
-
-  retval = g_new0 (GMenuTreeAlias, 1);
-
-  retval->item.type     = GMENU_TREE_ITEM_ALIAS;
-  retval->item.parent   = parent;
-  retval->item.refcount = 1;
-
-  retval->directory    = gmenu_tree_item_ref (directory);
-  if (item->type != GMENU_TREE_ITEM_ALIAS)
-    retval->aliased_item = gmenu_tree_item_ref (item);
-  else
-    retval->aliased_item = gmenu_tree_item_ref (gmenu_tree_alias_get_item (GMENU_TREE_ALIAS (item)));
-
-  gmenu_tree_item_set_parent (GMENU_TREE_ITEM (retval->directory), NULL);
-  gmenu_tree_item_set_parent (retval->aliased_item, NULL);
-
-  return retval;
-}
-
-static void
-gmenu_tree_alias_finalize (GMenuTreeAlias *alias)
-{
-  g_assert (alias->item.refcount == 0);
-
-  if (alias->directory != NULL)
-    gmenu_tree_item_unref (alias->directory);
-  alias->directory = NULL;
-
-  if (alias->aliased_item != NULL)
-    gmenu_tree_item_unref (alias->aliased_item);
-  alias->aliased_item = NULL;
-}
-
-static GMenuTreeEntry *
-gmenu_tree_entry_new (GMenuTreeDirectory *parent,
-                     DesktopEntry       *desktop_entry,
-                     const char         *desktop_file_id,
-                     gboolean            is_excluded,
-                      gboolean            is_nodisplay)
-{
-  GMenuTreeEntry *retval;
-
-  retval = g_new0 (GMenuTreeEntry, 1);
-
-  retval->item.type     = GMENU_TREE_ITEM_ENTRY;
-  retval->item.parent   = parent;
-  retval->item.refcount = 1;
-
-  retval->desktop_entry   = desktop_entry_ref (desktop_entry);
-  retval->desktop_file_id = g_strdup (desktop_file_id);
-  retval->is_excluded     = is_excluded != FALSE;
-  retval->is_nodisplay    = is_nodisplay != FALSE;
-
-  return retval;
-}
-
-static void
-gmenu_tree_entry_finalize (GMenuTreeEntry *entry)
-{
-  g_assert (entry->item.refcount == 0);
-
-  g_free (entry->desktop_file_id);
-  entry->desktop_file_id = NULL;
-
-  if (entry->desktop_entry)
-    desktop_entry_unref (entry->desktop_entry);
-  entry->desktop_entry = NULL;
-}
-
-static int
-gmenu_tree_entry_compare_by_id (GMenuTreeItem *a,
-                               GMenuTreeItem *b)
-{
-  if (a->type == GMENU_TREE_ITEM_ALIAS)
-    a = GMENU_TREE_ALIAS (a)->aliased_item;
-
-  if (b->type == GMENU_TREE_ITEM_ALIAS)
-    b = GMENU_TREE_ALIAS (b)->aliased_item;
-
-  return strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id,
-                 GMENU_TREE_ENTRY (b)->desktop_file_id);
-}
-
-gpointer
-gmenu_tree_item_ref (gpointer itemp)
-{
-  GMenuTreeItem *item;
-  
-  item = (GMenuTreeItem *) itemp;
-
-  g_return_val_if_fail (item != NULL, NULL);
-  g_return_val_if_fail (item->refcount > 0, NULL);
-
-  item->refcount++;
-
-  return item;
-}
-
-void
-gmenu_tree_item_unref (gpointer itemp)
-{
-  GMenuTreeItem *item;
-
-  item = (GMenuTreeItem *) itemp;
-
-  g_return_if_fail (item != NULL);
-  g_return_if_fail (item->refcount > 0);
-
-  if (--item->refcount == 0)
-    {
-      switch (item->type)
-       {
-       case GMENU_TREE_ITEM_DIRECTORY:
-         gmenu_tree_directory_finalize (GMENU_TREE_DIRECTORY (item));
-         break;
-
-       case GMENU_TREE_ITEM_ENTRY:
-         gmenu_tree_entry_finalize (GMENU_TREE_ENTRY (item));
-         break;
-
-       case GMENU_TREE_ITEM_SEPARATOR:
-         break;
-
-       case GMENU_TREE_ITEM_HEADER:
-         gmenu_tree_header_finalize (GMENU_TREE_HEADER (item));
-         break;
-
-       case GMENU_TREE_ITEM_ALIAS:
-         gmenu_tree_alias_finalize (GMENU_TREE_ALIAS (item));
-         break;
-
-       default:
-         g_assert_not_reached ();
-         break;
-       }
-
-      if (item->dnotify)
-       item->dnotify (item->user_data);
-      item->user_data = NULL;
-      item->dnotify   = NULL;
-
-      item->parent = NULL;
-
-      g_free (item);
-    }
-}
-
-static void
-gmenu_tree_item_unref_and_unset_parent (gpointer itemp)
-{
-  GMenuTreeItem *item;
-
-  item = (GMenuTreeItem *) itemp;
-
-  g_return_if_fail (item != NULL);
-
-  gmenu_tree_item_set_parent (item, NULL);
-  gmenu_tree_item_unref (item);
-}
-
-void
-gmenu_tree_item_set_user_data (GMenuTreeItem  *item,
-                              gpointer        user_data,
-                              GDestroyNotify  dnotify)
-{
-  g_return_if_fail (item != NULL);
-
-  if (item->dnotify != NULL)
-    item->dnotify (item->user_data);
-
-  item->dnotify   = dnotify;
-  item->user_data = user_data;
-}
-
-gpointer
-gmenu_tree_item_get_user_data (GMenuTreeItem *item)
-{
-  g_return_val_if_fail (item != NULL, NULL);
-
-  return item->user_data;
-}
-
-static inline const char *
-gmenu_tree_item_compare_get_name_helper (GMenuTreeItem    *item,
-                                        GMenuTreeSortKey  sort_key)
-{
-  const char *name;
-
-  name = NULL;
-
-  switch (item->type)
-    {
-    case GMENU_TREE_ITEM_DIRECTORY:
-      if (GMENU_TREE_DIRECTORY (item)->directory_entry)
-       name = desktop_entry_get_name (GMENU_TREE_DIRECTORY (item)->directory_entry);
-      else
-       name = GMENU_TREE_DIRECTORY (item)->name;
-      break;
-
-    case GMENU_TREE_ITEM_ENTRY:
-      switch (sort_key)
-       {
-       case GMENU_TREE_SORT_NAME:
-         name = desktop_entry_get_name (GMENU_TREE_ENTRY (item)->desktop_entry);
-         break;
-       case GMENU_TREE_SORT_DISPLAY_NAME:
-         name = gmenu_tree_entry_get_display_name (GMENU_TREE_ENTRY (item));
-         break;
-       default:
-         g_assert_not_reached ();
-         break;
-       }
-      break;
-
-    case GMENU_TREE_ITEM_ALIAS:
-      {
-        GMenuTreeItem *dir;
-        dir = GMENU_TREE_ITEM (GMENU_TREE_ALIAS (item)->directory);
-        name = gmenu_tree_item_compare_get_name_helper (dir, sort_key);
-      }
-      break;
-
-    case GMENU_TREE_ITEM_SEPARATOR:
-    case GMENU_TREE_ITEM_HEADER:
-    default:
-      g_assert_not_reached ();
-      break;
-    }
-
-  return name;
-}
-
-static int
-gmenu_tree_item_compare (GMenuTreeItem *a,
-                        GMenuTreeItem *b,
-                        gpointer       sort_key_p)
-{
-  const char       *name_a;
-  const char       *name_b;
-  GMenuTreeSortKey  sort_key;
-
-  sort_key = GPOINTER_TO_INT (sort_key_p);
-
-  name_a = gmenu_tree_item_compare_get_name_helper (a, sort_key);
-  name_b = gmenu_tree_item_compare_get_name_helper (b, sort_key);
-
-  return g_utf8_collate (name_a, name_b);
-}
-
-static MenuLayoutNode *
-find_menu_child (MenuLayoutNode *layout)
-{
-  MenuLayoutNode *child;
-
-  child = menu_layout_node_get_children (layout);
-  while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU)
-    child = menu_layout_node_get_next (child);
-
-  return child;
-}
-
-static void
-merge_resolved_children (GMenuTree      *tree,
-                        GHashTable     *loaded_menu_files,
-                         MenuLayoutNode *where,
-                         MenuLayoutNode *from)
-{
-  MenuLayoutNode *insert_after;
-  MenuLayoutNode *menu_child;
-  MenuLayoutNode *from_child;
-
-  gmenu_tree_resolve_files (tree, loaded_menu_files, from);
-
-  insert_after = where;
-  g_assert (menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT);
-  g_assert (menu_layout_node_get_parent (insert_after) != NULL);
-
-  /* skip root node */
-  menu_child = find_menu_child (from);
-  g_assert (menu_child != NULL);
-  g_assert (menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU);
-
-  /* merge children of toplevel <Menu> */
-  from_child = menu_layout_node_get_children (menu_child);
-  while (from_child != NULL)
-    {
-      MenuLayoutNode *next;
-
-      next = menu_layout_node_get_next (from_child);
-
-      menu_verbose ("Merging ");
-      menu_debug_print_layout (from_child, FALSE);
-      menu_verbose (" after ");
-      menu_debug_print_layout (insert_after, FALSE);
-
-      switch (menu_layout_node_get_type (from_child))
-        {
-        case MENU_LAYOUT_NODE_NAME:
-          menu_layout_node_unlink (from_child); /* delete this */
-          break;
-
-        default:
-          menu_layout_node_steal (from_child);
-          menu_layout_node_insert_after (insert_after, from_child);
-          menu_layout_node_unref (from_child);
-
-          insert_after = from_child;
-          break;
-        }
-
-      from_child = next;
-    }
-}
-
-static gboolean
-load_merge_file (GMenuTree      *tree,
-                GHashTable     *loaded_menu_files,
-                 const char     *filename,
-                 gboolean        is_canonical,
-                gboolean        add_monitor,
-                 MenuLayoutNode *where)
-{
-  MenuLayoutNode *to_merge;
-  const char     *canonical;
-  char           *dirname;
-  char           *freeme;
-  gboolean        retval;
-
-  freeme = NULL;
-  retval = FALSE;
-
-  if (!is_canonical)
-    {
-      canonical = freeme = menu_canonicalize_file_name (filename, FALSE);
-      if (canonical == NULL)
-        {
-         if (add_monitor)
-           gmenu_tree_add_menu_file_monitor (tree,
-                                            filename,
-                                            MENU_FILE_MONITOR_NONEXISTENT_FILE);
-
-          menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n",
-                        filename, g_strerror (errno));
-         goto out;
-        }
-    }
-  else
-    {
-      canonical = filename;
-    }
-
-  /* only add this merge file if it's parent dir is not a merge dir. */
-  dirname = g_path_get_dirname(canonical);
-  if( ! g_slist_find_custom(all_used_dirs, dirname, (GCompareFunc)strcmp ) )
-    all_used_files = g_slist_prepend( all_used_files, g_strdup(canonical) );
-  g_free(dirname);
-
-  if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL)
-    {
-      g_warning ("Not loading \"%s\": recursive loop detected in .menu files",
-                canonical);
-      retval = TRUE;
-      goto out;
-    }
-
-  menu_verbose ("Merging file \"%s\"\n", canonical);
-
-  to_merge = menu_layout_load (canonical, NULL, NULL);
-  if (to_merge == NULL)
-    {
-      menu_verbose ("No menu for file \"%s\" found when merging\n",
-                    canonical);
-      goto out;
-    }
-
-  retval = TRUE;
-
-  g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE));
-
-  if (add_monitor)
-    gmenu_tree_add_menu_file_monitor (tree,
-                                     canonical,
-                                     MENU_FILE_MONITOR_FILE);
-
-  merge_resolved_children (tree, loaded_menu_files, where, to_merge);
-
-  g_hash_table_remove (loaded_menu_files, canonical);
-
-  menu_layout_node_unref (to_merge);
-
- out:
-  if (freeme)
-    g_free (freeme);
-
-  return retval;
-}
-
-static gboolean
-load_merge_file_with_config_dir (GMenuTree      *tree,
-                                GHashTable     *loaded_menu_files,
-                                const char     *menu_file,
-                                const char     *config_dir,
-                                MenuLayoutNode *where)
-{
-  char     *merge_file;
-  gboolean  loaded;
-
-  loaded = FALSE;
-
-  merge_file = g_build_filename (config_dir, "menus", menu_file, NULL);
-
-  if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE, TRUE, where))
-    loaded = TRUE;
-
-  g_free (merge_file);
-
-  return loaded;
-}
-
-static gboolean
-compare_basedir_to_config_dir (const char *canonical_basedir,
-                              const char *config_dir)
-{
-  char     *dirname;
-  char     *canonical_menus_dir;
-  gboolean  retval;
-
-  menu_verbose ("Checking to see if basedir '%s' is in '%s'\n",
-               canonical_basedir, config_dir);
-
-  dirname = g_build_filename (config_dir, "menus", NULL);
-
-  retval = FALSE;
-
-  canonical_menus_dir = menu_canonicalize_file_name (dirname, FALSE);
-  if (canonical_menus_dir != NULL &&
-      strcmp (canonical_basedir, canonical_menus_dir) == 0)
-    {
-      retval = TRUE;
-    }
-
-  g_free (canonical_menus_dir);
-  g_free (dirname);
-
-  return retval;
-}
-
-static gboolean
-load_parent_merge_file_from_basename (GMenuTree      *tree,
-                                      GHashTable     *loaded_menu_files,
-                                     MenuLayoutNode *layout,
-                                      const char     *menu_file,
-                                      const char     *canonical_basedir)
-{
-  gboolean            found_basedir;
-  const char * const *system_config_dirs;
-  int                 i;
-
-  /* We're not interested in menu files that are in directories which are not a
-   * parent of the base directory of this menu file */
-  found_basedir = compare_basedir_to_config_dir (canonical_basedir,
-                                                g_get_user_config_dir ());
-
-  system_config_dirs = g_get_system_config_dirs ();
-
-  i = 0;
-  while (system_config_dirs[i] != NULL)
-    {
-      if (!found_basedir)
-       {
-         found_basedir = compare_basedir_to_config_dir (canonical_basedir,
-                                                        system_config_dirs[i]);
-       }
-      else
-       {
-         menu_verbose ("Looking for parent menu file '%s' in '%s'\n",
-                       menu_file, system_config_dirs[i]);
-
-         if (load_merge_file_with_config_dir (tree,
-                                              loaded_menu_files,
-                                              menu_file,
-                                              system_config_dirs[i],
-                                              layout))
-           {
-             break;
-           }
-       }
-
-      ++i;
-    }
-
-  return system_config_dirs[i] != NULL;
-}
-
-static gboolean
-load_parent_merge_file (GMenuTree      *tree,
-                       GHashTable     *loaded_menu_files,
-                       MenuLayoutNode *layout)
-{
-  MenuLayoutNode     *root;
-  const char         *basedir;
-  const char         *menu_name;
-  char               *canonical_basedir;
-  char               *menu_file;
-  gboolean            found;
-
-  root = menu_layout_node_get_root (layout);
-
-  basedir   = menu_layout_node_root_get_basedir (root);
-  menu_name = menu_layout_node_root_get_name (root);
-
-  canonical_basedir = menu_canonicalize_file_name (basedir, FALSE);
-  if (canonical_basedir == NULL)
-    {
-      menu_verbose ("Menu basedir '%s' no longer exists, not merging parent\n",
-                   basedir);
-      return FALSE;
-    }
-
-  found = FALSE;
-  menu_file = g_strconcat (menu_name, ".menu", NULL);
-
-  if (strcmp (menu_file, "applications.menu") == 0 &&
-      g_getenv ("XDG_MENU_PREFIX"))
-    {
-      char *prefixed_basename;
-      prefixed_basename = g_strdup_printf ("%s%s",
-                                           g_getenv ("XDG_MENU_PREFIX"),
-                                           menu_file);
-      found = load_parent_merge_file_from_basename (tree, loaded_menu_files,
-                                                    layout, prefixed_basename,
-                                                    canonical_basedir);
-      g_free (prefixed_basename);
-    }
-
-  if (!found)
-    {
-      found = load_parent_merge_file_from_basename (tree, loaded_menu_files,
-                                                    layout, menu_file,
-                                                    canonical_basedir);
-    }
-
-  g_free (menu_file);
-  g_free (canonical_basedir);
-
-  return found;
-}
-
-static void
-load_merge_dir (GMenuTree      *tree,
-               GHashTable     *loaded_menu_files,
-                const char     *dirname,
-                MenuLayoutNode *where)
-{
-  GDir       *dir;
-  const char *menu_file;
-
-  menu_verbose ("Loading merge dir \"%s\"\n", dirname);
-
-  if( ! g_slist_find_custom(all_used_dirs, dirname, (GCompareFunc)strcmp ) )
-    all_used_dirs = g_slist_append( all_used_dirs, g_strdup(dirname) );
-
-  gmenu_tree_add_menu_file_monitor (tree,
-                                   dirname,
-                                   MENU_FILE_MONITOR_DIRECTORY);
-
-  if ((dir = g_dir_open (dirname, 0, NULL)) == NULL)
-    return;
-
-  while ((menu_file = g_dir_read_name (dir)))
-    {
-      if (g_str_has_suffix (menu_file, ".menu"))
-        {
-          char *full_path;
-
-          full_path = g_build_filename (dirname, menu_file, NULL);
-
-          load_merge_file (tree, loaded_menu_files, full_path, TRUE, FALSE, where);
-
-          g_free (full_path);
-        }
-    }
-
-  g_dir_close (dir);
-}
-
-static void
-load_merge_dir_with_config_dir (GMenuTree      *tree,
-                               GHashTable     *loaded_menu_files,
-                                const char     *config_dir,
-                                const char     *dirname,
-                                MenuLayoutNode *where)
-{
-  char *path;
-
-  path = g_build_filename (config_dir, "menus", dirname, NULL);
-
-  load_merge_dir (tree, loaded_menu_files, path, where);
-
-  g_free (path);
-}
-
-static void
-resolve_merge_file (GMenuTree      *tree,
-                   GHashTable     *loaded_menu_files,
-                    MenuLayoutNode *layout)
-{
-  char *filename;
-
-  if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT)
-    {
-      if (load_parent_merge_file (tree, loaded_menu_files, layout))
-        return;
-    }
-
-  filename = menu_layout_node_get_content_as_path (layout);
-  if (filename == NULL)
-    {
-      menu_verbose ("didn't get node content as a path, not merging file\n");
-    }
-  else
-    {
-      load_merge_file (tree, loaded_menu_files, filename, FALSE, TRUE, layout);
-
-      g_free (filename);
-    }
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static void
-resolve_merge_dir (GMenuTree      *tree,
-                  GHashTable     *loaded_menu_files,
-                   MenuLayoutNode *layout)
-{
-  char *path;
-
-  path = menu_layout_node_get_content_as_path (layout);
-  if (path == NULL)
-    {
-      menu_verbose ("didn't get layout node content as a path, not merging dir\n");
-    }
-  else
-    {
-      load_merge_dir (tree, loaded_menu_files, path, layout);
-
-      g_free (path);
-    }
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static MenuLayoutNode *
-add_app_dir (GMenuTree      *tree,
-             MenuLayoutNode *before,
-             const char     *data_dir)
-{
-  MenuLayoutNode *tmp;
-  char           *dirname;
-
-  tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR);
-  dirname = g_build_filename (data_dir, "applications", NULL);
-  menu_layout_node_set_content (tmp, dirname);
-  menu_layout_node_insert_before (before, tmp);
-  menu_layout_node_unref (before);
-
-  menu_verbose ("Adding <AppDir>%s</AppDir> in <DefaultAppDirs/>\n",
-                dirname);
-
-  g_free (dirname);
-
-  return tmp;
-}
-
-static void
-resolve_default_app_dirs (GMenuTree      *tree,
-                          MenuLayoutNode *layout)
-{
-  MenuLayoutNode     *before;
-  const char * const *system_data_dirs;
-  int                 i;
-
-  system_data_dirs = g_get_system_data_dirs ();
-
-  before = add_app_dir (tree,
-                       menu_layout_node_ref (layout),
-                       g_get_user_data_dir ());
-
-  i = 0;
-  while (system_data_dirs[i] != NULL)
-    {
-      before = add_app_dir (tree, before, system_data_dirs[i]);
-
-      ++i;
-    }
-
-  menu_layout_node_unref (before);
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static MenuLayoutNode *
-add_directory_dir (GMenuTree      *tree,
-                   MenuLayoutNode *before,
-                   const char     *data_dir)
-{
-  MenuLayoutNode *tmp;
-  char           *dirname;
-
-  tmp = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY_DIR);
-  dirname = g_build_filename (data_dir, "desktop-directories", NULL);
-  menu_layout_node_set_content (tmp, dirname);
-  menu_layout_node_insert_before (before, tmp);
-  menu_layout_node_unref (before);
-
-  menu_verbose ("Adding <DirectoryDir>%s</DirectoryDir> in <DefaultDirectoryDirs/>\n",
-                dirname);
-
-  g_free (dirname);
-
-  return tmp;
-}
-
-static void
-resolve_default_directory_dirs (GMenuTree      *tree,
-                                MenuLayoutNode *layout)
-{
-  MenuLayoutNode     *before;
-  const char * const *system_data_dirs;
-  int                 i;
-
-  system_data_dirs = g_get_system_data_dirs ();
-
-  before = add_directory_dir (tree,
-                             menu_layout_node_ref (layout),
-                             g_get_user_data_dir ());
-
-  i = 0;
-  while (system_data_dirs[i] != NULL)
-    {
-      before = add_directory_dir (tree, before, system_data_dirs[i]);
-
-      ++i;
-    }
-
-  menu_layout_node_unref (before);
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static void
-resolve_default_merge_dirs (GMenuTree      *tree,
-                           GHashTable     *loaded_menu_files,
-                            MenuLayoutNode *layout)
-{
-  MenuLayoutNode     *root;
-  const char         *menu_name;
-  char               *merge_name;
-  const char * const *system_config_dirs;
-  int                 i;
-
-  root = menu_layout_node_get_root (layout);
-  menu_name = menu_layout_node_root_get_name (root);
-
-  merge_name = g_strconcat (menu_name, "-merged", NULL);
-
-  system_config_dirs = g_get_system_config_dirs ();
-
-  /* Merge in reverse order */
-  i = 0;
-  while (system_config_dirs[i] != NULL) i++;
-  while (i > 0)
-    {
-      i--;
-      load_merge_dir_with_config_dir (tree,
-                                     loaded_menu_files,
-                                      system_config_dirs[i],
-                                      merge_name,
-                                      layout);
-    }
-
-  load_merge_dir_with_config_dir (tree,
-                                 loaded_menu_files,
-                                  g_get_user_config_dir (),
-                                  merge_name,
-                                  layout);
-
-  g_free (merge_name);
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static void
-add_filename_include (const char     *desktop_file_id,
-                      DesktopEntry   *entry,
-                      MenuLayoutNode *include)
-{
-  if (!desktop_entry_has_categories (entry))
-    {
-      MenuLayoutNode *node;
-
-      node = menu_layout_node_new (MENU_LAYOUT_NODE_FILENAME);
-      menu_layout_node_set_content (node, desktop_file_id);
-
-      menu_layout_node_append_child (include, node);
-      menu_layout_node_unref (node);
-    }
-}
-
-static void
-is_dot_directory (const char   *basename,
-                 DesktopEntry *entry,
-                 gboolean     *has_dot_directory)
-{
-  if (!strcmp (basename, ".directory"))
-    *has_dot_directory = TRUE;
-}
-
-static gboolean
-add_menu_for_legacy_dir (MenuLayoutNode *parent,
-                         const char     *legacy_dir,
-                        const char     *relative_path,
-                         const char     *legacy_prefix,
-                         const char     *menu_name)
-{
-  EntryDirectory  *ed;
-  DesktopEntrySet *desktop_entries;
-  DesktopEntrySet *directory_entries;
-  GSList          *subdirs;
-  gboolean         menu_added;
-  gboolean         has_dot_directory;
-
-  ed = entry_directory_new_legacy (DESKTOP_ENTRY_INVALID, legacy_dir, legacy_prefix);
-  if (!ed)
-    return FALSE;
-
-  subdirs = NULL;
-  desktop_entries   = desktop_entry_set_new ();
-  directory_entries = desktop_entry_set_new ();
-
-  entry_directory_get_flat_contents (ed,
-                                     desktop_entries,
-                                     directory_entries,
-                                     &subdirs);
-  entry_directory_unref (ed);
-
-  has_dot_directory = FALSE;
-  desktop_entry_set_foreach (directory_entries,
-                            (DesktopEntrySetForeachFunc) is_dot_directory,
-                            &has_dot_directory);
-  desktop_entry_set_unref (directory_entries);
-
-  menu_added = FALSE;
-  if (desktop_entry_set_get_count (desktop_entries) > 0 || subdirs)
-    {
-      MenuLayoutNode *menu;
-      MenuLayoutNode *node;
-      GString        *subdir_path;
-      GString        *subdir_relative;
-      GSList         *tmp;
-      int             legacy_dir_len;
-      int             relative_path_len;
-
-      menu = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
-      menu_layout_node_append_child (parent, menu);
-
-      menu_added = TRUE;
-
-      g_assert (menu_name != NULL);
-
-      node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
-      menu_layout_node_set_content (node, menu_name);
-      menu_layout_node_append_child (menu, node);
-      menu_layout_node_unref (node);
-
-      if (has_dot_directory)
-       {
-         node = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY);
-         if (relative_path != NULL)
-           {
-             char *directory_entry_path;
-
-             directory_entry_path = g_strdup_printf ("%s/.directory", relative_path);
-             menu_layout_node_set_content (node, directory_entry_path);
-             g_free (directory_entry_path);
-           }
-         else
-           {
-             menu_layout_node_set_content (node, ".directory");
-           }
-         menu_layout_node_append_child (menu, node);
-         menu_layout_node_unref (node);
-       }
-
-      if (desktop_entry_set_get_count (desktop_entries) > 0)
-       {
-         MenuLayoutNode *include;
-
-         include = menu_layout_node_new (MENU_LAYOUT_NODE_INCLUDE);
-         menu_layout_node_append_child (menu, include);
-
-         desktop_entry_set_foreach (desktop_entries,
-                                    (DesktopEntrySetForeachFunc) add_filename_include,
-                                    include);
-
-         menu_layout_node_unref (include);
-       }
-
-      subdir_path = g_string_new (legacy_dir);
-      legacy_dir_len = strlen (legacy_dir);
-
-      subdir_relative = g_string_new (relative_path);
-      relative_path_len = relative_path ? strlen (relative_path) : 0;
-
-      tmp = subdirs;
-      while (tmp != NULL)
-        {
-          const char *subdir = tmp->data;
-
-          g_string_append_c (subdir_path, G_DIR_SEPARATOR);
-          g_string_append (subdir_path, subdir);
-
-         if (relative_path_len)
-           {
-             g_string_append_c (subdir_relative, G_DIR_SEPARATOR);
-           }
-          g_string_append (subdir_relative, subdir);
-
-          add_menu_for_legacy_dir (menu,
-                                   subdir_path->str,
-                                  subdir_relative->str,
-                                   legacy_prefix,
-                                   subdir);
-
-          g_string_truncate (subdir_relative, relative_path_len);
-          g_string_truncate (subdir_path, legacy_dir_len);
-
-          tmp = tmp->next;
-        }
-
-      g_string_free (subdir_path, TRUE);
-      g_string_free (subdir_relative, TRUE);
-
-      menu_layout_node_unref (menu);
-    }
-
-  desktop_entry_set_unref (desktop_entries);
-
-  g_slist_foreach (subdirs, (GFunc) g_free, NULL);
-  g_slist_free (subdirs);
-
-  return menu_added;
-}
-
-static void
-resolve_legacy_dir (GMenuTree      *tree,
-                   GHashTable     *loaded_menu_files,
-                    MenuLayoutNode *legacy)
-{
-  MenuLayoutNode *to_merge;
-  MenuLayoutNode *menu;
-
-  to_merge = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
-
-  menu = menu_layout_node_get_parent (legacy);
-  g_assert (menu_layout_node_get_type (menu) == MENU_LAYOUT_NODE_MENU);
-
-  if (add_menu_for_legacy_dir (to_merge,
-                               menu_layout_node_get_content (legacy),
-                              NULL,
-                               menu_layout_node_legacy_dir_get_prefix (legacy),
-                               menu_layout_node_menu_get_name (menu)))
-    {
-      merge_resolved_children (tree, loaded_menu_files, legacy, to_merge);
-    }
-
-  menu_layout_node_unref (to_merge);
-}
-
-static MenuLayoutNode *
-add_legacy_dir (GMenuTree      *tree,
-               GHashTable     *loaded_menu_files,
-                MenuLayoutNode *before,
-                const char     *data_dir)
-{
-  MenuLayoutNode *legacy;
-  char           *dirname;
-
-  dirname = g_build_filename (data_dir, "applnk", NULL);
-
-  legacy = menu_layout_node_new (MENU_LAYOUT_NODE_LEGACY_DIR);
-  menu_layout_node_set_content (legacy, dirname);
-  menu_layout_node_legacy_dir_set_prefix (legacy, "kde");
-  menu_layout_node_insert_before (before, legacy);
-  menu_layout_node_unref (before);
-
-  menu_verbose ("Adding <LegacyDir>%s</LegacyDir> in <KDELegacyDirs/>\n",
-                dirname);
-
-  resolve_legacy_dir (tree, loaded_menu_files, legacy);
-
-  g_free (dirname);
-
-  return legacy;
-}
-
-static void
-resolve_kde_legacy_dirs (GMenuTree      *tree,
-                        GHashTable     *loaded_menu_files,
-                         MenuLayoutNode *layout)
-{
-  MenuLayoutNode     *before;
-  const char * const *system_data_dirs;
-  int                 i;
-
-  system_data_dirs = g_get_system_data_dirs ();
-
-  before = add_legacy_dir (tree,
-                          loaded_menu_files,
-                          menu_layout_node_ref (layout),
-                          g_get_user_data_dir ());
-
-  i = 0;
-  while (system_data_dirs[i] != NULL)
-    {
-      before = add_legacy_dir (tree, loaded_menu_files, before, system_data_dirs[i]);
-
-      ++i;
-    }
-
-  menu_layout_node_unref (before);
-
-  /* remove the now-replaced node */
-  menu_layout_node_unlink (layout);
-}
-
-static void
-gmenu_tree_resolve_files (GMenuTree      *tree,
-                         GHashTable     *loaded_menu_files,
-                         MenuLayoutNode *layout)
-{
-  MenuLayoutNode *child;
-
-  menu_verbose ("Resolving files in: ");
-  menu_debug_print_layout (layout, TRUE);
-
-  switch (menu_layout_node_get_type (layout))
-    {
-    case MENU_LAYOUT_NODE_MERGE_FILE:
-      resolve_merge_file (tree, loaded_menu_files, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_MERGE_DIR:
-      resolve_merge_dir (tree, loaded_menu_files, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
-      resolve_default_app_dirs (tree, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
-      resolve_default_directory_dirs (tree, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
-      resolve_default_merge_dirs (tree, loaded_menu_files, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_LEGACY_DIR:
-      resolve_legacy_dir (tree, loaded_menu_files, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
-      resolve_kde_legacy_dirs (tree, loaded_menu_files, layout);
-      break;
-
-    case MENU_LAYOUT_NODE_PASSTHROUGH:
-      /* Just get rid of these, we don't need the memory usage */
-      menu_layout_node_unlink (layout);
-      break;
-
-    default:
-      /* Recurse */
-      child = menu_layout_node_get_children (layout);
-      while (child != NULL)
-        {
-          MenuLayoutNode *next = menu_layout_node_get_next (child);
-
-          gmenu_tree_resolve_files (tree, loaded_menu_files, child);
-
-          child = next;
-        }
-      break;
-    }
-}
-
-static void
-move_children (MenuLayoutNode *from,
-               MenuLayoutNode *to)
-{
-  MenuLayoutNode *from_child;
-  MenuLayoutNode *insert_before;
-
-  insert_before = menu_layout_node_get_children (to);
-  from_child    = menu_layout_node_get_children (from);
-
-  while (from_child != NULL)
-    {
-      MenuLayoutNode *next;
-
-      next = menu_layout_node_get_next (from_child);
-
-      menu_layout_node_steal (from_child);
-
-      if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME)
-        {
-          ; /* just drop the Name in the old <Menu> */
-        }
-      else if (insert_before)
-        {
-          menu_layout_node_insert_before (insert_before, from_child);
-          g_assert (menu_layout_node_get_next (from_child) == insert_before);
-        }
-      else
-        {
-          menu_layout_node_append_child (to, from_child);
-        }
-
-      menu_layout_node_unref (from_child);
-
-      from_child = next;
-    }
-}
-
-static int
-null_safe_strcmp (const char *a,
-                  const char *b)
-{
-  if (a == NULL && b == NULL)
-    return 0;
-  else if (a == NULL)
-    return -1;
-  else if (b == NULL)
-    return 1;
-  else
-    return strcmp (a, b);
-}
-
-static int
-node_compare_func (const void *a,
-                   const void *b)
-{
-  MenuLayoutNode *node_a = (MenuLayoutNode*) a;
-  MenuLayoutNode *node_b = (MenuLayoutNode*) b;
-  MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a);
-  MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b);
-
-  if (t_a < t_b)
-    return -1;
-  else if (t_a > t_b)
-    return 1;
-  else
-    {
-      const char *c_a = menu_layout_node_get_content (node_a);
-      const char *c_b = menu_layout_node_get_content (node_b);
-
-      return null_safe_strcmp (c_a, c_b);
-    }
-}
-
-static int
-node_menu_compare_func (const void *a,
-                        const void *b)
-{
-  MenuLayoutNode *node_a = (MenuLayoutNode*) a;
-  MenuLayoutNode *node_b = (MenuLayoutNode*) b;
-  MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a);
-  MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b);
-
-  if (parent_a < parent_b)
-    return -1;
-  else if (parent_a > parent_b)
-    return 1;
-  else
-    return null_safe_strcmp (menu_layout_node_menu_get_name (node_a),
-                             menu_layout_node_menu_get_name (node_b));
-}
-
-static void
-gmenu_tree_strip_duplicate_children (GMenuTree      *tree,
-                                    MenuLayoutNode *layout)
-{
-  MenuLayoutNode *child;
-  GSList         *simple_nodes;
-  GSList         *menu_layout_nodes;
-  GSList         *prev;
-  GSList         *tmp;
-
-  /* to strip dups, we find all the child nodes where
-   * we want to kill dups, sort them,
-   * then nuke the adjacent nodes that are equal
-   */
-
-  simple_nodes = NULL;
-  menu_layout_nodes = NULL;
-
-  child = menu_layout_node_get_children (layout);
-  while (child != NULL)
-    {
-      switch (menu_layout_node_get_type (child))
-        {
-          /* These are dups if their content is the same */
-        case MENU_LAYOUT_NODE_APP_DIR:
-        case MENU_LAYOUT_NODE_DIRECTORY_DIR:
-        case MENU_LAYOUT_NODE_DIRECTORY:
-          simple_nodes = g_slist_prepend (simple_nodes, child);
-          break;
-
-          /* These have to be merged in a more complicated way,
-           * and then recursed
-           */
-        case MENU_LAYOUT_NODE_MENU:
-          menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child);
-          break;
-
-        default:
-          break;
-        }
-
-      child = menu_layout_node_get_next (child);
-    }
-
-  /* Note that the lists are all backward. So we want to keep
-   * the items that are earlier in the list, because they were
-   * later in the file
-   */
-
-  /* stable sort the simple nodes */
-  simple_nodes = g_slist_sort (simple_nodes,
-                               node_compare_func);
-
-  prev = NULL;
-  tmp = simple_nodes;
-  while (tmp != NULL)
-    {
-      GSList *next = tmp->next;
-
-      if (prev)
-        {
-          MenuLayoutNode *p = prev->data;
-          MenuLayoutNode *n = tmp->data;
-
-          if (node_compare_func (p, n) == 0)
-            {
-              /* nuke it! */
-              menu_layout_node_unlink (n);
-             simple_nodes = g_slist_delete_link (simple_nodes, tmp);
-             tmp = prev;
-            }
-        }
-
-      prev = tmp;
-      tmp = next;
-    }
-
-  g_slist_free (simple_nodes);
-  simple_nodes = NULL;
-
-  /* stable sort the menu nodes (the sort includes the
-   * parents of the nodes in the comparison). Remember
-   * the list is backward.
-   */
-  menu_layout_nodes = g_slist_sort (menu_layout_nodes,
-                                   node_menu_compare_func);
-
-  prev = NULL;
-  tmp = menu_layout_nodes;
-  while (tmp != NULL)
-    {
-      GSList *next = tmp->next;
-
-      if (prev)
-        {
-          MenuLayoutNode *p = prev->data;
-          MenuLayoutNode *n = tmp->data;
-
-          if (node_menu_compare_func (p, n) == 0)
-            {
-              /* Move children of first menu to the start of second
-               * menu and nuke the first menu
-               */
-              move_children (n, p);
-              menu_layout_node_unlink (n);
-             menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp);
-             tmp = prev;
-            }
-        }
-
-      prev = tmp;
-      tmp = next;
-    }
-
-  g_slist_free (menu_layout_nodes);
-  menu_layout_nodes = NULL;
-
-  /* Recursively clean up all children */
-  child = menu_layout_node_get_children (layout);
-  while (child != NULL)
-    {
-      if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU)
-        gmenu_tree_strip_duplicate_children (tree, child);
-
-      child = menu_layout_node_get_next (child);
-    }
-}
-
-static MenuLayoutNode *
-find_submenu (MenuLayoutNode *layout,
-              const char     *path,
-              gboolean        create_if_not_found)
-{
-  MenuLayoutNode *child;
-  const char     *slash;
-  const char     *next_path;
-  char           *name;
-
-  menu_verbose (" (splitting \"%s\")\n", path);
-
-  if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR)
-    return NULL;
-
-  slash = strchr (path, G_DIR_SEPARATOR);
-  if (slash != NULL)
-    {
-      name = g_strndup (path, slash - path);
-      next_path = slash + 1;
-      if (*next_path == '\0')
-        next_path = NULL;
-    }
-  else
-    {
-      name = g_strdup (path);
-      next_path = NULL;
-    }
-
-  child = menu_layout_node_get_children (layout);
-  while (child != NULL)
-    {
-      switch (menu_layout_node_get_type (child))
-        {
-        case MENU_LAYOUT_NODE_MENU:
-          {
-            if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0)
-              {
-                menu_verbose ("MenuNode %p found for path component \"%s\"\n",
-                              child, name);
-
-                g_free (name);
-
-                if (!next_path)
-                  {
-                    menu_verbose (" Found menu node %p parent is %p\n",
-                                  child, layout);
-                    return child;
-                  }
-
-                return find_submenu (child, next_path, create_if_not_found);
-              }
-          }
-          break;
-
-        default:
-          break;
-        }
-
-      child = menu_layout_node_get_next (child);
-    }
-
-  if (create_if_not_found)
-    {
-      MenuLayoutNode *name_node;
-
-      child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
-      menu_layout_node_append_child (layout, child);
-
-      name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
-      menu_layout_node_set_content (name_node, name);
-      menu_layout_node_append_child (child, name_node);
-      menu_layout_node_unref (name_node);
-
-      menu_verbose (" Created menu node %p parent is %p\n",
-                    child, layout);
-
-      menu_layout_node_unref (child);
-      g_free (name);
-
-      if (!next_path)
-        return child;
-
-      return find_submenu (child, next_path, create_if_not_found);
-    }
-  else
-    {
-      g_free (name);
-      return NULL;
-    }
-}
-
-/* To call this you first have to strip duplicate children once,
- * otherwise when you move a menu Foo to Bar then you may only
- * move one of Foo, not all the merged Foo.
- */
-static void
-gmenu_tree_execute_moves (GMenuTree      *tree,
-                         MenuLayoutNode *layout,
-                         gboolean       *need_remove_dups_p)
-{
-  MenuLayoutNode *child;
-  gboolean        need_remove_dups;
-  GSList         *move_nodes;
-  GSList         *tmp;
-
-  need_remove_dups = FALSE;
-
-  move_nodes = NULL;
-
-  child = menu_layout_node_get_children (layout);
-  while (child != NULL)
-    {
-      switch (menu_layout_node_get_type (child))
-        {
-        case MENU_LAYOUT_NODE_MENU:
-          /* Recurse - we recurse first and process the current node
-           * second, as the spec dictates.
-           */
-          gmenu_tree_execute_moves (tree, child, &need_remove_dups);
-          break;
-
-        case MENU_LAYOUT_NODE_MOVE:
-          move_nodes = g_slist_prepend (move_nodes, child);
-          break;
-
-        default:
-          break;
-        }
-
-      child = menu_layout_node_get_next (child);
-    }
-
-  /* We need to execute the move operations in the order that they appear */
-  move_nodes = g_slist_reverse (move_nodes);
-
-  tmp = move_nodes;
-  while (tmp != NULL)
-    {
-      MenuLayoutNode *move_node = tmp->data;
-      MenuLayoutNode *old_node;
-      GSList         *next = tmp->next;
-      const char     *old;
-      const char     *new;
-
-      old = menu_layout_node_move_get_old (move_node);
-      new = menu_layout_node_move_get_new (move_node);
-      g_assert (old != NULL && new != NULL);
-
-      menu_verbose ("executing <Move> old = \"%s\" new = \"%s\"\n",
-                    old, new);
-
-      old_node = find_submenu (layout, old, FALSE);
-      if (old_node != NULL)
-        {
-          MenuLayoutNode *new_node;
-
-          /* here we can create duplicates anywhere below the
-           * node
-           */
-          need_remove_dups = TRUE;
-
-          /* look up new node creating it and its parents if
-           * required
-           */
-          new_node = find_submenu (layout, new, TRUE);
-          g_assert (new_node != NULL);
-
-          move_children (old_node, new_node);
-
-          menu_layout_node_unlink (old_node);
-        }
-
-      menu_layout_node_unlink (move_node);
-
-      tmp = next;
-    }
-
-  g_slist_free (move_nodes);
-
-  /* This oddness is to ensure we only remove dups once,
-   * at the root, instead of recursing the tree over
-   * and over.
-   */
-  if (need_remove_dups_p)
-    *need_remove_dups_p = need_remove_dups;
-  else if (need_remove_dups)
-    gmenu_tree_strip_duplicate_children (tree, layout);
-}
-
-static void
-gmenu_tree_load_layout (GMenuTree *tree)
-{
-  GHashTable *loaded_menu_files;
-  GError     *error;
-
-  if (tree->layout)
-    return;
-
-  if (!gmenu_tree_canonicalize_path (tree))
-    return;
-
-  menu_verbose ("Loading menu layout from \"%s\"\n",
-                tree->canonical_path);
-
-  error = NULL;
-  tree->layout = menu_layout_load (tree->canonical_path,
-                                   tree->type == GMENU_TREE_BASENAME ?
-                                        tree->basename : NULL,
-                                   &error);
-  if (tree->layout == NULL)
-    {
-      g_warning ("Error loading menu layout from \"%s\": %s",
-                 tree->canonical_path, error->message);
-      g_error_free (error);
-      return;
-    }
-
-  loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
-  g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE));
-  gmenu_tree_resolve_files (tree, loaded_menu_files, tree->layout);
-  g_hash_table_destroy (loaded_menu_files);
-
-  gmenu_tree_strip_duplicate_children (tree, tree->layout);
-  gmenu_tree_execute_moves (tree, tree->layout, NULL);
-}
-
-static void
-gmenu_tree_force_reload (GMenuTree *tree)
-{
-  gmenu_tree_force_rebuild (tree);
-
-  if (tree->layout)
-    menu_layout_node_unref (tree->layout);
-  tree->layout = NULL;
-}
-
-typedef struct
-{
-  DesktopEntrySet *set;
-  const char      *category;
-} GetByCategoryForeachData;
-
-static void
-get_by_category_foreach (const char               *file_id,
-                        DesktopEntry             *entry,
-                        GetByCategoryForeachData *data)
-{
-  if (desktop_entry_has_category (entry, data->category))
-    desktop_entry_set_add_entry (data->set, entry, file_id);
-}
-
-static void
-get_by_category (DesktopEntrySet *entry_pool,
-                DesktopEntrySet *set,
-                const char      *category)
-{
-  GetByCategoryForeachData data;
-
-  data.set      = set;
-  data.category = category;
-
-  desktop_entry_set_foreach (entry_pool,
-                            (DesktopEntrySetForeachFunc) get_by_category_foreach,
-                            &data);
-}
-
-static DesktopEntrySet *
-process_include_rules (MenuLayoutNode  *layout,
-                      DesktopEntrySet *entry_pool)
-{
-  DesktopEntrySet *set = NULL;
-
-  switch (menu_layout_node_get_type (layout))
-    {
-    case MENU_LAYOUT_NODE_AND:
-      {
-        MenuLayoutNode *child;
-
-       menu_verbose ("Processing <And>\n");
-
-        child = menu_layout_node_get_children (layout);
-        while (child != NULL)
-          {
-            DesktopEntrySet *child_set;
-
-            child_set = process_include_rules (child, entry_pool);
-
-            if (set == NULL)
-              {
-                set = child_set;
-              }
-            else
-              {
-                desktop_entry_set_intersection (set, child_set);
-                desktop_entry_set_unref (child_set);
-              }
-
-            /* as soon as we get empty results, we can bail,
-             * because it's an AND
-             */
-            if (desktop_entry_set_get_count (set) == 0)
-              break;
-
-            child = menu_layout_node_get_next (child);
-          }
-       menu_verbose ("Processed <And>\n");
-      }
-      break;
-
-    case MENU_LAYOUT_NODE_OR:
-      {
-        MenuLayoutNode *child;
-
-       menu_verbose ("Processing <Or>\n");
-
-        child = menu_layout_node_get_children (layout);
-        while (child != NULL)
-          {
-            DesktopEntrySet *child_set;
-
-            child_set = process_include_rules (child, entry_pool);
-
-            if (set == NULL)
-              {
-                set = child_set;
-              }
-            else
-              {
-                desktop_entry_set_union (set, child_set);
-                desktop_entry_set_unref (child_set);
-              }
-
-            child = menu_layout_node_get_next (child);
-          }
-       menu_verbose ("Processed <Or>\n");
-      }
-      break;
-
-    case MENU_LAYOUT_NODE_NOT:
-      {
-        /* First get the OR of all the rules */
-        MenuLayoutNode *child;
-
-       menu_verbose ("Processing <Not>\n");
-
-        child = menu_layout_node_get_children (layout);
-        while (child != NULL)
-          {
-            DesktopEntrySet *child_set;
-
-            child_set = process_include_rules (child, entry_pool);
-
-            if (set == NULL)
-              {
-                set = child_set;
-              }
-            else
-              {
-                desktop_entry_set_union (set, child_set);
-                desktop_entry_set_unref (child_set);
-              }
-
-            child = menu_layout_node_get_next (child);
-          }
-
-        if (set != NULL)
-          {
-           DesktopEntrySet *inverted;
-
-           /* Now invert the result */
-           inverted = desktop_entry_set_new ();
-           desktop_entry_set_union (inverted, entry_pool);
-           desktop_entry_set_subtract (inverted, set);
-           desktop_entry_set_unref (set);
-           set = inverted;
-          }
-       menu_verbose ("Processed <Not>\n");
-      }
-      break;
-
-    case MENU_LAYOUT_NODE_ALL:
-      menu_verbose ("Processing <All>\n");
-      set = desktop_entry_set_new ();
-      desktop_entry_set_union (set, entry_pool);
-      menu_verbose ("Processed <All>\n");
-      break;
-
-    case MENU_LAYOUT_NODE_FILENAME:
-      {
-        DesktopEntry *entry;
-
-       menu_verbose ("Processing <Filename>%s</Filename>\n",
-                     menu_layout_node_get_content (layout));
-
-        entry = desktop_entry_set_lookup (entry_pool,
-                                         menu_layout_node_get_content (layout));
-        if (entry != NULL)
-          {
-            set = desktop_entry_set_new ();
-            desktop_entry_set_add_entry (set,
-                                         entry,
-                                         menu_layout_node_get_content (layout));
-          }
-       menu_verbose ("Processed <Filename>%s</Filename>\n",
-                     menu_layout_node_get_content (layout));
-      }
-      break;
-
-    case MENU_LAYOUT_NODE_CATEGORY:
-      menu_verbose ("Processing <Category>%s</Category>\n",
-                   menu_layout_node_get_content (layout));
-      set = desktop_entry_set_new ();
-      get_by_category (entry_pool, set, menu_layout_node_get_content (layout));
-      menu_verbose ("Processed <Category>%s</Category>\n",
-                   menu_layout_node_get_content (layout));
-      break;
-
-    default:
-      break;
-    }
-
-  if (set == NULL)
-    set = desktop_entry_set_new (); /* create an empty set */
-
-  menu_verbose ("Matched %d entries\n", desktop_entry_set_get_count (set));
-
-  return set;
-}
-
-static void
-collect_layout_info (MenuLayoutNode  *layout,
-                    GSList         **layout_info)
-{
-  MenuLayoutNode *iter;
-
-  g_slist_foreach (*layout_info,
-                  (GFunc) menu_layout_node_unref,
-                  NULL);
-  g_slist_free (*layout_info);
-  *layout_info = NULL;
-
-  iter = menu_layout_node_get_children (layout);
-  while (iter != NULL)
-    {
-      switch (menu_layout_node_get_type (iter))
-       {
-       case MENU_LAYOUT_NODE_MENUNAME: 
-       case MENU_LAYOUT_NODE_FILENAME: 
-       case MENU_LAYOUT_NODE_SEPARATOR: 
-       case MENU_LAYOUT_NODE_MERGE: 
-         *layout_info = g_slist_prepend (*layout_info,
-                                         menu_layout_node_ref (iter));
-         break;
-
-       default:
-         break;
-       }
-
-      iter = menu_layout_node_get_next (iter);
-    }
-
-  *layout_info = g_slist_reverse (*layout_info);
-}
-
-static void
-entries_listify_foreach (const char         *desktop_file_id,
-                         DesktopEntry       *desktop_entry,
-                         GMenuTreeDirectory *directory)
-{
-  directory->entries =
-    g_slist_prepend (directory->entries,
-                    gmenu_tree_entry_new (directory,
-                                           desktop_entry,
-                                           desktop_file_id,
-                                           FALSE,
-                                           desktop_entry_get_no_display (desktop_entry)));
-}
-
-static void
-excluded_entries_listify_foreach (const char         *desktop_file_id,
-                                 DesktopEntry       *desktop_entry,
-                                 GMenuTreeDirectory *directory)
-{
-  directory->entries =
-    g_slist_prepend (directory->entries,
-                    gmenu_tree_entry_new (directory,
-                                          desktop_entry,
-                                          desktop_file_id,
-                                          TRUE,
-                                           desktop_entry_get_no_display (desktop_entry)));
-}
-
-static void
-set_default_layout_values (GMenuTreeDirectory *parent,
-                           GMenuTreeDirectory *child)
-{
-  GSList *tmp;
-
-  /* if the child has a defined default layout, we don't want to override its
-   * values. The parent might have a non-defined layout info (ie, no child of
-   * the DefaultLayout node) but it doesn't meant the default layout values
-   * (ie, DefaultLayout attributes) aren't different from the global defaults.
-   */
-  if (child->default_layout_info != NULL ||
-      child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE)
-    return;
-
-  child->default_layout_values = parent->default_layout_values;
-
-  tmp = child->subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-
-      set_default_layout_values (child, subdir);
-
-      tmp = tmp->next;
-   }
-}
-
-static GMenuTreeDirectory *
-process_layout (GMenuTree          *tree,
-                GMenuTreeDirectory *parent,
-                MenuLayoutNode     *layout,
-                DesktopEntrySet    *allocated)
-{
-  MenuLayoutNode     *layout_iter;
-  GMenuTreeDirectory *directory;
-  DesktopEntrySet    *entry_pool;
-  DesktopEntrySet    *entries;
-  DesktopEntrySet    *allocated_set;
-  DesktopEntrySet    *excluded_set;
-  gboolean            deleted;
-  gboolean            only_unallocated;
-  GSList             *tmp;
-
-  g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU);
-  g_assert (menu_layout_node_menu_get_name (layout) != NULL);
-
-  directory = gmenu_tree_directory_new (parent,
-                                       menu_layout_node_menu_get_name (layout),
-                                       parent == NULL);
-
-  menu_verbose ("=== Menu name = %s ===\n", directory->name);
-
-
-  deleted = FALSE;
-  only_unallocated = FALSE;
-
-  entries = desktop_entry_set_new ();
-  allocated_set = desktop_entry_set_new ();
-
-  if (tree->flags & GMENU_TREE_FLAGS_INCLUDE_EXCLUDED)
-    excluded_set = desktop_entry_set_new ();
-  else
-    excluded_set = NULL;
-
-  entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout));
-
-  layout_iter = menu_layout_node_get_children (layout);
-  while (layout_iter != NULL)
-    {
-      switch (menu_layout_node_get_type (layout_iter))
-        {
-        case MENU_LAYOUT_NODE_MENU:
-          /* recurse */
-          {
-            GMenuTreeDirectory *child_dir;
-
-           menu_verbose ("Processing <Menu>\n");
-
-            child_dir = process_layout (tree,
-                                        directory,
-                                        layout_iter,
-                                        allocated);
-            if (child_dir)
-              directory->subdirs = g_slist_prepend (directory->subdirs,
-                                                    child_dir);
-
-           menu_verbose ("Processed <Menu>\n");
-          }
-          break;
-
-        case MENU_LAYOUT_NODE_INCLUDE:
-          {
-            /* The match rule children of the <Include> are
-             * independent (logical OR) so we can process each one by
-             * itself
-             */
-            MenuLayoutNode *rule;
-
-           menu_verbose ("Processing <Include> (%d entries)\n",
-                         desktop_entry_set_get_count (entries));
-
-            rule = menu_layout_node_get_children (layout_iter);
-            while (rule != NULL)
-              {
-                DesktopEntrySet *rule_set;
-
-                rule_set = process_include_rules (rule, entry_pool);
-                if (rule_set != NULL)
-                  {
-                    desktop_entry_set_union (entries, rule_set);
-                    desktop_entry_set_union (allocated_set, rule_set);
-                   if (excluded_set != NULL)
-                     desktop_entry_set_subtract (excluded_set, rule_set);
-                    desktop_entry_set_unref (rule_set);
-                  }
-
-                rule = menu_layout_node_get_next (rule);
-              }
-
-           menu_verbose ("Processed <Include> (%d entries)\n",
-                         desktop_entry_set_get_count (entries));
-          }
-          break;
-
-        case MENU_LAYOUT_NODE_EXCLUDE:
-          {
-            /* The match rule children of the <Exclude> are
-             * independent (logical OR) so we can process each one by
-             * itself
-             */
-            MenuLayoutNode *rule;
-
-           menu_verbose ("Processing <Exclude> (%d entries)\n",
-                         desktop_entry_set_get_count (entries));
-
-            rule = menu_layout_node_get_children (layout_iter);
-            while (rule != NULL)
-              {
-                DesktopEntrySet *rule_set;
-
-                rule_set = process_include_rules (rule, entry_pool);
-                if (rule_set != NULL)
-                  {
-                   if (excluded_set != NULL)
-                     desktop_entry_set_union (excluded_set, rule_set);
-                   desktop_entry_set_subtract (entries, rule_set);
-                   desktop_entry_set_unref (rule_set);
-                  }
-
-                rule = menu_layout_node_get_next (rule);
-              }
-
-           menu_verbose ("Processed <Exclude> (%d entries)\n",
-                         desktop_entry_set_get_count (entries));
-          }
-          break;
-
-        case MENU_LAYOUT_NODE_DIRECTORY:
-          {
-            DesktopEntry *entry;
-
-           menu_verbose ("Processing <Directory>%s</Directory>\n",
-                         menu_layout_node_get_content (layout_iter));
-
-           /*
-             * The last <Directory> to exist wins, so we always try overwriting
-             */
-            entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout),
-                                                        menu_layout_node_get_content (layout_iter));
-
-            if (entry != NULL)
-              {
-                if (!desktop_entry_get_hidden (entry))
-                  {
-                    if (directory->directory_entry)
-                      desktop_entry_unref (directory->directory_entry);
-                    directory->directory_entry = entry; /* pass ref ownership */
-                  }
-                else
-                  {
-                    desktop_entry_unref (entry);
-                  }
-              }
-
-            menu_verbose ("Processed <Directory> new directory entry = %p (%s)\n",
-                          directory->directory_entry,
-                         directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null");
-          }
-          break;
-
-        case MENU_LAYOUT_NODE_DELETED:
-         menu_verbose ("Processed <Deleted/>\n");
-          deleted = TRUE;
-          break;
-
-        case MENU_LAYOUT_NODE_NOT_DELETED:
-         menu_verbose ("Processed <NotDeleted/>\n");
-          deleted = FALSE;
-          break;
-
-        case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
-         menu_verbose ("Processed <OnlyUnallocated/>\n");
-          only_unallocated = TRUE;
-          break;
-
-        case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
-         menu_verbose ("Processed <NotOnlyUnallocated/>\n");
-          only_unallocated = FALSE;
-          break;
-
-       case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
-         menu_layout_node_default_layout_get_values (layout_iter,
-                                                     &directory->default_layout_values);
-         collect_layout_info (layout_iter, &directory->default_layout_info);
-         menu_verbose ("Processed <DefaultLayout/>\n");
-         break;
-
-       case MENU_LAYOUT_NODE_LAYOUT:
-         collect_layout_info (layout_iter, &directory->layout_info);
-         menu_verbose ("Processed <Layout/>\n");
-         break;
-
-        default:
-          break;
-        }
-
-      layout_iter = menu_layout_node_get_next (layout_iter);
-    }
-
-  desktop_entry_set_unref (entry_pool);
-
-  directory->only_unallocated = only_unallocated;
-
-  if (!directory->only_unallocated)
-    desktop_entry_set_union (allocated, allocated_set);
-
-  desktop_entry_set_unref (allocated_set);
-
-  if (directory->directory_entry)
-    {
-      if (desktop_entry_get_no_display (directory->directory_entry))
-        {
-          directory->is_nodisplay = TRUE;
-
-          if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY))
-            {
-              menu_verbose ("Not showing menu %s because NoDisplay=true\n",
-                        desktop_entry_get_name (directory->directory_entry));
-              deleted = TRUE;
-            }
-        }
-
-      if (!desktop_entry_get_show_in_gnome (directory->directory_entry))
-        {
-          menu_verbose ("Not showing menu %s because OnlyShowIn!=GNOME or NotShowIn=GNOME\n",
-                        desktop_entry_get_name (directory->directory_entry));
-          deleted = TRUE;
-        }
-    }
-
-  if (deleted)
-    {
-      if (excluded_set != NULL)
-       desktop_entry_set_unref (excluded_set);
-      desktop_entry_set_unref (entries);
-      gmenu_tree_item_unref (directory);
-      return NULL;
-    }
-
-  desktop_entry_set_foreach (entries,
-                             (DesktopEntrySetForeachFunc) entries_listify_foreach,
-                             directory);
-  desktop_entry_set_unref (entries);
-
-  if (excluded_set != NULL)
-    {
-      desktop_entry_set_foreach (excluded_set,
-                                (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach,
-                                directory);
-      desktop_entry_set_unref (excluded_set);
-    }
-
-  tmp = directory->subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-
-      set_default_layout_values (directory, subdir);
-
-      tmp = tmp->next;
-   }
-
-  tmp = directory->entries;
-  while (tmp != NULL)
-    {
-      GMenuTreeEntry *entry = tmp->data;
-      GSList         *next  = tmp->next;
-      gboolean        delete = FALSE;
-
-      if (desktop_entry_get_hidden (entry->desktop_entry))
-        {
-          menu_verbose ("Deleting %s because Hidden=true\n",
-                        desktop_entry_get_name (entry->desktop_entry));
-          delete = TRUE;
-        }
-
-      if (!(tree->flags & GMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
-          desktop_entry_get_no_display (entry->desktop_entry))
-        {
-          menu_verbose ("Deleting %s because NoDisplay=true\n",
-                        desktop_entry_get_name (entry->desktop_entry));
-          delete = TRUE;
-        }
-
-      if (!desktop_entry_get_show_in_gnome (entry->desktop_entry))
-        {
-          menu_verbose ("Deleting %s because OnlyShowIn!=GNOME or NotShowIn=GNOME\n",
-                        desktop_entry_get_name (entry->desktop_entry));
-          delete = TRUE;
-        }
-
-      if (desktop_entry_get_tryexec_failed (entry->desktop_entry))
-        {
-          menu_verbose ("Deleting %s because TryExec failed\n",
-                        desktop_entry_get_name (entry->desktop_entry));
-          delete = TRUE;
-        }
-
-      if (delete)
-        {
-          directory->entries = g_slist_delete_link (directory->entries,
-                                                   tmp);
-          gmenu_tree_item_unref_and_unset_parent (entry);
-        }
-
-      tmp = next;
-    }
-
-  g_assert (directory->name != NULL);
-
-  return directory;
-}
-
-static void
-process_only_unallocated (GMenuTree          *tree,
-                         GMenuTreeDirectory *directory,
-                         DesktopEntrySet    *allocated)
-{
-  GSList *tmp;
-
-  /* For any directory marked only_unallocated, we have to remove any
-   * entries that were in fact allocated.
-   */
-
-  if (directory->only_unallocated)
-    {
-      tmp = directory->entries;
-      while (tmp != NULL)
-        {
-          GMenuTreeEntry *entry = tmp->data;
-          GSList         *next  = tmp->next;
-
-          if (desktop_entry_set_lookup (allocated, entry->desktop_file_id))
-            {
-              directory->entries = g_slist_delete_link (directory->entries,
-                                                        tmp);
-              gmenu_tree_item_unref_and_unset_parent (entry);
-            }
-
-          tmp = next;
-        }
-    }
-
-  tmp = directory->subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-
-      process_only_unallocated (tree, subdir, allocated);
-
-      tmp = tmp->next;
-   }
-}
-
-static void preprocess_layout_info (GMenuTree          *tree,
-                                    GMenuTreeDirectory *directory);
-
-static GSList *
-get_layout_info (GMenuTreeDirectory *directory,
-                 gboolean           *is_default_layout)
-{
-  GMenuTreeDirectory *iter;
-
-  if (directory->layout_info != NULL)
-    {
-      if (is_default_layout)
-        {
-          *is_default_layout = FALSE;
-        }
-      return directory->layout_info;
-    }
-
-  /* Even if there's no layout information at all, the result will be an
-   * implicit default layout */
-  if (is_default_layout)
-    {
-      *is_default_layout = TRUE;
-    }
-
-  iter = directory;
-  while (iter != NULL)
-    {
-      /* FIXME: this is broken: we might skip real parent in the
-       * XML structure, that are hidden because of inlining. */
-      if (iter->default_layout_info != NULL)
-       {
-         return iter->default_layout_info;
-       }
-
-      iter = GMENU_TREE_ITEM (iter)->parent;
-    }
-
-  return NULL;
-}
-
-static void
-get_values_with_defaults (MenuLayoutNode   *node,
-                         MenuLayoutValues *layout_values,
-                         MenuLayoutValues *default_layout_values)
-{
-  menu_layout_node_menuname_get_values (node, layout_values);
-
-  if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY))
-    layout_values->show_empty = default_layout_values->show_empty;
-
-  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS))
-    layout_values->inline_menus = default_layout_values->inline_menus;
-
-  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT))
-    layout_values->inline_limit = default_layout_values->inline_limit;
-
-  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER))
-    layout_values->inline_header = default_layout_values->inline_header;
-
-  if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS))
-    layout_values->inline_alias = default_layout_values->inline_alias;
-}
-
-static guint
-get_real_subdirs_len (GMenuTreeDirectory *directory)
-{
-  guint   len;
-  GSList *tmp;
-
-  len = 0;
-
-  tmp = directory->subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-
-      tmp = tmp->next;
-
-      if (subdir->will_inline_header != G_MAXUINT16)
-        {
-          len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1;
-        }
-      else
-        len += 1;
-    }
-
-  return len;
-}
-
-static void
-preprocess_layout_info_subdir_helper (GMenuTree          *tree,
-                                      GMenuTreeDirectory *directory,
-                                      GMenuTreeDirectory *subdir,
-                                      MenuLayoutValues   *layout_values,
-                                      gboolean           *contents_added,
-                                      gboolean           *should_remove)
-{
-  preprocess_layout_info (tree, subdir);
-
-  *should_remove = FALSE;
-  *contents_added = FALSE;
-
-  if (subdir->subdirs == NULL && subdir->entries == NULL)
-    {
-      if (!(tree->flags & GMENU_TREE_FLAGS_SHOW_EMPTY) &&
-          !layout_values->show_empty)
-       {
-         menu_verbose ("Not showing empty menu '%s'\n", subdir->name);
-         *should_remove = TRUE;
-       }
-    }
-
-  else if (layout_values->inline_menus)
-    {
-      guint real_subdirs_len;
-
-      real_subdirs_len = get_real_subdirs_len (subdir);
-
-      if (layout_values->inline_alias &&
-          real_subdirs_len + g_slist_length (subdir->entries) == 1)
-        {
-          GMenuTreeAlias *alias;
-          GMenuTreeItem  *item;
-          GSList         *list;
-
-          if (subdir->subdirs != NULL)
-            list = subdir->subdirs;
-          else
-            list = subdir->entries;
-
-          item = GMENU_TREE_ITEM (list->data);
-
-          menu_verbose ("Inline aliasing '%s' to '%s'\n",
-                        item->type == GMENU_TREE_ITEM_ENTRY ?
-                          gmenu_tree_entry_get_name (GMENU_TREE_ENTRY (item)) :
-                          (item->type == GMENU_TREE_ITEM_DIRECTORY ?
-                             gmenu_tree_directory_get_name (GMENU_TREE_DIRECTORY (item)) :
-                             gmenu_tree_directory_get_name (GMENU_TREE_ALIAS (item)->directory)),
-                        subdir->name);
-
-          alias = gmenu_tree_alias_new (directory, subdir, item);
-
-          g_slist_foreach (list,
-                           (GFunc) gmenu_tree_item_unref_and_unset_parent,
-                           NULL);
-          g_slist_free (list);
-          subdir->subdirs = NULL;
-          subdir->entries = NULL;
-
-          if (item->type == GMENU_TREE_ITEM_DIRECTORY)
-            directory->subdirs = g_slist_append (directory->subdirs, alias);
-          else
-            directory->entries = g_slist_append (directory->entries, alias);
-
-          *contents_added = TRUE;
-          *should_remove = TRUE;
-        }
-
-      else if (layout_values->inline_limit == 0 ||
-               layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries))
-        {
-          if (layout_values->inline_header)
-            {
-              menu_verbose ("Creating inline header with name '%s'\n", subdir->name);
-              /* we're limited to 16-bits to spare some memory; if the limit is
-               * higher than that (would be crazy), we just consider it's
-               * unlimited */
-              if (layout_values->inline_limit < G_MAXUINT16)
-                subdir->will_inline_header = layout_values->inline_limit;
-              else
-                subdir->will_inline_header = 0;
-            }
-          else
-            {
-              g_slist_foreach (subdir->subdirs,
-                               (GFunc) gmenu_tree_item_set_parent,
-                               directory);
-              directory->subdirs = g_slist_concat (directory->subdirs,
-                                                   subdir->subdirs);
-              subdir->subdirs = NULL;
-
-              g_slist_foreach (subdir->entries,
-                               (GFunc) gmenu_tree_item_set_parent,
-                               directory);
-              directory->entries = g_slist_concat (directory->entries,
-                                                   subdir->entries);
-              subdir->entries = NULL;
-
-              *contents_added = TRUE;
-              *should_remove = TRUE;
-            }
-
-          menu_verbose ("Inlining directory contents of '%s' to '%s'\n",
-                        subdir->name, directory->name);
-        }
-    }
-}
-
-static void
-preprocess_layout_info (GMenuTree          *tree,
-                        GMenuTreeDirectory *directory)
-{
-  GSList   *tmp;
-  GSList   *layout_info;
-  gboolean  using_default_layout;
-  GSList   *last_subdir;
-  gboolean  strip_duplicates;
-  gboolean  contents_added;
-  gboolean  should_remove;
-  GSList   *subdirs_sentinel;
-
-  /* Note: we need to preprocess all menus, even if the layout mask for a menu
-   * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus;
-   * and the layout mask can be different for a submenu anyway */
-
-  menu_verbose ("Processing menu layout inline hints for %s\n", directory->name);
-  g_assert (!directory->preprocessed);
-
-  strip_duplicates = FALSE;
-  /* we use last_subdir to track the last non-inlined subdirectory */
-  last_subdir = g_slist_last (directory->subdirs);
-
-  /*
-   * First process subdirectories with explicit layout
-   */
-  layout_info = get_layout_info (directory, &using_default_layout);
-  tmp = layout_info;
-  /* see comment below about Menuname to understand why we leave the loop if
-   * last_subdir is NULL */
-  while (tmp != NULL && last_subdir != NULL)
-    {
-      MenuLayoutNode     *node = tmp->data;
-      MenuLayoutValues    layout_values;
-      const char         *name;
-      GMenuTreeDirectory *subdir;
-      GSList             *subdir_l;
-
-      tmp = tmp->next;
-
-      /* only Menuname nodes are relevant here */
-      if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME)
-        continue;
-
-      get_values_with_defaults (node,
-                                &layout_values,
-                                &directory->default_layout_values);
-
-      /* find the subdirectory that is affected by those attributes */
-      name = menu_layout_node_get_content (node);
-      subdir = NULL;
-      subdir_l = directory->subdirs;
-      while (subdir_l != NULL)
-        {
-          subdir = subdir_l->data;
-
-          if (!strcmp (subdir->name, name))
-            break;
-
-          subdir = NULL;
-          subdir_l = subdir_l->next;
-
-          /* We do not want to use Menuname on a menu that appeared via
-           * inlining: without inlining, the Menuname wouldn't have matched
-           * anything, and we want to keep the same behavior.
-           * Unless the layout is a default layout, in which case the Menuname
-           * does match the subdirectory. */
-          if (!using_default_layout && subdir_l == last_subdir)
-            {
-              subdir_l = NULL;
-              break;
-            }
-        }
-
-      if (subdir == NULL)
-        continue;
-
-      preprocess_layout_info_subdir_helper (tree, directory,
-                                            subdir, &layout_values,
-                                            &contents_added, &should_remove);
-      strip_duplicates = strip_duplicates || contents_added;
-      if (should_remove)
-        {
-          if (last_subdir == subdir_l)
-            {
-              /* we need to recompute last_subdir since we'll remove it from
-               * the list */
-              GSList *buf;
-
-              if (subdir_l == directory->subdirs)
-                last_subdir = NULL;
-              else
-                {
-                  buf = directory->subdirs;
-                  while (buf != NULL && buf->next != subdir_l)
-                    buf = buf->next;
-                  last_subdir = buf;
-                }
-            }
-
-          directory->subdirs = g_slist_remove (directory->subdirs, subdir);
-          gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir));
-        }
-    }
-
-  /*
-   * Now process the subdirectories with no explicit layout
-   */
-  /* this is bogus data, but we just need the pointer anyway */
-  subdirs_sentinel = g_slist_prepend (directory->subdirs, (gpointer)PACKAGE);
-  directory->subdirs = subdirs_sentinel;
-
-  tmp = directory->subdirs;
-  while (tmp->next != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->next->data;
-
-      if (subdir->preprocessed)
-        {
-          tmp = tmp->next;
-          continue;
-        }
-
-      preprocess_layout_info_subdir_helper (tree, directory,
-                                            subdir, &directory->default_layout_values,
-                                            &contents_added, &should_remove);
-      strip_duplicates = strip_duplicates || contents_added;
-      if (should_remove)
-        {
-          tmp = g_slist_delete_link (tmp, tmp->next);
-          gmenu_tree_item_unref_and_unset_parent (GMENU_TREE_ITEM (subdir));
-        }
-      else
-        tmp = tmp->next;
-    }
-
-  /* remove the sentinel */
-  directory->subdirs = g_slist_delete_link (directory->subdirs,
-                                            directory->subdirs);
-
-  /*
-   * Finally, remove duplicates if needed
-   */
-  if (strip_duplicates)
-    {
-      /* strip duplicate entries; there should be no duplicate directories */
-      directory->entries = g_slist_sort (directory->entries,
-                                         (GCompareFunc) gmenu_tree_entry_compare_by_id);
-      tmp = directory->entries;
-      while (tmp != NULL && tmp->next != NULL)
-        {
-          GMenuTreeItem *a = tmp->data;
-          GMenuTreeItem *b = tmp->next->data;
-
-          if (a->type == GMENU_TREE_ITEM_ALIAS)
-            a = GMENU_TREE_ALIAS (a)->aliased_item;
-
-          if (b->type == GMENU_TREE_ITEM_ALIAS)
-            b = GMENU_TREE_ALIAS (b)->aliased_item;
-
-          if (strcmp (GMENU_TREE_ENTRY (a)->desktop_file_id,
-                      GMENU_TREE_ENTRY (b)->desktop_file_id) == 0)
-            {
-              tmp = g_slist_delete_link (tmp, tmp->next);
-              gmenu_tree_item_unref (b);
-            }
-          else
-            tmp = tmp->next;
-        }
-    }
-
-  directory->preprocessed = TRUE;
-}
-
-static void process_layout_info (GMenuTree          *tree,
-                                GMenuTreeDirectory *directory);
-
-static void
-check_pending_separator (GMenuTreeDirectory *directory)
-{
-  if (directory->layout_pending_separator)
-    {
-      menu_verbose ("Adding pending separator in '%s'\n", directory->name);
-
-      directory->contents = g_slist_append (directory->contents,
-                                           gmenu_tree_separator_new (directory));
-      directory->layout_pending_separator = FALSE;
-    }
-}
-
-static void
-merge_alias (GMenuTree          *tree,
-            GMenuTreeDirectory *directory,
-            GMenuTreeAlias     *alias)
-{
-  menu_verbose ("Merging alias '%s' in directory '%s'\n",
-               alias->directory->name, directory->name);
-
-  if (alias->aliased_item->type == GMENU_TREE_ITEM_DIRECTORY)
-    {
-      process_layout_info (tree, GMENU_TREE_DIRECTORY (alias->aliased_item));
-    }
-
-  check_pending_separator (directory);
-
-  directory->contents = g_slist_append (directory->contents,
-                                       gmenu_tree_item_ref (alias));
-}
-
-static void
-merge_subdir (GMenuTree          *tree,
-             GMenuTreeDirectory *directory,
-             GMenuTreeDirectory *subdir)
-{
-  menu_verbose ("Merging subdir '%s' in directory '%s'\n",
-               subdir->name, directory->name);
-
-  process_layout_info (tree, subdir);
-
-  check_pending_separator (directory);
-
-  if (subdir->will_inline_header == 0 ||
-      (subdir->will_inline_header != G_MAXUINT16 &&
-       g_slist_length (subdir->contents) <= subdir->will_inline_header))
-    {
-      GMenuTreeHeader *header;
-
-      header = gmenu_tree_header_new (directory, subdir);
-      directory->contents = g_slist_append (directory->contents, header);
-
-      g_slist_foreach (subdir->contents,
-                       (GFunc) gmenu_tree_item_set_parent,
-                       directory);
-      directory->contents = g_slist_concat (directory->contents,
-                                            subdir->contents);
-      subdir->contents = NULL;
-      subdir->will_inline_header = G_MAXUINT16;
-
-      gmenu_tree_item_set_parent (GMENU_TREE_ITEM (subdir), NULL);
-    }
-  else
-    {
-      directory->contents = g_slist_append (directory->contents,
-                                           gmenu_tree_item_ref (subdir));
-    }
-}
-
-static void
-merge_subdir_by_name (GMenuTree          *tree,
-                     GMenuTreeDirectory *directory,
-                     const char         *subdir_name)
-{
-  GSList *tmp;
-
-  menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n",
-               subdir_name, directory->name);
-
-  tmp = directory->subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-      GSList             *next = tmp->next;
-
-      /* if it's an alias, then it cannot be affected by
-       * the Merge nodes in the layout */
-      if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS)
-        continue;
-
-      if (!strcmp (subdir->name, subdir_name))
-       {
-         directory->subdirs = g_slist_delete_link (directory->subdirs, tmp);
-         merge_subdir (tree, directory, subdir);
-         gmenu_tree_item_unref (subdir);
-       }
-      
-      tmp = next;
-    }
-}
-
-static void
-merge_entry (GMenuTree          *tree,
-            GMenuTreeDirectory *directory,
-            GMenuTreeEntry     *entry)
-{
-  menu_verbose ("Merging entry '%s' in directory '%s'\n",
-               entry->desktop_file_id, directory->name);
-
-  check_pending_separator (directory);
-  directory->contents = g_slist_append (directory->contents,
-                                       gmenu_tree_item_ref (entry));
-}
-
-static void
-merge_entry_by_id (GMenuTree          *tree,
-                  GMenuTreeDirectory *directory,
-                  const char         *file_id)
-{
-  GSList *tmp;
-
-  menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n",
-               file_id, directory->name);
-
-  tmp = directory->entries;
-  while (tmp != NULL)
-    {
-      GMenuTreeEntry *entry = tmp->data;
-      GSList         *next = tmp->next;
-
-      /* if it's an alias, then it cannot be affected by
-       * the Merge nodes in the layout */
-      if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS)
-        continue;
-
-      if (!strcmp (entry->desktop_file_id, file_id))
-       {
-         directory->entries = g_slist_delete_link (directory->entries, tmp);
-         merge_entry (tree, directory, entry);
-         gmenu_tree_item_unref (entry);
-       }
-      
-      tmp = next;
-    }
-}
-
-static inline gboolean
-find_name_in_list (const char *name,
-                  GSList     *list)
-{
-  while (list != NULL)
-    {
-      if (!strcmp (name, list->data))
-       return TRUE;
-
-      list = list->next;
-    }
-
-  return FALSE;
-}
-
-static void
-merge_subdirs (GMenuTree          *tree,
-              GMenuTreeDirectory *directory,
-              GSList             *except)
-{
-  GSList *subdirs;
-  GSList *tmp;
-
-  menu_verbose ("Merging subdirs in directory '%s'\n", directory->name);
-
-  subdirs = directory->subdirs;
-  directory->subdirs = NULL;
-
-  subdirs = g_slist_sort_with_data (subdirs,
-                                   (GCompareDataFunc) gmenu_tree_item_compare,
-                                    GINT_TO_POINTER (GMENU_TREE_SORT_NAME));
-
-  tmp = subdirs;
-  while (tmp != NULL)
-    {
-      GMenuTreeDirectory *subdir = tmp->data;
-
-      if (GMENU_TREE_ITEM (subdir)->type == GMENU_TREE_ITEM_ALIAS)
-        {
-         merge_alias (tree, directory, GMENU_TREE_ALIAS (subdir));
-         gmenu_tree_item_unref (subdir);
-        }
-      else if (!find_name_in_list (subdir->name, except))
-       {
-         merge_subdir (tree, directory, subdir);
-         gmenu_tree_item_unref (subdir);
-       }
-      else
-       {
-         menu_verbose ("Not merging directory '%s' yet\n", subdir->name);
-         directory->subdirs = g_slist_append (directory->subdirs, subdir);
-       }
-
-      tmp = tmp->next;
-    }
-
-  g_slist_free (subdirs);
-  g_slist_free (except);
-}
-
-static void
-merge_entries (GMenuTree          *tree,
-              GMenuTreeDirectory *directory,
-              GSList             *except)
-{
-  GSList *entries;
-  GSList *tmp;
-
-  menu_verbose ("Merging entries in directory '%s'\n", directory->name);
-
-  entries = directory->entries;
-  directory->entries = NULL;
-
-  entries = g_slist_sort_with_data (entries,
-                                   (GCompareDataFunc) gmenu_tree_item_compare,
-                                   GINT_TO_POINTER (tree->sort_key));
-
-  tmp = entries;
-  while (tmp != NULL)
-    {
-      GMenuTreeEntry *entry = tmp->data;
-
-      if (GMENU_TREE_ITEM (entry)->type == GMENU_TREE_ITEM_ALIAS)
-        {
-         merge_alias (tree, directory, GMENU_TREE_ALIAS (entry));
-         gmenu_tree_item_unref (entry);
-        }
-      else if (!find_name_in_list (entry->desktop_file_id, except))
-       {
-         merge_entry (tree, directory, entry);
-         gmenu_tree_item_unref (entry);
-       }
-      else
-       {
-         menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id);
-         directory->entries = g_slist_append (directory->entries, entry);
-       }
-
-      tmp = tmp->next;
-    }
-
-  g_slist_free (entries);
-  g_slist_free (except);
-}
-
-static void
-merge_subdirs_and_entries (GMenuTree          *tree,
-                          GMenuTreeDirectory *directory,
-                          GSList             *except_subdirs,
-                          GSList             *except_entries)
-{
-  GSList *items;
-  GSList *tmp;
-
-  menu_verbose ("Merging subdirs and entries together in directory %s\n",
-               directory->name);
-
-  items = g_slist_concat (directory->subdirs, directory->entries);
-
-  directory->subdirs = NULL;
-  directory->entries = NULL;
-
-  items = g_slist_sort_with_data (items,
-                                 (GCompareDataFunc) gmenu_tree_item_compare,
-                                 GINT_TO_POINTER (tree->sort_key));
-
-  tmp = items;
-  while (tmp != NULL)
-    {
-      GMenuTreeItem     *item = tmp->data;
-      GMenuTreeItemType  type;
-
-      type = gmenu_tree_item_get_type (item);
-
-      if (type == GMENU_TREE_ITEM_ALIAS)
-        {
-          merge_alias (tree, directory, GMENU_TREE_ALIAS (item));
-          gmenu_tree_item_unref (item);
-        }
-      else if (type == GMENU_TREE_ITEM_DIRECTORY)
-       {
-         if (!find_name_in_list (GMENU_TREE_DIRECTORY (item)->name, except_subdirs))
-           {
-             merge_subdir (tree,
-                           directory,
-                           GMENU_TREE_DIRECTORY (item));
-             gmenu_tree_item_unref (item);
-           }
-         else
-           {
-             menu_verbose ("Not merging directory '%s' yet\n",
-                           GMENU_TREE_DIRECTORY (item)->name);
-             directory->subdirs = g_slist_append (directory->subdirs, item);
-           }
-       }
-      else if (type == GMENU_TREE_ITEM_ENTRY)
-       {
-         if (!find_name_in_list (GMENU_TREE_ENTRY (item)->desktop_file_id, except_entries))
-           {
-             merge_entry (tree, directory, GMENU_TREE_ENTRY (item));
-             gmenu_tree_item_unref (item);
-           }
-         else
-           {
-             menu_verbose ("Not merging entry '%s' yet\n",
-                           GMENU_TREE_ENTRY (item)->desktop_file_id);
-             directory->entries = g_slist_append (directory->entries, item);
-           }
-       }
-      else
-        {
-          g_assert_not_reached ();
-        }
-
-      tmp = tmp->next;
-    }
-
-  g_slist_free (items);
-  g_slist_free (except_subdirs);
-  g_slist_free (except_entries);
-}
-
-static GSList *
-get_subdirs_from_layout_info (GSList *layout_info)
-{
-  GSList *subdirs;
-  GSList *tmp;
-
-  subdirs = NULL;
-
-  tmp = layout_info;
-  while (tmp != NULL)
-    {
-      MenuLayoutNode *node = tmp->data;
-
-      if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME)
-       {
-         subdirs = g_slist_append (subdirs,
-                                   (char *) menu_layout_node_get_content (node));
-       }
-
-      tmp = tmp->next;
-    }
-
-  return subdirs;
-}
-
-static GSList *
-get_entries_from_layout_info (GSList *layout_info)
-{
-  GSList *entries;
-  GSList *tmp;
-
-  entries = NULL;
-
-  tmp = layout_info;
-  while (tmp != NULL)
-    {
-      MenuLayoutNode *node = tmp->data;
-
-      if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME)
-       {
-         entries = g_slist_append (entries,
-                                   (char *) menu_layout_node_get_content (node));
-       }
-
-      tmp = tmp->next;
-    }
-
-  return entries;
-}
-
-static void
-process_layout_info (GMenuTree          *tree,
-                    GMenuTreeDirectory *directory)
-{
-  GSList *layout_info;
-
-  menu_verbose ("Processing menu layout hints for %s\n", directory->name);
-
-  g_slist_foreach (directory->contents,
-                  (GFunc) gmenu_tree_item_unref_and_unset_parent,
-                  NULL);
-  g_slist_free (directory->contents);
-  directory->contents = NULL;
-  directory->layout_pending_separator = FALSE;
-
-  layout_info = get_layout_info (directory, NULL);
-
-  if (layout_info == NULL)
-    {
-      merge_subdirs (tree, directory, NULL);
-      merge_entries (tree, directory, NULL);
-    }
-  else
-    {
-      GSList *tmp;
-
-      tmp = layout_info;
-      while (tmp != NULL)
-       {
-         MenuLayoutNode *node = tmp->data;
-
-         switch (menu_layout_node_get_type (node))
-           {
-           case MENU_LAYOUT_NODE_MENUNAME:
-              merge_subdir_by_name (tree,
-                                    directory,
-                                    menu_layout_node_get_content (node));
-             break;
-
-           case MENU_LAYOUT_NODE_FILENAME: 
-             merge_entry_by_id (tree,
-                                directory,
-                                menu_layout_node_get_content (node));
-             break;
-
-           case MENU_LAYOUT_NODE_SEPARATOR:
-             /* Unless explicitly told to show all separators, do not show a
-              * separator at the beginning of a menu. Note that we don't add
-              * the separators now, and instead make it pending. This way, we
-              * won't show two consecutive separators nor will we show a
-              * separator at the end of a menu. */
-              if (tree->flags & GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS)
-               {
-                 directory->layout_pending_separator = TRUE;
-                 check_pending_separator (directory);
-               }
-             else if (directory->contents)
-               {
-                 menu_verbose ("Adding a potential separator in '%s'\n",
-                               directory->name);
-
-                 directory->layout_pending_separator = TRUE;
-               }
-             else
-               {
-                 menu_verbose ("Skipping separator at the beginning of '%s'\n",
-                               directory->name);
-               }
-             break;
-
-           case MENU_LAYOUT_NODE_MERGE:
-             switch (menu_layout_node_merge_get_type (node))
-               {
-               case MENU_LAYOUT_MERGE_NONE:
-                 break;
-
-               case MENU_LAYOUT_MERGE_MENUS:
-                 merge_subdirs (tree,
-                                directory,
-                                get_subdirs_from_layout_info (tmp->next));
-                 break;
-
-               case MENU_LAYOUT_MERGE_FILES:
-                 merge_entries (tree,
-                                directory,
-                                get_entries_from_layout_info (tmp->next));
-                 break;
-
-               case MENU_LAYOUT_MERGE_ALL:
-                 merge_subdirs_and_entries (tree,
-                                            directory,
-                                            get_subdirs_from_layout_info (tmp->next),
-                                            get_entries_from_layout_info (tmp->next));
-                 break;
-
-               default:
-                 g_assert_not_reached ();
-                 break;
-               }
-             break;
-
-           default:
-             g_assert_not_reached ();
-             break;
-           }
-
-         tmp = tmp->next;
-       }
-    }
-
-  g_slist_foreach (directory->subdirs,
-                  (GFunc) gmenu_tree_item_unref,
-                  NULL);
-  g_slist_free (directory->subdirs);
-  directory->subdirs = NULL;
-
-  g_slist_foreach (directory->entries,
-                  (GFunc) gmenu_tree_item_unref,
-                  NULL);
-  g_slist_free (directory->entries);
-  directory->entries = NULL;
-
-  g_slist_foreach (directory->default_layout_info,
-                  (GFunc) menu_layout_node_unref,
-                  NULL);
-  g_slist_free (directory->default_layout_info);
-  directory->default_layout_info = NULL;
-
-  g_slist_foreach (directory->layout_info,
-                  (GFunc) menu_layout_node_unref,
-                  NULL);
-  g_slist_free (directory->layout_info);
-  directory->layout_info = NULL;
-}
-
-static void
-handle_entries_changed (MenuLayoutNode *layout,
-                        GMenuTree       *tree)
-{
-  if (tree->layout == layout)
-    {
-      gmenu_tree_force_rebuild (tree);
-      gmenu_tree_invoke_monitors (tree);
-    }
-}
-
-static void
-gmenu_tree_build_from_layout (GMenuTree *tree)
-{
-  DesktopEntrySet *allocated;
-
-  if (tree->root)
-    return;
-
-  gmenu_tree_load_layout (tree);
-  if (!tree->layout)
-    return;
-
-  menu_verbose ("Building menu tree from layout\n");
-
-  allocated = desktop_entry_set_new ();
-
-  /* create the menu structure */
-  tree->root = process_layout (tree,
-                               NULL,
-                               find_menu_child (tree->layout),
-                               allocated);
-  if (tree->root)
-    {
-      gmenu_tree_directory_set_tree (tree->root, tree);
-
-      process_only_unallocated (tree, tree->root, allocated);
-
-      /* process the layout info part that can move/remove items:
-       * inline, show_empty, etc. */
-      preprocess_layout_info (tree, tree->root);
-      /* populate the menu structure that we got with the items, and order it
-       * according to the layout info */
-      process_layout_info (tree, tree->root);
-
-      menu_layout_node_root_add_entries_monitor (tree->layout,
-                                                 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
-                                                 tree);
-    }
-
-  desktop_entry_set_unref (allocated);
-}
-
-static void
-gmenu_tree_force_rebuild (GMenuTree *tree)
-{
-  if (tree->root)
-    {
-      gmenu_tree_directory_set_tree (tree->root, NULL);
-      gmenu_tree_item_unref (tree->root);
-      tree->root = NULL;
-
-      g_assert (tree->layout != NULL);
-
-      menu_layout_node_root_remove_entries_monitor (tree->layout,
-                                                    (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
-                                                    tree);
-    }
-}
diff --git a/menu-cache-gen/gmenu-tree.h b/menu-cache-gen/gmenu-tree.h
deleted file mode 100644 (file)
index aa6c3e0..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __GMENU_TREE_H__
-#define __GMENU_TREE_H__
-
-#ifndef GMENU_I_KNOW_THIS_IS_UNSTABLE
-#error "libgnome-menu should only be used if you understand that it's subject to frequent change, and is not supported as a fixed API/ABI or as part of the platform"
-#endif
-
-#include <glib.h>
-
-G_BEGIN_DECLS
-
-typedef struct GMenuTree          GMenuTree;
-typedef struct GMenuTreeItem      GMenuTreeItem;
-typedef struct GMenuTreeDirectory GMenuTreeDirectory;
-typedef struct GMenuTreeEntry     GMenuTreeEntry;
-typedef struct GMenuTreeSeparator GMenuTreeSeparator;
-typedef struct GMenuTreeHeader    GMenuTreeHeader;
-typedef struct GMenuTreeAlias     GMenuTreeAlias;
-
-typedef void (*GMenuTreeChangedFunc) (GMenuTree *tree,
-                                     gpointer  user_data);
-
-typedef enum
-{
-  GMENU_TREE_ITEM_INVALID = 0,
-  GMENU_TREE_ITEM_DIRECTORY,
-  GMENU_TREE_ITEM_ENTRY,
-  GMENU_TREE_ITEM_SEPARATOR,
-  GMENU_TREE_ITEM_HEADER,
-  GMENU_TREE_ITEM_ALIAS
-} GMenuTreeItemType;
-
-#define GMENU_TREE_ITEM(i)      ((GMenuTreeItem *)(i))
-#define GMENU_TREE_DIRECTORY(i) ((GMenuTreeDirectory *)(i))
-#define GMENU_TREE_ENTRY(i)     ((GMenuTreeEntry *)(i))
-#define GMENU_TREE_SEPARATOR(i) ((GMenuTreeSeparator *)(i))
-#define GMENU_TREE_HEADER(i)    ((GMenuTreeHeader *)(i))
-#define GMENU_TREE_ALIAS(i)     ((GMenuTreeAlias *)(i))
-
-typedef enum
-{
-  GMENU_TREE_FLAGS_NONE                = 0,
-  GMENU_TREE_FLAGS_INCLUDE_EXCLUDED    = 1 << 0,
-  GMENU_TREE_FLAGS_SHOW_EMPTY          = 1 << 1,
-  GMENU_TREE_FLAGS_INCLUDE_NODISPLAY   = 1 << 2,
-  GMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS = 1 << 3,
-  GMENU_TREE_FLAGS_MASK                = 0x0f
-} GMenuTreeFlags;
-
-typedef enum
-{
-  #define GMENU_TREE_SORT_FIRST GMENU_TREE_SORT_NAME
-  GMENU_TREE_SORT_NAME = 0,
-  GMENU_TREE_SORT_DISPLAY_NAME
-  #define GMENU_TREE_SORT_LAST GMENU_TREE_SORT_DISPLAY_NAME
-} GMenuTreeSortKey;
-
-GMenuTree *gmenu_tree_lookup (const char     *menu_file,
-                             GMenuTreeFlags  flags);
-
-GMenuTree *gmenu_tree_ref   (GMenuTree *tree);
-void       gmenu_tree_unref (GMenuTree *tree);
-
-void     gmenu_tree_set_user_data (GMenuTree       *tree,
-                                  gpointer        user_data,
-                                  GDestroyNotify  dnotify);
-gpointer gmenu_tree_get_user_data (GMenuTree       *tree);
-
-const char         *gmenu_tree_get_menu_file           (GMenuTree  *tree);
-GMenuTreeDirectory *gmenu_tree_get_root_directory      (GMenuTree  *tree);
-GMenuTreeDirectory *gmenu_tree_get_directory_from_path (GMenuTree  *tree,
-                                                       const char *path);
-
-GMenuTreeSortKey     gmenu_tree_get_sort_key (GMenuTree        *tree);
-void                 gmenu_tree_set_sort_key (GMenuTree        *tree,
-                                             GMenuTreeSortKey  sort_key);
-
-GSList* all_used_dirs;
-GSList* all_used_files;
-
-
-gpointer gmenu_tree_item_ref   (gpointer item);
-void     gmenu_tree_item_unref (gpointer item);
-
-void     gmenu_tree_item_set_user_data (GMenuTreeItem   *item,
-                                       gpointer        user_data,
-                                       GDestroyNotify  dnotify);
-gpointer gmenu_tree_item_get_user_data (GMenuTreeItem   *item);
-
-GMenuTreeItemType   gmenu_tree_item_get_type   (GMenuTreeItem *item);
-GMenuTreeDirectory *gmenu_tree_item_get_parent (GMenuTreeItem *item);
-
-
-GSList     *gmenu_tree_directory_get_contents          (GMenuTreeDirectory *directory);
-const char *gmenu_tree_directory_get_name              (GMenuTreeDirectory *directory);
-const char *gmenu_tree_directory_get_comment           (GMenuTreeDirectory *directory);
-const char *gmenu_tree_directory_get_icon              (GMenuTreeDirectory *directory);
-const char *gmenu_tree_directory_get_desktop_file_path (GMenuTreeDirectory *directory);
-const char *gmenu_tree_directory_get_menu_id           (GMenuTreeDirectory *directory);
-GMenuTree  *gmenu_tree_directory_get_tree              (GMenuTreeDirectory *directory);
-
-gboolean gmenu_tree_directory_get_is_nodisplay (GMenuTreeDirectory *directory);
-
-char *gmenu_tree_directory_make_path (GMenuTreeDirectory *directory,
-                                     GMenuTreeEntry     *entry);
-
-
-const char *gmenu_tree_entry_get_name               (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_generic_name       (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_display_name       (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_comment            (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_icon               (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_exec               (GMenuTreeEntry *entry);
-gboolean    gmenu_tree_entry_get_launch_in_terminal (GMenuTreeEntry *entry);
-gboolean    gmenu_tree_entry_get_use_startup_notify (GMenuTreeEntry *entry);
-
-guint32     gmenu_tree_entry_get_show_in_flags      (GMenuTreeEntry *entry);
-
-const char *gmenu_tree_entry_get_desktop_file_path (GMenuTreeEntry *entry);
-const char *gmenu_tree_entry_get_desktop_file_id   (GMenuTreeEntry *entry);
-
-gboolean gmenu_tree_entry_get_is_excluded  (GMenuTreeEntry *entry);
-gboolean gmenu_tree_entry_get_is_nodisplay (GMenuTreeEntry *entry);
-
-GMenuTreeDirectory *gmenu_tree_header_get_directory (GMenuTreeHeader *header);
-
-GMenuTreeDirectory *gmenu_tree_alias_get_directory (GMenuTreeAlias *alias);
-GMenuTreeItem      *gmenu_tree_alias_get_item      (GMenuTreeAlias *alias);
-
-void gmenu_tree_add_monitor    (GMenuTree            *tree,
-                               GMenuTreeChangedFunc  callback,
-                               gpointer             user_data);
-void gmenu_tree_remove_monitor (GMenuTree            *tree,
-                               GMenuTreeChangedFunc  callback,
-                               gpointer             user_data);
-
-G_END_DECLS
-
-#endif /* __GMENU_TREE_H__ */
diff --git a/menu-cache-gen/main.c b/menu-cache-gen/main.c
new file mode 100644 (file)
index 0000000..33a1914
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ *      main.c : the main() function for menu-cache-gen binary.
+ *
+ *      Copyright 2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of libmenu-cache package and created program
+ *      should be not used without the library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "menu-tags.h"
+
+#include <string.h>
+#include <locale.h>
+
+char **languages = NULL;
+GSList *MenuFiles = NULL;
+
+gint verbose = 0;
+
+static gboolean option_verbose (const gchar *option_name, const gchar *value,
+                                gpointer data, GError **error)
+{
+    verbose++;
+    return TRUE;
+}
+
+/* GLib options parser data is taken from previous menu-cache-gen code
+ *
+ *      Copyright 2008 PCMan <pcman.tw@google.com>
+ */
+static char* ifile = NULL;
+static char* ofile = NULL;
+static char* lang = NULL;
+
+GOptionEntry opt_entries[] =
+{
+/*
+    {"force", 'f', 0, G_OPTION_ARG_NONE, &force, "Force regeneration of cache even if it's up-to-dat
+e.", NULL },
+*/
+    {"input", 'i', 0, G_OPTION_ARG_FILENAME, &ifile, "Source *.menu file to read", "FILENAME" },
+    {"output", 'o', 0, G_OPTION_ARG_FILENAME, &ofile, "Output file to write cache to", "FILENAME" },
+    {"lang", 'l', 0, G_OPTION_ARG_STRING, &lang, "Language", "LANG_LIST" },
+    {"verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, &option_verbose, "Send debug messages to terminal", NULL },
+    { NULL }
+};
+
+int main(int argc, char **argv)
+{
+    FmXmlFile *xmlfile = NULL;
+    GOptionContext *opt_ctx;
+    GError *err = NULL;
+    MenuMenu *menu;
+    int rc = 1;
+    gboolean with_hidden = FALSE;
+
+    /* wish we could use some POSIX parser but there isn't one for long options */
+    opt_ctx = g_option_context_new("Generate cache for freedesktop.org compliant menus.");
+    g_option_context_add_main_entries(opt_ctx, opt_entries, NULL);
+    if (!g_option_context_parse(opt_ctx, &argc, &argv, &err))
+    {
+        g_printerr("menu-cache-gen: %s\n", err->message);
+        g_error_free(err);
+        return 1;
+    }
+
+    /* do with -l: if language is NULL then query it from environment */
+    if (lang == NULL || lang[0] == '\0')
+        languages = (char **)g_get_language_names();
+    else
+        languages = g_strsplit(lang, ":", 0);
+    setlocale(LC_ALL, "");
+
+    /* do with files: both ifile and ofile should be set correctly */
+    if (ifile == NULL || ofile == NULL)
+    {
+        g_printerr("menu-cache-gen: failed: both input and output files must be defined.\n");
+        return 1;
+    }
+    with_hidden = g_str_has_suffix(ifile, "+hidden");
+    if (with_hidden)
+        ifile[strlen(ifile)-7] = '\0';
+    if (G_LIKELY(!g_path_is_absolute(ifile)))
+    {
+        /* resolv the path */
+        char *path;
+        gboolean found;
+        const gchar * const *dirs = g_get_system_config_dirs();
+
+        const char *menu_prefix = g_getenv("XDG_MENU_PREFIX");
+        if (menu_prefix != NULL && menu_prefix[0] != '\0')
+        {
+            char *path = g_strconcat(menu_prefix, ifile, NULL);
+            g_free(ifile);
+            ifile = path;
+        }
+        path = g_build_filename(g_get_user_config_dir(), "menus", ifile, NULL);
+        found = g_file_test(path, G_FILE_TEST_IS_REGULAR);
+        while (!found && dirs[0] != NULL)
+        {
+            MenuFiles = g_slist_append(MenuFiles, (gpointer)g_intern_string(path));
+            g_free(path);
+            path = g_build_filename(dirs[0], "menus", ifile, NULL);
+            found = g_file_test(path, G_FILE_TEST_IS_REGULAR);
+            dirs++;
+        }
+        if (!found)
+        {
+            g_printerr("menu-cache-gen: failed: cannot find file '%s'\n", ifile);
+            return 1;
+        }
+        g_free(ifile);
+        ifile = path;
+    }
+    MenuFiles = g_slist_append(MenuFiles, (gpointer)g_intern_string(ifile));
+
+#if !GLIB_CHECK_VERSION(2, 36, 0)
+    g_type_init();
+#endif
+
+    /* load, merge menu file, and create menu */
+    menu = get_merged_menu(ifile, &xmlfile, &err);
+    if (menu == NULL)
+    {
+        g_printerr("menu-cache-gen: %s\n", err->message);
+        g_error_free(err);
+        return 1;
+    }
+
+    /* save the layout */
+    rc = !save_menu_cache(menu, ifile, ofile, with_hidden);
+    if (xmlfile != NULL)
+        g_object_unref(xmlfile);
+    return rc;
+}
diff --git a/menu-cache-gen/menu-cache-gen.c b/menu-cache-gen/menu-cache-gen.c
deleted file mode 100644 (file)
index 1c8c2bd..0000000
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- *      menu-cache-gen.c
- *      
- *      Copyright 2008 PCMan <pcman@thinkpad>
- *      
- *      This program is free software; you can redistribute it and/or modify
- *      it under the terms of the GNU General Public License as published by
- *      the Free Software Foundation; either version 2 of the License, or
- *      (at your option) any later version.
- *      
- *      This program is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *      GNU General Public License for more details.
- *      
- *      You should have received a copy of the GNU General Public License
- *      along with this program; if not, write to the Free Software
- *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- *      MA 02110-1301, USA.
- */
-
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <glib/gi18n.h>
-#include <gio/gio.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <locale.h>
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include "gmenu-tree.h"
-#include "version.h"
-#include "menu-cache.h"
-#include "menu-cache-gen.h"
-
-static char* ifile = NULL;
-static char* ofile = NULL;
-static char* lang = NULL;
-
-GOptionEntry opt_entries[] =
-{
-/*
-    {"force", 'f', 0, G_OPTION_ARG_NONE, &force, "Force regeneration of cache even if it's up-to-date.", NULL },
-*/
-    {"input", 'i', 0, G_OPTION_ARG_FILENAME, &ifile, "Source *.menu file to read", NULL },
-    {"output", 'o', 0, G_OPTION_ARG_FILENAME, &ofile, "Output file to write cache to", NULL },
-    {"lang", 'l', 0, G_OPTION_ARG_STRING, &lang, "Language", NULL },
-    {0}
-};
-
-GHashTable* dir_hash = NULL;
-
-static int n_known_de = N_KNOWN_DESKTOPS;
-static GHashTable* de_hash = NULL;
-
-/* Convert desktop environment names to bitmasks.
- * Since it's impossible to have OnlyShowIn=<a list of 32 desktop environments>,
- * I think a 32 bit integer is quite enough. If it's not, in the future we can
- * use guint64 here.
- */
-guint32 menu_cache_get_de_flag( const char* de_name )
-{
-    gpointer val = g_hash_table_lookup(de_hash, de_name);
-    guint32 flag;
-    if( G_LIKELY(val) )
-        flag = GPOINTER_TO_UINT(val);
-    else
-    {
-        if( G_UNLIKELY(n_known_de > 31) )
-        {
-            g_debug("only up to 31 different DEs are supported");
-            return 0;
-        }
-        flag = (1 << n_known_de);
-        ++n_known_de;
-        g_hash_table_insert(de_hash, g_strdup(de_name), GUINT_TO_POINTER(flag));
-    }
-    /* g_debug("flag of %s is %d", de_name, flag); */
-    return flag;
-}
-
-static int dirname_index( const char* dir )
-{
-    GSList* l;
-    int i = 0;
-    for( l = all_used_dirs; l; l = l->next )
-    {
-        if( strcmp(dir, l->data) == 0 )
-            return i;
-        ++i;
-    }
-    return -1;
-}
-
-#if 0
-void write_item_ex_info( FILE* of, const char* desktop_file )
-{
-    gsize len;
-    GKeyFile* kf = g_key_file_new();
-    if( g_key_file_load_from_file( kf, desktop_file, 0, NULL ) )
-    {
-        char** keys = g_key_file_get_keys( kf, "Desktop Entry", &len, NULL );
-        char** key;
-        char* val;
-        char** vals;
-        gsize n_vals;
-
-        for( key = keys; *key; ++key )
-        {
-            if( g_str_has_prefix( *key, "X-" ) )
-            {
-                char* val = g_key_file_get_value( kf, "Desktop Entry", *key, NULL );
-                fprintf( of, "%s=%s\n", *key, val );
-                g_free( val );
-            }
-        }
-        g_strfreev( keys );
-    }
-    g_key_file_free( kf );
-}
-#endif
-
-static void write_entry( FILE* of, GMenuTreeEntry* item )
-{
-    const char* cstr;
-    char* str;
-    int flags = 0;
-
-//    if( gmenu_tree_entry_get_is_nodisplay(item) /* || gmenu_tree_entry_get_is_excluded(item) */ )
-//        return;
-
-    /* dekstop id, not necessarily the same as file basename */
-    fprintf( of, "-%s\n", gmenu_tree_entry_get_desktop_file_id( item ) );
-
-    /* Name */
-    fprintf( of, "%s\n", gmenu_tree_entry_get_name( item ) );
-
-    /* Comment */
-    cstr = gmenu_tree_entry_get_comment( item );
-    fprintf( of, "%s\n", cstr ? cstr : "" );
-
-    /* Icon */
-    cstr = gmenu_tree_entry_get_icon( item );
-    fprintf( of, "%s\n", cstr ? cstr : "" );
-
-    /* file dir/basename */
-    if( gmenu_tree_entry_get_desktop_file_path( item ) )
-    {
-        /* file basenames are the same as desktop ids, except that sometimes
-         * the '/' in paths are replaced with '-'.
-         * for ex, /usr/share/applications/foo/bar.desktop has the app dir
-         * /usr/share/applications, the filename foo/bar.desltop, and the
-         * desktop id: foo-bar.desktop
-         */
-
-        /* filename */
-        str = g_path_get_basename( gmenu_tree_entry_get_desktop_file_path( item ) );
-        if( strcmp(str, gmenu_tree_entry_get_desktop_file_id(item) ) )
-            fprintf( of, "%s\n", str );
-        else
-            fprintf( of, "\n" );
-        g_free( str );
-
-        /* dirname */
-        str = g_path_get_dirname( gmenu_tree_entry_get_desktop_file_path( item ) );
-        fprintf( of, "%d\n", dirname_index( str) );
-        g_free( str );
-    }
-    else
-    {
-        fprintf( of, "\n-1\n" );
-    }
-
-    /* DisplayName */
-    cstr = gmenu_tree_entry_get_display_name( item );
-    fprintf( of, "%s\n", cstr ? cstr : "" );
-
-    /* Exec */
-    fprintf( of, "%s\n", gmenu_tree_entry_get_exec( item ) );
-
-    /* Terminal/StartupNotify flags */
-    if( gmenu_tree_entry_get_launch_in_terminal( item ) )
-        flags |= FLAG_USE_TERMINAL;
-    if( gmenu_tree_entry_get_use_startup_notify( item ) )
-        flags |= FLAG_USE_SN;
-    if( gmenu_tree_entry_get_is_nodisplay( item ) )
-        flags |= FLAG_IS_NODISPLAY;
-    fprintf( of, "%u\n", flags );
-
-    /* ShowIn info */
-    fprintf( of, "%d\n", gmenu_tree_entry_get_show_in_flags(item) );
-/*
-    if( gmenu_tree_entry_get_desktop_file_path( item ) )
-        write_item_ex_info(of, gmenu_tree_entry_get_desktop_file_path( item ));
-    fputs( "\n", of );
-*/
-
-}
-
-static void write_dir( FILE* of, GMenuTreeDirectory* dir )
-{
-    GSList* l;
-    const char* cstr;
-    char* str;
-
-    fprintf( of, "+%s\n", gmenu_tree_directory_get_menu_id( dir ) );
-    fprintf( of, "%s\n", gmenu_tree_directory_get_name( dir ) );
-    cstr = gmenu_tree_directory_get_comment( dir );
-    fprintf( of, "%s\n", cstr ? cstr : "" );
-    cstr = gmenu_tree_directory_get_icon( dir );
-    fprintf( of, "%s\n", cstr ? cstr : "" );
-
-    if( gmenu_tree_directory_get_desktop_file_path( dir ) )
-    {
-        /* get basename of its desktop file. */
-        str = g_path_get_basename( gmenu_tree_directory_get_desktop_file_path( dir ) );
-        fprintf( of, "%s\n", str );
-        g_free( str );
-
-        /* get the location of its desktop file. */
-        str = g_path_get_dirname( gmenu_tree_directory_get_desktop_file_path( dir ) );
-        fprintf( of, "%d\n", dirname_index( str ) );
-        g_free( str );
-    }
-    else
-    {
-        fprintf( of, "\n-1\n" );
-    }
-
-    // fprintf( of, "\n" );    /* end of item info */
-
-    for( l = gmenu_tree_directory_get_contents(dir); l; l = l->next )
-    {
-        GMenuTreeItem* item = (GMenuTreeItem*)l->data;
-        GMenuTreeItemType type = gmenu_tree_item_get_type(item);
-
-        if( type == GMENU_TREE_ITEM_DIRECTORY )
-        {
-            write_dir( of, (GMenuTreeDirectory*)item );
-        }
-        else if( type == GMENU_TREE_ITEM_ENTRY )
-        {
-            write_entry( of, (GMenuTreeEntry*)item );
-        }
-        else if( type == GMENU_TREE_ITEM_SEPARATOR )
-            fputs( "-\n", of );
-    }
-    fputs( "\n", of );
-}
-
-#if 0
-static gboolean is_src_newer( const char* src, const char* dest )
-{
-    struct stat src_st, dest_st;
-
-    if( stat(dest, &dest_st) == -1 )
-        return TRUE;
-
-    if( stat(src, &src_st) == -1 )
-        return TRUE;
-
-    return (src_st.st_mtime > dest_st.st_mtime);
-}
-
-static gboolean is_menu_uptodate()
-{
-    MenuCacheDir* menu;
-    struct stat menu_st, cache_st;
-    GHashTable* hash;
-    GList *dirs, *l;
-    gboolean ret = TRUE;
-
-    if( is_src_newer( ifile, ofile ) )
-        return FALSE;
-
-    return menu_cache_file_is_updated( ofile );
-}
-#endif
-
-static void write_de_name(char* de_name, gpointer _flag, FILE* of)
-{
-    guint32 flag = GPOINTER_TO_UINT(_flag);
-    if( flag >= (1 << N_KNOWN_DESKTOPS) ) /* only write it if it's not a known DE. */
-    {
-        fprintf( of, "%s;", de_name );
-    }
-}
-
-int main(int argc, char** argv)
-{
-    GOptionContext* opt_ctx;
-    GError* err = NULL;
-    GMenuTree* menu_tree = NULL;
-    GMenuTreeDirectory* root_dir;
-    GSList* l;
-    FILE *of;
-    int ofd;
-    char *tmp;
-    char *dir;
-    const gchar* const * xdg_cfg_dirs;
-    const gchar* const * pdir;
-    const char* menu_prefix;
-    char* menu_file;
-    char* plus_ptr = NULL;
-
-    setlocale (LC_ALL, "");
-
-    opt_ctx = g_option_context_new("Generate cache for freedesktop.org compliant menus.");
-    g_option_context_add_main_entries( opt_ctx, opt_entries, NULL );
-    if( ! g_option_context_parse( opt_ctx, &argc, &argv, &err ) )
-    {
-        g_print( "%s", err->message );
-        g_error_free( err );
-        return 1;
-    }
-
-    if( lang )
-        g_setenv( "LANGUAGE", lang, TRUE );
-#if 0
-    /* if the cache is already up-to-date, just leave it. */
-    if( !force && is_menu_uptodate() )
-    {
-        g_print("upda-to-date, re-generation is not needed.");
-        return 0;
-    }
-#endif
-
-    /* some memory leaks happen here if g_free is not used to free the keys. */
-    de_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-    g_hash_table_insert( de_hash, (gpointer)"LXDE", (gpointer)SHOW_IN_LXDE );
-    g_hash_table_insert( de_hash, (gpointer)"GNOME", (gpointer)SHOW_IN_GNOME );
-    g_hash_table_insert( de_hash, (gpointer)"KDE", (gpointer)SHOW_IN_KDE );
-    g_hash_table_insert( de_hash, (gpointer)"XFCE", (gpointer)SHOW_IN_XFCE );
-    g_hash_table_insert( de_hash, (gpointer)"ROX", (gpointer)SHOW_IN_ROX );
-
-    if(ifile)
-        plus_ptr = strrchr(ifile, '+');
-
-    if(plus_ptr != NULL && strcmp(plus_ptr, "+hidden") != 0)
-        plus_ptr = NULL;
-
-    if(plus_ptr)
-    {
-        *plus_ptr = '\0';
-        menu_tree = gmenu_tree_lookup( ifile, GMENU_TREE_FLAGS_INCLUDE_NODISPLAY | GMENU_TREE_FLAGS_INCLUDE_EXCLUDED | GMENU_TREE_FLAGS_SHOW_EMPTY );
-        *plus_ptr = '+';
-    }
-    else
-        menu_tree = gmenu_tree_lookup( ifile, GMENU_TREE_FLAGS_INCLUDE_EXCLUDED );
-    if( ! menu_tree )
-    {
-        g_print("Error loading source menu file: %s\n", ifile);
-        return 1;
-    }
-
-    dir = g_path_get_dirname( ofile );
-    if( !g_file_test( dir, G_FILE_TEST_EXISTS ) )
-        g_mkdir_with_parents( dir, 0700 );
-    g_free( dir );
-
-    /* write the tree to cache. */
-    tmp = g_malloc( strlen( ofile ) + 7 );
-    strcpy( tmp, ofile );
-    strcat( tmp, "XXXXXX" );
-    ofd = g_mkstemp( tmp );
-    if( ofd == -1 )
-    {
-        g_print( "Error writing output file: %s\n", g_strerror(errno) );
-        return 1;
-    }
-
-    of = fdopen( ofd, "w" );
-    if( ! of )
-    {
-        g_print( "Error writing output file: %s\n", ofile );
-        return 1;
-    }
-
-    /* Version number should be added to the head of this cache file. */
-    fprintf( of, "%d.%d\n", VER_MAJOR, VER_MINOR );
-
-    /* the first line is menu name */
-    fprintf( of, "%s\n", ifile );
-
-    root_dir = gmenu_tree_get_root_directory( menu_tree );
-
-    /* add the source menu file itself to the list of files requiring monitor */
-    if(plus_ptr)
-        *plus_ptr = '\0';
-    if( g_path_is_absolute(ifile) )
-    {
-        if( ! g_slist_find_custom(all_used_files, ifile, (GCompareFunc)strcmp ) )
-            all_used_files = g_slist_prepend(all_used_files, g_strdup(ifile));
-    }
-    else
-    {
-        char* file_name;
-        xdg_cfg_dirs = g_get_system_config_dirs();
-        menu_prefix = g_getenv("XDG_MENU_PREFIX");
-        file_name = menu_prefix ? g_strconcat(menu_prefix, ifile, NULL) : ifile;
-        for( pdir = xdg_cfg_dirs; *pdir; ++pdir )
-        {
-            menu_file = g_build_filename( *pdir, "menus", file_name, NULL );
-            if( ! g_slist_find_custom(all_used_dirs, menu_file, (GCompareFunc)strcmp ) )
-                all_used_files = g_slist_prepend(all_used_files, menu_file);
-            else
-                g_free( menu_file );
-        }
-        menu_file = g_build_filename( g_get_user_config_dir(), "menus", file_name, NULL );
-        if( file_name != ifile )
-            g_free(file_name);
-
-        if( ! g_slist_find_custom(all_used_dirs, menu_file, (GCompareFunc)strcmp ) )
-            all_used_files = g_slist_prepend(all_used_files, menu_file);
-        else
-            g_free(menu_file);
-    }
-
-    /* write a list of all files which need to be monitored for changes. */
-    /* write number of files first */
-    fprintf( of, "%d\n", g_slist_length(all_used_dirs) + g_slist_length(all_used_files) );
-
-    /* list all files.
-     * add D or F at the begin of each line to indicate whether it's a
-     * file or directory. */
-    for( l = all_used_dirs; l; l = l->next )
-    {
-        fprintf( of, "D%s\n", (char*)l->data );
-    }
-    for( l = all_used_files; l; l = l->next )
-    {
-        fprintf( of, "F%s\n", (char*)l->data );
-    }
-
-    /* write all DE names in this menu. Known DEs such as LXDE, GNOME, and KDE don't need to be listed here */
-    if( g_hash_table_size(de_hash) > N_KNOWN_DESKTOPS ) /* if there are some unknown DEs added to the hash */
-        g_hash_table_foreach(de_hash, (GHFunc)write_de_name, of );
-    fputc('\n', of);
-
-    /* write the whole menu tree */
-    write_dir( of, root_dir );
-
-    fclose( of );
-
-    gmenu_tree_unref( menu_tree );
-
-    g_hash_table_destroy(de_hash);
-
-    if( g_rename( tmp, ofile ) == -1 )
-    {
-        g_print( "Error writing output file: %s\n", g_strerror( errno ) );
-    }
-    g_free( tmp );
-    /* g_print("success!\n"); */
-    return 0;
-}
diff --git a/menu-cache-gen/menu-cache-gen.h b/menu-cache-gen/menu-cache-gen.h
deleted file mode 100644 (file)
index 7d9be42..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef __MENU_CACHE_GEN_H__
-#define __MENU_CACHE_GEN_H__ 1
-G_BEGIN_DECLS
-
-guint32 menu_cache_get_de_flag (const char* de_name);
-
-G_END_DECLS
-#endif
\ No newline at end of file
diff --git a/menu-cache-gen/menu-compose.c b/menu-cache-gen/menu-compose.c
new file mode 100644 (file)
index 0000000..c01b1c8
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ *      menu-compose.c : scans appropriate .desktop files and composes cache file.
+ *
+ *      Copyright 2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
+ *
+ *      This file is a part of libmenu-cache package and created program
+ *      should be not used without the library.
+ *
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2.1 of the License, or (at your option) any later version.
+ *
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ *
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "menu-tags.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <glib/gstdio.h>
+
+#define NONULL(a) (a == NULL) ? "" : a
+
+static GSList *DEs = NULL;
+
+static void menu_app_reset(MenuApp *app)
+{
+    g_free(app->filename);
+    g_free(app->title);
+    g_free(app->key);
+    app->key = NULL;
+    g_free(app->comment);
+    g_free(app->icon);
+    g_free(app->generic_name);
+    g_free(app->exec);
+    g_free(app->wd);
+    g_free(app->categories);
+    g_free(app->show_in);
+    g_free(app->hide_in);
+}
+
+static void menu_app_free(gpointer data)
+{
+    MenuApp *app = data;
+
+    menu_app_reset(app);
+    g_free(app->id);
+    g_list_free(app->dirs);
+    g_list_free(app->menus);
+    g_slice_free(MenuApp, app);
+}
+
+/* g_key_file_get_locale_string is too much limited so implement replacement */
+static char *_get_language_string(GKeyFile *kf, const char *key)
+{
+    char **lang;
+    char *try_key, *str;
+
+    for (lang = languages; lang[0] != NULL; lang++)
+    {
+        try_key = g_strdup_printf("%s[%s]", key, lang[0]);
+        str = g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP, try_key, NULL);
+        g_free(try_key);
+        if (str != NULL)
+            return str;
+    }
+    return g_key_file_get_locale_string(kf, G_KEY_FILE_DESKTOP_GROUP, key, languages[0], NULL);
+}
+
+static void _fill_menu_from_file(MenuMenu *menu, const char *path)
+{
+    GKeyFile *kf;
+
+    if (!g_str_has_suffix(path, ".directory")) /* ignore random names */
+        return;
+    kf = g_key_file_new();
+    if (!g_key_file_load_from_file(kf, path, G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+        goto exit;
+    menu->title = _get_language_string(kf, G_KEY_FILE_DESKTOP_KEY_NAME);
+    menu->comment = _get_language_string(kf, G_KEY_FILE_DESKTOP_KEY_COMMENT);
+    menu->icon = g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                       G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
+    menu->layout.nodisplay = g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                                    G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL);
+    menu->layout.is_set = TRUE;
+exit:
+    g_key_file_free(kf);
+}
+
+static const char **menu_app_intern_key_file_list(GKeyFile *kf, const char *key,
+                                                  gboolean add_to_des)
+{
+    gsize len, i;
+    char **val = g_key_file_get_string_list(kf, G_KEY_FILE_DESKTOP_GROUP, key, &len, NULL);
+    const char **res;
+
+    if (val == NULL)
+        return NULL;
+    res = (const char **)g_new(char *, len + 1);
+    for (i = 0; i < len; i++)
+    {
+        res[i] = g_intern_string(val[i]);
+        if (add_to_des && g_slist_find(DEs, res[i]) == NULL)
+            DEs = g_slist_append(DEs, (gpointer)res[i]);
+    }
+    res[i] = NULL;
+    g_strfreev(val);
+    return res;
+}
+
+static void _fill_app_from_key_file(MenuApp *app, GKeyFile *kf)
+{
+    app->title = _get_language_string(kf, G_KEY_FILE_DESKTOP_KEY_NAME);
+    app->comment = _get_language_string(kf, G_KEY_FILE_DESKTOP_KEY_COMMENT);
+    app->icon = g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                      G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
+    app->generic_name = _get_language_string(kf, G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME);
+    app->exec = g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                      G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
+    //app->wd =
+    app->categories = menu_app_intern_key_file_list(kf, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, FALSE);
+    app->show_in = menu_app_intern_key_file_list(kf, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, TRUE);
+    app->hide_in = menu_app_intern_key_file_list(kf, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, TRUE);
+    app->use_terminal = g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                               G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL);
+    app->use_notification = g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                                   G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL);
+    app->hidden = g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                         G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL);
+}
+
+static GHashTable *all_apps = NULL;
+
+static GSList *loaded_dirs = NULL;
+
+static void _fill_apps_from_dir(MenuMenu *menu, GList *lptr, GString *prefix,
+                                gboolean is_legacy)
+{
+    const char *dir = lptr->data;
+    GDir *gd;
+    const char *name;
+    char *filename, *id;
+    gsize prefix_len = prefix->len;
+    MenuApp *app;
+    GKeyFile *kf;
+
+    if (g_slist_find(loaded_dirs, dir) == NULL)
+        loaded_dirs = g_slist_prepend(loaded_dirs, (gpointer)dir);
+    /* the directory might be scanned with different prefix already */
+    else if (prefix->str[0] == '\0')
+        return;
+    gd = g_dir_open(dir, 0, NULL);
+    if (gd == NULL)
+        return;
+    kf = g_key_file_new();
+    DBG("fill apps from dir [%s]%s", prefix->str, dir);
+    /* Scan the directory with subdirs,
+       ignore not .desktop files,
+       ignore already present files that are allocated */
+    while ((name = g_dir_read_name(gd)) != NULL)
+    {
+        filename = g_build_filename(dir, name, NULL);
+        if (g_file_test(filename, G_FILE_TEST_IS_DIR))
+        {
+            /* recursion */
+            if (is_legacy)
+            {
+                MenuMenu *submenu = g_slice_new0(MenuMenu);
+                MenuMerge def_files = { .type = MENU_CACHE_TYPE_NONE, .merge_type = MERGE_FILES };
+                MenuMerge def_menus = { .type = MENU_CACHE_TYPE_NONE, .merge_type = MERGE_MENUS };
+                submenu->layout = menu->layout; /* copy all */
+                submenu->layout.items = g_list_prepend(g_list_prepend(NULL, &def_files), &def_menus);
+                submenu->layout.inline_limit_is_set = TRUE; /* marker */
+                submenu->name = g_strdup(name);
+                submenu->dir = g_intern_string(filename);
+                menu->children = g_list_append(menu->children, submenu);
+            }
+            else
+            {
+                g_string_append(prefix, name);
+                g_string_append_c(prefix, '-');
+                name = g_intern_string(filename);
+                /* a little trick here - we insert new node after this one */
+                lptr = g_list_insert_before(lptr, lptr->next, (gpointer)name);
+                _fill_apps_from_dir(menu, lptr->next, prefix, FALSE);
+                g_string_truncate(prefix, prefix_len);
+            }
+        }
+        else if (!g_str_has_suffix(name, ".desktop") ||
+                 !g_file_test(filename, G_FILE_TEST_IS_REGULAR) ||
+                 !g_key_file_load_from_file(kf, filename,
+                                            G_KEY_FILE_KEEP_TRANSLATIONS, NULL))
+            ; /* ignore not key files */
+        else if ((id = g_key_file_get_string(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                             G_KEY_FILE_DESKTOP_KEY_TYPE, NULL)) == NULL ||
+                 strcmp(id, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
+            /* ignore non-applications */
+            g_free(id);
+        else
+        {
+            g_free(id);
+            if (prefix_len > 0)
+            {
+                g_string_append(prefix, name);
+                app = g_hash_table_lookup(all_apps, prefix->str);
+            }
+            else
+                app = g_hash_table_lookup(all_apps, name);
+            if (app == NULL)
+            {
+                if (!g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                            G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL))
+                {
+                    /* deleted file should be ignored */
+                    app = g_slice_new0(MenuApp);
+                    app->type = MENU_CACHE_TYPE_APP;
+                    app->filename = (prefix_len > 0) ? g_strdup(name) : NULL;
+                    app->id = g_strdup((prefix_len > 0) ? prefix->str : name);
+                    VDBG("found app id=%s", app->id);
+                    g_hash_table_insert(all_apps, app->id, app);
+                    app->dirs = g_list_prepend(NULL, (gpointer)dir);
+                    _fill_app_from_key_file(app, kf);
+                }
+            }
+            else if (app->allocated)
+                g_warning("id '%s' already allocated for %s and requested to"
+                          " change to %s, ignoring request", name,
+                          (const char *)app->dirs->data, dir);
+            else if (g_key_file_get_boolean(kf, G_KEY_FILE_DESKTOP_GROUP,
+                                            G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL))
+            {
+                VDBG("removing app id=%s", app->id),
+                g_hash_table_remove(all_apps, name);
+            }
+            else
+            {
+                /* reset the data */
+                menu_app_reset(app);
+                app->filename = (prefix_len > 0) ? g_strdup(name) : NULL;
+                /* reorder dirs list */
+                app->dirs = g_list_remove(app->dirs, dir);
+                app->dirs = g_list_prepend(app->dirs, (gpointer)dir);
+                _fill_app_from_key_file(app, kf);
+                /* FIXME: conform to spec about Legacy in Categories field */
+            }
+            if (prefix_len > 0)
+                g_string_truncate(prefix, prefix_len);
+        }
+        g_free(filename);
+    }
+    g_dir_close(gd);
+    g_key_file_free(kf);
+}
+
+static int _compare_items(gconstpointer a, gconstpointer b)
+{
+    /* return negative value to reverse sort list */
+    return -strcmp(((MenuApp*)a)->type == MENU_CACHE_TYPE_APP ? ((MenuApp*)a)->key
+                                                              : ((MenuMenu*)a)->key,
+                   ((MenuApp*)b)->type == MENU_CACHE_TYPE_APP ? ((MenuApp*)b)->key
+                                                              : ((MenuMenu*)b)->key);
+}
+
+static gboolean menu_app_match_tag(MenuApp *app, FmXmlFileItem *it)
+{
+    FmXmlFileTag tag = fm_xml_file_item_get_tag(it);
+    GList *children, *child;
+    gboolean ok = FALSE;
+
+    VVDBG("menu_app_match_tag: entering <%s>", fm_xml_file_item_get_tag_name(it));
+    children = fm_xml_file_item_get_children(it);
+    if (tag == menuTag_Or)
+    {
+        for (child = children; child; child = child->next)
+            if (menu_app_match_tag(app, child->data))
+                break;
+        ok = (child != NULL);
+    }
+    else if (tag == menuTag_And)
+    {
+        for (child = children; child; child = child->next)
+            if (!menu_app_match_tag(app, child->data))
+                break;
+        ok = (child == NULL);
+    }
+    else if (tag == menuTag_Not)
+    {
+        for (child = children; child; child = child->next)
+            if (menu_app_match_tag(app, child->data))
+                break;
+        ok = (child == NULL);
+    }
+    else if (tag == menuTag_All)
+        ok = TRUE;
+    else if (tag == menuTag_Filename)
+    {
+        register const char *id = fm_xml_file_item_get_data(children->data, NULL);
+        ok = (g_strcmp0(id, app->id) == 0);
+    }
+    else if (tag == menuTag_Category)
+    {
+        if (app->categories != NULL)
+        {
+            const char *cat = g_intern_string(fm_xml_file_item_get_data(children->data, NULL));
+            const char **cats = app->categories;
+            while (*cats)
+                if (*cats == cat)
+                    break;
+                else
+                    cats++;
+            ok = (*cats != NULL);
+        }
+    }
+    g_list_free(children);
+    VVDBG("menu_app_match_tag %s: leaving <%s>: %d", app->id, fm_xml_file_item_get_tag_name(it), ok);
+    return ok;
+}
+
+static gboolean menu_app_match_excludes(MenuApp *app, GList *rules);
+
+static gboolean menu_app_match(MenuApp *app, GList *rules, gboolean do_all)
+{
+    MenuRule *rule;
+    GList *children, *child;
+
+    for (; rules != NULL; rules = rules->next)
+    {
+        rule = rules->data;
+        if (rule->type != MENU_CACHE_TYPE_NONE ||
+            fm_xml_file_item_get_tag(rule->rule) != menuTag_Include)
+            continue;
+        children = fm_xml_file_item_get_children(rule->rule);
+        for (child = children; child; child = child->next)
+            if (menu_app_match_tag(app, child->data))
+                break;
+        g_list_free(children);
+        if (child != NULL)
+            return (!do_all || !menu_app_match_excludes(app, rules->next));
+    }
+    return FALSE;
+}
+
+static gboolean menu_app_match_excludes(MenuApp *app, GList *rules)
+{
+    MenuRule *rule;
+    GList *children, *child;
+
+    for (; rules != NULL; rules = rules->next)
+    {
+        rule = rules->data;
+        if (rule->type != MENU_CACHE_TYPE_NONE ||
+            fm_xml_file_item_get_tag(rule->rule) != menuTag_Exclude)
+            continue;
+        children = fm_xml_file_item_get_children(rule->rule);
+        for (child = children; child; child = child->next)
+            if (menu_app_match_tag(app, child->data))
+                break;
+        g_list_free(children);
+        if (child != NULL)
+            /* application might be included again later so check for it */
+            return !menu_app_match(app, rules->next, TRUE);
+    }
+    return FALSE;
+}
+
+static void _free_leftovers(GList *item);
+
+static void menu_menu_free(MenuMenu *menu)
+{
+    g_free(menu->name);
+    g_free(menu->key);
+    g_list_foreach(menu->id, (GFunc)g_free, NULL);
+    g_list_free(menu->id);
+    _free_leftovers(menu->children);
+    _free_layout_items(menu->layout.items);
+    g_free(menu->title);
+    g_free(menu->comment);
+    g_free(menu->icon);
+    g_slice_free(MenuMenu, menu);
+}
+
+static void _free_leftovers(GList *item)
+{
+    union {
+        MenuMenu *menu;
+        MenuRule *rule;
+    } a = { NULL };
+
+    while (item)
+    {
+        a.menu = item->data;
+        if (a.rule->type == MENU_CACHE_TYPE_NONE)
+            g_slice_free(MenuRule, a.rule);
+        else if (a.rule->type == MENU_CACHE_TYPE_DIR)
+            menu_menu_free(a.menu);
+        /* MenuApp and MenuSep are not allocated in menu->children */
+        item = g_list_delete_link(item, item);
+    }
+}
+
+/* dirs are in order "first is more relevant" */
+static void _stage1(MenuMenu *menu, GList *dirs, GList *apps, GList *legacy, GList *p)
+{
+    GList *child, *_dirs = NULL, *_apps = NULL, *_legs = NULL, *_lprefs = NULL;
+    GList *l, *available = NULL, *result;
+    const char *id;
+    char *filename;
+    GString *prefix;
+    MenuApp *app;
+    FmXmlFileTag tag;
+    GHashTableIter iter;
+
+    DBG("... entering %s (%d dirs %d apps)", menu->name, g_list_length(dirs), g_list_length(apps));
+    /* Gather our dirs : DirectoryDir AppDir LegacyDir KDELegacyDirs */
+    for (child = menu->children; child; child = child->next)
+    {
+        MenuRule *rule = child->data;
+        if (rule->type != MENU_CACHE_TYPE_NONE)
+            continue;
+        tag = fm_xml_file_item_get_tag(rule->rule);
+        if (tag == menuTag_DirectoryDir) {
+            id = g_intern_string(fm_xml_file_item_get_data(fm_xml_file_item_find_child(rule->rule,
+                                                           FM_XML_FILE_TEXT), NULL));
+            DBG("new DirectoryDir %s", id);
+            if (_dirs == NULL)
+                _dirs = g_list_copy(dirs);
+            /* replace and reorder the list */
+            _dirs = g_list_remove(_dirs, id);
+            _dirs = g_list_prepend(_dirs, (gpointer)id);
+        } else if (tag == menuTag_AppDir) {
+            id = g_intern_string(fm_xml_file_item_get_data(fm_xml_file_item_find_child(rule->rule,
+                                                           FM_XML_FILE_TEXT), NULL));
+            DBG("new AppDir %s", id);
+            _apps = g_list_prepend(_apps, (gpointer)id);
+        } else if (tag == menuTag_LegacyDir) {
+            id = g_intern_string(fm_xml_file_item_get_data(fm_xml_file_item_find_child(rule->rule,
+                                                           FM_XML_FILE_TEXT), NULL));
+            DBG("new LegacyDir %s", id);
+            _legs = g_list_prepend(_legs, (gpointer)id);
+            _lprefs = g_list_prepend(_lprefs, (gpointer)fm_xml_file_item_get_comment(rule->rule));
+        }
+    }
+    /* Gather data from files in the dirs */
+    if (_dirs != NULL) dirs = _dirs;
+    for (l = menu->id; l; l = l->next)
+    {
+        /* scan dirs now for availability of any of ids */
+        filename = NULL;
+        for (child = dirs; child; child = child->next)
+        {
+            filename = g_build_filename(child->data, l->data, NULL);
+            if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+                break;
+            g_free(filename);
+            filename = NULL;
+        }
+        if (filename == NULL) for (child = _legs; child; child = child->next)
+        {
+            filename = g_build_filename(child->data, l->data, NULL);
+            if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+                break;
+            g_free(filename);
+            filename = NULL;
+        }
+        if (filename == NULL) for (child = legacy; child; child = child->next)
+        {
+            filename = g_build_filename(child->data, l->data, NULL);
+            if (g_file_test(filename, G_FILE_TEST_IS_REGULAR))
+                break;
+            g_free(filename);
+            filename = NULL;
+        }
+        if (filename != NULL)
+        {
+            VVDBG("found dir file %s", filename);
+            _fill_menu_from_file(menu, filename);
+            g_free(filename);
+            if (!menu->layout.is_set)
+                continue;
+            menu->dir = child->data;
+            if (l != menu->id) /* relocate matched id to top if required */
+            {
+                menu->id = g_list_remove_link(menu->id, l);
+                menu->id = g_list_concat(menu->id, l);
+            }
+            break;
+        }
+    }
+    if (menu->layout.inline_limit_is_set && !menu->layout.is_set)
+    {
+        filename = g_build_filename(menu->dir, ".directory", NULL);
+        _fill_menu_from_file(menu, filename);
+        g_free(filename);
+        filename = NULL;
+    }
+    prefix = g_string_new("");
+    _apps = g_list_reverse(_apps);
+    _legs = g_list_reverse(_legs);
+    /* the same directory being scanned with different prefix should be denied */
+    for (l = _apps; l; l = l->next)
+    {
+        for (child = _apps; child; child = result)
+        {
+            int len;
+
+            result = child->next; /* it is not used yet so we can use it now */
+            if (child == l)
+                continue;
+            len = strlen(l->data);
+            if (strncmp(l->data, child->data, len) == 0 &&
+                ((const char *)child->data)[len] == G_DIR_SEPARATOR)
+                _apps = g_list_delete_link(_apps, child);
+        }
+        for (child = _legs; child; child = result)
+        {
+            int len;
+
+            result = child->next;
+            len = strlen(l->data);
+            if (strncmp(l->data, child->data, len) == 0 &&
+                ((const char *)child->data)[len] == G_DIR_SEPARATOR)
+            {
+                len = g_list_position(_legs, child);
+                _legs = g_list_delete_link(_legs, child);
+                child = g_list_nth(_lprefs, len);
+                _lprefs = g_list_delete_link(_lprefs, child);
+            }
+        }
+    }
+    if (!menu->layout.inline_limit_is_set) for (l = _apps; l; l = l->next)
+    {
+        /* scan and fill the list */
+        _fill_apps_from_dir(menu, l, prefix, FALSE);
+    }
+    if (_apps != NULL)
+        apps = _apps = g_list_concat(g_list_copy(apps), _apps);
+    for (l = _legs, child = _lprefs; l; l = l->next, child = child->next)
+    {
+        /* use prefix from <LegacyDir> attribute */
+        g_string_assign(prefix, NONULL(child->data));
+        VDBG("got legacy prefix %s", (char*)child->data);
+        _fill_apps_from_dir(menu, l, prefix, TRUE);
+        g_string_truncate(prefix, 0);
+    }
+    if (_legs != NULL)
+        legacy = _legs = g_list_concat(g_list_copy(legacy), _legs);
+    if (_lprefs != NULL)
+        p = _lprefs = g_list_concat(g_list_copy(p), _lprefs);
+    /* Gather all available files (some in $all_apps may be not in $apps) */
+    VDBG("... do matching");
+    g_hash_table_iter_init(&iter, all_apps);
+    while (g_hash_table_iter_next(&iter, NULL, (gpointer *)&app))
+    {
+        app->matched = FALSE;
+        /* check every dir if it is in $apps */
+        if (menu->layout.inline_limit_is_set)
+        {
+            for (child = app->dirs; child; child = child->next)
+                if (menu->dir == child->data)
+                    break;
+        }
+        else for (child = app->dirs; child; child = child->next)
+        {
+            for (l = apps; l; l = l->next)
+            {
+                if (l->data == child->data)
+                    break;
+            }
+            if (l != NULL) /* found one */
+                break;
+        }
+        VVDBG("check %s in %s: %d", app->id, app->dirs ? (const char *)app->dirs->data : "(nil)", child != NULL);
+        if (child == NULL) /* not matched */
+            continue;
+        /* Check matching : Include And Or Not All */
+        if (menu->layout.inline_limit_is_set)
+            app->matched = (app->categories == NULL); /* see the spec */
+        else
+            app->matched = menu_app_match(app, menu->children, FALSE);
+        if (!app->matched)
+            continue;
+        app->allocated = TRUE;
+        /* Mark it by Exclude And Or Not All */
+        app->excluded = menu_app_match_excludes(app, menu->children);
+        VVDBG("found match: %s excluded:%d", app->id, app->excluded);
+        if (!app->excluded)
+            available = g_list_prepend(available, app);
+    }
+    /* Compose layout using available list and replace menu->children */
+    VDBG("... compose (available=%d)", g_list_length(available));
+    result = NULL;
+    for (child = menu->layout.items; child; child = child->next)
+    {
+        GList *next;
+        app = child->data; /* either: MenuMenuname, MemuFilename, MenuSep, MenuMerge */
+        switch (app->type) {
+        case MENU_CACHE_TYPE_DIR: /* MenuMenuname */
+            VDBG("composing Menuname %s", ((MenuMenuname *)app)->name);
+            for (l = menu->children; l; l = l->next)
+                if (((MenuMenu *)l->data)->layout.type == MENU_CACHE_TYPE_DIR &&
+                    strcmp(((MenuMenuname *)app)->name, ((MenuMenu *)l->data)->name) == 0)
+                    break;
+            if (l != NULL) /* found such menu */
+            {
+                /* apply custom settings */
+                if (((MenuMenuname *)app)->layout.only_unallocated)
+                    ((MenuMenu *)l->data)->layout.show_empty = ((MenuMenuname *)app)->layout.show_empty;
+                if (((MenuMenuname *)app)->layout.is_set)
+                    ((MenuMenu *)l->data)->layout.allow_inline = ((MenuMenuname *)app)->layout.allow_inline;
+                if (((MenuMenuname *)app)->layout.inline_header_is_set)
+                    ((MenuMenu *)l->data)->layout.inline_header = ((MenuMenuname *)app)->layout.inline_header;
+                if (((MenuMenuname *)app)->layout.inline_alias_is_set)
+                    ((MenuMenu *)l->data)->layout.inline_alias = ((MenuMenuname *)app)->layout.inline_alias;
+                if (((MenuMenuname *)app)->layout.inline_limit_is_set)
+                    ((MenuMenu *)l->data)->layout.inline_limit = ((MenuMenuname *)app)->layout.inline_limit;
+                /* remove from menu->children */
+                menu->children = g_list_remove_link(menu->children, l);
+                /* prepend to result */
+                result = g_list_concat(l, result);
+                /* ready for recursion now */
+                _stage1(l->data, dirs, apps, legacy, p);
+            }
+            break;
+        case MENU_CACHE_TYPE_APP: /* MemuFilename */
+            VDBG("composing Filename %s", ((MenuFilename *)app)->id);
+            app = g_hash_table_lookup(all_apps, ((MenuFilename *)app)->id);
+            if (app == NULL || !app->matched || app->excluded)
+                /* not available, ignoring it */
+                break;
+            l = g_list_find(result, app); /* this might be slow but we have
+                                             to do this because app might be
+                                             already added into result */
+            if (l != NULL)
+                /* move it out to this place */
+                result = g_list_remove_link(result, l);
+            else
+            {
+                l = g_list_find(available, app);
+                if (l != NULL)
+                    available = g_list_remove_link(available, l);
+            }
+            result = g_list_concat(l, result);
+            break;
+        case MENU_CACHE_TYPE_SEP: /* MenuSep */
+            VDBG("composing Separator");
+            result = g_list_prepend(result, app);
+            break;
+        case MENU_CACHE_TYPE_NONE: /* MenuMerge */
+            VDBG("composing Merge type %d", ((MenuMerge *)app)->merge_type);
+            next = NULL;
+            switch (((MenuMerge *)app)->merge_type) {
+            case MERGE_FILES:
+                tag = 1; /* use it as mark to not add dirs */
+            case MERGE_ALL:
+                for (l = available; l; l = l->next)
+                {
+                    app = l->data;
+                    VVDBG("+++ composing app %s", app->id);
+                    if (app->key == NULL)
+                    {
+                        if (app->title != NULL)
+                            app->key = g_utf8_collate_key(app->title, -1);
+                        else
+                            g_warning("id %s has no Name", app->id),
+                            app->key = g_utf8_collate_key(app->id, -1);
+                    }
+                    app->menus = g_list_prepend(app->menus, menu);
+                }
+                next = available;
+                available = NULL;
+                /* continue with menus */
+            case MERGE_MENUS:
+                for (l = menu->children; l; )
+                {
+                    if (((MenuMenu *)l->data)->layout.type == MENU_CACHE_TYPE_DIR)
+                    {
+                        GList *this = l;
+
+                        /* find it in the rest of layout and skip if it's found */
+                        for (l = child->next; l; l = l->next)
+                            if (((MenuMenuname *)l->data)->layout.type == MENU_CACHE_TYPE_DIR &&
+                                strcmp(((MenuMenuname *)l->data)->name, ((MenuMenu *)this->data)->name) == 0)
+                                break;
+                        if (l != NULL)
+                        {
+                            /* it will be added later by MenuMenuname handler */
+                            l = this->next;
+                            continue;
+                        }
+     &nb